]> WPIA git - motion.git/blob - tests/test_motion.py
Merge branch 'proxy-vote' into 'master'
[motion.git] / tests / test_motion.py
1 import motion
2 import unittest
3 import postgresql
4 from unittest import TestCase
5 from motion import app
6 from datetime import datetime
7
8 app.config.update(
9     DEBUGUSER = {},
10     GROUP_PREFIX = {'127.0.0.1:5000': {'group1': 'g1', 'group2': 'g2'}},
11     DURATION = {'127.0.0.1:5000':[3, 7, 14]},
12     SERVER_NAME = '127.0.0.1:5000',
13     MAX_PROXY=2
14 )
15
16 app.config['TESTING'] = True
17 app.config['DEBUG'] = False
18
19
20 class BasicTest(TestCase):
21
22     def init_test(self):
23         self.app = app.test_client()
24         self.assertEqual(app.debug, False)
25
26         # reset database
27         self.db_clear()
28
29     # functions to manipulate motions
30     def createVote(self, user, motion, vote, voter):
31         return self.app.post(
32             '/motion/' + motion + '/vote/' + str(voter),
33             environ_base={'USER_ROLES': user},
34             data=dict(vote=vote)
35         )
36         
37
38     def createMotion(self, user, motiontitle, motioncontent, days, category):
39         return self.app.post(
40             '/motion',
41             environ_base={'USER_ROLES': user},
42             data=dict(title=motiontitle, content=motioncontent, days=days, category=category)
43         )
44
45     def cancelMotion(self, user, motion, reason):
46         return self.app.post(
47             '/motion/' + motion +'/cancel',
48             environ_base={'USER_ROLES': user},
49             data=dict(reason=reason)
50         )
51
52     def finishMotion(self, user, motion):
53         return self.app.post(
54             '/motion/' + motion +'/finish',
55             environ_base={'USER_ROLES': user}
56         )
57
58     def addProxy(self, user, voter, proxy):
59         return self.app.post(
60             '/proxy/add',
61             environ_base={'USER_ROLES': user},
62             data=dict(voter=voter, proxy=proxy)
63         )
64
65     def revokeProxy(self, user, id):
66         return self.app.post(
67             '/proxy/revoke',
68             environ_base={'USER_ROLES': user},
69             data=dict(id=id)
70         )
71
72     def buildResultText(self, motiontext, yes, no, abstain):
73         return '<p>'+motiontext+'</p></p>\n    <p>\nYes <span class=\"badge badge-pill badge-secondary\">'+str(yes)+'</span><br>'\
74             + '\nNo <span class=\"badge badge-pill badge-secondary\">'+str(no)+'</span><br>'\
75             + '\nAbstain <span class=\"badge badge-pill badge-secondary\">'+str(abstain)+'</span>'
76
77     # functions to clear database
78     def db_clear(self):
79         with postgresql.open(app.config.get("DATABASE"), user=app.config.get("USER"), password=app.config.get("PASSWORD")) as db:
80             with app.open_resource('sql/schema.sql', mode='r') as f:
81                 db.execute(f.read())
82
83     def db_sampledata(self):
84         with postgresql.open(app.config.get("DATABASE"), user=app.config.get("USER"), password=app.config.get("PASSWORD")) as db:
85             with app.open_resource('sql/sample_data.sql', mode='r') as f:
86                 db.execute(f.read())
87
88
89 # no specific rights required
90 class GeneralTests(BasicTest):
91
92     def setUp(self):
93         self.init_test()
94         global user
95         user = 'testuser/'
96         global userid
97         userid = 4
98         self.db_sampledata()
99
100     def tearDown(self):
101         pass
102
103     def test_main_page(self):
104         response = self.app.get('/', environ_base={'USER_ROLES': user}, follow_redirects=True)
105         self.assertEqual(response.status_code, 200)
106
107     def test_basic_results_data(self):
108         result = self.app.get('/', environ_base={'USER_ROLES': user}, follow_redirects=True)
109         testtext= '<div class="motion card" id="motion-3">\n  <div class="motion-title card-heading alert-warning">'\
110             + '\n    <span class=\"title-text\">Motion C</span> (Canceled)\n    <span class=\"motion-type\">group1</span>'\
111             + '\n    <div># g1.20200402.003'\
112             + '\n    <a class="btn btn-primary" href="/motion/g1.20200402.003" role="button">Result</a>'\
113             + '\n    </div>'\
114             + '\n    <div class=\"date\">\n      <div>Proposed: 2020-04-02 21:47:24 (UTC) by User A</div>'\
115             + '\n      <div>Canceled: 2020-04-03 21:48:24 (UTC) by User A</div></div>\n  </div>'\
116             + '\n  <div class=\"card-body\">\n    <p><p>A third motion</p></p>'\
117             + '\n    <p>\nYes <span class=\"badge badge-pill badge-secondary\">1</span><br>'\
118             + '\nNo <span class=\"badge badge-pill badge-secondary\">0</span><br>'\
119             + '\nAbstain <span class=\"badge badge-pill badge-secondary\">0</span><br>\n    </p>'\
120             + '\n    <p>Cancelation reason: Entered with wrong text</p>\n  </div>\n</div>'
121         self.assertIn(str.encode(testtext), result.data)
122         testtext= '<div class="motion card" id="motion-2">\n  <div class="motion-title card-heading alert-danger">'\
123             + '\n    <span class=\"title-text\">Motion B</span> (Finished)\n    <span class=\"motion-type\">group1</span>'\
124             + '\n    <div># g1.20200402.002'\
125             + '\n    <a class="btn btn-primary" href="/motion/g1.20200402.002" role="button">Result</a>'\
126             + '\n    </div>'\
127             + '\n    <div class=\"date\">\n      <div>Proposed: 2020-04-02 21:41:26 (UTC) by User A</div>'\
128             + '\n      <div>Votes until: 2020-04-04 21:41:26 (UTC)</div></div>\n  </div>'\
129             + '\n  <div class=\"card-body\">\n    <p><p>A second motion</p></p>'\
130             + '\n    <p>\nYes <span class=\"badge badge-pill badge-secondary\">1</span><br>'\
131             + '\nNo <span class=\"badge badge-pill badge-secondary\">2</span><br>'\
132             + '\nAbstain <span class=\"badge badge-pill badge-secondary\">0</span><br>\n    </p>\n  </div>\n</div>\n'
133         self.assertIn(str.encode(testtext), result.data)
134         testtext= '<div class=\"motion card\" id=\"motion-1\">\n  <div class=\"motion-title card-heading alert-success\">'\
135             + '\n    <span class=\"title-text\">Motion A</span> (Finished)\n    <span class=\"motion-type\">group1</span>'\
136             + '\n    <div># g1.20200402.001'\
137             + '\n    <a class="btn btn-primary" href="/motion/g1.20200402.001" role="button">Result</a>'\
138             + '\n    </div>'\
139             + '\n    <div class=\"date">\n      <div>Proposed: 2020-04-02 21:40:33 (UTC) by User A</div>'\
140             + '\n      <div>Votes until: 2020-04-02 21:40:33 (UTC)</div></div>\n  </div>'\
141             + '\n  <div class=\"card-body\">\n    <p><p>My special motion</p></p>'\
142             + '\n    <p>\nYes <span class=\"badge badge-pill badge-secondary\">2</span><br>'\
143             + '\nNo <span class=\"badge badge-pill badge-secondary\">1</span><br>'\
144             + '\nAbstain <span class=\"badge badge-pill badge-secondary\">0</span><br>\n    </p>\n  </div>\n</div>\n</div>'
145         self.assertIn(str.encode(testtext), result.data)
146         testtext= 'Proxy management'
147         self.assertNotIn(str.encode(testtext), result.data)
148
149         # start with second motion
150         result = self.app.get('/', environ_base={'USER_ROLES': user}, query_string=dict(start=2))
151         testtext= 'id=\"motion-3\">'
152         self.assertNotIn(str.encode(testtext), result.data)
153         testtext= 'id=\"motion-2">'
154         self.assertIn(str.encode(testtext), result.data)
155         testtext= 'id=\"motion-1\">'
156         self.assertIn(str.encode(testtext), result.data)
157
158     def test_basic_results_data_details(self):
159         motion='g1.20200402.002'
160         result = self.app.get('/motion/' + motion, environ_base={'USER_ROLES': user}, follow_redirects=True)
161         testtext= '<p>A second motion</p></p>\n  </div>\n</div>\n<a href=\"/?start=2#motion-2\" class=\"btn btn-primary\">Back</a>'
162         self.assertIn(str.encode(testtext), result.data)
163
164     def test_vote(self):
165         motion='g1.20200402.004'
166         response = self.createVote(user, motion, 'yes', userid)
167         self.assertEqual(response.status_code, 403)
168         self.assertIn(str.encode('Forbidden'), response.data)
169
170     def test_no_user(self):
171         result = self.app.get('/', follow_redirects=True)
172         self.assertEqual(result.status_code, 500)
173         self.assertIn(str.encode('Server misconfigured'), result.data)
174
175     def test_user_invalid(self):
176         result = self.app.get('/', environ_base={'USER_ROLES': '<invalid>/'}, follow_redirects=True)
177         self.assertEqual(result.status_code, 403)
178         self.assertIn(str.encode('Access denied'), result.data)
179
180     def test_basic_env(self):
181         result = self.app.get('/', environ_base={'USER': 'testuser', 'ROLES':''}, follow_redirects=True)
182         testtext= 'id=\"motion-3\">'
183         self.assertIn(str.encode(testtext), result.data)
184
185     def test_basic_results_data_details_not_given(self):
186         motion='g1.30190402.001'
187         result = self.app.get('/motion/' + motion, environ_base={'USER_ROLES': user}, follow_redirects=True)
188         self.assertEqual(result.status_code, 404)
189         self.assertIn(str.encode('Error, Not found'), result.data)
190
191     def test_no_proxy(self):
192         result = self.app.get('proxy', environ_base={'USER_ROLES': user}, follow_redirects=True)
193         self.assertEqual(result.status_code, 403)
194         self.assertIn(str.encode('Forbidden'), result.data)
195
196     def test_no_proxy_add(self):
197         result = self.app.post('proxy/add', environ_base={'USER_ROLES': user}, follow_redirects=True)
198         self.assertEqual(result.status_code, 403)
199         self.assertIn(str.encode('Forbidden'), result.data)
200
201     def test_no_proxy_revoke(self):
202         result = self.app.post('proxy/revoke', environ_base={'USER_ROLES': user}, follow_redirects=True)
203         self.assertEqual(result.status_code, 403)
204         self.assertIn(str.encode('Forbidden'), result.data)
205
206     def test_no_proxy_revokeAll(self):
207         result = self.app.post('proxy/revokeall', environ_base={'USER_ROLES': user}, follow_redirects=True)
208         self.assertEqual(result.status_code, 403)
209         self.assertIn(str.encode('Forbidden'), result.data)
210         
211 class VoterTests(BasicTest):
212
213     def setUp(self):
214         self.init_test()
215         global user
216         user='testuser/vote:*'
217         global userid
218         userid = 4
219         self.db_sampledata()
220
221     def tearDown(self):
222         pass
223
224     def test_main_page(self):
225         response = self.app.get('/', environ_base={'USER_ROLES': user}, follow_redirects=True)
226         self.assertEqual(response.status_code, 200)
227
228     def test_home_data(self):
229         result = self.app.get('/', environ_base={'USER_ROLES': user})
230         self.assertNotIn("<select class=\"float form-control\" name=\"category\">", str(result.data) )
231
232     def test_vote_yes(self):
233         motion='g1.20200402.004'
234         response = self.createVote(user, motion, 'yes', userid)
235         self.assertEqual(response.status_code, 302)
236         result = self.app.get('/', environ_base={'USER_ROLES': user})
237         resulttext=self.buildResultText('A fourth motion', 1, 0, 0)
238         result = self.app.get('/motion/' + motion, environ_base={'USER_ROLES': user}, follow_redirects=True)
239         testtext= 'class=\"btn btn-success\" name=\"vote\" value="yes" id="vote-yes">yes</button>'
240         self.assertIn(str.encode(testtext), result.data)
241         testtext= 'class=\"btn btn-primary\" name=\"vote\" value=\"no\" id=\"vote-no\">no</button>'
242         self.assertIn(str.encode(testtext), result.data)
243         testtext= 'class=\"btn btn-primary\" name=\"vote\" value=\"abstain\" id=\"vote-abstain\">abstain</button>'
244         self.assertIn(str.encode(testtext), result.data)
245
246     def test_vote_no(self):
247         motion='g1.20200402.004'
248         response = self.createVote(user, motion, 'no', userid)
249         self.assertEqual(response.status_code, 302)
250         result = self.app.get('/', environ_base={'USER_ROLES': user})
251         resulttext=self.buildResultText('A fourth motion', 0, 1, 0)
252         self.assertIn(str.encode(resulttext), result.data)
253         result = self.app.get('/motion/' + motion, environ_base={'USER_ROLES': user}, follow_redirects=True)
254         testtext= 'class="btn btn-primary" name="vote\" value=\"yes\" id=\"vote-yes\">yes</button>'
255         self.assertIn(str.encode(testtext), result.data)
256         testtext= 'class=\"btn btn-success\" name=\"vote\" value=\"no\" id=\"vote-no\">no</button>'
257         self.assertIn(str.encode(testtext), result.data)
258         testtext= 'class=\"btn btn-primary\" name=\"vote\" value=\"abstain\" id=\"vote-abstain\">abstain</button>'
259         self.assertIn(str.encode(testtext), result.data)
260
261     def test_vote_abstain(self):
262         motion='g1.20200402.004'
263         response = self.createVote(user, motion, 'abstain', userid)
264         self.assertEqual(response.status_code, 302)
265         result = self.app.get('/', environ_base={'USER_ROLES': user})
266         resulttext=self.buildResultText('A fourth motion', 0, 0, 1)
267         self.assertIn(str.encode(resulttext), result.data)
268         result = self.app.get('/motion/' + motion, environ_base={'USER_ROLES': user}, follow_redirects=True)
269         testtext= 'class=\"btn btn-primary\" name=\"vote\" value=\"yes\" id=\"vote-yes\">yes</button>'
270         self.assertIn(str.encode(testtext), result.data)
271         testtext= 'class=\"btn btn-primary\" name=\"vote\" value=\"no\" id=\"vote-no\">no</button>'
272         self.assertIn(str.encode(testtext), result.data)
273         testtext= 'class=\"btn btn-success\" name=\"vote\" value=\"abstain\" id=\"vote-abstain\">abstain</button>'
274         self.assertIn(str.encode(testtext), result.data)
275
276     def test_vote_change(self):
277         motion='g1.20200402.004'
278         response = self.createVote(user, motion, 'yes', userid)
279         self.assertEqual(response.status_code, 302)
280         result = self.app.get('/', environ_base={'USER_ROLES': user})
281         resulttext=self.buildResultText('A fourth motion', 1, 0, 0)
282         self.assertIn(str.encode(resulttext), result.data)
283         response = self.createVote(user, motion, 'no', userid)
284         self.assertEqual(response.status_code, 302)
285         result = self.app.get('/', environ_base={'USER_ROLES': user})
286         resulttext=self.buildResultText('A fourth motion', 0, 1, 0)
287         self.assertIn(str.encode(resulttext), result.data)
288         response = self.createVote(user, motion, 'abstain', userid)
289         self.assertEqual(response.status_code, 302)
290         result = self.app.get('/', environ_base={'USER_ROLES': user})
291         resulttext=self.buildResultText('A fourth motion', 0, 0, 1)
292         self.assertIn(str.encode(resulttext), result.data)
293
294     def test_vote_group(self):
295         motion='g1.20200402.004'
296         response = self.createVote(user, motion, 'yes', userid)
297         self.assertEqual(response.status_code, 302)
298
299         motion='g1.20200402.004'
300         user1='testuser/vote:group1'
301         response = self.createVote(user1, motion, 'yes', userid)
302         self.assertEqual(response.status_code, 302)
303
304         motion='g1.20200402.004'
305         user1='testuser/vote:group1 vote:group2'
306         response = self.createVote(user1, motion, 'yes', userid)
307         self.assertEqual(response.status_code, 302)
308
309     def test_vote_wrong_group(self):
310         motion='g1.20200402.004'
311         user1='testuser/vote:group2'
312         response = self.createVote(user1, motion, 'yes', userid)
313         self.assertEqual(response.status_code, 403)
314         self.assertIn(str.encode('Forbidden'), response.data)
315
316     def test_vote_closed(self):
317         motion='g1.20200402.002'
318         response = self.createVote(user, motion, 'abstain', userid)
319         self.assertEqual(response.status_code, 403)
320         self.assertIn(str.encode('Error, out of time'), response.data)
321
322     def test_vote_canceled(self):
323         motion='g1.20200402.003'
324         response = self.createVote(user, motion, 'abstain', userid)
325         self.assertEqual(response.status_code, 403)
326         self.assertIn(str.encode('Error, motion was canceled'), response.data)
327
328     def test_vote_not_given(self):
329         motion='g1.30190402.001'
330         response = self.createVote(user, motion, 'abstain', userid)
331         self.assertEqual(response.status_code, 404)
332         self.assertIn(str.encode('Error, Not found'), response.data)
333
334     def test_cancelMotion(self):
335         motion='g1.20200402.004'
336         reason="none"
337         response = self.cancelMotion(user, motion, reason)
338         self.assertEqual(response.status_code, 403)
339         self.assertIn(str.encode('Forbidden'), response.data)
340
341     def test_finishMotion(self):
342         motion='g1.20200402.004'
343         response = self.finishMotion(user, motion)
344         self.assertEqual(response.status_code, 403)
345         self.assertIn(str.encode('Forbidden'), response.data)
346
347     def test_see_old_vote(self):
348         motion='g1.20200402.002'
349         result = self.app.get('/motion/' + motion, environ_base={'USER_ROLES': user}, follow_redirects=True)
350         testtext= '<div>Proposed: 2020-04-02 21:41:26 (UTC) by User A</div>\n      <div>Votes until: 2020-04-04 21:41:26 (UTC)</div></div>'\
351             + '\n  </div>\n  <div class="card-body">\n    <p><p>A second motion</p></p>\n  </div>\n</div>'\
352             + '\n<a href="/?start=2#motion-2" class="btn btn-primary">Back</a>'
353         self.assertIn(str.encode(testtext), result.data)
354
355     def test_createMotion(self):
356         title='My Motion'
357         content='My body'
358         response = self.createMotion(user, title, content, '3', 'group1')
359         self.assertEqual(response.status_code, 403)
360         self.assertIn(str.encode('Forbidden'), response.data)
361
362
363 class CreateMotionTests(BasicTest):
364
365     def setUp(self):
366         self.init_test()
367         global user
368         user='testuser/vote:* create:* cancel:* finish:*'
369         self.db_clear()
370
371     def tearDown(self):
372         pass
373
374     def test_main_page(self):
375         response = self.app.get('/', environ_base={'USER_ROLES': user}, follow_redirects=True)
376         self.assertEqual(response.status_code, 200)
377
378     def test_home_data(self):
379         result = self.app.get('/', environ_base={'USER_ROLES': user})
380
381         # assert the response data
382         self.assertIn(b'User: testuser', result.data)
383         self.assertIn("<select class=\"float form-control\" name=\"category\">", str(result.data) )
384
385     def test_createMotion(self):
386         title='My Motion'
387         content='My body'
388         response = self.createMotion(user, title, content, '3', 'group1')
389         self.assertEqual(response.status_code, 302)
390         result = self.app.get('/', environ_base={'USER_ROLES': user})
391         self.assertIn(str.encode(title), result.data)
392         self.assertIn(str.encode(content), result.data)
393         self.assertIn(str.encode('g1.'+datetime.today().strftime('%Y%m%d')+'.001'), result.data)
394         testtext='<a class=\"btn btn-primary" href=\"/motion/g1.'+datetime.today().strftime('%Y%m%d')+'.001\" role=\"button\">Vote</a>'
395         self.assertIn(str.encode(testtext), result.data)
396
397         title='My Motion1'
398         content='My body1'
399         response = self.createMotion(user, title, content, '3', 'group1')
400         self.assertEqual(response.status_code, 302)
401         result = self.app.get('/', environ_base={'USER_ROLES': user})
402         self.assertIn(str.encode(title), result.data)
403         self.assertIn(str.encode(content), result.data)
404         self.assertIn(str.encode('g1.'+datetime.today().strftime('%Y%m%d')+'.002'), result.data)
405
406         title='My Motion2'
407         content='My body2'
408         response = self.createMotion(user, title, content, '3', 'group2')
409         self.assertEqual(response.status_code, 302)
410         result = self.app.get('/', environ_base={'USER_ROLES': user})
411         self.assertIn(str.encode(title), result.data)
412         self.assertIn(str.encode(content), result.data)
413         self.assertIn(str.encode('g2.'+datetime.today().strftime('%Y%m%d')+'.001'), result.data)
414
415         title='My Motion3'
416         content='My body3'
417         user1='testuser/vote:* create:group1 cancel:*'
418         response = self.createMotion(user1, title, content, '3', 'group1')
419         self.assertEqual(response.status_code, 302)
420
421         title='My Motion4'
422         content='My body4'
423         user1='testuser/vote:* create:group1 create:group2 cancel:*'
424         response = self.createMotion(user1, title, content, '3', 'group1')
425         self.assertEqual(response.status_code, 302)
426
427
428     def test_createMotionMarkdown(self):
429         title='Markdown Test'
430         content= 'MyMotionBody MD [text](https//domain.tld/link)'
431         response = self.createMotion(user, title, content, '3', 'group1')
432         self.assertEqual(response.status_code, 302)
433         result = self.app.get('/', environ_base={'USER_ROLES': user})
434         self.assertIn(str.encode(title), result.data)
435         self.assertIn(b'MyMotionBody MD <a href=\"https//domain.tld/link\">text</a>', result.data)
436
437     def test_createMotionMarkdownDirectLink(self):
438         title='Markdown Test Link'
439         content='MyMotionBody MD <a href=\"https//domain.tld/link\">direct</a'
440         response = self.createMotion(user, title, content, '3', 'group1')
441         self.assertEqual(response.status_code, 302)
442         result = self.app.get('/', environ_base={'USER_ROLES': user})
443         self.assertIn(str.encode(title), result.data)
444         self.assertIn(b'MyMotionBody MD &lt;a href="https//domain.tld/link"&gt;direct&lt;/a', result.data)
445
446     def test_createMotionMarkdownCombined(self):
447         title='Markdown Test Link'
448         content='Body [combined](https//domain.tld/link) <a href=\"https//domain.tld/link\">combined1</a'
449         response = self.createMotion(user, title, content, '3', 'group1')
450         self.assertEqual(response.status_code, 302)
451         result = self.app.get('/', environ_base={'USER_ROLES': user})
452         self.assertIn(str.encode(title), result.data)
453         self.assertIn(b'Body <a href=\"https//domain.tld/link\">combined</a> &lt;a href="https//domain.tld/link"&gt;combined1&lt;/a', result.data)
454
455     def test_createMotionWrongDayLength(self):
456         title='My Motion'
457         content='My body'
458         response = self.createMotion(user, title, content, '21', 'group1')
459         self.assertEqual(response.status_code, 400)
460         self.assertIn(str.encode('Error, invalid length'), response.data)
461
462     def test_createMotionMissingData(self):
463         title=''
464         content=''
465         response = self.createMotion(user, title, content, '3', 'group1')
466         self.assertEqual(response.status_code, 400)
467         self.assertIn(str.encode('Error, missing title'), response.data)
468         title='New Motion'
469         response = self.createMotion(user, title, content, '3', 'group1')
470         self.assertEqual(response.status_code, 400)
471         self.assertIn(str.encode('Error, missing content'), response.data)
472         title=''
473         content='New Content'
474         response = self.createMotion(user, title, content, '3', 'group1')
475         self.assertEqual(response.status_code, 400)
476         self.assertIn(str.encode('Error, missing title'), response.data)
477
478     def test_createMotionWrongGroup(self):
479         title='My Motion'
480         content='My body'
481         response = self.createMotion(user, title, content, '3', 'test1')
482         self.assertEqual(response.status_code, 403)
483         self.assertIn(str.encode('Forbidden'), response.data)
484
485         user1='testuser/vote:* create:group1 cancel:*'
486         response = self.createMotion(user1, title, content, '3', 'group2')
487         self.assertEqual(response.status_code, 403)
488         self.assertIn(str.encode('Forbidden'), response.data)
489
490     def test_cancelMotion(self):
491         self.db_sampledata()
492
493         motion='g1.20200402.004'
494         reason="none"
495         response = self.cancelMotion(user, motion, reason)
496         self.assertEqual(response.status_code, 500)
497         self.assertIn(str.encode('Error, form requires reason'), response.data)
498
499         reason='cancel-test'
500         response = self.cancelMotion(user, motion, reason)
501         self.assertEqual(response.status_code, 302)
502         result = self.app.get('/', environ_base={'USER_ROLES': user})
503         self.assertIn(b'Cancelation reason: ' + str.encode(reason), result.data)
504
505         motion='g1.20190402.001'
506         reason="none"
507         response = self.cancelMotion(user, motion, reason)
508         self.assertEqual(response.status_code, 404)
509         self.assertIn(str.encode('Error, Not found'), response.data)
510
511         motion='g1.30200402.001'
512         reason="cancel-test"
513         response = self.cancelMotion(user, motion, reason)
514         self.assertEqual(response.status_code, 404)
515         self.assertIn(str.encode('Error, Not found'), response.data)
516
517         motion='g1.20200402.004'
518         response = self.cancelMotion(user, motion, reason)
519         self.assertEqual(response.status_code, 403)
520         self.assertIn(str.encode('Error, motion was canceled'), response.data)
521
522     def test_finishMotion(self):
523         self.db_sampledata()
524
525         motion='g1.20200402.004'
526         response = self.finishMotion(user, motion)
527         self.assertEqual(response.status_code, 302)
528         result = self.app.get('/', environ_base={'USER_ROLES': user})
529         self.assertIn(b'Motion D</span> (Finished)', result.data)
530
531         motion='g1.30190402.001'
532         response = self.finishMotion(user, motion)
533         self.assertEqual(response.status_code, 404)
534         self.assertIn(str.encode('Error, Not found'), response.data)
535         
536         motion='g1.20200402.001'
537         response = self.finishMotion(user, motion)
538         self.assertEqual(response.status_code, 403)
539         self.assertIn(str.encode('Error, out of time'), response.data)
540
541 class AuditMotionTests(BasicTest):
542
543     def setUp(self):
544         self.init_test()
545         global user
546         user='testuser/audit:*'
547         self.db_sampledata()
548
549     def tearDown(self):
550         pass
551
552     def test_see_old_vote(self):
553         motion='g1.20200402.002'
554         result = self.app.get('/motion/' + motion, environ_base={'USER_ROLES': user}, follow_redirects=True)
555         testtext= '<div class="motion card" id="votes">\n  <div class="card-heading text-white bg-info">\n    Motion Votes\n  </div>'\
556             + '\n  <div class="card-body">\n    <div>User A: yes</div>\n    <div>User B: no</div>'\
557             + '\n    <div>User C: no</div>\n  </div>\n</div>\n<a href="/?start=2#motion-2" class="btn btn-primary">Back</a>'
558         self.assertIn(str.encode(testtext), result.data)
559
560 class ProxyManagementTests(BasicTest):
561
562     def setUp(self):
563         self.init_test()
564         global user
565         user='testuser/proxyadmin:*'
566         self.db_sampledata()
567
568     def tearDown(self):
569         pass
570
571     def test_see_proxy(self):
572         result = self.app.get('proxy', environ_base={'USER_ROLES': user}, follow_redirects=True)
573         testtext= 'div class="container">\n<form action="/proxy/add" method="POST">'
574         self.assertIn(str.encode(testtext), result.data)
575         testtext= 'proxy granted to:'
576         self.assertNotIn(str.encode(testtext), result.data)
577         testtext= 'holds proxy of:'
578         self.assertNotIn(str.encode(testtext), result.data)
579         testtext= '<select class="float form-control" name="voter">\n        '\
580             + '<option>User A</option>\n        <option>User B</option>\n        '\
581             + '<option>User C</option>\n        '\
582             + '<option>testuser</option>\n      '\
583             + '</select>\n'
584         self.assertIn(str.encode(testtext), result.data)
585         testtext= '<select class="float form-control" name="proxy">\n          '\
586             + '<option>User A</option>\n          '\
587             + '<option>User B</option>\n          '\
588             + '<option>User C</option>\n          '\
589             + '<option>testuser</option>\n      '\
590             + '</select>\n'
591         self.assertIn(str.encode(testtext), result.data)
592         testtext= '<table>\n      '\
593             + '<thead>\n        '\
594             + '<th>Voter</th>\n        <th>Proxy</th>\n        <th></th>\n      </thead>\n    '\
595             + '</table>\n'
596         self.assertIn(str.encode(testtext), result.data)
597         testtext= '<a class="nav-link" href="/proxy">Proxy management</a>'
598         self.assertIn(str.encode(testtext), result.data)
599
600     def test_add_proxy(self):
601         voter=''
602         proxy=''
603         response = self.addProxy(user, voter, proxy)
604         self.assertEqual(response.status_code, 400)
605         self.assertIn(str.encode('Error, voter equals proxy.'), response.data)
606
607         voter='User A'
608         response = self.addProxy(user, voter, proxy)
609         self.assertEqual(response.status_code, 400)
610         self.assertIn(str.encode('Error, proxy not found.'), response.data)
611
612         voter='User Z'
613         response = self.addProxy(user, voter, proxy)
614         self.assertEqual(response.status_code, 400)
615         self.assertIn(str.encode('Error, voter not found.'), response.data)
616
617         voter=''
618         proxy='User B'
619         response = self.addProxy(user, voter, proxy)
620         self.assertEqual(response.status_code, 400)
621         self.assertIn(str.encode('Error, voter not found.'), response.data)
622
623         voter='User B'
624         proxy='User B'
625         response = self.addProxy(user, voter, proxy)
626         self.assertEqual(response.status_code, 400)
627         self.assertIn(str.encode('Error, voter equals proxy.'), response.data)
628
629         voter='User A'
630         proxy='User B'
631         response = self.addProxy(user, voter, proxy)
632         self.assertEqual(response.status_code, 302)
633         result = self.app.get('proxy', environ_base={'USER_ROLES': user}, follow_redirects=True)
634         testtext= '<form action="/proxy/revoke" method="POST">'
635         self.assertIn(str.encode(testtext), result.data)
636         testtext= '<table>\n      '\
637             + '<thead>\n        '\
638             + '<th>Voter</th>\n        '\
639             + '<th>Proxy</th>\n        <th></th>\n      </thead>\n      '\
640             + '<tr>\n        <td>User A</td>\n        <td>User B</td>\n        '\
641             + '<td><button type="submit" class="btn btn-danger" name="id" value="1">Revoke</button></td>\n      '\
642             + '</tr>\n    </table>\n'
643         self.assertIn(str.encode(testtext), result.data)
644
645         response = self.addProxy(user, voter, proxy)
646         self.assertEqual(response.status_code, 400)
647         self.assertIn(str.encode('Error, proxy allready given.'), response.data)
648
649         voter='User A'
650         proxy='User C'
651         response = self.addProxy(user, voter, proxy)
652         self.assertEqual(response.status_code, 400)
653         self.assertIn(str.encode('Error, proxy allready given.'), response.data)
654
655         voter='User C'
656         proxy='User B'
657         response = self.addProxy(user, voter, proxy)
658         self.assertEqual(response.status_code, 302)
659         result = self.app.get('proxy', environ_base={'USER_ROLES': user}, follow_redirects=True)
660         testtext= '<table>\n      '\
661             + '<thead>\n        '\
662             + '<th>Voter</th>\n        '\
663             + '<th>Proxy</th>\n        <th></th>\n      </thead>\n      '\
664             + '<tr>\n        <td>User A</td>\n        <td>User B</td>\n        '\
665             + '<td><button type="submit" class="btn btn-danger" name="id" value="1">Revoke</button></td>\n      </tr>\n      '\
666             + '<tr>\n        <td>User C</td>\n        <td>User B</td>\n        '\
667             + '<td><button type="submit" class="btn btn-danger" name="id" value="2">Revoke</button></td>\n      '\
668             + '</tr>\n    </table>\n'
669         self.assertIn(str.encode(testtext), result.data)
670         testtext= 'proxy granted to:'
671         self.assertNotIn(str.encode(testtext), result.data)
672         testtext= 'holds proxy of:'
673         self.assertNotIn(str.encode(testtext), result.data)
674
675         voter='testuser'
676         proxy='User B'
677         response = self.addProxy(user, voter, proxy)
678         self.assertEqual(response.status_code, 400)
679         self.assertIn(str.encode('Error, Max proxy for \'User B\' reached.'), response.data)
680         
681         voter='testuser'
682         proxy='User A'
683         response = self.addProxy(user, voter, proxy)
684         self.assertEqual(response.status_code, 302)
685         result = self.app.get('proxy', environ_base={'USER_ROLES': user}, follow_redirects=True)
686         testtext= '<table>\n      '\
687             + '<thead>\n        '\
688             + '<th>Voter</th>\n        <th>Proxy</th>\n        <th></th>\n      </thead>\n      '\
689             + '<tr>\n        <td>testuser</td>\n        <td>User A</td>\n        '\
690             + '<td><button type="submit" class="btn btn-danger" name="id" value="3">Revoke</button></td>\n      </tr>\n      '\
691             + '<tr>\n        <td>User A</td>\n        <td>User B</td>\n        '\
692             + '<td><button type="submit" class="btn btn-danger" name="id" value="1">Revoke</button></td>\n      </tr>\n      '\
693             + '<tr>\n        <td>User C</td>\n        <td>User B</td>\n        '\
694             + '<td><button type="submit" class="btn btn-danger" name="id" value="2">Revoke</button></td>\n      '\
695             + '</tr>\n    </table>\n'
696         self.assertIn(str.encode(testtext), result.data)
697         testtext= 'proxy granted to: User A\n'
698         self.assertIn(str.encode(testtext), result.data)
699         testtext= 'holds proxy of:'
700         self.assertNotIn(str.encode(testtext), result.data)
701
702         voter='User B'
703         proxy='testuser'
704         response = self.addProxy(user, voter, proxy)
705         self.assertEqual(response.status_code, 302)
706         result = self.app.get('proxy', environ_base={'USER_ROLES': user}, follow_redirects=True)
707         testtext= '<table>\n      '\
708             + '<thead>\n        '\
709             + '<th>Voter</th>\n        <th>Proxy</th>\n        <th></th>\n      </thead>\n      '\
710             + '<tr>\n        <td>testuser</td>\n        <td>User A</td>\n        '\
711             + '<td><button type="submit" class="btn btn-danger" name="id" value="3">Revoke</button></td>\n      </tr>\n      '\
712             + '<tr>\n        <td>User A</td>\n        <td>User B</td>\n        '\
713             + '<td><button type="submit" class="btn btn-danger" name="id" value="1">Revoke</button></td>\n      </tr>\n      '\
714             + '<tr>\n        <td>User B</td>\n        <td>testuser</td>\n        '\
715             + '<td><button type="submit" class="btn btn-danger" name="id" value="4">Revoke</button></td>\n      </tr>\n      '\
716             + '<tr>\n        <td>User C</td>\n        <td>User B</td>\n        '\
717             + '<td><button type="submit" class="btn btn-danger" name="id" value="2">Revoke</button></td>\n      '\
718             + '</tr>\n    </table>\n'
719         self.assertIn(str.encode(testtext), result.data)
720         testtext= 'proxy granted to: User A\n'
721         self.assertIn(str.encode(testtext), result.data)
722         testtext= 'holds proxy of: User B\n'
723         self.assertIn(str.encode(testtext), result.data)
724
725         response = self.revokeProxy(user, userid)
726         self.assertEqual(response.status_code, 302)
727         result = self.app.get('proxy', environ_base={'USER_ROLES': user}, follow_redirects=True)
728         testtext= '<table>\n      '\
729             + '<thead>\n        '\
730             + '<th>Voter</th>\n        <th>Proxy</th>\n        <th></th>\n      </thead>\n      '\
731             + '<tr>\n        <td>testuser</td>\n        <td>User A</td>\n        '\
732             + '<td><button type="submit" class="btn btn-danger" name="id" value="3">Revoke</button></td>\n      </tr>\n      '\
733             + '<tr>\n        <td>User A</td>\n        <td>User B</td>\n        '\
734             + '<td><button type="submit" class="btn btn-danger" name="id" value="1">Revoke</button></td>\n      </tr>\n      '\
735             + '<tr>\n        <td>User C</td>\n        <td>User B</td>\n        '\
736             + '<td><button type="submit" class="btn btn-danger" name="id" value="2">Revoke</button></td>\n      '\
737             + '</tr>\n    </table>\n'
738         self.assertIn(str.encode(testtext), result.data)
739         testtext= 'proxy granted to: User A\n'
740         self.assertIn(str.encode(testtext), result.data)
741         testtext= 'holds proxy of:'
742         self.assertNotIn(str.encode(testtext), result.data)
743
744         response = self.revokeProxy(user, 3)
745         self.assertEqual(response.status_code, 302)
746         result = self.app.get('proxy', environ_base={'USER_ROLES': user}, follow_redirects=True)
747         testtext= '<table>\n      '\
748             + '<thead>\n        '\
749             + '<th>Voter</th>\n        <th>Proxy</th>\n        <th></th>\n      </thead>\n      '\
750             + '<tr>\n        <td>User A</td>\n        <td>User B</td>\n        '\
751             + '<td><button type="submit" class="btn btn-danger" name="id" value="1">Revoke</button></td>\n      </tr>\n      '\
752             + '<tr>\n        <td>User C</td>\n        <td>User B</td>\n        '\
753             + '<td><button type="submit" class="btn btn-danger" name="id" value="2">Revoke</button></td>\n      '\
754             + '</tr>\n    </table>\n'
755         self.assertIn(str.encode(testtext), result.data)
756         testtext= 'proxy granted to:'
757         self.assertNotIn(str.encode(testtext), result.data)
758         testtext= 'holds proxy of:'
759         self.assertNotIn(str.encode(testtext), result.data)
760
761         result = self.app.post('proxy/revokeall', environ_base={'USER_ROLES': user}, follow_redirects=True)
762         self.assertEqual(response.status_code, 302)
763         result = self.app.get('proxy', environ_base={'USER_ROLES': user}, follow_redirects=True)
764         testtext= '<table>\n      '\
765             + '<thead>\n        '\
766             + '<th>Voter</th>\n        <th>Proxy</th>\n        <th></th>\n      </thead>\n    '\
767             + '</table>\n'
768         self.assertIn(str.encode(testtext), result.data)
769
770 class ProxyVoteTests(BasicTest):
771
772     def setUp(self):
773         self.init_test()
774         global user
775         user='testuser/vote:* proxyadmin:*'
776         self.db_sampledata()
777
778     def tearDown(self):
779         pass
780
781     def test_proxy_vote(self):
782         voter='testuser'
783         proxy='User B'
784         proxyid=2
785         proxyuser='User B/vote:*'
786
787         response = self.addProxy(user, proxy, voter)
788         self.assertEqual(response.status_code, 302)
789
790         motion='g1.20200402.004'
791         response = self.createVote(user, motion, 'yes', proxyid)
792         self.assertEqual(response.status_code, 302)
793
794         # testuser view
795         result = self.app.get('/motion/' + motion, environ_base={'USER_ROLES': user}, follow_redirects=True)
796         # own vote without change
797         testtext= '<form action="/motion/g1.20200402.004/vote/4" method="POST">\n'\
798             + '<button type="submit" class="btn btn-primary" name="vote" value="yes" id="vote-yes">yes</button>\n'\
799             + '<button type="submit" class="btn btn-primary" name="vote" value="no" id="vote-no">no</button>\n'\
800             + '<button type="submit" class="btn btn-primary" name="vote" value="abstain" id="vote-abstain">abstain</button>\n</form>'
801         self.assertIn(str.encode(testtext), result.data)
802         # proxy vote with change
803         testtext= '<form action="/motion/g1.20200402.004/vote/2" method="POST">\n'\
804             + '<button type="submit" class="btn btn-success" name="vote" value="yes" id="vote-yes">yes</button>\n'\
805             + '<button type="submit" class="btn btn-primary" name="vote" value="no" id="vote-no">no</button>\n'\
806             + '<button type="submit" class="btn btn-primary" name="vote" value="abstain" id="vote-abstain">abstain</button>\n</form>\n'
807         self.assertIn(str.encode(testtext), result.data)
808         
809         # User B view
810         result = self.app.get('/motion/' + motion, environ_base={'USER_ROLES': proxyuser}, follow_redirects=True)
811         # own vote without change
812         testtext= '<h3>My vote</h3>\nGiven by testuser\n'\
813             + '<form action="/motion/g1.20200402.004/vote/2" method="POST">\n'\
814             + '<button type="submit" class="btn btn-success" name="vote" value="yes" id="vote-yes">yes</button>\n'\
815             + '<button type="submit" class="btn btn-primary" name="vote" value="no" id="vote-no">no</button>\n'\
816             + '<button type="submit" class="btn btn-primary" name="vote" value="abstain" id="vote-abstain">abstain</button>\n</form>'
817         self.assertIn(str.encode(testtext), result.data)
818         
819         # change vote
820         response = self.createVote(user, motion, 'no', proxyid)
821         self.assertEqual(response.status_code, 302)
822
823         result = self.app.get('/motion/' + motion, environ_base={'USER_ROLES': user}, follow_redirects=True)
824         testtext= '<form action="/motion/g1.20200402.004/vote/2" method="POST">\n'\
825             + '<button type="submit" class="btn btn-primary" name="vote" value="yes" id="vote-yes">yes</button>\n'\
826             + '<button type="submit" class="btn btn-success" name="vote" value="no" id="vote-no">no</button>\n'\
827             + '<button type="submit" class="btn btn-primary" name="vote" value="abstain" id="vote-abstain">abstain</button>\n</form>\n'
828         self.assertIn(str.encode(testtext), result.data)
829
830     def test_proxy_vote_no_proxy(self):
831         voter='testuser'
832         proxy='User B'
833         # wrong proxy id
834         proxyid=3
835
836         response = self.addProxy(user, proxy, voter)
837         self.assertEqual(response.status_code, 302)
838
839         motion='g1.20200402.004'
840         response = self.createVote(user, motion, 'yes', proxyid)
841         self.assertEqual(response.status_code, 400)
842         self.assertIn(str.encode('Error, proxy not found'), response.data)
843         
844         # non existing id
845         proxyid=10000
846
847         motion='g1.20200402.004'
848         response = self.createVote(user, motion, 'yes', proxyid)
849         self.assertEqual(response.status_code, 400)
850         self.assertIn(str.encode('Error, proxy not found'), response.data)
851
852     def test_proxy_vote_no_voter(self):
853         voter='User A'
854         proxy='User B'
855         proxyid=2
856
857         response = self.addProxy(user, proxy, voter)
858         self.assertEqual(response.status_code, 302)
859
860         user1='testuser1/'
861         motion='g1.20200402.004'
862         response = self.createVote(user1, motion, 'yes', proxyid)
863         self.assertEqual(response.status_code, 403)
864         self.assertIn(str.encode('Forbidden'), response.data)
865
866
867
868 if __name__ == "__main__":
869     unittest.main()