def post(self, auth_id):
        self._require_admin()

        auth = ApiAuthAccess.get_by_id(auth_id)

        auth_types_enum = []
        if self.request.get('allow_edit_teams'):
            auth_types_enum.append(AuthType.EVENT_TEAMS)
        if self.request.get('allow_edit_matches'):
            auth_types_enum.append(AuthType.EVENT_MATCHES)
        if self.request.get('allow_edit_rankings'):
            auth_types_enum.append(AuthType.EVENT_RANKINGS)
        if self.request.get('allow_edit_alliances'):
            auth_types_enum.append(AuthType.EVENT_ALLIANCES)
        if self.request.get('allow_edit_awards'):
            auth_types_enum.append(AuthType.EVENT_AWARDS)
        if self.request.get('allow_edit_match_video'):
            auth_types_enum.append(AuthType.MATCH_VIDEO)

        if not auth:
            auth = ApiAuthAccess(
                id=auth_id,
                description=self.request.get('description'),
                secret=''.join(random.choice(string.ascii_lowercase + string.ascii_uppercase + string.digits) for _ in range(64)),
                event_list=[ndb.Key(Event, event_key.strip()) for event_key in self.request.get('event_list_str').split(',')],
                auth_types_enum=auth_types_enum,
            )
        else:
            auth.description = self.request.get('description')
            auth.event_list = event_list=[ndb.Key(Event, event_key.strip()) for event_key in self.request.get('event_list_str').split(',')]
            auth.auth_types_enum = auth_types_enum

        auth.put()

        self.redirect("/admin/api_auth/manage")
    def post(self, auth_id):
        self._require_admin()

        auth = ApiAuthAccess.get_by_id(auth_id)

        auth_types_enum = []
        if self.request.get('allow_edit_teams'):
            auth_types_enum.append(AuthType.EVENT_TEAMS)
        if self.request.get('allow_edit_matches'):
            auth_types_enum.append(AuthType.EVENT_MATCHES)
        if self.request.get('allow_edit_rankings'):
            auth_types_enum.append(AuthType.EVENT_RANKINGS)
        if self.request.get('allow_edit_alliances'):
            auth_types_enum.append(AuthType.EVENT_ALLIANCES)
        if self.request.get('allow_edit_awards'):
            auth_types_enum.append(AuthType.EVENT_AWARDS)
        if self.request.get('allow_edit_match_video'):
            auth_types_enum.append(AuthType.MATCH_VIDEO)

        if self.request.get('owner', None):
            owner = Account.query(Account.email == self.request.get('owner')).fetch()
            owner_key = owner[0].key if owner else None
        else:
            owner_key = None

        if self.request.get('expiration', None):
            expiration = datetime.strptime(self.request.get('expiration'), '%Y-%m-%d')
        else:
            expiration = None

        if not auth:
            auth = ApiAuthAccess(
                id=auth_id,
                description=self.request.get('description'),
                owner=owner_key,
                expiration=expiration,
                secret=''.join(random.choice(string.ascii_lowercase + string.ascii_uppercase + string.digits) for _ in range(64)),
                event_list=[ndb.Key(Event, event_key.strip()) for event_key in self.request.get('event_list_str').split(',')],
                auth_types_enum=auth_types_enum,
            )
        else:
            auth.description = self.request.get('description')
            auth.event_list = event_list=[ndb.Key(Event, event_key.strip()) for event_key in self.request.get('event_list_str').split(',')]
            auth.auth_types_enum = auth_types_enum
            auth.owner = owner_key
            auth.expiration = expiration

        auth.put()

        self.redirect("/admin/api_auth/manage")
Beispiel #3
0
    def _process_accepted(self, suggestion_id, message):
        suggestion = Suggestion.get_by_id(suggestion_id)
        event_key = suggestion.contents['event_key']
        user = suggestion.author.get()
        event = Event.get_by_id(event_key)

        auth_id = ''.join(
            random.choice(string.ascii_lowercase + string.ascii_uppercase +
                          string.digits) for _ in range(16))
        auth_types = self.request.get_all("auth_types", [])
        expiration_offset = int(self.request.get("expiration_days"))
        if expiration_offset != -1:
            expiration_event_end = event.end_date + timedelta(
                days=expiration_offset + 1)
            expiration_now = datetime.now() + timedelta(days=expiration_offset)
            expiration = max(expiration_event_end, expiration_now)
        else:
            expiration = None
        auth = ApiAuthAccess(
            id=auth_id,
            description="{} @ {}".format(user.display_name,
                                         suggestion.contents['event_key']),
            secret=''.join(
                random.choice(string.ascii_lowercase + string.ascii_uppercase +
                              string.digits) for _ in range(64)),
            event_list=[ndb.Key(Event, event_key)],
            auth_types_enum=[int(type) for type in auth_types],
            owner=suggestion.author,
            expiration=expiration)
        auth.put()

        suggestion.review_state = Suggestion.REVIEW_ACCEPTED
        suggestion.reviewer = self.user_bundle.account.key
        suggestion.reviewed_at = datetime.now()
        suggestion.put()

        return auth_id, user, event_key, """Hi {},

We graciously accept your request for auth tokens so you can add data to the following event: {} {}

You can find the keys on your account overview page: https://www.thebluealliance.com/account
{}
If you have any questions, please don't heasitate to reach out to us at [email protected]

Thanks,
TBA Admins
            """.format(user.display_name, event.year, event.name, message)
    def _process_accepted(self, suggestion_id, message):
        suggestion = Suggestion.get_by_id(suggestion_id)
        event_key = suggestion.contents['event_key']
        user = suggestion.author.get()
        event = Event.get_by_id(event_key)

        auth_id = ''.join(
            random.choice(string.ascii_lowercase + string.ascii_uppercase + string.digits) for _ in
            range(16))
        auth_types = self.request.get_all("auth_types", [])
        expiration_offset = int(self.request.get("expiration_days"))
        if expiration_offset != -1:
            expiration_event_end = event.end_date + timedelta(days=expiration_offset + 1)
            expiration_now = datetime.now() + timedelta(days=expiration_offset)
            expiration = max(expiration_event_end, expiration_now)
        else:
            expiration = None
        auth = ApiAuthAccess(
            id=auth_id,
            description="{} @ {}".format(user.display_name, suggestion.contents['event_key']),
            secret=''.join(
                random.choice(string.ascii_lowercase + string.ascii_uppercase + string.digits) for _
                in range(64)),
            event_list=[ndb.Key(Event, event_key)],
            auth_types_enum=[int(type) for type in auth_types],
            owner=suggestion.author,
            expiration=expiration
        )
        auth.put()

        suggestion.review_state = Suggestion.REVIEW_ACCEPTED
        suggestion.reviewer = self.user_bundle.account.key
        suggestion.reviewed_at = datetime.now()
        suggestion.put()

        return auth_id, user, event_key, """Hi {},

We graciously accept your request for auth tokens so you can add data to the following event: {} {}

You can find the keys on your account overview page: https://www.thebluealliance.com/account
{}
If you have any questions, please don't heasitate to reach out to us at [email protected]

Thanks,
TBA Admins
            """.format(user.display_name, event.year, event.name, message)
    def testExistingAuthKeys(self):
        self.loginUser()
        self.givePermission()

        existing_auth = ApiAuthAccess(id='tEsT_id_0',
                                      secret='321tEsTsEcReT',
                                      description='test',
                                      event_list=[ndb.Key(Event, '2016necmp')],
                                      auth_types_enum=[AuthType.EVENT_TEAMS])
        existing_auth.put()

        suggestion_id = self.createSuggestion()
        form = self.getSuggestionForm(suggestion_id)
        response = form.submit('verdict', value='accept').follow()
        self.assertEqual(response.status_int, 200)

        auths = ApiAuthAccess.query().fetch()
        self.assertTrue(len(auths), 2)
Beispiel #6
0
    def testExistingAuthKeys(self):
        self.loginUser()
        self.givePermission()

        existing_auth = ApiAuthAccess(id='tEsT_id_0',
                                      secret='321tEsTsEcReT',
                                      description='test',
                                      event_list=[ndb.Key(Event, '2016necmp')],
                                      auth_types_enum=[AuthType.EVENT_TEAMS])
        existing_auth.put()

        suggestion_id = self.createSuggestion()
        form = self.getSuggestionForm(suggestion_id)
        response = form.submit('verdict', value='accept').follow()
        self.assertEqual(response.status_int, 200)

        auths = ApiAuthAccess.query().fetch()
        self.assertTrue(len(auths), 2)
    def post(self, auth_id):
        self._require_admin()

        auth = ApiAuthAccess.get_by_id(auth_id)
        if not auth:
            auth = ApiAuthAccess(
                id=auth_id,
                description=self.request.get('description'),
                secret=''.join(random.choice(string.ascii_lowercase + string.ascii_uppercase + string.digits) for _ in range(64)),
                event_list=[ndb.Key(Event, event_key.strip()) for event_key in self.request.get('event_list_str').split(',')],
            )
        else:
            auth.description = self.request.get('description')
            auth.event_list = event_list=[ndb.Key(Event, event_key.strip()) for event_key in self.request.get('event_list_str').split(',')]

        auth.put()

        self.redirect("/admin/api_auth/manage")
Beispiel #8
0
    def post(self, auth_id):
        self._require_admin()

        auth = ApiAuthAccess.get_by_id(auth_id)

        auth_types_enum = []
        if self.request.get('allow_edit_teams'):
            auth_types_enum.append(AuthType.EVENT_TEAMS)
        if self.request.get('allow_edit_matches'):
            auth_types_enum.append(AuthType.EVENT_MATCHES)
        if self.request.get('allow_edit_rankings'):
            auth_types_enum.append(AuthType.EVENT_RANKINGS)
        if self.request.get('allow_edit_alliances'):
            auth_types_enum.append(AuthType.EVENT_ALLIANCES)
        if self.request.get('allow_edit_awards'):
            auth_types_enum.append(AuthType.EVENT_AWARDS)
        if self.request.get('allow_edit_match_video'):
            auth_types_enum.append(AuthType.MATCH_VIDEO)

        if not auth:
            auth = ApiAuthAccess(
                id=auth_id,
                description=self.request.get('description'),
                secret=''.join(
                    random.choice(string.ascii_lowercase +
                                  string.ascii_uppercase + string.digits)
                    for _ in range(64)),
                event_list=[
                    ndb.Key(Event, event_key.strip()) for event_key in
                    self.request.get('event_list_str').split(',')
                ],
                auth_types_enum=auth_types_enum,
            )
        else:
            auth.description = self.request.get('description')
            auth.event_list = event_list = [
                ndb.Key(Event, event_key.strip())
                for event_key in self.request.get('event_list_str').split(',')
            ]
            auth.auth_types_enum = auth_types_enum

        auth.put()

        self.redirect("/admin/api_auth/manage")
Beispiel #9
0
class TestApiTrustedController(unittest2.TestCase):
    def setUp(self):
        self.testapp = webtest.TestApp(api_main.app)

        self.testbed = testbed.Testbed()
        self.testbed.activate()
        self.testbed.init_datastore_v3_stub()
        self.testbed.init_urlfetch_stub()
        self.testbed.init_memcache_stub()
        self.testbed.init_user_stub()
        ndb.get_context().clear_cache(
        )  # Prevent data from leaking between tests

        self.testbed.init_taskqueue_stub(root_path=".")

        self.teams_auth = ApiAuthAccess(
            id='tEsT_id_0',
            secret='321tEsTsEcReT',
            description='test',
            event_list=[ndb.Key(Event, '2014casj')],
            auth_types_enum=[AuthType.EVENT_TEAMS])

        self.matches_auth = ApiAuthAccess(
            id='tEsT_id_1',
            secret='321tEsTsEcReT',
            description='test',
            event_list=[ndb.Key(Event, '2014casj')],
            auth_types_enum=[AuthType.EVENT_MATCHES])

        self.rankings_auth = ApiAuthAccess(
            id='tEsT_id_2',
            secret='321tEsTsEcReT',
            description='test',
            event_list=[ndb.Key(Event, '2014casj')],
            auth_types_enum=[AuthType.EVENT_RANKINGS])

        self.alliances_auth = ApiAuthAccess(
            id='tEsT_id_3',
            secret='321tEsTsEcReT',
            description='test',
            event_list=[ndb.Key(Event, '2014casj')],
            auth_types_enum=[AuthType.EVENT_ALLIANCES])

        self.awards_auth = ApiAuthAccess(
            id='tEsT_id_4',
            secret='321tEsTsEcReT',
            description='test',
            event_list=[ndb.Key(Event, '2014casj')],
            auth_types_enum=[AuthType.EVENT_AWARDS])

        self.video_auth = ApiAuthAccess(
            id='tEsT_id_5',
            secret='321tEsTsEcReT',
            description='test',
            event_list=[ndb.Key(Event, '2014casj')],
            auth_types_enum=[AuthType.MATCH_VIDEO])

        self.expired_auth = ApiAuthAccess(
            id='tEsT_id_6',
            secret='321tEsTsEcReT',
            description='test',
            event_list=[ndb.Key(Event, '2014casj')],
            auth_types_enum=[AuthType.EVENT_MATCHES],
            expiration=datetime.datetime(year=1970, month=1, day=1))

        self.owned_auth = ApiAuthAccess(
            id='tEsT_id_7',
            secret='321tEsTsEcReT',
            description='test',
            event_list=[ndb.Key(Event, '2014casj')],
            auth_types_enum=[AuthType.EVENT_MATCHES],
            owner=ndb.Key(Account, "42"))

        self.owned_auth_expired = ApiAuthAccess(
            id='tEsT_id_8',
            secret='321tEsTsEcReT',
            description='test',
            event_list=[ndb.Key(Event, '2014casj')],
            auth_types_enum=[AuthType.EVENT_MATCHES],
            owner=ndb.Key(Account, "42"),
            expiration=datetime.datetime(year=1970, month=1, day=1))

        self.event = Event(
            id='2014casj',
            event_type_enum=EventType.REGIONAL,
            event_short='casj',
            year=2014,
        )
        self.event.put()

    def tearDown(self):
        self.testbed.deactivate()

    def loginUser(self, is_admin=False):
        self.testbed.setup_env(user_email="*****@*****.**",
                               user_id="42",
                               user_is_admin='1' if is_admin else '0',
                               overwrite=True)

    def test_auth(self):
        request_path = '/api/trusted/v1/event/2014casj/matches/update'
        request_path_caps_key = '/api/trusted/v1/event/2014CASJ/matches/update'

        # Fail
        response = self.testapp.post(request_path, expect_errors=True)
        self.assertEqual(response.status_code, 400)
        self.assertTrue('Error' in response.json)

        # Fail
        request_body = json.dumps([])
        sig = md5.new('{}{}{}'.format('321tEsTsEcReT', request_path,
                                      request_body)).hexdigest()
        response = self.testapp.post(request_path,
                                     request_body,
                                     headers={
                                         'X-TBA-Auth-Id': 'tEsT_id_1',
                                         'X-TBA-Auth-Sig': sig
                                     },
                                     expect_errors=True)
        self.assertEqual(response.status_code, 401)
        self.assertTrue('Error' in response.json)

        self.rankings_auth.put()
        self.matches_auth.put()

        # Pass
        sig = md5.new('{}{}{}'.format('321tEsTsEcReT', request_path,
                                      request_body)).hexdigest()
        response = self.testapp.post(request_path,
                                     request_body,
                                     headers={
                                         'X-TBA-Auth-Id': 'tEsT_id_1',
                                         'X-TBA-Auth-Sig': sig
                                     },
                                     expect_errors=True)
        self.assertEqual(response.status_code, 200)

        # Pass; all caps key
        sig = md5.new('{}{}{}'.format('321tEsTsEcReT', request_path_caps_key,
                                      request_body)).hexdigest()
        response = self.testapp.post(request_path_caps_key,
                                     request_body,
                                     headers={
                                         'X-TBA-Auth-Id': 'tEsT_id_1',
                                         'X-TBA-Auth-Sig': sig
                                     },
                                     expect_errors=True)
        self.assertEqual(response.status_code, 200)

        # Fail; bad X-TBA-Auth-Id
        sig = md5.new('{}{}{}'.format('321tEsTsEcReT', request_path,
                                      request_body)).hexdigest()
        response = self.testapp.post(request_path,
                                     request_body,
                                     headers={
                                         'X-TBA-Auth-Id': 'badTestAuthId',
                                         'X-TBA-Auth-Sig': sig
                                     },
                                     expect_errors=True)
        self.assertEqual(response.status_code, 401)
        self.assertTrue('Error' in response.json)

        # Fail; bad sig
        response = self.testapp.post(request_path,
                                     request_body,
                                     headers={
                                         'X-TBA-Auth-Id': 'tEsT_id_1',
                                         'X-TBA-Auth-Sig': '123abc'
                                     },
                                     expect_errors=True)
        self.assertEqual(response.status_code, 401)
        self.assertTrue('Error' in response.json)

        # Fail; bad sig due to wrong body
        body2 = json.dumps([{}])
        sig = md5.new('{}{}{}'.format('321tEsTsEcReT', request_path,
                                      request_body)).hexdigest()
        response = self.testapp.post(request_path,
                                     body2,
                                     headers={
                                         'X-TBA-Auth-Id': 'tEsT_id_1',
                                         'X-TBA-Auth-Sig': sig
                                     },
                                     expect_errors=True)
        self.assertEqual(response.status_code, 401)
        self.assertTrue('Error' in response.json)

        # Fail; bad event
        request_path2 = '/api/trusted/v1/event/2014cama/matches/update'
        sig = md5.new('{}{}{}'.format('321tEsTsEcReT', request_path2,
                                      request_body)).hexdigest()
        response = self.testapp.post(request_path,
                                     request_body,
                                     headers={
                                         'X-TBA-Auth-Id': 'tEsT_id_1',
                                         'X-TBA-Auth-Sig': sig
                                     },
                                     expect_errors=True)
        self.assertEqual(response.status_code, 401)
        self.assertTrue('Error' in response.json)

        # Fail; insufficient auth_types_enum
        sig = md5.new('{}{}{}'.format('321tEsTsEcReT', request_path,
                                      request_body)).hexdigest()
        response = self.testapp.post(request_path,
                                     request_body,
                                     headers={
                                         'X-TBA-Auth-Id': 'tEsT_id_2',
                                         'X-TBA-Auth-Sig': sig
                                     },
                                     expect_errors=True)
        self.assertEqual(response.status_code, 401)
        self.assertTrue('Error' in response.json)

        # Fail; expired keys
        sig = md5.new('{}{}{}'.format('321tEsTsEcReT', request_path,
                                      request_body)).hexdigest()
        response = self.testapp.post(request_path,
                                     request_body,
                                     headers={
                                         'X-TBA-Auth-Id': 'tEsT_id_6',
                                         'X-TBA-Auth-Sig': sig
                                     },
                                     expect_errors=True)
        self.assertEqual(response.status_code, 401)
        self.assertTrue('Error' in response.json)

    def test_admin_auth(self):
        # Ensure that a logged in admin user can access any evet
        request_path = '/api/trusted/v1/event/2014casj/matches/update'
        request_body = json.dumps([])
        self.loginUser(is_admin=True)

        response = self.testapp.post(request_path,
                                     request_body,
                                     expect_errors=True)
        self.assertEqual(response.status_code, 200)

    def test_user_auth(self):
        # Ensure that a logged in user can use auths granted to their account
        request_path = '/api/trusted/v1/event/2014casj/matches/update'
        request_body = json.dumps([])
        self.owned_auth.put()
        self.loginUser()

        response = self.testapp.post(request_path,
                                     request_body,
                                     expect_errors=True)
        self.assertEqual(response.status_code, 200)

    def test_user_expired_auth(self):
        # Ensure that a logged in user can use auths granted to their account
        request_path = '/api/trusted/v1/event/2014casj/matches/update'
        request_body = json.dumps([])
        self.owned_auth_expired.put()
        self.loginUser()

        # Should end up with a 400 error because the expired key didn't count and no explicit
        # Auth-Id header was passed
        response = self.testapp.post(request_path,
                                     request_body,
                                     expect_errors=True)
        self.assertEqual(response.status_code, 400)
        self.assertTrue('Error' in response.json)

    def test_alliance_selections_update(self):
        self.alliances_auth.put()

        alliances = [['frc971', 'frc254', 'frc1662'],
                     ['frc1678', 'frc368', 'frc4171'],
                     ['frc2035', 'frc192', 'frc4990'],
                     ['frc1323', 'frc846', 'frc2135'],
                     ['frc2144', 'frc1388', 'frc668'],
                     ['frc1280', 'frc604', 'frc100'],
                     ['frc114', 'frc852', 'frc841'],
                     ['frc2473', 'frc3256', 'frc1868']]
        request_body = json.dumps(alliances)

        request_path = '/api/trusted/v1/event/2014casj/alliance_selections/update'
        sig = md5.new('{}{}{}'.format('321tEsTsEcReT', request_path,
                                      request_body)).hexdigest()
        response = self.testapp.post(request_path,
                                     request_body,
                                     headers={
                                         'X-TBA-Auth-Id': 'tEsT_id_3',
                                         'X-TBA-Auth-Sig': sig
                                     },
                                     expect_errors=True)

        self.assertEqual(response.status_code, 200)

        self.assertEqual(len(self.event.alliance_selections), 8)
        for i, selection in enumerate(self.event.alliance_selections):
            self.assertEqual(alliances[i], selection['picks'])

    def test_empty_alliance_selections_update(self):
        self.alliances_auth.put()

        alliances = [['frc971', 'frc254', 'frc1662'],
                     ['frc1678', 'frc368', 'frc4171'],
                     ['frc2035', 'frc192', 'frc4990'],
                     ['frc1323', 'frc846', 'frc2135'], [], [], [], []]
        request_body = json.dumps(alliances)

        request_path = '/api/trusted/v1/event/2014casj/alliance_selections/update'
        sig = md5.new('{}{}{}'.format('321tEsTsEcReT', request_path,
                                      request_body)).hexdigest()
        response = self.testapp.post(request_path,
                                     request_body,
                                     headers={
                                         'X-TBA-Auth-Id': 'tEsT_id_3',
                                         'X-TBA-Auth-Sig': sig
                                     },
                                     expect_errors=True)

        self.assertEqual(response.status_code, 200)

        self.assertEqual(len(self.event.alliance_selections), 4)
        for i, selection in enumerate(self.event.alliance_selections):
            self.assertEqual(alliances[i], selection['picks'])

    def test_awards_update(self):
        self.awards_auth.put()

        awards = [{
            'name_str': 'Winner',
            'team_key': 'frc254'
        }, {
            'name_str': 'Winner',
            'team_key': 'frc604'
        }, {
            'name_str': 'Volunteer Blahblah',
            'team_key': 'frc1',
            'awardee': 'Bob Bobby'
        }]
        request_body = json.dumps(awards)

        request_path = '/api/trusted/v1/event/2014casj/awards/update'
        sig = md5.new('{}{}{}'.format('321tEsTsEcReT', request_path,
                                      request_body)).hexdigest()
        response = self.testapp.post(request_path,
                                     request_body,
                                     headers={
                                         'X-TBA-Auth-Id': 'tEsT_id_4',
                                         'X-TBA-Auth-Sig': sig
                                     },
                                     expect_errors=True)

        self.assertEqual(response.status_code, 200)

        db_awards = Award.query(Award.event == self.event.key).fetch(None)
        self.assertEqual(len(db_awards), 2)
        self.assertTrue('2014casj_1' in [a.key.id() for a in db_awards])
        self.assertTrue('2014casj_5' in [a.key.id() for a in db_awards])

        awards = [{
            'name_str': 'Winner',
            'team_key': 'frc254'
        }, {
            'name_str': 'Winner',
            'team_key': 'frc604'
        }]
        request_body = json.dumps(awards)

        sig = md5.new('{}{}{}'.format('321tEsTsEcReT', request_path,
                                      request_body)).hexdigest()
        response = self.testapp.post(request_path,
                                     request_body,
                                     headers={
                                         'X-TBA-Auth-Id': 'tEsT_id_4',
                                         'X-TBA-Auth-Sig': sig
                                     },
                                     expect_errors=True)

        self.assertEqual(response.status_code, 200)

        db_awards = Award.query(Award.event == self.event.key).fetch(None)
        self.assertEqual(len(db_awards), 1)
        self.assertTrue('2014casj_1' in [a.key.id() for a in db_awards])

    def test_matches_update(self):
        self.matches_auth.put()

        update_request_path = '/api/trusted/v1/event/2014casj/matches/update'
        delete_request_path = '/api/trusted/v1/event/2014casj/matches/delete'
        delete_all_request_path = '/api/trusted/v1/event/2014casj/matches/delete_all'

        # add one match
        matches = [{
            'comp_level': 'qm',
            'set_number': 1,
            'match_number': 1,
            'alliances': {
                'red': {
                    'teams': ['frc1', 'frc2', 'frc3'],
                    'score': 25
                },
                'blue': {
                    'teams': ['frc4', 'frc5', 'frc6'],
                    'score': 26
                },
            },
            'time_string': '9:00 AM',
            'time_utc': '2014-08-31T16:00:00',
        }]
        request_body = json.dumps(matches)
        sig = md5.new('{}{}{}'.format('321tEsTsEcReT', update_request_path,
                                      request_body)).hexdigest()
        response = self.testapp.post(update_request_path,
                                     request_body,
                                     headers={
                                         'X-TBA-Auth-Id': 'tEsT_id_1',
                                         'X-TBA-Auth-Sig': sig
                                     },
                                     expect_errors=True)

        self.assertEqual(response.status_code, 200)

        db_matches = Match.query(Match.event == self.event.key).fetch(None)
        self.assertEqual(len(db_matches), 1)
        self.assertTrue('2014casj_qm1' in [m.key.id() for m in db_matches])

        # add another match
        matches = [{
            'comp_level': 'f',
            'set_number': 1,
            'match_number': 1,
            'alliances': {
                'red': {
                    'teams': ['frc1', 'frc2', 'frc3'],
                    'score': 250
                },
                'blue': {
                    'teams': ['frc4', 'frc5', 'frc6'],
                    'score': 260
                },
            },
            'time_string': '10:00 AM',
            'time_utc': '2014-08-31T17:00:00',
        }]
        request_body = json.dumps(matches)
        sig = md5.new('{}{}{}'.format('321tEsTsEcReT', update_request_path,
                                      request_body)).hexdigest()
        response = self.testapp.post(update_request_path,
                                     request_body,
                                     headers={
                                         'X-TBA-Auth-Id': 'tEsT_id_1',
                                         'X-TBA-Auth-Sig': sig
                                     },
                                     expect_errors=True)
        self.assertEqual(response.status_code, 200)

        db_matches = Match.query(Match.event == self.event.key).fetch(None)
        self.assertEqual(len(db_matches), 2)
        self.assertTrue('2014casj_qm1' in [m.key.id() for m in db_matches])
        self.assertTrue('2014casj_f1m1' in [m.key.id() for m in db_matches])

        # add a match and delete a match
        matches = [{
            'comp_level': 'f',
            'set_number': 1,
            'match_number': 2,
            'alliances': {
                'red': {
                    'teams': ['frc1', 'frc2', 'frc3'],
                    'score': 250
                },
                'blue': {
                    'teams': ['frc4', 'frc5', 'frc6'],
                    'score': 260
                },
            },
            'score_breakdown': {
                'red': {
                    'auto': 20,
                    'assist': 40,
                    'truss+catch': 20,
                    'teleop_goal+foul': 20
                },
                'blue': {
                    'auto': 40,
                    'assist': 60,
                    'truss+catch': 10,
                    'teleop_goal+foul': 40
                },
            },
            'time_string': '11:00 AM',
            'time_utc': '2014-08-31T18:00:00',
        }]
        request_body = json.dumps(matches)
        sig = md5.new('{}{}{}'.format('321tEsTsEcReT', update_request_path,
                                      request_body)).hexdigest()
        response = self.testapp.post(update_request_path,
                                     request_body,
                                     headers={
                                         'X-TBA-Auth-Id': 'tEsT_id_1',
                                         'X-TBA-Auth-Sig': sig
                                     },
                                     expect_errors=True)
        self.assertEqual(response.status_code, 200)

        keys_to_delete = ['qm1']
        request_body = json.dumps(keys_to_delete)
        sig = md5.new('{}{}{}'.format('321tEsTsEcReT', delete_request_path,
                                      request_body)).hexdigest()
        response = self.testapp.post(delete_request_path,
                                     request_body,
                                     headers={
                                         'X-TBA-Auth-Id': 'tEsT_id_1',
                                         'X-TBA-Auth-Sig': sig
                                     },
                                     expect_errors=True)
        self.assertEqual(response.status_code, 200)
        self.assertEqual(response.json['keys_deleted'], ['qm1'])

        db_matches = Match.query(Match.event == self.event.key).fetch(None)
        self.assertEqual(len(db_matches), 2)
        self.assertTrue('2014casj_f1m1' in [m.key.id() for m in db_matches])
        self.assertTrue('2014casj_f1m2' in [m.key.id() for m in db_matches])

        db_matches = Match.query(Match.event == self.event.key).fetch(None)
        self.assertEqual(len(db_matches), 2)
        self.assertTrue('2014casj_f1m1' in [m.key.id() for m in db_matches])
        self.assertTrue('2014casj_f1m2' in [m.key.id() for m in db_matches])

        # verify match data
        match = Match.get_by_id('2014casj_f1m2')
        self.assertEqual(match.time, datetime.datetime(2014, 8, 31, 18, 0))
        self.assertEqual(match.time_string, '11:00 AM')
        self.assertEqual(match.alliances['red']['score'], 250)
        self.assertEqual(match.score_breakdown['red']['truss+catch'], 20)

        # test delete all matches
        request_body = ''
        sig = md5.new('{}{}{}'.format('321tEsTsEcReT', delete_all_request_path,
                                      request_body)).hexdigest()
        response = self.testapp.post(delete_all_request_path,
                                     request_body,
                                     headers={
                                         'X-TBA-Auth-Id': 'tEsT_id_1',
                                         'X-TBA-Auth-Sig': sig
                                     },
                                     expect_errors=True)
        self.assertEqual(response.status_code, 400)

        request_body = '2014casj'
        sig = md5.new('{}{}{}'.format('321tEsTsEcReT', delete_all_request_path,
                                      request_body)).hexdigest()
        response = self.testapp.post(delete_all_request_path,
                                     request_body,
                                     headers={
                                         'X-TBA-Auth-Id': 'tEsT_id_1',
                                         'X-TBA-Auth-Sig': sig
                                     },
                                     expect_errors=True)
        self.assertEqual(response.status_code, 200)

        db_matches = Match.query(Match.event == self.event.key).fetch(None)
        self.assertEqual(len(db_matches), 0)

    def test_rankings_update(self):
        self.rankings_auth.put()

        rankings = {
            'breakdowns': ['QS', 'Auton', 'Teleop', 'T&C'],
            'rankings': [{
                'team_key': 'frc254',
                'rank': 1,
                'played': 10,
                'dqs': 0,
                'QS': 20,
                'Auton': 500,
                'Teleop': 500,
                'T&C': 200
            }, {
                'team_key': 'frc971',
                'rank': 2,
                'played': 10,
                'dqs': 0,
                'QS': 20,
                'Auton': 500,
                'Teleop': 500,
                'T&C': 200
            }],
        }
        request_body = json.dumps(rankings)

        request_path = '/api/trusted/v1/event/2014casj/rankings/update'
        sig = md5.new('{}{}{}'.format('321tEsTsEcReT', request_path,
                                      request_body)).hexdigest()
        response = self.testapp.post(request_path,
                                     request_body,
                                     headers={
                                         'X-TBA-Auth-Id': 'tEsT_id_2',
                                         'X-TBA-Auth-Sig': sig
                                     },
                                     expect_errors=True)

        self.assertEqual(response.status_code, 200)
        self.assertEqual(
            self.event.rankings[0],
            ['Rank', 'Team', 'QS', 'Auton', 'Teleop', 'T&C', 'DQ', 'Played'])
        self.assertEqual(self.event.rankings[1],
                         [1, '254', 20, 500, 500, 200, 0, 10])

    def test_rankings_wlt_update(self):
        self.rankings_auth.put()

        rankings = {
            'breakdowns':
            ['QS', 'Auton', 'Teleop', 'T&C', 'wins', 'losses', 'ties'],
            'rankings': [{
                'team_key': 'frc254',
                'rank': 1,
                'wins': 10,
                'losses': 0,
                'ties': 0,
                'played': 10,
                'dqs': 0,
                'QS': 20,
                'Auton': 500,
                'Teleop': 500,
                'T&C': 200
            }, {
                'team_key': 'frc971',
                'rank': 2,
                'wins': 10,
                'losses': 0,
                'ties': 0,
                'played': 10,
                'dqs': 0,
                'QS': 20,
                'Auton': 500,
                'Teleop': 500,
                'T&C': 200
            }],
        }
        request_body = json.dumps(rankings)

        request_path = '/api/trusted/v1/event/2014casj/rankings/update'
        sig = md5.new('{}{}{}'.format('321tEsTsEcReT', request_path,
                                      request_body)).hexdigest()
        response = self.testapp.post(request_path,
                                     request_body,
                                     headers={
                                         'X-TBA-Auth-Id': 'tEsT_id_2',
                                         'X-TBA-Auth-Sig': sig
                                     },
                                     expect_errors=True)

        self.assertEqual(response.status_code, 200)
        self.assertEqual(self.event.rankings[0], [
            'Rank', 'Team', 'QS', 'Auton', 'Teleop', 'T&C', 'Record (W-L-T)',
            'DQ', 'Played'
        ])
        self.assertEqual(self.event.rankings[1],
                         [1, '254', 20, 500, 500, 200, '10-0-0', 0, 10])

    def test_eventteams_update(self):
        self.teams_auth.put()

        team_list = ['frc254', 'frc971', 'frc604']
        request_body = json.dumps(team_list)

        # Insert teams into db, otherwise they won't get added (see 072058b)
        Team(id='frc254', team_number=254).put()
        Team(id='frc971', team_number=971).put()
        Team(id='frc604', team_number=604).put()
        Team(id='frc100', team_number=100).put()

        request_path = '/api/trusted/v1/event/2014casj/team_list/update'
        sig = md5.new('{}{}{}'.format('321tEsTsEcReT', request_path,
                                      request_body)).hexdigest()
        response = self.testapp.post(request_path,
                                     request_body,
                                     headers={
                                         'X-TBA-Auth-Id': 'tEsT_id_0',
                                         'X-TBA-Auth-Sig': sig
                                     },
                                     expect_errors=True)

        self.assertEqual(response.status_code, 200)

        db_eventteams = EventTeam.query(
            EventTeam.event == self.event.key).fetch(None)
        self.assertEqual(len(db_eventteams), 3)
        self.assertTrue(
            '2014casj_frc254' in [et.key.id() for et in db_eventteams])
        self.assertTrue(
            '2014casj_frc971' in [et.key.id() for et in db_eventteams])
        self.assertTrue(
            '2014casj_frc604' in [et.key.id() for et in db_eventteams])

        team_list = ['frc254', 'frc100']
        request_body = json.dumps(team_list)

        sig = md5.new('{}{}{}'.format('321tEsTsEcReT', request_path,
                                      request_body)).hexdigest()
        response = self.testapp.post(request_path,
                                     request_body,
                                     headers={
                                         'X-TBA-Auth-Id': 'tEsT_id_0',
                                         'X-TBA-Auth-Sig': sig
                                     },
                                     expect_errors=True)

        self.assertEqual(response.status_code, 200)

        db_eventteams = EventTeam.query(
            EventTeam.event == self.event.key).fetch(None)
        self.assertEqual(len(db_eventteams), 2)
        self.assertTrue(
            '2014casj_frc254' in [et.key.id() for et in db_eventteams])
        self.assertTrue(
            '2014casj_frc100' in [et.key.id() for et in db_eventteams])

    def test_eventteams_unknown(self):
        self.teams_auth.put()

        team_list = ['frc254', 'frc971', 'frc604']
        request_body = json.dumps(team_list)

        # Insert teams into db, otherwise they won't get added (see 072058b)
        Team(id='frc254', team_number=254).put()
        Team(id='frc971', team_number=971).put()

        request_path = '/api/trusted/v1/event/2014casj/team_list/update'
        sig = md5.new('{}{}{}'.format('321tEsTsEcReT', request_path,
                                      request_body)).hexdigest()
        response = self.testapp.post(request_path,
                                     request_body,
                                     headers={
                                         'X-TBA-Auth-Id': 'tEsT_id_0',
                                         'X-TBA-Auth-Sig': sig
                                     },
                                     expect_errors=True)

        self.assertEqual(response.status_code, 200)

        db_eventteams = EventTeam.query(
            EventTeam.event == self.event.key).fetch(None)
        self.assertEqual(len(db_eventteams), 2)
        self.assertTrue(
            '2014casj_frc254' in [et.key.id() for et in db_eventteams])
        self.assertTrue(
            '2014casj_frc971' in [et.key.id() for et in db_eventteams])
        self.assertTrue(
            '2014casj_frc604' not in [et.key.id() for et in db_eventteams])

        team_list = ['frc254', 'frc100']
        request_body = json.dumps(team_list)

        sig = md5.new('{}{}{}'.format('321tEsTsEcReT', request_path,
                                      request_body)).hexdigest()
        response = self.testapp.post(request_path,
                                     request_body,
                                     headers={
                                         'X-TBA-Auth-Id': 'tEsT_id_0',
                                         'X-TBA-Auth-Sig': sig
                                     },
                                     expect_errors=True)

        self.assertEqual(response.status_code, 200)

        db_eventteams = EventTeam.query(
            EventTeam.event == self.event.key).fetch(None)
        self.assertEqual(len(db_eventteams), 1)
        self.assertTrue(
            '2014casj_frc254' in [et.key.id() for et in db_eventteams])
        self.assertTrue(
            '2014casj_frc100' not in [et.key.id() for et in db_eventteams])

    def test_match_videos_add(self):
        self.video_auth.put()

        match1 = Match(
            id="2014casj_qm1",
            alliances_json=
            """{"blue": {"score": -1, "teams": ["frc3464", "frc20", "frc1073"]}, "red": {"score": -1, "teams": ["frc69", "frc571", "frc176"]}}""",
            comp_level="qm",
            event=ndb.Key(Event, '2014casj'),
            year=2014,
            set_number=1,
            match_number=1,
            team_key_names=[
                u'frc69', u'frc571', u'frc176', u'frc3464', u'frc20',
                u'frc1073'
            ],
            youtube_videos=["abcdef"])
        match1.put()

        match2 = Match(
            id="2014casj_sf1m1",
            alliances_json=
            """{"blue": {"score": -1, "teams": ["frc3464", "frc20", "frc1073"]}, "red": {"score": -1, "teams": ["frc69", "frc571", "frc176"]}}""",
            comp_level="sf",
            event=ndb.Key(Event, '2014casj'),
            year=2014,
            set_number=1,
            match_number=1,
            team_key_names=[
                u'frc69', u'frc571', u'frc176', u'frc3464', u'frc20',
                u'frc1073'
            ],
        )
        match2.put()

        match_videos = {'qm1': 'aFZy8iibMD0', 'sf1m1': 'RpSgUrsghv4'}

        request_body = json.dumps(match_videos)

        request_path = '/api/trusted/v1/event/2014casj/match_videos/add'
        sig = md5.new('{}{}{}'.format('321tEsTsEcReT', request_path,
                                      request_body)).hexdigest()
        response = self.testapp.post(request_path,
                                     request_body,
                                     headers={
                                         'X-TBA-Auth-Id': 'tEsT_id_5',
                                         'X-TBA-Auth-Sig': sig
                                     },
                                     expect_errors=True)

        self.assertEqual(response.status_code, 200)

        self.assertEqual(set(Match.get_by_id('2014casj_qm1').youtube_videos),
                         {'abcdef', 'aFZy8iibMD0'})
        self.assertEqual(set(Match.get_by_id('2014casj_sf1m1').youtube_videos),
                         {'RpSgUrsghv4'})
class TestApiTrustedController(unittest2.TestCase):

    def setUp(self):
        self.testapp = webtest.TestApp(api_main.app)

        self.testbed = testbed.Testbed()
        self.testbed.activate()
        self.testbed.init_datastore_v3_stub()
        self.testbed.init_urlfetch_stub()
        self.testbed.init_memcache_stub()
        self.testbed.init_taskqueue_stub(root_path=".")

        self.teams_auth = ApiAuthAccess(id='tEsT_id_0',
                                        secret='321tEsTsEcReT',
                                        description='test',
                                        event_list=[ndb.Key(Event, '2014casj')],
                                        auth_types_enum=[AuthType.EVENT_TEAMS])

        self.matches_auth = ApiAuthAccess(id='tEsT_id_1',
                                          secret='321tEsTsEcReT',
                                          description='test',
                                          event_list=[ndb.Key(Event, '2014casj')],
                                          auth_types_enum=[AuthType.EVENT_MATCHES])

        self.rankings_auth = ApiAuthAccess(id='tEsT_id_2',
                                           secret='321tEsTsEcReT',
                                           description='test',
                                           event_list=[ndb.Key(Event, '2014casj')],
                                           auth_types_enum=[AuthType.EVENT_RANKINGS])

        self.alliances_auth = ApiAuthAccess(id='tEsT_id_3',
                                            secret='321tEsTsEcReT',
                                            description='test',
                                            event_list=[ndb.Key(Event, '2014casj')],
                                            auth_types_enum=[AuthType.EVENT_ALLIANCES])

        self.awards_auth = ApiAuthAccess(id='tEsT_id_4',
                                         secret='321tEsTsEcReT',
                                         description='test',
                                         event_list=[ndb.Key(Event, '2014casj')],
                                         auth_types_enum=[AuthType.EVENT_AWARDS])

        self.video_auth = ApiAuthAccess(id='tEsT_id_5',
                                        secret='321tEsTsEcReT',
                                        description='test',
                                        event_list=[ndb.Key(Event, '2014casj')],
                                        auth_types_enum=[AuthType.MATCH_VIDEO])

        self.event = Event(
            id='2014casj',
            event_type_enum=EventType.REGIONAL,
            event_short='casj',
            year=2014,
        )
        self.event.put()

    def tearDown(self):
        self.testbed.deactivate()

    def test_auth(self):
        request_path = '/api/trusted/v1/event/2014casj/matches/update'

        # Fail
        response = self.testapp.post(request_path, expect_errors=True)
        self.assertEqual(response.status_code, 400)
        self.assertTrue('Error' in response.json)

        # Fail
        request_body = json.dumps([])
        sig = md5.new('{}{}{}'.format('321tEsTsEcReT', request_path, request_body)).hexdigest()
        response = self.testapp.post(request_path, request_body, headers={'X-TBA-Auth-Id': 'tEsT_id_1', 'X-TBA-Auth-Sig': sig}, expect_errors=True)
        self.assertEqual(response.status_code, 400)
        self.assertTrue('Error' in response.json)

        self.rankings_auth.put()
        self.matches_auth.put()

        # Pass
        sig = md5.new('{}{}{}'.format('321tEsTsEcReT', request_path, request_body)).hexdigest()
        response = self.testapp.post(request_path, request_body, headers={'X-TBA-Auth-Id': 'tEsT_id_1', 'X-TBA-Auth-Sig': sig}, expect_errors=True)
        self.assertEqual(response.status_code, 200)

        # Fail; bad X-TBA-Auth-Id
        sig = md5.new('{}{}{}'.format('321tEsTsEcReT', request_path, request_body)).hexdigest()
        response = self.testapp.post(request_path, request_body, headers={'X-TBA-Auth-Id': 'badTestAuthId', 'X-TBA-Auth-Sig': sig}, expect_errors=True)
        self.assertEqual(response.status_code, 400)
        self.assertTrue('Error' in response.json)

        # Fail; bad sig
        response = self.testapp.post(request_path, request_body, headers={'X-TBA-Auth-Id': 'tEsT_id_1', 'X-TBA-Auth-Sig': '123abc'}, expect_errors=True)
        self.assertEqual(response.status_code, 400)
        self.assertTrue('Error' in response.json)

        # Fail; bad sig due to wrong body
        body2 = json.dumps([{}])
        sig = md5.new('{}{}{}'.format('321tEsTsEcReT', request_path, request_body)).hexdigest()
        response = self.testapp.post(request_path, body2, headers={'X-TBA-Auth-Id': 'tEsT_id_1', 'X-TBA-Auth-Sig': sig}, expect_errors=True)
        self.assertEqual(response.status_code, 400)
        self.assertTrue('Error' in response.json)

        # Fail; bad event
        request_path2 = '/api/trusted/v1/event/2014cama/matches/update'
        sig = md5.new('{}{}{}'.format('321tEsTsEcReT', request_path2, request_body)).hexdigest()
        response = self.testapp.post(request_path, request_body, headers={'X-TBA-Auth-Id': 'tEsT_id_1', 'X-TBA-Auth-Sig': sig}, expect_errors=True)
        self.assertEqual(response.status_code, 400)
        self.assertTrue('Error' in response.json)

        # Fail; insufficient auth_types_enum
        sig = md5.new('{}{}{}'.format('321tEsTsEcReT', request_path, request_body)).hexdigest()
        response = self.testapp.post(request_path, request_body, headers={'X-TBA-Auth-Id': 'tEsT_id_2', 'X-TBA-Auth-Sig': sig}, expect_errors=True)
        self.assertEqual(response.status_code, 400)

    def test_alliance_selections_update(self):
        self.alliances_auth.put()

        alliances = [['frc971', 'frc254', 'frc1662'],
                     ['frc1678', 'frc368', 'frc4171'],
                     ['frc2035', 'frc192', 'frc4990'],
                     ['frc1323', 'frc846', 'frc2135'],
                     ['frc2144', 'frc1388', 'frc668'],
                     ['frc1280', 'frc604', 'frc100'],
                     ['frc114', 'frc852', 'frc841'],
                     ['frc2473', 'frc3256', 'frc1868']]
        request_body = json.dumps(alliances)

        request_path = '/api/trusted/v1/event/2014casj/alliance_selections/update'
        sig = md5.new('{}{}{}'.format('321tEsTsEcReT', request_path, request_body)).hexdigest()
        response = self.testapp.post(request_path, request_body, headers={'X-TBA-Auth-Id': 'tEsT_id_3', 'X-TBA-Auth-Sig': sig}, expect_errors=True)

        self.assertEqual(response.status_code, 200)

        for i, selection in enumerate(self.event.alliance_selections):
            self.assertEqual(alliances[i], selection['picks'])

    def test_awards_update(self):
        self.awards_auth.put()

        awards = [{'name_str': 'Winner', 'team_key': 'frc254'},
                  {'name_str': 'Winner', 'team_key': 'frc604'},
                  {'name_str': 'Volunteer Blahblah', 'team_key': 'frc1', 'awardee': 'Bob Bobby'}]
        request_body = json.dumps(awards)

        request_path = '/api/trusted/v1/event/2014casj/awards/update'
        sig = md5.new('{}{}{}'.format('321tEsTsEcReT', request_path, request_body)).hexdigest()
        response = self.testapp.post(request_path, request_body, headers={'X-TBA-Auth-Id': 'tEsT_id_4', 'X-TBA-Auth-Sig': sig}, expect_errors=True)

        self.assertEqual(response.status_code, 200)

        db_awards = Award.query(Award.event == self.event.key).fetch(None)
        self.assertEqual(len(db_awards), 2)
        self.assertTrue('2014casj_1' in [a.key.id() for a in db_awards])
        self.assertTrue('2014casj_5' in [a.key.id() for a in db_awards])

        awards = [{'name_str': 'Winner', 'team_key': 'frc254'},
                  {'name_str': 'Winner', 'team_key': 'frc604'}]
        request_body = json.dumps(awards)

        sig = md5.new('{}{}{}'.format('321tEsTsEcReT', request_path, request_body)).hexdigest()
        response = self.testapp.post(request_path, request_body, headers={'X-TBA-Auth-Id': 'tEsT_id_4', 'X-TBA-Auth-Sig': sig}, expect_errors=True)

        self.assertEqual(response.status_code, 200)

        db_awards = Award.query(Award.event == self.event.key).fetch(None)
        self.assertEqual(len(db_awards), 1)
        self.assertTrue('2014casj_1' in [a.key.id() for a in db_awards])

    def test_matches_update(self):
        self.matches_auth.put()

        update_request_path = '/api/trusted/v1/event/2014casj/matches/update'
        delete_request_path = '/api/trusted/v1/event/2014casj/matches/delete'

        # add one match
        matches = [{
            'comp_level': 'qm',
            'set_number': 1,
            'match_number': 1,
            'alliances': {
                'red': {'teams': ['frc1', 'frc2', 'frc3'],
                        'score': 25},
                'blue': {'teams': ['frc4', 'frc5', 'frc6'],
                         'score': 26},
            },
            'time_string': '9:00 AM',
            'time_utc': '2014-08-31T16:00:00',
        }]
        request_body = json.dumps(matches)
        sig = md5.new('{}{}{}'.format('321tEsTsEcReT', update_request_path, request_body)).hexdigest()
        response = self.testapp.post(update_request_path, request_body, headers={'X-TBA-Auth-Id': 'tEsT_id_1', 'X-TBA-Auth-Sig': sig}, expect_errors=True)

        self.assertEqual(response.status_code, 200)

        db_matches = Match.query(Match.event == self.event.key).fetch(None)
        self.assertEqual(len(db_matches), 1)
        self.assertTrue('2014casj_qm1' in [m.key.id() for m in db_matches])

        # add another match
        matches = [{
            'comp_level': 'f',
            'set_number': 1,
            'match_number': 1,
            'alliances': {
                'red': {'teams': ['frc1', 'frc2', 'frc3'],
                        'score': 250},
                'blue': {'teams': ['frc4', 'frc5', 'frc6'],
                         'score': 260},
            },
            'time_string': '10:00 AM',
            'time_utc': '2014-08-31T17:00:00',
        }]
        request_body = json.dumps(matches)
        sig = md5.new('{}{}{}'.format('321tEsTsEcReT', update_request_path, request_body)).hexdigest()
        response = self.testapp.post(update_request_path, request_body, headers={'X-TBA-Auth-Id': 'tEsT_id_1', 'X-TBA-Auth-Sig': sig}, expect_errors=True)
        self.assertEqual(response.status_code, 200)

        db_matches = Match.query(Match.event == self.event.key).fetch(None)
        self.assertEqual(len(db_matches), 2)
        self.assertTrue('2014casj_qm1' in [m.key.id() for m in db_matches])
        self.assertTrue('2014casj_f1m1' in [m.key.id() for m in db_matches])

        # add a match and delete a match
        matches = [{
            'comp_level': 'f',
            'set_number': 1,
            'match_number': 2,
            'alliances': {
                'red': {'teams': ['frc1', 'frc2', 'frc3'],
                        'score': 250},
                'blue': {'teams': ['frc4', 'frc5', 'frc6'],
                         'score': 260},
            },
            'score_breakdown': {
                'red': {'auto': 20, 'assist': 40, 'truss+catch': 20, 'teleop_goal+foul': 20},
                'blue': {'auto': 40, 'assist': 60, 'truss+catch': 10, 'teleop_goal+foul': 40},
            },
            'time_string': '11:00 AM',
            'time_utc': '2014-08-31T18:00:00',
        }]
        request_body = json.dumps(matches)
        sig = md5.new('{}{}{}'.format('321tEsTsEcReT', update_request_path, request_body)).hexdigest()
        response = self.testapp.post(update_request_path, request_body, headers={'X-TBA-Auth-Id': 'tEsT_id_1', 'X-TBA-Auth-Sig': sig}, expect_errors=True)
        self.assertEqual(response.status_code, 200)

        keys_to_delete = ['qm1']
        request_body = json.dumps(keys_to_delete)
        sig = md5.new('{}{}{}'.format('321tEsTsEcReT', delete_request_path, request_body)).hexdigest()
        response = self.testapp.post(delete_request_path, request_body, headers={'X-TBA-Auth-Id': 'tEsT_id_1', 'X-TBA-Auth-Sig': sig}, expect_errors=True)
        self.assertEqual(response.status_code, 200)
        self.assertEqual(response.json['keys_deleted'], ['qm1'])

        db_matches = Match.query(Match.event == self.event.key).fetch(None)
        self.assertEqual(len(db_matches), 2)
        self.assertTrue('2014casj_f1m1' in [m.key.id() for m in db_matches])
        self.assertTrue('2014casj_f1m2' in [m.key.id() for m in db_matches])

        db_matches = Match.query(Match.event == self.event.key).fetch(None)
        self.assertEqual(len(db_matches), 2)
        self.assertTrue('2014casj_f1m1' in [m.key.id() for m in db_matches])
        self.assertTrue('2014casj_f1m2' in [m.key.id() for m in db_matches])

        # verify match data
        match = Match.get_by_id('2014casj_f1m2')
        self.assertEqual(match.time, datetime.datetime(2014, 8, 31, 18, 0))
        self.assertEqual(match.time_string, '11:00 AM')
        self.assertEqual(match.alliances['red']['score'], 250)
        self.assertEqual(match.score_breakdown['red']['truss+catch'], 20)

    def test_rankings_update(self):
        self.rankings_auth.put()

        rankings = {
            'breakdowns': ['QS', 'Auton', 'Teleop', 'T&C'],
            'rankings': [
                {'team_key': 'frc254', 'rank': 1, 'played': 10, 'dqs': 0, 'QS': 20, 'Auton': 500, 'Teleop': 500, 'T&C': 200},
                {'team_key': 'frc971', 'rank': 2, 'played': 10, 'dqs': 0, 'QS': 20, 'Auton': 500, 'Teleop': 500, 'T&C': 200}
            ],
        }
        request_body = json.dumps(rankings)

        request_path = '/api/trusted/v1/event/2014casj/rankings/update'
        sig = md5.new('{}{}{}'.format('321tEsTsEcReT', request_path, request_body)).hexdigest()
        response = self.testapp.post(request_path, request_body, headers={'X-TBA-Auth-Id': 'tEsT_id_2', 'X-TBA-Auth-Sig': sig}, expect_errors=True)

        self.assertEqual(response.status_code, 200)
        self.assertEqual(self.event.rankings[0], ['Rank', 'Team', 'QS', 'Auton', 'Teleop', 'T&C', 'DQ', 'Played'])
        self.assertEqual(self.event.rankings[1], [1, '254', 20, 500, 500, 200, 0, 10])

    def test_rankings_wlt_update(self):
        self.rankings_auth.put()

        rankings = {
            'breakdowns': ['QS', 'Auton', 'Teleop', 'T&C', 'wins', 'losses', 'ties'],
            'rankings': [
                {'team_key': 'frc254', 'rank': 1, 'wins': 10, 'losses': 0, 'ties': 0, 'played': 10, 'dqs': 0, 'QS': 20, 'Auton': 500, 'Teleop': 500, 'T&C': 200},
                {'team_key': 'frc971', 'rank': 2, 'wins': 10, 'losses': 0, 'ties': 0, 'played': 10, 'dqs': 0, 'QS': 20, 'Auton': 500, 'Teleop': 500, 'T&C': 200}
            ],
        }
        request_body = json.dumps(rankings)

        request_path = '/api/trusted/v1/event/2014casj/rankings/update'
        sig = md5.new('{}{}{}'.format('321tEsTsEcReT', request_path, request_body)).hexdigest()
        response = self.testapp.post(request_path, request_body, headers={'X-TBA-Auth-Id': 'tEsT_id_2', 'X-TBA-Auth-Sig': sig}, expect_errors=True)

        self.assertEqual(response.status_code, 200)
        self.assertEqual(self.event.rankings[0], ['Rank', 'Team', 'QS', 'Auton', 'Teleop', 'T&C', 'Record (W-L-T)', 'DQ', 'Played'])
        self.assertEqual(self.event.rankings[1], [1, '254', 20, 500, 500, 200, '10-0-0', 0, 10])

    def test_eventteams_update(self):
        self.teams_auth.put()

        team_list = ['frc254', 'frc971', 'frc604']
        request_body = json.dumps(team_list)

        request_path = '/api/trusted/v1/event/2014casj/team_list/update'
        sig = md5.new('{}{}{}'.format('321tEsTsEcReT', request_path, request_body)).hexdigest()
        response = self.testapp.post(request_path, request_body, headers={'X-TBA-Auth-Id': 'tEsT_id_0', 'X-TBA-Auth-Sig': sig}, expect_errors=True)

        self.assertEqual(response.status_code, 200)

        db_eventteams = EventTeam.query(EventTeam.event == self.event.key).fetch(None)
        self.assertEqual(len(db_eventteams), 3)
        self.assertTrue('2014casj_frc254' in [et.key.id() for et in db_eventteams])
        self.assertTrue('2014casj_frc971' in [et.key.id() for et in db_eventteams])
        self.assertTrue('2014casj_frc604' in [et.key.id() for et in db_eventteams])

        team_list = ['frc254', 'frc100']
        request_body = json.dumps(team_list)

        sig = md5.new('{}{}{}'.format('321tEsTsEcReT', request_path, request_body)).hexdigest()
        response = self.testapp.post(request_path, request_body, headers={'X-TBA-Auth-Id': 'tEsT_id_0', 'X-TBA-Auth-Sig': sig}, expect_errors=True)

        self.assertEqual(response.status_code, 200)

        db_eventteams = EventTeam.query(EventTeam.event == self.event.key).fetch(None)
        self.assertEqual(len(db_eventteams), 2)
        self.assertTrue('2014casj_frc254' in [et.key.id() for et in db_eventteams])
        self.assertTrue('2014casj_frc100' in [et.key.id() for et in db_eventteams])

    def test_match_videos_add(self):
        self.video_auth.put()

        match1 = Match(
            id="2014casj_qm1",
            alliances_json="""{"blue": {"score": -1, "teams": ["frc3464", "frc20", "frc1073"]}, "red": {"score": -1, "teams": ["frc69", "frc571", "frc176"]}}""",
            comp_level="qm",
            event=ndb.Key(Event, '2014casj'),
            year=2014,
            set_number=1,
            match_number=1,
            team_key_names=[u'frc69', u'frc571', u'frc176', u'frc3464', u'frc20', u'frc1073'],
            youtube_videos=["abcdef"]
        )
        match1.put()

        match2 = Match(
            id="2014casj_sf1m1",
            alliances_json="""{"blue": {"score": -1, "teams": ["frc3464", "frc20", "frc1073"]}, "red": {"score": -1, "teams": ["frc69", "frc571", "frc176"]}}""",
            comp_level="sf",
            event=ndb.Key(Event, '2014casj'),
            year=2014,
            set_number=1,
            match_number=1,
            team_key_names=[u'frc69', u'frc571', u'frc176', u'frc3464', u'frc20', u'frc1073'],
        )
        match2.put()

        match_videos = {'qm1': 'aFZy8iibMD0', 'sf1m1': 'RpSgUrsghv4'}

        request_body = json.dumps(match_videos)

        request_path = '/api/trusted/v1/event/2014casj/match_videos/add'
        sig = md5.new('{}{}{}'.format('321tEsTsEcReT', request_path, request_body)).hexdigest()
        response = self.testapp.post(request_path, request_body, headers={'X-TBA-Auth-Id': 'tEsT_id_5', 'X-TBA-Auth-Sig': sig}, expect_errors=True)

        self.assertEqual(response.status_code, 200)

        self.assertEqual(set(Match.get_by_id('2014casj_qm1').youtube_videos), {'abcdef', 'aFZy8iibMD0'})
        self.assertEqual(set(Match.get_by_id('2014casj_sf1m1').youtube_videos), {'RpSgUrsghv4'})
Beispiel #11
0
    def post(self, auth_id):
        self._require_admin()

        auth = ApiAuthAccess.get_by_id(auth_id)

        auth_types_enum = [
            AuthType.READ_API
        ] if auth and AuthType.READ_API in auth.auth_types_enum else []
        if self.request.get('allow_edit_teams'):
            auth_types_enum.append(AuthType.EVENT_TEAMS)
        if self.request.get('allow_edit_matches'):
            auth_types_enum.append(AuthType.EVENT_MATCHES)
        if self.request.get('allow_edit_rankings'):
            auth_types_enum.append(AuthType.EVENT_RANKINGS)
        if self.request.get('allow_edit_alliances'):
            auth_types_enum.append(AuthType.EVENT_ALLIANCES)
        if self.request.get('allow_edit_awards'):
            auth_types_enum.append(AuthType.EVENT_AWARDS)
        if self.request.get('allow_edit_match_video'):
            auth_types_enum.append(AuthType.MATCH_VIDEO)
        if self.request.get('allow_edit_info'):
            auth_types_enum.append(AuthType.EVENT_INFO)
        if self.request.get('allow_edit_zebra_motionworks'):
            auth_types_enum.append(AuthType.ZEBRA_MOTIONWORKS)

        if self.request.get('owner', None):
            owner = Account.query(
                Account.email == self.request.get('owner')).fetch()
            owner_key = owner[0].key if owner else None
        else:
            owner_key = None

        if self.request.get('expiration', None):
            expiration = datetime.strptime(self.request.get('expiration'),
                                           '%Y-%m-%d')
        else:
            expiration = None

        if self.request.get('event_list_str'):
            split_events = self.request.get('event_list_str', '').split(',')
            event_list = [
                ndb.Key(Event, event_key.strip()) for event_key in split_events
            ]
        else:
            event_list = []

        if not auth:
            auth = ApiAuthAccess(
                id=auth_id,
                description=self.request.get('description'),
                owner=owner_key,
                expiration=expiration,
                allow_admin=True if self.request.get('allow_admin') else False,
                secret=''.join(
                    random.choice(string.ascii_lowercase +
                                  string.ascii_uppercase + string.digits)
                    for _ in range(64)),
                event_list=event_list,
                auth_types_enum=auth_types_enum,
            )
        else:
            auth.description = self.request.get('description')
            auth.event_list = event_list
            auth.auth_types_enum = auth_types_enum
            auth.owner = owner_key
            auth.expiration = expiration
            auth.allow_admin = True if self.request.get(
                'allow_admin') else False

        auth.put()

        self.redirect("/admin/api_auth/manage")
Beispiel #12
0
class TestApiTrustedController(unittest2.TestCase):
    def setUp(self):
        self.testapp = webtest.TestApp(api_main.app)

        self.testbed = testbed.Testbed()
        self.testbed.activate()
        self.testbed.init_datastore_v3_stub()
        self.testbed.init_urlfetch_stub()
        self.testbed.init_memcache_stub()
        self.testbed.init_taskqueue_stub(root_path=".")

        self.aaa = ApiAuthAccess(id='tEsT_id_1',
                                 secret='321tEsTsEcReT',
                                 description='test',
                                 event_list=[ndb.Key(Event, '2014casj')],
                                 auth_types_enum=[AuthType.EVENT_DATA])

        self.aaa2 = ApiAuthAccess(id='tEsT_id_2',
                                  secret='321tEsTsEcReT',
                                  description='test',
                                  event_list=[ndb.Key(Event, '2014casj')],
                                  auth_types_enum=[AuthType.MATCH_VIDEO])

        self.event = Event(
            id='2014casj',
            event_type_enum=EventType.REGIONAL,
            event_short='casj',
            year=2014,
        )
        self.event.put()

    def tearDown(self):
        self.testbed.deactivate()

    def test_auth(self):
        request_path = '/api/trusted/v1/event/2014casj/matches/update'

        # Fail
        response = self.testapp.post(request_path, expect_errors=True)
        self.assertEqual(response.status_code, 400)
        self.assertTrue('Error' in response.json)

        # Fail
        request_body = json.dumps([])
        sig = md5.new('{}{}{}'.format('321tEsTsEcReT', request_path,
                                      request_body)).hexdigest()
        response = self.testapp.post(request_path,
                                     request_body,
                                     headers={
                                         'X-TBA-Auth-Id': 'tEsT_id_1',
                                         'X-TBA-Auth-Sig': sig
                                     },
                                     expect_errors=True)
        self.assertEqual(response.status_code, 400)
        self.assertTrue('Error' in response.json)

        self.aaa.put()
        self.aaa2.put()

        # Pass
        sig = md5.new('{}{}{}'.format('321tEsTsEcReT', request_path,
                                      request_body)).hexdigest()
        response = self.testapp.post(request_path,
                                     request_body,
                                     headers={
                                         'X-TBA-Auth-Id': 'tEsT_id_1',
                                         'X-TBA-Auth-Sig': sig
                                     },
                                     expect_errors=True)
        self.assertEqual(response.status_code, 200)

        # Fail; bad X-TBA-Auth-Id
        sig = md5.new('{}{}{}'.format('321tEsTsEcReT', request_path,
                                      request_body)).hexdigest()
        response = self.testapp.post(request_path,
                                     request_body,
                                     headers={
                                         'X-TBA-Auth-Id': 'badTestAuthId',
                                         'X-TBA-Auth-Sig': sig
                                     },
                                     expect_errors=True)
        self.assertEqual(response.status_code, 400)
        self.assertTrue('Error' in response.json)

        # Fail; bad sig
        response = self.testapp.post(request_path,
                                     request_body,
                                     headers={
                                         'X-TBA-Auth-Id': 'tEsT_id_1',
                                         'X-TBA-Auth-Sig': '123abc'
                                     },
                                     expect_errors=True)
        self.assertEqual(response.status_code, 400)
        self.assertTrue('Error' in response.json)

        # Fail; bad sig due to wrong body
        body2 = json.dumps([{}])
        sig = md5.new('{}{}{}'.format('321tEsTsEcReT', request_path,
                                      request_body)).hexdigest()
        response = self.testapp.post(request_path,
                                     body2,
                                     headers={
                                         'X-TBA-Auth-Id': 'tEsT_id_1',
                                         'X-TBA-Auth-Sig': sig
                                     },
                                     expect_errors=True)
        self.assertEqual(response.status_code, 400)
        self.assertTrue('Error' in response.json)

        # Fail; bad event
        request_path2 = '/api/trusted/v1/event/2014cama/matches/update'
        sig = md5.new('{}{}{}'.format('321tEsTsEcReT', request_path2,
                                      request_body)).hexdigest()
        response = self.testapp.post(request_path,
                                     request_body,
                                     headers={
                                         'X-TBA-Auth-Id': 'tEsT_id_1',
                                         'X-TBA-Auth-Sig': sig
                                     },
                                     expect_errors=True)
        self.assertEqual(response.status_code, 400)
        self.assertTrue('Error' in response.json)

        # Fail; insufficient auth_types_enum
        sig = md5.new('{}{}{}'.format('321tEsTsEcReT', request_path,
                                      request_body)).hexdigest()
        response = self.testapp.post(request_path,
                                     request_body,
                                     headers={
                                         'X-TBA-Auth-Id': 'tEsT_id_2',
                                         'X-TBA-Auth-Sig': sig
                                     },
                                     expect_errors=True)
        self.assertEqual(response.status_code, 400)

    def test_alliance_selections_update(self):
        self.aaa.put()

        alliances = [['frc971', 'frc254', 'frc1662'],
                     ['frc1678', 'frc368', 'frc4171'],
                     ['frc2035', 'frc192', 'frc4990'],
                     ['frc1323', 'frc846', 'frc2135'],
                     ['frc2144', 'frc1388', 'frc668'],
                     ['frc1280', 'frc604', 'frc100'],
                     ['frc114', 'frc852', 'frc841'],
                     ['frc2473', 'frc3256', 'frc1868']]
        request_body = json.dumps(alliances)

        request_path = '/api/trusted/v1/event/2014casj/alliance_selections/update'
        sig = md5.new('{}{}{}'.format('321tEsTsEcReT', request_path,
                                      request_body)).hexdigest()
        response = self.testapp.post(request_path,
                                     request_body,
                                     headers={
                                         'X-TBA-Auth-Id': 'tEsT_id_1',
                                         'X-TBA-Auth-Sig': sig
                                     },
                                     expect_errors=True)

        self.assertEqual(response.status_code, 200)

        for i, selection in enumerate(self.event.alliance_selections):
            self.assertEqual(alliances[i], selection['picks'])

    def test_awards_update(self):
        self.aaa.put()

        awards = [{
            'name_str': 'Winner',
            'team_key': 'frc254'
        }, {
            'name_str': 'Winner',
            'team_key': 'frc604'
        }, {
            'name_str': 'Volunteer Blahblah',
            'team_key': 'frc1',
            'awardee': 'Bob Bobby'
        }]
        request_body = json.dumps(awards)

        request_path = '/api/trusted/v1/event/2014casj/awards/update'
        sig = md5.new('{}{}{}'.format('321tEsTsEcReT', request_path,
                                      request_body)).hexdigest()
        response = self.testapp.post(request_path,
                                     request_body,
                                     headers={
                                         'X-TBA-Auth-Id': 'tEsT_id_1',
                                         'X-TBA-Auth-Sig': sig
                                     },
                                     expect_errors=True)

        self.assertEqual(response.status_code, 200)

        db_awards = Award.query(Award.event == self.event.key).fetch(None)
        self.assertEqual(len(db_awards), 2)
        self.assertTrue('2014casj_1' in [a.key.id() for a in db_awards])
        self.assertTrue('2014casj_5' in [a.key.id() for a in db_awards])

        awards = [{
            'name_str': 'Winner',
            'team_key': 'frc254'
        }, {
            'name_str': 'Winner',
            'team_key': 'frc604'
        }]
        request_body = json.dumps(awards)

        sig = md5.new('{}{}{}'.format('321tEsTsEcReT', request_path,
                                      request_body)).hexdigest()
        response = self.testapp.post(request_path,
                                     request_body,
                                     headers={
                                         'X-TBA-Auth-Id': 'tEsT_id_1',
                                         'X-TBA-Auth-Sig': sig
                                     },
                                     expect_errors=True)

        self.assertEqual(response.status_code, 200)

        db_awards = Award.query(Award.event == self.event.key).fetch(None)
        self.assertEqual(len(db_awards), 1)
        self.assertTrue('2014casj_1' in [a.key.id() for a in db_awards])

    def test_matches_update(self):
        self.aaa.put()

        update_request_path = '/api/trusted/v1/event/2014casj/matches/update'
        delete_request_path = '/api/trusted/v1/event/2014casj/matches/delete'

        # add one match
        matches = [{
            'comp_level': 'qm',
            'set_number': 1,
            'match_number': 1,
            'alliances': {
                'red': {
                    'teams': ['frc1', 'frc2', 'frc3'],
                    'score': 25
                },
                'blue': {
                    'teams': ['frc4', 'frc5', 'frc6'],
                    'score': 26
                },
            },
            'time_string': '9:00 AM',
            'time_utc': '2014-08-31T16:00:00',
        }]
        request_body = json.dumps(matches)
        sig = md5.new('{}{}{}'.format('321tEsTsEcReT', update_request_path,
                                      request_body)).hexdigest()
        response = self.testapp.post(update_request_path,
                                     request_body,
                                     headers={
                                         'X-TBA-Auth-Id': 'tEsT_id_1',
                                         'X-TBA-Auth-Sig': sig
                                     },
                                     expect_errors=True)

        self.assertEqual(response.status_code, 200)

        db_matches = Match.query(Match.event == self.event.key).fetch(None)
        self.assertEqual(len(db_matches), 1)
        self.assertTrue('2014casj_qm1' in [m.key.id() for m in db_matches])

        # add another match
        matches = [{
            'comp_level': 'f',
            'set_number': 1,
            'match_number': 1,
            'alliances': {
                'red': {
                    'teams': ['frc1', 'frc2', 'frc3'],
                    'score': 250
                },
                'blue': {
                    'teams': ['frc4', 'frc5', 'frc6'],
                    'score': 260
                },
            },
            'time_string': '10:00 AM',
            'time_utc': '2014-08-31T17:00:00',
        }]
        request_body = json.dumps(matches)
        sig = md5.new('{}{}{}'.format('321tEsTsEcReT', update_request_path,
                                      request_body)).hexdigest()
        response = self.testapp.post(update_request_path,
                                     request_body,
                                     headers={
                                         'X-TBA-Auth-Id': 'tEsT_id_1',
                                         'X-TBA-Auth-Sig': sig
                                     },
                                     expect_errors=True)
        self.assertEqual(response.status_code, 200)

        db_matches = Match.query(Match.event == self.event.key).fetch(None)
        self.assertEqual(len(db_matches), 2)
        self.assertTrue('2014casj_qm1' in [m.key.id() for m in db_matches])
        self.assertTrue('2014casj_f1m1' in [m.key.id() for m in db_matches])

        # add a match and delete a match
        matches = [{
            'comp_level': 'f',
            'set_number': 1,
            'match_number': 2,
            'alliances': {
                'red': {
                    'teams': ['frc1', 'frc2', 'frc3'],
                    'score': 250
                },
                'blue': {
                    'teams': ['frc4', 'frc5', 'frc6'],
                    'score': 260
                },
            },
            'score_breakdown': {
                'red': {
                    'auto': 20,
                    'assist': 40,
                    'truss+catch': 20,
                    'teleop_goal+foul': 20
                },
                'blue': {
                    'auto': 40,
                    'assist': 60,
                    'truss+catch': 10,
                    'teleop_goal+foul': 40
                },
            },
            'time_string': '11:00 AM',
            'time_utc': '2014-08-31T18:00:00',
        }]
        request_body = json.dumps(matches)
        sig = md5.new('{}{}{}'.format('321tEsTsEcReT', update_request_path,
                                      request_body)).hexdigest()
        response = self.testapp.post(update_request_path,
                                     request_body,
                                     headers={
                                         'X-TBA-Auth-Id': 'tEsT_id_1',
                                         'X-TBA-Auth-Sig': sig
                                     },
                                     expect_errors=True)
        self.assertEqual(response.status_code, 200)

        keys_to_delete = ['qm1']
        request_body = json.dumps(keys_to_delete)
        sig = md5.new('{}{}{}'.format('321tEsTsEcReT', delete_request_path,
                                      request_body)).hexdigest()
        response = self.testapp.post(delete_request_path,
                                     request_body,
                                     headers={
                                         'X-TBA-Auth-Id': 'tEsT_id_1',
                                         'X-TBA-Auth-Sig': sig
                                     },
                                     expect_errors=True)
        self.assertEqual(response.status_code, 200)
        self.assertEqual(response.json['keys_deleted'], ['qm1'])

        db_matches = Match.query(Match.event == self.event.key).fetch(None)
        self.assertEqual(len(db_matches), 2)
        self.assertTrue('2014casj_f1m1' in [m.key.id() for m in db_matches])
        self.assertTrue('2014casj_f1m2' in [m.key.id() for m in db_matches])

        db_matches = Match.query(Match.event == self.event.key).fetch(None)
        self.assertEqual(len(db_matches), 2)
        self.assertTrue('2014casj_f1m1' in [m.key.id() for m in db_matches])
        self.assertTrue('2014casj_f1m2' in [m.key.id() for m in db_matches])

        # verify match data
        match = Match.get_by_id('2014casj_f1m2')
        self.assertEqual(match.time, datetime.datetime(2014, 8, 31, 18, 0))
        self.assertEqual(match.time_string, '11:00 AM')
        self.assertEqual(match.alliances['red']['score'], 250)
        self.assertEqual(match.score_breakdown['red']['truss+catch'], 20)

    def test_rankings_update(self):
        self.aaa.put()

        rankings = {
            'breakdowns': ['QS', 'Auton', 'Teleop', 'T&C'],
            'rankings': [{
                'team_key': 'frc254',
                'rank': 1,
                'wins': 10,
                'losses': 0,
                'ties': 0,
                'played': 10,
                'dqs': 0,
                'QS': 20,
                'Auton': 500,
                'Teleop': 500,
                'T&C': 200
            }, {
                'team_key': 'frc971',
                'rank': 2,
                'wins': 10,
                'losses': 0,
                'ties': 0,
                'played': 10,
                'dqs': 0,
                'QS': 20,
                'Auton': 500,
                'Teleop': 500,
                'T&C': 200
            }],
        }
        request_body = json.dumps(rankings)

        request_path = '/api/trusted/v1/event/2014casj/rankings/update'
        sig = md5.new('{}{}{}'.format('321tEsTsEcReT', request_path,
                                      request_body)).hexdigest()
        response = self.testapp.post(request_path,
                                     request_body,
                                     headers={
                                         'X-TBA-Auth-Id': 'tEsT_id_1',
                                         'X-TBA-Auth-Sig': sig
                                     },
                                     expect_errors=True)

        self.assertEqual(response.status_code, 200)
        self.assertEqual(self.event.rankings[0], [
            'Rank', 'Team', 'QS', 'Auton', 'Teleop', 'T&C', 'Record (W-L-T)',
            'DQ', 'Played'
        ])
        self.assertEqual(self.event.rankings[1],
                         [1, '254', 20, 500, 500, 200, '10-0-0', 0, 10])

    def test_eventteams_update(self):
        self.aaa.put()

        team_list = ['frc254', 'frc971', 'frc604']
        request_body = json.dumps(team_list)

        request_path = '/api/trusted/v1/event/2014casj/team_list/update'
        sig = md5.new('{}{}{}'.format('321tEsTsEcReT', request_path,
                                      request_body)).hexdigest()
        response = self.testapp.post(request_path,
                                     request_body,
                                     headers={
                                         'X-TBA-Auth-Id': 'tEsT_id_1',
                                         'X-TBA-Auth-Sig': sig
                                     },
                                     expect_errors=True)

        self.assertEqual(response.status_code, 200)

        db_eventteams = EventTeam.query(
            EventTeam.event == self.event.key).fetch(None)
        self.assertEqual(len(db_eventteams), 3)
        self.assertTrue(
            '2014casj_frc254' in [et.key.id() for et in db_eventteams])
        self.assertTrue(
            '2014casj_frc971' in [et.key.id() for et in db_eventteams])
        self.assertTrue(
            '2014casj_frc604' in [et.key.id() for et in db_eventteams])

        team_list = ['frc254', 'frc100']
        request_body = json.dumps(team_list)

        sig = md5.new('{}{}{}'.format('321tEsTsEcReT', request_path,
                                      request_body)).hexdigest()
        response = self.testapp.post(request_path,
                                     request_body,
                                     headers={
                                         'X-TBA-Auth-Id': 'tEsT_id_1',
                                         'X-TBA-Auth-Sig': sig
                                     },
                                     expect_errors=True)

        self.assertEqual(response.status_code, 200)

        db_eventteams = EventTeam.query(
            EventTeam.event == self.event.key).fetch(None)
        self.assertEqual(len(db_eventteams), 2)
        self.assertTrue(
            '2014casj_frc254' in [et.key.id() for et in db_eventteams])
        self.assertTrue(
            '2014casj_frc100' in [et.key.id() for et in db_eventteams])

    def test_match_videos_add(self):
        self.aaa2.put()

        match1 = Match(
            id="2014casj_qm1",
            alliances_json=
            """{"blue": {"score": -1, "teams": ["frc3464", "frc20", "frc1073"]}, "red": {"score": -1, "teams": ["frc69", "frc571", "frc176"]}}""",
            comp_level="qm",
            event=ndb.Key(Event, '2014casj'),
            game="frc_unknown",
            set_number=1,
            match_number=1,
            team_key_names=[
                u'frc69', u'frc571', u'frc176', u'frc3464', u'frc20',
                u'frc1073'
            ],
            youtube_videos=["abcdef"])
        match1.put()

        match2 = Match(
            id="2014casj_sf1m1",
            alliances_json=
            """{"blue": {"score": -1, "teams": ["frc3464", "frc20", "frc1073"]}, "red": {"score": -1, "teams": ["frc69", "frc571", "frc176"]}}""",
            comp_level="sf",
            event=ndb.Key(Event, '2014casj'),
            game="frc_unknown",
            set_number=1,
            match_number=1,
            team_key_names=[
                u'frc69', u'frc571', u'frc176', u'frc3464', u'frc20',
                u'frc1073'
            ],
        )
        match2.put()

        match_videos = {'qm1': 'aFZy8iibMD0', 'sf1m1': 'RpSgUrsghv4'}

        request_body = json.dumps(match_videos)

        request_path = '/api/trusted/v1/event/2014casj/match_videos/add'
        sig = md5.new('{}{}{}'.format('321tEsTsEcReT', request_path,
                                      request_body)).hexdigest()
        response = self.testapp.post(request_path,
                                     request_body,
                                     headers={
                                         'X-TBA-Auth-Id': 'tEsT_id_2',
                                         'X-TBA-Auth-Sig': sig
                                     },
                                     expect_errors=True)

        self.assertEqual(response.status_code, 200)

        self.assertEqual(set(Match.get_by_id('2014casj_qm1').youtube_videos),
                         {'abcdef', 'aFZy8iibMD0'})
        self.assertEqual(set(Match.get_by_id('2014casj_sf1m1').youtube_videos),
                         {'RpSgUrsghv4'})
class TestApiTrustedController(unittest2.TestCase):

    def setUp(self):
        self.testapp = webtest.TestApp(api_main.app)

        self.testbed = testbed.Testbed()
        self.testbed.activate()
        self.testbed.init_datastore_v3_stub()
        self.testbed.init_urlfetch_stub()
        self.testbed.init_memcache_stub()
        self.testbed.init_user_stub()
        ndb.get_context().clear_cache()  # Prevent data from leaking between tests

        self.testbed.init_taskqueue_stub(root_path=".")

        self.teams_auth = ApiAuthAccess(id='tEsT_id_0',
                                        secret='321tEsTsEcReT',
                                        description='test',
                                        event_list=[ndb.Key(Event, '2014casj')],
                                        auth_types_enum=[AuthType.EVENT_TEAMS])

        self.matches_auth = ApiAuthAccess(id='tEsT_id_1',
                                          secret='321tEsTsEcReT',
                                          description='test',
                                          event_list=[ndb.Key(Event, '2014casj')],
                                          auth_types_enum=[AuthType.EVENT_MATCHES])

        self.rankings_auth = ApiAuthAccess(id='tEsT_id_2',
                                           secret='321tEsTsEcReT',
                                           description='test',
                                           event_list=[ndb.Key(Event, '2014casj')],
                                           auth_types_enum=[AuthType.EVENT_RANKINGS])

        self.alliances_auth = ApiAuthAccess(id='tEsT_id_3',
                                            secret='321tEsTsEcReT',
                                            description='test',
                                            event_list=[ndb.Key(Event, '2014casj')],
                                            auth_types_enum=[AuthType.EVENT_ALLIANCES])

        self.awards_auth = ApiAuthAccess(id='tEsT_id_4',
                                         secret='321tEsTsEcReT',
                                         description='test',
                                         event_list=[ndb.Key(Event, '2014casj')],
                                         auth_types_enum=[AuthType.EVENT_AWARDS])

        self.video_auth = ApiAuthAccess(id='tEsT_id_5',
                                        secret='321tEsTsEcReT',
                                        description='test',
                                        event_list=[ndb.Key(Event, '2014casj')],
                                        auth_types_enum=[AuthType.MATCH_VIDEO])

        self.expired_auth = ApiAuthAccess(id='tEsT_id_6',
                                        secret='321tEsTsEcReT',
                                        description='test',
                                        event_list=[ndb.Key(Event, '2014casj')],
                                        auth_types_enum=[AuthType.EVENT_MATCHES],
                                        expiration=datetime.datetime(year=1970, month=1, day=1))

        self.owned_auth = ApiAuthAccess(id='tEsT_id_7',
                                        secret='321tEsTsEcReT',
                                        description='test',
                                        event_list=[ndb.Key(Event, '2014casj')],
                                        auth_types_enum=[AuthType.EVENT_MATCHES],
                                        owner=ndb.Key(Account, "42"))

        self.owned_auth_expired = ApiAuthAccess(id='tEsT_id_8',
                                        secret='321tEsTsEcReT',
                                        description='test',
                                        event_list=[ndb.Key(Event, '2014casj')],
                                        auth_types_enum=[AuthType.EVENT_MATCHES],
                                        owner=ndb.Key(Account, "42"),
                                        expiration=datetime.datetime(year=1970, month=1, day=1))

        self.event = Event(
            id='2014casj',
            event_type_enum=EventType.REGIONAL,
            event_short='casj',
            year=2014,
        )
        self.event.put()

    def tearDown(self):
        self.testbed.deactivate()

    def loginUser(self, is_admin=False):
        self.testbed.setup_env(
            user_email="*****@*****.**",
            user_id="42",
            user_is_admin='1' if is_admin else '0',
            overwrite=True)

    def test_auth(self):
        request_path = '/api/trusted/v1/event/2014casj/matches/update'
        request_path_caps_key = '/api/trusted/v1/event/2014CASJ/matches/update'

        # Fail
        response = self.testapp.post(request_path, expect_errors=True)
        self.assertEqual(response.status_code, 400)
        self.assertTrue('Error' in response.json)

        # Fail
        request_body = json.dumps([])
        sig = md5.new('{}{}{}'.format('321tEsTsEcReT', request_path, request_body)).hexdigest()
        response = self.testapp.post(request_path, request_body, headers={'X-TBA-Auth-Id': 'tEsT_id_1', 'X-TBA-Auth-Sig': sig}, expect_errors=True)
        self.assertEqual(response.status_code, 401)
        self.assertTrue('Error' in response.json)

        self.rankings_auth.put()
        self.matches_auth.put()

        # Pass
        sig = md5.new('{}{}{}'.format('321tEsTsEcReT', request_path, request_body)).hexdigest()
        response = self.testapp.post(request_path, request_body, headers={'X-TBA-Auth-Id': 'tEsT_id_1', 'X-TBA-Auth-Sig': sig}, expect_errors=True)
        self.assertEqual(response.status_code, 200)

        # Pass; all caps key
        sig = md5.new('{}{}{}'.format('321tEsTsEcReT', request_path_caps_key, request_body)).hexdigest()
        response = self.testapp.post(request_path_caps_key, request_body, headers={'X-TBA-Auth-Id': 'tEsT_id_1', 'X-TBA-Auth-Sig': sig}, expect_errors=True)
        self.assertEqual(response.status_code, 200)

        # Fail; bad X-TBA-Auth-Id
        sig = md5.new('{}{}{}'.format('321tEsTsEcReT', request_path, request_body)).hexdigest()
        response = self.testapp.post(request_path, request_body, headers={'X-TBA-Auth-Id': 'badTestAuthId', 'X-TBA-Auth-Sig': sig}, expect_errors=True)
        self.assertEqual(response.status_code, 401)
        self.assertTrue('Error' in response.json)

        # Fail; bad sig
        response = self.testapp.post(request_path, request_body, headers={'X-TBA-Auth-Id': 'tEsT_id_1', 'X-TBA-Auth-Sig': '123abc'}, expect_errors=True)
        self.assertEqual(response.status_code, 401)
        self.assertTrue('Error' in response.json)

        # Fail; bad sig due to wrong body
        body2 = json.dumps([{}])
        sig = md5.new('{}{}{}'.format('321tEsTsEcReT', request_path, request_body)).hexdigest()
        response = self.testapp.post(request_path, body2, headers={'X-TBA-Auth-Id': 'tEsT_id_1', 'X-TBA-Auth-Sig': sig}, expect_errors=True)
        self.assertEqual(response.status_code, 401)
        self.assertTrue('Error' in response.json)

        # Fail; bad event
        request_path2 = '/api/trusted/v1/event/2014cama/matches/update'
        sig = md5.new('{}{}{}'.format('321tEsTsEcReT', request_path2, request_body)).hexdigest()
        response = self.testapp.post(request_path, request_body, headers={'X-TBA-Auth-Id': 'tEsT_id_1', 'X-TBA-Auth-Sig': sig}, expect_errors=True)
        self.assertEqual(response.status_code, 401)
        self.assertTrue('Error' in response.json)

        # Fail; insufficient auth_types_enum
        sig = md5.new('{}{}{}'.format('321tEsTsEcReT', request_path, request_body)).hexdigest()
        response = self.testapp.post(request_path, request_body, headers={'X-TBA-Auth-Id': 'tEsT_id_2', 'X-TBA-Auth-Sig': sig}, expect_errors=True)
        self.assertEqual(response.status_code, 401)
        self.assertTrue('Error' in response.json)

        # Fail; expired keys
        sig = md5.new('{}{}{}'.format('321tEsTsEcReT', request_path, request_body)).hexdigest()
        response = self.testapp.post(request_path, request_body, headers={'X-TBA-Auth-Id': 'tEsT_id_6', 'X-TBA-Auth-Sig': sig}, expect_errors=True)
        self.assertEqual(response.status_code, 401)
        self.assertTrue('Error' in response.json)

    def test_admin_auth(self):
        # Ensure that a logged in admin user can access any evet
        request_path = '/api/trusted/v1/event/2014casj/matches/update'
        request_body = json.dumps([])
        self.loginUser(is_admin=True)

        response = self.testapp.post(request_path, request_body, expect_errors=True)
        self.assertEqual(response.status_code, 200)

    def test_user_auth(self):
        # Ensure that a logged in user can use auths granted to their account
        request_path = '/api/trusted/v1/event/2014casj/matches/update'
        request_body = json.dumps([])
        self.owned_auth.put()
        self.loginUser()

        response = self.testapp.post(request_path, request_body, expect_errors=True)
        self.assertEqual(response.status_code, 200)

    def test_user_expired_auth(self):
        # Ensure that a logged in user can use auths granted to their account
        request_path = '/api/trusted/v1/event/2014casj/matches/update'
        request_body = json.dumps([])
        self.owned_auth_expired.put()
        self.loginUser()

        # Should end up with a 400 error because the expired key didn't count and no explicit
        # Auth-Id header was passed
        response = self.testapp.post(request_path, request_body, expect_errors=True)
        self.assertEqual(response.status_code, 400)
        self.assertTrue('Error' in response.json)

    def test_killswitch(self):
        request_path = '/api/trusted/v1/event/2014casj/matches/update'
        request_body = json.dumps([])

        # Pass
        self.matches_auth.put()
        sig = md5.new('{}{}{}'.format('321tEsTsEcReT', request_path, request_body)).hexdigest()
        response = self.testapp.post(request_path, request_body, headers={'X-TBA-Auth-Id': 'tEsT_id_1', 'X-TBA-Auth-Sig': sig}, expect_errors=True)
        self.assertEqual(response.status_code, 200)

        # Now, set the disable sitevar
        trusted_sitevar = Sitevar(
            id='trustedapi',
            values_json=json.dumps({
                3: False,
            })
        )
        trusted_sitevar.put()

        # Fail
        sig = md5.new('{}{}{}'.format('321tEsTsEcReT', request_path, request_body)).hexdigest()
        response = self.testapp.post(request_path, request_body, headers={'X-TBA-Auth-Id': 'tEsT_id_1', 'X-TBA-Auth-Sig': sig}, expect_errors=True)
        self.assertEqual(response.status_code, 401)

    def test_alliance_selections_update(self):
        self.alliances_auth.put()

        alliances = [['frc971', 'frc254', 'frc1662'],
                     ['frc1678', 'frc368', 'frc4171'],
                     ['frc2035', 'frc192', 'frc4990'],
                     ['frc1323', 'frc846', 'frc2135'],
                     ['frc2144', 'frc1388', 'frc668'],
                     ['frc1280', 'frc604', 'frc100'],
                     ['frc114', 'frc852', 'frc841'],
                     ['frc2473', 'frc3256', 'frc1868']]
        request_body = json.dumps(alliances)

        request_path = '/api/trusted/v1/event/2014casj/alliance_selections/update'
        sig = md5.new('{}{}{}'.format('321tEsTsEcReT', request_path, request_body)).hexdigest()
        response = self.testapp.post(request_path, request_body, headers={'X-TBA-Auth-Id': 'tEsT_id_3', 'X-TBA-Auth-Sig': sig}, expect_errors=True)

        self.assertEqual(response.status_code, 200)

        self.assertEqual(len(self.event.alliance_selections), 8)
        for i, selection in enumerate(self.event.alliance_selections):
            self.assertEqual(alliances[i], selection['picks'])

    def test_empty_alliance_selections_update(self):
        self.alliances_auth.put()

        alliances = [['frc971', 'frc254', 'frc1662'],
                     ['frc1678', 'frc368', 'frc4171'],
                     ['frc2035', 'frc192', 'frc4990'],
                     ['frc1323', 'frc846', 'frc2135'],
                     [],[],[],[]]
        request_body = json.dumps(alliances)

        request_path = '/api/trusted/v1/event/2014casj/alliance_selections/update'
        sig = md5.new('{}{}{}'.format('321tEsTsEcReT', request_path, request_body)).hexdigest()
        response = self.testapp.post(request_path, request_body, headers={'X-TBA-Auth-Id': 'tEsT_id_3', 'X-TBA-Auth-Sig': sig}, expect_errors=True)

        self.assertEqual(response.status_code, 200)

        self.assertEqual(len(self.event.alliance_selections), 4)
        for i, selection in enumerate(self.event.alliance_selections):
            self.assertEqual(alliances[i], selection['picks'])

    def test_awards_update(self):
        self.awards_auth.put()

        awards = [{'name_str': 'Winner', 'team_key': 'frc254'},
                  {'name_str': 'Winner', 'team_key': 'frc604'},
                  {'name_str': 'Volunteer Blahblah', 'team_key': 'frc1', 'awardee': 'Bob Bobby'}]
        request_body = json.dumps(awards)

        request_path = '/api/trusted/v1/event/2014casj/awards/update'
        sig = md5.new('{}{}{}'.format('321tEsTsEcReT', request_path, request_body)).hexdigest()
        response = self.testapp.post(request_path, request_body, headers={'X-TBA-Auth-Id': 'tEsT_id_4', 'X-TBA-Auth-Sig': sig}, expect_errors=True)

        self.assertEqual(response.status_code, 200)

        db_awards = Award.query(Award.event == self.event.key).fetch(None)
        self.assertEqual(len(db_awards), 2)
        self.assertTrue('2014casj_1' in [a.key.id() for a in db_awards])
        self.assertTrue('2014casj_5' in [a.key.id() for a in db_awards])

        awards = [{'name_str': 'Winner', 'team_key': 'frc254'},
                  {'name_str': 'Winner', 'team_key': 'frc604'}]
        request_body = json.dumps(awards)

        sig = md5.new('{}{}{}'.format('321tEsTsEcReT', request_path, request_body)).hexdigest()
        response = self.testapp.post(request_path, request_body, headers={'X-TBA-Auth-Id': 'tEsT_id_4', 'X-TBA-Auth-Sig': sig}, expect_errors=True)

        self.assertEqual(response.status_code, 200)

        db_awards = Award.query(Award.event == self.event.key).fetch(None)
        self.assertEqual(len(db_awards), 1)
        self.assertTrue('2014casj_1' in [a.key.id() for a in db_awards])

    def test_matches_update(self):
        self.matches_auth.put()

        update_request_path = '/api/trusted/v1/event/2014casj/matches/update'
        delete_request_path = '/api/trusted/v1/event/2014casj/matches/delete'
        delete_all_request_path = '/api/trusted/v1/event/2014casj/matches/delete_all'

        # add one match
        matches = [{
            'comp_level': 'qm',
            'set_number': 1,
            'match_number': 1,
            'alliances': {
                'red': {'teams': ['frc1', 'frc2', 'frc3'],
                        'score': 25},
                'blue': {'teams': ['frc4', 'frc5', 'frc6'],
                         'score': 26},
            },
            'time_string': '9:00 AM',
            'time_utc': '2014-08-31T16:00:00',
        }]
        request_body = json.dumps(matches)
        sig = md5.new('{}{}{}'.format('321tEsTsEcReT', update_request_path, request_body)).hexdigest()
        response = self.testapp.post(update_request_path, request_body, headers={'X-TBA-Auth-Id': 'tEsT_id_1', 'X-TBA-Auth-Sig': sig}, expect_errors=True)

        self.assertEqual(response.status_code, 200)

        db_matches = Match.query(Match.event == self.event.key).fetch(None)
        self.assertEqual(len(db_matches), 1)
        self.assertTrue('2014casj_qm1' in [m.key.id() for m in db_matches])

        # add another match
        matches = [{
            'comp_level': 'f',
            'set_number': 1,
            'match_number': 1,
            'alliances': {
                'red': {'teams': ['frc1', 'frc2', 'frc3'],
                        'score': 250},
                'blue': {'teams': ['frc4', 'frc5', 'frc6'],
                         'score': 260},
            },
            'time_string': '10:00 AM',
            'time_utc': '2014-08-31T17:00:00',
        }]
        request_body = json.dumps(matches)
        sig = md5.new('{}{}{}'.format('321tEsTsEcReT', update_request_path, request_body)).hexdigest()
        response = self.testapp.post(update_request_path, request_body, headers={'X-TBA-Auth-Id': 'tEsT_id_1', 'X-TBA-Auth-Sig': sig}, expect_errors=True)
        self.assertEqual(response.status_code, 200)

        db_matches = Match.query(Match.event == self.event.key).fetch(None)
        self.assertEqual(len(db_matches), 2)
        self.assertTrue('2014casj_qm1' in [m.key.id() for m in db_matches])
        self.assertTrue('2014casj_f1m1' in [m.key.id() for m in db_matches])

        # add a match and delete a match
        matches = [{
            'comp_level': 'f',
            'set_number': 1,
            'match_number': 2,
            'alliances': {
                'red': {'teams': ['frc1', 'frc2', 'frc3'],
                        'score': 250},
                'blue': {'teams': ['frc4', 'frc5', 'frc6'],
                         'score': 260},
            },
            'score_breakdown': {
                'red': {'auto': 20, 'assist': 40, 'truss+catch': 20, 'teleop_goal+foul': 20},
                'blue': {'auto': 40, 'assist': 60, 'truss+catch': 10, 'teleop_goal+foul': 40},
            },
            'time_string': '11:00 AM',
            'time_utc': '2014-08-31T18:00:00',
        }]
        request_body = json.dumps(matches)
        sig = md5.new('{}{}{}'.format('321tEsTsEcReT', update_request_path, request_body)).hexdigest()
        response = self.testapp.post(update_request_path, request_body, headers={'X-TBA-Auth-Id': 'tEsT_id_1', 'X-TBA-Auth-Sig': sig}, expect_errors=True)
        self.assertEqual(response.status_code, 200)

        keys_to_delete = ['qm1']
        request_body = json.dumps(keys_to_delete)
        sig = md5.new('{}{}{}'.format('321tEsTsEcReT', delete_request_path, request_body)).hexdigest()
        response = self.testapp.post(delete_request_path, request_body, headers={'X-TBA-Auth-Id': 'tEsT_id_1', 'X-TBA-Auth-Sig': sig}, expect_errors=True)
        self.assertEqual(response.status_code, 200)
        self.assertEqual(response.json['keys_deleted'], ['qm1'])

        db_matches = Match.query(Match.event == self.event.key).fetch(None)
        self.assertEqual(len(db_matches), 2)
        self.assertTrue('2014casj_f1m1' in [m.key.id() for m in db_matches])
        self.assertTrue('2014casj_f1m2' in [m.key.id() for m in db_matches])

        db_matches = Match.query(Match.event == self.event.key).fetch(None)
        self.assertEqual(len(db_matches), 2)
        self.assertTrue('2014casj_f1m1' in [m.key.id() for m in db_matches])
        self.assertTrue('2014casj_f1m2' in [m.key.id() for m in db_matches])

        # verify match data
        match = Match.get_by_id('2014casj_f1m2')
        self.assertEqual(match.time, datetime.datetime(2014, 8, 31, 18, 0))
        self.assertEqual(match.time_string, '11:00 AM')
        self.assertEqual(match.alliances['red']['score'], 250)
        self.assertEqual(match.score_breakdown['red']['truss+catch'], 20)

        # test delete all matches
        request_body = ''
        sig = md5.new('{}{}{}'.format('321tEsTsEcReT', delete_all_request_path, request_body)).hexdigest()
        response = self.testapp.post(delete_all_request_path, request_body, headers={'X-TBA-Auth-Id': 'tEsT_id_1', 'X-TBA-Auth-Sig': sig}, expect_errors=True)
        self.assertEqual(response.status_code, 400)

        request_body = '2014casj'
        sig = md5.new('{}{}{}'.format('321tEsTsEcReT', delete_all_request_path, request_body)).hexdigest()
        response = self.testapp.post(delete_all_request_path, request_body, headers={'X-TBA-Auth-Id': 'tEsT_id_1', 'X-TBA-Auth-Sig': sig}, expect_errors=True)
        self.assertEqual(response.status_code, 200)

        db_matches = Match.query(Match.event == self.event.key).fetch(None)
        self.assertEqual(len(db_matches), 0)

    def test_rankings_update(self):
        self.rankings_auth.put()

        rankings = {
            'breakdowns': ['QS', 'Auton', 'Teleop', 'T&C'],
            'rankings': [
                {'team_key': 'frc254', 'rank': 1, 'played': 10, 'dqs': 0, 'QS': 20, 'Auton': 500, 'Teleop': 500, 'T&C': 200},
                {'team_key': 'frc971', 'rank': 2, 'played': 10, 'dqs': 0, 'QS': 20, 'Auton': 500, 'Teleop': 500, 'T&C': 200}
            ],
        }
        request_body = json.dumps(rankings)

        request_path = '/api/trusted/v1/event/2014casj/rankings/update'
        sig = md5.new('{}{}{}'.format('321tEsTsEcReT', request_path, request_body)).hexdigest()
        response = self.testapp.post(request_path, request_body, headers={'X-TBA-Auth-Id': 'tEsT_id_2', 'X-TBA-Auth-Sig': sig}, expect_errors=True)

        self.assertEqual(response.status_code, 200)
        self.assertEqual(self.event.rankings[0], ['Rank', 'Team', 'QS', 'Auton', 'Teleop', 'T&C', 'DQ', 'Played'])
        self.assertEqual(self.event.rankings[1], [1, '254', 20, 500, 500, 200, 0, 10])

    def test_rankings_wlt_update(self):
        self.rankings_auth.put()

        rankings = {
            'breakdowns': ['QS', 'Auton', 'Teleop', 'T&C', 'wins', 'losses', 'ties'],
            'rankings': [
                {'team_key': 'frc254', 'rank': 1, 'wins': 10, 'losses': 0, 'ties': 0, 'played': 10, 'dqs': 0, 'QS': 20, 'Auton': 500, 'Teleop': 500, 'T&C': 200},
                {'team_key': 'frc971', 'rank': 2, 'wins': 10, 'losses': 0, 'ties': 0, 'played': 10, 'dqs': 0, 'QS': 20, 'Auton': 500, 'Teleop': 500, 'T&C': 200}
            ],
        }
        request_body = json.dumps(rankings)

        request_path = '/api/trusted/v1/event/2014casj/rankings/update'
        sig = md5.new('{}{}{}'.format('321tEsTsEcReT', request_path, request_body)).hexdigest()
        response = self.testapp.post(request_path, request_body, headers={'X-TBA-Auth-Id': 'tEsT_id_2', 'X-TBA-Auth-Sig': sig}, expect_errors=True)

        self.assertEqual(response.status_code, 200)
        self.assertEqual(self.event.rankings[0], ['Rank', 'Team', 'QS', 'Auton', 'Teleop', 'T&C', 'Record (W-L-T)', 'DQ', 'Played'])
        self.assertEqual(self.event.rankings[1], [1, '254', 20, 500, 500, 200, '10-0-0', 0, 10])

    def test_eventteams_update(self):
        self.teams_auth.put()

        team_list = ['frc254', 'frc971', 'frc604']
        request_body = json.dumps(team_list)

        # Insert teams into db, otherwise they won't get added (see 072058b)
        Team(id='frc254', team_number=254).put()
        Team(id='frc971', team_number=971).put()
        Team(id='frc604', team_number=604).put()
        Team(id='frc100', team_number=100).put()

        request_path = '/api/trusted/v1/event/2014casj/team_list/update'
        sig = md5.new('{}{}{}'.format('321tEsTsEcReT', request_path, request_body)).hexdigest()
        response = self.testapp.post(request_path, request_body, headers={'X-TBA-Auth-Id': 'tEsT_id_0', 'X-TBA-Auth-Sig': sig}, expect_errors=True)

        self.assertEqual(response.status_code, 200)

        db_eventteams = EventTeam.query(EventTeam.event == self.event.key).fetch(None)
        self.assertEqual(len(db_eventteams), 3)
        self.assertTrue('2014casj_frc254' in [et.key.id() for et in db_eventteams])
        self.assertTrue('2014casj_frc971' in [et.key.id() for et in db_eventteams])
        self.assertTrue('2014casj_frc604' in [et.key.id() for et in db_eventteams])

        team_list = ['frc254', 'frc100']
        request_body = json.dumps(team_list)

        sig = md5.new('{}{}{}'.format('321tEsTsEcReT', request_path, request_body)).hexdigest()
        response = self.testapp.post(request_path, request_body, headers={'X-TBA-Auth-Id': 'tEsT_id_0', 'X-TBA-Auth-Sig': sig}, expect_errors=True)

        self.assertEqual(response.status_code, 200)

        db_eventteams = EventTeam.query(EventTeam.event == self.event.key).fetch(None)
        self.assertEqual(len(db_eventteams), 2)
        self.assertTrue('2014casj_frc254' in [et.key.id() for et in db_eventteams])
        self.assertTrue('2014casj_frc100' in [et.key.id() for et in db_eventteams])

    def test_eventteams_unknown(self):
        self.teams_auth.put()

        team_list = ['frc254', 'frc971', 'frc604']
        request_body = json.dumps(team_list)

        # Insert teams into db, otherwise they won't get added (see 072058b)
        Team(id='frc254', team_number=254).put()
        Team(id='frc971', team_number=971).put()

        request_path = '/api/trusted/v1/event/2014casj/team_list/update'
        sig = md5.new('{}{}{}'.format('321tEsTsEcReT', request_path, request_body)).hexdigest()
        response = self.testapp.post(request_path, request_body, headers={'X-TBA-Auth-Id': 'tEsT_id_0', 'X-TBA-Auth-Sig': sig}, expect_errors=True)

        self.assertEqual(response.status_code, 200)

        db_eventteams = EventTeam.query(EventTeam.event == self.event.key).fetch(None)
        self.assertEqual(len(db_eventteams), 2)
        self.assertTrue('2014casj_frc254' in [et.key.id() for et in db_eventteams])
        self.assertTrue('2014casj_frc971' in [et.key.id() for et in db_eventteams])
        self.assertTrue('2014casj_frc604' not in [et.key.id() for et in db_eventteams])

        team_list = ['frc254', 'frc100']
        request_body = json.dumps(team_list)

        sig = md5.new('{}{}{}'.format('321tEsTsEcReT', request_path, request_body)).hexdigest()
        response = self.testapp.post(request_path, request_body, headers={'X-TBA-Auth-Id': 'tEsT_id_0', 'X-TBA-Auth-Sig': sig}, expect_errors=True)

        self.assertEqual(response.status_code, 200)

        db_eventteams = EventTeam.query(EventTeam.event == self.event.key).fetch(None)
        self.assertEqual(len(db_eventteams), 1)
        self.assertTrue('2014casj_frc254' in [et.key.id() for et in db_eventteams])
        self.assertTrue('2014casj_frc100' not in [et.key.id() for et in db_eventteams])

    def test_match_videos_add(self):
        self.video_auth.put()

        match1 = Match(
            id="2014casj_qm1",
            alliances_json="""{"blue": {"score": -1, "teams": ["frc3464", "frc20", "frc1073"]}, "red": {"score": -1, "teams": ["frc69", "frc571", "frc176"]}}""",
            comp_level="qm",
            event=ndb.Key(Event, '2014casj'),
            year=2014,
            set_number=1,
            match_number=1,
            team_key_names=[u'frc69', u'frc571', u'frc176', u'frc3464', u'frc20', u'frc1073'],
            youtube_videos=["abcdef"]
        )
        match1.put()

        match2 = Match(
            id="2014casj_sf1m1",
            alliances_json="""{"blue": {"score": -1, "teams": ["frc3464", "frc20", "frc1073"]}, "red": {"score": -1, "teams": ["frc69", "frc571", "frc176"]}}""",
            comp_level="sf",
            event=ndb.Key(Event, '2014casj'),
            year=2014,
            set_number=1,
            match_number=1,
            team_key_names=[u'frc69', u'frc571', u'frc176', u'frc3464', u'frc20', u'frc1073'],
        )
        match2.put()

        match_videos = {'qm1': 'aFZy8iibMD0', 'sf1m1': 'RpSgUrsghv4'}

        request_body = json.dumps(match_videos)

        request_path = '/api/trusted/v1/event/2014casj/match_videos/add'
        sig = md5.new('{}{}{}'.format('321tEsTsEcReT', request_path, request_body)).hexdigest()
        response = self.testapp.post(request_path, request_body, headers={'X-TBA-Auth-Id': 'tEsT_id_5', 'X-TBA-Auth-Sig': sig}, expect_errors=True)

        self.assertEqual(response.status_code, 200)

        self.assertEqual(set(Match.get_by_id('2014casj_qm1').youtube_videos), {'abcdef', 'aFZy8iibMD0'})
        self.assertEqual(set(Match.get_by_id('2014casj_sf1m1').youtube_videos), {'RpSgUrsghv4'})