Rietveld Code Review Tool
Help | Bug tracker | Discussion group | Source code

Delta Between Two Patch Sets: sitescripts/formmail/test/test_formmail2.py

Issue 29374647: Issue 4814 - Adds csv log to formmail2 (Closed) Base URL: https://hg.adblockplus.org/sitescripts
Left Patch Set: Created Feb. 13, 2017, 12:54 p.m.
Right Patch Set: Created March 23, 2017, 6:50 p.m.
Left:
Right:
Use n/p to move between diff chunks; N/P to move between comments.
Jump to:
Left: Side by side diff | Download
Right: Side by side diff | Download
« no previous file with change/comment | « .sitescripts.example ('k') | sitescripts/formmail/web/formmail2.py » ('j') | no next file with change/comment »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
LEFTRIGHT
1 # This file is part of the Adblock Plus web scripts, 1 # This file is part of the Adblock Plus web scripts,
2 # Copyright (C) 2006-2016 Eyeo GmbH 2 # Copyright (C) 2006-2017 eyeo GmbH
3 # 3 #
4 # Adblock Plus is free software: you can redistribute it and/or modify 4 # Adblock Plus is free software: you can redistribute it and/or modify
5 # it under the terms of the GNU General Public License version 3 as 5 # it under the terms of the GNU General Public License version 3 as
6 # published by the Free Software Foundation. 6 # published by the Free Software Foundation.
7 # 7 #
8 # Adblock Plus is distributed in the hope that it will be useful, 8 # Adblock Plus is distributed in the hope that it will be useful,
9 # but WITHOUT ANY WARRANTY; without even the implied warranty of 9 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 10 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # GNU General Public License for more details. 11 # GNU General Public License for more details.
12 # 12 #
13 # You should have received a copy of the GNU General Public License 13 # You should have received a copy of the GNU General Public License
14 # along with Adblock Plus. If not, see <http://www.gnu.org/licenses/>. 14 # along with Adblock Plus. If not, see <http://www.gnu.org/licenses/>.
15 15
16 from urllib import urlencode 16 from urllib import urlencode
17 from urllib2 import urlopen, HTTPError 17 from urllib2 import urlopen, HTTPError
18 from csv import DictReader
18 19
19 import pytest 20 import pytest
20 import datetime
21 from wsgi_intercept import (urllib_intercept, add_wsgi_intercept, 21 from wsgi_intercept import (urllib_intercept, add_wsgi_intercept,
22 remove_wsgi_intercept) 22 remove_wsgi_intercept)
23 from csv import DictReader 23
Sebastian Noack 2017/02/14 09:54:14 The imports are grouped incorrectly here. Accordin
Jon Sonesen 2017/02/21 14:19:15 Done.
24 from sitescripts.formmail.web import formmail2 24 from sitescripts.formmail.web import formmail2
25 25
26 HOST = 'test.local'
27 LOG_PORT = 80
28 NO_LOG_PORT = 81
26 29
27 @pytest.fixture() 30
28 def form_config(): 31 @pytest.fixture
32 def log_path(tmpdir):
33 return str(tmpdir.join('test.csv_log'))
34
35
36 @pytest.fixture
37 def log_form_config():
29 return formmail2.conf_parse(formmail2.get_config_items())['test'] 38 return formmail2.conf_parse(formmail2.get_config_items())['test']
30 39
31 40
32 @pytest.fixture() 41 @pytest.fixture
33 def form_handler(form_config, log_path): 42 def form_config():
34 # override configured path to log file with tmpdir path 43 config = formmail2.conf_parse(formmail2.get_config_items())['test']
35 form_config['csv_log'].value = log_path 44 del config['csv_log']
36 return formmail2.make_handler('test', form_config)[1] 45 return config
37 46
38 47
39 @pytest.fixture() 48 @pytest.fixture
40 def log_path(form_config, tmpdir): 49 def form_handler(log_path, form_config, log_form_config):
41 log = tmpdir.mkdir('logs').join('test.csv_log') 50 """ Create two handlers, one that logs and another that doesn't """
Sebastian Noack 2017/02/14 09:54:14 The temporary variable seems unnecessary. Why not
Vasily Kuznetsov 2017/02/15 10:22:26 Also mkdir() seems unnecessary. tmpdir.join('test.
Jon Sonesen 2017/02/20 09:55:15 Done.
Jon Sonesen 2017/02/20 09:55:16 Done.
42 return str(log) 51 log_form_config['csv_log'].value = log_path
52 return (formmail2.make_handler('test', log_form_config)[1],
53 formmail2.make_handler('test', form_config)[1])
43 54
44 55
45 # We make this a fixture instead of a constant so we can modify it in each 56 # We make this a fixture instead of a constant so we can modify it in each
46 # test as needed without affecting other tests. 57 # test as needed without affecting other tests.
47 @pytest.fixture 58 @pytest.fixture
48 def form_data(): 59 def form_data():
49 return { 60 return {
50 'email': 'john_doe@gmail.com', 61 'email': 'john_doe@gmail.com',
51 'mandatory': 'john_doe@gmail.com', 62 'mandatory': 'john_doe@gmail.com',
52 'non_mandatory_message': 'Once upon a time\nthere lived a king.', 63 'non_mandatory_message': 'Once upon a time\nthere lived a king.',
53 'non_mandatory_email': 'test@test.com', 64 'non_mandatory_email': 'test@test.com',
54 } 65 }
55 66
56 67
57 @pytest.fixture() 68 @pytest.fixture
58 def response_for(form_handler, log_path): 69 def response_for(form_handler):
59 host, port = 'test.local', 80 70 """ Registers two intercepts, returns responses for them based on bool """
60 urllib_intercept.install_opener() 71 urllib_intercept.install_opener()
61 add_wsgi_intercept(host, port, lambda: form_handler) 72 add_wsgi_intercept(HOST, LOG_PORT, lambda: form_handler[0])
62 url = 'http://{}:{}'.format(host, port) 73 add_wsgi_intercept(HOST, NO_LOG_PORT, lambda: form_handler[1])
63 74
64 def response_for(data): 75 def response_for(data, log=False):
76 if log:
77 url = 'http://{}:{}'.format(HOST, LOG_PORT)
78 else:
79 url = 'http://{}:{}'.format(HOST, NO_LOG_PORT)
65 if data is None: 80 if data is None:
66 response = urlopen(url) 81 response = urlopen(url)
67 else: 82 else:
68 response = urlopen(url, urlencode(data)) 83 response = urlopen(url, urlencode(data))
69 return response.code, response.read() 84 return response.code, response.read()
70 85
71 yield response_for 86 yield response_for
72 remove_wsgi_intercept() 87 remove_wsgi_intercept()
73 88
74 89
90 @pytest.fixture
91 def sm_mock(mocker):
92 return mocker.patch('sitescripts.formmail.web.formmail2.sendMail')
93
94
95 @pytest.mark.parametrize('key,message', [
96 ('url', 'No URL configured for form handler: test'),
97 ('fields', 'No fields configured for form handler: test'),
98 ('template', 'No template configured for form handler: test'),
99 ])
100 def test_config_errors(key, message, form_config):
101 del form_config[key]
102 with pytest.raises(Exception) as error:
103 formmail2.make_handler('test', form_config)[1]
104 assert error.value.message == message
105
106
107 @pytest.mark.parametrize('field,message', [
108 (('new_field', 'foo'), 'Unexpected field/fields: new_field'),
109 (('mandatory', ''), 'No mandatory entered'),
110 (('non_mandatory_email', 'asfaf'), 'Invalid email'),
111 (('email', 'asfaf'), 'You failed the email validation'),
112 (('email', ''), 'You failed the email test'),
113 ])
114 def test_http_errs(field, message, response_for, form_data, sm_mock):
115 key, value = field
116 form_data[key] = value
117 with pytest.raises(HTTPError) as error:
118 response_for(form_data)
119 assert error.value.read() == message
120
121
122 @pytest.mark.parametrize('field,expected', [
123 (('non_mandatory_message', '\xc3\xb6'), (200, '')),
124 (('non_mandatory_message', ''), (200, '')),
125 ])
126 def test_success(field, expected, log_path, response_for, form_data, sm_mock):
127 key, value = field
128 form_data[key] = value
129 assert response_for(form_data, log=False) == expected
130 assert sm_mock.call_count == 1
131
132 params = sm_mock.call_args[0][1]['fields']
133 assert set(params.keys()) == set(form_data.keys())
134 for key, value in form_data.items():
135 assert params[key] == value.decode('utf8')
136
137 assert response_for(form_data, log=True) == expected
138 assert sm_mock.call_count == 2
139
140 assert response_for(form_data, log=True) == expected
141 assert sm_mock.call_count == 3
142
143 with open(log_path) as log_file:
144 reader = DictReader(log_file)
145 row = reader.next()
146 # rows should not be equal because the time field
147 # is added by the logging function.
148 assert row != reader.next()
149
150
151 def test_config_field_errors(form_config):
152 form_config['fields'] = {}
153 with pytest.raises(Exception) as error:
154 formmail2.make_handler('test', form_config)[1]
155 assert error.value.message == 'No fields configured for form handler: test'
156
157
158 def test_config_template_errors(form_config):
159 form_config['template'].value = 'no'
160 with pytest.raises(Exception) as error:
161 formmail2.make_handler('test', form_config)[1]
162 assert error.value.message == 'Template not found at: no'
163
164
75 def test_config_parse(form_config): 165 def test_config_parse(form_config):
76 assert form_config['url'].value == 'test/apply/submit' 166 assert form_config['url'].value == 'test/apply/submit'
77 assert form_config['fields']['email'].value == 'mandatory, email' 167 assert form_config['fields']['email'].value == 'mandatory, email'
78 168
79 169
80 def test_success(response_for, form_data, mocker): 170 def test_sendmail_fail(log_path, response_for, form_data, sm_mock):
81 sm_mock = mocker.patch('sitescripts.formmail.web.formmail2.sendMail') 171 sm_mock.side_effect = Exception('Sendmail Fail')
82 assert response_for(form_data) == (200, '') 172 with pytest.raises(HTTPError):
83 assert sm_mock.call_count == 1 173 response_for(form_data, log=True)
84 params = sm_mock.call_args[0][1]['fields']
85 assert set(params.keys()) == set(form_data.keys())
86 for key, value in form_data.items():
87 assert params[key] == value
88 174
89 175 with open(log_path) as log_file:
90 def test_non_mandatory_no_msg(response_for, form_data, mocker): 176 row = DictReader(log_file).next()
91 mocker.patch('sitescripts.formmail.web.formmail2.sendMail') 177 assert row != form_data
92 form_data['non_mandatory'] = ''
93 assert response_for(form_data) == (200, '')
94
95
96 def test_invalid_email_cstm_msg(response_for, form_data, mocker, form_config):
97 mocker.patch('sitescripts.formmail.web.formmail2.sendMail')
98 form_data['email'] = 'bademail'
99 with pytest.raises(HTTPError) as error:
100 response_for(form_data)
101 assert error.value.read() == 'You failed the email validation'
102
103
104 def test_valid_nan_mandatory_email(response_for, form_data, mocker):
105 mocker.patch('sitescripts.formmail.web.formmail2.sendMail')
106 form_data['non_mandatory_email'] = 'asfaf'
107 with pytest.raises(HTTPError) as error:
108 response_for(form_data)
109 assert error.value.read() == 'Invalid email'
110
111 del form_data['non_mandatory_email']
112 assert response_for(form_data) == (200, '')
113
114
115 def test_mandatory_fail_dflt_msg(response_for, form_data, mocker):
116 mocker.patch('sitescripts.formmail.web.formmail2.sendMail')
117 del form_data['mandatory']
118 with pytest.raises(HTTPError) as error:
119 response_for(form_data)
120 assert error.value.read() == 'No mandatory entered'
121
122
123 def test_collect_with_tmpl(log_path, form_data):
124 form_data['time'] = 'test'
125 formmail2.collect_formdata(form_data, log_path)
126 with open(log_path) as csvfile:
127 reader = DictReader(csvfile)
Sebastian Noack 2017/02/14 09:54:14 Any reason you didn't just inline those variables?
Jon Sonesen 2017/02/20 09:55:15 Will do.
128 row = reader.next()
129 assert row == form_data
130
131
132 def test_collect_no_tmpl(log_path, form_data, form_config):
133 del(form_config['template'])
134 form_data['time'] = 'test'
135 formmail2.collect_formdata(form_data, log_path)
136 with open(log_path) as csvfile:
137 reader = DictReader(csvfile)
138 row = reader.next()
139 assert row == form_data
140
141
142 def test_fieldnames(log_path, form_data):
143 form_data['time'] = str(datetime.datetime.now())
144 formmail2.collect_formdata(form_data, log_path)
145 with open(log_path) as csvfile:
146 reader = DictReader(csvfile)
147 for field in reader.fieldnames:
148 assert field in tuple(form_data.keys())
Sebastian Noack 2017/02/14 09:54:14 It seems unnecessary to coerce it to a tuple.
Jon Sonesen 2017/02/20 09:55:15 Yeah, you are right.
149
150
151 def test_field_err(form_config, form_data, log_path):
152 """
153 Submits a form that does not have the dame fields as previous submissions
154 that have the same form name, asserts that proper message is returned and
155 the row was properly written
156 """
157 formmail2.collect_formdata(form_data, log_path)
158 del(form_config['fields']['email'])
159 del(form_data['email'])
160 try:
161 formmail2.collect_formdata(form_data, log_path)
162 except Exception as e:
163 assert e.message == \
164 'Field names have changed, error log written to {}'\
165 .format(log_path + '_error')
166
167 with open(log_path+'_error') as error_log:
168 reader = DictReader(error_log)
169 assert reader.next() == form_data
170 178
171 179
172 def test_append_field_err(form_config, form_data, log_path): 180 def test_append_field_err(form_config, form_data, log_path):
181 """ Checks that error logs are correctly written and appended
182
183 Submits three forms, the second two have different fields to the first
184 and should be added to the same log file as each other, and be identical
173 """ 185 """
174 Submits two identical forms that do not match the previous fields 186 formmail2.log_formdata(form_data, log_path)
175 found in the log file, triggering two rows to be added to the error 187 del form_data['email']
176 log and asserting the proper message is returned and that the rows
177 were written as expected
178 """
179 formmail2.collect_formdata(form_data, log_path)
180 del(form_config['fields']['email'])
181 del(form_data['email'])
182 try:
183 formmail2.collect_formdata(form_data, log_path)
184 except Exception as e:
Sebastian Noack 2017/02/14 09:54:13 Since you ignore the exception, there is no point
Jon Sonesen 2017/02/20 09:55:15 I was playing around with asserting the message he
185 pass
186 try:
187 formmail2.collect_formdata(form_data, log_path)
188 except Exception as e:
189 assert e.message == \
Sebastian Noack 2017/02/14 09:54:14 The way the code if wrapped here seems hard to fol
Jon Sonesen 2017/02/20 09:55:15 I agree, it is better how you suggest, thank you
190 'Field names have changed, error log appended to {}'\
191 .format(log_path + '_error')
192 188
193 with open(log_path+'_error') as error_log: 189 # submit two forms with fields that dont match the config
190 # this should append the second form to the error log file
191 with pytest.raises(Exception):
192 formmail2.log_formdata(form_data, log_path)
193 with pytest.raises(Exception):
194 formmail2.log_formdata(form_data, log_path)
195
196 with open(log_path + '_error') as error_log:
194 reader = DictReader(error_log) 197 reader = DictReader(error_log)
195 # two identical rows should be in the rror log
196 assert reader.next() == form_data 198 assert reader.next() == form_data
197 assert reader.next() == form_data 199 assert reader.next() == form_data
198
199
200 def test_append_log(form_data, log_path):
201 """
202 collect data twice, altering a field in the second call
203 assert that the 2nd row is equal to the resulting form data
204 """
205 form_data['time'] = str(datetime.datetime.now())
206 formmail2.collect_formdata(form_data, log_path)
207 form_data['email'] = 'test@foo.com'
208 formmail2.collect_formdata(form_data, log_path)
209 with open(log_path) as csvfile:
210 reader = DictReader(csvfile, fieldnames=form_data.keys())
211 # header
212 reader.next()
213 row1 = reader.next()
214 row2 = reader.next()
215
216 assert row2['email'] == form_data['email']
217 assert row2['email'] != row1['email']
LEFTRIGHT

Powered by Google App Engine
This is Rietveld