def test_get_current_matches(app): with app.app_context(): g.user_id = '3' auth_service = AuthService('secret', None, None) match_service = MatchService() no_matches = match_service.get_current_matches() assert no_matches == {} with app.app_context(): g.user_id = '1' auth_service = AuthService('secret', None, None) match_service = MatchService() matches = match_service.get_current_matches() mock_matches = { '2': { 'active': True, 'percent_unlocked': 0, 'profile': { 'animal': '', 'color': '', 'gender': '', 'preferred_gender': '', 'first_name': '', 'last_name': '', 'profile_picture': '', 'interests': {} } } } assert matches == mock_matches
def test_update_chat_state(app): with app.app_context(): g.user_id = '1' auth_service = AuthService('secret') thread_service = ThreadService() # test runtime error when received invalid user id or thread id with pytest.raises(RuntimeError): thread_service.update_chat_state(None, None) with app.app_context(): g.user_id = '1' auth_service = AuthService('secret') thread_service = ThreadService() update = thread_service.update_chat_state(1, 1) assert update is not None with app.app_context(): g.user_id = '1' auth_service = AuthService('secret') thread_service = ThreadService() old_update = thread_service.update_chat_state(1, 1) assert old_update is not None message1 = Message(1, 1, 'testing1') message2 = Message(2, 1, 'testing2') message_added = thread_service.add_message(message2) message_added = thread_service.add_message(message1) # test that message pairs gets updated when more messages are added to the thread new_update = thread_service.update_chat_state(2, 1) assert new_update is not None and old_update != new_update
def test_get_thread(app): with app.app_context(): g.user_id = '1' auth_service = AuthService('secret') thread_service = ThreadService() thread = thread_service.get_thread(1, 1) assert 1 in [user.id for user in thread.users] with app.app_context(): g.user_id = '1' auth_service = AuthService('secret') thread_service = ThreadService() # test authorization error when requesting thread with users that do not exist with pytest.raises(AuthorizationError): thread = thread_service.get_thread(100, 1) with app.app_context(): g.user_id = '1' auth_service = AuthService('secret') thread_service = ThreadService() # test not found error when requesting thread with invalid id with pytest.raises(NotFoundError): thread = thread_service.get_thread(1, 100)
def test_user_is_in_thread(app): with app.app_context(): g.user_id = '1' auth_service = AuthService('secret') thread_service = ThreadService() thread = thread_service.get_thread(1, 1) inclusion = thread_service.user_is_in_thread(1, thread) assert inclusion == True with app.app_context(): g.user_id = '1' auth_service = AuthService('secret') thread_service = ThreadService() thread = thread_service.get_thread(1, 1) inclusion = thread_service.user_is_in_thread(3, thread) assert inclusion == False with app.app_context(): g.user_id = '1' auth_service = AuthService('secret') thread_service = ThreadService() # test that SYSTEM_USER is always in thread users thread = thread_service.get_thread(1, 1) inclusion = thread_service.user_is_in_thread(0, thread) assert inclusion == True
def test_create_threads(app): with app.app_context(): g.user_id = '1' auth_service = AuthService('secret') thread_service = ThreadService() user = auth_service.get_current_user() matches = {'2': '', '3': '', '4': ''} # test threads are created for all matches threads = thread_service.create_threads(user, matches) assert threads is True
def test_inactivate_match_noop(app): with app.app_context(): g.user_id = '1' auth_service = AuthService('secret', None, None) match_service = MatchService() # Check user 2 is as an active match for user 1 mock_matches_1 = { '2': { 'active': True, 'percent_unlocked': 0, 'profile': { 'animal': '', 'color': '', 'gender': '', 'preferred_gender': '', 'first_name': '', 'last_name': '', 'profile_picture': '', 'interests': {} } } } matches_for_1 = match_service.get_current_matches() assert matches_for_1 == mock_matches_1 matched_deleted = match_service.inactivate_match(3) assert matched_deleted == False # Since user 3 is not a match for user 1, there is no change to matches matches_for_1 = match_service.get_current_matches() assert matches_for_1 == mock_matches_1
def test_find_match_3(app): with app.app_context(): g.user_id = '1' auth_service = AuthService('secret') match_service = MatchService() # Check user 1 and user 3 are currently not matched matches_for_1 = match_service.get_current_matches() assert '3' not in matches_for_1.keys() g.user_id = '3' matches_for_3 = match_service.get_current_matches() assert '1' not in matches_for_3.keys() g.user_id = '1' # Check that only one match is generated because user 2 is # already in user 1's match history find_match_for_1 = match_service.find_match(1, 2) assert len(find_match_for_1) == 1 # Check user 3 got added as an active match for user 1 matches_for_1 = match_service.get_current_matches() assert len(matches_for_1) == 2 assert matches_for_1['3']['profile']['interests'] == {'': ''} # Check user 1 got added as an active match for user 3 g.user_id = '3' matches_for_3 = match_service.get_current_matches() assert len(matches_for_3) == 1 assert matches_for_3['1']['profile']['interests'] == {'': ''}
def test_find_match_1(app): with app.app_context(): g.user_id = '3' auth_service = AuthService('secret') match_service = MatchService() # Check user 3 and user 4 are currently not matched matches_for_3 = match_service.get_current_matches() assert '4' not in matches_for_3.keys() g.user_id = '4' matches_for_4 = match_service.get_current_matches() assert '3' not in matches_for_4.keys() g.user_id = '3' # Check users 3 and 4 got matched on interest3 find_match_for_3 = match_service.find_match(3, 1) match_result = (4, {'interest3': 'value3'}) assert len(find_match_for_3) == 1 assert match_result in find_match_for_3 # Check user 4 got added as an active match for user 3 matches_for_3 = match_service.get_current_matches() assert len(matches_for_3) == 1 assert matches_for_3['4']['profile']['interests'] == { 'interest3': 'value3' } # Check user 3 got added as an active match for user 4 g.user_id = '4' matches_for_4 = match_service.get_current_matches() assert len(matches_for_4) == 1 assert matches_for_4['3']['profile']['interests'] == { 'interest3': 'value3' }
def test_get_messages(app): with app.app_context(): g.user_id = '1' auth_service = AuthService('secret') thread_service = ThreadService() messages = thread_service.get_messages(1, 1) assert messages is not None
def test_inactivate_match_1(app): with app.app_context(): g.user_id = '1' auth_service = AuthService('secret', None, None) match_service = MatchService() # Check user 2 is as an active match for user 1 mock_matches_1 = { '2': { 'active': True, 'percent_unlocked': 0, 'profile': { 'animal': '', 'color': '', 'gender': '', 'preferred_gender': '', 'first_name': '', 'last_name': '', 'profile_picture': '', 'interests': {} } } } matches_for_1 = match_service.get_current_matches() assert matches_for_1 == mock_matches_1 # Check user 1 is as an active match for user 2 g.user_id = '2' mock_matches_2 = { '1': { 'active': True, 'percent_unlocked': 0, 'profile': { 'animal': '', 'color': '', 'gender': '', 'preferred_gender': '', 'first_name': '', 'last_name': '', 'profile_picture': '', 'interests': {} } } } matches_for_2 = match_service.get_current_matches() assert matches_for_2 == mock_matches_2 g.user_id = '1' match_deleted = match_service.inactivate_match(2) assert match_deleted == True # Since both users only have one match each, they should no longer have any matches_for_1 = match_service.get_current_matches() assert matches_for_1 == {} g.user_id = '2' matches_for_2 = match_service.get_current_matches() assert matches_for_2 == {}
def test_get_current_user_profile(app): with app.app_context(): g.user_id = '1' service = AuthService('secret') profile = service.get_current_user_profile()._asdict() # Ignore created/updated time profile['created_at'], profile['updated_at'] = 0, 0 # Profile._asdict() does not return match_history mocked_profile = { 'id': 1, 'user_id': 1, 'first_name': 'Joe', 'last_name': 'Bruin', 'profile_picture': '', 'gender': 'Male', 'preferred_gender': 'Female', 'color': '', 'animal': '', 'interests': {'interest1': 'value1'}, 'created_at': 0, 'updated_at': 0 } assert profile == mocked_profile with app.app_context(): service = AuthService('secret') profile = service.get_current_user() assert profile is None
def test_delete_thread(app): with app.app_context(): g.user_id = '1' auth_service = AuthService('secret') thread_service = ThreadService() old_thread = thread_service.get_thread(1, 1) assert old_thread.is_active == True deleted = thread_service.delete_thread(2) # test thread deletion (to be used after match deletion) new_thread = thread_service.get_thread(1, 1) assert new_thread.is_active == False
def test_add_message(app): with app.app_context(): g.user_id = '1' auth_service = AuthService('secret') thread_service = ThreadService() # test that invalid message raises a runtime error with pytest.raises(RuntimeError): thread_service.add_message(None) with app.app_context(): g.user_id = '1' auth_service = AuthService('secret') thread_service = ThreadService() old_messages = thread_service.get_messages(1, 1) assert len(old_messages) == 2 message = Message(1, 1, 'testing') message_added = thread_service.add_message(message) assert message_added is not None new_messages = thread_service.get_messages(1, 1) assert len(new_messages) == 3
def test_validate_token(app): valid_token = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6MTIzfQ.4OcRLSO_GlqmtdRD_eKLcLiVpSaX8ueIM2ddAOrxY1I' bad_signature_token = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6IjEyMyJ9.Gyndb3IcLowcYksGg20QWouK6DkRQ28Jqlh80tjG9J8' with app.app_context(): service = AuthService('secret', None, None) service.validate_token(valid_token) assert g.user_id == 123 with pytest.raises(AuthorizationError): service.validate_token(bad_signature_token)
def test_get_current_user_id(app): with app.app_context(): g.user_id = 'test' service = AuthService('secret', None, None) id = service.get_current_user_id() assert id == 'test' with app.app_context(): service = AuthService('secret', None, None) id = service.get_current_user_id() assert id is None
def test_login_required(app): with app.app_context(): g.user_id = 'test' service = AuthService('secret', None, None) f = service.login_required(lambda x: x) value = f(1) assert value == 1 with app.app_context(): service = AuthService('secret', None, None) f = service.login_required(lambda x: x) with pytest.raises(AuthorizationError): f(1)
def test_get_user_for_user_id(app): with app.app_context(): g.user_id = 'test' service = AuthService('secret') user = service.get_user_for_user_id(1) assert user.id == 1 with app.app_context(): g.user_id = 'test' service = AuthService('secret') user = service.get_user_for_user_id(100) assert user is None
def test_find_match_2(app): with app.app_context(): g.user_id = '3' auth_service = AuthService('secret') match_service = MatchService() # Check user 3 and users 1 and 4 are currently not matched matches_for_3 = match_service.get_current_matches() assert '1' not in matches_for_3.keys() assert '4' not in matches_for_3.keys() g.user_id = '4' matches_for_4 = match_service.get_current_matches() assert '3' not in matches_for_4.keys() g.user_id = '1' matches_for_1 = match_service.get_current_matches() assert '3' not in matches_for_1.keys() g.user_id = '3' find_match_for_3 = match_service.find_match(3, 2) assert len(find_match_for_3) == 2 # Check users 1 and 4 got added as an active match for user 3 # User 4 is matched based on the shared interest3 # User 1 is matched even without common interests because it is the only # other user in the database satisying user 3's gender preferences matches_for_3 = match_service.get_current_matches() assert len(matches_for_3) == 2 assert matches_for_3['4']['profile']['interests'] == { 'interest3': 'value3' } assert matches_for_3['1']['profile']['interests'] == {'': ''} # Check user 3 got added as an active match for user 4 g.user_id = '4' matches_for_4 = match_service.get_current_matches() assert len(matches_for_4) == 1 assert matches_for_4['3']['profile']['interests'] == { 'interest3': 'value3' } # Check user 3 got added as an active match for user 1 g.user_id = '1' matches_for_1 = match_service.get_current_matches() assert len(matches_for_1) == 2 assert matches_for_1['3']['profile']['interests'] == {'': ''}
def test_add_match_to_profile_with_duplicate_match(app): with app.app_context(): g.user_id = '1' auth_service = AuthService('secret', None, None) match_service = MatchService() # Check user 1 and user 2 are currently matched matches_for_1 = match_service.get_current_matches() assert '2' in matches_for_1.keys() g.user_id = '2' matches_for_2 = match_service.get_current_matches() assert '1' in matches_for_2.keys() g.user_id = '1' # Simulate duplicate match between user 1 and user 2 on interest, should fail matched_interest = {'test_key': 'test_val'} created_match = match_service.add_match_to_profile(2, matched_interest) assert created_match == False # Check user 2 did not get added as a duplicate match for user 1 mock_matches_1 = { '2': { 'active': True, 'percent_unlocked': 0, 'profile': { 'animal': '', 'color': '', 'gender': '', 'preferred_gender': '', 'first_name': '', 'last_name': '', 'profile_picture': '', 'interests': {} } } } matches_for_1 = match_service.get_current_matches() assert len(matches_for_1) == 1 assert matches_for_1['2'] == mock_matches_1['2']
def test_get_current_user(app): with app.app_context(): g.user_id = '1' service = AuthService('secret') user = service.get_current_user()._asdict() # Ignore created/updated time user['created_at'], user['updated_at'] = 0, 0 mocked_user = { 'id': 1, 'email': '*****@*****.**', 'created_at': 0, 'updated_at': 0 } assert user == mocked_user with app.app_context(): service = AuthService('secret') user = service.get_current_user() assert user is None
def test_create_thread(app): with app.app_context(): auth_service = AuthService('secret') thread_service = ThreadService() user1 = auth_service.get_user_for_user_id(100) user2 = auth_service.get_user_for_user_id(101) # test runtime error for users that do not exist with pytest.raises(RuntimeError): thread = thread_service.create_thread(user1, user2) assert thread is None with app.app_context(): auth_service = AuthService('secret') thread_service = ThreadService() user1 = auth_service.get_user_for_user_id(1) user2 = auth_service.get_user_for_user_id(2) # test not creating duplicate thread for thread that already exists thread = thread_service.create_thread(user1, user2) assert thread is None with app.app_context(): auth_service = AuthService('secret') thread_service = ThreadService() user1 = auth_service.get_user_for_user_id(1) user2 = auth_service.get_user_for_user_id(2) # test overriding thread for thread that already exists thread = thread_service.create_thread(user1, user2, force=True) assert thread is not None and thread.is_active is True with app.app_context(): auth_service = AuthService('secret') thread_service = ThreadService() user3 = auth_service.get_user_for_user_id(3) user4 = auth_service.get_user_for_user_id(4) # test new thread creation thread = thread_service.create_thread(user3, user4, force=True) assert thread
def add_routes(app, socketio): """ Adds callable endpoints to Flask. :param app: The configured Flask backend application to add endpoints to. :param socketio: The configured socketio instance :return: None. """ auth_service = AuthService(app.config['SECRET_KEY']) @app.before_request def check_for_token(): if request.headers.get('Authorization') is not None: token = request.headers.get('Authorization') auth_service.validate_token(token) # register user views user_view = UserAPI.as_view('user_api', auth_service) profile_view = auth_service.login_required( ProfileAPI.as_view('profile_api', auth_service)) app.add_url_rule('/api/users', view_func=user_view, methods=['POST']) app.add_url_rule('/api/users', view_func=profile_view, methods=['GET', 'PUT']) # register thread views thread_service = ThreadService() thread_view = auth_service.login_required( ThreadAPI.as_view('thread_api', auth_service)) thread_message_view = auth_service.login_required( MessageAPI.as_view('message_api', thread_service, auth_service)) app.add_url_rule('/api/threads', view_func=thread_view, methods=['GET']) app.add_url_rule('/api/threads/<int:thread_id>/messages', view_func=thread_message_view, methods=['GET']) app.add_url_rule('/api/threads/<int:user_id>', view_func=thread_message_view, methods=['DELETE']) # register match views match_service = MatchService() match_view = auth_service.login_required( MatchAPI.as_view('match_api', match_service, auth_service, thread_service)) app.add_url_rule('/api/matches', view_func=match_view, methods=['GET']) app.add_url_rule('/api/matches/<int:user_id>', view_func=match_view, methods=['DELETE']) # register socket socketio.on_namespace( ThreadSockets(None, auth_service, thread_service, match_service, app.logger)) @socketio.on_error_default def handle_socket_error(e): emit('error', e.to_dict()) @app.route('/') def health_check(): return jsonify({'health': 'ok'}) @app.route('/api/force_match/<int:user_id>') def force_match(user_id): """ Processes a HTTP GET request for the force match debugging REST API endpoint. .. code-block:: bash GET localhost:8000/api/force_match/2 Example response: .. code-block:: json { "success": true } :return: a Flask HTTP response with a forced match success boolean. """ user1 = auth_service.get_current_user() user2 = auth_service.get_user_for_user_id(user_id) user1_dict = user1.profile._asdict() user2_dict = user2.profile._asdict() user1_interests = set(user1_dict['interests'].keys()) user2_interests = set(user2_dict['interests'].keys()) shared_interests = user1_interests & user2_interests if len(shared_interests) == 0: return jsonify(success=False) shared_interest_key = shared_interests.pop() interest = { shared_interest_key: user1_dict['interests'][shared_interest_key] } success = match_service.add_match_to_profile(user_id, interest, force=True) thread = thread_service.create_thread(user1, user2, force=True) return jsonify(success=success and thread is not None) @app.route('/api/force_match_delete/<int:user_id>') def force_match_delete(user_id): """ Processes a HTTP GET request for the force delete debugging REST API endpoint. .. code-block:: bash GET localhost:8000/api/force_match_delete/2 Example response: .. code-block:: json { "success": true } :return: a Flask HTTP response with a forced delete success boolean. """ delete_match = match_service.inactivate_match(user_id, purge=True) delete_thread = thread_service.delete_thread(user_id, purge=True) return jsonify(status=delete_match and delete_thread) @app.route('/api/force_unlock/<int:user_id>') def force_unlock(user_id): """ Processes a HTTP GET request for the force profile unlock debugging REST API endpoint. .. code-block:: bash GET localhost:8000/api/force_match_delete/2 Example response: .. code-block:: json { "progress": { "user_percent_unlocked": 25, "matched_user_percent_unlocked": 14, "user_unlocked_feature": {"interest5": "value5"} "matched_user_unlocked_feature": {"interest2": "value2"} } } :return: a Flask HTTP response with the next unlocked feature for the matched user and current user. """ data = match_service.unlock_next_profile_feature(user_id) return jsonify(progress=data)
def test_update_current_user_profile(app): mocked_profile = { 'id': 1, 'user_id': 1, 'first_name': 'Tim', 'last_name': 'Bruin', 'profile_picture': '', 'gender': 'Male', 'preferred_gender': 'Female', 'color': '', 'animal': '', 'interests': {'interest1': 'value1', 'interest2': 'value2'}, 'created_at': 0, 'updated_at': 0 } with app.app_context(): g.user_id = '1' service = AuthService('secret') old_profile = service.get_current_user_profile()._asdict() fields = {'first_name' : 'Tim', 'interests': {'interest1': 'value1', 'interest2': 'value2'}} new_profile = service.update_current_user_profile(fields)._asdict() assert new_profile is not old_profile and \ new_profile['updated_at'] > old_profile['updated_at'] # Ignore created/updated time new_profile['created_at'], new_profile['updated_at'] = 0, 0 assert new_profile == mocked_profile with app.app_context(): service = AuthService('secret') old_profile = service.get_current_user_profile() assert old_profile is None with app.app_context(): g.user_id = '1' service = AuthService('secret') old_profile = service.get_current_user_profile()._asdict() fields = {'thisisnotavalidkey' : 'thisisnotavalidvalue', 'first_name': 'Tim', 'interests': {'interest1': 'value1', 'interest2': 'value2'}} new_profile = service.update_current_user_profile(fields)._asdict() assert new_profile is not old_profile and \ new_profile['updated_at'] > old_profile['updated_at'] # Ignore created/updated time new_profile['created_at'], new_profile['updated_at'] = 0, 0 assert new_profile == mocked_profile
def test_add_match_to_profile(app): with app.app_context(): g.user_id = '1' auth_service = AuthService('secret', None, None) match_service = MatchService() # Check user 1 and user 3 are currently not matched matches_for_1 = match_service.get_current_matches() assert '3' not in matches_for_1.keys() g.user_id = '3' matches_for_3 = match_service.get_current_matches() assert '1' not in matches_for_3.keys() g.user_id = '1' # Simulate match between user 1 and user 3 on interest matched_interest = {'test_key': 'test_val'} created_match = match_service.add_match_to_profile(3, matched_interest) assert created_match == True # Check user 3 got added as an active match for user 1 mock_matches_1 = { '3': { 'active': True, 'percent_unlocked': 25, 'profile': { 'animal': '', 'color': '', 'gender': 'Female', 'preferred_gender': 'Male', 'first_name': '', 'last_name': '', 'profile_picture': '', 'interests': { 'test_key': 'test_val' } } } } matches_for_1 = match_service.get_current_matches() assert len(matches_for_1) == 2 assert matches_for_1['3'] == mock_matches_1['3'] # Check user 1 got added as an active match for user 3 mock_matches_3 = { '1': { 'active': True, 'percent_unlocked': 25, 'profile': { 'animal': '', 'color': '', 'gender': 'Male', 'preferred_gender': 'Female', 'first_name': '', 'last_name': '', 'profile_picture': '', 'interests': { 'test_key': 'test_val' } } } } g.user_id = '3' matches_for_3 = match_service.get_current_matches() assert len(matches_for_3) == 1 assert matches_for_3['1'] == mock_matches_3['1']
def test_unlock_next_profile_feature_2(app): with app.app_context(): g.user_id = '1' auth_service = AuthService('secret', None, None) match_service = MatchService() # Check user 1 and user 2 are currently matched matches_for_1 = match_service.get_current_matches() assert '2' in matches_for_1.keys() g.user_id = '2' matches_for_2 = match_service.get_current_matches() assert '1' in matches_for_2.keys() g.user_id = '1' # Unlock next two profile features for user 1 and user 2 # # Each user has 1 interest, so the unlocked percentages should be 25% after # first unlock because they have not yet unlocked each other's first_name, # last_name, or profile_picture; after the second unlock, the unlocked # percentages should be 50%. data = match_service.unlock_next_profile_feature(2) assert data['user_percent_unlocked'] == 25 assert data['matched_user_percent_unlocked'] == 25 data = match_service.unlock_next_profile_feature(2) assert data['user_percent_unlocked'] == 50 assert data['matched_user_percent_unlocked'] == 50 # Check user 2's interest and first_name show in the active match for user 1 mock_matches_1 = { '2': { 'active': True, 'percent_unlocked': 50, 'profile': { 'animal': '', 'color': '', 'gender': '', 'preferred_gender': '', 'first_name': 'Josephine', 'last_name': '', 'profile_picture': '', 'interests': { 'interest2': 'value2' } } } } matches_for_1 = match_service.get_current_matches() assert matches_for_1['2'] == mock_matches_1['2'] # Check user 1's interest and first_name show in the active match for user 2 mock_matches_2 = { '1': { 'active': True, 'percent_unlocked': 50, 'profile': { 'animal': '', 'color': '', 'gender': '', 'preferred_gender': '', 'first_name': 'Joe', 'last_name': '', 'profile_picture': '', 'interests': { 'interest1': 'value1' } } } } g.user_id = '2' matches_for_2 = match_service.get_current_matches() assert matches_for_2['1'] == mock_matches_2['1']