Beispiel #1
0
def test_ballot_manifest_upload_bad_csv(
    client: FlaskClient, election_id: str, jurisdiction_ids: List[str]
):
    set_logged_in_user(client, UserType.JURISDICTION_ADMIN, DEFAULT_JA_EMAIL)
    rv = client.put(
        f"/election/{election_id}/jurisdiction/{jurisdiction_ids[0]}/ballot-manifest",
        data={"manifest": (io.BytesIO(b"not a CSV file"), "random.txt")},
    )
    assert_ok(rv)

    bgcompute_update_ballot_manifest_file()

    rv = client.get(
        f"/election/{election_id}/jurisdiction/{jurisdiction_ids[0]}/ballot-manifest"
    )
    compare_json(
        json.loads(rv.data),
        {
            "file": {"name": "random.txt", "uploadedAt": assert_is_date,},
            "processing": {
                "status": ProcessingStatus.ERRORED,
                "startedAt": assert_is_date,
                "completedAt": assert_is_date,
                "error": "Please submit a valid CSV file with columns separated by commas.",
            },
        },
    )
Beispiel #2
0
def test_ballot_manifest_upload_invalid_num_ballots(
    client: FlaskClient, election_id: str, jurisdiction_ids: List[str]
):
    set_logged_in_user(client, UserType.JURISDICTION_ADMIN, DEFAULT_JA_EMAIL)
    rv = client.put(
        f"/election/{election_id}/jurisdiction/{jurisdiction_ids[0]}/ballot-manifest",
        data={
            "manifest": (
                io.BytesIO(
                    b"Batch Name,Number of Ballots,Storage Location,Tabulator\n"
                    b"1,not a number,Bin 2,Tabulator 1\n"
                ),
                "manifest.csv",
            )
        },
    )
    assert_ok(rv)

    bgcompute_update_ballot_manifest_file()

    rv = client.get(
        f"/election/{election_id}/jurisdiction/{jurisdiction_ids[0]}/ballot-manifest"
    )
    compare_json(
        json.loads(rv.data),
        {
            "file": {"name": "manifest.csv", "uploadedAt": assert_is_date,},
            "processing": {
                "status": ProcessingStatus.ERRORED,
                "startedAt": assert_is_date,
                "completedAt": assert_is_date,
                "error": "Expected a number in column Number of Ballots, row 1. Got: not a number.",
            },
        },
    )
Beispiel #3
0
def manifests(client: FlaskClient, election_id: str,
              jurisdiction_ids: List[str]):
    set_logged_in_user(client, UserType.JURISDICTION_ADMIN, DEFAULT_JA_EMAIL)
    rv = client.put(
        f"/election/{election_id}/jurisdiction/{jurisdiction_ids[0]}/ballot-manifest",
        data={
            "manifest": (
                io.BytesIO(b"Batch Name,Number of Ballots\n"
                           b"1,23\n"
                           b"2,101\n"
                           b"3,122\n"
                           b"4,400"),
                "manifest.csv",
            )
        },
    )
    assert_ok(rv)
    rv = client.put(
        f"/election/{election_id}/jurisdiction/{jurisdiction_ids[1]}/ballot-manifest",
        data={
            "manifest": (
                io.BytesIO(b"Batch Name,Number of Ballots\n"
                           b"1,20\n"
                           b"2,10\n"
                           b"3,220\n"
                           b"4,40"),
                "manifest.csv",
            )
        },
    )
    assert_ok(rv)
    bgcompute_update_ballot_manifest_file()
Beispiel #4
0
def test_contests_create_get_update_one(client, election_id, json_contests):
    contest = json_contests[0]
    rv = put_json(client, f"/election/{election_id}/contest", [contest])
    assert_ok(rv)

    rv = client.get(f"/election/{election_id}/contest")
    contests = json.loads(rv.data)
    expected_contest = {**contest, "currentRoundStatus": None}
    assert contests == {"contests": [expected_contest]}

    contest["totalBallotsCast"] = contest["totalBallotsCast"] + 21
    contest["numWinners"] = 2
    contest["choices"].append({
        "id": str(uuid.uuid4()),
        "name": "candidate 3",
        "numVotes": 21,
    })

    rv = put_json(client, f"/election/{election_id}/contest", [contest])
    assert_ok(rv)

    rv = client.get(f"/election/{election_id}/contest")
    contests = json.loads(rv.data)
    expected_contest = {**contest, "currentRoundStatus": None}
    assert contests == {"contests": [expected_contest]}
Beispiel #5
0
def test_contests_create_get_update_multiple(
    client: FlaskClient,
    election_id: str,
    json_contests: List[JSONDict],
    jurisdiction_ids: List[str],
):
    rv = put_json(client, f"/election/{election_id}/contest", json_contests)
    assert_ok(rv)

    rv = client.get(f"/election/{election_id}/contest")
    contests = json.loads(rv.data)
    expected_contests = [{
        **contest, "currentRoundStatus": None
    } for contest in json_contests]
    assert contests == {"contests": expected_contests}

    json_contests[0]["name"] = "Changed name"
    json_contests[1]["isTargeted"] = True
    json_contests[2]["jurisdictionIds"] = jurisdiction_ids[1:]

    rv = put_json(client, f"/election/{election_id}/contest", json_contests)
    assert_ok(rv)

    rv = client.get(f"/election/{election_id}/contest")
    contests = json.loads(rv.data)
    expected_contests = [{
        **contest, "currentRoundStatus": None
    } for contest in json_contests]
    assert contests == {"contests": expected_contests}
def test_replace_jurisdictions_file(client, election_id):
    # Create the initial file.
    rv = client.put(
        f"/election/{election_id}/jurisdiction/file",
        data={
            "jurisdictions": (
                io.BytesIO(b"Jurisdiction,Admin Email\n" b"J1,[email protected]"),
                "jurisdictions.csv",
            )
        },
    )
    assert_ok(rv)
    assert File.query.count() == 1, "the file should exist before a response is sent"

    # Replace it with another file.
    rv = client.put(
        f"/election/{election_id}/jurisdiction/file",
        data={
            "jurisdictions": (
                io.BytesIO(b"Jurisdiction,Admin Email\n" b"J2,[email protected]"),
                "jurisdictions2.csv",
            )
        },
    )
    assert_ok(rv)
    assert File.query.count() == 1, "the old file should have been deleted"
Beispiel #7
0
def audit_board_round_2_ids(
    client: FlaskClient,
    election_id: str,
    jurisdiction_ids: str,
    round_2_id: str,
) -> List[str]:
    set_logged_in_user(client, UserType.JURISDICTION_ADMIN, DEFAULT_JA_EMAIL)
    rv = post_json(
        client,
        f"/election/{election_id}/jurisdiction/{jurisdiction_ids[0]}/round/{round_2_id}/audit-board",
        [
            {
                "name": "Audit Board #1"
            },
            {
                "name": "Audit Board #2"
            },
            {
                "name": "Audit Board #3"
            },
        ],
    )
    assert_ok(rv)
    rv = client.get(
        f"/election/{election_id}/jurisdiction/{jurisdiction_ids[0]}/round/{round_2_id}/audit-board"
    )
    audit_boards = json.loads(rv.data)["auditBoards"]
    return [ab["id"] for ab in audit_boards]
Beispiel #8
0
def test_audit_board_only_one_member_sign_off_happy_path(
    client: FlaskClient,
    election_id: str,
    jurisdiction_ids: List[str],
    contest_ids: List[str],
    round_1_id: str,
    audit_board_round_1_ids: List[str],
):
    audit_board_id = audit_board_round_1_ids[0]
    member_1, _ = set_up_audit_board(
        client,
        election_id,
        jurisdiction_ids[0],
        contest_ids[0],
        audit_board_id,
        only_one_member=True,
    )
    set_logged_in_user(client, UserType.AUDIT_BOARD, audit_board_id)

    rv = post_json(
        client,
        f"/election/{election_id}/jurisdiction/{jurisdiction_ids[0]}/round/{round_1_id}/audit-board/{audit_board_id}/sign-off",
        {
            "memberName1": member_1,
            "memberName2": ""
        },
    )
    assert_ok(rv)
def test_single_jurisdiction_multiple_admins(client, election_id):
    rv = client.put(
        f"/election/{election_id}/jurisdiction/file",
        data={
            "jurisdictions": (
                io.BytesIO(
                    b"Jurisdiction,Admin Email\nJ1,[email protected]\nJ1,[email protected]"
                ),
                "jurisdictions.csv",
            )
        },
    )
    assert_ok(rv)

    # Process the file in the background.
    assert bgcompute_update_election_jurisdictions_file() == 1

    election = Election.query.filter_by(id=election_id).one()
    assert [j.name for j in election.jurisdictions] == ["J1"]

    jurisdiction = election.jurisdictions[0]
    assert [a.user.email for a in jurisdiction.jurisdiction_administrations] == [
        "*****@*****.**",
        "*****@*****.**",
    ]
Beispiel #10
0
def test_audit_boards_already_created(
    client: FlaskClient,
    election_id: str,
    jurisdiction_ids: List[str],
    round_1_id: str,
):
    set_logged_in_user(client, UserType.JURISDICTION_ADMIN, DEFAULT_JA_EMAIL)
    rv = post_json(
        client,
        f"/election/{election_id}/jurisdiction/{jurisdiction_ids[0]}/round/{round_1_id}/audit-board",
        [{
            "name": "Audit Board #1"
        }],
    )
    assert_ok(rv)

    rv = post_json(
        client,
        f"/election/{election_id}/jurisdiction/{jurisdiction_ids[0]}/round/{round_1_id}/audit-board",
        [{
            "name": "Audit Board #2"
        }],
    )
    assert rv.status_code == 409
    assert json.loads(rv.data) == {
        "errors": [{
            "errorType": "Conflict",
            "message": "Audit boards already created for round 1",
        }]
    }
Beispiel #11
0
def test_audit_boards_create_round_2(
    client: FlaskClient,
    election_id: str,
    jurisdiction_ids: List[str],
    round_2_id: str,
):
    set_logged_in_user(client, UserType.JURISDICTION_ADMIN, DEFAULT_JA_EMAIL)
    rv = post_json(
        client,
        f"/election/{election_id}/jurisdiction/{jurisdiction_ids[0]}/round/{round_2_id}/audit-board",
        [
            {
                "name": "Audit Board #1"
            },
            {
                "name": "Audit Board #2"
            },
            {
                "name": "Audit Board #3"
            },
        ],
    )
    assert_ok(rv)

    assert_ballots_got_assigned_correctly(
        jurisdiction_ids[0],
        round_2_id,
        expected_num_audit_boards=3,
        expected_num_samples=J1_SAMPLES_ROUND_2,
    )
def test_ballot_list_jurisdiction_two_rounds(client, election_id):
    (
        url_prefix,
        contest_id,
        candidate_id_1,
        candidate_id_2,
        jurisdiction_id,
        _audit_board_id_1,
        _audit_board_id_2,
        num_ballots,
    ) = setup_whole_audit(client, election_id, "Primary 2019", 10,
                          "12345678901234567890")

    # Get the sample size and round id
    rv = client.get(f"/election/{election_id}/audit/status")
    status = json.loads(rv.data)
    sample_size = status["rounds"][0]["contests"][0]["sampleSize"]
    round_id = status["rounds"][0]["id"]

    # Retrieve the ballot list
    rv = client.get(
        f"/election/{election_id}/jurisdiction/{jurisdiction_id}/round/{round_id}/ballot-list"
    )
    ballot_list = json.loads(rv.data)["ballots"]
    assert ballot_list
    assert len(ballot_list) == sample_size

    # Post results for round 1 with 50/50 split, which should trigger a second round.
    num_for_winner = int(num_ballots * 0.5)
    num_for_loser = num_ballots - num_for_winner
    rv = post_json(
        client,
        "{}/jurisdiction/{}/1/results".format(url_prefix, jurisdiction_id),
        {
            "contests": [{
                "id": contest_id,
                "results": {
                    candidate_id_1: num_for_winner,
                    candidate_id_2: num_for_loser,
                },
            }]
        },
    )
    assert_ok(rv)
    bgcompute.bgcompute()

    # Get the sample size and round id for the second round
    rv = client.get(f"/election/{election_id}/audit/status")
    status = json.loads(rv.data)
    sample_size = status["rounds"][1]["contests"][0]["sampleSize"]
    round_id = status["rounds"][1]["id"]

    # Retrieve the ballot list
    rv = client.get(
        f"/election/{election_id}/jurisdiction/{jurisdiction_id}/round/{round_id}/ballot-list"
    )
    ballot_list = json.loads(rv.data)["ballots"]
    assert ballot_list
    assert len(ballot_list) == sample_size
Beispiel #13
0
def test_ballot_manifest_replace(
    client: FlaskClient, election_id: str, jurisdiction_ids: List[str]
):
    set_logged_in_user(client, UserType.JURISDICTION_ADMIN, DEFAULT_JA_EMAIL)
    rv = client.put(
        f"/election/{election_id}/jurisdiction/{jurisdiction_ids[0]}/ballot-manifest",
        data={
            "manifest": (
                io.BytesIO(
                    b"Batch Name,Number of Ballots,Storage Location,Tabulator\n"
                    b"1,23,Bin 2,Tabulator 1\n"
                    b"12,100,Bin 3,Tabulator 2\n"
                    b"6,0,,\n"
                ),
                "manifest.csv",
            )
        },
    )
    assert_ok(rv)

    num_files = File.query.count()

    bgcompute_update_ballot_manifest_file()

    set_logged_in_user(client, UserType.JURISDICTION_ADMIN, DEFAULT_JA_EMAIL)
    rv = client.put(
        f"/election/{election_id}/jurisdiction/{jurisdiction_ids[0]}/ballot-manifest",
        data={
            "manifest": (
                io.BytesIO(
                    b"Batch Name,Number of Ballots,Storage Location,Tabulator\n"
                    b"1,23,Bin 2,Tabulator 1\n"
                    b"12,6,Bin 6,Tabulator 2\n"
                ),
                "manifest.csv",
            )
        },
    )
    assert_ok(rv)

    # The old file should have been deleted
    assert File.query.count() == num_files

    bgcompute_update_ballot_manifest_file()

    jurisdiction = Jurisdiction.query.get(jurisdiction_ids[0])
    assert jurisdiction.manifest_num_batches == 2
    assert jurisdiction.manifest_num_ballots == 29
    assert len(jurisdiction.batches) == 2
    assert jurisdiction.batches[0].name == "1"
    assert jurisdiction.batches[0].num_ballots == 23
    assert jurisdiction.batches[0].storage_location == "Bin 2"
    assert jurisdiction.batches[0].tabulator == "Tabulator 1"
    assert jurisdiction.batches[1].name == "12"
    assert jurisdiction.batches[1].num_ballots == 6
    assert jurisdiction.batches[1].storage_location == "Bin 6"
    assert jurisdiction.batches[1].tabulator == "Tabulator 2"
Beispiel #14
0
def set_up_audit_board(
    client: FlaskClient,
    election_id: str,
    jurisdiction_id: str,
    contest_id: str,
    audit_board_id: str,
    only_one_member=False,
) -> Tuple[str, str]:
    silly_names = [
        "Joe Schmo",
        "Jane Plain",
        "Derk Clerk",
        "Bubbikin Republican",
        "Clem O'Hat Democrat",
    ]
    rand = random.Random(12345)
    member_1 = rand.choice(silly_names)
    member_2 = rand.choice(silly_names)

    member_names = [
        {
            "name": member_1,
            "affiliation": "DEM"
        },
        {
            "name": "" if only_one_member else member_2,
            "affiliation": ""
        },
    ]

    # Set up the audit board
    rv = post_json(
        client,
        f"/election/{election_id}/jurisdiction/{jurisdiction_id}/audit-board/{audit_board_id}",
        {"members": member_names},
    )
    assert_ok(rv)

    # Fake auditing all the ballots
    # We iterate over the ballot draws so that we can ensure the computed
    # results are counting based on the samples, not the ballots.
    ballot_draws = (SampledBallotDraw.query.join(SampledBallot).filter_by(
        audit_board_id=audit_board_id).join(Batch).order_by(
            Batch.name, SampledBallot.ballot_position).all())
    choices = (ContestChoice.query.filter_by(contest_id=contest_id).order_by(
        ContestChoice.name).all())
    for draw in ballot_draws[:CHOICE_1_VOTES]:
        audit_ballot(draw.sampled_ballot, contest_id, Interpretation.VOTE,
                     choices[0].id)
    for draw in ballot_draws[CHOICE_1_VOTES:CHOICE_1_VOTES + CHOICE_2_VOTES]:
        audit_ballot(draw.sampled_ballot, contest_id, Interpretation.VOTE,
                     choices[1].id)
    for draw in ballot_draws[CHOICE_1_VOTES + CHOICE_2_VOTES:]:
        audit_ballot(draw.sampled_ballot, contest_id, Interpretation.BLANK)
    db.session.commit()

    return member_1, member_2
Beispiel #15
0
def election_settings(client: FlaskClient, election_id: str):
    settings = {
        "electionName": "Test Election",
        "online": True,
        "randomSeed": "1234567890",
        "riskLimit": 10,
        "state": USState.California,
    }
    rv = put_json(client, f"/election/{election_id}/settings", settings)
    assert_ok(rv)
def test_metadata(client, election_id):
    rv = client.get(f"/election/{election_id}/jurisdiction/file")
    assert json.loads(rv.data) == {"file": None, "processing": None}

    rv = client.put(
        f"/election/{election_id}/jurisdiction/file",
        data={
            "jurisdictions": (
                io.BytesIO(b"Jurisdiction,Admin Email\n" b"J1,[email protected]"),
                "jurisdictions.csv",
            )
        },
    )
    assert_ok(rv)

    election = Election.query.filter_by(id=election_id).one()
    assert election.jurisdictions_file.contents == (
        "Jurisdiction,Admin Email\n" "J1,[email protected]"
    )
    assert election.jurisdictions_file.name == "jurisdictions.csv"
    assert election.jurisdictions_file.uploaded_at

    # Get the file data before processing.
    rv = client.get(f"/election/{election_id}/jurisdiction/file")
    response = json.loads(rv.data)
    file = response["file"]
    processing = response["processing"]
    assert file["name"] == "jurisdictions.csv"
    assert file["uploadedAt"]
    assert processing["status"] == ProcessingStatus.READY_TO_PROCESS
    assert processing["startedAt"] is None
    assert processing["completedAt"] is None
    assert processing["error"] is None

    # Actually process the file.
    assert bgcompute_update_election_jurisdictions_file() == 1

    # Now there should be data.
    rv = client.get(f"/election/{election_id}/jurisdiction/file")
    response = json.loads(rv.data)
    file = response["file"]
    processing = response["processing"]
    assert file["name"] == "jurisdictions.csv"
    assert file["uploadedAt"]
    assert processing["status"] == ProcessingStatus.PROCESSED
    assert processing["startedAt"]
    assert processing["completedAt"]
    assert processing["error"] is None

    rv = client.get(f"/election/{election_id}/jurisdiction/file/csv")
    assert (
        rv.headers["Content-Disposition"] == 'attachment; filename="jurisdictions.csv"'
    )
    assert rv.data.decode("utf-8") == election.jurisdictions_file.contents
Beispiel #17
0
def test_election_reset(client, election_id):
    rv = client.post(f"/election/{election_id}/audit/reset")
    assert_ok(rv)

    rv = client.get(f"/election/{election_id}/audit/status")
    status = json.loads(rv.data)

    assert status["riskLimit"] is None
    assert status["randomSeed"] is None
    assert status["contests"] == []
    assert status["jurisdictions"] == []
    assert status["rounds"] == []
Beispiel #18
0
 def run_audit_board_flow(jurisdiction_id: str, audit_board_id: str):
     member_1, member_2 = set_up_audit_board(client, election_id,
                                             jurisdiction_id,
                                             contest_ids[0], audit_board_id)
     set_logged_in_user(client, UserType.AUDIT_BOARD, audit_board_id)
     rv = post_json(
         client,
         f"/election/{election_id}/jurisdiction/{jurisdiction_id}/round/{round_1_id}/audit-board/{audit_board_id}/sign-off",
         {
             "memberName1": member_1,
             "memberName2": member_2
         },
     )
     assert_ok(rv)
def test_audit_basic_update_sets_default_for_contest_is_targeted(
        client, election_id):
    contest_id = str(uuid.uuid4())
    candidate_id_1 = str(uuid.uuid4())
    candidate_id_2 = str(uuid.uuid4())

    rv = post_json(
        client,
        f"/election/{election_id}/audit/basic",
        {
            "name":
            "Create Contest",
            "riskLimit":
            10,
            "randomSeed":
            "a1234567890987654321b",
            "online":
            False,
            "contests": [{
                "id":
                contest_id,
                "name":
                "Contest 1",
                "choices": [
                    {
                        "id": candidate_id_1,
                        "name": "Candidate 1",
                        "numVotes": 1325
                    },
                    {
                        "id": candidate_id_2,
                        "name": "Candidate 2",
                        "numVotes": 792
                    },
                ],
                "totalBallotsCast":
                2123,
                "numWinners":
                1,
                "votesAllowed":
                1,
            }],
        },
    )

    assert_ok(rv)

    rv = client.get(f"/election/{election_id}/audit/status")
    assert json.loads(rv.data)["contests"][0]["isTargeted"] is True
Beispiel #20
0
def setup_audit_board(client, election_id, jurisdiction_id, audit_board_id):
    rv = post_json(
        client,
        "/election/{}/jurisdiction/{}/audit-board/{}".format(
            election_id, jurisdiction_id, audit_board_id
        ),
        {
            "name": "Audit Board #1",
            "members": [
                {"name": "Joe Schmo", "affiliation": "REP"},
                {"name": "Jane Plain", "affiliation": ""},
            ],
        },
    )

    assert_ok(rv)
Beispiel #21
0
def test_audit_board_contests_list_empty(
    client: FlaskClient,
    election_id: str,
    jurisdiction_ids: List[str],
    round_1_id: str,
    audit_board_round_1_ids: List[str],
    json_contests: List[JSONDict],
):
    rv = put_json(client, f"/election/{election_id}/contest",
                  [json_contests[1]])
    assert_ok(rv)
    set_logged_in_user(client,
                       UserType.AUDIT_BOARD,
                       user_key=audit_board_round_1_ids[0])
    rv = client.get(
        f"/election/{election_id}/jurisdiction/{jurisdiction_ids[0]}/round/{round_1_id}/audit-board/{audit_board_round_1_ids[0]}/contest"
    )
    assert json.loads(rv.data) == {"contests": []}
def test_no_jurisdiction(client, election_id):
    rv = client.put(
        f"/election/{election_id}/jurisdiction/file",
        data={
            "jurisdictions": (
                io.BytesIO(b"Jurisdiction,Admin Email"),
                "jurisdictions.csv",
            )
        },
    )
    assert_ok(rv)

    # Process the file in the background.
    assert bgcompute_update_election_jurisdictions_file() == 1

    election = Election.query.filter_by(id=election_id).one()
    assert election.jurisdictions == []
    assert JurisdictionAdministration.query.count() == 0
    assert User.query.count() == 0
Beispiel #23
0
def jurisdiction_ids(client: FlaskClient, election_id: str) -> List[str]:
    rv = client.put(
        f"/election/{election_id}/jurisdiction/file",
        data={
            "jurisdictions": (
                # We expect the API to order the jurisdictions by name, so we
                # upload them out of order.
                io.BytesIO(("Jurisdiction,Admin Email\n"
                            f"J2,{DEFAULT_JA_EMAIL}\n"
                            "J3,[email protected]\n"
                            f"J1,{DEFAULT_JA_EMAIL}\n").encode()),
                "jurisdictions.csv",
            )
        },
    )
    assert_ok(rv)
    bgcompute_update_election_jurisdictions_file()
    jurisdictions = (Jurisdiction.query.filter_by(
        election_id=election_id).order_by(Jurisdiction.name).all())
    return [j.id for j in jurisdictions]
Beispiel #24
0
def round_2_id(
        client: FlaskClient,
        election_id: str,
        contest_ids: str,
        round_1_id: str,
        audit_board_round_1_ids: List[str],  # pylint: disable=unused-argument
) -> str:
    run_audit_round(round_1_id, contest_ids[0], 0.5)

    set_logged_in_user(client, UserType.AUDIT_ADMIN, DEFAULT_AA_EMAIL)
    rv = post_json(
        client,
        f"/election/{election_id}/round",
        {"roundNum": 2},
    )
    assert_ok(rv)

    rv = client.get(f"/election/{election_id}/round", )
    rounds = json.loads(rv.data)["rounds"]
    return str(rounds[1]["id"])
Beispiel #25
0
def run_whole_audit_flow(client, election_id, name, risk_limit, random_seed):
    (
        url_prefix,
        contest_id,
        candidate_id_1,
        candidate_id_2,
        jurisdiction_id,
        _audit_board_id_1,
        _audit_board_id_2,
        num_ballots,
    ) = setup_whole_audit(client, election_id, name, risk_limit, random_seed)

    # post results for round 1
    num_for_winner = int(num_ballots * 0.56)
    num_for_loser = num_ballots - num_for_winner
    rv = post_json(
        client,
        "{}/jurisdiction/{}/1/results".format(url_prefix, jurisdiction_id),
        {
            "contests": [
                {
                    "id": contest_id,
                    "results": {
                        candidate_id_1: num_for_winner,
                        candidate_id_2: num_for_loser,
                    },
                }
            ]
        },
    )

    assert_ok(rv)

    rv = client.get("{}/audit/status".format(url_prefix))
    status = json.loads(rv.data)
    round_contest = status["rounds"][0]["contests"][0]
    assert round_contest["id"] == contest_id
    assert round_contest["results"][candidate_id_1] == num_for_winner
    assert round_contest["results"][candidate_id_2] == num_for_loser
    assert round_contest["endMeasurements"]["isComplete"]
    assert math.floor(round_contest["endMeasurements"]["pvalue"] * 100) <= 5
Beispiel #26
0
def round_1_id(
        client: FlaskClient,
        election_id: str,
        jurisdiction_ids: List[str],  # pylint: disable=unused-argument
        contest_ids: str,  # pylint: disable=unused-argument
        election_settings,  # pylint: disable=unused-argument
        manifests,  # pylint: disable=unused-argument
) -> str:
    set_logged_in_user(client, UserType.AUDIT_ADMIN, DEFAULT_AA_EMAIL)
    rv = post_json(
        client,
        f"/election/{election_id}/round",
        {
            "roundNum": 1,
            "sampleSize": SAMPLE_SIZE_ROUND_1
        },
    )
    assert_ok(rv)
    rv = client.get(f"/election/{election_id}/round", )
    rounds = json.loads(rv.data)["rounds"]
    return str(rounds[0]["id"])
def test_update_election(client: FlaskClient, election_id: str):
    # Get the existing data.
    rv = client.get(f"/election/{election_id}/settings")

    # Update the values.
    election = json.loads(rv.data)
    election["electionName"] = "An Updated Name"
    election["online"] = True
    election["randomSeed"] = "a new random seed"
    election["riskLimit"] = 15
    election["state"] = USState.Mississippi

    rv = put_json(client, f"/election/{election_id}/settings", election)
    assert_ok(rv)

    election_record = Election.query.filter_by(id=election_id).one()
    assert election_record.election_name == "An Updated Name"
    assert election_record.online is True
    assert election_record.random_seed == "a new random seed"
    assert election_record.risk_limit == 15
    assert election_record.state == USState.Mississippi
Beispiel #28
0
def test_audit_board_contests_list(
    client: FlaskClient,
    election_id: str,
    jurisdiction_ids: List[str],
    round_1_id: str,
    audit_board_round_1_ids: List[str],
    json_contests: List[JSONDict],
):
    rv = put_json(client, f"/election/{election_id}/contest", json_contests)
    assert_ok(rv)

    set_logged_in_user(client,
                       UserType.AUDIT_BOARD,
                       user_key=audit_board_round_1_ids[0])
    rv = client.get(
        f"/election/{election_id}/jurisdiction/{jurisdiction_ids[0]}/round/{round_1_id}/audit-board/{audit_board_round_1_ids[0]}/contest"
    )
    contests = json.loads(rv.data)
    expected_contests = [{
        **contest, "currentRoundStatus": None
    } for contest in [json_contests[0], json_contests[2]]]
    assert contests == {"contests": expected_contests}
Beispiel #29
0
def test_ballot_manifest_upload_duplicate_batch_name(
    client: FlaskClient, election_id: str, jurisdiction_ids: List[str]
):
    set_logged_in_user(client, UserType.JURISDICTION_ADMIN, DEFAULT_JA_EMAIL)
    rv = client.put(
        f"/election/{election_id}/jurisdiction/{jurisdiction_ids[0]}/ballot-manifest",
        data={
            "manifest": (
                io.BytesIO(
                    b"Batch Name,Number of Ballots,Storage Location,Tabulator\n"
                    b"12,23,Bin 2,Tabulator 1\n"
                    b"12,100,Bin 3,Tabulator 2\n"
                    b"6,0,,\n"
                ),
                "manifest.csv",
            )
        },
    )
    assert_ok(rv)

    bgcompute_update_ballot_manifest_file()

    rv = client.get(
        f"/election/{election_id}/jurisdiction/{jurisdiction_ids[0]}/ballot-manifest"
    )
    compare_json(
        json.loads(rv.data),
        {
            "file": {"name": "manifest.csv", "uploadedAt": assert_is_date,},
            "processing": {
                "status": ProcessingStatus.ERRORED,
                "startedAt": assert_is_date,
                "completedAt": assert_is_date,
                "error": "Values in column Batch Name must be unique. Found duplicate value: 12.",
            },
        },
    )
Beispiel #30
0
def test_ballot_manifest_clear(
    client: FlaskClient, election_id: str, jurisdiction_ids: List[str]
):
    set_logged_in_user(client, UserType.JURISDICTION_ADMIN, DEFAULT_JA_EMAIL)
    rv = client.put(
        f"/election/{election_id}/jurisdiction/{jurisdiction_ids[0]}/ballot-manifest",
        data={
            "manifest": (
                io.BytesIO(
                    b"Batch Name,Number of Ballots,Storage Location,Tabulator\n"
                    b"1,23,Bin 2,Tabulator 1\n"
                ),
                "manifest.csv",
            )
        },
    )
    assert_ok(rv)

    num_files = File.query.count()

    bgcompute_update_ballot_manifest_file()

    rv = client.delete(
        f"/election/{election_id}/jurisdiction/{jurisdiction_ids[0]}/ballot-manifest",
    )
    assert_ok(rv)

    rv = client.get(
        f"/election/{election_id}/jurisdiction/{jurisdiction_ids[0]}/ballot-manifest"
    )
    assert json.loads(rv.data) == {"file": None, "processing": None}

    jurisdiction = Jurisdiction.query.get(jurisdiction_ids[0])
    assert jurisdiction.manifest_num_batches is None
    assert jurisdiction.manifest_num_ballots is None
    assert jurisdiction.batches == []
    assert File.query.count() == num_files - 1