"""
Unit Tests for Helios
"""

import datetime
import re
import urllib

import django_webtest
import uuid
from django.conf import settings
from django.core import mail
from django.core.files import File
from django.test import TestCase
from django.utils.html import escape as html_escape

import helios.datatypes as datatypes
import helios.models as models
import helios.utils as utils
import helios.views as views
from helios_auth import models as auth_models


class ElectionModelTests(TestCase):
    fixtures = ['users.json']
    allow_database_queries = True

    def create_election(self):
        return models.Election.get_or_create(
            short_name='demo',
            name='Demo Election',
            description='Demo Election Description',
            admin=self.user)

    def setup_questions(self):
        QUESTIONS = [{"answer_urls": [None, None, None], "answers": ["a", "b", "c"], "choice_type": "approval", "max": 1, "min": 0, "question": "w?", "result_type": "absolute", "short_name": "w?", "tally_type": "homomorphic"}]
        self.election.questions = QUESTIONS

    def setup_trustee(self):
        self.election.generate_trustee(views.ELGAMAL_PARAMS)

    def setup_openreg(self):
        self.election.openreg=True
        self.election.save()
    
    def setUp(self):
        self.user = auth_models.User.objects.get(user_id='ben@adida.net', user_type='google')
        self.fb_user = auth_models.User.objects.filter(user_type='facebook')[0]
        self.election, self.created_p = self.create_election()

    def test_create_election(self):
        # election should be created
        self.assertTrue(self.created_p)

        # should have a creation time
        self.assertNotEquals(self.election.created_at, None)
        self.assertTrue(self.election.created_at < datetime.datetime.utcnow())

    def test_find_election(self):
        election = models.Election.get_by_user_as_admin(self.user)[0]
        self.assertEquals(self.election, election)

        election = models.Election.get_by_uuid(self.election.uuid)
        self.assertEquals(self.election, election)

        election = models.Election.get_by_short_name(self.election.short_name)
        self.assertEquals(self.election, election)
        
    def test_setup_trustee(self):
        self.setup_trustee()
        self.assertEquals(self.election.num_trustees, 1)

    def test_add_voters_file(self):
        election = self.election

        FILE = "helios/fixtures/voter-file.csv"
        vf = models.VoterFile.objects.create(election = election, voter_file = File(open(FILE), "voter_file.css"))
        vf.process()

        # make sure that we stripped things correctly
        voter = election.voter_set.get(voter_login_id = 'benadida5')
        self.assertEquals(voter.voter_email, 'ben5@adida.net')
        self.assertEquals(voter.voter_name, 'Ben5 Adida')

    def test_check_issues_before_freeze(self):
        # should be three issues: no trustees, and no questions, and no voters
        issues = self.election.issues_before_freeze
        self.assertEquals(len(issues), 3)

        self.setup_questions()

        # should be two issues: no trustees, and no voters
        issues = self.election.issues_before_freeze
        self.assertEquals(len(issues), 2)

        self.election.questions = None

        self.setup_trustee()

        # should be two issues: no questions, and no voters
        issues = self.election.issues_before_freeze
        self.assertEquals(len(issues), 2)
        
        self.setup_questions()

        # move to open reg
        self.setup_openreg()

        issues = self.election.issues_before_freeze
        self.assertEquals(len(issues), 0)
        
    def test_helios_trustee(self):
        self.election.generate_trustee(views.ELGAMAL_PARAMS)

        self.assertTrue(self.election.has_helios_trustee())

        trustee = self.election.get_helios_trustee()
        self.assertNotEquals(trustee, None)

    def test_log(self):
        LOGS = ["testing 1", "testing 2", "testing 3"]

        for l in LOGS:
            self.election.append_log(l)

        pulled_logs = [l.log for l in self.election.get_log().all()]
        pulled_logs.reverse()

        self.assertEquals(LOGS,pulled_logs)

    def test_eligibility(self):
        self.election.eligibility = [{'auth_system': self.user.user_type}]

        # without openreg, this should be false
        self.assertFalse(self.election.user_eligible_p(self.user))
        
        # what about after saving?
        self.election.save()
        e = models.Election.objects.get(uuid = self.election.uuid)
        self.assertEquals(e.eligibility, [{'auth_system': self.user.user_type}])

        self.election.openreg = True

        # without openreg, and now true
        self.assertTrue(self.election.user_eligible_p(self.user))

        # try getting pretty eligibility, make sure it doesn't throw an exception
        assert self.user.user_type in self.election.pretty_eligibility

    def test_facebook_eligibility(self):
        self.election.eligibility = [{'auth_system': 'facebook', 'constraint':[{'group': {'id': '123', 'name':'Fake Group'}}]}]

        # without openreg, this should be false
        self.assertFalse(self.election.user_eligible_p(self.fb_user))
        
        self.election.openreg = True

        # fake out the facebook constraint checking, since
        # our access_token is obviously wrong
        from helios_auth.auth_systems import facebook

        def fake_check_constraint(constraint, user):
            return constraint == {'group': {'id': '123', 'name':'Fake Group'}} and user == self.fb_user                
        facebook.check_constraint = fake_check_constraint

        self.assertTrue(self.election.user_eligible_p(self.fb_user))

        # also check that eligibility_category_id does the right thing
        self.assertEquals(self.election.eligibility_category_id('facebook'), '123')

    def test_freeze(self):
        # freezing without trustees and questions, no good
        def try_freeze():
            self.election.freeze()
        self.assertRaises(Exception, try_freeze)
        
        self.setup_questions()
        self.setup_trustee()
        self.setup_openreg()

        # this time it should work
        try_freeze()
        
        # make sure it logged something
        self.assertTrue(len(self.election.get_log().all()) > 0)

    def test_archive(self):
        self.election.archived_at = datetime.datetime.utcnow()
        self.assertTrue(self.election.is_archived)

        self.election.archived_at = None
        self.assertFalse(self.election.is_archived)

    def test_voter_registration(self):
        # before adding a voter
        voters = models.Voter.get_by_election(self.election)
        self.assertEquals(0, len(voters))

        # make sure no voter yet
        voter = models.Voter.get_by_election_and_user(self.election, self.user)
        self.assertIsNone(voter)

        # make sure no voter at all across all elections
        voters = models.Voter.get_by_user(self.user)
        self.assertEquals(0, len(voters))

        # register the voter
        voter = models.Voter.register_user_in_election(self.user, self.election)
        
        # make sure voter is there now
        voter_2 = models.Voter.get_by_election_and_user(self.election, self.user)

        self.assertIsNotNone(voter)
        self.assertIsNotNone(voter_2)
        self.assertEquals(voter, voter_2)

        # make sure voter is there in this call too
        voters = models.Voter.get_by_user(self.user)
        self.assertEquals(1, len(voters))
        self.assertEquals(voter, voters[0])

        voter_2 = models.Voter.get_by_election_and_uuid(self.election, voter.uuid)
        self.assertEquals(voter, voter_2)

        self.assertEquals(voter.user, self.user)



class VoterModelTests(TestCase):
    fixtures = ['users.json', 'election.json']
    allow_database_queries = True

    def setUp(self):
        self.election = models.Election.objects.get(short_name='test')

    def test_create_password_voter(self):
        v = models.Voter(uuid = str(uuid.uuid1()), election = self.election, voter_login_id = 'voter_test_1', voter_name = 'Voter Test 1', voter_email='foobar@acme.com')

        v.generate_password()

        v.save()
        
        # password has been generated!
        self.assertFalse(v.voter_password == None)

        # can't generate passwords twice
        self.assertRaises(Exception, lambda: v.generate_password())
        
        # check that you can get at the voter user structure
        self.assertEquals(v.get_user().user_id, v.voter_email)


class CastVoteModelTests(TestCase):
    fixtures = ['users.json', 'election.json']
    allow_database_queries = True

    def setUp(self):
        self.election = models.Election.objects.get(short_name='test')
        self.user = auth_models.User.objects.get(user_id='ben@adida.net', user_type='google')

        # register the voter
        self.voter = models.Voter.register_user_in_election(self.user, self.election)

    def test_cast_vote(self):
        pass

class DatatypeTests(TestCase):
    fixtures = ['users.json', 'election.json']
    allow_database_queries = True

    def setUp(self):
        self.election = models.Election.objects.all()[0]
        self.election.generate_trustee(views.ELGAMAL_PARAMS)

    def test_instantiate(self):
        ld_obj = datatypes.LDObject.instantiate(self.election.get_helios_trustee(), '2011/01/Trustee')
        foo = ld_obj.serialize()

    def test_from_dict(self):
        ld_obj = datatypes.LDObject.fromDict({
                'y' : '1234',
                'p' : '23434',
                'g' : '2343243242',
                'q' : '2343242343434'}, type_hint = 'pkc/elgamal/PublicKey')

    def test_dictobject_from_dict(self):
        original_dict = {
            'A' : '35423432',
            'B' : '234324243'}
        ld_obj = datatypes.LDObject.fromDict(original_dict, type_hint = 'legacy/EGZKProofCommitment')

        self.assertEquals(original_dict, ld_obj.toDict())
        
        
        

##
## Black box tests
##

class DataFormatBlackboxTests(object):
    def setUp(self):
        self.election = models.Election.objects.all()[0]

    def assertEqualsToFile(self, response, file_path):
        expected = open(file_path)
        self.assertEquals(response.content, expected.read())
        expected.close()

    def test_election(self):
        response = self.client.get("/helios/elections/%s" % self.election.uuid, follow=False)
        self.assertEqualsToFile(response, self.EXPECTED_ELECTION_FILE)

    def test_election_metadata(self):
        response = self.client.get("/helios/elections/%s/meta" % self.election.uuid, follow=False)
        self.assertEqualsToFile(response, self.EXPECTED_ELECTION_METADATA_FILE)

    def test_voters_list(self):
        response = self.client.get("/helios/elections/%s/voters/" % self.election.uuid, follow=False)
        self.assertEqualsToFile(response, self.EXPECTED_VOTERS_FILE)

    def test_trustees_list(self):
        response = self.client.get("/helios/elections/%s/trustees/" % self.election.uuid, follow=False)
        self.assertEqualsToFile(response, self.EXPECTED_TRUSTEES_FILE)

    def test_ballots_list(self):
        response = self.client.get("/helios/elections/%s/ballots/" % self.election.uuid, follow=False)
        self.assertEqualsToFile(response, self.EXPECTED_BALLOTS_FILE)

## now we have a set of fixtures and expected results for various formats
## note how TestCase is used as a "mixin" here, so that the generic DataFormatBlackboxTests
## does not register as a set of test cases to run, but each concrete data format does.

class LegacyElectionBlackboxTests(DataFormatBlackboxTests, TestCase):
    fixtures = ['legacy-data.json']
    allow_database_queries = True
    EXPECTED_ELECTION_FILE = 'helios/fixtures/legacy-election-expected.json'
    EXPECTED_ELECTION_METADATA_FILE = 'helios/fixtures/legacy-election-metadata-expected.json'
    EXPECTED_VOTERS_FILE = 'helios/fixtures/legacy-election-voters-expected.json'
    EXPECTED_TRUSTEES_FILE = 'helios/fixtures/legacy-trustees-expected.json'
    EXPECTED_BALLOTS_FILE = 'helios/fixtures/legacy-ballots-expected.json'

#class V3_1_ElectionBlackboxTests(DataFormatBlackboxTests, TestCase):
#    fixtures = ['v3.1-data.json']
#    EXPECTED_ELECTION_FILE = 'helios/fixtures/v3.1-election-expected.json'
#    EXPECTED_VOTERS_FILE = 'helios/fixtures/v3.1-election-voters-expected.json'
#    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 assertStatusCode(self, response, status_code):
        if hasattr(response, 'status_code'):
            assert response.status_code == status_code, response.status_code
        else:
            assert response.status_int == status_code, response.status_int


    def assertRedirects(self, response, url):
        """
        reimplement this in case it's a WebOp response
        and it seems to be screwing up in a few places too
        thus the localhost exception
        """
        if hasattr(response, 'location'):
            assert url in response.location, response.location
        else:
            assert url in response['location'], response['location']
        self.assertStatusCode(response, 302)
        #return super(django_webtest.WebTest, self).assertRedirects(response, url)
        #assert url in response.location, "redirected to %s instead of %s" % (response.location, url)


    def assertContains(self, response, text):
        self.assertStatusCode(response, 200)

        if hasattr(response, "testbody"):
            assert text in response.testbody, "missing text %s" % text
        elif hasattr(response, "body"):
            assert text in response.body, "missing text %s" % text
        else:
            assert text in response.content, "missing text %s" % text


##
## overall operation of the system
##

class ElectionBlackboxTests(WebTest):
    fixtures = ['users.json', 'election.json']
    allow_database_queries = True

    def setUp(self):
        self.election = models.Election.objects.all()[0]
        self.user = auth_models.User.objects.get(user_id='ben@adida.net', user_type='google')

    def setup_login(self, from_scratch=False, **kwargs):
        if from_scratch:
            # a bogus call to set up the session
            self.client.get("/")
        # set up the session
        session = self.client.session
        if kwargs:
            user = auth_models.User.objects.get(**kwargs)
        else:
            user = self.user
        session['user'] = {'type': user.user_type, 'user_id': user.user_id}
        session.save()

        # set up the app, too
        # this does not appear to work, boohoo
        #session = self.app.session
        #session['user'] = {'type': self.user.user_type, 'user_id': self.user.user_id}

    def clear_login(self):
        session = self.client.session
        del session['user']
        session.save()        

    def test_election_params(self):
        response = self.client.get("/helios/elections/params")
        self.assertEquals(response.content, views.ELGAMAL_PARAMS_LD_OBJECT.serialize())

    def test_election_404(self):
        response = self.client.get("/helios/elections/foobar")
        self.assertStatusCode(response, 404)

    def test_election_bad_trustee(self):
        response = self.client.get("/helios/t/%s/foobar@bar.com/badsecret" % self.election.short_name)
        self.assertStatusCode(response, 404)

    def test_get_election_shortcut(self):
        response = self.client.get("/helios/e/%s" % self.election.short_name, follow=True)
        self.assertContains(response, self.election.description)
        
    def test_get_election_raw(self):
        response = self.client.get("/helios/elections/%s" % self.election.uuid, follow=False)
        self.assertEquals(response.content, self.election.toJSON())
    
    def test_get_election(self):
        response = self.client.get("/helios/elections/%s/view" % self.election.uuid, follow=False)
        self.assertContains(response, self.election.description)

    def test_get_election_questions(self):
        response = self.client.get("/helios/elections/%s/questions" % self.election.uuid, follow=False)
        for q in self.election.questions:
            self.assertContains(response, q['question'])
    
    def test_get_election_trustees(self):
        response = self.client.get("/helios/elections/%s/trustees" % self.election.uuid, follow=False)
        for t in self.election.trustee_set.all():
            self.assertContains(response, t.name)

    def test_get_election_voters(self):
        response = self.client.get("/helios/elections/%s/voters/list" % self.election.uuid, follow=False)
        # check total count of voters
        if self.election.num_voters == 0:
            self.assertContains(response, "no voters")
        else:
            self.assertContains(response, "(of %s)" % self.election.num_voters)

    def test_get_election_voters_raw(self):
        response = self.client.get("/helios/elections/%s/voters/" % self.election.uuid, follow=False)
        self.assertEquals(len(utils.from_json(response.content)), self.election.num_voters)
        
    def test_election_creation_not_logged_in(self):
        response = self.client.post("/helios/elections/new", {
                "short_name" : "test-complete",
                "name" : "Test Complete",
                "description" : "A complete election test",
                "election_type" : "referendum",
                "use_voter_aliases": "0",
                "use_advanced_audit_features": "1",
                "private_p" : "False"})

        self.assertRedirects(response, "/auth/?return_url=/helios/elections/new")
    
    def test_election_edit(self):
        self.setup_login(from_scratch=True)
        response = self.client.get("/helios/elections/%s/edit" % self.election.uuid)
        response = self.client.post("/helios/elections/%s/edit" % self.election.uuid, {
                "short_name" : self.election.short_name + "-2",
                "name" : self.election.name,
                "description" : self.election.description,
                "election_type" : self.election.election_type,
                "use_voter_aliases": self.election.use_voter_aliases,
                'csrf_token': self.client.session['csrf_token']
                })

        self.assertRedirects(response, "/helios/elections/%s/view" % self.election.uuid)

        new_election = models.Election.objects.get(uuid = self.election.uuid)
        self.assertEquals(new_election.short_name, self.election.short_name + "-2")

    def test_get_election_stats(self):
        self.setup_login(from_scratch=True, user_id='mccio@github.com', user_type='google')
        response = self.client.get("/helios/stats/", follow=False)
        self.assertStatusCode(response, 200)
        response = self.client.get("/helios/stats/force-queue", follow=False)
        self.assertRedirects(response, "/helios/stats/")
        response = self.client.get("/helios/stats/elections", follow=False)
        self.assertStatusCode(response, 200)
        response = self.client.get("/helios/stats/problem-elections", follow=False)
        self.assertStatusCode(response, 200)
        response = self.client.get("/helios/stats/recent-votes", follow=False)
        self.assertStatusCode(response, 200)
        self.clear_login()
        response = self.client.get("/helios/stats/", follow=False)
        self.assertStatusCode(response, 403)
        self.setup_login()
        response = self.client.get("/helios/stats/", follow=False)
        self.assertStatusCode(response, 403)
        self.clear_login()

    def _setup_complete_election(self, election_params=None):
        "do the setup part of a whole election"

        # REPLACE with params?
        self.setup_login(from_scratch=True)

        # create the election
        full_election_params = {
            "short_name" : "test-complete",
            "name" : "Test Complete",
            "description" : "A complete election test",
            "election_type" : "referendum",
            "use_voter_aliases": "0",
            "use_advanced_audit_features": "1",
            "private_p" : "False",
            'csrf_token': self.client.session['csrf_token']
        }

        # override with the given
        full_election_params.update(election_params or {})

        response = self.client.post("/helios/elections/new", full_election_params)

        # we are redirected to the election, let's extract the ID out of the URL
        election_id = re.search('/elections/([^/]+)/', str(response['Location']))
        self.assertIsNotNone(election_id, "Election id not found in redirect: %s" % str(response['Location']))
        election_id = election_id.group(1)

        # helios is automatically added as a trustee

        # check that helios is indeed a trustee
        response = self.client.get("/helios/elections/%s/trustees/view" % election_id)
        self.assertContains(response, "Trustee #1")

        # add a few voters with an improperly placed email address
        FILE = "helios/fixtures/voter-badfile.csv"
        voters_file = open(FILE)
        response = self.client.post("/helios/elections/%s/voters/upload" % election_id, {'voters_file': voters_file})
        voters_file.close()
        self.assertContains(response, "HOLD ON")

        # add a few voters, via file upload
        # this file now includes a UTF-8 encoded unicode character
        # yes I know that's not how you spell Ernesto.
        # I just needed some unicode quickly.
        FILE = "helios/fixtures/voter-file.csv"
        voters_file = open(FILE)
        response = self.client.post("/helios/elections/%s/voters/upload" % election_id, {'voters_file': voters_file})
        voters_file.close()
        self.assertContains(response, "first few rows of this file")

        # now we confirm the upload
        response = self.client.post("/helios/elections/%s/voters/upload" % election_id, {'confirm_p': "1"})
        self.assertRedirects(response, "/helios/elections/%s/voters/list" % election_id)

        # and we want to check that there are now voters
        response = self.client.get("/helios/elections/%s/voters/" % election_id)
        NUM_VOTERS = 4
        self.assertEquals(len(utils.from_json(response.content)), NUM_VOTERS)

        # let's get a single voter
        single_voter = models.Election.objects.get(uuid = election_id).voter_set.all()[0]
        response = self.client.get("/helios/elections/%s/voters/%s" % (election_id, single_voter.uuid))
        self.assertContains(response, '"uuid": "%s"' % single_voter.uuid)

        response = self.client.get("/helios/elections/%s/voters/foobar" % election_id)
        self.assertStatusCode(response, 404)
        
        # add questions
        response = self.client.post("/helios/elections/%s/save_questions" % election_id, {
                'questions_json': utils.to_json([{"answer_urls": ["http://example.com",None], "answers": ["Alice", "Bob"], "choice_type": "approval", "max": 1, "min": 0, "question": "Who should be president?", "result_type": "absolute", "short_name": "Who should be president?", "tally_type": "homomorphic"}]),
                'csrf_token': self.client.session['csrf_token']})

        self.assertContains(response, "SUCCESS")

        # freeze election
        response = self.client.post("/helios/elections/%s/freeze" % election_id, {
                "csrf_token" : self.client.session['csrf_token']})
        self.assertRedirects(response, "/helios/elections/%s/view" % election_id)

        # email the voters
        num_messages_before = len(mail.outbox)
        response = self.client.post("/helios/elections/%s/voters/email" % election_id, {
                "csrf_token" : self.client.session['csrf_token'],
                "subject" : "your password",
                "body" : "time to vote",
                "suppress_election_links" : "0",
                "send_to" : "all"
                })
        self.assertRedirects(response, "/helios/elections/%s/view" % election_id)
        num_messages_after = len(mail.outbox)
        self.assertEquals(num_messages_after - num_messages_before, NUM_VOTERS)

        email_message = mail.outbox[num_messages_before]
        assert "your password" in email_message.subject, "bad subject in email"

        # get the username and password
        username = re.search('voter ID: (.*)', email_message.body).group(1)
        password = re.search('password: (.*)', email_message.body).group(1)

        # now log out as administrator
        self.clear_login()
        self.assertEquals(self.client.session.has_key('user'), False)

        # return the voter username and password to vote
        return election_id, username, password

    def _cast_ballot(self, election_id, username, password, need_login=True, check_user_logged_in=False):
        """
        check_user_logged_in looks for the "you're already logged" message
        """
        # vote by preparing a ballot via the server-side encryption
        response = self.app.post("/helios/elections/%s/encrypt-ballot" % election_id,
                   params={'answers_json': utils.to_json([[1]])})
        self.assertContains(response, "answers")

        # parse it as an encrypted vote with randomness, and make sure randomness is there
        the_ballot = utils.from_json(response.testbody)
        assert the_ballot['answers'][0].has_key('randomness'), "no randomness"
        assert len(the_ballot['answers'][0]['randomness']) == 2, "not enough randomness"
        
        # parse it as an encrypted vote, and re-serialize it
        ballot = datatypes.LDObject.fromDict(utils.from_json(response.testbody), type_hint='legacy/EncryptedVote')
        encrypted_vote = ballot.serialize()
        
        # cast the ballot
        response = self.app.post("/helios/elections/%s/cast" % election_id,
                   params={'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:
                self.assertContains(cast_confirm_page, "You are logged in as")
                self.assertContains(cast_confirm_page, "requires election-specific credentials")

            # set the form
            login_form = cast_confirm_page.form
            login_form['voter_id'] = username
            login_form['password'] = password

            response = login_form.submit()
        else:
            # here we should be at the cast-confirm page and logged in
            self.assertContains(cast_confirm_page, "CAST this ballot")

            # 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
        # at position num_messages after, since that was the len() before we cast this ballot
        email_message = mail.outbox[len(mail.outbox) - 1]
        url = re.search('https?://[^/]+(/[^ \n]*)', email_message.body).group(1)

        # check that we can get at that URL
        if not need_login:
            # confusing piece: if need_login is True, that means it was a public election
            # that required login before casting a ballot.
            # 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
            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
                
                # try with extra spaces
                login_form['voter_id'] = '  ' + username + '   '
                login_form['password'] = '  ' + password + '      '
                login_form.submit()
            
        response = self.app.get(url, auto_follow=True)
        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.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
        self.setup_login()

        # encrypted tally
        response = self.client.post("/helios/elections/%s/compute_tally" % election_id, {
                "csrf_token" : self.client.session['csrf_token']                
                })
        self.assertRedirects(response, "/helios/elections/%s/view" % election_id)

        # should trigger helios decryption automatically
        self.assertNotEquals(models.Election.objects.get(uuid=election_id).get_helios_trustee().decryption_proofs, None)

        # combine decryptions
        response = self.client.post("/helios/elections/%s/combine_decryptions" % election_id, {
                "csrf_token" : self.client.session['csrf_token'],
                })

        # after tallying, we now go back to election_view
        self.assertRedirects(response, "/helios/elections/%s/view" % election_id)

        # check that we can't get the tally yet
        response = self.client.get("/helios/elections/%s/result" % election_id)
        self.assertStatusCode(response, 403)

        # release
        response = self.client.post("/helios/elections/%s/release_result" % election_id, {
                "csrf_token" : self.client.session['csrf_token'],
                })

        # check that tally matches
        response = self.client.get("/helios/elections/%s/result" % election_id)
        self.assertEquals(utils.from_json(response.content), [[0,1]])
        
    def test_do_complete_election(self):
        election_id, username, password = self._setup_complete_election()
        
        # cast a ballot while not logged in
        self._cast_ballot(election_id, username, password, check_user_logged_in=False)

        # cast a ballot while logged in as a user (not a voter)
        self.setup_login()

        ## for now the above does not work, it's a testing problem
        ## where the cookie isn't properly set. We'll have to figure this out.
        ## FIXME FIXME FIXME 
        #self._cast_ballot(election_id, username, password, check_user_logged_in=True)
        self._cast_ballot(election_id, username, password, check_user_logged_in=False)
        self.clear_login()

        self._do_tally(election_id)

    def test_do_complete_election_private(self):
        # private election
        election_id, username, password = self._setup_complete_election({'private_p' : "True"})

        # 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?%s" % (election_id, urllib.urlencode({"return_url": "/helios/elections/%s/view" % election_id})))

        login_form = response.follow().form

        login_form['voter_id'] = username
        login_form['password'] = password

        response = login_form.submit()
        self.assertRedirects(response, "/helios/elections/%s/view" % election_id)

        self._cast_ballot(election_id, username, password, need_login = False)
        self._do_tally(election_id)

    def test_election_voters_eligibility(self):
        # create the election
        self.setup_login(from_scratch=True)
        response = self.client.post("/helios/elections/new", {
                "short_name" : "test-eligibility",
                "name" : "Test Eligibility",
                "description" : "An election test for voter eligibility",
                "election_type" : "election",
                "use_voter_aliases": "0",
                "use_advanced_audit_features": "1",
                "private_p" : "False",
                'csrf_token': self.client.session['csrf_token']})

        election_id = re.match("(.*)/elections/(.*)/view", str(response['Location']))
        self.assertIsNotNone(election_id, "Election id not found in redirect: %s" % str(response['Location']))
        election_id = election_id.group(2)

        # update eligiblity
        response = self.client.post("/helios/elections/%s/voters/eligibility" % election_id, {
                "csrf_token" : self.client.session['csrf_token'],
                "eligibility": "openreg"})

        self.clear_login()
        response = self.client.get("/helios/elections/%s/voters/list" % election_id)
        self.assertContains(response, "Anyone can vote")

        self.setup_login()
        response = self.client.post("/helios/elections/%s/voters/eligibility" % election_id, {
                "csrf_token" : self.client.session['csrf_token'],
                "eligibility": "closedreg"})

        self.clear_login()
        response = self.client.get("/helios/elections/%s/voters/list" % election_id)
        self.assertContains(response, "Only the voters listed here")

    def test_do_complete_election_with_trustees(self):
        """
        FIXME: do the this test
        """
        pass