"""Test /users/123/session/alw/linked-accounts GET.""" import json from unittest.mock import patch from moto import mock_sqs from owsresponse import response from tests.unit import db_utils from users import constants from users.app import app @db_utils.test_schema @mock_sqs @patch('users.handlers.user.Neo4jSession.__enter__') @patch('users.handlers.user.Neo4jSession.__exit__') def test_get_linked_accounts_by_identity(neo4j_exit, neo4j_enter, context, mocker): """Test get_linked_accounts resolves via Neo4j Identity -> LabelProfile path. Seeds MySQL users with DIFFERENT auth0_user_ids to prove that the identity- based path resolves linked accounts regardless of auth0_user_id mismatch. """ user_id = 55281 identity_id = 'identity-uuid-123' # Seed users with DIFFERENT auth0_user_ids to prove that the identity-based # path resolves linked accounts regardless of auth0_user_id mismatch users = [ {'id': 102, 'auth0_id': 'auth0|original', 'vendor_id': 8869}, {'id': 103, 'auth0_id': 'auth0|original', 'vendor_id': 2244}, ] expected_users = db_utils.seed_vendor_vc_subaccount(users) db_utils.seed_vendor_vc_subaccount( [{'id': user_id, 'auth0_id': 'google-apps|rewritten', 'vendor_id': 7123}] ) # Mock get_identity_for_profile to find the Identity from vc_id (LabelProfile.profileId) mocker.patch( 'users.logic.user_info.identities.get_identity_for_profile', return_value=response.Response(message={'id': identity_id}), ) # Mock get_linked_label_profiles to return LabelProfiles with active vendor access mocker.patch( 'users.logic.user_info.profiles.get_linked_label_profiles', return_value=[ {'profile_id': 102, 'profile_type': 'LabelProfile', 'uuid': 'uuid-102'}, {'profile_id': 103, 'profile_type': 'LabelProfile', 'uuid': 'uuid-103'}, {'profile_id': user_id, 'profile_type': 'LabelProfile', 'uuid': 'uuid-55281'}, ], ) path = '/users/{}/session/alw/linked-accounts'.format(user_id) navigator = app.test_client() request = navigator.get(path, content_type='application/json') response_data = json.loads(request.data.decode('utf-8')) assert request.status_code == 200 assert len(response_data) == 2 for actual_user in response_data: assert 'profile_uuid' in actual_user # check the original MySQL fields still match assert {k: v for k, v in actual_user.items() if k != 'profile_uuid'} in expected_users # profile_uuid is enriched from Neo4j Profile.uuid, not from MySQL actual_uuids = {u['vc_id']: u['profile_uuid'] for u in response_data} assert actual_uuids[102] == 'uuid-102' assert actual_uuids[103] == 'uuid-103' @db_utils.test_schema @mock_sqs @patch('users.handlers.user.Neo4jSession.__enter__') @patch('users.handlers.user.Neo4jSession.__exit__') def test_get_linked_accounts_by_identity_filters_deleted_access( neo4j_exit, neo4j_enter, context, mocker ): """Test that profiles with DELETED_HAS_ACCESS_TO are excluded. Simulates a user migration where old profiles had their Neo4j vendor access soft-deleted but remain active in MySQL. Only profiles with active HAS_ACCESS_TO should appear in linked accounts. """ user_id = 55281 identity_id = 'identity-uuid-123' # Seed 3 MySQL users, all active. Profile 104 has DELETED_HAS_ACCESS_TO # in Neo4j (simulated by excluding it from get_linked_label_profiles). users = [ {'id': 102, 'auth0_id': 'auth0|original', 'vendor_id': 8869}, {'id': 103, 'auth0_id': 'auth0|original', 'vendor_id': 2244}, {'id': 104, 'auth0_id': 'auth0|original', 'vendor_id': 3355}, ] all_seeded = db_utils.seed_vendor_vc_subaccount(users) expected_users = [u for u in all_seeded if u['vc_id'] != 104] db_utils.seed_vendor_vc_subaccount( [{'id': user_id, 'auth0_id': 'google-apps|rewritten', 'vendor_id': 7123}] ) mocker.patch( 'users.logic.user_info.identities.get_identity_for_profile', return_value=response.Response(message={'id': identity_id}), ) # get_linked_label_profiles only returns profiles with active HAS_ACCESS_TO, # so 104 (DELETED_HAS_ACCESS_TO) is excluded mocker.patch( 'users.logic.user_info.profiles.get_linked_label_profiles', return_value=[ {'profile_id': 102, 'profile_type': 'LabelProfile', 'uuid': 'uuid-102'}, {'profile_id': 103, 'profile_type': 'LabelProfile', 'uuid': 'uuid-103'}, {'profile_id': user_id, 'profile_type': 'LabelProfile', 'uuid': 'uuid-55281'}, ], ) path = '/users/{}/session/alw/linked-accounts'.format(user_id) navigator = app.test_client() request = navigator.get(path, content_type='application/json') response_data = json.loads(request.data.decode('utf-8')) assert request.status_code == 200 assert len(response_data) == len(expected_users) == 2 for actual_user in response_data: assert 'profile_uuid' in actual_user assert {k: v for k, v in actual_user.items() if k != 'profile_uuid'} in expected_users @db_utils.test_schema @mock_sqs @patch('users.handlers.user.Neo4jSession.__enter__') @patch('users.handlers.user.Neo4jSession.__exit__') def test_get_linked_accounts_by_identity_no_identity_found( neo4j_exit, neo4j_enter, context, mocker ): """Test that when Identity is not found in Neo4j, returns empty list.""" user_id = 55281 db_utils.seed_vendor_vc_subaccount( [{'id': user_id, 'auth0_id': 'google-apps|rewritten', 'vendor_id': 7123}] ) # Identity not found in Neo4j for this LabelProfile mocker.patch( 'users.logic.user_info.identities.get_identity_for_profile', return_value=response.create_not_found_response( message=constants.ERROR_MESSAGE_IDENTITY_NOT_FOUND ), ) path = '/users/{}/session/alw/linked-accounts'.format(user_id) navigator = app.test_client() request = navigator.get(path, content_type='application/json') response_data = json.loads(request.data.decode('utf-8')) assert request.status_code == 200 assert response_data == []