def test_in_org_duplicate_audit_name(client: FlaskClient): org_id, _user_id = create_org_and_admin(user_email="*****@*****.**") set_logged_in_user(client, UserType.AUDIT_ADMIN, "*****@*****.**") rv = post_json( client, "/election/new", { "auditName": "Test Audit", "organizationId": org_id, "isMultiJurisdiction": True, }, ) assert rv.status_code == 200 assert json.loads(rv.data)["electionId"] rv = post_json( client, "/election/new", { "auditName": "Test Audit", "organizationId": org_id, "isMultiJurisdiction": True, }, ) assert rv.status_code == 409 assert json.loads(rv.data) == { "errors": [ { "message": "An audit with name 'Test Audit' already exists within your organization", "errorType": "Conflict", } ] }
def test_two_orgs_same_name(client: FlaskClient): org_id_1, _ = create_org_and_admin(user_email="*****@*****.**") org_id_2, _ = create_org_and_admin(user_email="*****@*****.**") set_logged_in_user(client, UserType.AUDIT_ADMIN, "*****@*****.**") rv = post_json( client, "/election/new", { "auditName": "Test Audit", "organizationId": org_id_1, "isMultiJurisdiction": True, }, ) assert rv.status_code == 200 assert json.loads(rv.data)["electionId"] set_logged_in_user(client, UserType.AUDIT_ADMIN, "*****@*****.**") rv = post_json( client, "/election/new", { "auditName": "Test Audit", "organizationId": org_id_2, "isMultiJurisdiction": True, }, ) assert rv.status_code == 200 assert json.loads(rv.data)["electionId"]
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", }] }
def test_login_mocked(mocker, app, client): mocker.patch('automoticz.utils.beacons.get_pin', return_value='0000') mocker.patch('automoticz.app.get_beacon_pin.delay') mocker.patch('automoticz.utils.beacons.get_default_auth_beacon_name', return_value='beacons/test_beacon') mocker.patch('automoticz.utils.beacons.get_default_project_namespace', return_value='project/automoticz-project') mocker.patch('automoticz.utils.beacons.unset_pin') mocker.patch('automoticz.utils.beacons.generate_pin', return_value='0000') mocker.patch('automoticz.api.beacon_auth.routes.set_beacon_pin') domoticz_login = app.config.DOMOTICZ_USERNAME domoticz_password = to_base64(str(app.config.DOMOTICZ_PASSWORD)) domoticz_password_invalid = to_base64('1234') json_payload = { 'pin': to_base64('0000'), 'client': 'Android Google Pixel 3', 'client_uuid': str(uuid.uuid1()), 'login': domoticz_login, 'password': domoticz_password } response = post_json(client, 'api/beacon_auth/login', json_payload) response_json = json_response(response) assert response.status_code == 200 assert 'access_token' in response_json json_payload = { 'pin': to_base64('0000'), 'client': 'Android Google Pixel 3', 'client_uuid': str(uuid.uuid1()), 'login': domoticz_login, 'password': domoticz_password_invalid } response = post_json(client, 'api/beacon_auth/login', json_payload) response_json = json_response(response) assert response.status_code == 400 assert constants.RESPONSE_MESSAGE.INVALID_CREDS == response_json['message'] json_payload = {'client': 'Android Google Pixel 3'} response = post_json(client, 'api/beacon_auth/login', json_payload) assert response.status_code == 400 json_payload = { 'pin': to_base64('1111'), 'client': 'Chrome Browser', 'client_uuid': str(uuid.uuid1()), 'login': domoticz_login, 'password': domoticz_password } response = post_json(client, 'api/beacon_auth/login', json_payload) response_json = json_response(response) assert response.status_code == 400 assert constants.RESPONSE_MESSAGE.INVALID_PIN == response_json['message']
def test_login(app, client): domoticz_login = app.config.DOMOTICZ_USERNAME domoticz_password = to_base64(str(app.config.DOMOTICZ_PASSWORD)) domoticz_password_invalid = to_base64('1234') with app.app_context(): valid_pin = to_base64(get_pin()) json_payload = { 'pin': valid_pin, 'client': 'Android Google Pixel 3', 'client_uuid': str(uuid.uuid1()), 'login': domoticz_login, 'password': domoticz_password } response = post_json(client, 'api/beacon_auth/login', json_payload) response_json = json_response(response) assert response.status_code == 200 assert 'access_token' in response_json with app.app_context(): valid_pin = to_base64(get_pin()) json_payload = { 'pin': valid_pin, 'client': 'Android Google Pixel 3', 'client_uuid': str(uuid.uuid1()), 'login': domoticz_login, 'password': domoticz_password_invalid } response = post_json(client, 'api/beacon_auth/login', json_payload) response_json = json_response(response) assert response.status_code == 400 assert constants.RESPONSE_MESSAGE.INVALID_CREDS == response_json['message'] json_payload = {'client': 'Android Google Pixel 3'} response = post_json(client, 'api/beacon_auth/login', json_payload) assert response.status_code == 400 json_payload = { 'pin': to_base64('1111'), 'client': 'Chrome Browser', 'client_uuid': str(uuid.uuid1()), 'login': domoticz_login, 'password': domoticz_password } response = post_json(client, 'api/beacon_auth/login', json_payload) response_json = json_response(response) assert response.status_code == 400 assert constants.RESPONSE_MESSAGE.INVALID_PIN == response_json['message']
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_audit_boards_sign_off_missing_name( 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, member_2 = set_up_audit_board(client, election_id, jurisdiction_ids[0], contest_ids[0], audit_board_id) set_logged_in_user(client, UserType.AUDIT_BOARD, audit_board_id) for missing_field in ["memberName1", "memberName2"]: sign_off_request_body = { "memberName1": member_1, "memberName2": member_2 } del sign_off_request_body[missing_field] rv = post_json( client, f"/election/{election_id}/jurisdiction/{jurisdiction_ids[0]}/round/{round_1_id}/audit-board/{audit_board_id}/sign-off", sign_off_request_body, ) assert rv.status_code == 400 assert json.loads(rv.data) == { "errors": [{ "errorType": "Bad Request", "message": f"'{missing_field}' is a required property", }] }
def test_ws_devices(app, client): domoticz_login = app.config.DOMOTICZ_USERNAME domoticz_password = to_base64(str(app.config.DOMOTICZ_PASSWORD)) domoticz_password_invalid = to_base64('1234') with app.app_context(): valid_pin = to_base64(get_pin()) json_payload = { 'pin': valid_pin, 'client': 'Android Google Pixel 3', 'client_uuid': str(uuid.uuid1()), 'login': domoticz_login, 'password': domoticz_password } response = post_json(client, 'api/beacon_auth/login', json_payload) response_json = json_response(response) assert response.status_code == 200 assert 'access_token' in response_json access_token = response_json['access_token'] json_payload = {'access_token': access_token} response = get_json( client, 'api/system/ws_devices', headers={'Authorization': 'Bearer {}'.format(access_token)}) assert response.status_code == 200 response_json = json_response(response) assert response_json assert type(response_json['wsdevices']) == list
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_without_org_duplicate_audit_name(client: FlaskClient): rv = post_json( client, "/election/new", {"auditName": "Test Audit", "isMultiJurisdiction": False}, ) assert rv.status_code == 200 assert json.loads(rv.data)["electionId"] rv = post_json( client, "/election/new", {"auditName": "Test Audit", "isMultiJurisdiction": False}, ) assert rv.status_code == 200 assert json.loads(rv.data)["electionId"]
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]
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
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
def test_car_splunk(client, pattern, translation): response = post_json(client, '/car-splunk', pattern) assert response.status_code == 200 expectedValue = {} expectedValue['pattern'] = pattern expectedValue['validated'] = True expectedValue['car-splunk'] = translation assert response.status_code == 200 assert json.dumps(json.loads(response.data.decode('utf8')), sort_keys=True) == json.dumps(expectedValue, sort_keys=True)
def test_missing_audit_name(client: FlaskClient): rv = post_json(client, "/election/new", {}) assert rv.status_code == 400 assert json.loads(rv.data) == { "errors": [ { "message": "'auditName' is a required property", "errorType": "Bad Request", } ] }
def test_validate(client, pattern, validated): """ Test the Validate endpoint """ response = post_json(client, '/validate', pattern) assert response.status_code == 200 expectedValue = {} expectedValue['pattern'] = pattern expectedValue['validated'] = validated assert response.data.decode('utf8') == json.dumps(expectedValue, sort_keys=True)
def test_audit_board(client, election_id): ( url_prefix, _contest_id, _candidate_id_1, _candidate_id_2, _candidate_id_3, jurisdiction_id, audit_board_id_1, _audit_board_id_2, _num_ballots, ) = setup_whole_multi_winner_audit( client, election_id, "Multi-Round Multi-winner Audit", 10, "32423432423432" ) url = "{}/jurisdiction/{}/audit-board/{}".format( url_prefix, jurisdiction_id, audit_board_id_1 ) ## check audit board rv = client.get(url) response = json.loads(rv.data) assert response["id"] == audit_board_id_1 assert response["name"] assert response["members"] == [] ## submit new data rv = post_json( client, url, { "name": "Awesome Audit Board", "members": [ {"name": "Darth Vader", "affiliation": "EMP"}, {"name": "Leia Organa", "affiliation": "REB"}, ], }, ) response = json.loads(rv.data) assert response["status"] == "ok" ## check new data rv = client.get(url) response = json.loads(rv.data) assert response["id"] == audit_board_id_1 assert response["name"] == "Awesome Audit Board" assert response["members"] == [ {"name": "Darth Vader", "affiliation": "EMP"}, {"name": "Leia Organa", "affiliation": "REB"}, ]
def test_get_object(client, pattern, objects): """ Tests the "get_objects" endpoint """ response = post_json(client, '/get-objects', pattern) expectedValue = {} expectedValue['pattern'] = pattern expectedValue['validated'] = True expectedValue['object'] = json.loads(objects) assert response.status_code == 200 assert json.dumps(json.loads(response.data.decode('utf8')), sort_keys=True) == json.dumps(expectedValue, sort_keys=True)
def test_translate_all(client, pattern, validated, translatedResults): """ Test the translate-all endpoint """ response = post_json(client, '/translate-all', pattern) assert response.status_code == 200 expectedValue = {} expectedValue['pattern'] = pattern expectedValue['validated'] = validated for i, endpoint in enumerate(["car-elastic", "car-splunk", "cim-splunk"]): expectedValue[endpoint] = translatedResults[i] assert json.dumps(json.loads(response.data.decode('utf8')), sort_keys=True) == json.dumps(expectedValue, sort_keys=True)
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
def test_every_translate(client, pattern, validated, translatedResults): """ Test each of the car-elastic, car-splunk and cim-splunk endpoints This test makes it easier to document multiple translations for a given pattern """ for i, endpoint in enumerate(["car-elastic", "car-splunk", "cim-splunk"]): response = post_json(client, '/' + endpoint, pattern) expectedValue = {} expectedValue['pattern'] = pattern expectedValue['validated'] = validated expectedValue[endpoint] = translatedResults[i] assert response.status_code == 200 assert json.dumps(json.loads(response.data.decode('utf8')), sort_keys=True) == json.dumps(expectedValue, sort_keys=True)
def test_audit_boards_bad_round_id( client: FlaskClient, election_id: str, jurisdiction_ids: List[str], round_1_id: str, # pylint: disable=unused-argument ): set_logged_in_user(client, UserType.JURISDICTION_ADMIN, DEFAULT_JA_EMAIL) rv = post_json( client, f"/election/{election_id}/jurisdiction/{jurisdiction_ids[0]}/round/not-a-valid-id/audit-board", [{ "name": "Audit Board #1" }], ) assert rv.status_code == 404
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)
def test_ws_details(app, client): domoticz_login = app.config.DOMOTICZ_USERNAME domoticz_password = to_base64(str(app.config.DOMOTICZ_PASSWORD)) with app.app_context(): valid_pin = to_base64(get_pin()) json_payload = { 'pin': valid_pin, 'client': 'Android Google Pixel 3', 'client_uuid': str(uuid.uuid1()), 'login': domoticz_login, 'password': domoticz_password } response = post_json(client, 'api/beacon_auth/login', json_payload) response_json = json_response(response) assert response.status_code == 200 assert 'access_token' in response_json access_token = response_json['access_token']
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"])
def test_in_org_with_logged_in_admin(client: FlaskClient): org_id, _user_id = create_org_and_admin(user_email="*****@*****.**") set_logged_in_user(client, UserType.AUDIT_ADMIN, "*****@*****.**") rv = post_json( client, "/election/new", { "auditName": "Test Audit", "organizationId": org_id, "isMultiJurisdiction": True, }, ) response = json.loads(rv.data) election_id = response.get("electionId", None) assert election_id, response rv = client.get(f"/election/{election_id}/audit/status") assert json.loads(rv.data)["organizationId"] == org_id
def test_in_org_with_anonymous_user(client: FlaskClient): org = create_organization() rv = post_json( client, "/election/new", { "auditName": "Test Audit", "organizationId": org.id, "isMultiJurisdiction": True, }, ) assert json.loads(rv.data) == { "errors": [ { "message": f"Anonymous users do not have access to organization {org.id}", "errorType": "Unauthorized", } ] } assert rv.status_code == 401
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
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"])