blob: 70c40fda867f5e12104e37d9e44e1d9c1a73d146 [file] [log] [blame]
Adrià Vilanova Martínezf19ea432024-01-23 20:20:52 +01001# Copyright 2016 The Chromium Authors
2# Use of this source code is governed by a BSD-style license that can be
3# found in the LICENSE file.
Copybara854996b2021-09-07 19:36:02 +00004
5"""Helpers for testing."""
6from __future__ import print_function
7from __future__ import division
8from __future__ import absolute_import
9
10import email
Adrià Vilanova Martínezf19ea432024-01-23 20:20:52 +010011from six.moves import urllib
Copybara854996b2021-09-07 19:36:02 +000012
13from framework import emailfmt
14from framework import framework_bizobj
Adrià Vilanova Martínezf19ea432024-01-23 20:20:52 +010015from mrproto import user_pb2
Copybara854996b2021-09-07 19:36:02 +000016from services import service_manager
17from services import template_svc
18from testing import fake
19from tracker import tracker_constants
Copybara854996b2021-09-07 19:36:02 +000020
21DEFAULT_HOST = '127.0.0.1'
22
23MINIMAL_HEADER_LINES = [
24 ('From', 'user@example.com'),
25 ('To', 'proj@monorail.example.com'),
26 ('Cc', 'ningerso@chromium.org'),
27 ('Subject', 'Issue 123 in proj: broken link'),
28]
29
30# Add one more (long) line for In-Reply-To
31HEADER_LINES = MINIMAL_HEADER_LINES + [
32 ('In-Reply-To', '<0=969704940193871313=13442892928193434663='
33 'proj@monorail.example.com>'),
34]
35
36AlertEmailHeader = emailfmt.AlertEmailHeader
37ALERT_EMAIL_HEADER_LINES = HEADER_LINES + [
38 (AlertEmailHeader.INCIDENT_ID, '1234567890123456789'),
39 (AlertEmailHeader.OWNER, 'owner@example.com'),
40 (AlertEmailHeader.CC, 'cc1@example.com,cc2@example.com'),
41 (AlertEmailHeader.PRIORITY, '0'),
42 (AlertEmailHeader.STATUS, 'Unconfirmed'),
43 (AlertEmailHeader.COMPONENT, 'Component'),
44 (AlertEmailHeader.TYPE, 'Bug'),
45 (AlertEmailHeader.OS, 'Android,Windows'),
46 (AlertEmailHeader.LABEL, ''),
47]
48
49
50# TODO(crbug/monorail/7238): this should be moved to framework_bizobj
51# as this is no longer only used for testing.
52def ObscuredEmail(address):
53 (_username, _domain, _obs_username,
54 obs_email) = framework_bizobj.ParseAndObscureAddress(address)
55 return obs_email
56
57def MakeMessage(header_list, body):
58 """Convenience function to make an email.message.Message."""
59 msg = email.message.Message()
60 for key, value in header_list:
61 msg[key] = value
62 msg.set_payload(body)
63 return msg
64
65
66def MakeMonorailRequest(*args, **kwargs):
67 """Get just the monorailrequest.MonorailRequest() from GetRequestObjects."""
68 _request, mr = GetRequestObjects(*args, **kwargs)
69 return mr
70
71
72def GetRequestObjects(
Adrià Vilanova Martínezf19ea432024-01-23 20:20:52 +010073 headers=None, path='/', params=None, user_info=None,
Copybara854996b2021-09-07 19:36:02 +000074 project=None, method='GET', perms=None, services=None, hotlist=None):
75 """Make fake request and MonorailRequest objects for testing.
76
77 Host param will override the 'Host' header, and has a default value of
78 '127.0.0.1'.
79
80 Args:
81 headers: Dict of HTTP header strings.
82 path: Path part of the URL in the request.
83 params: Dict of query-string parameters.
84 user_info: Dict of user attributes to set on a MonorailRequest object.
85 For example, "user_id: 5" causes self.auth.user_id=5.
86 project: optional Project object for the current request.
87 method: 'GET' or 'POST'.
88 perms: PermissionSet to use for this request.
89 services: Connections to backends.
90 hotlist: optional Hotlist object for the current request
91
92 Returns:
93 A tuple of (http Request, monorailrequest.MonorailRequest()).
94 """
95 headers = headers or {}
96 params = params or {}
97
98 headers.setdefault('Host', DEFAULT_HOST)
99 post_items=None
Adrià Vilanova Martínezf19ea432024-01-23 20:20:52 +0100100 if method == 'POST' and params:
Copybara854996b2021-09-07 19:36:02 +0000101 post_items = params
102
103 if not services:
104 services = service_manager.Services(
105 project=fake.ProjectService(),
106 user=fake.UserService(),
107 usergroup=fake.UserGroupService(),
108 features=fake.FeaturesService())
109 services.project.TestAddProject('proj')
110 services.features.TestAddHotlist('hotlist')
111
Adrià Vilanova Martínezf19ea432024-01-23 20:20:52 +0100112 request = RequestStub(path, headers=headers, values=post_items)
Copybara854996b2021-09-07 19:36:02 +0000113 mr = fake.MonorailRequest(
114 services, user_info=user_info, project=project, perms=perms,
115 params=params, hotlist=hotlist)
116 mr.ParseRequest(
117 request, services, do_user_lookups=False)
118 mr.auth.user_pb = user_pb2.MakeUser(0)
119 return request, mr
120
121
Adrià Vilanova Martínezf19ea432024-01-23 20:20:52 +0100122class RequestStub(object):
123 """flask.Request stub object.
124
125 This stub is a drop-in replacement for flask.Request that implements all
126 fields used in MonorailRequest.ParseRequest(). Its constructor API is
127 designed to mimic webapp2.Request.blank() for backwards compatibility with
128 existing unit tests previously written for webapp2.
129 """
130
131 def __init__(self, path, headers=None, values=None):
132 self.scheme = 'http'
133 self.path = path
134 self.headers = headers or {}
135 # webapp2.Request.blank() overrides the host from the request headers.
136 self.host = self.headers.get('Host', 'localhost:80')
137 self.host_url = self.scheme + '://' + self.host + '/'
138 self.url = self.scheme + '://' + self.host + path
139
140 parsed_url = urllib.parse.urlsplit(self.url)
141 self.base_url = self.host_url + parsed_url.path # No query string.
142
143 self.values = values or {}
144 # webapp2.Request.blank() parses the query string from the path.
145 query = urllib.parse.parse_qs(parsed_url.query, True)
146 self.values.update({key: value[0] for key, value in query.items()})
147
148
Copybara854996b2021-09-07 19:36:02 +0000149class Blank(object):
150 """Simple class that assigns all named args to attributes.
151
152 Tip: supply a lambda to define a method.
153 """
154
155 def __init__(self, **kwargs):
156 vars(self).update(kwargs)
157
158 def __repr__(self):
159 return '%s(%s)' % (self.__class__.__name__, str(vars(self)))
160
161 def __eq__(self, other):
162 if other is None:
163 return False
164 return vars(self) == vars(other)
165
166
167def DefaultTemplateRows():
168 return [(
169 None,
170 789,
171 template_dict['name'],
172 template_dict['content'],
173 template_dict['summary'],
174 template_dict.get('summary_must_be_edited'),
175 None,
176 template_dict['status'],
177 template_dict.get('members_only', False),
178 template_dict.get('owner_defaults_to_member', True),
179 template_dict.get('component_required', False),
180 ) for template_dict in tracker_constants.DEFAULT_TEMPLATES]
181
182
183def DefaultTemplates():
184 return [template_svc.UnpackTemplate(t) for t in DefaultTemplateRows()]