From 74021d704329584ad064abd069cab8dea9eac37a Mon Sep 17 00:00:00 2001
From: Ben Adida <ben@adida.net>
Date: Sun, 5 Jun 2011 15:34:01 -0700
Subject: [PATCH] added more true integration tests with dependencey
 django-webtest and webtest

---
 README.txt         |   4 ++
 helios/security.py |  37 +++++++++++++--
 helios/tests.py    | 113 ++++++++++++++++++++++++++++++---------------
 helios/views.py    |  24 +++-------
 4 files changed, 122 insertions(+), 56 deletions(-)

diff --git a/README.txt b/README.txt
index c89c4fa..caf0756 100644
--- a/README.txt
+++ b/README.txt
@@ -21,6 +21,10 @@ NEEDS:
 - South for schema migration
 -- easy_install South
 
+- django-webtest for testing
+-- http://pypi.python.org/pypi/django-webtest
+-- easy_install webtest
+-- easy_install django-webtest
 
 GETTING SOUTH WORKING ON EXISTING INSTALL
 - as of Helios v3.0.4, we're using South to migrate data models
diff --git a/helios/security.py b/helios/security.py
index 50e4a03..05e99b4 100644
--- a/helios/security.py
+++ b/helios/security.py
@@ -19,6 +19,23 @@ import urllib
 
 import helios
 
+# current voter
+def get_voter(request, user, election):
+  """
+  return the current voter
+  """
+  voter = None
+  if request.session.has_key('CURRENT_VOTER'):
+    voter = request.session['CURRENT_VOTER']
+    if voter.election != election:
+      voter = None
+
+  if not voter:
+    if user:
+      voter = Voter.get_by_election_and_user(election, user)
+  
+  return voter
+
 # a function to check if the current user is a trustee
 HELIOS_TRUSTEE_UUID = 'helios_trustee_uuid'
 def get_logged_in_trustee(request):
@@ -80,9 +97,8 @@ def election_view(**checks):
 
       # if private election, only logged in voters
       if election.private_p and not checks.get('allow_logins',False):
-        from views import get_voter, get_user, password_voter_login
-        user = get_user(request)
-        if not user_can_admin_election(user, election) and not get_voter(request, user, election):
+        from views import password_voter_login
+        if not user_can_see_election(request, election):
           return_url = request.get_full_path()
           return HttpResponseRedirect("%s?%s" % (reverse(password_voter_login, args=[election.uuid]), urllib.urlencode({
                   'return_url' : return_url
@@ -101,6 +117,21 @@ def user_can_admin_election(user, election):
   # election or site administrator
   return election.admin == user or user.admin_p
   
+def user_can_see_election(request, election):
+  user = get_user(request)
+
+  if not election.private_p:
+    return True
+
+  # election is private
+  
+  # but maybe this user is the administrator?
+  if user_can_admin_election(user, election):
+    return True
+
+  # then this user has to be a voter
+  return (get_voter(request, user, election) != None)
+
 def api_client_can_admin_election(api_client, election):
   return election.api_client == api_client and api_client != None
   
diff --git a/helios/tests.py b/helios/tests.py
index ad026a5..91a414f 100644
--- a/helios/tests.py
+++ b/helios/tests.py
@@ -3,6 +3,7 @@ Unit Tests for Helios
 """
 
 import unittest, datetime, re
+import django_webtest
 
 import models
 import datatypes
@@ -316,11 +317,30 @@ class LegacyElectionBlackboxTests(DataFormatBlackboxTests, TestCase):
 #    EXPECTED_TRUSTEES_FILE = 'helios/fixtures/v3.1-trustees-expected.json'
 #    EXPECTED_BALLOTS_FILE = 'helios/fixtures/v3.1-ballots-expected.json'
 
+class WebTest(django_webtest.WebTest):
+    def assertRedirects(self, response, url):
+        """
+        reimplement this in case it's a WebOp response
+        """
+        if hasattr(response, 'status_code'):
+            return super(django_webtest.WebTest, self).assertRedirects(response, url)
+
+        assert response.status_int == 302
+        assert url in response.location, "redirected to %s instead of %s" % (response.location, url)
+
+    def assertContains(self, response, text):
+        if hasattr(response, 'status_code'):
+            return super(django_webtest.WebTest, self).assertContains(response, text)
+
+        assert response.status_int == 200
+        assert text in response.testbody, "missing text %s" % text
+        
+
 ##
 ## overall operation of the system
 ##
 
-class ElectionBlackboxTests(TestCase):
+class ElectionBlackboxTests(WebTest):
     fixtures = ['users.json', 'election.json']
 
     def setUp(self):
@@ -331,7 +351,10 @@ class ElectionBlackboxTests(TestCase):
         # set up the session
         session = self.client.session
         session['user'] = {'type': self.user.user_type, 'user_id': self.user.user_id}
-        session.save()        
+        session.save()
+        
+        # set up the app, too
+        self.app.cookies['sessionid'] = self.client.cookies.get('sessionid').value
 
     def clear_login(self):
         session = self.client.session
@@ -513,38 +536,45 @@ class ElectionBlackboxTests(TestCase):
         check_user_logged_in looks for the "you're already logged" message
         """
         # vote by preparing a ballot via the server-side encryption
-        response = self.client.post("/helios/elections/%s/encrypt-ballot" % election_id, {
+        response = self.app.post("/helios/elections/%s/encrypt-ballot" % election_id, {
                 'answers_json': utils.to_json([[1]])})
         self.assertContains(response, "answers")
         
         # parse it as an encrypted vote, and re-serialize it
-        ballot = datatypes.LDObject.fromDict(utils.from_json(response.content), type_hint='legacy/EncryptedVote')
+        ballot = datatypes.LDObject.fromDict(utils.from_json(response.testbody), type_hint='legacy/EncryptedVote')
         encrypted_vote = ballot.serialize()
         
         # cast the ballot
-        response = self.client.post("/helios/elections/%s/cast" % election_id, {
+        response = self.app.post("/helios/elections/%s/cast" % election_id, {
                 'encrypted_vote': encrypted_vote})
         self.assertRedirects(response, "%s/helios/elections/%s/cast_confirm" % (settings.SECURE_URL_HOST, election_id))        
 
+        cast_confirm_page = response.follow()
+        
         if need_login:
             if check_user_logged_in:
-                response = self.client.get("/helios/elections/%s/cast_confirm" % election_id)
-                self.assertContains(response, "You are logged in as")
-                self.assertContains(response, "requires election-specific credentials")                
-
-            response = self.client.post("/helios/elections/%s/password_voter_login" % election_id, {
-                    'voter_id' : username,
-                    'password' : password
-                    })
-            self.assertRedirects(response, "/helios/elections/%s/cast_confirm" % election_id)
-        else:
-            response = self.client.get("/helios/elections/%s/cast_confirm" % election_id)
-            self.assertContains(response, "I am ")
+                self.assertContains(cast_confirm_page, "You are logged in as")
+                self.assertContains(cast_confirm_page, "requires election-specific credentials")
 
-        # confirm the vote
-        response = self.client.post("/helios/elections/%s/cast_confirm" % election_id, {
-                "csrf_token" : self.client.session['csrf_token'],
-                "status_update" : False})
+            # set the form
+            login_form = cast_confirm_page.form
+            login_form['voter_id'] = username
+            login_form['password'] = password
+
+            cast_confirm_page = login_form.submit()
+
+            self.assertRedirects(cast_confirm_page, "/helios/elections/%s/cast_confirm" % election_id)
+            cast_confirm_page = cast_confirm_page.follow()
+
+        # here we should be at the cast-confirm page and logged in
+        self.assertContains(cast_confirm_page, "I am ")
+
+        # confirm the vote, now with the actual form
+        cast_form = cast_confirm_page.form
+        
+        if 'status_update' in cast_form.fields.keys():
+            cast_form['status_update'] = False
+        response = cast_form.submit()
         self.assertRedirects(response, "%s/helios/elections/%s/cast_done" % (settings.URL_HOST, election_id))
 
         # at this point an email should have gone out to the user
@@ -559,18 +589,25 @@ class ElectionBlackboxTests(TestCase):
             # so if need_login is False, it was a private election, and we do need to re-login here
             # we need to re-login if it's a private election, because all data, including ballots
             # is otherwise private
-            response = self.client.post("/helios/elections/%s/password_voter_login" % election_id, {
-                    'voter_id' : username,
-                    'password' : password
-                    })
+            login_page = self.app.get("/helios/elections/%s/password_voter_login" % election_id)
+
+            # if we redirected, that's because we can see the page, I think
+            if login_page.status_int != 302:
+                login_form = login_page.form
+                
+                login_form['voter_id'] = username
+                login_form['password'] = password
+                login_form.submit()
             
-        response = self.client.get(url)
+        response = self.app.get(url)
         self.assertContains(response, ballot.hash)
         self.assertContains(response, html_escape(encrypted_vote))
 
         # if we request the redirect to cast_done, the voter should be logged out, but not the user
-        response = self.client.get("/helios/elections/%s/cast_done" % election_id)
-        assert not self.client.session.has_key('CURRENT_VOTER')
+        response = self.app.get("/helios/elections/%s/cast_done" % election_id)
+
+        # FIXME: how to check this? We can't do it by checking session that we're doign webtes
+        # assert not self.client.session.has_key('CURRENT_VOTER')
 
     def _do_tally(self, election_id):
         # log back in as administrator
@@ -615,15 +652,19 @@ class ElectionBlackboxTests(TestCase):
         # private election
         election_id, username, password = self._setup_complete_election({'private_p' : "1"})
 
-        # log in
-        response = self.client.post("/helios/elections/%s/password_voter_login" % election_id, {
-                'voter_id' : username,
-                'password' : password,
-                'return_url' : "/helios/elections/%s/view" % election_id
-                })
+        # get the password_voter_login_form via the front page
+        # (which will test that redirects are doing the right thing)
+        response = self.app.get("/helios/elections/%s/view" % election_id)
+
+        # ensure it redirects
+        self.assertRedirects(response, "/helios/elections/%s/password_voter_login" % election_id)
+
+        login_form = response.follow().form
+
+        login_form['voter_id'] = username
+        login_form['password'] = password
 
-        # FIXME: probably better to fetch password_voter_login as a get and post the form obtained
-        # rather than assume return_url
+        response = login_form.submit()
         self.assertRedirects(response, "/helios/elections/%s/view" % election_id)
 
         self._cast_ballot(election_id, username, password, need_login = False)
diff --git a/helios/views.py b/helios/views.py
index 119575b..33efb07 100644
--- a/helios/views.py
+++ b/helios/views.py
@@ -100,22 +100,6 @@ def stats(request):
                                             'limit' : limit})
 
 
-def get_voter(request, user, election):
-  """
-  return the current voter
-  """
-  voter = None
-  if request.session.has_key('CURRENT_VOTER'):
-    voter = request.session['CURRENT_VOTER']
-    if voter.election != election:
-      voter = None
-
-  if not voter:
-    if user:
-      voter = Voter.get_by_election_and_user(election, user)
-  
-  return voter
-
 ## 
 ## simple admin for development
 ##
@@ -546,10 +530,15 @@ def password_voter_login(request, election):
   """
   This is used to log in as a voter for a particular election
   """
-
+  
   # the URL to send the user to after they've logged in
   return_url = request.REQUEST.get('return_url', reverse(one_election_cast_confirm, args=[election.uuid]))
   if request.method == "GET":
+    # if user logged in somehow in the interim, e.g. using the login link for administration,
+    # then go!
+    if user_can_see_election(request, election):
+      return HttpResponseRedirect(reverse(one_election_view, args = [election.uuid]))
+
     password_login_form = forms.VoterPasswordForm()
     return render_template(request, 'password_voter_login', {'election': election, 
                                                              'return_url' : return_url,
@@ -663,6 +652,7 @@ def one_election_cast_confirm(request, election):
     return render_template(request, 'election_cast_confirm', {
         'login_box': login_box, 'election' : election, 'vote_fingerprint': vote_fingerprint,
         'past_votes': past_votes, 'issues': issues, 'voter' : voter,
+        'return_url': return_url,
         'status_update_label': status_update_label, 'status_update_message': status_update_message,
         'show_password': show_password, 'password_only': password_only, 'password_login_form': password_login_form,
         'bad_voter_login': bad_voter_login})
-- 
GitLab