From c49313a222e706e4197e0af3aa2b85e0e0bad096 Mon Sep 17 00:00:00 2001 From: Aurélien Bompard Date: Jul 31 2018 12:30:23 +0000 Subject: [PATCH 1/24] Remove bogus fedmsg call Fixes #58 --- diff --git a/fedora_elections/admin.py b/fedora_elections/admin.py index 292e30a..0f3dc21 100644 --- a/fedora_elections/admin.py +++ b/fedora_elections/admin.py @@ -115,7 +115,6 @@ def admin_new_election(): ) flask.flash('Election "%s" added' % election.alias) - fedmsgshim.publish(topic="election.new", msg=election) return flask.redirect(flask.url_for( 'admin_view_election', election_alias=election.alias)) return flask.render_template( From ab67689bd801443311dc240c719b99ec5e781e68 Mon Sep 17 00:00:00 2001 From: Ben Cotton Date: Oct 12 2018 18:10:25 +0000 Subject: [PATCH 2/24] Add support for requesting an 'I Voted' badge. Badges will be created to be requestable by URL. This URL will be provided by the admin. If set, the link will be given to the user after the ballot is submitted (or re-submitted). --- diff --git a/fedora_elections/admin.py b/fedora_elections/admin.py index 292e30a..9ff584a 100644 --- a/fedora_elections/admin.py +++ b/fedora_elections/admin.py @@ -74,6 +74,7 @@ def admin_new_election(): end_date=form.end_date.data, seats_elected=form.seats_elected.data, embargoed=int(form.embargoed.data), + voted_badge=form.voted_badge.data, voting_type=form.voting_type.data, max_votes=form.max_votes.data, candidates_are_fasusers=int(form.candidates_are_fasusers.data), diff --git a/fedora_elections/elections.py b/fedora_elections/elections.py index ac80ef7..817e3f1 100644 --- a/fedora_elections/elections.py +++ b/fedora_elections/elections.py @@ -168,7 +168,7 @@ def vote_range(election, revote): and candidate.short_name not in ['csrf_token', 'action'] ] process_vote(candidates, election, votes, revote) - flask.flash("Your vote has been recorded. Thank you!") + say_thank_you(election) return safe_redirect_back() if form.action.data == 'preview': @@ -224,7 +224,7 @@ def vote_select(election, revote): and candidate.short_name not in ['csrf_token', 'action'] ] process_vote(candidates, election, votes, revote, cand_name) - flask.flash("Your vote has been recorded. Thank you!") + say_thank_you(election) return safe_redirect_back() if form.action.data == 'preview': @@ -264,7 +264,7 @@ def vote_simple(election, revote): and candidate.short_name not in ['csrf_token', 'action'] ] process_vote(candidates, election, votes, revote, value=1) - flask.flash("Your vote has been recorded. Thank you!") + say_thank_you(election) return safe_redirect_back() if form.action.data == 'preview': @@ -302,7 +302,7 @@ def vote_irc(election, revote): and candidate.short_name not in ['csrf_token', 'action'] ] process_vote(candidates, election, votes, revote, cand_name) - flask.flash("Your vote has been recorded. Thank you!") + say_thank_you(election) return safe_redirect_back() if form.action.data == 'preview': @@ -345,3 +345,11 @@ def process_vote( ) SESSION.add(new_vote) SESSION.commit() + + +def say_thank_you(election): + thank_you = "Your vote has been recorded. Thank you!" + if election.voted_badge: + thank_you = thank_you + '
Claim your I Voted badge.' + flask.flash(thank_you) diff --git a/fedora_elections/forms.py b/fedora_elections/forms.py index f218965..61139d5 100644 --- a/fedora_elections/forms.py +++ b/fedora_elections/forms.py @@ -74,6 +74,12 @@ class ElectionForm(FlaskForm): embargoed = wtforms.BooleanField('Embargo results?', default=True) + voted_badge = wtforms.TextField( + 'Badge URL (optional)', [ + wtforms.validators.Optional(), + wtforms.validators.URL(), + wtforms.validators.Length(max=250)]) + lgl_voters = wtforms.TextField( 'Legal voters groups', [wtforms.validators.optional()]) diff --git a/fedora_elections/models.py b/fedora_elections/models.py index 1772d4d..b91a252 100644 --- a/fedora_elections/models.py +++ b/fedora_elections/models.py @@ -86,6 +86,7 @@ class Election(BASE): seats_elected = sa.Column(sa.Integer, nullable=False, default=1) embargoed = sa.Column(sa.Integer, nullable=False, default=0) voting_type = sa.Column(sa.Unicode(100), nullable=False, default=u'range') + voted_badge = sa.Column(sa.Unicode(250), nullable=False) max_votes = sa.Column(sa.Integer, nullable=True) candidates_are_fasusers = sa.Column( sa.Integer, nullable=False, default=0) diff --git a/fedora_elections/templates/_formhelpers.html b/fedora_elections/templates/_formhelpers.html index ba48487..f90bb94 100644 --- a/fedora_elections/templates/_formhelpers.html +++ b/fedora_elections/templates/_formhelpers.html @@ -112,6 +112,7 @@ {{ render_bootstrap_textfield_in_row(form.seats_elected) }} {{ render_bootstrap_checkbox_in_row(form.candidates_are_fasusers) }} {{ render_bootstrap_checkbox_in_row(form.embargoed) }} + {{ render_bootstrap_textfield_in_row(form.voted_badge) }} {{ render_bootstrap_textfield_in_row(form.lgl_voters, after="FAS groups allowed to vote on this election (CLA-done is always required)") }} {{ render_bootstrap_textfield_in_row(form.admin_grp, after="FAS groups allowed to view the result despite the embargo") }} From d4e667256a422042cddbdcfbbc88f7b7a2cbc665 Mon Sep 17 00:00:00 2001 From: cclauss Date: Nov 12 2018 11:38:00 +0000 Subject: [PATCH 3/24] Modernize Python 2 code to get ready for Python 3 Old style exceptions --> new style in Python 3 Python 3 treats old style exceptions as syntax errors but new style exceptions work as expected in both Python 2 and Python 3. Use __print()__ function in both Python 2 and Python 3 __print()__ is a function in Python 3. --- diff --git a/fedora_elections/admin.py b/fedora_elections/admin.py index 0f3dc21..c6b2dda 100644 --- a/fedora_elections/admin.py +++ b/fedora_elections/admin.py @@ -409,7 +409,7 @@ def admin_delete_candidate(election_alias, candidate_id): candidate=candidate.to_json(), ) ) - except SQLAlchemyError, err: + except SQLAlchemyError as err: SESSION.rollback() APP.logger.debug('Could not delete candidate') APP.logger.exception(err) diff --git a/fedora_elections/fedmsgshim.py b/fedora_elections/fedmsgshim.py index 785fc90..47daf17 100644 --- a/fedora_elections/fedmsgshim.py +++ b/fedora_elections/fedmsgshim.py @@ -13,5 +13,5 @@ def publish(*args, **kwargs): # pragma: no cover try: import fedmsg fedmsg.publish(*args, **kwargs) - except Exception, e: + except Exception as e: warnings.warn(str(e)) diff --git a/fedora_elections/forms.py b/fedora_elections/forms.py index f218965..322ed4d 100644 --- a/fedora_elections/forms.py +++ b/fedora_elections/forms.py @@ -150,7 +150,7 @@ def get_simple_voting_form(candidates, fasusers): try: title = \ FAS2.person_by_username(candidate.name)['human_name'] - except (KeyError, AuthError), err: + except (KeyError, AuthError) as err: APP.logger.debug(err) if candidate.url: title = '%s [Info]' % (title, candidate.url) diff --git a/tests/__init__.py b/tests/__init__.py index 91616df..f5db328 100644 --- a/tests/__init__.py +++ b/tests/__init__.py @@ -20,6 +20,7 @@ fedora_elections test script """ +from __future__ import print_function __requires__ = ['SQLAlchemy >= 0.7'] import pkg_resources @@ -57,7 +58,7 @@ if os.environ.get('BUILD_ID'): req = requests.get('%s/new' % FAITOUT_URL) if req.status_code == 200: DB_PATH = req.text - print 'Using faitout at: %s' % DB_PATH + print('Using faitout at: %s' % DB_PATH) except: pass From 2045aa19800e7b27de7f857b0423f56d4e1c7f8f Mon Sep 17 00:00:00 2001 From: Pierre-Yves Chibon Date: Jan 09 2019 10:55:16 +0000 Subject: [PATCH 4/24] Fix the logic around showing who won the election With more recent version of jinja2 the way we were using {% set %} was no longer working because of the limited scope of this macro. With this commit we replace the use of {% set %} in the inner scope by using a list and just checking the last element in that list. From there, we could mimic the old behavior we had with sets. For simplicity, we also moved the sorting of the candidates out of the templates and into the controller just before calling the template. Signed-off-by: Pierre-Yves Chibon --- diff --git a/fedora_elections/__init__.py b/fedora_elections/__init__.py index 88adfa2..7d51af8 100644 --- a/fedora_elections/__init__.py +++ b/fedora_elections/__init__.py @@ -272,7 +272,10 @@ def about_election(election_alias): stats=stats, voted=voted, evolution_label=evolution_label, - evolution_data=evolution_data) + evolution_data=evolution_data, + candidates=sorted( + election.candidates, key=lambda x: x.vote_count, reverse=True), + ) @APP.route('/archives') diff --git a/fedora_elections/elections.py b/fedora_elections/elections.py index ac80ef7..8516066 100644 --- a/fedora_elections/elections.py +++ b/fedora_elections/elections.py @@ -141,6 +141,8 @@ def election_results_text(election_alias): election=election, usernamemap=usernamemap, stats=stats, + candidates=sorted( + election.candidates, key=lambda x: x.vote_count, reverse=True) ) diff --git a/fedora_elections/templates/about.html b/fedora_elections/templates/about.html index 2518b27..b87fea8 100644 --- a/fedora_elections/templates/about.html +++ b/fedora_elections/templates/about.html @@ -107,41 +107,44 @@ {% endif %}

Results

- - - - - {% if stats['candidate_voters'] %} - - - {% endif %} - + + + + + {% if stats['candidate_voters'] %} + + + {% endif %} + - {% for candidate in election.candidates|sort(attribute='vote_count', reverse=True) %} - {% if loop.index <= election.seats_elected %} - {# If we are below the number of user that will be selected, get the number - of votes and the flag to False#} - {% set flag = False %} - {% set votes = candidate.vote_count %} - {% elif loop.index > election.seats_elected and votes > candidate.vote_count and not flag %} - {# if we are above the number of user that will be selected (seats - available), check if the number of votes for this candidate is lower than - the number of votes for the last candidate and if the Flag is False - So this takes care of the case where there are 10 seats elected and the 11th - candidate has the same score as the 10th one. - In this case we would end up with one more person that the number of seats - available and we'll need to either find a way to select one over the other - or deal with having one more candidate accepted #} - {% set flag = True %} - {% set lastrow = True %} - {% else %} - {# we are above the number of seats available, the number of votes is below - that of the last candidate above selected and the Flag is True which means - we already passed the condition above #} - {% set lastrow = False %} - {% endif %} - + {%- set lastrow = [0] -%} + {%- set flag = [0] -%} + {% for candidate in candidates %} + {% if loop.index <= election.seats_elected %} + {# If we are below the number of user that will be selected, get the number + of votes and the flag to False#} + {%- set _ = flag.append(0) -%} + {%- elif loop.index > election.seats_elected + and candidates[loop.index -2].vote_count > candidate.vote_count + and flag[-1] == 0 -%} + {# if we are above the number of user that will be selected (seats + available), check if the number of votes for this candidate is lower than + the number of votes for the last candidate and if the Flag is False + So this takes care of the case where there are 10 seats elected and the 11th + candidate has the same score as the 10th one. + In this case we would end up with one more person that the number of seats + available and we'll need to either find a way to select one over the other + or deal with having one more candidate accepted #} + {%- set _ = lastrow.append(1) -%} + {%- set _ = flag.append(1) -%} + {% else %} + {# we are above the number of seats available, the number of votes is below + that of the last candidate above selected and the Flag is True which means + we already passed the condition above #} + {% set _ = lastrow.append(0) -%} + {% endif %} + diff --git a/fedora_elections/templates/results_text.html b/fedora_elections/templates/results_text.html index 9c99734..14f9503 100644 --- a/fedora_elections/templates/results_text.html +++ b/fedora_elections/templates/results_text.html @@ -21,14 +21,17 @@ could accumulate up to {{ stats['n_voters'] * stats['max_vote'] }} votes ({{stat The results for the elections are as follows: # votes | name -- --------+----------------------{% -for candidate in election.candidates|sort(attribute='vote_count', reverse=True) -%} +- --------+---------------------- +{%- set lastrow = [0] -%} +{%- set flag = [0] -%} +{%- for candidate in candidates -%} {% if loop.index <= election.seats_elected -%} - {# If we are below the number of user that will be selected, + {# If we are below the number of user that will be selected get the number of votes and the flag to False -#} - {% set flag = False -%} - {% set votes = candidate.vote_count -%} - {%- elif loop.index > election.seats_elected and votes > candidate.vote_count and not flag -%} + {%- set _ = flag.append(0) -%} + {%- elif loop.index > election.seats_elected + and candidates[loop.index -2].vote_count > candidate.vote_count + and flag[-1] == 0 -%} {# if we are above the number of user that will be selected (seats available), check if the number of votes for this candidate is lower than the number of votes for the last candidate and if the Flag is @@ -38,14 +41,17 @@ for candidate in election.candidates|sort(attribute='vote_count', reverse=True) In this case we would end up with one more person that the number of seats available and we'll need to either find a way to select one over the other or deal with having one more candidate accepted -#} - {% set flag = True -%} - {% set lastrow = True -%} + {%- set _ = lastrow.append(1) -%} + {%- set _ = flag.append(1) -%} {%- else -%} {# we are above the number of seats available, the number of votes is below that of the last candidate above selected and the Flag is True which means we already passed the condition above -#} - {% set lastrow = False -%} {%- endif %}{% if lastrow == True %} -- --------+---------------------- {%- endif %} + {% set _ = lastrow.append(0) -%} + {%- endif -%} + {%- if lastrow[-1] == 1 %} +- --------+---------------------- + {%- endif %} {{ candidate.vote_count | rjust(8) }} | {% if election.candidates_are_fasusers -%} {{ usernamemap[candidate.id] }} {%- else -%} {{candidate.name}} {%- endif %} From cfa2f34a4c049a68b021bc82704416e7d517f183 Mon Sep 17 00:00:00 2001 From: Pierre-Yves Chibon Date: Jan 21 2019 11:16:39 +0000 Subject: [PATCH 5/24] Get the version by reading the file rather than importing it Signed-off-by: Pierre-Yves Chibon --- diff --git a/setup.py b/setup.py index 1821fe5..8836c03 100644 --- a/setup.py +++ b/setup.py @@ -4,13 +4,19 @@ Setup script """ -# These two lines are needed to run on EL6 -__requires__ = ['SQLAlchemy >= 0.7', 'jinja2 >= 2.4'] -import pkg_resources +import os +import re +from setuptools import setup -from fedora_elections import __version__ +versionfile = os.path.join( + os.path.dirname(__file__), 'fedora_elections', '__init__.py') -from setuptools import setup +# Thanks to SQLAlchemy: +# https://github.com/zzzeek/sqlalchemy/blob/master/setup.py#L104 +with open(versionfile) as stream: + __version__ = re.compile( + r".*__version__ = '(.*?)'", re.S + ).match(stream.read()).group(1) setup( name='fedora-elections', From 55e6afc6ffbd7aed4b6268fa1d69a9a8b0b49d80 Mon Sep 17 00:00:00 2001 From: Pierre-Yves Chibon Date: Jan 21 2019 11:17:15 +0000 Subject: [PATCH 6/24] Add a tox.ini to run the tests Signed-off-by: Pierre-Yves Chibon --- diff --git a/.gitignore b/.gitignore index a7b527f..b8bb54d 100644 --- a/.gitignore +++ b/.gitignore @@ -7,3 +7,4 @@ dist/ .coverage alembic.ini .vagrant/ +.tox/ diff --git a/tox.ini b/tox.ini new file mode 100644 index 0000000..5e141b9 --- /dev/null +++ b/tox.ini @@ -0,0 +1,15 @@ +[tox] +envlist = py27 +skipsdist = True + +[testenv] +usedevelop = True +deps = + -rrequirements.txt + nose + coverage +setenv = + PYTHONPATH={toxinidir} +commands = + nosetests --with-coverage --cover-erase --cover-package=fedora_elections {posargs} + From cb89af2c2c99e5994042a421a6d2985b9ee6813f Mon Sep 17 00:00:00 2001 From: Pierre-Yves Chibon Date: Jan 21 2019 13:09:01 +0000 Subject: [PATCH 7/24] Add a .cico.pipeline file Signed-off-by: Pierre-Yves Chibon --- diff --git a/.cico.pipeline b/.cico.pipeline new file mode 100644 index 0000000..7631029 --- /dev/null +++ b/.cico.pipeline @@ -0,0 +1 @@ +fedoraInfraTox { } From 05780afb216ba5f372a8833b0de5ce8bbdc937f3 Mon Sep 17 00:00:00 2001 From: Pierre-Yves Chibon Date: Jan 21 2019 14:08:41 +0000 Subject: [PATCH 8/24] Specify the HOME variable to tox Signed-off-by: Pierre-Yves Chibon --- diff --git a/tox.ini b/tox.ini index 5e141b9..0fb20cd 100644 --- a/tox.ini +++ b/tox.ini @@ -4,6 +4,7 @@ skipsdist = True [testenv] usedevelop = True +passenv = HOME deps = -rrequirements.txt nose From cdf58571dd24c21d8cd7c2453bc71de03e3c3760 Mon Sep 17 00:00:00 2001 From: Pierre-Yves Chibon Date: Jan 21 2019 20:34:38 +0000 Subject: [PATCH 9/24] Add diff-cover to the tox.ini Signed-off-by: Pierre-Yves Chibon --- diff --git a/tox.ini b/tox.ini index 0fb20cd..e370e22 100644 --- a/tox.ini +++ b/tox.ini @@ -1,5 +1,5 @@ [tox] -envlist = py27 +envlist = py27,diff-cover skipsdist = True [testenv] @@ -12,5 +12,13 @@ deps = setenv = PYTHONPATH={toxinidir} commands = - nosetests --with-coverage --cover-erase --cover-package=fedora_elections {posargs} + nosetests --with-coverage --cover-erase \ + --cover-package=fedora_elections --cover-xml \ + {posargs} + +[testenv:diff-cover] +deps = + diff-cover +commands = + diff-cover coverage.xml --compare-branch=origin/develop --fail-under=100 From c760941a826a18b12548608891c1c20cf2e898e4 Mon Sep 17 00:00:00 2001 From: Pierre-Yves Chibon Date: Feb 13 2019 09:38:14 +0000 Subject: [PATCH 10/24] Fix the test suite The wtforms behavior changed in newer version adding support for the html5 "required" tag in the input fields that are required. This commit adjusts the test for this change. We're also only pre-filling the ElectionForm when doing GET request as otherwise required fields may be populated while the POST request did not provide them. Finally, we add a couple of utility methods to retrieve the version of the wtforms library in use and to retrieve a CSRF token. Signed-off-by: Pierre-Yves Chibon --- diff --git a/fedora_elections/admin.py b/fedora_elections/admin.py index c6b2dda..c84417f 100644 --- a/fedora_elections/admin.py +++ b/fedora_elections/admin.py @@ -129,7 +129,11 @@ def admin_view_election(election_alias): election = models.Election.get(SESSION, alias=election_alias) if not election: flask.abort(404) - form = forms.ElectionForm(election.id, obj=election) + if flask.request.method == 'GET': + form = forms.ElectionForm(election.id, obj=election) + else: + form = forms.ElectionForm(election.id) + if form.validate_on_submit(): form.embargoed.data = int(form.embargoed.data) if form.max_votes.data: diff --git a/tests/__init__.py b/tests/__init__.py index f5db328..56ffe3c 100644 --- a/tests/__init__.py +++ b/tests/__init__.py @@ -139,6 +139,27 @@ class ModelFlasktests(Modeltests): self.app = fedora_elections.APP.test_client() + def get_wtforms_version(self): + """Returns the wtforms version as a tuple.""" + import wtforms + wtforms_v = wtforms.__version__.split('.') + for idx, val in enumerate(wtforms_v): + try: + val = int(val) + except ValueError: + pass + wtforms_v[idx] = val + return tuple(wtforms_v) + + def get_csrf(self, url='/admin/new', output=None): + """Retrieve a CSRF token from given URL.""" + if output is None: + output = self.app.get(url) + self.assertEqual(output.status_code, 200) + + return output.get_data(as_text=True).split( + 'name="csrf_token" type="hidden" value="')[1].split('">')[0] + class FakeGroup(object): """ Fake object used to make the FakeUser object closer to the diff --git a/tests/test_flask_admin.py b/tests/test_flask_admin.py index 3054e73..b7d5bc3 100644 --- a/tests/test_flask_admin.py +++ b/tests/test_flask_admin.py @@ -60,7 +60,6 @@ class FlaskAdmintests(ModelFlasktests): self.assertEqual(output.status_code, 200) self.assertTrue('Candidates 3' in output.data) - def test_admin_new_election(self): """ Test the admin_new_election function. """ self.setup_db() @@ -73,9 +72,14 @@ class FlaskAdmintests(ModelFlasktests): self.assertEqual(output.status_code, 200) self.assertTrue( '

Create new election

' in output.data) - self.assertTrue( - 'input class="form-control" id="shortdesc" name="shortdesc" type="text"' - in output.data) + if self.get_wtforms_version() >= (2, 2): + self.assertIn( + 'input class="form-control" id="shortdesc" ' + 'name="shortdesc" required type="text" ', output.data) + else: + self.assertIn( + 'input class="form-control" id="shortdesc" ' + 'name="shortdesc" type="text" ', output.data) # No csrf provided data = { @@ -95,12 +99,16 @@ class FlaskAdmintests(ModelFlasktests): self.assertEqual(output.status_code, 200) self.assertTrue( '

Create new election

' in output.data) - self.assertTrue( - 'input class="form-control" id="shortdesc" name="shortdesc" type="text"' - in output.data) + if self.get_wtforms_version() >= (2, 2): + self.assertIn( + 'input class="form-control" id="shortdesc" ' + 'name="shortdesc" required type="text" ', output.data) + else: + self.assertIn( + 'input class="form-control" id="shortdesc" ' + 'name="shortdesc" type="text" ', output.data) - csrf_token = output.data.split( - 'name="csrf_token" type="hidden" value="')[1].split('">')[0] + csrf_token = self.get_csrf(output=output) # Description missing data = { @@ -120,9 +128,14 @@ class FlaskAdmintests(ModelFlasktests): self.assertEqual(output.status_code, 200) self.assertTrue( '

Create new election

' in output.data) - self.assertTrue( - 'input class="form-control" id="shortdesc" name="shortdesc" type="text"' - in output.data) + if self.get_wtforms_version() >= (2, 2): + self.assertIn( + 'input class="form-control" id="shortdesc" ' + 'name="shortdesc" required type="text" ', output.data) + else: + self.assertIn( + 'input class="form-control" id="shortdesc" ' + 'name="shortdesc" type="text" ', output.data) self.assertTrue( '' in output.data) @@ -146,9 +159,14 @@ class FlaskAdmintests(ModelFlasktests): self.assertEqual(output.status_code, 200) self.assertTrue( '

Create new election

' in output.data) - self.assertTrue( - 'input class="form-control" id="shortdesc" name="shortdesc" type="text"' - in output.data) + if self.get_wtforms_version() >= (2, 2): + self.assertIn( + 'input class="form-control" id="shortdesc" ' + 'name="shortdesc" required type="text" ', output.data) + else: + self.assertIn( + 'input class="form-control" id="shortdesc" ' + 'name="shortdesc" type="text" ', output.data) self.assertTrue( '' in output.data) @@ -172,9 +190,14 @@ class FlaskAdmintests(ModelFlasktests): self.assertEqual(output.status_code, 200) self.assertTrue( '

Create new election

' in output.data) - self.assertTrue( - 'input class="form-control" id="shortdesc" name="shortdesc" type="text"' - in output.data) + if self.get_wtforms_version() >= (2, 2): + self.assertIn( + 'input class="form-control" id="shortdesc" ' + 'name="shortdesc" required type="text" ', output.data) + else: + self.assertIn( + 'input class="form-control" id="shortdesc" ' + 'name="shortdesc" type="text" ', output.data) self.assertTrue( 'class="form-control-feedback">End date must be later than start date.' in output.data) @@ -198,9 +221,14 @@ class FlaskAdmintests(ModelFlasktests): self.assertEqual(output.status_code, 200) self.assertTrue( '

Create new election

' in output.data) - self.assertTrue( - 'input class="form-control" id="shortdesc" name="shortdesc" type="text"' - in output.data) + if self.get_wtforms_version() >= (2, 2): + self.assertIn( + 'input class="form-control" id="shortdesc" ' + 'name="shortdesc" required type="text" ', output.data) + else: + self.assertIn( + 'input class="form-control" id="shortdesc" ' + 'name="shortdesc" type="text" ', output.data) self.assertTrue( '' in output.data) @@ -224,9 +252,14 @@ class FlaskAdmintests(ModelFlasktests): self.assertEqual(output.status_code, 200) self.assertTrue( '

Create new election

' in output.data) - self.assertTrue( - 'input class="form-control" id="shortdesc" name="shortdesc" type="text"' - in output.data) + if self.get_wtforms_version() >= (2, 2): + self.assertIn( + 'input class="form-control" id="shortdesc" ' + 'name="shortdesc" required type="text" ', output.data) + else: + self.assertIn( + 'input class="form-control" id="shortdesc" ' + 'name="shortdesc" type="text" ', output.data) self.assertTrue( '' in output.data) @@ -257,15 +290,18 @@ class FlaskAdmintests(ModelFlasktests): in output.data) self.assertTrue( 'There are no candidates.' in output.data) - self.assertTrue( - '' - in output.data) - self.assertTrue( - '' - in output.data) - self.assertTrue( - '' - in output.data) + self.assertIn( + 'input class="form-control" id="admin_grp" ' + 'name="admin_grp" type="text" ' + 'value="sysadmin-main, testers">', output.data) + self.assertIn( + 'input class="form-control" id="lgl_voters" ' + 'name="lgl_voters" type="text" ' + 'value="packager, testers">', output.data) + self.assertIn( + 'input class="form-control" id="max_votes" ' + 'name="max_votes" type="text" ' + 'value="">', output.data) # All good - max_votes is ignored as it is not a integer data = { @@ -292,15 +328,18 @@ class FlaskAdmintests(ModelFlasktests): in output.data) self.assertTrue( 'There are no candidates.' in output.data) - self.assertTrue( - '' - in output.data) - self.assertTrue( - '' - in output.data) - self.assertTrue( - '' - in output.data) + self.assertIn( + 'input class="form-control" id="admin_grp" ' + 'name="admin_grp" type="text" ' + 'value="sysadmin-main, testers">', output.data) + self.assertIn( + 'input class="form-control" id="lgl_voters" ' + 'name="lgl_voters" type="text" ' + 'value="packager, testers">', output.data) + self.assertIn( + 'input class="form-control" id="max_votes" ' + 'name="max_votes" type="text" ' + 'value="">', output.data) def test_admin_edit_election(self): """ Test the admin_edit_election function. """ @@ -318,9 +357,14 @@ class FlaskAdmintests(ModelFlasktests): self.assertEqual(output.status_code, 200) self.assertTrue( 'Election Details' in output.data) - self.assertTrue( - 'input class="form-control" id="shortdesc" name="shortdesc" type="text"' - in output.data) + if self.get_wtforms_version() >= (2, 2): + self.assertIn( + 'input class="form-control" id="shortdesc" ' + 'name="shortdesc" required type="text" ', output.data) + else: + self.assertIn( + 'input class="form-control" id="shortdesc" ' + 'name="shortdesc" type="text" ', output.data) data = { 'alias': 'test_election', @@ -339,16 +383,19 @@ class FlaskAdmintests(ModelFlasktests): self.assertEqual(output.status_code, 200) self.assertTrue( 'Election Details' in output.data) - self.assertTrue( - 'input class="form-control" id="shortdesc" name="shortdesc" type="text"' - in output.data) + if self.get_wtforms_version() >= (2, 2): + self.assertIn( + 'input class="form-control" id="shortdesc" ' + 'name="shortdesc" required type="text" ', output.data) + else: + self.assertIn( + 'input class="form-control" id="shortdesc" ' + 'name="shortdesc" type="text" ', output.data) - csrf_token = output.data.split( - 'name="csrf_token" type="hidden" value="')[1].split('">')[0] + csrf_token = self.get_csrf() data = { 'alias': 'test_election', - 'shortdesc': 'test election shortdesc', 'voting_type': 'simple', 'url': 'https://fedoraproject.org', 'start_date': TODAY - timedelta(days=10), @@ -359,22 +406,37 @@ class FlaskAdmintests(ModelFlasktests): 'csrf_token': csrf_token, } - output = self.app.post('/admin/test_election/', data=data) + output = self.app.post( + '/admin/test_election/', data=data) self.assertEqual(output.status_code, 200) self.assertTrue( 'Election Details' in output.data) - self.assertTrue( - 'input class="form-control" id="shortdesc" name="shortdesc" type="text"' - in output.data) - self.assertTrue( - '' - in output.data) + if self.get_wtforms_version() >= (2, 2): + self.assertIn( + 'input class="form-control" id="shortdesc" ' + 'name="shortdesc" required type="text" ', output.data) + else: + self.assertIn( + 'input class="form-control" id="shortdesc" ' + 'name="shortdesc" type="text" ', output.data) + self.assertIn( + '', + output.data) # Check election before edit output = self.app.get('/admin/test_election/') self.assertEqual(output.status_code, 200) self.assertTrue('Candidates 3' in output.data) - self.assertTrue('' in output.data) + if self.get_wtforms_version() >= (2, 2): + self.assertIn( + 'input class="form-control" id="seats_elected" ' + 'name="seats_elected" required type="text" ' + 'value="1">', output.data) + else: + self.assertIn( + 'input class="form-control" id="seats_elected" ' + 'name="seats_elected" type="text" ' + 'value="1">', output.data) data = { 'alias': 'test_election', @@ -398,8 +460,16 @@ class FlaskAdmintests(ModelFlasktests): 'Election "test_election" saved' in output.data) # We edited the seats_elected from 1 to 2 - self.assertTrue( - '' in output.data) + if self.get_wtforms_version() >= (2, 2): + self.assertIn( + 'input class="form-control" id="seats_elected" ' + 'name="seats_elected" required type="text" ' + 'value="2">', output.data) + else: + self.assertIn( + 'input class="form-control" id="seats_elected" ' + 'name="seats_elected" type="text" ' + 'value="2">', output.data) self.assertTrue('Candidates 3' in output.data) def test_admin_edit_election_admin_groups(self): @@ -417,16 +487,23 @@ class FlaskAdmintests(ModelFlasktests): with user_set(fedora_elections.APP, user): output = self.app.get('/admin/test_election2/') self.assertEqual(output.status_code, 200) - csrf_token = output.data.split( - 'name="csrf_token" type="hidden" value="')[1].split('">')[0] + csrf_token = self.get_csrf(output=output) # Edit Admin Group # Check election before edit output = self.app.get('/admin/test_election2/') self.assertEqual(output.status_code, 200) - self.assertTrue( - '' in output.data) + if self.get_wtforms_version() >= (2, 2): + self.assertIn( + 'input class="form-control" id="seats_elected" ' + 'name="seats_elected" required type="text" ' + 'value="1">', output.data) + else: + self.assertIn( + 'input class="form-control" id="seats_elected" ' + 'name="seats_elected" type="text" ' + 'value="1">', output.data) self.assertTrue('Candidates ' in output.data) + if self.get_wtforms_version() >= (2, 2): + self.assertIn( + 'input class="form-control" id="seats_elected" ' + 'name="seats_elected" required type="text" ' + 'value="2">', output.data) + else: + self.assertIn( + 'input class="form-control" id="seats_elected" ' + 'name="seats_elected" type="text" ' + 'value="2">', output.data) # We edited the admin groups - self.assertTrue( - '' - in output.data) + self.assertIn( + 'input class="form-control" id="admin_grp" ' + 'name="admin_grp" type="text" ' + 'value="sysadmin-main, testers">', output.data) # Remove an existing group: testers data = { @@ -480,12 +566,21 @@ class FlaskAdmintests(ModelFlasktests): self.assertTrue( 'Election "test_election2" saved' in output.data) - self.assertTrue( - '' in output.data) + if self.get_wtforms_version() >= (2, 2): + self.assertIn( + 'input class="form-control" id="seats_elected" ' + 'name="seats_elected" required type="text" ' + 'value="2">', output.data) + else: + self.assertIn( + 'input class="form-control" id="seats_elected" ' + 'name="seats_elected" type="text" ' + 'value="2">', output.data) # We edited the admin groups - self.assertTrue( - '' - in output.data) + self.assertIn( + 'input class="form-control" id="admin_grp" ' + 'name="admin_grp" type="text" ' + 'value="sysadmin-main">', output.data) def test_admin_edit_election_legal_voters(self): """ Test the admin_edit_election function when editing legal voters. @@ -502,23 +597,29 @@ class FlaskAdmintests(ModelFlasktests): with user_set(fedora_elections.APP, user): output = self.app.get('/admin/test_election3/') self.assertEqual(output.status_code, 200) - csrf_token = output.data.split( - 'name="csrf_token" type="hidden" value="')[1].split('">')[0] + csrf_token = self.get_csrf(output=output) # Edit LegalVoter Group # Check election before edit output = self.app.get('/admin/test_election3/') self.assertEqual(output.status_code, 200) - self.assertTrue( - '' - in output.data) + if self.get_wtforms_version() >= (2, 2): + self.assertIn( + 'input class="form-control" id="seats_elected" ' + 'name="seats_elected" required type="text"', output.data) + else: + self.assertIn( + 'input class="form-control" id="seats_elected" ' + 'name="seats_elected" type="text"', output.data) self.assertTrue('Candidates ' in output.data) - self.assertTrue('' - in output.data) + self.assertIn( + 'input class="form-control" id="lgl_voters" ' + 'name="lgl_voters" type="text" value="voters">', + output.data) # Add a new admin group: sysadmin-main data = { @@ -543,8 +644,10 @@ class FlaskAdmintests(ModelFlasktests): 'Election "test_election3" saved' in output.data) # We edited the legal_voters - self.assertTrue('' - in output.data) + self.assertIn( + 'input class="form-control" id="lgl_voters" ' + 'name="lgl_voters" type="text" ' + 'value="sysadmin-main, voters">', output.data) # Remove an existing group: voters data = { @@ -569,8 +672,10 @@ class FlaskAdmintests(ModelFlasktests): 'Election "test_election3" saved' in output.data) # We edited the legal_voters - self.assertTrue('' - in output.data) + self.assertIn( + 'input class="form-control" id="lgl_voters" ' + 'name="lgl_voters" type="text" ' + 'value="sysadmin-main">', output.data) def test_admin_add_candidate(self): """ Test the admin_add_candidate function. """ @@ -588,8 +693,14 @@ class FlaskAdmintests(ModelFlasktests): self.assertEqual(output.status_code, 200) self.assertTrue('Add candidate' in output.data) - self.assertTrue( - 'input class="form-control" id="name" name="name" type="text"' in output.data) + if self.get_wtforms_version() >= (2, 2): + self.assertIn( + 'input class="form-control" id="name" name="name" ' + 'required type="text"', output.data) + else: + self.assertIn( + 'input class="form-control" id="name" name="name" ' + 'type="text"', output.data) data = { 'name': 'pingou', @@ -600,11 +711,16 @@ class FlaskAdmintests(ModelFlasktests): '/admin/test_election/candidates/new', data=data) self.assertEqual(output.status_code, 200) self.assertTrue('Add candidate' in output.data) - self.assertTrue( - 'input class="form-control" id="name" name="name" type="text"' in output.data) + if self.get_wtforms_version() >= (2, 2): + self.assertIn( + 'input class="form-control" id="name" name="name" ' + 'required type="text"', output.data) + else: + self.assertIn( + 'input class="form-control" id="name" name="name" ' + 'type="text"', output.data) - csrf_token = output.data.split( - 'name="csrf_token" type="hidden" value="')[1].split('">')[0] + csrf_token = self.get_csrf(output=output) data = { 'name': '', @@ -616,8 +732,14 @@ class FlaskAdmintests(ModelFlasktests): '/admin/test_election/candidates/new', data=data) self.assertEqual(output.status_code, 200) self.assertTrue('Add candidate' in output.data) - self.assertTrue( - 'input class="form-control" id="name" name="name" type="text"' in output.data) + if self.get_wtforms_version() >= (2, 2): + self.assertIn( + 'input class="form-control" id="name" name="name" ' + 'required type="text"', output.data) + else: + self.assertIn( + 'input class="form-control" id="name" name="name" ' + 'type="text"', output.data) self.assertTrue( '' in output.data) @@ -653,9 +775,14 @@ class FlaskAdmintests(ModelFlasktests): self.assertEqual(output.status_code, 200) self.assertTrue('Add candidates' in output.data) - self.assertTrue( - 'input class="form-control" id="candidate" name="candidate" type="text"' - in output.data) + if self.get_wtforms_version() >= (2, 2): + self.assertIn( + 'input class="form-control" id="candidate" ' + 'name="candidate" required type="text"', output.data) + else: + self.assertIn( + 'input class="form-control" id="candidate" ' + 'name="candidate" type="text"', output.data) data = { 'candidate': 'pingou', @@ -665,12 +792,16 @@ class FlaskAdmintests(ModelFlasktests): '/admin/test_election/candidates/new/multi', data=data) self.assertEqual(output.status_code, 200) self.assertTrue('Add candidates' in output.data) - self.assertTrue( - 'input class="form-control" id="candidate" name="candidate" type="text"' - in output.data) + if self.get_wtforms_version() >= (2, 2): + self.assertIn( + 'input class="form-control" id="candidate" ' + 'name="candidate" required type="text"', output.data) + else: + self.assertIn( + 'input class="form-control" id="candidate" ' + 'name="candidate" type="text"', output.data) - csrf_token = output.data.split( - 'name="csrf_token" type="hidden" value="')[1].split('">')[0] + csrf_token = self.get_csrf(output=output) data = { 'candidate': '', @@ -681,9 +812,14 @@ class FlaskAdmintests(ModelFlasktests): '/admin/test_election/candidates/new/multi', data=data) self.assertEqual(output.status_code, 200) self.assertTrue('Add candidates' in output.data) - self.assertTrue( - 'input class="form-control" id="candidate" name="candidate" type="text"' - in output.data) + if self.get_wtforms_version() >= (2, 2): + self.assertIn( + 'input class="form-control" id="candidate" ' + 'name="candidate" required type="text"', output.data) + else: + self.assertIn( + 'input class="form-control" id="candidate" ' + 'name="candidate" type="text"', output.data) self.assertTrue( '' in output.data) @@ -731,8 +867,14 @@ class FlaskAdmintests(ModelFlasktests): self.assertEqual(output.status_code, 200) self.assertTrue('Edit candidate' in output.data) - self.assertTrue( - 'input class="form-control" id="name" name="name" type="text"' in output.data) + if self.get_wtforms_version() >= (2, 2): + self.assertIn( + 'input class="form-control" id="name" name="name" ' + 'required type="text"', output.data) + else: + self.assertIn( + 'input class="form-control" id="name" name="name" ' + 'type="text"', output.data) data = { 'name': 'Toshio Kuratomi', @@ -740,11 +882,16 @@ class FlaskAdmintests(ModelFlasktests): } self.assertTrue('Edit candidate' in output.data) - self.assertTrue( - 'input class="form-control" id="name" name="name" type="text"' in output.data) + if self.get_wtforms_version() >= (2, 2): + self.assertIn( + 'input class="form-control" id="name" name="name" ' + 'required type="text"', output.data) + else: + self.assertIn( + 'input class="form-control" id="name" name="name" ' + 'type="text"', output.data) - csrf_token = output.data.split( - 'name="csrf_token" type="hidden" value="')[1].split('">')[0] + csrf_token = self.get_csrf(output=output) data = { 'name': '', @@ -755,8 +902,14 @@ class FlaskAdmintests(ModelFlasktests): '/admin/test_election/candidates/1/edit', data=data) self.assertEqual(output.status_code, 200) self.assertTrue('Edit candidate' in output.data) - self.assertTrue( - 'input class="form-control" id="name" name="name" type="text"' in output.data) + if self.get_wtforms_version() >= (2, 2): + self.assertIn( + 'input class="form-control" id="name" name="name" ' + 'required type="text"', output.data) + else: + self.assertIn( + 'input class="form-control" id="name" name="name" ' + 'type="text"', output.data) self.assertTrue( '' in output.data) @@ -765,7 +918,16 @@ class FlaskAdmintests(ModelFlasktests): output = self.app.get('/admin/test_election/') self.assertEqual(output.status_code, 200) self.assertTrue('Candidates 3' in output.data) - self.assertTrue('' in output.data) + if self.get_wtforms_version() >= (2, 2): + self.assertIn( + 'input class="form-control" id="seats_elected" ' + 'name="seats_elected" required type="text" value="1"', + output.data) + else: + self.assertIn( + 'input class="form-control" id="seats_elected" ' + 'name="seats_elected" type="text" value="1"', + output.data) self.assertTrue('
Toshio' in output.data) data = { @@ -823,8 +985,7 @@ class FlaskAdmintests(ModelFlasktests): 'p>Are you sure you want to delete candidate "Toshio"?')[0] + csrf_token = self.get_csrf(output=output) # Try to delete while there are votes link to this candidates data = { @@ -843,7 +1004,16 @@ class FlaskAdmintests(ModelFlasktests): # Check election before edit output = self.app.get('/admin/test_election4/') self.assertTrue('Candidates 2' in output.data) - self.assertTrue('' in output.data) + if self.get_wtforms_version() >= (2, 2): + self.assertIn( + 'input class="form-control" id="seats_elected" ' + 'name="seats_elected" required type="text" value="1"', + output.data) + else: + self.assertIn( + 'input class="form-control" id="seats_elected" ' + 'name="seats_elected" type="text" value="1"', + output.data) self.assertTrue('
Toshio' in output.data) self.assertTrue( 'value="test election 4 shortdesc">' in output.data) From 75aa26ed6c4788fa5d15815714133fca2ce4c136 Mon Sep 17 00:00:00 2001 From: Pierre-Yves Chibon Date: Mar 05 2019 19:40:32 +0000 Subject: [PATCH 11/24] Port elections to openid-connect This should fix the issue with finding out if an user is CLA+1 or not and thus allow anyone who should to vote. Signed-off-by: Pierre-Yves Chibon --- diff --git a/fedora_elections/__init__.py b/fedora_elections/__init__.py index 7d51af8..5e232b5 100644 --- a/fedora_elections/__init__.py +++ b/fedora_elections/__init__.py @@ -39,10 +39,11 @@ from functools import wraps # noqa from urlparse import urlparse, urljoin # noqa import flask # noqa +import munch # noqa from fedora.client import AuthError, AppError # noqa from fedora.client.fas2 import AccountSystem # noqa -from flask_fas_openid import FAS # noqa +from flask_oidc import OpenIDConnect # noqa import fedora_elections.fedmsgshim # noqa import fedora_elections.mail_logging # noqa @@ -54,7 +55,7 @@ if 'FEDORA_ELECTIONS_CONFIG' in os.environ: # pragma: no cover APP.config.from_envvar('FEDORA_ELECTIONS_CONFIG') # set up FAS -FAS = FAS(APP) +OIDC = OpenIDConnect(APP, credentials_store=flask.session) # Set up the logging if not APP.debug: @@ -106,12 +107,15 @@ def is_safe_url(target): ref_url.netloc == test_url.netloc -def is_admin(user): +def is_admin(user, user_groups=None): ''' Is the user an elections admin. ''' + if not user_groups: + user_groups = [] + if not user: return False - if not user.cla_done or len(user.groups) < 1: + if not user.cla_done or len(user_groups) < 1: return False admins = APP.config['FEDORA_ELECTIONS_ADMIN_GROUP'] @@ -120,7 +124,7 @@ def is_admin(user): else: admins = set(admins) - return len(set(user.groups).intersection(admins)) > 0 + return len(set(user_groups).intersection(admins)) > 0 def is_election_admin(user, election_id): @@ -163,9 +167,11 @@ def inject_variables(): template). ''' user = None - if hasattr(flask.g, 'fas_user'): + user_groups = None + if is_authenticated() and OIDC.user_loggedin: user = flask.g.fas_user - return dict(is_admin=is_admin(user), + user_groups = OIDC.user_getfield('groups') + return dict(is_admin=is_admin(user, user_groups), version=__version__) @@ -199,6 +205,23 @@ def set_session(): """ Set the flask session as permanent. """ flask.session.permanent = True + if OIDC.user_loggedin: + if not hasattr(flask.session, 'fas_user') or not flask.session.fas_user: + flask.session.fas_user = munch.Munch({ + 'username': OIDC.user_getfield('nickname'), + 'email': OIDC.user_getfield('email') or '', + 'timezone': OIDC.user_getfield('zoneinfo'), + 'cla_done': \ + 'http://admin.fedoraproject.org/accounts/cla/done' \ + in (OIDC.user_getfield('cla') or []), + }) + flask.g.fas_user = flask.session.fas_user + else: + flask.session.fas_user = None + flask.g.fas_user = None + print(flask.g.fas_user) + + # pylint: disable=W0613 @APP.teardown_request @@ -294,6 +317,7 @@ def archived_elections(): @APP.route('/login', methods=('GET', 'POST')) +@OIDC.require_login def auth_login(): next_url = None if 'next' in flask.request.args: @@ -310,14 +334,15 @@ def auth_login(): if isinstance(groups, basestring): groups = [groups] groups.extend(models.get_groups(SESSION)) - return FAS.login(return_url=next_url, groups=groups) + return flask.redirect(return_point) @APP.route('/logout') def auth_logout(): if hasattr(flask.g, 'fas_user') and flask.g.fas_user is not None: - FAS.logout() + OIDC.logout() flask.g.fas_user = None + flask.session.fas_user = None flask.flash('You have been logged out') return safe_redirect_back() diff --git a/fedora_elections/default_config.py b/fedora_elections/default_config.py index 979dd9d..50ada7f 100644 --- a/fedora_elections/default_config.py +++ b/fedora_elections/default_config.py @@ -3,7 +3,7 @@ ''' Fedora elections default configuration. ''' - +import os from datetime import timedelta # Set the time after which the session expires @@ -22,3 +22,8 @@ FAS_BASE_URL = 'https://admin.stg.fedoraproject.org/accounts/' FAS_USERNAME = '' FAS_PASSWORD = '' FAS_CHECK_CERT = False + + +OIDC_CLIENT_SECRETS = os.path.join(os.path.dirname( + os.path.abspath(__file__)), 'client_secrets.json') +OIDC_SCOPES = ['openid', 'email', 'profile', 'fedora'] diff --git a/fedora_elections/elections.py b/fedora_elections/elections.py index 8516066..4b515e2 100644 --- a/fedora_elections/elections.py +++ b/fedora_elections/elections.py @@ -32,7 +32,7 @@ import flask from fedora_elections import forms from fedora_elections import models from fedora_elections import ( - APP, SESSION, is_authenticated, is_admin, is_election_admin, + OIDC, APP, SESSION, is_authenticated, is_admin, is_election_admin, safe_redirect_back, ) from fedora_elections.utils import build_name_map @@ -48,11 +48,13 @@ def login_required(f): flask.flash( 'You must sign the CLA to vote', 'error') return safe_redirect_back() - elif len(flask.g.fas_user.groups) == 0: - flask.flash( - 'You need to be in one another group than CLA to vote', - 'error') - return safe_redirect_back() + else: + user_groups = OIDC.user_getfield('groups') + if len(user_groups) == 0: + flask.flash( + 'You need to be in one another group than CLA to vote', + 'error') + return safe_redirect_back() return f(*args, **kwargs) return decorated_function @@ -93,7 +95,8 @@ def vote(election_alias): return election if election.legal_voters_list: - if len(set(flask.g.fas_user.groups).intersection( + user_groups = OIDC.user_getfield('groups') + if len(set(user_groups).intersection( set(election.legal_voters_list))) == 0: flask.flash( 'You are not among the groups that are allowed to vote ' From fc0882f4adacccbfd0d17906bdc24ef21274fc01 Mon Sep 17 00:00:00 2001 From: Pierre-Yves Chibon Date: Mar 05 2019 19:40:32 +0000 Subject: [PATCH 12/24] Add the OIDC_OPENID_REALM on port 5005 Signed-off-by: Pierre-Yves Chibon --- diff --git a/fedora_elections/default_config.py b/fedora_elections/default_config.py index 50ada7f..6799bf6 100644 --- a/fedora_elections/default_config.py +++ b/fedora_elections/default_config.py @@ -27,3 +27,4 @@ FAS_CHECK_CERT = False OIDC_CLIENT_SECRETS = os.path.join(os.path.dirname( os.path.abspath(__file__)), 'client_secrets.json') OIDC_SCOPES = ['openid', 'email', 'profile', 'fedora'] +OIDC_OPENID_REALM = 'http://localhost:5005/oidc_callback' diff --git a/runserver.py b/runserver.py old mode 100755 new mode 100644 index 415f49f..9b516c1 --- a/runserver.py +++ b/runserver.py @@ -23,7 +23,7 @@ parser.add_argument( default=False, help='Profile the application.') parser.add_argument( - '--port', '-p', default=5000, + '--port', '-p', default=5005, help='Port for the flask application.') parser.add_argument( '--host', default="127.0.0.1", From fba415fa2b7c805e5136fc0b92914a595bb2363d Mon Sep 17 00:00:00 2001 From: Pierre-Yves Chibon Date: Mar 05 2019 19:40:32 +0000 Subject: [PATCH 13/24] fixup oidc Signed-off-by: Pierre-Yves Chibon --- diff --git a/fedora_elections/__init__.py b/fedora_elections/__init__.py index 5e232b5..1c855eb 100644 --- a/fedora_elections/__init__.py +++ b/fedora_elections/__init__.py @@ -115,7 +115,14 @@ def is_admin(user, user_groups=None): if not user: return False - if not user.cla_done or len(user_groups) < 1: + if not user.cla_done: + return False + + user_groups = [] + if is_authenticated() and OIDC.user_loggedin: + user_groups = OIDC.user_getfield('groups') + + if len(user_groups) < 1: return False admins = APP.config['FEDORA_ELECTIONS_ADMIN_GROUP'] @@ -167,11 +174,9 @@ def inject_variables(): template). ''' user = None - user_groups = None if is_authenticated() and OIDC.user_loggedin: user = flask.g.fas_user - user_groups = OIDC.user_getfield('groups') - return dict(is_admin=is_admin(user, user_groups), + return dict(is_admin=is_admin(user), version=__version__) @@ -219,8 +224,6 @@ def set_session(): else: flask.session.fas_user = None flask.g.fas_user = None - print(flask.g.fas_user) - # pylint: disable=W0613 @@ -334,7 +337,7 @@ def auth_login(): if isinstance(groups, basestring): groups = [groups] groups.extend(models.get_groups(SESSION)) - return flask.redirect(return_point) + return flask.redirect(next_url) @APP.route('/logout') From 1e9ad274fdb6fc89416d602f5d9fb64d021d3de3 Mon Sep 17 00:00:00 2001 From: Pierre-Yves Chibon Date: Mar 05 2019 19:41:52 +0000 Subject: [PATCH 14/24] Port unit-tests to OpenID-Connect Signed-off-by: Pierre-Yves Chibon --- diff --git a/runtests.sh b/runtests.sh index 47f97e4..8ac84b0 100755 --- a/runtests.sh +++ b/runtests.sh @@ -1,4 +1,5 @@ #!/bin/bash +FEDORA_ELECTIONS_CONFIG=`pwd`/tests/config \ PYTHONPATH=fedora_elections ./nosetests \ --with-coverage --cover-erase --cover-package=fedora_elections $* diff --git a/tests/__init__.py b/tests/__init__.py index 56ffe3c..68b0a3a 100644 --- a/tests/__init__.py +++ b/tests/__init__.py @@ -67,7 +67,7 @@ TODAY = date.today() @contextmanager -def user_set(APP, user): +def user_set(APP, user, oidc_id_token=None): """ Set the provided user as fas_user in the provided application.""" # Hack used to remove the before_request function set by @@ -78,6 +78,7 @@ def user_set(APP, user): def handler(sender, **kwargs): g.fas_user = user + g.oidc_id_token = oidc_id_token with appcontext_pushed.connected_to(handler, APP): yield diff --git a/tests/client_secrets.json b/tests/client_secrets.json new file mode 100644 index 0000000..dcae170 --- /dev/null +++ b/tests/client_secrets.json @@ -0,0 +1,12 @@ +{ + "web": { + "redirect_uris": ["http://localhost:5005/oidc_callback"], + "token_uri": "https://iddev.fedorainfracloud.org/openidc/Token", + "auth_uri": "https://iddev.fedorainfracloud.org/openidc/Authorization", + "client_id": "client_id", + "client_secret": "client_secret", + "userinfo_uri": "https://iddev.fedorainfracloud.org/openidc/UserInfo", + "token_introspection_uri": "https://iddev.fedorainfracloud.org/openidc/TokenInfo", + "issuer": "https://iddev.fedorainfracloud.org/openidc/" + } +} diff --git a/tests/config b/tests/config new file mode 100644 index 0000000..cb683d1 --- /dev/null +++ b/tests/config @@ -0,0 +1,4 @@ +import os + +OIDC_CLIENT_SECRETS = os.path.join(os.path.dirname( + os.path.abspath(__file__)), 'client_secrets.json') diff --git a/tests/test_flask.py b/tests/test_flask.py index 868cccc..1899954 100644 --- a/tests/test_flask.py +++ b/tests/test_flask.py @@ -31,6 +31,7 @@ from datetime import time from datetime import timedelta import flask +from mock import patch, MagicMock sys.path.insert(0, os.path.join(os.path.dirname( os.path.abspath(__file__)), '..')) @@ -117,18 +118,31 @@ class Flasktests(ModelFlasktests): flask.g.fas_user = None self.assertFalse(fedora_elections.is_admin(flask.g.fas_user)) - flask.g.fas_user = FakeUser() - self.assertFalse(fedora_elections.is_admin(flask.g.fas_user)) + flask.g.oidc_id_token = 'foobar' + + with patch( + 'fedora_elections.OIDC.user_getfield', + MagicMock(return_value=['foobar'])): + flask.g.fas_user = FakeUser() + self.assertFalse(fedora_elections.is_admin(flask.g.fas_user)) - flask.g.fas_user = FakeUser( - fedora_elections.APP.config['FEDORA_ELECTIONS_ADMIN_GROUP']) - self.assertTrue(fedora_elections.is_admin(flask.g.fas_user)) + with patch( + 'fedora_elections.OIDC.user_getfield', + MagicMock(return_value=['elections'])): + flask.g.fas_user = FakeUser( + fedora_elections.APP.config['FEDORA_ELECTIONS_ADMIN_GROUP']) + self.assertTrue( + fedora_elections.is_admin(flask.g.fas_user, ['elections'])) - fedora_elections.APP.config['FEDORA_ELECTIONS_ADMIN_GROUP'] = [ - 'sysadmin-main', 'sysadmin-elections'] - flask.g.fas_user = FakeUser( - fedora_elections.APP.config['FEDORA_ELECTIONS_ADMIN_GROUP']) - self.assertTrue(fedora_elections.is_admin(flask.g.fas_user)) + with patch( + 'fedora_elections.OIDC.user_getfield', + MagicMock(return_value=['sysadmin-main'])): + + fedora_elections.APP.config['FEDORA_ELECTIONS_ADMIN_GROUP'] = [ + 'sysadmin-main', 'sysadmin-elections'] + flask.g.fas_user = FakeUser(['sysadmin-main']) + self.assertTrue( + fedora_elections.is_admin(flask.g.fas_user, ['sysadmin-main'])) def test_is_election_admin(self): """ Test the is_election_admin function. """ @@ -140,31 +154,42 @@ class Flasktests(ModelFlasktests): fedora_elections.is_election_admin( flask.g.fas_user, 1) ) + + flask.g.oidc_id_token = 'foobar' + flask.g.fas_user = FakeUser() self.assertFalse( fedora_elections.is_election_admin( flask.g.fas_user, 1) ) - flask.g.fas_user = FakeUser( - fedora_elections.APP.config['FEDORA_ELECTIONS_ADMIN_GROUP']) - self.assertTrue(fedora_elections.is_election_admin( - flask.g.fas_user, 1)) + with patch( + 'fedora_elections.OIDC.user_getfield', + MagicMock(return_value=['elections'])): + flask.g.fas_user = FakeUser( + fedora_elections.APP.config['FEDORA_ELECTIONS_ADMIN_GROUP']) + self.assertTrue(fedora_elections.is_election_admin( + flask.g.fas_user, 1)) self.setup_db() with app.test_request_context(): flask.g.fas_user = FakeUser('testers') - # This is user is not an admin for election #1 - self.assertFalse( - fedora_elections.is_election_admin( - flask.g.fas_user, 1) - ) + flask.g.oidc_id_token = 'foobar' - # This is user is an admin for election #2 - self.assertTrue( - fedora_elections.is_election_admin( - flask.g.fas_user, 2) - ) + # This is user is not an admin for election #1 + with patch( + 'fedora_elections.OIDC.user_getfield', + MagicMock(return_value=['foobar'])): + self.assertFalse( + fedora_elections.is_election_admin( + flask.g.fas_user, 1) + ) + + # This is user is an admin for election #2 + self.assertTrue( + fedora_elections.is_election_admin( + flask.g.fas_user, 2) + ) def test_is_safe_url(self): """ Test the is_safe_url function. """ @@ -182,17 +207,23 @@ class Flasktests(ModelFlasktests): with app.test_request_context(): flask.g.fas_user = FakeUser(['gitr2spec']) output = self.app.get('/login') - self.assertEqual(output.status_code, 200) + self.assertEqual(output.status_code, 302) + self.assertIn( + 'https://iddev.fedorainfracloud.org/openidc/Authorization?', + output.data) output = self.app.get('/login?next=http://localhost/') - self.assertEqual(output.status_code, 200) - - self.setup_db() - user = FakeUser([], username='pingou') - with user_set(fedora_elections.APP, user): - output = self.app.get('/login', follow_redirects=True) - self.assertEqual(output.status_code, 200) - self.assertTrue('Fedora elections' in output.data) + self.assertEqual(output.status_code, 302) + self.assertIn( + 'https://iddev.fedorainfracloud.org/openidc/Authorization?', + output.data) + + # self.setup_db() + # user = FakeUser([], username='pingou') + # with user_set(fedora_elections.APP, user): + # output = self.app.get('/login', follow_redirects=True) + # self.assertEqual(output.status_code, 200) + # self.assertTrue('Fedora elections' in output.data) def test_auth_logout(self): """ Test the auth_logout function. """ diff --git a/tests/test_flask_elections.py b/tests/test_flask_elections.py index 934c9bc..5400851 100644 --- a/tests/test_flask_elections.py +++ b/tests/test_flask_elections.py @@ -32,6 +32,7 @@ from datetime import time from datetime import timedelta import flask +from mock import patch, MagicMock sys.path.insert(0, os.path.join(os.path.dirname( os.path.abspath(__file__)), '..')) @@ -48,30 +49,30 @@ class FlaskElectionstests(ModelFlasktests): """ Test the vote function without data. """ output = self.app.get('/vote/test_election') self.assertEqual(output.status_code, 302) - - output = self.app.get('/vote/test_election', follow_redirects=True) - self.assertEqual(output.status_code, 200) - self.assertTrue( - 'OpenID transaction in progress' in output.data - or 'discoveryfailure' in output.data) + self.assertIn( + '/login?next=http%3A%2F%2Flocalhost%2Fvote%2Ftest_election', + output.data) user = FakeUser(['packager'], username='pingou') - with user_set(fedora_elections.APP, user): - output = self.app.get('/vote/test_election') - self.assertEqual(output.status_code, 302) - - output = self.app.get('/vote/test_election', follow_redirects=True) - self.assertTrue( - 'The election, test_election, does not exist.' - in output.data) + with user_set(fedora_elections.APP, user, oidc_id_token='foobar'): + with patch( + 'fedora_elections.OIDC.user_getfield', + MagicMock(return_value=['packager'])): + output = self.app.get('/vote/test_election') + self.assertEqual(output.status_code, 302) + + output = self.app.get('/vote/test_election', follow_redirects=True) + self.assertTrue( + 'The election, test_election, does not exist.' + in output.data) def test_vote(self): """ Test the vote function. """ - output = self.app.get('/vote/test_election', follow_redirects=True) - self.assertEqual(output.status_code, 200) - self.assertTrue( - 'OpenID transaction in progress' in output.data - or 'discoveryfailure' in output.data) + output = self.app.get('/vote/test_election') + self.assertEqual(output.status_code, 302) + self.assertIn( + '/login?next=http%3A%2F%2Flocalhost%2Fvote%2Ftest_election', + output.data) self.setup_db() @@ -88,72 +89,81 @@ class FlaskElectionstests(ModelFlasktests): in output.data) user = FakeUser([], username='pingou') - with user_set(fedora_elections.APP, user): + with user_set(fedora_elections.APP, user, oidc_id_token='foobar'): + with patch( + 'fedora_elections.OIDC.user_getfield', + MagicMock(return_value=[])): - output = self.app.get('/vote/test_election') - self.assertEqual(output.status_code, 302) + output = self.app.get('/vote/test_election') + self.assertEqual(output.status_code, 302) - output = self.app.get( - '/vote/test_election', follow_redirects=True) - self.assertTrue( - 'You need to be in one another group than ' - 'CLA to vote' in output.data) + output = self.app.get( + '/vote/test_election', follow_redirects=True) + self.assertTrue( + 'You need to be in one another group than ' + 'CLA to vote' in output.data) user = FakeUser(['packager'], username='pingou') - with user_set(fedora_elections.APP, user): + with user_set(fedora_elections.APP, user, oidc_id_token='foobar'): + with patch( + 'fedora_elections.OIDC.user_getfield', + MagicMock(return_value=['packager'])): - output = self.app.get('/vote/test_election3') - self.assertEqual(output.status_code, 302) + output = self.app.get('/vote/test_election3') + self.assertEqual(output.status_code, 302) - # Election closed and results open - output = self.app.get( - '/vote/test_election3', follow_redirects=True) - self.assertTrue( - 'You are not among the groups that are ' - 'allowed to vote for this election' in output.data) + # Election closed and results open + output = self.app.get( + '/vote/test_election3', follow_redirects=True) + self.assertTrue( + 'You are not among the groups that are ' + 'allowed to vote for this election' in output.data) user = FakeUser(['voters'], username='pingou') - with user_set(fedora_elections.APP, user): - - output = self.app.get('/vote/test_election') - self.assertEqual(output.status_code, 302) - - # Election closed and results open - output = self.app.get( - '/vote/test_election', follow_redirects=True) - self.assertTrue( - 'Election Closed' - in output.data) - - # Election closed and results are embargoed - output = self.app.get( - '/vote/test_election2', follow_redirects=True) - self.assertTrue( - 'Election Closed' - in output.data) - - # Election still pending - output = self.app.get( - '/vote/test_election4', follow_redirects=True) - self.assertTrue( - 'Voting has not yet started, sorry.' - in output.data) - - # Election in progress - output = self.app.get('/vote/test_election3') - self.assertTrue( - 'test election 3 shortdesc' in output.data) - self.assertTrue( - '' - in output.data) - - # Election in progress - output = self.app.get('/vote/test_election5') - self.assertTrue( - 'test election 5 shortdesc' in output.data) - self.assertTrue( - '' - in output.data) + with user_set(fedora_elections.APP, user, oidc_id_token='foobar'): + with patch( + 'fedora_elections.OIDC.user_getfield', + MagicMock(return_value=['voters'])): + + output = self.app.get('/vote/test_election') + self.assertEqual(output.status_code, 302) + + # Election closed and results open + output = self.app.get( + '/vote/test_election', follow_redirects=True) + self.assertTrue( + 'Election Closed' + in output.data) + + # Election closed and results are embargoed + output = self.app.get( + '/vote/test_election2', follow_redirects=True) + self.assertTrue( + 'Election Closed' + in output.data) + + # Election still pending + output = self.app.get( + '/vote/test_election4', follow_redirects=True) + self.assertTrue( + 'Voting has not yet started, sorry.' + in output.data) + + # Election in progress + output = self.app.get('/vote/test_election3') + self.assertTrue( + 'test election 3 shortdesc' in output.data) + self.assertTrue( + '' + in output.data) + + # Election in progress + output = self.app.get('/vote/test_election5') + self.assertTrue( + 'test election 5 shortdesc' in output.data) + self.assertTrue( + '' + in output.data) def test_election_results(self): """ Test the election_results function - the preview part. """ @@ -194,22 +204,25 @@ class FlaskElectionstests(ModelFlasktests): user = FakeUser( fedora_elections.APP.config['FEDORA_ELECTIONS_ADMIN_GROUP'], username='toshio') - with user_set(fedora_elections.APP, user): - output = self.app.get( - '/about/test_election2', follow_redirects=True) - self.assertEqual(output.status_code, 200) - self.assertTrue( - 'You are only seeing these results because you are an admin.' - in output.data) - self.assertTrue( - 'The results for this election are currently embargoed ' - in output.data) - self.assertTrue( - '
' - in output.data) - self.assertTrue( - '

Some statistics about this election

' - in output.data) + with user_set(fedora_elections.APP, user, oidc_id_token='foobar'): + with patch( + 'fedora_elections.OIDC.user_getfield', + MagicMock(return_value=['elections'])): + output = self.app.get( + '/about/test_election2', follow_redirects=True) + self.assertEqual(output.status_code, 200) + self.assertTrue( + 'You are only seeing these results because you are an admin.' + in output.data) + self.assertTrue( + 'The results for this election are currently embargoed ' + in output.data) + self.assertTrue( + '' + in output.data) + self.assertTrue( + '

Some statistics about this election

' + in output.data) user = FakeUser(['gitr2spec'], username='kevin') with user_set(fedora_elections.APP, user): @@ -251,11 +264,14 @@ class FlaskElectionstests(ModelFlasktests): user = FakeUser( fedora_elections.APP.config['FEDORA_ELECTIONS_ADMIN_GROUP'], username='toshio') - with user_set(fedora_elections.APP, user): - output = self.app.get( - '/results/test_election/text', follow_redirects=True) - self.assertEqual(output.status_code, 200) - exp = """ + with user_set(fedora_elections.APP, user, oidc_id_token='foobar'): + with patch( + 'fedora_elections.OIDC.user_getfield', + MagicMock(return_value=['elections'])): + output = self.app.get( + '/results/test_election/text', follow_redirects=True) + self.assertEqual(output.status_code, 200) + exp = """ @@ -291,12 +307,13 @@ candidates for running this elections! """ - self.assertEqual(output.data, exp) + print(output.data) + self.assertEqual(output.data, exp) - output = self.app.get( - '/results/test_election2/text', follow_redirects=True) - self.assertEqual(output.status_code, 200) - exp = """ + output = self.app.get( + '/results/test_election2/text', follow_redirects=True) + self.assertEqual(output.status_code, 200) + exp = """ @@ -329,7 +346,7 @@ candidates for running this elections! """ - self.assertEqual(output.data, exp) + self.assertEqual(output.data, exp) user = FakeUser(['gitr2spec'], username='kevin') with user_set(fedora_elections.APP, user): diff --git a/tests/test_flask_irc.py b/tests/test_flask_irc.py index 893fddd..59fed2a 100644 --- a/tests/test_flask_irc.py +++ b/tests/test_flask_irc.py @@ -32,6 +32,7 @@ from datetime import time from datetime import timedelta import flask +from mock import patch, MagicMock sys.path.insert(0, os.path.join(os.path.dirname( os.path.abspath(__file__)), '..')) @@ -46,100 +47,104 @@ class FlaskIrcElectionstests(ModelFlasktests): def test_vote_irc(self): """ Test the vote_irc function - the preview part. """ - output = self.app.get( - '/vote/test_election', follow_redirects=True) - self.assertEqual(output.status_code, 200) - self.assertTrue( - 'OpenID transaction in progress' in output.data - or 'discoveryfailure' in output.data) + output = self.app.get('/vote/test_election') + self.assertEqual(output.status_code, 302) + self.assertIn( + '/login?next=http%3A%2F%2Flocalhost%2Fvote%2Ftest_election', + output.data) self.setup_db() user = FakeUser(['packager'], username='nerdsville') - with user_set(fedora_elections.APP, user): - output = self.app.get( - '/vote/test_election7') - self.assertTrue( - 'test election 7 shortdesc' in output.data) - self.assertTrue( - '' - in output.data) - - csrf_token = output.data.split( - 'name="csrf_token" type="hidden" value="')[1].split('">')[0] - - # Invalid vote: No candidate - data = { - 'action': 'preview', - } - - output = self.app.post('/vote/test_election7', data=data) - self.assertEqual(output.status_code, 200) - self.assertTrue( - 'test election 7 shortdesc' in output.data) - - # Valid input - data = { - 'Kevin': -1, - 'Toshio': '0', - 'action': 'preview', - 'csrf_token': csrf_token, - } - - output = self.app.post('/vote/test_election7', data=data) - self.assertEqual(output.status_code, 200) - self.assertTrue( - 'test election 7 shortdesc' in output.data) - self.assertTrue( - '' - in output.data) - self.assertTrue( - 'Please confirm your vote!' - in output.data) + with user_set(fedora_elections.APP, user, oidc_id_token='foobar'): + with patch( + 'fedora_elections.OIDC.user_getfield', + MagicMock(return_value=['packager'])): + output = self.app.get( + '/vote/test_election7') + self.assertTrue( + 'test election 7 shortdesc' in output.data) + self.assertTrue( + '' + in output.data) + + csrf_token = output.data.split( + 'name="csrf_token" type="hidden" value="')[1].split('">')[0] + + # Invalid vote: No candidate + data = { + 'action': 'preview', + } + + output = self.app.post('/vote/test_election7', data=data) + self.assertEqual(output.status_code, 200) + self.assertTrue( + 'test election 7 shortdesc' in output.data) + + # Valid input + data = { + 'Kevin': -1, + 'Toshio': '0', + 'action': 'preview', + 'csrf_token': csrf_token, + } + + output = self.app.post('/vote/test_election7', data=data) + self.assertEqual(output.status_code, 200) + self.assertTrue( + 'test election 7 shortdesc' in output.data) + self.assertTrue( + '' + in output.data) + self.assertTrue( + 'Please confirm your vote!' + in output.data) def test_vote_irc_process(self): """ Test the vote_irc function - the voting part. """ - output = self.app.get( - '/vote/test_election', follow_redirects=True) - self.assertEqual(output.status_code, 200) - self.assertTrue( - 'OpenID transaction in progress' in output.data - or 'discoveryfailure' in output.data) + output = self.app.get('/vote/test_election') + self.assertEqual(output.status_code, 302) + self.assertIn( + '/login?next=http%3A%2F%2Flocalhost%2Fvote%2Ftest_election', + output.data) self.setup_db() user = FakeUser(['packager'], username='pingou') - with user_set(fedora_elections.APP, user): - # Invalid candidate id - no csrf - data = { - 'candidate': 1, - 'action': 'submit', - } - - output = self.app.post( - '/vote/test_election7', data=data, - follow_redirects=True) - self.assertEqual(output.status_code, 200) - - csrf_token = output.data.split( - 'name="csrf_token" type="hidden" value="')[1].split('">')[0] - - # Valid input - data = { - 'Toshio': '0', - 'Kevin': '1', - 'action': 'submit', - 'csrf_token': csrf_token, - } - - output = self.app.post( - '/vote/test_election7', data=data, - follow_redirects=True) - self.assertEqual(output.status_code, 200) - self.assertTrue( - 'Your vote has been recorded. Thank you!' - in output.data) - self.assertTrue('Open elections' in output.data) + with user_set(fedora_elections.APP, user, oidc_id_token='foobar'): + with patch( + 'fedora_elections.OIDC.user_getfield', + MagicMock(return_value=['packager'])): + # Invalid candidate id - no csrf + data = { + 'candidate': 1, + 'action': 'submit', + } + + output = self.app.post( + '/vote/test_election7', data=data, + follow_redirects=True) + self.assertEqual(output.status_code, 200) + + csrf_token = output.data.split( + 'name="csrf_token" type="hidden" value="')[1].split('">')[0] + + # Valid input + data = { + 'Toshio': '0', + 'Kevin': '1', + 'action': 'submit', + 'csrf_token': csrf_token, + } + + output = self.app.post( + '/vote/test_election7', data=data, + follow_redirects=True) + self.assertEqual(output.status_code, 200) + self.assertTrue( + 'Your vote has been recorded. Thank you!' + in output.data) + self.assertTrue('Open elections' in output.data) def test_vote_irc_revote(self): """ Test the vote_irc function - the re-voting part. """ @@ -147,45 +152,48 @@ class FlaskIrcElectionstests(ModelFlasktests): self.setup_db() user = FakeUser(['voters'], username='nerdsville') - with user_set(fedora_elections.APP, user): - retrieve_csrf = self.app.post('/vote/test_election7') - csrf_token = retrieve_csrf.data.split( - 'name="csrf_token" type="hidden" value="')[1].split('">')[0] - # Valid input - data = { - 'Kevin': -1, - 'Toshio': 1, - 'action': 'submit', - 'csrf_token': csrf_token, - } - self.app.post('/vote/test_election7', data=data, follow_redirects=True) - vote = fedora_elections.models.Vote - votes = vote.of_user_on_election(self.session, "nerdsville", '7') - self.assertEqual(votes[0].value, 1) - self.assertEqual(votes[1].value, -1) - #Let's not do repetition of what is tested above we aren't testing the - #functionality of voting as that has already been asserted - - #Next, we need to try revoting - newdata = { - 'Kevin': 1, - 'Toshio': -1, - 'action': 'submit', - 'csrf_token': csrf_token, - } - output = self.app.post('/vote/test_election7', data=newdata, follow_redirects=True) - #Next, we need to check if the vote has been recorded - self.assertEqual(output.status_code, 200) - self.assertTrue( - 'Your vote has been recorded. Thank you!' - in output.data) - self.assertTrue('Open elections' in output.data) - vote = fedora_elections.models.Vote - votes = vote.of_user_on_election(self.session, "nerdsville", '7') - self.assertEqual(votes[0].value, -1) - self.assertEqual(votes[1].value, 1) - - #If we haven't failed yet, HOORAY! + with user_set(fedora_elections.APP, user, oidc_id_token='foobar'): + with patch( + 'fedora_elections.OIDC.user_getfield', + MagicMock(return_value=['packager'])): + retrieve_csrf = self.app.post('/vote/test_election7') + csrf_token = retrieve_csrf.data.split( + 'name="csrf_token" type="hidden" value="')[1].split('">')[0] + # Valid input + data = { + 'Kevin': -1, + 'Toshio': 1, + 'action': 'submit', + 'csrf_token': csrf_token, + } + self.app.post('/vote/test_election7', data=data, follow_redirects=True) + vote = fedora_elections.models.Vote + votes = vote.of_user_on_election(self.session, "nerdsville", '7') + self.assertEqual(votes[0].value, 1) + self.assertEqual(votes[1].value, -1) + #Let's not do repetition of what is tested above we aren't testing the + #functionality of voting as that has already been asserted + + #Next, we need to try revoting + newdata = { + 'Kevin': 1, + 'Toshio': -1, + 'action': 'submit', + 'csrf_token': csrf_token, + } + output = self.app.post('/vote/test_election7', data=newdata, follow_redirects=True) + #Next, we need to check if the vote has been recorded + self.assertEqual(output.status_code, 200) + self.assertTrue( + 'Your vote has been recorded. Thank you!' + in output.data) + self.assertTrue('Open elections' in output.data) + vote = fedora_elections.models.Vote + votes = vote.of_user_on_election(self.session, "nerdsville", '7') + self.assertEqual(votes[0].value, -1) + self.assertEqual(votes[1].value, 1) + + #If we haven't failed yet, HOORAY! if __name__ == '__main__': SUITE = unittest.TestLoader().loadTestsFromTestCase(FlaskIrcElectionstests) diff --git a/tests/test_flask_range_voting.py b/tests/test_flask_range_voting.py index bf290e0..32477cf 100644 --- a/tests/test_flask_range_voting.py +++ b/tests/test_flask_range_voting.py @@ -32,6 +32,7 @@ from datetime import time from datetime import timedelta import flask +from mock import patch, MagicMock sys.path.insert(0, os.path.join(os.path.dirname( os.path.abspath(__file__)), '..')) @@ -46,242 +47,246 @@ class FlaskRangeElectionstests(ModelFlasktests): def test_vote_range(self): """ Test the vote_range function - the preview part. """ - output = self.app.get( - '/vote/test_election', follow_redirects=True) - self.assertEqual(output.status_code, 200) - self.assertTrue( - 'OpenID transaction in progress' in output.data - or 'discoveryfailure' in output.data) + output = self.app.get('/vote/test_election') + self.assertEqual(output.status_code, 302) + self.assertIn( + '/login?next=http%3A%2F%2Flocalhost%2Fvote%2Ftest_election', + output.data) self.setup_db() user = FakeUser(['voters'], username='pingou') - with user_set(fedora_elections.APP, user): - output = self.app.get( - '/vote/test_election3') - self.assertTrue( - 'test election 3 shortdesc' in output.data) - self.assertTrue( - '' - in output.data) - - # Invalid candidate id - data = { - 'Toshio': 1, - 'kevin': 3, - 'Ralph': 2, - 'action': 'preview', - } - - output = self.app.post('/vote/test_election3', data=data) - self.assertEqual(output.status_code, 200) - self.assertEqual( - output.data.count(''), - 3) - - csrf_token = output.data.split( - 'name="csrf_token" type="hidden" value="')[1].split('">')[0] - - # Invalid candidate id - data = { - 'Toshio': 1, - 'Kevin': 3, - 'Ralph': 2, - 'action': 'preview', - 'csrf_token': csrf_token, - } - - output = self.app.post('/vote/test_election3', data=data) - self.assertEqual(output.status_code, 200) - self.assertEqual( - output.data.count(''), - 3) - - # Invalid vote: too low - data = { - '9': -1, - '6': 0, - '5': 2, - 'action': 'preview', - 'csrf_token': csrf_token, - } - - output = self.app.post('/vote/test_election3', data=data) - self.assertEqual(output.status_code, 200) - self.assertTrue( - 'test election 3 shortdesc' in output.data) - self.assertTrue( - '' - in output.data) - self.assertEqual( - output.data.count(''), - 1) - - # Invalid vote: 2 are too high - data = { - '9': 5, - '6': 3, - '5': 2, - 'action': 'preview', - 'csrf_token': csrf_token, - } - - output = self.app.post('/vote/test_election3', data=data) - self.assertEqual(output.status_code, 200) - self.assertTrue( - 'test election 3 shortdesc' in output.data) - self.assertTrue( - '' - in output.data) - self.assertEqual( - output.data.count(''), - 2) - - # Invalid vote: Not numeric - data = { - '9': 'a', - '6': 0, - '5': 2, - 'action': 'preview', - 'csrf_token': csrf_token, - } - - output = self.app.post('/vote/test_election3', data=data) - self.assertEqual(output.status_code, 200) - self.assertTrue( - 'test election 3 shortdesc' in output.data) - self.assertTrue( - '' - in output.data) - self.assertEqual( - output.data.count(''), - 1) - - # Valid input - data = { - '4': 1, - '6': 0, - '5': 2, - 'action': 'preview', - 'csrf_token': csrf_token, - } - - output = self.app.post('/vote/test_election3', data=data) - self.assertEqual(output.status_code, 200) - self.assertTrue( - 'test election 3 shortdesc' in output.data) - self.assertTrue( - '' - in output.data) + with user_set(fedora_elections.APP, user, oidc_id_token='foobar'): + with patch( + 'fedora_elections.OIDC.user_getfield', + MagicMock(return_value=['voters'])): + output = self.app.get( + '/vote/test_election3') + self.assertTrue( + 'test election 3 shortdesc' in output.data) + self.assertTrue( + '' + in output.data) + + # Invalid candidate id + data = { + 'Toshio': 1, + 'kevin': 3, + 'Ralph': 2, + 'action': 'preview', + } + + output = self.app.post('/vote/test_election3', data=data) + self.assertEqual(output.status_code, 200) + self.assertEqual( + output.data.count(''), + 3) + + csrf_token = output.data.split( + 'name="csrf_token" type="hidden" value="')[1].split('">')[0] + + # Invalid candidate id + data = { + 'Toshio': 1, + 'Kevin': 3, + 'Ralph': 2, + 'action': 'preview', + 'csrf_token': csrf_token, + } + + output = self.app.post('/vote/test_election3', data=data) + self.assertEqual(output.status_code, 200) + self.assertEqual( + output.data.count(''), + 3) + + # Invalid vote: too low + data = { + '9': -1, + '6': 0, + '5': 2, + 'action': 'preview', + 'csrf_token': csrf_token, + } + + output = self.app.post('/vote/test_election3', data=data) + self.assertEqual(output.status_code, 200) + self.assertTrue( + 'test election 3 shortdesc' in output.data) + self.assertTrue( + '' + in output.data) + self.assertEqual( + output.data.count(''), + 1) + + # Invalid vote: 2 are too high + data = { + '9': 5, + '6': 3, + '5': 2, + 'action': 'preview', + 'csrf_token': csrf_token, + } + + output = self.app.post('/vote/test_election3', data=data) + self.assertEqual(output.status_code, 200) + self.assertTrue( + 'test election 3 shortdesc' in output.data) + self.assertTrue( + '' + in output.data) + self.assertEqual( + output.data.count(''), + 2) + + # Invalid vote: Not numeric + data = { + '9': 'a', + '6': 0, + '5': 2, + 'action': 'preview', + 'csrf_token': csrf_token, + } + + output = self.app.post('/vote/test_election3', data=data) + self.assertEqual(output.status_code, 200) + self.assertTrue( + 'test election 3 shortdesc' in output.data) + self.assertTrue( + '' + in output.data) + self.assertEqual( + output.data.count(''), + 1) + + # Valid input + data = { + '4': 1, + '6': 0, + '5': 2, + 'action': 'preview', + 'csrf_token': csrf_token, + } + + output = self.app.post('/vote/test_election3', data=data) + self.assertEqual(output.status_code, 200) + self.assertTrue( + 'test election 3 shortdesc' in output.data) + self.assertTrue( + '' + in output.data) def test_vote_range_process(self): """ Test the vote_range function - the voting part. """ - output = self.app.get( - '/vote/test_election', follow_redirects=True) - self.assertEqual(output.status_code, 200) - self.assertTrue( - 'OpenID transaction in progress' in output.data - or 'discoveryfailure' in output.data) + output = self.app.get('/vote/test_election') + self.assertEqual(output.status_code, 302) + self.assertIn( + '/login?next=http%3A%2F%2Flocalhost%2Fvote%2Ftest_election', + output.data) self.setup_db() user = FakeUser(['voters'], username='pingou') - with user_set(fedora_elections.APP, user): - # No csrf token provided - data = { - 'Toshio': 1, - 'Kevin': 3, - 'Ralph': 2, - 'action': 'submit', - } - - output = self.app.post('/vote/test_election3', data=data) - self.assertEqual(output.status_code, 200) - self.assertEqual( - output.data.count(''), - 3) - - csrf_token = output.data.split( - 'name="csrf_token" type="hidden" value="')[1].split('">')[0] - - # Invalid vote: invalid username - data = { - 'Toshio': 1, - 'Kevin': 3, - 'Ralph': 2, - 'action': 'submit', - } - - output = self.app.post('/vote/test_election3', data=data) - self.assertEqual(output.status_code, 200) - self.assertEqual( - output.data.count(''), - 3) - - # Invalid vote: too low - data = { - '4': -1, - '5': 0, - '6': 2, - 'action': 'submit', - 'csrf_token': csrf_token, - } - - output = self.app.post( - '/vote/test_election3', data=data) - self.assertEqual(output.status_code, 200) - self.assertEqual( - output.data.count(''), - 1) - - # Invalid vote: 2 are too high - data = { - '4': 5, - '5': 3, - '6': 2, - 'action': 'submit', - 'csrf_token': csrf_token, - } - - output = self.app.post( - '/vote/test_election3', data=data, follow_redirects=True) - self.assertEqual(output.status_code, 200) - self.assertEqual( - output.data.count(''), - 2) - - # Invalid vote: Not numeric - data = { - '4': 'a', - '5': 0, - '6': 2, - 'action': 'submit', - 'csrf_token': csrf_token, - } - - output = self.app.post( - '/vote/test_election3', data=data, follow_redirects=True) - self.assertEqual(output.status_code, 200) - self.assertEqual( - output.data.count(''), - 1) - - # Valid input - data = { - '4': 1, - '5': 0, - '6': 2, - 'action': 'submit', - 'csrf_token': csrf_token, - } - - output = self.app.post( - '/vote/test_election3', data=data, follow_redirects=True) - self.assertEqual(output.status_code, 200) - self.assertTrue( - 'Your vote has been recorded. Thank you!' - in output.data) - self.assertTrue('Open elections' in output.data) + with user_set(fedora_elections.APP, user, oidc_id_token='foobar'): + with patch( + 'fedora_elections.OIDC.user_getfield', + MagicMock(return_value=['voters'])): + # No csrf token provided + data = { + 'Toshio': 1, + 'Kevin': 3, + 'Ralph': 2, + 'action': 'submit', + } + + output = self.app.post('/vote/test_election3', data=data) + self.assertEqual(output.status_code, 200) + self.assertEqual( + output.data.count(''), + 3) + + csrf_token = output.data.split( + 'name="csrf_token" type="hidden" value="')[1].split('">')[0] + + # Invalid vote: invalid username + data = { + 'Toshio': 1, + 'Kevin': 3, + 'Ralph': 2, + 'action': 'submit', + } + + output = self.app.post('/vote/test_election3', data=data) + self.assertEqual(output.status_code, 200) + self.assertEqual( + output.data.count(''), + 3) + + # Invalid vote: too low + data = { + '4': -1, + '5': 0, + '6': 2, + 'action': 'submit', + 'csrf_token': csrf_token, + } + + output = self.app.post( + '/vote/test_election3', data=data) + self.assertEqual(output.status_code, 200) + self.assertEqual( + output.data.count(''), + 1) + + # Invalid vote: 2 are too high + data = { + '4': 5, + '5': 3, + '6': 2, + 'action': 'submit', + 'csrf_token': csrf_token, + } + + output = self.app.post( + '/vote/test_election3', data=data, follow_redirects=True) + self.assertEqual(output.status_code, 200) + self.assertEqual( + output.data.count(''), + 2) + + # Invalid vote: Not numeric + data = { + '4': 'a', + '5': 0, + '6': 2, + 'action': 'submit', + 'csrf_token': csrf_token, + } + + output = self.app.post( + '/vote/test_election3', data=data, follow_redirects=True) + self.assertEqual(output.status_code, 200) + self.assertEqual( + output.data.count(''), + 1) + + # Valid input + data = { + '4': 1, + '5': 0, + '6': 2, + 'action': 'submit', + 'csrf_token': csrf_token, + } + + output = self.app.post( + '/vote/test_election3', data=data, follow_redirects=True) + self.assertEqual(output.status_code, 200) + self.assertTrue( + 'Your vote has been recorded. Thank you!' + in output.data) + self.assertTrue('Open elections' in output.data) def test_vote_range_revote(self): """ Test the vote_range function - the re-voting part. """ @@ -289,58 +294,61 @@ class FlaskRangeElectionstests(ModelFlasktests): self.setup_db() user = FakeUser(['voters'], username='nerdsville') - with user_set(fedora_elections.APP, user): - retrieve_csrf = self.app.post('/vote/test_election3') - csrf_token = retrieve_csrf.data.split( - 'name="csrf_token" type="hidden" value="')[1].split('">')[0] - data = { - '4': 1, - '5': 0, - '6': 2, - 'action': 'submit', - 'csrf_token': csrf_token, - } - self.app.post('/vote/test_election3', data=data, follow_redirects=True) - vote = fedora_elections.models.Vote - votes = vote.of_user_on_election(self.session, "nerdsville", '3') - self.assertEqual(votes[0].value, 1) - self.assertEqual(votes[1].value, 0) - self.assertEqual(votes[2].value, 2) - #Let's not do repetition of what is tested above we aren't testing the - #functionality of voting as that has already been asserted - - obj = fedora_elections.models.Candidate( # id:16 - election_id=3, - name='Josh', - url='https://fedoraproject.org/wiki/User:Nerdsville', - ) - self.session.add(obj) - self.session.commit() - - - #Next, we need to try revoting - newdata = { - '4': 2, - '5': 1, - '6': 1, - '16': 0, - 'action': 'submit', - 'csrf_token': csrf_token, - } - output = self.app.post('/vote/test_election3', data=newdata, follow_redirects=True) - #Next, we need to check if the vote has been recorded - self.assertEqual(output.status_code, 200) - self.assertTrue( - 'Your vote has been recorded. Thank you!' - in output.data) - self.assertTrue('Open elections' in output.data) - vote = fedora_elections.models.Vote - votes = vote.of_user_on_election(self.session, "nerdsville", '3') - self.assertEqual(votes[0].value, 2) - self.assertEqual(votes[1].value, 1) - self.assertEqual(votes[2].value, 1) - self.assertEqual(votes[3].value, 0) - #If we haven't failed yet, HOORAY! + with user_set(fedora_elections.APP, user, oidc_id_token='foobar'): + with patch( + 'fedora_elections.OIDC.user_getfield', + MagicMock(return_value=['voters'])): + retrieve_csrf = self.app.post('/vote/test_election3') + csrf_token = retrieve_csrf.data.split( + 'name="csrf_token" type="hidden" value="')[1].split('">')[0] + data = { + '4': 1, + '5': 0, + '6': 2, + 'action': 'submit', + 'csrf_token': csrf_token, + } + self.app.post('/vote/test_election3', data=data, follow_redirects=True) + vote = fedora_elections.models.Vote + votes = vote.of_user_on_election(self.session, "nerdsville", '3') + self.assertEqual(votes[0].value, 1) + self.assertEqual(votes[1].value, 0) + self.assertEqual(votes[2].value, 2) + #Let's not do repetition of what is tested above we aren't testing the + #functionality of voting as that has already been asserted + + obj = fedora_elections.models.Candidate( # id:16 + election_id=3, + name='Josh', + url='https://fedoraproject.org/wiki/User:Nerdsville', + ) + self.session.add(obj) + self.session.commit() + + + #Next, we need to try revoting + newdata = { + '4': 2, + '5': 1, + '6': 1, + '16': 0, + 'action': 'submit', + 'csrf_token': csrf_token, + } + output = self.app.post('/vote/test_election3', data=newdata, follow_redirects=True) + #Next, we need to check if the vote has been recorded + self.assertEqual(output.status_code, 200) + self.assertTrue( + 'Your vote has been recorded. Thank you!' + in output.data) + self.assertTrue('Open elections' in output.data) + vote = fedora_elections.models.Vote + votes = vote.of_user_on_election(self.session, "nerdsville", '3') + self.assertEqual(votes[0].value, 2) + self.assertEqual(votes[1].value, 1) + self.assertEqual(votes[2].value, 1) + self.assertEqual(votes[3].value, 0) + #If we haven't failed yet, HOORAY! if __name__ == '__main__': diff --git a/tests/test_flask_select_voting.py b/tests/test_flask_select_voting.py index 61412be..8af5fb8 100644 --- a/tests/test_flask_select_voting.py +++ b/tests/test_flask_select_voting.py @@ -32,6 +32,7 @@ from datetime import time from datetime import timedelta import flask +from mock import patch, MagicMock sys.path.insert(0, os.path.join(os.path.dirname( os.path.abspath(__file__)), '..')) @@ -46,117 +47,121 @@ class FlaskSimpleElectionstests(ModelFlasktests): def test_vote_select(self): """ Test the vote_select function - the preview part. """ - output = self.app.get( - '/vote/test_election', follow_redirects=True) - self.assertEqual(output.status_code, 200) - self.assertTrue( - 'OpenID transaction in progress' in output.data - or 'discoveryfailure' in output.data) + output = self.app.get('/vote/test_election') + self.assertEqual(output.status_code, 302) + self.assertIn( + '/login?next=http%3A%2F%2Flocalhost%2Fvote%2Ftest_election', + output.data) self.setup_db() user = FakeUser(['packager'], username='pingou') - with user_set(fedora_elections.APP, user): - output = self.app.get( - '/vote/test_election6') - self.assertTrue( - 'test election 6 shortdesc' in output.data) - self.assertTrue( - '' - in output.data) - - csrf_token = output.data.split( - 'name="csrf_token" type="hidden" value="')[1].split('">')[0] - - # Invalid vote: No candidate - data = { - 'action': 'preview', - } - - output = self.app.post('/vote/test_election6', data=data) - self.assertEqual(output.status_code, 200) - self.assertTrue( - 'test election 6 shortdesc' in output.data) - - # Invalid vote: Too many candidates - data = { - 'Kevin': True, - 'Toshio': True, - 'action': 'preview', - 'csrf_token': csrf_token, - } - - output = self.app.post('/vote/test_election6', data=data) - self.assertEqual(output.status_code, 200) - self.assertTrue( - 'test election 6 shortdesc' in output.data) - self.assertTrue( - '' - in output.data) - self.assertTrue( - 'Too many candidates submitted' - in output.data) - - # Valid input - data = { - 'Kevin': True, - 'action': 'preview', - 'csrf_token': csrf_token, - } - - output = self.app.post('/vote/test_election6', data=data) - self.assertEqual(output.status_code, 200) - self.assertTrue( - 'test election 6 shortdesc' in output.data) - self.assertTrue( - '' - in output.data) - self.assertTrue( - 'Please confirm your vote!' - in output.data) + with user_set(fedora_elections.APP, user, oidc_id_token='foobar'): + with patch( + 'fedora_elections.OIDC.user_getfield', + MagicMock(return_value=['packager'])): + output = self.app.get( + '/vote/test_election6') + self.assertTrue( + 'test election 6 shortdesc' in output.data) + self.assertTrue( + '' + in output.data) + + csrf_token = output.data.split( + 'name="csrf_token" type="hidden" value="')[1].split('">')[0] + + # Invalid vote: No candidate + data = { + 'action': 'preview', + } + + output = self.app.post('/vote/test_election6', data=data) + self.assertEqual(output.status_code, 200) + self.assertTrue( + 'test election 6 shortdesc' in output.data) + + # Invalid vote: Too many candidates + data = { + 'Kevin': True, + 'Toshio': True, + 'action': 'preview', + 'csrf_token': csrf_token, + } + + output = self.app.post('/vote/test_election6', data=data) + self.assertEqual(output.status_code, 200) + self.assertTrue( + 'test election 6 shortdesc' in output.data) + self.assertTrue( + '' + in output.data) + self.assertTrue( + 'Too many candidates submitted' + in output.data) + + # Valid input + data = { + 'Kevin': True, + 'action': 'preview', + 'csrf_token': csrf_token, + } + + output = self.app.post('/vote/test_election6', data=data) + self.assertEqual(output.status_code, 200) + self.assertTrue( + 'test election 6 shortdesc' in output.data) + self.assertTrue( + '' + in output.data) + self.assertTrue( + 'Please confirm your vote!' + in output.data) def test_vote_select_process(self): """ Test the vote_select function - the voting part. """ - output = self.app.get( - '/vote/test_election', follow_redirects=True) - self.assertEqual(output.status_code, 200) - self.assertTrue( - 'OpenID transaction in progress' in output.data - or 'discoveryfailure' in output.data) + output = self.app.get('/vote/test_election') + self.assertEqual(output.status_code, 302) + self.assertIn( + '/login?next=http%3A%2F%2Flocalhost%2Fvote%2Ftest_election', + output.data) self.setup_db() user = FakeUser(['packager'], username='pingou') - with user_set(fedora_elections.APP, user): - # Invalid candidate id - no csrf - data = { - 'candidate': 1, - 'action': 'submit', - } - - output = self.app.post( - '/vote/test_election6', data=data, - follow_redirects=True) - self.assertEqual(output.status_code, 200) - - csrf_token = output.data.split( - 'name="csrf_token" type="hidden" value="')[1].split('">')[0] - - # Valid input - data = { - 'Toshio': True, - 'action': 'submit', - 'csrf_token': csrf_token, - } - - output = self.app.post( - '/vote/test_election6', data=data, - follow_redirects=True) - self.assertEqual(output.status_code, 200) - self.assertTrue( - 'Your vote has been recorded. Thank you!' - in output.data) - self.assertTrue('Open elections' in output.data) + with user_set(fedora_elections.APP, user, oidc_id_token='foobar'): + with patch( + 'fedora_elections.OIDC.user_getfield', + MagicMock(return_value=['packager'])): + # Invalid candidate id - no csrf + data = { + 'candidate': 1, + 'action': 'submit', + } + + output = self.app.post( + '/vote/test_election6', data=data, + follow_redirects=True) + self.assertEqual(output.status_code, 200) + + csrf_token = output.data.split( + 'name="csrf_token" type="hidden" value="')[1].split('">')[0] + + # Valid input + data = { + 'Toshio': True, + 'action': 'submit', + 'csrf_token': csrf_token, + } + + output = self.app.post( + '/vote/test_election6', data=data, + follow_redirects=True) + self.assertEqual(output.status_code, 200) + self.assertTrue( + 'Your vote has been recorded. Thank you!' + in output.data) + self.assertTrue('Open elections' in output.data) def test_vote_select_revote(self): """ Test the vote_select function - the re-voting part. """ @@ -164,45 +169,47 @@ class FlaskSimpleElectionstests(ModelFlasktests): self.setup_db() user = FakeUser(['voters'], username='nerdsville') - with user_set(fedora_elections.APP, user): - retrieve_csrf = self.app.post('/vote/test_election6') - csrf_token = retrieve_csrf.data.split( - 'name="csrf_token" type="hidden" value="')[1].split('">')[0] - - # Valid input - data = { - 'Kevin': True, - 'action': 'submit', - 'csrf_token': csrf_token, - } - self.app.post('/vote/test_election6', data=data, follow_redirects=True) - vote = fedora_elections.models.Vote - votes = vote.of_user_on_election(self.session, "nerdsville", '6') - self.assertEqual(votes[0].candidate.name, "Toshio") - self.assertEqual(votes[0].value, 0) - self.assertEqual(votes[1].candidate.name, "Kevin") - self.assertEqual(votes[1].value, 1) - - #Next, we need to try revoting - newdata = { - 'Toshio': True, - 'action': 'submit', - 'csrf_token': csrf_token, - } - output = self.app.post('/vote/test_election6', data=newdata, follow_redirects=True) - #Next, we need to check if the vote has been recorded - self.assertEqual(output.status_code, 200) - self.assertTrue( - 'Your vote has been recorded. Thank you!' - in output.data) - self.assertTrue('Open elections' in output.data) - vote = fedora_elections.models.Vote - votes = vote.of_user_on_election(self.session, "nerdsville", '6') - self.assertEqual(votes[0].value, 1) - self.assertEqual(votes[1].value, 0) - - - #If we haven't failed yet, HOORAY! + with user_set(fedora_elections.APP, user, oidc_id_token='foobar'): + with patch( + 'fedora_elections.OIDC.user_getfield', + MagicMock(return_value=['voters'])): + retrieve_csrf = self.app.post('/vote/test_election6') + csrf_token = retrieve_csrf.data.split( + 'name="csrf_token" type="hidden" value="')[1].split('">')[0] + + # Valid input + data = { + 'Kevin': True, + 'action': 'submit', + 'csrf_token': csrf_token, + } + self.app.post('/vote/test_election6', data=data, follow_redirects=True) + vote = fedora_elections.models.Vote + votes = vote.of_user_on_election(self.session, "nerdsville", '6') + self.assertEqual(votes[0].candidate.name, "Toshio") + self.assertEqual(votes[0].value, 0) + self.assertEqual(votes[1].candidate.name, "Kevin") + self.assertEqual(votes[1].value, 1) + + #Next, we need to try revoting + newdata = { + 'Toshio': True, + 'action': 'submit', + 'csrf_token': csrf_token, + } + output = self.app.post('/vote/test_election6', data=newdata, follow_redirects=True) + #Next, we need to check if the vote has been recorded + self.assertEqual(output.status_code, 200) + self.assertTrue( + 'Your vote has been recorded. Thank you!' + in output.data) + self.assertTrue('Open elections' in output.data) + vote = fedora_elections.models.Vote + votes = vote.of_user_on_election(self.session, "nerdsville", '6') + self.assertEqual(votes[0].value, 1) + self.assertEqual(votes[1].value, 0) + + #If we haven't failed yet, HOORAY! if __name__ == '__main__': diff --git a/tests/test_flask_simple_voting.py b/tests/test_flask_simple_voting.py index 4076c85..fe694b7 100644 --- a/tests/test_flask_simple_voting.py +++ b/tests/test_flask_simple_voting.py @@ -32,6 +32,7 @@ from datetime import time from datetime import timedelta import flask +from mock import patch, MagicMock sys.path.insert(0, os.path.join(os.path.dirname( os.path.abspath(__file__)), '..')) @@ -46,177 +47,181 @@ class FlaskSimpleElectionstests(ModelFlasktests): def test_vote_simple(self): """ Test the vote_simple function - the preview part. """ - output = self.app.get( - '/vote/test_election', follow_redirects=True) - self.assertEqual(output.status_code, 200) - self.assertTrue( - 'OpenID transaction in progress' in output.data - or 'discoveryfailure' in output.data) + output = self.app.get('/vote/test_election') + self.assertEqual(output.status_code, 302) + self.assertIn( + '/login?next=http%3A%2F%2Flocalhost%2Fvote%2Ftest_election', + output.data) self.setup_db() user = FakeUser(['packager'], username='pingou') - with user_set(fedora_elections.APP, user): - output = self.app.get( - '/vote/test_election5') - self.assertTrue( - 'test election 5 shortdesc' in output.data) - self.assertTrue( - '' - in output.data) - - # Invalid vote: No candidate - data = { - 'action': 'preview', - } - - output = self.app.post('/vote/test_election5', data=data) - self.assertEqual(output.status_code, 200) - self.assertTrue( - 'test election 5 shortdesc' in output.data) - self.assertTrue( - 'Preview your vote' - in output.data) - self.assertTrue( - '' - in output.data) - - csrf_token = output.data.split( - 'name="csrf_token" type="hidden" value="')[1].split('">')[0] - - # Invalid vote: No candidate - data = { - 'action': 'preview', - 'csrf_token': csrf_token, - } - - output = self.app.post('/vote/test_election5', data=data) - self.assertEqual(output.status_code, 200) - self.assertTrue( - 'test election 5 shortdesc' in output.data) - self.assertTrue( - 'Preview your vote' - in output.data) - self.assertTrue( - '' - in output.data) - - # Invalid vote: Not numeric - data = { - 'candidate': 'a', - 'action': 'preview', - 'csrf_token': csrf_token, - } - - output = self.app.post('/vote/test_election5', data=data) - self.assertEqual(output.status_code, 200) - self.assertTrue( - 'test election 5 shortdesc' in output.data) - self.assertTrue( - '' - in output.data) - - - # Valid input - data = { - 'candidate': 7, - 'action': 'preview', - 'csrf_token': csrf_token, - } - - output = self.app.post('/vote/test_election5', data=data) - self.assertEqual(output.status_code, 200) - self.assertTrue( - 'test election 5 shortdesc' in output.data) - self.assertTrue( - '' - in output.data) - self.assertTrue( - 'Please confirm your vote!' - in output.data) + with user_set(fedora_elections.APP, user, oidc_id_token='foobar'): + with patch( + 'fedora_elections.OIDC.user_getfield', + MagicMock(return_value=['voters'])): + output = self.app.get( + '/vote/test_election5') + self.assertTrue( + 'test election 5 shortdesc' in output.data) + self.assertTrue( + '' + in output.data) + + # Invalid vote: No candidate + data = { + 'action': 'preview', + } + + output = self.app.post('/vote/test_election5', data=data) + self.assertEqual(output.status_code, 200) + self.assertTrue( + 'test election 5 shortdesc' in output.data) + self.assertTrue( + 'Preview your vote' + in output.data) + self.assertTrue( + '' + in output.data) + + csrf_token = output.data.split( + 'name="csrf_token" type="hidden" value="')[1].split('">')[0] + + # Invalid vote: No candidate + data = { + 'action': 'preview', + 'csrf_token': csrf_token, + } + + output = self.app.post('/vote/test_election5', data=data) + self.assertEqual(output.status_code, 200) + self.assertTrue( + 'test election 5 shortdesc' in output.data) + self.assertTrue( + 'Preview your vote' + in output.data) + self.assertTrue( + '' + in output.data) + + # Invalid vote: Not numeric + data = { + 'candidate': 'a', + 'action': 'preview', + 'csrf_token': csrf_token, + } + + output = self.app.post('/vote/test_election5', data=data) + self.assertEqual(output.status_code, 200) + self.assertTrue( + 'test election 5 shortdesc' in output.data) + self.assertTrue( + '' + in output.data) + + + # Valid input + data = { + 'candidate': 7, + 'action': 'preview', + 'csrf_token': csrf_token, + } + + output = self.app.post('/vote/test_election5', data=data) + self.assertEqual(output.status_code, 200) + self.assertTrue( + 'test election 5 shortdesc' in output.data) + self.assertTrue( + '' + in output.data) + self.assertTrue( + 'Please confirm your vote!' + in output.data) def test_vote_simple_process(self): """ Test the vote_simple function - the voting part. """ - output = self.app.get( - '/vote/test_election', follow_redirects=True) - self.assertEqual(output.status_code, 200) - self.assertTrue( - 'OpenID transaction in progress' in output.data - or 'discoveryfailure' in output.data) + output = self.app.get('/vote/test_election') + self.assertEqual(output.status_code, 302) + self.assertIn( + '/login?next=http%3A%2F%2Flocalhost%2Fvote%2Ftest_election', + output.data) self.setup_db() user = FakeUser(['packager'], username='pingou') - with user_set(fedora_elections.APP, user): - # Invalid candidate id - no csrf - data = { - 'candidate': 1, - 'action': 'submit', - } - - output = self.app.post( - '/vote/test_election5', data=data, - follow_redirects=True) - self.assertEqual(output.status_code, 200) - - - csrf_token = output.data.split( - 'name="csrf_token" type="hidden" value="')[1].split('">')[0] - - # Invalid candidate id - data = { - 'candidate': 1, - 'action': 'submit', - 'csrf_token': csrf_token, - } - - output = self.app.post( - '/vote/test_election5', data=data, - follow_redirects=True) - self.assertEqual(output.status_code, 200) - - - # Invalid vote: too low - data = { - 'candidate': -1, - 'action': 'submit', - 'csrf_token': csrf_token, - } - - output = self.app.post( - '/vote/test_election5', data=data, - follow_redirects=True) - self.assertEqual(output.status_code, 200) - - - # Invalid vote: Not numeric - data = { - 'candidate': 'a', - 'action': 'submit', - 'csrf_token': csrf_token, - } - - output = self.app.post( - '/vote/test_election5', data=data, - follow_redirects=True) - self.assertEqual(output.status_code, 200) - - - # Valid input - data = { - 'candidate': 8, - 'action': 'submit', - 'csrf_token': csrf_token, - } - - output = self.app.post( - '/vote/test_election5', data=data, - follow_redirects=True) - self.assertEqual(output.status_code, 200) - self.assertTrue( - 'Your vote has been recorded. Thank you!' - in output.data) - self.assertTrue('Open elections' in output.data) + with user_set(fedora_elections.APP, user, oidc_id_token='foobar'): + with patch( + 'fedora_elections.OIDC.user_getfield', + MagicMock(return_value=['voters'])): + # Invalid candidate id - no csrf + data = { + 'candidate': 1, + 'action': 'submit', + } + + output = self.app.post( + '/vote/test_election5', data=data, + follow_redirects=True) + self.assertEqual(output.status_code, 200) + + + csrf_token = output.data.split( + 'name="csrf_token" type="hidden" value="')[1].split('">')[0] + + # Invalid candidate id + data = { + 'candidate': 1, + 'action': 'submit', + 'csrf_token': csrf_token, + } + + output = self.app.post( + '/vote/test_election5', data=data, + follow_redirects=True) + self.assertEqual(output.status_code, 200) + + + # Invalid vote: too low + data = { + 'candidate': -1, + 'action': 'submit', + 'csrf_token': csrf_token, + } + + output = self.app.post( + '/vote/test_election5', data=data, + follow_redirects=True) + self.assertEqual(output.status_code, 200) + + + # Invalid vote: Not numeric + data = { + 'candidate': 'a', + 'action': 'submit', + 'csrf_token': csrf_token, + } + + output = self.app.post( + '/vote/test_election5', data=data, + follow_redirects=True) + self.assertEqual(output.status_code, 200) + + + # Valid input + data = { + 'candidate': 8, + 'action': 'submit', + 'csrf_token': csrf_token, + } + + output = self.app.post( + '/vote/test_election5', data=data, + follow_redirects=True) + self.assertEqual(output.status_code, 200) + self.assertTrue( + 'Your vote has been recorded. Thank you!' + in output.data) + self.assertTrue('Open elections' in output.data) def test_vote_simple_revote(self): """ Test the vote_simple function - the re-voting part. """ @@ -224,43 +229,46 @@ class FlaskSimpleElectionstests(ModelFlasktests): self.setup_db() user = FakeUser(['voters'], username='nerdsville') - with user_set(fedora_elections.APP, user): - retrieve_csrf = self.app.post('/vote/test_election5') - csrf_token = retrieve_csrf.data.split( - 'name="csrf_token" type="hidden" value="')[1].split('">')[0] - # Valid input - data = { - 'candidate': 8, - 'action': 'submit', - 'csrf_token': csrf_token, - } - - self.app.post('/vote/test_election5', data=data, follow_redirects=True) - vote = fedora_elections.models.Vote - votes = vote.of_user_on_election(self.session, "nerdsville", '5') - self.assertEqual(votes[0].candidate_id, 8) - #Let's not do repetition of what is tested above we aren't testing the - #functionality of voting as that has already been asserted - - #Next, we need to try revoting - # Valid input - newdata = { - 'candidate': 9, - 'action': 'submit', - 'csrf_token': csrf_token, - } - output = self.app.post('/vote/test_election5', data=newdata, follow_redirects=True) - #Next, we need to check if the vote has been recorded - self.assertEqual(output.status_code, 200) - self.assertTrue( - 'Your vote has been recorded. Thank you!' - in output.data) - self.assertTrue('Open elections' in output.data) - vote = fedora_elections.models.Vote - votes = vote.of_user_on_election(self.session, "nerdsville", '5') - self.assertEqual(votes[0].candidate_id, 9) - - #If we haven't failed yet, HOORAY! + with user_set(fedora_elections.APP, user, oidc_id_token='foobar'): + with patch( + 'fedora_elections.OIDC.user_getfield', + MagicMock(return_value=['voters'])): + retrieve_csrf = self.app.post('/vote/test_election5') + csrf_token = retrieve_csrf.data.split( + 'name="csrf_token" type="hidden" value="')[1].split('">')[0] + # Valid input + data = { + 'candidate': 8, + 'action': 'submit', + 'csrf_token': csrf_token, + } + + self.app.post('/vote/test_election5', data=data, follow_redirects=True) + vote = fedora_elections.models.Vote + votes = vote.of_user_on_election(self.session, "nerdsville", '5') + self.assertEqual(votes[0].candidate_id, 8) + #Let's not do repetition of what is tested above we aren't testing the + #functionality of voting as that has already been asserted + + #Next, we need to try revoting + # Valid input + newdata = { + 'candidate': 9, + 'action': 'submit', + 'csrf_token': csrf_token, + } + output = self.app.post('/vote/test_election5', data=newdata, follow_redirects=True) + #Next, we need to check if the vote has been recorded + self.assertEqual(output.status_code, 200) + self.assertTrue( + 'Your vote has been recorded. Thank you!' + in output.data) + self.assertTrue('Open elections' in output.data) + vote = fedora_elections.models.Vote + votes = vote.of_user_on_election(self.session, "nerdsville", '5') + self.assertEqual(votes[0].candidate_id, 9) + + #If we haven't failed yet, HOORAY! if __name__ == '__main__': From 22e8ebec509bd83e2889fb02fb1173f102253b84 Mon Sep 17 00:00:00 2001 From: Pierre-Yves Chibon Date: Mar 05 2019 19:41:52 +0000 Subject: [PATCH 15/24] Document how to register the app for dev Signed-off-by: Pierre-Yves Chibon --- diff --git a/README.md b/README.md index 4d5c999..325ada6 100644 --- a/README.md +++ b/README.md @@ -43,7 +43,7 @@ vagrant ssh [vagrant@localhost ~]$ pushd /vagrant/; ./runserver.py --host "0.0.0.0"; ``` -Once that is running, go to [localhost:5002](http://localhost:5002/) in your +Once that is running, go to [localhost:5005](http://localhost:5005/) in your browser to see your running Fedora Elections test instance. ### A note about fonts @@ -116,6 +116,32 @@ Run: python createdb.py ``` +### Register the application using openid-connect + +Run: + +``` +oidc-register https://iddev.fedorainfracloud.org/ http://localhost:5005/oidc_callback +``` + +Copy the corresponding ``client_secrets.json`` in the sources: + +``` +cp client_secrets.json fedora_elections/client_secrets.json +``` + +### Create a local configuration file + +Run: + +``` +cat > config < Date: Mar 06 2019 11:29:43 +0000 Subject: [PATCH 16/24] Add mock as a requirement to run the tests Signed-off-by: Pierre-Yves Chibon --- diff --git a/tox.ini b/tox.ini index e370e22..8eff405 100644 --- a/tox.ini +++ b/tox.ini @@ -7,6 +7,7 @@ usedevelop = True passenv = HOME deps = -rrequirements.txt + mock nose coverage setenv = From 21c25540f4430b710e06ac8a69eeed43e3b553df Mon Sep 17 00:00:00 2001 From: Pierre-Yves Chibon Date: Mar 06 2019 11:29:55 +0000 Subject: [PATCH 17/24] Adjust the list of dependency, drop python-openid* for flask-oidc Signed-off-by: Pierre-Yves Chibon --- diff --git a/requirements.txt b/requirements.txt index 61553e1..e70a503 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,12 +1,10 @@ # Used for when working from a virtualenv. # Use this file by running "$ pip install -r requirements.txt" Flask>=0.10 +flask-oidc Flask-wtf kitchen python-fedora -python-openid -python-openid-cla -python-openid-teams SQLAlchemy wtforms arrow From 5cb7927c04ce341aaa6d6000600acac51b0a6231 Mon Sep 17 00:00:00 2001 From: Pierre-Yves Chibon Date: Mar 06 2019 11:30:20 +0000 Subject: [PATCH 18/24] Port the flask_admin unit-tests to OIDC. Basically, we need to patch user_getfield to that it returns the admin group making the user an admin, which is necessary to test these endpoints. Signed-off-by: Pierre-Yves Chibon --- diff --git a/tests/test_flask_admin.py b/tests/test_flask_admin.py index b7d5bc3..45ebcc6 100644 --- a/tests/test_flask_admin.py +++ b/tests/test_flask_admin.py @@ -32,6 +32,7 @@ from datetime import time from datetime import timedelta import flask +from mock import patch, MagicMock sys.path.insert(0, os.path.join(os.path.dirname( os.path.abspath(__file__)), '..')) @@ -49,16 +50,39 @@ class FlaskAdmintests(ModelFlasktests): user = FakeUser( fedora_elections.APP.config['FEDORA_ELECTIONS_ADMIN_GROUP'], username='toshio') - with user_set(fedora_elections.APP, user): - output = self.app.get('/admin/election_test/') - self.assertEqual(output.status_code, 404) + with user_set(fedora_elections.APP, user, oidc_id_token='foobar'): + with patch( + 'fedora_elections.OIDC.user_getfield', + MagicMock(return_value=['elections'])): + output = self.app.get('/admin/election_test/') + self.assertEqual(output.status_code, 404) + + self.setup_db() + + with user_set(fedora_elections.APP, user, oidc_id_token='foobar'): + with patch( + 'fedora_elections.OIDC.user_getfield', + MagicMock(return_value=['elections'])): + output = self.app.get('/admin/test_election/') + self.assertEqual(output.status_code, 200) + self.assertTrue('Candidates 3' in output.data) + + def test_admin_no_cla(self): + """ Test the admin_new_election function. """ self.setup_db() - with user_set(fedora_elections.APP, user): - output = self.app.get('/admin/test_election/') - self.assertEqual(output.status_code, 200) - self.assertTrue('Candidates 3' in output.data) + user = FakeUser( + fedora_elections.APP.config['FEDORA_ELECTIONS_ADMIN_GROUP'], + username='toshio') + + user.cla_done = False + with user_set(fedora_elections.APP, user, oidc_id_token='foobar'): + with patch( + 'fedora_elections.OIDC.user_getfield', + MagicMock(return_value=['elections'])): + output = self.app.get('/admin/new') + self.assertEqual(output.status_code, 403) def test_admin_new_election(self): """ Test the admin_new_election function. """ @@ -67,410 +91,421 @@ class FlaskAdmintests(ModelFlasktests): user = FakeUser( fedora_elections.APP.config['FEDORA_ELECTIONS_ADMIN_GROUP'], username='toshio') - with user_set(fedora_elections.APP, user): - output = self.app.get('/admin/new') - self.assertEqual(output.status_code, 200) - self.assertTrue( - '

Create new election

' in output.data) - if self.get_wtforms_version() >= (2, 2): - self.assertIn( - 'input class="form-control" id="shortdesc" ' - 'name="shortdesc" required type="text" ', output.data) - else: - self.assertIn( - 'input class="form-control" id="shortdesc" ' - 'name="shortdesc" type="text" ', output.data) - - # No csrf provided - data = { - 'alias': 'new_election', - 'shortdesc': 'new election shortdesc', - 'description': 'new election description', - 'voting_type': 'simple', - 'url': 'https://fedoraproject.org', - 'start_date': TODAY + timedelta(days=2), - 'end_date': TODAY + timedelta(days=4), - 'seats_elected': '2', - 'candidates_are_fasusers': False, - 'embargoed': True, - } - - output = self.app.post('/admin/new', data=data) - self.assertEqual(output.status_code, 200) - self.assertTrue( - '

Create new election

' in output.data) - if self.get_wtforms_version() >= (2, 2): - self.assertIn( - 'input class="form-control" id="shortdesc" ' - 'name="shortdesc" required type="text" ', output.data) - else: - self.assertIn( - 'input class="form-control" id="shortdesc" ' - 'name="shortdesc" type="text" ', output.data) - - csrf_token = self.get_csrf(output=output) - - # Description missing - data = { - 'alias': 'new_election', - 'shortdesc': 'new election shortdesc', - 'voting_type': 'simple', - 'url': 'https://fedoraproject.org', - 'start_date': TODAY + timedelta(days=2), - 'end_date': TODAY + timedelta(days=4), - 'seats_elected': '2', - 'candidates_are_fasusers': False, - 'embargoed': True, - 'csrf_token': csrf_token, - } - - output = self.app.post('/admin/new', data=data) - self.assertEqual(output.status_code, 200) - self.assertTrue( - '

Create new election

' in output.data) - if self.get_wtforms_version() >= (2, 2): - self.assertIn( - 'input class="form-control" id="shortdesc" ' - 'name="shortdesc" required type="text" ', output.data) - else: - self.assertIn( - 'input class="form-control" id="shortdesc" ' - 'name="shortdesc" type="text" ', output.data) - self.assertTrue( - '' - in output.data) - - # Invalid alias - data = { - 'alias': 'new', - 'shortdesc': 'new election shortdesc', - 'description': 'new election description', - 'voting_type': 'simple', - 'url': 'https://fedoraproject.org', - 'start_date': TODAY + timedelta(days=2), - 'end_date': TODAY + timedelta(days=4), - 'seats_elected': 2, - 'candidates_are_fasusers': False, - 'embargoed': True, - 'csrf_token': csrf_token, - } - - output = self.app.post('/admin/new', data=data) - self.assertEqual(output.status_code, 200) - self.assertTrue( - '

Create new election

' in output.data) - if self.get_wtforms_version() >= (2, 2): - self.assertIn( - 'input class="form-control" id="shortdesc" ' - 'name="shortdesc" required type="text" ', output.data) - else: - self.assertIn( - 'input class="form-control" id="shortdesc" ' - 'name="shortdesc" type="text" ', output.data) - self.assertTrue( - '' - in output.data) - - # Invalid: end_date earlier than start_date - data = { - 'alias': 'new_election', - 'shortdesc': 'new election shortdesc', - 'description': 'new election description', - 'voting_type': 'simple', - 'url': 'https://fedoraproject.org', - 'start_date': TODAY + timedelta(days=6), - 'end_date': TODAY + timedelta(days=4), - 'seats_elected': 2, - 'candidates_are_fasusers': False, - 'embargoed': True, - 'csrf_token': csrf_token, - } - - output = self.app.post('/admin/new', data=data) - self.assertEqual(output.status_code, 200) - self.assertTrue( - '

Create new election

' in output.data) - if self.get_wtforms_version() >= (2, 2): + + with user_set(fedora_elections.APP, user, oidc_id_token='foobar'): + with patch( + 'fedora_elections.OIDC.user_getfield', + MagicMock(return_value=['elections'])): + output = self.app.get('/admin/new') + self.assertEqual(output.status_code, 200) + self.assertTrue( + '

Create new election

' in output.data) + if self.get_wtforms_version() >= (2, 2): + self.assertIn( + 'input class="form-control" id="shortdesc" ' + 'name="shortdesc" required type="text" ', output.data) + else: + self.assertIn( + 'input class="form-control" id="shortdesc" ' + 'name="shortdesc" type="text" ', output.data) + + # No csrf provided + data = { + 'alias': 'new_election', + 'shortdesc': 'new election shortdesc', + 'description': 'new election description', + 'voting_type': 'simple', + 'url': 'https://fedoraproject.org', + 'start_date': TODAY + timedelta(days=2), + 'end_date': TODAY + timedelta(days=4), + 'seats_elected': '2', + 'candidates_are_fasusers': False, + 'embargoed': True, + } + + output = self.app.post('/admin/new', data=data) + self.assertEqual(output.status_code, 200) + self.assertTrue( + '

Create new election

' in output.data) + if self.get_wtforms_version() >= (2, 2): + self.assertIn( + 'input class="form-control" id="shortdesc" ' + 'name="shortdesc" required type="text" ', output.data) + else: + self.assertIn( + 'input class="form-control" id="shortdesc" ' + 'name="shortdesc" type="text" ', output.data) + + csrf_token = self.get_csrf(output=output) + + # Description missing + data = { + 'alias': 'new_election', + 'shortdesc': 'new election shortdesc', + 'voting_type': 'simple', + 'url': 'https://fedoraproject.org', + 'start_date': TODAY + timedelta(days=2), + 'end_date': TODAY + timedelta(days=4), + 'seats_elected': '2', + 'candidates_are_fasusers': False, + 'embargoed': True, + 'csrf_token': csrf_token, + } + + output = self.app.post('/admin/new', data=data) + self.assertEqual(output.status_code, 200) + self.assertTrue( + '

Create new election

' in output.data) + if self.get_wtforms_version() >= (2, 2): + self.assertIn( + 'input class="form-control" id="shortdesc" ' + 'name="shortdesc" required type="text" ', output.data) + else: + self.assertIn( + 'input class="form-control" id="shortdesc" ' + 'name="shortdesc" type="text" ', output.data) + self.assertTrue( + '' + in output.data) + + # Invalid alias + data = { + 'alias': 'new', + 'shortdesc': 'new election shortdesc', + 'description': 'new election description', + 'voting_type': 'simple', + 'url': 'https://fedoraproject.org', + 'start_date': TODAY + timedelta(days=2), + 'end_date': TODAY + timedelta(days=4), + 'seats_elected': 2, + 'candidates_are_fasusers': False, + 'embargoed': True, + 'csrf_token': csrf_token, + } + + output = self.app.post('/admin/new', data=data) + self.assertEqual(output.status_code, 200) + self.assertTrue( + '

Create new election

' in output.data) + if self.get_wtforms_version() >= (2, 2): + self.assertIn( + 'input class="form-control" id="shortdesc" ' + 'name="shortdesc" required type="text" ', output.data) + else: + self.assertIn( + 'input class="form-control" id="shortdesc" ' + 'name="shortdesc" type="text" ', output.data) + self.assertTrue( + '' + in output.data) + + # Invalid: end_date earlier than start_date + data = { + 'alias': 'new_election', + 'shortdesc': 'new election shortdesc', + 'description': 'new election description', + 'voting_type': 'simple', + 'url': 'https://fedoraproject.org', + 'start_date': TODAY + timedelta(days=6), + 'end_date': TODAY + timedelta(days=4), + 'seats_elected': 2, + 'candidates_are_fasusers': False, + 'embargoed': True, + 'csrf_token': csrf_token, + } + + output = self.app.post('/admin/new', data=data) + self.assertEqual(output.status_code, 200) + self.assertTrue( + '

Create new election

' in output.data) + if self.get_wtforms_version() >= (2, 2): + self.assertIn( + 'input class="form-control" id="shortdesc" ' + 'name="shortdesc" required type="text" ', output.data) + else: + self.assertIn( + 'input class="form-control" id="shortdesc" ' + 'name="shortdesc" type="text" ', output.data) + self.assertTrue( + 'class="form-control-feedback">End date must be later than start date.' + in output.data) + + # Invalid: alias already taken + data = { + 'alias': 'test_election', + 'shortdesc': 'new election shortdesc', + 'description': 'new election description', + 'voting_type': 'simple', + 'url': 'https://fedoraproject.org', + 'start_date': TODAY + timedelta(days=6), + 'end_date': TODAY + timedelta(days=4), + 'seats_elected': 2, + 'candidates_are_fasusers': False, + 'embargoed': True, + 'csrf_token': csrf_token, + } + + output = self.app.post('/admin/new', data=data) + self.assertEqual(output.status_code, 200) + self.assertTrue( + '

Create new election

' in output.data) + if self.get_wtforms_version() >= (2, 2): + self.assertIn( + 'input class="form-control" id="shortdesc" ' + 'name="shortdesc" required type="text" ', output.data) + else: + self.assertIn( + 'input class="form-control" id="shortdesc" ' + 'name="shortdesc" type="text" ', output.data) + self.assertTrue( + '' in output.data) + + # Invalid: shortdesc already taken + data = { + 'alias': 'new_election', + 'shortdesc': 'test election shortdesc', + 'description': 'new election description', + 'voting_type': 'simple', + 'url': 'https://fedoraproject.org', + 'start_date': TODAY + timedelta(days=6), + 'end_date': TODAY + timedelta(days=4), + 'seats_elected': 2, + 'candidates_are_fasusers': False, + 'embargoed': True, + 'csrf_token': csrf_token, + } + + output = self.app.post('/admin/new', data=data) + self.assertEqual(output.status_code, 200) + self.assertTrue( + '

Create new election

' in output.data) + if self.get_wtforms_version() >= (2, 2): + self.assertIn( + 'input class="form-control" id="shortdesc" ' + 'name="shortdesc" required type="text" ', output.data) + else: + self.assertIn( + 'input class="form-control" id="shortdesc" ' + 'name="shortdesc" type="text" ', output.data) + self.assertTrue( + '' in output.data) + + # All good - max_votes is ignored as it is not a integer + data = { + 'alias': 'new_election', + 'shortdesc': 'new election shortdesc', + 'description': 'new election description', + 'voting_type': 'simple', + 'url': 'https://fedoraproject.org', + 'start_date': TODAY + timedelta(days=2), + 'end_date': TODAY + timedelta(days=4), + 'seats_elected': 2, + 'candidates_are_fasusers': False, + 'embargoed': True, + 'admin_grp': 'testers, sysadmin-main,,', + 'lgl_voters': 'testers, packager,,', + 'max_votes': 'wrong', + 'csrf_token': csrf_token, + } + + output = self.app.post( + '/admin/new', data=data, follow_redirects=True) + self.assertEqual(output.status_code, 200) + self.assertTrue( + 'Election "new_election" added' + in output.data) + self.assertTrue( + 'There are no candidates.' in output.data) self.assertIn( - 'input class="form-control" id="shortdesc" ' - 'name="shortdesc" required type="text" ', output.data) - else: + 'input class="form-control" id="admin_grp" ' + 'name="admin_grp" type="text" ' + 'value="sysadmin-main, testers">', output.data) self.assertIn( - 'input class="form-control" id="shortdesc" ' - 'name="shortdesc" type="text" ', output.data) - self.assertTrue( - 'class="form-control-feedback">End date must be later than start date.' - in output.data) - - # Invalid: alias already taken - data = { - 'alias': 'test_election', - 'shortdesc': 'new election shortdesc', - 'description': 'new election description', - 'voting_type': 'simple', - 'url': 'https://fedoraproject.org', - 'start_date': TODAY + timedelta(days=6), - 'end_date': TODAY + timedelta(days=4), - 'seats_elected': 2, - 'candidates_are_fasusers': False, - 'embargoed': True, - 'csrf_token': csrf_token, - } - - output = self.app.post('/admin/new', data=data) - self.assertEqual(output.status_code, 200) - self.assertTrue( - '

Create new election

' in output.data) - if self.get_wtforms_version() >= (2, 2): + 'input class="form-control" id="lgl_voters" ' + 'name="lgl_voters" type="text" ' + 'value="packager, testers">', output.data) self.assertIn( - 'input class="form-control" id="shortdesc" ' - 'name="shortdesc" required type="text" ', output.data) - else: + 'input class="form-control" id="max_votes" ' + 'name="max_votes" type="text" ' + 'value="">', output.data) + + # All good - max_votes is ignored as it is not a integer + data = { + 'alias': 'new_election2', + 'shortdesc': 'new election2 shortdesc', + 'description': 'new election2 description', + 'voting_type': 'simple', + 'url': 'https://fedoraproject.org', + 'start_date': TODAY + timedelta(days=2), + 'end_date': TODAY + timedelta(days=4), + 'seats_elected': 2, + 'candidates_are_fasusers': False, + 'embargoed': True, + 'admin_grp': 'testers, , sysadmin-main,,', + 'lgl_voters': 'testers, packager,,,', + 'csrf_token': csrf_token, + } + + output = self.app.post( + '/admin/new', data=data, follow_redirects=True) + self.assertEqual(output.status_code, 200) + self.assertTrue( + 'Election "new_election2" added' + in output.data) + self.assertTrue( + 'There are no candidates.' in output.data) self.assertIn( - 'input class="form-control" id="shortdesc" ' - 'name="shortdesc" type="text" ', output.data) - self.assertTrue( - '' in output.data) - - # Invalid: shortdesc already taken - data = { - 'alias': 'new_election', - 'shortdesc': 'test election shortdesc', - 'description': 'new election description', - 'voting_type': 'simple', - 'url': 'https://fedoraproject.org', - 'start_date': TODAY + timedelta(days=6), - 'end_date': TODAY + timedelta(days=4), - 'seats_elected': 2, - 'candidates_are_fasusers': False, - 'embargoed': True, - 'csrf_token': csrf_token, - } - - output = self.app.post('/admin/new', data=data) - self.assertEqual(output.status_code, 200) - self.assertTrue( - '

Create new election

' in output.data) - if self.get_wtforms_version() >= (2, 2): + 'input class="form-control" id="admin_grp" ' + 'name="admin_grp" type="text" ' + 'value="sysadmin-main, testers">', output.data) self.assertIn( - 'input class="form-control" id="shortdesc" ' - 'name="shortdesc" required type="text" ', output.data) - else: + 'input class="form-control" id="lgl_voters" ' + 'name="lgl_voters" type="text" ' + 'value="packager, testers">', output.data) self.assertIn( - 'input class="form-control" id="shortdesc" ' - 'name="shortdesc" type="text" ', output.data) - self.assertTrue( - '' in output.data) - - # All good - max_votes is ignored as it is not a integer - data = { - 'alias': 'new_election', - 'shortdesc': 'new election shortdesc', - 'description': 'new election description', - 'voting_type': 'simple', - 'url': 'https://fedoraproject.org', - 'start_date': TODAY + timedelta(days=2), - 'end_date': TODAY + timedelta(days=4), - 'seats_elected': 2, - 'candidates_are_fasusers': False, - 'embargoed': True, - 'admin_grp': 'testers, sysadmin-main,,', - 'lgl_voters': 'testers, packager,,', - 'max_votes': 'wrong', - 'csrf_token': csrf_token, - } - - output = self.app.post( - '/admin/new', data=data, follow_redirects=True) - self.assertEqual(output.status_code, 200) - self.assertTrue( - 'Election "new_election" added' - in output.data) - self.assertTrue( - 'There are no candidates.' in output.data) - self.assertIn( - 'input class="form-control" id="admin_grp" ' - 'name="admin_grp" type="text" ' - 'value="sysadmin-main, testers">', output.data) - self.assertIn( - 'input class="form-control" id="lgl_voters" ' - 'name="lgl_voters" type="text" ' - 'value="packager, testers">', output.data) - self.assertIn( - 'input class="form-control" id="max_votes" ' - 'name="max_votes" type="text" ' - 'value="">', output.data) - - # All good - max_votes is ignored as it is not a integer - data = { - 'alias': 'new_election2', - 'shortdesc': 'new election2 shortdesc', - 'description': 'new election2 description', - 'voting_type': 'simple', - 'url': 'https://fedoraproject.org', - 'start_date': TODAY + timedelta(days=2), - 'end_date': TODAY + timedelta(days=4), - 'seats_elected': 2, - 'candidates_are_fasusers': False, - 'embargoed': True, - 'admin_grp': 'testers, , sysadmin-main,,', - 'lgl_voters': 'testers, packager,,,', - 'csrf_token': csrf_token, - } - - output = self.app.post( - '/admin/new', data=data, follow_redirects=True) - self.assertEqual(output.status_code, 200) - self.assertTrue( - 'Election "new_election2" added' - in output.data) - self.assertTrue( - 'There are no candidates.' in output.data) - self.assertIn( - 'input class="form-control" id="admin_grp" ' - 'name="admin_grp" type="text" ' - 'value="sysadmin-main, testers">', output.data) - self.assertIn( - 'input class="form-control" id="lgl_voters" ' - 'name="lgl_voters" type="text" ' - 'value="packager, testers">', output.data) - self.assertIn( - 'input class="form-control" id="max_votes" ' - 'name="max_votes" type="text" ' - 'value="">', output.data) + 'input class="form-control" id="max_votes" ' + 'name="max_votes" type="text" ' + 'value="">', output.data) def test_admin_edit_election(self): """ Test the admin_edit_election function. """ user = FakeUser( fedora_elections.APP.config['FEDORA_ELECTIONS_ADMIN_GROUP'], username='toshio') - with user_set(fedora_elections.APP, user): - output = self.app.get('/admin/test_election/') - self.assertEqual(output.status_code, 404) + + with user_set(fedora_elections.APP, user, oidc_id_token='foobar'): + with patch( + 'fedora_elections.OIDC.user_getfield', + MagicMock(return_value=['elections'])): + output = self.app.get('/admin/test_election/') + self.assertEqual(output.status_code, 404) self.setup_db() - with user_set(fedora_elections.APP, user): - output = self.app.get('/admin/test_election/') - self.assertEqual(output.status_code, 200) - self.assertTrue( - 'Election Details' in output.data) - if self.get_wtforms_version() >= (2, 2): + with user_set(fedora_elections.APP, user, oidc_id_token='foobar'): + with patch( + 'fedora_elections.OIDC.user_getfield', + MagicMock(return_value=['elections'])): + output = self.app.get('/admin/test_election/') + self.assertEqual(output.status_code, 200) + self.assertTrue( + 'Election Details' in output.data) + if self.get_wtforms_version() >= (2, 2): + self.assertIn( + 'input class="form-control" id="shortdesc" ' + 'name="shortdesc" required type="text" ', output.data) + else: + self.assertIn( + 'input class="form-control" id="shortdesc" ' + 'name="shortdesc" type="text" ', output.data) + + data = { + 'alias': 'test_election', + 'shortdesc': 'test election shortdesc', + 'description': 'test election description', + 'voting_type': 'simple', + 'url': 'https://fedoraproject.org', + 'start_date': TODAY - timedelta(days=10), + 'end_date': TODAY - timedelta(days=8), + 'seats_elected': '2', + 'candidates_are_fasusers': False, + 'embargoed': False, + } + + output = self.app.post('/admin/test_election/', data=data) + self.assertEqual(output.status_code, 200) + self.assertTrue( + 'Election Details' in output.data) + if self.get_wtforms_version() >= (2, 2): + self.assertIn( + 'input class="form-control" id="shortdesc" ' + 'name="shortdesc" required type="text" ', output.data) + else: + self.assertIn( + 'input class="form-control" id="shortdesc" ' + 'name="shortdesc" type="text" ', output.data) + + csrf_token = self.get_csrf() + + data = { + 'alias': 'test_election', + 'voting_type': 'simple', + 'url': 'https://fedoraproject.org', + 'start_date': TODAY - timedelta(days=10), + 'end_date': TODAY - timedelta(days=8), + 'seats_elected': '2', + 'candidates_are_fasusers': False, + 'embargoed': False, + 'csrf_token': csrf_token, + } + + output = self.app.post( + '/admin/test_election/', data=data) + self.assertEqual(output.status_code, 200) + self.assertTrue( + 'Election Details' in output.data) + if self.get_wtforms_version() >= (2, 2): + self.assertIn( + 'input class="form-control" id="shortdesc" ' + 'name="shortdesc" required type="text" ', output.data) + else: + self.assertIn( + 'input class="form-control" id="shortdesc" ' + 'name="shortdesc" type="text" ', output.data) self.assertIn( - 'input class="form-control" id="shortdesc" ' - 'name="shortdesc" required type="text" ', output.data) - else: - self.assertIn( - 'input class="form-control" id="shortdesc" ' - 'name="shortdesc" type="text" ', output.data) - - data = { - 'alias': 'test_election', - 'shortdesc': 'test election shortdesc', - 'description': 'test election description', - 'voting_type': 'simple', - 'url': 'https://fedoraproject.org', - 'start_date': TODAY - timedelta(days=10), - 'end_date': TODAY - timedelta(days=8), - 'seats_elected': '2', - 'candidates_are_fasusers': False, - 'embargoed': False, - } - - output = self.app.post('/admin/test_election/', data=data) - self.assertEqual(output.status_code, 200) - self.assertTrue( - 'Election Details' in output.data) - if self.get_wtforms_version() >= (2, 2): - self.assertIn( - 'input class="form-control" id="shortdesc" ' - 'name="shortdesc" required type="text" ', output.data) - else: - self.assertIn( - 'input class="form-control" id="shortdesc" ' - 'name="shortdesc" type="text" ', output.data) - - csrf_token = self.get_csrf() - - data = { - 'alias': 'test_election', - 'voting_type': 'simple', - 'url': 'https://fedoraproject.org', - 'start_date': TODAY - timedelta(days=10), - 'end_date': TODAY - timedelta(days=8), - 'seats_elected': '2', - 'candidates_are_fasusers': False, - 'embargoed': False, - 'csrf_token': csrf_token, - } - - output = self.app.post( - '/admin/test_election/', data=data) - self.assertEqual(output.status_code, 200) - self.assertTrue( - 'Election Details' in output.data) - if self.get_wtforms_version() >= (2, 2): - self.assertIn( - 'input class="form-control" id="shortdesc" ' - 'name="shortdesc" required type="text" ', output.data) - else: - self.assertIn( - 'input class="form-control" id="shortdesc" ' - 'name="shortdesc" type="text" ', output.data) - self.assertIn( - '', - output.data) - - # Check election before edit - output = self.app.get('/admin/test_election/') - self.assertEqual(output.status_code, 200) - self.assertTrue('Candidates 3' in output.data) - if self.get_wtforms_version() >= (2, 2): - self.assertIn( - 'input class="form-control" id="seats_elected" ' - 'name="seats_elected" required type="text" ' - 'value="1">', output.data) - else: - self.assertIn( - 'input class="form-control" id="seats_elected" ' - 'name="seats_elected" type="text" ' - 'value="1">', output.data) - - data = { - 'alias': 'test_election', - 'shortdesc': 'test election shortdesc', - 'description': 'test election description', - 'voting_type': 'simple', - 'url': 'https://fedoraproject.org', - 'start_date': TODAY - timedelta(days=10), - 'end_date': TODAY - timedelta(days=8), - 'seats_elected': '2', - 'candidates_are_fasusers': False, - 'embargoed': False, - 'max_votes': 'wrong', - 'csrf_token': csrf_token, - } - - output = self.app.post( - '/admin/test_election/', data=data, follow_redirects=True) - self.assertEqual(output.status_code, 200) - self.assertTrue( - 'Election "test_election" saved' - in output.data) - # We edited the seats_elected from 1 to 2 - if self.get_wtforms_version() >= (2, 2): - self.assertIn( - 'input class="form-control" id="seats_elected" ' - 'name="seats_elected" required type="text" ' - 'value="2">', output.data) - else: - self.assertIn( - 'input class="form-control" id="seats_elected" ' - 'name="seats_elected" type="text" ' - 'value="2">', output.data) - self.assertTrue('Candidates 3' in output.data) + '', + output.data) + + # Check election before edit + output = self.app.get('/admin/test_election/') + self.assertEqual(output.status_code, 200) + self.assertTrue('Candidates 3' in output.data) + if self.get_wtforms_version() >= (2, 2): + self.assertIn( + 'input class="form-control" id="seats_elected" ' + 'name="seats_elected" required type="text" ' + 'value="1">', output.data) + else: + self.assertIn( + 'input class="form-control" id="seats_elected" ' + 'name="seats_elected" type="text" ' + 'value="1">', output.data) + + data = { + 'alias': 'test_election', + 'shortdesc': 'test election shortdesc', + 'description': 'test election description', + 'voting_type': 'simple', + 'url': 'https://fedoraproject.org', + 'start_date': TODAY - timedelta(days=10), + 'end_date': TODAY - timedelta(days=8), + 'seats_elected': '2', + 'candidates_are_fasusers': False, + 'embargoed': False, + 'max_votes': 'wrong', + 'csrf_token': csrf_token, + } + + output = self.app.post( + '/admin/test_election/', data=data, follow_redirects=True) + self.assertEqual(output.status_code, 200) + self.assertTrue( + 'Election "test_election" saved' + in output.data) + # We edited the seats_elected from 1 to 2 + if self.get_wtforms_version() >= (2, 2): + self.assertIn( + 'input class="form-control" id="seats_elected" ' + 'name="seats_elected" required type="text" ' + 'value="2">', output.data) + else: + self.assertIn( + 'input class="form-control" id="seats_elected" ' + 'name="seats_elected" type="text" ' + 'value="2">', output.data) + self.assertTrue('Candidates 3' in output.data) def test_admin_edit_election_admin_groups(self): """ Test the admin_edit_election function when editing admin groups. @@ -478,109 +513,116 @@ class FlaskAdmintests(ModelFlasktests): user = FakeUser( fedora_elections.APP.config['FEDORA_ELECTIONS_ADMIN_GROUP'], username='toshio') - with user_set(fedora_elections.APP, user): - output = self.app.get('/admin/test_election/edit') - self.assertEqual(output.status_code, 404) - - self.setup_db() - with user_set(fedora_elections.APP, user): - output = self.app.get('/admin/test_election2/') - self.assertEqual(output.status_code, 200) - csrf_token = self.get_csrf(output=output) + with user_set(fedora_elections.APP, user, oidc_id_token='foobar'): + with patch( + 'fedora_elections.OIDC.user_getfield', + MagicMock(return_value=['elections'])): + output = self.app.get('/admin/test_election/edit') + self.assertEqual(output.status_code, 404) - # Edit Admin Group + self.setup_db() - # Check election before edit - output = self.app.get('/admin/test_election2/') - self.assertEqual(output.status_code, 200) - if self.get_wtforms_version() >= (2, 2): - self.assertIn( - 'input class="form-control" id="seats_elected" ' - 'name="seats_elected" required type="text" ' - 'value="1">', output.data) - else: + with user_set(fedora_elections.APP, user, oidc_id_token='foobar'): + with patch( + 'fedora_elections.OIDC.user_getfield', + MagicMock(return_value=['elections'])): + output = self.app.get('/admin/test_election2/') + self.assertEqual(output.status_code, 200) + csrf_token = self.get_csrf(output=output) + + # Edit Admin Group + + # Check election before edit + output = self.app.get('/admin/test_election2/') + self.assertEqual(output.status_code, 200) + if self.get_wtforms_version() >= (2, 2): + self.assertIn( + 'input class="form-control" id="seats_elected" ' + 'name="seats_elected" required type="text" ' + 'value="1">', output.data) + else: + self.assertIn( + 'input class="form-control" id="seats_elected" ' + 'name="seats_elected" type="text" ' + 'value="1">', output.data) + self.assertTrue('Candidates
CandidateVotesVoters per candidateAverage votes per candidate
CandidateVotesVoters per candidateAverage votes per candidate
{% if candidate.url %} @@ -154,7 +157,7 @@ {% if candidate.url %} {% endif %} - {% if not flag %} + {% if flag[-1] == 0 %} Elected {% endif %} VotesVotes
- - - - - {% if stats['candidate_voters'] %} - - - {% endif %} - + + + + + {% if stats['candidate_voters'] %} + + + {% endif %} + - {% for candidate in election.candidates|sort(attribute='vote_count', reverse=True) %} - {% if loop.index <= election.seats_elected %} - {# If we are below the number of user that will be selected, get the number - of votes and the flag to False#} - {% set flag = False %} - {% set votes = candidate.vote_count %} - {% elif loop.index > election.seats_elected and votes > candidate.vote_count and not flag %} - {# if we are above the number of user that will be selected (seats - available), check if the number of votes for this candidate is lower than - the number of votes for the last candidate and if the Flag is False - So this takes care of the case where there are 10 seats elected and the 11th - candidate has the same score as the 10th one. - In this case we would end up with one more person that the number of seats - available and we'll need to either find a way to select one over the other - or deal with having one more candidate accepted #} - {% set flag = True %} - {% set lastrow = True %} - {% else %} - {# we are above the number of seats available, the number of votes is below - that of the last candidate above selected and the Flag is True which means - we already passed the condition above #} - {% set lastrow = False %} - {% endif %} - + {%- set lastrow = [0] -%} + {%- set flag = [0] -%} + {% for candidate in candidates %} + {% if loop.index <= election.seats_elected %} + {# If we are below the number of user that will be selected, get the number + of votes and the flag to False#} + {%- set _ = flag.append(0) -%} + {%- elif loop.index > election.seats_elected + and candidates[loop.index -2].vote_count > candidate.vote_count + and flag[-1] == 0 -%} + {# if we are above the number of user that will be selected (seats + available), check if the number of votes for this candidate is lower than + the number of votes for the last candidate and if the Flag is False + So this takes care of the case where there are 10 seats elected and the 11th + candidate has the same score as the 10th one. + In this case we would end up with one more person that the number of seats + available and we'll need to either find a way to select one over the other + or deal with having one more candidate accepted #} + {%- set _ = lastrow.append(1) -%} + {%- set _ = flag.append(1) -%} + {% else %} + {# we are above the number of seats available, the number of votes is below + that of the last candidate above selected and the Flag is True which means + we already passed the condition above #} + {% set _ = lastrow.append(0) -%} + {% endif %} + diff --git a/fedora_elections/templates/results_text.html b/fedora_elections/templates/results_text.html index 9c99734..14f9503 100644 --- a/fedora_elections/templates/results_text.html +++ b/fedora_elections/templates/results_text.html @@ -21,14 +21,17 @@ could accumulate up to {{ stats['n_voters'] * stats['max_vote'] }} votes ({{stat The results for the elections are as follows: # votes | name -- --------+----------------------{% -for candidate in election.candidates|sort(attribute='vote_count', reverse=True) -%} +- --------+---------------------- +{%- set lastrow = [0] -%} +{%- set flag = [0] -%} +{%- for candidate in candidates -%} {% if loop.index <= election.seats_elected -%} - {# If we are below the number of user that will be selected, + {# If we are below the number of user that will be selected get the number of votes and the flag to False -#} - {% set flag = False -%} - {% set votes = candidate.vote_count -%} - {%- elif loop.index > election.seats_elected and votes > candidate.vote_count and not flag -%} + {%- set _ = flag.append(0) -%} + {%- elif loop.index > election.seats_elected + and candidates[loop.index -2].vote_count > candidate.vote_count + and flag[-1] == 0 -%} {# if we are above the number of user that will be selected (seats available), check if the number of votes for this candidate is lower than the number of votes for the last candidate and if the Flag is @@ -38,14 +41,17 @@ for candidate in election.candidates|sort(attribute='vote_count', reverse=True) In this case we would end up with one more person that the number of seats available and we'll need to either find a way to select one over the other or deal with having one more candidate accepted -#} - {% set flag = True -%} - {% set lastrow = True -%} + {%- set _ = lastrow.append(1) -%} + {%- set _ = flag.append(1) -%} {%- else -%} {# we are above the number of seats available, the number of votes is below that of the last candidate above selected and the Flag is True which means we already passed the condition above -#} - {% set lastrow = False -%} {%- endif %}{% if lastrow == True %} -- --------+---------------------- {%- endif %} + {% set _ = lastrow.append(0) -%} + {%- endif -%} + {%- if lastrow[-1] == 1 %} +- --------+---------------------- + {%- endif %} {{ candidate.vote_count | rjust(8) }} | {% if election.candidates_are_fasusers -%} {{ usernamemap[candidate.id] }} {%- else -%} {{candidate.name}} {%- endif %} diff --git a/requirements.txt b/requirements.txt index 61553e1..e70a503 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,12 +1,10 @@ # Used for when working from a virtualenv. # Use this file by running "$ pip install -r requirements.txt" Flask>=0.10 +flask-oidc Flask-wtf kitchen python-fedora -python-openid -python-openid-cla -python-openid-teams SQLAlchemy wtforms arrow diff --git a/runserver.py b/runserver.py old mode 100755 new mode 100644 index 415f49f..9b516c1 --- a/runserver.py +++ b/runserver.py @@ -23,7 +23,7 @@ parser.add_argument( default=False, help='Profile the application.') parser.add_argument( - '--port', '-p', default=5000, + '--port', '-p', default=5005, help='Port for the flask application.') parser.add_argument( '--host', default="127.0.0.1", diff --git a/runtests.sh b/runtests.sh index 47f97e4..8ac84b0 100755 --- a/runtests.sh +++ b/runtests.sh @@ -1,4 +1,5 @@ #!/bin/bash +FEDORA_ELECTIONS_CONFIG=`pwd`/tests/config \ PYTHONPATH=fedora_elections ./nosetests \ --with-coverage --cover-erase --cover-package=fedora_elections $* diff --git a/setup.py b/setup.py index 1821fe5..8836c03 100644 --- a/setup.py +++ b/setup.py @@ -4,13 +4,19 @@ Setup script """ -# These two lines are needed to run on EL6 -__requires__ = ['SQLAlchemy >= 0.7', 'jinja2 >= 2.4'] -import pkg_resources +import os +import re +from setuptools import setup -from fedora_elections import __version__ +versionfile = os.path.join( + os.path.dirname(__file__), 'fedora_elections', '__init__.py') -from setuptools import setup +# Thanks to SQLAlchemy: +# https://github.com/zzzeek/sqlalchemy/blob/master/setup.py#L104 +with open(versionfile) as stream: + __version__ = re.compile( + r".*__version__ = '(.*?)'", re.S + ).match(stream.read()).group(1) setup( name='fedora-elections', diff --git a/tests/__init__.py b/tests/__init__.py index 91616df..68b0a3a 100644 --- a/tests/__init__.py +++ b/tests/__init__.py @@ -20,6 +20,7 @@ fedora_elections test script """ +from __future__ import print_function __requires__ = ['SQLAlchemy >= 0.7'] import pkg_resources @@ -57,7 +58,7 @@ if os.environ.get('BUILD_ID'): req = requests.get('%s/new' % FAITOUT_URL) if req.status_code == 200: DB_PATH = req.text - print 'Using faitout at: %s' % DB_PATH + print('Using faitout at: %s' % DB_PATH) except: pass @@ -66,7 +67,7 @@ TODAY = date.today() @contextmanager -def user_set(APP, user): +def user_set(APP, user, oidc_id_token=None): """ Set the provided user as fas_user in the provided application.""" # Hack used to remove the before_request function set by @@ -77,6 +78,7 @@ def user_set(APP, user): def handler(sender, **kwargs): g.fas_user = user + g.oidc_id_token = oidc_id_token with appcontext_pushed.connected_to(handler, APP): yield @@ -138,6 +140,27 @@ class ModelFlasktests(Modeltests): self.app = fedora_elections.APP.test_client() + def get_wtforms_version(self): + """Returns the wtforms version as a tuple.""" + import wtforms + wtforms_v = wtforms.__version__.split('.') + for idx, val in enumerate(wtforms_v): + try: + val = int(val) + except ValueError: + pass + wtforms_v[idx] = val + return tuple(wtforms_v) + + def get_csrf(self, url='/admin/new', output=None): + """Retrieve a CSRF token from given URL.""" + if output is None: + output = self.app.get(url) + self.assertEqual(output.status_code, 200) + + return output.get_data(as_text=True).split( + 'name="csrf_token" type="hidden" value="')[1].split('">')[0] + class FakeGroup(object): """ Fake object used to make the FakeUser object closer to the diff --git a/tests/client_secrets.json b/tests/client_secrets.json new file mode 100644 index 0000000..dcae170 --- /dev/null +++ b/tests/client_secrets.json @@ -0,0 +1,12 @@ +{ + "web": { + "redirect_uris": ["http://localhost:5005/oidc_callback"], + "token_uri": "https://iddev.fedorainfracloud.org/openidc/Token", + "auth_uri": "https://iddev.fedorainfracloud.org/openidc/Authorization", + "client_id": "client_id", + "client_secret": "client_secret", + "userinfo_uri": "https://iddev.fedorainfracloud.org/openidc/UserInfo", + "token_introspection_uri": "https://iddev.fedorainfracloud.org/openidc/TokenInfo", + "issuer": "https://iddev.fedorainfracloud.org/openidc/" + } +} diff --git a/tests/config b/tests/config new file mode 100644 index 0000000..cb683d1 --- /dev/null +++ b/tests/config @@ -0,0 +1,4 @@ +import os + +OIDC_CLIENT_SECRETS = os.path.join(os.path.dirname( + os.path.abspath(__file__)), 'client_secrets.json') diff --git a/tests/test_flask.py b/tests/test_flask.py index 868cccc..1899954 100644 --- a/tests/test_flask.py +++ b/tests/test_flask.py @@ -31,6 +31,7 @@ from datetime import time from datetime import timedelta import flask +from mock import patch, MagicMock sys.path.insert(0, os.path.join(os.path.dirname( os.path.abspath(__file__)), '..')) @@ -117,18 +118,31 @@ class Flasktests(ModelFlasktests): flask.g.fas_user = None self.assertFalse(fedora_elections.is_admin(flask.g.fas_user)) - flask.g.fas_user = FakeUser() - self.assertFalse(fedora_elections.is_admin(flask.g.fas_user)) + flask.g.oidc_id_token = 'foobar' + + with patch( + 'fedora_elections.OIDC.user_getfield', + MagicMock(return_value=['foobar'])): + flask.g.fas_user = FakeUser() + self.assertFalse(fedora_elections.is_admin(flask.g.fas_user)) - flask.g.fas_user = FakeUser( - fedora_elections.APP.config['FEDORA_ELECTIONS_ADMIN_GROUP']) - self.assertTrue(fedora_elections.is_admin(flask.g.fas_user)) + with patch( + 'fedora_elections.OIDC.user_getfield', + MagicMock(return_value=['elections'])): + flask.g.fas_user = FakeUser( + fedora_elections.APP.config['FEDORA_ELECTIONS_ADMIN_GROUP']) + self.assertTrue( + fedora_elections.is_admin(flask.g.fas_user, ['elections'])) - fedora_elections.APP.config['FEDORA_ELECTIONS_ADMIN_GROUP'] = [ - 'sysadmin-main', 'sysadmin-elections'] - flask.g.fas_user = FakeUser( - fedora_elections.APP.config['FEDORA_ELECTIONS_ADMIN_GROUP']) - self.assertTrue(fedora_elections.is_admin(flask.g.fas_user)) + with patch( + 'fedora_elections.OIDC.user_getfield', + MagicMock(return_value=['sysadmin-main'])): + + fedora_elections.APP.config['FEDORA_ELECTIONS_ADMIN_GROUP'] = [ + 'sysadmin-main', 'sysadmin-elections'] + flask.g.fas_user = FakeUser(['sysadmin-main']) + self.assertTrue( + fedora_elections.is_admin(flask.g.fas_user, ['sysadmin-main'])) def test_is_election_admin(self): """ Test the is_election_admin function. """ @@ -140,31 +154,42 @@ class Flasktests(ModelFlasktests): fedora_elections.is_election_admin( flask.g.fas_user, 1) ) + + flask.g.oidc_id_token = 'foobar' + flask.g.fas_user = FakeUser() self.assertFalse( fedora_elections.is_election_admin( flask.g.fas_user, 1) ) - flask.g.fas_user = FakeUser( - fedora_elections.APP.config['FEDORA_ELECTIONS_ADMIN_GROUP']) - self.assertTrue(fedora_elections.is_election_admin( - flask.g.fas_user, 1)) + with patch( + 'fedora_elections.OIDC.user_getfield', + MagicMock(return_value=['elections'])): + flask.g.fas_user = FakeUser( + fedora_elections.APP.config['FEDORA_ELECTIONS_ADMIN_GROUP']) + self.assertTrue(fedora_elections.is_election_admin( + flask.g.fas_user, 1)) self.setup_db() with app.test_request_context(): flask.g.fas_user = FakeUser('testers') - # This is user is not an admin for election #1 - self.assertFalse( - fedora_elections.is_election_admin( - flask.g.fas_user, 1) - ) + flask.g.oidc_id_token = 'foobar' - # This is user is an admin for election #2 - self.assertTrue( - fedora_elections.is_election_admin( - flask.g.fas_user, 2) - ) + # This is user is not an admin for election #1 + with patch( + 'fedora_elections.OIDC.user_getfield', + MagicMock(return_value=['foobar'])): + self.assertFalse( + fedora_elections.is_election_admin( + flask.g.fas_user, 1) + ) + + # This is user is an admin for election #2 + self.assertTrue( + fedora_elections.is_election_admin( + flask.g.fas_user, 2) + ) def test_is_safe_url(self): """ Test the is_safe_url function. """ @@ -182,17 +207,23 @@ class Flasktests(ModelFlasktests): with app.test_request_context(): flask.g.fas_user = FakeUser(['gitr2spec']) output = self.app.get('/login') - self.assertEqual(output.status_code, 200) + self.assertEqual(output.status_code, 302) + self.assertIn( + 'https://iddev.fedorainfracloud.org/openidc/Authorization?', + output.data) output = self.app.get('/login?next=http://localhost/') - self.assertEqual(output.status_code, 200) - - self.setup_db() - user = FakeUser([], username='pingou') - with user_set(fedora_elections.APP, user): - output = self.app.get('/login', follow_redirects=True) - self.assertEqual(output.status_code, 200) - self.assertTrue('Fedora elections' in output.data) + self.assertEqual(output.status_code, 302) + self.assertIn( + 'https://iddev.fedorainfracloud.org/openidc/Authorization?', + output.data) + + # self.setup_db() + # user = FakeUser([], username='pingou') + # with user_set(fedora_elections.APP, user): + # output = self.app.get('/login', follow_redirects=True) + # self.assertEqual(output.status_code, 200) + # self.assertTrue('Fedora elections' in output.data) def test_auth_logout(self): """ Test the auth_logout function. """ diff --git a/tests/test_flask_admin.py b/tests/test_flask_admin.py index 3054e73..45ebcc6 100644 --- a/tests/test_flask_admin.py +++ b/tests/test_flask_admin.py @@ -32,6 +32,7 @@ from datetime import time from datetime import timedelta import flask +from mock import patch, MagicMock sys.path.insert(0, os.path.join(os.path.dirname( os.path.abspath(__file__)), '..')) @@ -49,17 +50,39 @@ class FlaskAdmintests(ModelFlasktests): user = FakeUser( fedora_elections.APP.config['FEDORA_ELECTIONS_ADMIN_GROUP'], username='toshio') - with user_set(fedora_elections.APP, user): - output = self.app.get('/admin/election_test/') - self.assertEqual(output.status_code, 404) + with user_set(fedora_elections.APP, user, oidc_id_token='foobar'): + with patch( + 'fedora_elections.OIDC.user_getfield', + MagicMock(return_value=['elections'])): + output = self.app.get('/admin/election_test/') + self.assertEqual(output.status_code, 404) + + self.setup_db() + + with user_set(fedora_elections.APP, user, oidc_id_token='foobar'): + with patch( + 'fedora_elections.OIDC.user_getfield', + MagicMock(return_value=['elections'])): + output = self.app.get('/admin/test_election/') + self.assertEqual(output.status_code, 200) + self.assertTrue('Candidates 3' in output.data) + + def test_admin_no_cla(self): + """ Test the admin_new_election function. """ self.setup_db() - with user_set(fedora_elections.APP, user): - output = self.app.get('/admin/test_election/') - self.assertEqual(output.status_code, 200) - self.assertTrue('Candidates 3' in output.data) + user = FakeUser( + fedora_elections.APP.config['FEDORA_ELECTIONS_ADMIN_GROUP'], + username='toshio') + user.cla_done = False + with user_set(fedora_elections.APP, user, oidc_id_token='foobar'): + with patch( + 'fedora_elections.OIDC.user_getfield', + MagicMock(return_value=['elections'])): + output = self.app.get('/admin/new') + self.assertEqual(output.status_code, 403) def test_admin_new_election(self): """ Test the admin_new_election function. """ @@ -68,339 +91,421 @@ class FlaskAdmintests(ModelFlasktests): user = FakeUser( fedora_elections.APP.config['FEDORA_ELECTIONS_ADMIN_GROUP'], username='toshio') - with user_set(fedora_elections.APP, user): - output = self.app.get('/admin/new') - self.assertEqual(output.status_code, 200) - self.assertTrue( - '

Create new election

' in output.data) - self.assertTrue( - 'input class="form-control" id="shortdesc" name="shortdesc" type="text"' - in output.data) - - # No csrf provided - data = { - 'alias': 'new_election', - 'shortdesc': 'new election shortdesc', - 'description': 'new election description', - 'voting_type': 'simple', - 'url': 'https://fedoraproject.org', - 'start_date': TODAY + timedelta(days=2), - 'end_date': TODAY + timedelta(days=4), - 'seats_elected': '2', - 'candidates_are_fasusers': False, - 'embargoed': True, - } - - output = self.app.post('/admin/new', data=data) - self.assertEqual(output.status_code, 200) - self.assertTrue( - '

Create new election

' in output.data) - self.assertTrue( - 'input class="form-control" id="shortdesc" name="shortdesc" type="text"' - in output.data) - - csrf_token = output.data.split( - 'name="csrf_token" type="hidden" value="')[1].split('">')[0] - - # Description missing - data = { - 'alias': 'new_election', - 'shortdesc': 'new election shortdesc', - 'voting_type': 'simple', - 'url': 'https://fedoraproject.org', - 'start_date': TODAY + timedelta(days=2), - 'end_date': TODAY + timedelta(days=4), - 'seats_elected': '2', - 'candidates_are_fasusers': False, - 'embargoed': True, - 'csrf_token': csrf_token, - } - - output = self.app.post('/admin/new', data=data) - self.assertEqual(output.status_code, 200) - self.assertTrue( - '

Create new election

' in output.data) - self.assertTrue( - 'input class="form-control" id="shortdesc" name="shortdesc" type="text"' - in output.data) - self.assertTrue( - '' - in output.data) - - # Invalid alias - data = { - 'alias': 'new', - 'shortdesc': 'new election shortdesc', - 'description': 'new election description', - 'voting_type': 'simple', - 'url': 'https://fedoraproject.org', - 'start_date': TODAY + timedelta(days=2), - 'end_date': TODAY + timedelta(days=4), - 'seats_elected': 2, - 'candidates_are_fasusers': False, - 'embargoed': True, - 'csrf_token': csrf_token, - } - - output = self.app.post('/admin/new', data=data) - self.assertEqual(output.status_code, 200) - self.assertTrue( - '

Create new election

' in output.data) - self.assertTrue( - 'input class="form-control" id="shortdesc" name="shortdesc" type="text"' - in output.data) - self.assertTrue( - '' - in output.data) - - # Invalid: end_date earlier than start_date - data = { - 'alias': 'new_election', - 'shortdesc': 'new election shortdesc', - 'description': 'new election description', - 'voting_type': 'simple', - 'url': 'https://fedoraproject.org', - 'start_date': TODAY + timedelta(days=6), - 'end_date': TODAY + timedelta(days=4), - 'seats_elected': 2, - 'candidates_are_fasusers': False, - 'embargoed': True, - 'csrf_token': csrf_token, - } - - output = self.app.post('/admin/new', data=data) - self.assertEqual(output.status_code, 200) - self.assertTrue( - '

Create new election

' in output.data) - self.assertTrue( - 'input class="form-control" id="shortdesc" name="shortdesc" type="text"' - in output.data) - self.assertTrue( - 'class="form-control-feedback">End date must be later than start date.' - in output.data) - - # Invalid: alias already taken - data = { - 'alias': 'test_election', - 'shortdesc': 'new election shortdesc', - 'description': 'new election description', - 'voting_type': 'simple', - 'url': 'https://fedoraproject.org', - 'start_date': TODAY + timedelta(days=6), - 'end_date': TODAY + timedelta(days=4), - 'seats_elected': 2, - 'candidates_are_fasusers': False, - 'embargoed': True, - 'csrf_token': csrf_token, - } - - output = self.app.post('/admin/new', data=data) - self.assertEqual(output.status_code, 200) - self.assertTrue( - '

Create new election

' in output.data) - self.assertTrue( - 'input class="form-control" id="shortdesc" name="shortdesc" type="text"' - in output.data) - self.assertTrue( - '' in output.data) - - # Invalid: shortdesc already taken - data = { - 'alias': 'new_election', - 'shortdesc': 'test election shortdesc', - 'description': 'new election description', - 'voting_type': 'simple', - 'url': 'https://fedoraproject.org', - 'start_date': TODAY + timedelta(days=6), - 'end_date': TODAY + timedelta(days=4), - 'seats_elected': 2, - 'candidates_are_fasusers': False, - 'embargoed': True, - 'csrf_token': csrf_token, - } - - output = self.app.post('/admin/new', data=data) - self.assertEqual(output.status_code, 200) - self.assertTrue( - '

Create new election

' in output.data) - self.assertTrue( - 'input class="form-control" id="shortdesc" name="shortdesc" type="text"' - in output.data) - self.assertTrue( - '' in output.data) - - # All good - max_votes is ignored as it is not a integer - data = { - 'alias': 'new_election', - 'shortdesc': 'new election shortdesc', - 'description': 'new election description', - 'voting_type': 'simple', - 'url': 'https://fedoraproject.org', - 'start_date': TODAY + timedelta(days=2), - 'end_date': TODAY + timedelta(days=4), - 'seats_elected': 2, - 'candidates_are_fasusers': False, - 'embargoed': True, - 'admin_grp': 'testers, sysadmin-main,,', - 'lgl_voters': 'testers, packager,,', - 'max_votes': 'wrong', - 'csrf_token': csrf_token, - } - - output = self.app.post( - '/admin/new', data=data, follow_redirects=True) - self.assertEqual(output.status_code, 200) - self.assertTrue( - 'Election "new_election" added' - in output.data) - self.assertTrue( - 'There are no candidates.' in output.data) - self.assertTrue( - '' - in output.data) - self.assertTrue( - '' - in output.data) - self.assertTrue( - '' - in output.data) - - # All good - max_votes is ignored as it is not a integer - data = { - 'alias': 'new_election2', - 'shortdesc': 'new election2 shortdesc', - 'description': 'new election2 description', - 'voting_type': 'simple', - 'url': 'https://fedoraproject.org', - 'start_date': TODAY + timedelta(days=2), - 'end_date': TODAY + timedelta(days=4), - 'seats_elected': 2, - 'candidates_are_fasusers': False, - 'embargoed': True, - 'admin_grp': 'testers, , sysadmin-main,,', - 'lgl_voters': 'testers, packager,,,', - 'csrf_token': csrf_token, - } - - output = self.app.post( - '/admin/new', data=data, follow_redirects=True) - self.assertEqual(output.status_code, 200) - self.assertTrue( - 'Election "new_election2" added' - in output.data) - self.assertTrue( - 'There are no candidates.' in output.data) - self.assertTrue( - '' - in output.data) - self.assertTrue( - '' - in output.data) - self.assertTrue( - '' - in output.data) + + with user_set(fedora_elections.APP, user, oidc_id_token='foobar'): + with patch( + 'fedora_elections.OIDC.user_getfield', + MagicMock(return_value=['elections'])): + output = self.app.get('/admin/new') + self.assertEqual(output.status_code, 200) + self.assertTrue( + '

Create new election

' in output.data) + if self.get_wtforms_version() >= (2, 2): + self.assertIn( + 'input class="form-control" id="shortdesc" ' + 'name="shortdesc" required type="text" ', output.data) + else: + self.assertIn( + 'input class="form-control" id="shortdesc" ' + 'name="shortdesc" type="text" ', output.data) + + # No csrf provided + data = { + 'alias': 'new_election', + 'shortdesc': 'new election shortdesc', + 'description': 'new election description', + 'voting_type': 'simple', + 'url': 'https://fedoraproject.org', + 'start_date': TODAY + timedelta(days=2), + 'end_date': TODAY + timedelta(days=4), + 'seats_elected': '2', + 'candidates_are_fasusers': False, + 'embargoed': True, + } + + output = self.app.post('/admin/new', data=data) + self.assertEqual(output.status_code, 200) + self.assertTrue( + '

Create new election

' in output.data) + if self.get_wtforms_version() >= (2, 2): + self.assertIn( + 'input class="form-control" id="shortdesc" ' + 'name="shortdesc" required type="text" ', output.data) + else: + self.assertIn( + 'input class="form-control" id="shortdesc" ' + 'name="shortdesc" type="text" ', output.data) + + csrf_token = self.get_csrf(output=output) + + # Description missing + data = { + 'alias': 'new_election', + 'shortdesc': 'new election shortdesc', + 'voting_type': 'simple', + 'url': 'https://fedoraproject.org', + 'start_date': TODAY + timedelta(days=2), + 'end_date': TODAY + timedelta(days=4), + 'seats_elected': '2', + 'candidates_are_fasusers': False, + 'embargoed': True, + 'csrf_token': csrf_token, + } + + output = self.app.post('/admin/new', data=data) + self.assertEqual(output.status_code, 200) + self.assertTrue( + '

Create new election

' in output.data) + if self.get_wtforms_version() >= (2, 2): + self.assertIn( + 'input class="form-control" id="shortdesc" ' + 'name="shortdesc" required type="text" ', output.data) + else: + self.assertIn( + 'input class="form-control" id="shortdesc" ' + 'name="shortdesc" type="text" ', output.data) + self.assertTrue( + '' + in output.data) + + # Invalid alias + data = { + 'alias': 'new', + 'shortdesc': 'new election shortdesc', + 'description': 'new election description', + 'voting_type': 'simple', + 'url': 'https://fedoraproject.org', + 'start_date': TODAY + timedelta(days=2), + 'end_date': TODAY + timedelta(days=4), + 'seats_elected': 2, + 'candidates_are_fasusers': False, + 'embargoed': True, + 'csrf_token': csrf_token, + } + + output = self.app.post('/admin/new', data=data) + self.assertEqual(output.status_code, 200) + self.assertTrue( + '

Create new election

' in output.data) + if self.get_wtforms_version() >= (2, 2): + self.assertIn( + 'input class="form-control" id="shortdesc" ' + 'name="shortdesc" required type="text" ', output.data) + else: + self.assertIn( + 'input class="form-control" id="shortdesc" ' + 'name="shortdesc" type="text" ', output.data) + self.assertTrue( + '' + in output.data) + + # Invalid: end_date earlier than start_date + data = { + 'alias': 'new_election', + 'shortdesc': 'new election shortdesc', + 'description': 'new election description', + 'voting_type': 'simple', + 'url': 'https://fedoraproject.org', + 'start_date': TODAY + timedelta(days=6), + 'end_date': TODAY + timedelta(days=4), + 'seats_elected': 2, + 'candidates_are_fasusers': False, + 'embargoed': True, + 'csrf_token': csrf_token, + } + + output = self.app.post('/admin/new', data=data) + self.assertEqual(output.status_code, 200) + self.assertTrue( + '

Create new election

' in output.data) + if self.get_wtforms_version() >= (2, 2): + self.assertIn( + 'input class="form-control" id="shortdesc" ' + 'name="shortdesc" required type="text" ', output.data) + else: + self.assertIn( + 'input class="form-control" id="shortdesc" ' + 'name="shortdesc" type="text" ', output.data) + self.assertTrue( + 'class="form-control-feedback">End date must be later than start date.' + in output.data) + + # Invalid: alias already taken + data = { + 'alias': 'test_election', + 'shortdesc': 'new election shortdesc', + 'description': 'new election description', + 'voting_type': 'simple', + 'url': 'https://fedoraproject.org', + 'start_date': TODAY + timedelta(days=6), + 'end_date': TODAY + timedelta(days=4), + 'seats_elected': 2, + 'candidates_are_fasusers': False, + 'embargoed': True, + 'csrf_token': csrf_token, + } + + output = self.app.post('/admin/new', data=data) + self.assertEqual(output.status_code, 200) + self.assertTrue( + '

Create new election

' in output.data) + if self.get_wtforms_version() >= (2, 2): + self.assertIn( + 'input class="form-control" id="shortdesc" ' + 'name="shortdesc" required type="text" ', output.data) + else: + self.assertIn( + 'input class="form-control" id="shortdesc" ' + 'name="shortdesc" type="text" ', output.data) + self.assertTrue( + '' in output.data) + + # Invalid: shortdesc already taken + data = { + 'alias': 'new_election', + 'shortdesc': 'test election shortdesc', + 'description': 'new election description', + 'voting_type': 'simple', + 'url': 'https://fedoraproject.org', + 'start_date': TODAY + timedelta(days=6), + 'end_date': TODAY + timedelta(days=4), + 'seats_elected': 2, + 'candidates_are_fasusers': False, + 'embargoed': True, + 'csrf_token': csrf_token, + } + + output = self.app.post('/admin/new', data=data) + self.assertEqual(output.status_code, 200) + self.assertTrue( + '

Create new election

' in output.data) + if self.get_wtforms_version() >= (2, 2): + self.assertIn( + 'input class="form-control" id="shortdesc" ' + 'name="shortdesc" required type="text" ', output.data) + else: + self.assertIn( + 'input class="form-control" id="shortdesc" ' + 'name="shortdesc" type="text" ', output.data) + self.assertTrue( + '' in output.data) + + # All good - max_votes is ignored as it is not a integer + data = { + 'alias': 'new_election', + 'shortdesc': 'new election shortdesc', + 'description': 'new election description', + 'voting_type': 'simple', + 'url': 'https://fedoraproject.org', + 'start_date': TODAY + timedelta(days=2), + 'end_date': TODAY + timedelta(days=4), + 'seats_elected': 2, + 'candidates_are_fasusers': False, + 'embargoed': True, + 'admin_grp': 'testers, sysadmin-main,,', + 'lgl_voters': 'testers, packager,,', + 'max_votes': 'wrong', + 'csrf_token': csrf_token, + } + + output = self.app.post( + '/admin/new', data=data, follow_redirects=True) + self.assertEqual(output.status_code, 200) + self.assertTrue( + 'Election "new_election" added' + in output.data) + self.assertTrue( + 'There are no candidates.' in output.data) + self.assertIn( + 'input class="form-control" id="admin_grp" ' + 'name="admin_grp" type="text" ' + 'value="sysadmin-main, testers">', output.data) + self.assertIn( + 'input class="form-control" id="lgl_voters" ' + 'name="lgl_voters" type="text" ' + 'value="packager, testers">', output.data) + self.assertIn( + 'input class="form-control" id="max_votes" ' + 'name="max_votes" type="text" ' + 'value="">', output.data) + + # All good - max_votes is ignored as it is not a integer + data = { + 'alias': 'new_election2', + 'shortdesc': 'new election2 shortdesc', + 'description': 'new election2 description', + 'voting_type': 'simple', + 'url': 'https://fedoraproject.org', + 'start_date': TODAY + timedelta(days=2), + 'end_date': TODAY + timedelta(days=4), + 'seats_elected': 2, + 'candidates_are_fasusers': False, + 'embargoed': True, + 'admin_grp': 'testers, , sysadmin-main,,', + 'lgl_voters': 'testers, packager,,,', + 'csrf_token': csrf_token, + } + + output = self.app.post( + '/admin/new', data=data, follow_redirects=True) + self.assertEqual(output.status_code, 200) + self.assertTrue( + 'Election "new_election2" added' + in output.data) + self.assertTrue( + 'There are no candidates.' in output.data) + self.assertIn( + 'input class="form-control" id="admin_grp" ' + 'name="admin_grp" type="text" ' + 'value="sysadmin-main, testers">', output.data) + self.assertIn( + 'input class="form-control" id="lgl_voters" ' + 'name="lgl_voters" type="text" ' + 'value="packager, testers">', output.data) + self.assertIn( + 'input class="form-control" id="max_votes" ' + 'name="max_votes" type="text" ' + 'value="">', output.data) def test_admin_edit_election(self): """ Test the admin_edit_election function. """ user = FakeUser( fedora_elections.APP.config['FEDORA_ELECTIONS_ADMIN_GROUP'], username='toshio') - with user_set(fedora_elections.APP, user): - output = self.app.get('/admin/test_election/') - self.assertEqual(output.status_code, 404) + + with user_set(fedora_elections.APP, user, oidc_id_token='foobar'): + with patch( + 'fedora_elections.OIDC.user_getfield', + MagicMock(return_value=['elections'])): + output = self.app.get('/admin/test_election/') + self.assertEqual(output.status_code, 404) self.setup_db() - with user_set(fedora_elections.APP, user): - output = self.app.get('/admin/test_election/') - self.assertEqual(output.status_code, 200) - self.assertTrue( - 'Election Details' in output.data) - self.assertTrue( - 'input class="form-control" id="shortdesc" name="shortdesc" type="text"' - in output.data) - - data = { - 'alias': 'test_election', - 'shortdesc': 'test election shortdesc', - 'description': 'test election description', - 'voting_type': 'simple', - 'url': 'https://fedoraproject.org', - 'start_date': TODAY - timedelta(days=10), - 'end_date': TODAY - timedelta(days=8), - 'seats_elected': '2', - 'candidates_are_fasusers': False, - 'embargoed': False, - } - - output = self.app.post('/admin/test_election/', data=data) - self.assertEqual(output.status_code, 200) - self.assertTrue( - 'Election Details' in output.data) - self.assertTrue( - 'input class="form-control" id="shortdesc" name="shortdesc" type="text"' - in output.data) - - csrf_token = output.data.split( - 'name="csrf_token" type="hidden" value="')[1].split('">')[0] - - data = { - 'alias': 'test_election', - 'shortdesc': 'test election shortdesc', - 'voting_type': 'simple', - 'url': 'https://fedoraproject.org', - 'start_date': TODAY - timedelta(days=10), - 'end_date': TODAY - timedelta(days=8), - 'seats_elected': '2', - 'candidates_are_fasusers': False, - 'embargoed': False, - 'csrf_token': csrf_token, - } - - output = self.app.post('/admin/test_election/', data=data) - self.assertEqual(output.status_code, 200) - self.assertTrue( - 'Election Details' in output.data) - self.assertTrue( - 'input class="form-control" id="shortdesc" name="shortdesc" type="text"' - in output.data) - self.assertTrue( - '' - in output.data) - - # Check election before edit - output = self.app.get('/admin/test_election/') - self.assertEqual(output.status_code, 200) - self.assertTrue('Candidates 3' in output.data) - self.assertTrue('' in output.data) - - data = { - 'alias': 'test_election', - 'shortdesc': 'test election shortdesc', - 'description': 'test election description', - 'voting_type': 'simple', - 'url': 'https://fedoraproject.org', - 'start_date': TODAY - timedelta(days=10), - 'end_date': TODAY - timedelta(days=8), - 'seats_elected': '2', - 'candidates_are_fasusers': False, - 'embargoed': False, - 'max_votes': 'wrong', - 'csrf_token': csrf_token, - } - - output = self.app.post( - '/admin/test_election/', data=data, follow_redirects=True) - self.assertEqual(output.status_code, 200) - self.assertTrue( - 'Election "test_election" saved' - in output.data) - # We edited the seats_elected from 1 to 2 - self.assertTrue( - '' in output.data) - self.assertTrue('Candidates 3' in output.data) + with user_set(fedora_elections.APP, user, oidc_id_token='foobar'): + with patch( + 'fedora_elections.OIDC.user_getfield', + MagicMock(return_value=['elections'])): + output = self.app.get('/admin/test_election/') + self.assertEqual(output.status_code, 200) + self.assertTrue( + 'Election Details' in output.data) + if self.get_wtforms_version() >= (2, 2): + self.assertIn( + 'input class="form-control" id="shortdesc" ' + 'name="shortdesc" required type="text" ', output.data) + else: + self.assertIn( + 'input class="form-control" id="shortdesc" ' + 'name="shortdesc" type="text" ', output.data) + + data = { + 'alias': 'test_election', + 'shortdesc': 'test election shortdesc', + 'description': 'test election description', + 'voting_type': 'simple', + 'url': 'https://fedoraproject.org', + 'start_date': TODAY - timedelta(days=10), + 'end_date': TODAY - timedelta(days=8), + 'seats_elected': '2', + 'candidates_are_fasusers': False, + 'embargoed': False, + } + + output = self.app.post('/admin/test_election/', data=data) + self.assertEqual(output.status_code, 200) + self.assertTrue( + 'Election Details' in output.data) + if self.get_wtforms_version() >= (2, 2): + self.assertIn( + 'input class="form-control" id="shortdesc" ' + 'name="shortdesc" required type="text" ', output.data) + else: + self.assertIn( + 'input class="form-control" id="shortdesc" ' + 'name="shortdesc" type="text" ', output.data) + + csrf_token = self.get_csrf() + + data = { + 'alias': 'test_election', + 'voting_type': 'simple', + 'url': 'https://fedoraproject.org', + 'start_date': TODAY - timedelta(days=10), + 'end_date': TODAY - timedelta(days=8), + 'seats_elected': '2', + 'candidates_are_fasusers': False, + 'embargoed': False, + 'csrf_token': csrf_token, + } + + output = self.app.post( + '/admin/test_election/', data=data) + self.assertEqual(output.status_code, 200) + self.assertTrue( + 'Election Details' in output.data) + if self.get_wtforms_version() >= (2, 2): + self.assertIn( + 'input class="form-control" id="shortdesc" ' + 'name="shortdesc" required type="text" ', output.data) + else: + self.assertIn( + 'input class="form-control" id="shortdesc" ' + 'name="shortdesc" type="text" ', output.data) + self.assertIn( + '', + output.data) + + # Check election before edit + output = self.app.get('/admin/test_election/') + self.assertEqual(output.status_code, 200) + self.assertTrue('Candidates 3' in output.data) + if self.get_wtforms_version() >= (2, 2): + self.assertIn( + 'input class="form-control" id="seats_elected" ' + 'name="seats_elected" required type="text" ' + 'value="1">', output.data) + else: + self.assertIn( + 'input class="form-control" id="seats_elected" ' + 'name="seats_elected" type="text" ' + 'value="1">', output.data) + + data = { + 'alias': 'test_election', + 'shortdesc': 'test election shortdesc', + 'description': 'test election description', + 'voting_type': 'simple', + 'url': 'https://fedoraproject.org', + 'start_date': TODAY - timedelta(days=10), + 'end_date': TODAY - timedelta(days=8), + 'seats_elected': '2', + 'candidates_are_fasusers': False, + 'embargoed': False, + 'max_votes': 'wrong', + 'csrf_token': csrf_token, + } + + output = self.app.post( + '/admin/test_election/', data=data, follow_redirects=True) + self.assertEqual(output.status_code, 200) + self.assertTrue( + 'Election "test_election" saved' + in output.data) + # We edited the seats_elected from 1 to 2 + if self.get_wtforms_version() >= (2, 2): + self.assertIn( + 'input class="form-control" id="seats_elected" ' + 'name="seats_elected" required type="text" ' + 'value="2">', output.data) + else: + self.assertIn( + 'input class="form-control" id="seats_elected" ' + 'name="seats_elected" type="text" ' + 'value="2">', output.data) + self.assertTrue('Candidates 3' in output.data) def test_admin_edit_election_admin_groups(self): """ Test the admin_edit_election function when editing admin groups. @@ -408,84 +513,116 @@ class FlaskAdmintests(ModelFlasktests): user = FakeUser( fedora_elections.APP.config['FEDORA_ELECTIONS_ADMIN_GROUP'], username='toshio') - with user_set(fedora_elections.APP, user): - output = self.app.get('/admin/test_election/edit') - self.assertEqual(output.status_code, 404) + + with user_set(fedora_elections.APP, user, oidc_id_token='foobar'): + with patch( + 'fedora_elections.OIDC.user_getfield', + MagicMock(return_value=['elections'])): + output = self.app.get('/admin/test_election/edit') + self.assertEqual(output.status_code, 404) self.setup_db() - with user_set(fedora_elections.APP, user): - output = self.app.get('/admin/test_election2/') - self.assertEqual(output.status_code, 200) - csrf_token = output.data.split( - 'name="csrf_token" type="hidden" value="')[1].split('">')[0] - - # Edit Admin Group - - # Check election before edit - output = self.app.get('/admin/test_election2/') - self.assertEqual(output.status_code, 200) - self.assertTrue( - '' in output.data) - self.assertTrue('Candidates ' - in output.data) - self.assertTrue( - '

Some statistics about this election

' - in output.data) + with user_set(fedora_elections.APP, user, oidc_id_token='foobar'): + with patch( + 'fedora_elections.OIDC.user_getfield', + MagicMock(return_value=['elections'])): + output = self.app.get( + '/about/test_election2', follow_redirects=True) + self.assertEqual(output.status_code, 200) + self.assertTrue( + 'You are only seeing these results because you are an admin.' + in output.data) + self.assertTrue( + 'The results for this election are currently embargoed ' + in output.data) + self.assertTrue( + '' + in output.data) + self.assertTrue( + '

Some statistics about this election

' + in output.data) user = FakeUser(['gitr2spec'], username='kevin') with user_set(fedora_elections.APP, user): @@ -251,11 +264,14 @@ class FlaskElectionstests(ModelFlasktests): user = FakeUser( fedora_elections.APP.config['FEDORA_ELECTIONS_ADMIN_GROUP'], username='toshio') - with user_set(fedora_elections.APP, user): - output = self.app.get( - '/results/test_election/text', follow_redirects=True) - self.assertEqual(output.status_code, 200) - exp = """ + with user_set(fedora_elections.APP, user, oidc_id_token='foobar'): + with patch( + 'fedora_elections.OIDC.user_getfield', + MagicMock(return_value=['elections'])): + output = self.app.get( + '/results/test_election/text', follow_redirects=True) + self.assertEqual(output.status_code, 200) + exp = """ @@ -291,12 +307,13 @@ candidates for running this elections! """ - self.assertEqual(output.data, exp) + print(output.data) + self.assertEqual(output.data, exp) - output = self.app.get( - '/results/test_election2/text', follow_redirects=True) - self.assertEqual(output.status_code, 200) - exp = """ + output = self.app.get( + '/results/test_election2/text', follow_redirects=True) + self.assertEqual(output.status_code, 200) + exp = """ @@ -329,7 +346,7 @@ candidates for running this elections! """ - self.assertEqual(output.data, exp) + self.assertEqual(output.data, exp) user = FakeUser(['gitr2spec'], username='kevin') with user_set(fedora_elections.APP, user): diff --git a/tests/test_flask_irc.py b/tests/test_flask_irc.py index 893fddd..59fed2a 100644 --- a/tests/test_flask_irc.py +++ b/tests/test_flask_irc.py @@ -32,6 +32,7 @@ from datetime import time from datetime import timedelta import flask +from mock import patch, MagicMock sys.path.insert(0, os.path.join(os.path.dirname( os.path.abspath(__file__)), '..')) @@ -46,100 +47,104 @@ class FlaskIrcElectionstests(ModelFlasktests): def test_vote_irc(self): """ Test the vote_irc function - the preview part. """ - output = self.app.get( - '/vote/test_election', follow_redirects=True) - self.assertEqual(output.status_code, 200) - self.assertTrue( - 'OpenID transaction in progress' in output.data - or 'discoveryfailure' in output.data) + output = self.app.get('/vote/test_election') + self.assertEqual(output.status_code, 302) + self.assertIn( + '/login?next=http%3A%2F%2Flocalhost%2Fvote%2Ftest_election', + output.data) self.setup_db() user = FakeUser(['packager'], username='nerdsville') - with user_set(fedora_elections.APP, user): - output = self.app.get( - '/vote/test_election7') - self.assertTrue( - 'test election 7 shortdesc' in output.data) - self.assertTrue( - '' - in output.data) - - csrf_token = output.data.split( - 'name="csrf_token" type="hidden" value="')[1].split('">')[0] - - # Invalid vote: No candidate - data = { - 'action': 'preview', - } - - output = self.app.post('/vote/test_election7', data=data) - self.assertEqual(output.status_code, 200) - self.assertTrue( - 'test election 7 shortdesc' in output.data) - - # Valid input - data = { - 'Kevin': -1, - 'Toshio': '0', - 'action': 'preview', - 'csrf_token': csrf_token, - } - - output = self.app.post('/vote/test_election7', data=data) - self.assertEqual(output.status_code, 200) - self.assertTrue( - 'test election 7 shortdesc' in output.data) - self.assertTrue( - '' - in output.data) - self.assertTrue( - 'Please confirm your vote!' - in output.data) + with user_set(fedora_elections.APP, user, oidc_id_token='foobar'): + with patch( + 'fedora_elections.OIDC.user_getfield', + MagicMock(return_value=['packager'])): + output = self.app.get( + '/vote/test_election7') + self.assertTrue( + 'test election 7 shortdesc' in output.data) + self.assertTrue( + '' + in output.data) + + csrf_token = output.data.split( + 'name="csrf_token" type="hidden" value="')[1].split('">')[0] + + # Invalid vote: No candidate + data = { + 'action': 'preview', + } + + output = self.app.post('/vote/test_election7', data=data) + self.assertEqual(output.status_code, 200) + self.assertTrue( + 'test election 7 shortdesc' in output.data) + + # Valid input + data = { + 'Kevin': -1, + 'Toshio': '0', + 'action': 'preview', + 'csrf_token': csrf_token, + } + + output = self.app.post('/vote/test_election7', data=data) + self.assertEqual(output.status_code, 200) + self.assertTrue( + 'test election 7 shortdesc' in output.data) + self.assertTrue( + '' + in output.data) + self.assertTrue( + 'Please confirm your vote!' + in output.data) def test_vote_irc_process(self): """ Test the vote_irc function - the voting part. """ - output = self.app.get( - '/vote/test_election', follow_redirects=True) - self.assertEqual(output.status_code, 200) - self.assertTrue( - 'OpenID transaction in progress' in output.data - or 'discoveryfailure' in output.data) + output = self.app.get('/vote/test_election') + self.assertEqual(output.status_code, 302) + self.assertIn( + '/login?next=http%3A%2F%2Flocalhost%2Fvote%2Ftest_election', + output.data) self.setup_db() user = FakeUser(['packager'], username='pingou') - with user_set(fedora_elections.APP, user): - # Invalid candidate id - no csrf - data = { - 'candidate': 1, - 'action': 'submit', - } - - output = self.app.post( - '/vote/test_election7', data=data, - follow_redirects=True) - self.assertEqual(output.status_code, 200) - - csrf_token = output.data.split( - 'name="csrf_token" type="hidden" value="')[1].split('">')[0] - - # Valid input - data = { - 'Toshio': '0', - 'Kevin': '1', - 'action': 'submit', - 'csrf_token': csrf_token, - } - - output = self.app.post( - '/vote/test_election7', data=data, - follow_redirects=True) - self.assertEqual(output.status_code, 200) - self.assertTrue( - 'Your vote has been recorded. Thank you!' - in output.data) - self.assertTrue('Open elections' in output.data) + with user_set(fedora_elections.APP, user, oidc_id_token='foobar'): + with patch( + 'fedora_elections.OIDC.user_getfield', + MagicMock(return_value=['packager'])): + # Invalid candidate id - no csrf + data = { + 'candidate': 1, + 'action': 'submit', + } + + output = self.app.post( + '/vote/test_election7', data=data, + follow_redirects=True) + self.assertEqual(output.status_code, 200) + + csrf_token = output.data.split( + 'name="csrf_token" type="hidden" value="')[1].split('">')[0] + + # Valid input + data = { + 'Toshio': '0', + 'Kevin': '1', + 'action': 'submit', + 'csrf_token': csrf_token, + } + + output = self.app.post( + '/vote/test_election7', data=data, + follow_redirects=True) + self.assertEqual(output.status_code, 200) + self.assertTrue( + 'Your vote has been recorded. Thank you!' + in output.data) + self.assertTrue('Open elections' in output.data) def test_vote_irc_revote(self): """ Test the vote_irc function - the re-voting part. """ @@ -147,45 +152,48 @@ class FlaskIrcElectionstests(ModelFlasktests): self.setup_db() user = FakeUser(['voters'], username='nerdsville') - with user_set(fedora_elections.APP, user): - retrieve_csrf = self.app.post('/vote/test_election7') - csrf_token = retrieve_csrf.data.split( - 'name="csrf_token" type="hidden" value="')[1].split('">')[0] - # Valid input - data = { - 'Kevin': -1, - 'Toshio': 1, - 'action': 'submit', - 'csrf_token': csrf_token, - } - self.app.post('/vote/test_election7', data=data, follow_redirects=True) - vote = fedora_elections.models.Vote - votes = vote.of_user_on_election(self.session, "nerdsville", '7') - self.assertEqual(votes[0].value, 1) - self.assertEqual(votes[1].value, -1) - #Let's not do repetition of what is tested above we aren't testing the - #functionality of voting as that has already been asserted - - #Next, we need to try revoting - newdata = { - 'Kevin': 1, - 'Toshio': -1, - 'action': 'submit', - 'csrf_token': csrf_token, - } - output = self.app.post('/vote/test_election7', data=newdata, follow_redirects=True) - #Next, we need to check if the vote has been recorded - self.assertEqual(output.status_code, 200) - self.assertTrue( - 'Your vote has been recorded. Thank you!' - in output.data) - self.assertTrue('Open elections' in output.data) - vote = fedora_elections.models.Vote - votes = vote.of_user_on_election(self.session, "nerdsville", '7') - self.assertEqual(votes[0].value, -1) - self.assertEqual(votes[1].value, 1) - - #If we haven't failed yet, HOORAY! + with user_set(fedora_elections.APP, user, oidc_id_token='foobar'): + with patch( + 'fedora_elections.OIDC.user_getfield', + MagicMock(return_value=['packager'])): + retrieve_csrf = self.app.post('/vote/test_election7') + csrf_token = retrieve_csrf.data.split( + 'name="csrf_token" type="hidden" value="')[1].split('">')[0] + # Valid input + data = { + 'Kevin': -1, + 'Toshio': 1, + 'action': 'submit', + 'csrf_token': csrf_token, + } + self.app.post('/vote/test_election7', data=data, follow_redirects=True) + vote = fedora_elections.models.Vote + votes = vote.of_user_on_election(self.session, "nerdsville", '7') + self.assertEqual(votes[0].value, 1) + self.assertEqual(votes[1].value, -1) + #Let's not do repetition of what is tested above we aren't testing the + #functionality of voting as that has already been asserted + + #Next, we need to try revoting + newdata = { + 'Kevin': 1, + 'Toshio': -1, + 'action': 'submit', + 'csrf_token': csrf_token, + } + output = self.app.post('/vote/test_election7', data=newdata, follow_redirects=True) + #Next, we need to check if the vote has been recorded + self.assertEqual(output.status_code, 200) + self.assertTrue( + 'Your vote has been recorded. Thank you!' + in output.data) + self.assertTrue('Open elections' in output.data) + vote = fedora_elections.models.Vote + votes = vote.of_user_on_election(self.session, "nerdsville", '7') + self.assertEqual(votes[0].value, -1) + self.assertEqual(votes[1].value, 1) + + #If we haven't failed yet, HOORAY! if __name__ == '__main__': SUITE = unittest.TestLoader().loadTestsFromTestCase(FlaskIrcElectionstests) diff --git a/tests/test_flask_range_voting.py b/tests/test_flask_range_voting.py index bf290e0..32477cf 100644 --- a/tests/test_flask_range_voting.py +++ b/tests/test_flask_range_voting.py @@ -32,6 +32,7 @@ from datetime import time from datetime import timedelta import flask +from mock import patch, MagicMock sys.path.insert(0, os.path.join(os.path.dirname( os.path.abspath(__file__)), '..')) @@ -46,242 +47,246 @@ class FlaskRangeElectionstests(ModelFlasktests): def test_vote_range(self): """ Test the vote_range function - the preview part. """ - output = self.app.get( - '/vote/test_election', follow_redirects=True) - self.assertEqual(output.status_code, 200) - self.assertTrue( - 'OpenID transaction in progress' in output.data - or 'discoveryfailure' in output.data) + output = self.app.get('/vote/test_election') + self.assertEqual(output.status_code, 302) + self.assertIn( + '/login?next=http%3A%2F%2Flocalhost%2Fvote%2Ftest_election', + output.data) self.setup_db() user = FakeUser(['voters'], username='pingou') - with user_set(fedora_elections.APP, user): - output = self.app.get( - '/vote/test_election3') - self.assertTrue( - 'test election 3 shortdesc' in output.data) - self.assertTrue( - '' - in output.data) - - # Invalid candidate id - data = { - 'Toshio': 1, - 'kevin': 3, - 'Ralph': 2, - 'action': 'preview', - } - - output = self.app.post('/vote/test_election3', data=data) - self.assertEqual(output.status_code, 200) - self.assertEqual( - output.data.count(''), - 3) - - csrf_token = output.data.split( - 'name="csrf_token" type="hidden" value="')[1].split('">')[0] - - # Invalid candidate id - data = { - 'Toshio': 1, - 'Kevin': 3, - 'Ralph': 2, - 'action': 'preview', - 'csrf_token': csrf_token, - } - - output = self.app.post('/vote/test_election3', data=data) - self.assertEqual(output.status_code, 200) - self.assertEqual( - output.data.count(''), - 3) - - # Invalid vote: too low - data = { - '9': -1, - '6': 0, - '5': 2, - 'action': 'preview', - 'csrf_token': csrf_token, - } - - output = self.app.post('/vote/test_election3', data=data) - self.assertEqual(output.status_code, 200) - self.assertTrue( - 'test election 3 shortdesc' in output.data) - self.assertTrue( - '' - in output.data) - self.assertEqual( - output.data.count(''), - 1) - - # Invalid vote: 2 are too high - data = { - '9': 5, - '6': 3, - '5': 2, - 'action': 'preview', - 'csrf_token': csrf_token, - } - - output = self.app.post('/vote/test_election3', data=data) - self.assertEqual(output.status_code, 200) - self.assertTrue( - 'test election 3 shortdesc' in output.data) - self.assertTrue( - '' - in output.data) - self.assertEqual( - output.data.count(''), - 2) - - # Invalid vote: Not numeric - data = { - '9': 'a', - '6': 0, - '5': 2, - 'action': 'preview', - 'csrf_token': csrf_token, - } - - output = self.app.post('/vote/test_election3', data=data) - self.assertEqual(output.status_code, 200) - self.assertTrue( - 'test election 3 shortdesc' in output.data) - self.assertTrue( - '' - in output.data) - self.assertEqual( - output.data.count(''), - 1) - - # Valid input - data = { - '4': 1, - '6': 0, - '5': 2, - 'action': 'preview', - 'csrf_token': csrf_token, - } - - output = self.app.post('/vote/test_election3', data=data) - self.assertEqual(output.status_code, 200) - self.assertTrue( - 'test election 3 shortdesc' in output.data) - self.assertTrue( - '' - in output.data) + with user_set(fedora_elections.APP, user, oidc_id_token='foobar'): + with patch( + 'fedora_elections.OIDC.user_getfield', + MagicMock(return_value=['voters'])): + output = self.app.get( + '/vote/test_election3') + self.assertTrue( + 'test election 3 shortdesc' in output.data) + self.assertTrue( + '' + in output.data) + + # Invalid candidate id + data = { + 'Toshio': 1, + 'kevin': 3, + 'Ralph': 2, + 'action': 'preview', + } + + output = self.app.post('/vote/test_election3', data=data) + self.assertEqual(output.status_code, 200) + self.assertEqual( + output.data.count(''), + 3) + + csrf_token = output.data.split( + 'name="csrf_token" type="hidden" value="')[1].split('">')[0] + + # Invalid candidate id + data = { + 'Toshio': 1, + 'Kevin': 3, + 'Ralph': 2, + 'action': 'preview', + 'csrf_token': csrf_token, + } + + output = self.app.post('/vote/test_election3', data=data) + self.assertEqual(output.status_code, 200) + self.assertEqual( + output.data.count(''), + 3) + + # Invalid vote: too low + data = { + '9': -1, + '6': 0, + '5': 2, + 'action': 'preview', + 'csrf_token': csrf_token, + } + + output = self.app.post('/vote/test_election3', data=data) + self.assertEqual(output.status_code, 200) + self.assertTrue( + 'test election 3 shortdesc' in output.data) + self.assertTrue( + '' + in output.data) + self.assertEqual( + output.data.count(''), + 1) + + # Invalid vote: 2 are too high + data = { + '9': 5, + '6': 3, + '5': 2, + 'action': 'preview', + 'csrf_token': csrf_token, + } + + output = self.app.post('/vote/test_election3', data=data) + self.assertEqual(output.status_code, 200) + self.assertTrue( + 'test election 3 shortdesc' in output.data) + self.assertTrue( + '' + in output.data) + self.assertEqual( + output.data.count(''), + 2) + + # Invalid vote: Not numeric + data = { + '9': 'a', + '6': 0, + '5': 2, + 'action': 'preview', + 'csrf_token': csrf_token, + } + + output = self.app.post('/vote/test_election3', data=data) + self.assertEqual(output.status_code, 200) + self.assertTrue( + 'test election 3 shortdesc' in output.data) + self.assertTrue( + '' + in output.data) + self.assertEqual( + output.data.count(''), + 1) + + # Valid input + data = { + '4': 1, + '6': 0, + '5': 2, + 'action': 'preview', + 'csrf_token': csrf_token, + } + + output = self.app.post('/vote/test_election3', data=data) + self.assertEqual(output.status_code, 200) + self.assertTrue( + 'test election 3 shortdesc' in output.data) + self.assertTrue( + '' + in output.data) def test_vote_range_process(self): """ Test the vote_range function - the voting part. """ - output = self.app.get( - '/vote/test_election', follow_redirects=True) - self.assertEqual(output.status_code, 200) - self.assertTrue( - 'OpenID transaction in progress' in output.data - or 'discoveryfailure' in output.data) + output = self.app.get('/vote/test_election') + self.assertEqual(output.status_code, 302) + self.assertIn( + '/login?next=http%3A%2F%2Flocalhost%2Fvote%2Ftest_election', + output.data) self.setup_db() user = FakeUser(['voters'], username='pingou') - with user_set(fedora_elections.APP, user): - # No csrf token provided - data = { - 'Toshio': 1, - 'Kevin': 3, - 'Ralph': 2, - 'action': 'submit', - } - - output = self.app.post('/vote/test_election3', data=data) - self.assertEqual(output.status_code, 200) - self.assertEqual( - output.data.count(''), - 3) - - csrf_token = output.data.split( - 'name="csrf_token" type="hidden" value="')[1].split('">')[0] - - # Invalid vote: invalid username - data = { - 'Toshio': 1, - 'Kevin': 3, - 'Ralph': 2, - 'action': 'submit', - } - - output = self.app.post('/vote/test_election3', data=data) - self.assertEqual(output.status_code, 200) - self.assertEqual( - output.data.count(''), - 3) - - # Invalid vote: too low - data = { - '4': -1, - '5': 0, - '6': 2, - 'action': 'submit', - 'csrf_token': csrf_token, - } - - output = self.app.post( - '/vote/test_election3', data=data) - self.assertEqual(output.status_code, 200) - self.assertEqual( - output.data.count(''), - 1) - - # Invalid vote: 2 are too high - data = { - '4': 5, - '5': 3, - '6': 2, - 'action': 'submit', - 'csrf_token': csrf_token, - } - - output = self.app.post( - '/vote/test_election3', data=data, follow_redirects=True) - self.assertEqual(output.status_code, 200) - self.assertEqual( - output.data.count(''), - 2) - - # Invalid vote: Not numeric - data = { - '4': 'a', - '5': 0, - '6': 2, - 'action': 'submit', - 'csrf_token': csrf_token, - } - - output = self.app.post( - '/vote/test_election3', data=data, follow_redirects=True) - self.assertEqual(output.status_code, 200) - self.assertEqual( - output.data.count(''), - 1) - - # Valid input - data = { - '4': 1, - '5': 0, - '6': 2, - 'action': 'submit', - 'csrf_token': csrf_token, - } - - output = self.app.post( - '/vote/test_election3', data=data, follow_redirects=True) - self.assertEqual(output.status_code, 200) - self.assertTrue( - 'Your vote has been recorded. Thank you!' - in output.data) - self.assertTrue('Open elections' in output.data) + with user_set(fedora_elections.APP, user, oidc_id_token='foobar'): + with patch( + 'fedora_elections.OIDC.user_getfield', + MagicMock(return_value=['voters'])): + # No csrf token provided + data = { + 'Toshio': 1, + 'Kevin': 3, + 'Ralph': 2, + 'action': 'submit', + } + + output = self.app.post('/vote/test_election3', data=data) + self.assertEqual(output.status_code, 200) + self.assertEqual( + output.data.count(''), + 3) + + csrf_token = output.data.split( + 'name="csrf_token" type="hidden" value="')[1].split('">')[0] + + # Invalid vote: invalid username + data = { + 'Toshio': 1, + 'Kevin': 3, + 'Ralph': 2, + 'action': 'submit', + } + + output = self.app.post('/vote/test_election3', data=data) + self.assertEqual(output.status_code, 200) + self.assertEqual( + output.data.count(''), + 3) + + # Invalid vote: too low + data = { + '4': -1, + '5': 0, + '6': 2, + 'action': 'submit', + 'csrf_token': csrf_token, + } + + output = self.app.post( + '/vote/test_election3', data=data) + self.assertEqual(output.status_code, 200) + self.assertEqual( + output.data.count(''), + 1) + + # Invalid vote: 2 are too high + data = { + '4': 5, + '5': 3, + '6': 2, + 'action': 'submit', + 'csrf_token': csrf_token, + } + + output = self.app.post( + '/vote/test_election3', data=data, follow_redirects=True) + self.assertEqual(output.status_code, 200) + self.assertEqual( + output.data.count(''), + 2) + + # Invalid vote: Not numeric + data = { + '4': 'a', + '5': 0, + '6': 2, + 'action': 'submit', + 'csrf_token': csrf_token, + } + + output = self.app.post( + '/vote/test_election3', data=data, follow_redirects=True) + self.assertEqual(output.status_code, 200) + self.assertEqual( + output.data.count(''), + 1) + + # Valid input + data = { + '4': 1, + '5': 0, + '6': 2, + 'action': 'submit', + 'csrf_token': csrf_token, + } + + output = self.app.post( + '/vote/test_election3', data=data, follow_redirects=True) + self.assertEqual(output.status_code, 200) + self.assertTrue( + 'Your vote has been recorded. Thank you!' + in output.data) + self.assertTrue('Open elections' in output.data) def test_vote_range_revote(self): """ Test the vote_range function - the re-voting part. """ @@ -289,58 +294,61 @@ class FlaskRangeElectionstests(ModelFlasktests): self.setup_db() user = FakeUser(['voters'], username='nerdsville') - with user_set(fedora_elections.APP, user): - retrieve_csrf = self.app.post('/vote/test_election3') - csrf_token = retrieve_csrf.data.split( - 'name="csrf_token" type="hidden" value="')[1].split('">')[0] - data = { - '4': 1, - '5': 0, - '6': 2, - 'action': 'submit', - 'csrf_token': csrf_token, - } - self.app.post('/vote/test_election3', data=data, follow_redirects=True) - vote = fedora_elections.models.Vote - votes = vote.of_user_on_election(self.session, "nerdsville", '3') - self.assertEqual(votes[0].value, 1) - self.assertEqual(votes[1].value, 0) - self.assertEqual(votes[2].value, 2) - #Let's not do repetition of what is tested above we aren't testing the - #functionality of voting as that has already been asserted - - obj = fedora_elections.models.Candidate( # id:16 - election_id=3, - name='Josh', - url='https://fedoraproject.org/wiki/User:Nerdsville', - ) - self.session.add(obj) - self.session.commit() - - - #Next, we need to try revoting - newdata = { - '4': 2, - '5': 1, - '6': 1, - '16': 0, - 'action': 'submit', - 'csrf_token': csrf_token, - } - output = self.app.post('/vote/test_election3', data=newdata, follow_redirects=True) - #Next, we need to check if the vote has been recorded - self.assertEqual(output.status_code, 200) - self.assertTrue( - 'Your vote has been recorded. Thank you!' - in output.data) - self.assertTrue('Open elections' in output.data) - vote = fedora_elections.models.Vote - votes = vote.of_user_on_election(self.session, "nerdsville", '3') - self.assertEqual(votes[0].value, 2) - self.assertEqual(votes[1].value, 1) - self.assertEqual(votes[2].value, 1) - self.assertEqual(votes[3].value, 0) - #If we haven't failed yet, HOORAY! + with user_set(fedora_elections.APP, user, oidc_id_token='foobar'): + with patch( + 'fedora_elections.OIDC.user_getfield', + MagicMock(return_value=['voters'])): + retrieve_csrf = self.app.post('/vote/test_election3') + csrf_token = retrieve_csrf.data.split( + 'name="csrf_token" type="hidden" value="')[1].split('">')[0] + data = { + '4': 1, + '5': 0, + '6': 2, + 'action': 'submit', + 'csrf_token': csrf_token, + } + self.app.post('/vote/test_election3', data=data, follow_redirects=True) + vote = fedora_elections.models.Vote + votes = vote.of_user_on_election(self.session, "nerdsville", '3') + self.assertEqual(votes[0].value, 1) + self.assertEqual(votes[1].value, 0) + self.assertEqual(votes[2].value, 2) + #Let's not do repetition of what is tested above we aren't testing the + #functionality of voting as that has already been asserted + + obj = fedora_elections.models.Candidate( # id:16 + election_id=3, + name='Josh', + url='https://fedoraproject.org/wiki/User:Nerdsville', + ) + self.session.add(obj) + self.session.commit() + + + #Next, we need to try revoting + newdata = { + '4': 2, + '5': 1, + '6': 1, + '16': 0, + 'action': 'submit', + 'csrf_token': csrf_token, + } + output = self.app.post('/vote/test_election3', data=newdata, follow_redirects=True) + #Next, we need to check if the vote has been recorded + self.assertEqual(output.status_code, 200) + self.assertTrue( + 'Your vote has been recorded. Thank you!' + in output.data) + self.assertTrue('Open elections' in output.data) + vote = fedora_elections.models.Vote + votes = vote.of_user_on_election(self.session, "nerdsville", '3') + self.assertEqual(votes[0].value, 2) + self.assertEqual(votes[1].value, 1) + self.assertEqual(votes[2].value, 1) + self.assertEqual(votes[3].value, 0) + #If we haven't failed yet, HOORAY! if __name__ == '__main__': diff --git a/tests/test_flask_select_voting.py b/tests/test_flask_select_voting.py index 61412be..8af5fb8 100644 --- a/tests/test_flask_select_voting.py +++ b/tests/test_flask_select_voting.py @@ -32,6 +32,7 @@ from datetime import time from datetime import timedelta import flask +from mock import patch, MagicMock sys.path.insert(0, os.path.join(os.path.dirname( os.path.abspath(__file__)), '..')) @@ -46,117 +47,121 @@ class FlaskSimpleElectionstests(ModelFlasktests): def test_vote_select(self): """ Test the vote_select function - the preview part. """ - output = self.app.get( - '/vote/test_election', follow_redirects=True) - self.assertEqual(output.status_code, 200) - self.assertTrue( - 'OpenID transaction in progress' in output.data - or 'discoveryfailure' in output.data) + output = self.app.get('/vote/test_election') + self.assertEqual(output.status_code, 302) + self.assertIn( + '/login?next=http%3A%2F%2Flocalhost%2Fvote%2Ftest_election', + output.data) self.setup_db() user = FakeUser(['packager'], username='pingou') - with user_set(fedora_elections.APP, user): - output = self.app.get( - '/vote/test_election6') - self.assertTrue( - 'test election 6 shortdesc' in output.data) - self.assertTrue( - '' - in output.data) - - csrf_token = output.data.split( - 'name="csrf_token" type="hidden" value="')[1].split('">')[0] - - # Invalid vote: No candidate - data = { - 'action': 'preview', - } - - output = self.app.post('/vote/test_election6', data=data) - self.assertEqual(output.status_code, 200) - self.assertTrue( - 'test election 6 shortdesc' in output.data) - - # Invalid vote: Too many candidates - data = { - 'Kevin': True, - 'Toshio': True, - 'action': 'preview', - 'csrf_token': csrf_token, - } - - output = self.app.post('/vote/test_election6', data=data) - self.assertEqual(output.status_code, 200) - self.assertTrue( - 'test election 6 shortdesc' in output.data) - self.assertTrue( - '' - in output.data) - self.assertTrue( - 'Too many candidates submitted' - in output.data) - - # Valid input - data = { - 'Kevin': True, - 'action': 'preview', - 'csrf_token': csrf_token, - } - - output = self.app.post('/vote/test_election6', data=data) - self.assertEqual(output.status_code, 200) - self.assertTrue( - 'test election 6 shortdesc' in output.data) - self.assertTrue( - '' - in output.data) - self.assertTrue( - 'Please confirm your vote!' - in output.data) + with user_set(fedora_elections.APP, user, oidc_id_token='foobar'): + with patch( + 'fedora_elections.OIDC.user_getfield', + MagicMock(return_value=['packager'])): + output = self.app.get( + '/vote/test_election6') + self.assertTrue( + 'test election 6 shortdesc' in output.data) + self.assertTrue( + '' + in output.data) + + csrf_token = output.data.split( + 'name="csrf_token" type="hidden" value="')[1].split('">')[0] + + # Invalid vote: No candidate + data = { + 'action': 'preview', + } + + output = self.app.post('/vote/test_election6', data=data) + self.assertEqual(output.status_code, 200) + self.assertTrue( + 'test election 6 shortdesc' in output.data) + + # Invalid vote: Too many candidates + data = { + 'Kevin': True, + 'Toshio': True, + 'action': 'preview', + 'csrf_token': csrf_token, + } + + output = self.app.post('/vote/test_election6', data=data) + self.assertEqual(output.status_code, 200) + self.assertTrue( + 'test election 6 shortdesc' in output.data) + self.assertTrue( + '' + in output.data) + self.assertTrue( + 'Too many candidates submitted' + in output.data) + + # Valid input + data = { + 'Kevin': True, + 'action': 'preview', + 'csrf_token': csrf_token, + } + + output = self.app.post('/vote/test_election6', data=data) + self.assertEqual(output.status_code, 200) + self.assertTrue( + 'test election 6 shortdesc' in output.data) + self.assertTrue( + '' + in output.data) + self.assertTrue( + 'Please confirm your vote!' + in output.data) def test_vote_select_process(self): """ Test the vote_select function - the voting part. """ - output = self.app.get( - '/vote/test_election', follow_redirects=True) - self.assertEqual(output.status_code, 200) - self.assertTrue( - 'OpenID transaction in progress' in output.data - or 'discoveryfailure' in output.data) + output = self.app.get('/vote/test_election') + self.assertEqual(output.status_code, 302) + self.assertIn( + '/login?next=http%3A%2F%2Flocalhost%2Fvote%2Ftest_election', + output.data) self.setup_db() user = FakeUser(['packager'], username='pingou') - with user_set(fedora_elections.APP, user): - # Invalid candidate id - no csrf - data = { - 'candidate': 1, - 'action': 'submit', - } - - output = self.app.post( - '/vote/test_election6', data=data, - follow_redirects=True) - self.assertEqual(output.status_code, 200) - - csrf_token = output.data.split( - 'name="csrf_token" type="hidden" value="')[1].split('">')[0] - - # Valid input - data = { - 'Toshio': True, - 'action': 'submit', - 'csrf_token': csrf_token, - } - - output = self.app.post( - '/vote/test_election6', data=data, - follow_redirects=True) - self.assertEqual(output.status_code, 200) - self.assertTrue( - 'Your vote has been recorded. Thank you!' - in output.data) - self.assertTrue('Open elections' in output.data) + with user_set(fedora_elections.APP, user, oidc_id_token='foobar'): + with patch( + 'fedora_elections.OIDC.user_getfield', + MagicMock(return_value=['packager'])): + # Invalid candidate id - no csrf + data = { + 'candidate': 1, + 'action': 'submit', + } + + output = self.app.post( + '/vote/test_election6', data=data, + follow_redirects=True) + self.assertEqual(output.status_code, 200) + + csrf_token = output.data.split( + 'name="csrf_token" type="hidden" value="')[1].split('">')[0] + + # Valid input + data = { + 'Toshio': True, + 'action': 'submit', + 'csrf_token': csrf_token, + } + + output = self.app.post( + '/vote/test_election6', data=data, + follow_redirects=True) + self.assertEqual(output.status_code, 200) + self.assertTrue( + 'Your vote has been recorded. Thank you!' + in output.data) + self.assertTrue('Open elections' in output.data) def test_vote_select_revote(self): """ Test the vote_select function - the re-voting part. """ @@ -164,45 +169,47 @@ class FlaskSimpleElectionstests(ModelFlasktests): self.setup_db() user = FakeUser(['voters'], username='nerdsville') - with user_set(fedora_elections.APP, user): - retrieve_csrf = self.app.post('/vote/test_election6') - csrf_token = retrieve_csrf.data.split( - 'name="csrf_token" type="hidden" value="')[1].split('">')[0] - - # Valid input - data = { - 'Kevin': True, - 'action': 'submit', - 'csrf_token': csrf_token, - } - self.app.post('/vote/test_election6', data=data, follow_redirects=True) - vote = fedora_elections.models.Vote - votes = vote.of_user_on_election(self.session, "nerdsville", '6') - self.assertEqual(votes[0].candidate.name, "Toshio") - self.assertEqual(votes[0].value, 0) - self.assertEqual(votes[1].candidate.name, "Kevin") - self.assertEqual(votes[1].value, 1) - - #Next, we need to try revoting - newdata = { - 'Toshio': True, - 'action': 'submit', - 'csrf_token': csrf_token, - } - output = self.app.post('/vote/test_election6', data=newdata, follow_redirects=True) - #Next, we need to check if the vote has been recorded - self.assertEqual(output.status_code, 200) - self.assertTrue( - 'Your vote has been recorded. Thank you!' - in output.data) - self.assertTrue('Open elections' in output.data) - vote = fedora_elections.models.Vote - votes = vote.of_user_on_election(self.session, "nerdsville", '6') - self.assertEqual(votes[0].value, 1) - self.assertEqual(votes[1].value, 0) - - - #If we haven't failed yet, HOORAY! + with user_set(fedora_elections.APP, user, oidc_id_token='foobar'): + with patch( + 'fedora_elections.OIDC.user_getfield', + MagicMock(return_value=['voters'])): + retrieve_csrf = self.app.post('/vote/test_election6') + csrf_token = retrieve_csrf.data.split( + 'name="csrf_token" type="hidden" value="')[1].split('">')[0] + + # Valid input + data = { + 'Kevin': True, + 'action': 'submit', + 'csrf_token': csrf_token, + } + self.app.post('/vote/test_election6', data=data, follow_redirects=True) + vote = fedora_elections.models.Vote + votes = vote.of_user_on_election(self.session, "nerdsville", '6') + self.assertEqual(votes[0].candidate.name, "Toshio") + self.assertEqual(votes[0].value, 0) + self.assertEqual(votes[1].candidate.name, "Kevin") + self.assertEqual(votes[1].value, 1) + + #Next, we need to try revoting + newdata = { + 'Toshio': True, + 'action': 'submit', + 'csrf_token': csrf_token, + } + output = self.app.post('/vote/test_election6', data=newdata, follow_redirects=True) + #Next, we need to check if the vote has been recorded + self.assertEqual(output.status_code, 200) + self.assertTrue( + 'Your vote has been recorded. Thank you!' + in output.data) + self.assertTrue('Open elections' in output.data) + vote = fedora_elections.models.Vote + votes = vote.of_user_on_election(self.session, "nerdsville", '6') + self.assertEqual(votes[0].value, 1) + self.assertEqual(votes[1].value, 0) + + #If we haven't failed yet, HOORAY! if __name__ == '__main__': diff --git a/tests/test_flask_simple_voting.py b/tests/test_flask_simple_voting.py index 4076c85..fe694b7 100644 --- a/tests/test_flask_simple_voting.py +++ b/tests/test_flask_simple_voting.py @@ -32,6 +32,7 @@ from datetime import time from datetime import timedelta import flask +from mock import patch, MagicMock sys.path.insert(0, os.path.join(os.path.dirname( os.path.abspath(__file__)), '..')) @@ -46,177 +47,181 @@ class FlaskSimpleElectionstests(ModelFlasktests): def test_vote_simple(self): """ Test the vote_simple function - the preview part. """ - output = self.app.get( - '/vote/test_election', follow_redirects=True) - self.assertEqual(output.status_code, 200) - self.assertTrue( - 'OpenID transaction in progress' in output.data - or 'discoveryfailure' in output.data) + output = self.app.get('/vote/test_election') + self.assertEqual(output.status_code, 302) + self.assertIn( + '/login?next=http%3A%2F%2Flocalhost%2Fvote%2Ftest_election', + output.data) self.setup_db() user = FakeUser(['packager'], username='pingou') - with user_set(fedora_elections.APP, user): - output = self.app.get( - '/vote/test_election5') - self.assertTrue( - 'test election 5 shortdesc' in output.data) - self.assertTrue( - '' - in output.data) - - # Invalid vote: No candidate - data = { - 'action': 'preview', - } - - output = self.app.post('/vote/test_election5', data=data) - self.assertEqual(output.status_code, 200) - self.assertTrue( - 'test election 5 shortdesc' in output.data) - self.assertTrue( - 'Preview your vote' - in output.data) - self.assertTrue( - '' - in output.data) - - csrf_token = output.data.split( - 'name="csrf_token" type="hidden" value="')[1].split('">')[0] - - # Invalid vote: No candidate - data = { - 'action': 'preview', - 'csrf_token': csrf_token, - } - - output = self.app.post('/vote/test_election5', data=data) - self.assertEqual(output.status_code, 200) - self.assertTrue( - 'test election 5 shortdesc' in output.data) - self.assertTrue( - 'Preview your vote' - in output.data) - self.assertTrue( - '' - in output.data) - - # Invalid vote: Not numeric - data = { - 'candidate': 'a', - 'action': 'preview', - 'csrf_token': csrf_token, - } - - output = self.app.post('/vote/test_election5', data=data) - self.assertEqual(output.status_code, 200) - self.assertTrue( - 'test election 5 shortdesc' in output.data) - self.assertTrue( - '' - in output.data) - - - # Valid input - data = { - 'candidate': 7, - 'action': 'preview', - 'csrf_token': csrf_token, - } - - output = self.app.post('/vote/test_election5', data=data) - self.assertEqual(output.status_code, 200) - self.assertTrue( - 'test election 5 shortdesc' in output.data) - self.assertTrue( - '' - in output.data) - self.assertTrue( - 'Please confirm your vote!' - in output.data) + with user_set(fedora_elections.APP, user, oidc_id_token='foobar'): + with patch( + 'fedora_elections.OIDC.user_getfield', + MagicMock(return_value=['voters'])): + output = self.app.get( + '/vote/test_election5') + self.assertTrue( + 'test election 5 shortdesc' in output.data) + self.assertTrue( + '' + in output.data) + + # Invalid vote: No candidate + data = { + 'action': 'preview', + } + + output = self.app.post('/vote/test_election5', data=data) + self.assertEqual(output.status_code, 200) + self.assertTrue( + 'test election 5 shortdesc' in output.data) + self.assertTrue( + 'Preview your vote' + in output.data) + self.assertTrue( + '' + in output.data) + + csrf_token = output.data.split( + 'name="csrf_token" type="hidden" value="')[1].split('">')[0] + + # Invalid vote: No candidate + data = { + 'action': 'preview', + 'csrf_token': csrf_token, + } + + output = self.app.post('/vote/test_election5', data=data) + self.assertEqual(output.status_code, 200) + self.assertTrue( + 'test election 5 shortdesc' in output.data) + self.assertTrue( + 'Preview your vote' + in output.data) + self.assertTrue( + '' + in output.data) + + # Invalid vote: Not numeric + data = { + 'candidate': 'a', + 'action': 'preview', + 'csrf_token': csrf_token, + } + + output = self.app.post('/vote/test_election5', data=data) + self.assertEqual(output.status_code, 200) + self.assertTrue( + 'test election 5 shortdesc' in output.data) + self.assertTrue( + '' + in output.data) + + + # Valid input + data = { + 'candidate': 7, + 'action': 'preview', + 'csrf_token': csrf_token, + } + + output = self.app.post('/vote/test_election5', data=data) + self.assertEqual(output.status_code, 200) + self.assertTrue( + 'test election 5 shortdesc' in output.data) + self.assertTrue( + '' + in output.data) + self.assertTrue( + 'Please confirm your vote!' + in output.data) def test_vote_simple_process(self): """ Test the vote_simple function - the voting part. """ - output = self.app.get( - '/vote/test_election', follow_redirects=True) - self.assertEqual(output.status_code, 200) - self.assertTrue( - 'OpenID transaction in progress' in output.data - or 'discoveryfailure' in output.data) + output = self.app.get('/vote/test_election') + self.assertEqual(output.status_code, 302) + self.assertIn( + '/login?next=http%3A%2F%2Flocalhost%2Fvote%2Ftest_election', + output.data) self.setup_db() user = FakeUser(['packager'], username='pingou') - with user_set(fedora_elections.APP, user): - # Invalid candidate id - no csrf - data = { - 'candidate': 1, - 'action': 'submit', - } - - output = self.app.post( - '/vote/test_election5', data=data, - follow_redirects=True) - self.assertEqual(output.status_code, 200) - - - csrf_token = output.data.split( - 'name="csrf_token" type="hidden" value="')[1].split('">')[0] - - # Invalid candidate id - data = { - 'candidate': 1, - 'action': 'submit', - 'csrf_token': csrf_token, - } - - output = self.app.post( - '/vote/test_election5', data=data, - follow_redirects=True) - self.assertEqual(output.status_code, 200) - - - # Invalid vote: too low - data = { - 'candidate': -1, - 'action': 'submit', - 'csrf_token': csrf_token, - } - - output = self.app.post( - '/vote/test_election5', data=data, - follow_redirects=True) - self.assertEqual(output.status_code, 200) - - - # Invalid vote: Not numeric - data = { - 'candidate': 'a', - 'action': 'submit', - 'csrf_token': csrf_token, - } - - output = self.app.post( - '/vote/test_election5', data=data, - follow_redirects=True) - self.assertEqual(output.status_code, 200) - - - # Valid input - data = { - 'candidate': 8, - 'action': 'submit', - 'csrf_token': csrf_token, - } - - output = self.app.post( - '/vote/test_election5', data=data, - follow_redirects=True) - self.assertEqual(output.status_code, 200) - self.assertTrue( - 'Your vote has been recorded. Thank you!' - in output.data) - self.assertTrue('Open elections' in output.data) + with user_set(fedora_elections.APP, user, oidc_id_token='foobar'): + with patch( + 'fedora_elections.OIDC.user_getfield', + MagicMock(return_value=['voters'])): + # Invalid candidate id - no csrf + data = { + 'candidate': 1, + 'action': 'submit', + } + + output = self.app.post( + '/vote/test_election5', data=data, + follow_redirects=True) + self.assertEqual(output.status_code, 200) + + + csrf_token = output.data.split( + 'name="csrf_token" type="hidden" value="')[1].split('">')[0] + + # Invalid candidate id + data = { + 'candidate': 1, + 'action': 'submit', + 'csrf_token': csrf_token, + } + + output = self.app.post( + '/vote/test_election5', data=data, + follow_redirects=True) + self.assertEqual(output.status_code, 200) + + + # Invalid vote: too low + data = { + 'candidate': -1, + 'action': 'submit', + 'csrf_token': csrf_token, + } + + output = self.app.post( + '/vote/test_election5', data=data, + follow_redirects=True) + self.assertEqual(output.status_code, 200) + + + # Invalid vote: Not numeric + data = { + 'candidate': 'a', + 'action': 'submit', + 'csrf_token': csrf_token, + } + + output = self.app.post( + '/vote/test_election5', data=data, + follow_redirects=True) + self.assertEqual(output.status_code, 200) + + + # Valid input + data = { + 'candidate': 8, + 'action': 'submit', + 'csrf_token': csrf_token, + } + + output = self.app.post( + '/vote/test_election5', data=data, + follow_redirects=True) + self.assertEqual(output.status_code, 200) + self.assertTrue( + 'Your vote has been recorded. Thank you!' + in output.data) + self.assertTrue('Open elections' in output.data) def test_vote_simple_revote(self): """ Test the vote_simple function - the re-voting part. """ @@ -224,43 +229,46 @@ class FlaskSimpleElectionstests(ModelFlasktests): self.setup_db() user = FakeUser(['voters'], username='nerdsville') - with user_set(fedora_elections.APP, user): - retrieve_csrf = self.app.post('/vote/test_election5') - csrf_token = retrieve_csrf.data.split( - 'name="csrf_token" type="hidden" value="')[1].split('">')[0] - # Valid input - data = { - 'candidate': 8, - 'action': 'submit', - 'csrf_token': csrf_token, - } - - self.app.post('/vote/test_election5', data=data, follow_redirects=True) - vote = fedora_elections.models.Vote - votes = vote.of_user_on_election(self.session, "nerdsville", '5') - self.assertEqual(votes[0].candidate_id, 8) - #Let's not do repetition of what is tested above we aren't testing the - #functionality of voting as that has already been asserted - - #Next, we need to try revoting - # Valid input - newdata = { - 'candidate': 9, - 'action': 'submit', - 'csrf_token': csrf_token, - } - output = self.app.post('/vote/test_election5', data=newdata, follow_redirects=True) - #Next, we need to check if the vote has been recorded - self.assertEqual(output.status_code, 200) - self.assertTrue( - 'Your vote has been recorded. Thank you!' - in output.data) - self.assertTrue('Open elections' in output.data) - vote = fedora_elections.models.Vote - votes = vote.of_user_on_election(self.session, "nerdsville", '5') - self.assertEqual(votes[0].candidate_id, 9) - - #If we haven't failed yet, HOORAY! + with user_set(fedora_elections.APP, user, oidc_id_token='foobar'): + with patch( + 'fedora_elections.OIDC.user_getfield', + MagicMock(return_value=['voters'])): + retrieve_csrf = self.app.post('/vote/test_election5') + csrf_token = retrieve_csrf.data.split( + 'name="csrf_token" type="hidden" value="')[1].split('">')[0] + # Valid input + data = { + 'candidate': 8, + 'action': 'submit', + 'csrf_token': csrf_token, + } + + self.app.post('/vote/test_election5', data=data, follow_redirects=True) + vote = fedora_elections.models.Vote + votes = vote.of_user_on_election(self.session, "nerdsville", '5') + self.assertEqual(votes[0].candidate_id, 8) + #Let's not do repetition of what is tested above we aren't testing the + #functionality of voting as that has already been asserted + + #Next, we need to try revoting + # Valid input + newdata = { + 'candidate': 9, + 'action': 'submit', + 'csrf_token': csrf_token, + } + output = self.app.post('/vote/test_election5', data=newdata, follow_redirects=True) + #Next, we need to check if the vote has been recorded + self.assertEqual(output.status_code, 200) + self.assertTrue( + 'Your vote has been recorded. Thank you!' + in output.data) + self.assertTrue('Open elections' in output.data) + vote = fedora_elections.models.Vote + votes = vote.of_user_on_election(self.session, "nerdsville", '5') + self.assertEqual(votes[0].candidate_id, 9) + + #If we haven't failed yet, HOORAY! if __name__ == '__main__': diff --git a/tox.ini b/tox.ini new file mode 100644 index 0000000..4d3302a --- /dev/null +++ b/tox.ini @@ -0,0 +1,26 @@ +[tox] +envlist = py27,diff-cover +skipsdist = True + +[testenv] +usedevelop = True +passenv = HOME +deps = + -rrequirements.txt + mock + nose + coverage +setenv = + PYTHONPATH={toxinidir} + FEDORA_ELECTIONS_CONFIG={toxinidir}/tests/config +commands = + nosetests --with-coverage --cover-erase \ + --cover-package=fedora_elections --cover-xml \ + {posargs} + +[testenv:diff-cover] +deps = + diff-cover +commands = + diff-cover coverage.xml --compare-branch=origin/develop --fail-under=100 + From 6ac21a672fda12e3897865748060bd0f6d7ecdcc Mon Sep 17 00:00:00 2001 From: Ben Cotton Date: Mar 22 2019 14:11:03 +0000 Subject: [PATCH 23/24] Merge remote-tracking branch 'upstream/alembic_migration' into add_voted_badge --- diff --git a/alembic/versions/5ecdd55b4af4_add_badge_support_to_elections.py b/alembic/versions/5ecdd55b4af4_add_badge_support_to_elections.py new file mode 100644 index 0000000..716bd54 --- /dev/null +++ b/alembic/versions/5ecdd55b4af4_add_badge_support_to_elections.py @@ -0,0 +1,27 @@ +"""Add badge support to elections + +Revision ID: 5ecdd55b4af4 +Revises: 2b8f5a6f10a4 +Create Date: 2019-03-18 12:21:59.536380 + +""" + +# revision identifiers, used by Alembic. +revision = '5ecdd55b4af4' +down_revision = '2b8f5a6f10a4' + +from alembic import op +import sqlalchemy as sa + + +def upgrade(): + """ Add the voted_badge column to the Elections table. """ + op.add_column( + 'elections', + sa.Column('voted_badge', sa.Unicde(250), nullable=True) + ) + + +def downgrade(): + """ Drop the voted_badge column from the Elections table. """ + op.drop_column('elections', 'voted_badge') From 476f852b25b5e6eab74b1bf8df192a5322b3487c Mon Sep 17 00:00:00 2001 From: Ben Cotton Date: Mar 22 2019 14:17:26 +0000 Subject: [PATCH 24/24] Update the field name and correct the nullability --- diff --git a/alembic/versions/5ecdd55b4af4_add_badge_support_to_elections.py b/alembic/versions/5ecdd55b4af4_add_badge_support_to_elections.py index 716bd54..1e73942 100644 --- a/alembic/versions/5ecdd55b4af4_add_badge_support_to_elections.py +++ b/alembic/versions/5ecdd55b4af4_add_badge_support_to_elections.py @@ -15,13 +15,13 @@ import sqlalchemy as sa def upgrade(): - """ Add the voted_badge column to the Elections table. """ + """ Add the url_badge column to the Elections table. """ op.add_column( 'elections', - sa.Column('voted_badge', sa.Unicde(250), nullable=True) + sa.Column('url_badge', sa.Unicde(250), nullable=True) ) def downgrade(): - """ Drop the voted_badge column from the Elections table. """ - op.drop_column('elections', 'voted_badge') + """ Drop the url_badge column from the Elections table. """ + op.drop_column('elections', 'url_badge') diff --git a/fedora_elections/admin.py b/fedora_elections/admin.py index a1aafd4..c34694d 100644 --- a/fedora_elections/admin.py +++ b/fedora_elections/admin.py @@ -74,7 +74,7 @@ def admin_new_election(): end_date=form.end_date.data, seats_elected=form.seats_elected.data, embargoed=int(form.embargoed.data), - voted_badge=form.voted_badge.data, + url_badge=form.url_badge.data, voting_type=form.voting_type.data, max_votes=form.max_votes.data, candidates_are_fasusers=int(form.candidates_are_fasusers.data), diff --git a/fedora_elections/elections.py b/fedora_elections/elections.py index 24be36c..41a2804 100644 --- a/fedora_elections/elections.py +++ b/fedora_elections/elections.py @@ -354,7 +354,7 @@ def process_vote( def say_thank_you(election): thank_you = "Your vote has been recorded. Thank you!" - if election.voted_badge: + if election.url_badge: thank_you = thank_you + '
Claim your I Voted badge.' + election.url_badge + '" target=_new>Claim your I Voted badge.' flask.flash(thank_you) diff --git a/fedora_elections/forms.py b/fedora_elections/forms.py index 54d2e0c..1c3fb75 100644 --- a/fedora_elections/forms.py +++ b/fedora_elections/forms.py @@ -74,7 +74,7 @@ class ElectionForm(FlaskForm): embargoed = wtforms.BooleanField('Embargo results?', default=True) - voted_badge = wtforms.TextField( + url_badge = wtforms.TextField( 'Badge URL (optional)', [ wtforms.validators.Optional(), wtforms.validators.URL(), diff --git a/fedora_elections/models.py b/fedora_elections/models.py index b91a252..b82af00 100644 --- a/fedora_elections/models.py +++ b/fedora_elections/models.py @@ -86,7 +86,7 @@ class Election(BASE): seats_elected = sa.Column(sa.Integer, nullable=False, default=1) embargoed = sa.Column(sa.Integer, nullable=False, default=0) voting_type = sa.Column(sa.Unicode(100), nullable=False, default=u'range') - voted_badge = sa.Column(sa.Unicode(250), nullable=False) + url_badge = sa.Column(sa.Unicode(250), nullable=True) max_votes = sa.Column(sa.Integer, nullable=True) candidates_are_fasusers = sa.Column( sa.Integer, nullable=False, default=0) diff --git a/fedora_elections/templates/_formhelpers.html b/fedora_elections/templates/_formhelpers.html index f90bb94..0683977 100644 --- a/fedora_elections/templates/_formhelpers.html +++ b/fedora_elections/templates/_formhelpers.html @@ -112,7 +112,7 @@ {{ render_bootstrap_textfield_in_row(form.seats_elected) }} {{ render_bootstrap_checkbox_in_row(form.candidates_are_fasusers) }} {{ render_bootstrap_checkbox_in_row(form.embargoed) }} - {{ render_bootstrap_textfield_in_row(form.voted_badge) }} + {{ render_bootstrap_textfield_in_row(form.url_badge) }} {{ render_bootstrap_textfield_in_row(form.lgl_voters, after="FAS groups allowed to vote on this election (CLA-done is always required)") }} {{ render_bootstrap_textfield_in_row(form.admin_grp, after="FAS groups allowed to view the result despite the embargo") }}
CandidateVotesVoters per candidateAverage votes per candidate
CandidateVotesVoters per candidateAverage votes per candidate
{% if candidate.url %} @@ -154,7 +157,7 @@ {% if candidate.url %} {% endif %} - {% if not flag %} + {% if flag[-1] == 0 %} Elected {% endif %} VotesVotes