def test_user_can_download_profile_using_correct_api_tokens( user_gql_client, service_1, service_2, mocker ): def mock_download_gdpr_data(self, api_token: str): if (self.service.name == service_1.name and api_token == API_TOKEN_1) or ( self.service.name == service_2.name and api_token == API_TOKEN_2 ): return {} raise Exception("Wrong token used!") profile = ProfileFactory(user=user_gql_client.user) ServiceConnectionFactory(profile=profile, service=service_1) ServiceConnectionFactory(profile=profile, service=service_2) mocked_gdpr_download = mocker.patch.object( ServiceConnection, "download_gdpr_data", autospec=True, side_effect=mock_download_gdpr_data, ) mocked_token_exchange = mocker.patch.object( TunnistamoTokenExchange, "fetch_api_tokens", return_value=GDPR_API_TOKENS ) executed = user_gql_client.execute(DOWNLOAD_MY_PROFILE_MUTATION) mocked_token_exchange.assert_called_once() assert mocked_token_exchange.call_args == ((AUTHORIZATION_CODE,),) assert mocked_gdpr_download.call_count == 2 assert executed["data"]["downloadMyProfile"]
def test_user_can_download_profile_with_connected_services( user_gql_client, service_1, service_2, mocker ): expected = {"key": "SERVICE-1", "children": [{"key": "CUSTOMERID", "value": "123"}]} def mock_download_gdpr_data(self, api_token: str): if self.service.name == service_1.name: return expected else: return {} mocker.patch.object( ServiceConnection, "download_gdpr_data", autospec=True, side_effect=mock_download_gdpr_data, ) mocker.patch.object( TunnistamoTokenExchange, "fetch_api_tokens", return_value=GDPR_API_TOKENS ) profile = ProfileWithPrimaryEmailFactory(user=user_gql_client.user) ServiceConnectionFactory(profile=profile, service=service_1) ServiceConnectionFactory(profile=profile, service=service_2) executed = user_gql_client.execute(DOWNLOAD_MY_PROFILE_MUTATION) response_data = json.loads(executed["data"]["downloadMyProfile"])["children"] assert len(response_data) == 2 assert expected in response_data # Data does not contain the empty response from service_2 assert {} not in response_data
def test_staff_user_can_filter_profiles_by_profile_ids(user_gql_client, group, service): profile_1, profile_2, profile_3 = ProfileFactory.create_batch(3) ServiceConnectionFactory(profile=profile_1, service=service) ServiceConnectionFactory(profile=profile_2, service=service) ServiceConnectionFactory(profile=profile_3, service=service) user = user_gql_client.user user.groups.add(group) assign_perm("can_view_profiles", group, service) query = """ query getProfiles($ids: [UUID!]!) { profiles(id: $ids) { count totalCount } } """ expected_data = {"profiles": {"count": 2, "totalCount": 3}} executed = user_gql_client.execute( query, variables={ "ids": [str(profile_2.id), str(profile_1.id), str(uuid.uuid4())] }, service=service, ) assert "errors" not in executed assert executed["data"] == expected_data
def test_user_tries_deleting_his_profile_but_it_fails_partially( user_gql_client, service_1, service_2, mocker ): """Test an edge case where dry runs passes for all connected services, but the proper service connection delete fails for a single connected service. All other connected services should still get deleted. """ def mock_delete_gdpr_data(self, api_token, dry_run=False): if self.service.name == service_2.name and not dry_run: raise requests.HTTPError("Such big fail! :(") mocker.patch.object( ServiceConnection, "delete_gdpr_data", autospec=True, side_effect=mock_delete_gdpr_data, ) mocker.patch.object( TunnistamoTokenExchange, "fetch_api_tokens", return_value=GDPR_API_TOKENS ) profile = ProfileFactory(user=user_gql_client.user) ServiceConnectionFactory(profile=profile, service=service_1) ServiceConnectionFactory(profile=profile, service=service_2) executed = user_gql_client.execute(DELETE_MY_PROFILE_MUTATION) expected_data = {"deleteMyProfile": None} assert ServiceConnection.objects.count() == 1 assert ServiceConnection.objects.first().service == service_2 assert dict(executed["data"]) == expected_data assert_match_error_code(executed, CONNECTED_SERVICE_DELETION_FAILED_ERROR)
def test_staff_user_can_filter_profiles_by_a_field(field_name, user_gql_client, group, service): profile_1, profile_2 = ProfileFactory.create_batch(2) ServiceConnectionFactory(profile=profile_1, service=service) ServiceConnectionFactory(profile=profile_2, service=service) user = user_gql_client.user user.groups.add(group) assign_perm("can_view_profiles", group, service) gql_field_name = to_graphql_name(field_name) query = query_template.substitute(search_arg_name=gql_field_name) expected_data = { "profiles": { "count": 1, "totalCount": 2, "edges": [{ "node": { "firstName": profile_2.first_name } }], } } search_term = getattr(profile_2, field_name)[1:].upper() executed = user_gql_client.execute( query, variables={"searchString": search_term}, service=service, ) assert "errors" not in executed assert executed["data"] == expected_data
def test_user_can_delete_his_profile( user_gql_client, profile_service, service_1, requests_mock, mocker, with_serviceconnection, ): """Deletion is allowed when GDPR URL is set, and service returns a successful status.""" profile = ProfileFactory(user=user_gql_client.user) ServiceConnectionFactory(profile=profile, service=profile_service) if with_serviceconnection: requests_mock.delete( f"{service_1.gdpr_url}{profile.pk}", json={}, status_code=204 ) ServiceConnectionFactory(profile=profile, service=service_1) mocker.patch.object( TunnistamoTokenExchange, "fetch_api_tokens", return_value=GDPR_API_TOKENS ) executed = user_gql_client.execute(DELETE_MY_PROFILE_MUTATION, service=service_1) if with_serviceconnection: expected_data = {"deleteMyProfile": {"clientMutationId": None}} assert executed["data"] == expected_data with pytest.raises(Profile.DoesNotExist): profile.refresh_from_db() with pytest.raises(User.DoesNotExist): user_gql_client.user.refresh_from_db() else: assert_match_error_code(executed, "PERMISSION_DENIED_ERROR") assert executed["data"]["deleteMyProfile"] is None assert Profile.objects.filter(pk=profile.pk).exists()
def test_remove_service_gdpr_data_successful(profile, service, requests_mock): requests_mock.delete(f"{GDPR_URL}{profile.pk}", json={}, status_code=204) service_connection = ServiceConnectionFactory(profile=profile, service=service) dry_run_ok = service_connection.delete_gdpr_data(dry_run=True) real_ok = service_connection.delete_gdpr_data() assert dry_run_ok assert real_ok
def test_get_service_gdpr_data(monkeypatch, service_factory, profile): def mock_download_gdpr_data(self): if self.service.service_type == ServiceType.BERTH: return { "key": "BERTH", "children": [{ "key": "CUSTOMERID", "value": "123" }] } elif self.service.service_type == ServiceType.YOUTH_MEMBERSHIP: return { "key": "YOUTHPROFILE", "children": [{ "key": "BIRTH_DATE", "value": "2004-12-08" }], } else: return {} # Setup the data service_berth = service_factory(service_type=ServiceType.BERTH) service_youth = service_factory(service_type=ServiceType.YOUTH_MEMBERSHIP) service_kukkuu = service_factory( service_type=ServiceType.GODCHILDREN_OF_CULTURE) ServiceConnectionFactory(profile=profile, service=service_berth) ServiceConnectionFactory(profile=profile, service=service_youth) ServiceConnectionFactory(profile=profile, service=service_kukkuu) # Let's monkeypatch the method in ServiceConnection to mock the http requests monkeypatch.setattr(ServiceConnection, "download_gdpr_data", mock_download_gdpr_data) response = profile.get_service_gdpr_data() assert response == [ { "key": "BERTH", "children": [{ "key": "CUSTOMERID", "value": "123" }] }, { "key": "YOUTHPROFILE", "children": [{ "key": "BIRTH_DATE", "value": "2004-12-08" }], }, ]
def test_user_has_admin_perms_to_view_profile_util(user_should_have_perms, user, profile, group, service_factory): service_1 = service_factory(service_type=ServiceType.BERTH) service_2 = service_factory(service_type=ServiceType.YOUTH_MEMBERSHIP) user.groups.add(group) assign_perm("can_view_profiles", group, service_1) if user_should_have_perms: ServiceConnectionFactory(profile=profile, service=service_1) assert user_has_staff_perms_to_view_profile(user, profile) else: ServiceConnectionFactory(profile=profile, service=service_2) assert not user_has_staff_perms_to_view_profile(user, profile)
def test_staff_user_can_query_sensitive_data_with_given_permissions( user_gql_client, profile, group, service): sensitive_data = SensitiveDataFactory(profile=profile) ServiceConnectionFactory(profile=profile, service=service) user = user_gql_client.user user.groups.add(group) assign_perm("can_view_profiles", group, service) assign_perm("can_view_sensitivedata", group, service) t = Template(""" { profile(id: "${id}") { sensitivedata { ssn } } } """) query = t.substitute(id=relay.Node.to_global_id(ProfileNode._meta.name, profile.id), ) expected_data = {"profile": {"sensitivedata": {"ssn": sensitive_data.ssn}}} executed = user_gql_client.execute(query, service=service) assert "errors" not in executed assert executed["data"] == expected_data
def test_staff_user_cannot_query_a_profile_without_a_connection_to_their_service( user_gql_client, profile, group, service_factory): user = user_gql_client.user staff_user_service = service_factory() user.groups.add(group) assign_perm("can_view_profiles", group, staff_user_service) other_service = service_factory() ServiceConnectionFactory(profile=profile, service=other_service) t = Template(""" { profile(id: "${id}") { firstName lastName } } """) query = t.substitute(id=relay.Node.to_global_id(ProfileNode._meta.name, profile.id), ) executed = user_gql_client.execute(query, service=staff_user_service) assert "errors" in executed assert "code" in executed["errors"][0]["extensions"] assert executed["errors"][0]["extensions"][ "code"] == OBJECT_DOES_NOT_EXIST_ERROR
def test_allowed_data_fields_are_ordered_by_order_field(user_gql_client, service): fields = ( AllowedDataFieldFactory(field_name="1: field 2", final_order=2), AllowedDataFieldFactory(field_name="2: field 3", final_order=3), AllowedDataFieldFactory(field_name="3: field 1", final_order=1), ) service.allowed_data_fields.set(fields) profile = ProfileFactory(user=user_gql_client.user) ServiceConnectionFactory(profile=profile, service=service) executed = user_gql_client.execute(QUERY) connection_edges = executed["data"]["myProfile"]["serviceConnections"]["edges"] assert len(connection_edges) == 1 service_node = connection_edges[0]["node"]["service"] allowed_data_field_edges = service_node["allowedDataFields"]["edges"] assert len(allowed_data_field_edges) == 3 fields_in_expected_order = sorted(fields, key=lambda x: x.order) for allowed_data_field_edge, field in zip( allowed_data_field_edges, fields_in_expected_order ): received_field = allowed_data_field_edge["node"] assert received_field["fieldName"] == field.field_name assert received_field["order"] == field.order
def test_normal_user_can_query_his_own_profile(user_gql_client, service, with_service, with_serviceconnection): profile = ProfileFactory(user=user_gql_client.user) if with_serviceconnection: ServiceConnectionFactory(profile=profile, service=service) query = """ { myProfile { firstName lastName } } """ expected_data = { "myProfile": { "firstName": profile.first_name, "lastName": profile.last_name } } executed = user_gql_client.execute( query, service=service if with_service else None) if with_service and with_serviceconnection: assert executed["data"] == expected_data elif not with_service: assert_match_error_code(executed, "SERVICE_NOT_IDENTIFIED_ERROR") assert executed["data"]["myProfile"] is None else: assert_match_error_code(executed, "PERMISSION_DENIED_ERROR") assert executed["data"]["myProfile"] is None
def test_admin_user_can_query_profiles(superuser_gql_client, profile, service): ServiceConnectionFactory(profile=profile, service=service) query = """ { profiles { edges { node { firstName lastName nickname } } } } """ expected_data = { "profiles": { "edges": [{ "node": { "firstName": profile.first_name, "lastName": profile.last_name, "nickname": profile.nickname, } }] } } executed = superuser_gql_client.execute(query, service=service) assert executed["data"] == expected_data
def test_actor_service(live_server, user, group, service_client_id, cap_audit_log): profile = ProfileFactory() service = service_client_id.service ServiceConnectionFactory(profile=profile, service=service) user.groups.add(group) assign_perm("can_view_profiles", group, service) # serviceType is included in query just to ensure that it has NO affect on the audit log query = """ { profiles(serviceType: GODCHILDREN_OF_CULTURE) { edges { node { firstName } } } } """ cap_audit_log.clear() do_graphql_call_as_user(live_server, user, service=service, query=query) audit_logs = cap_audit_log.get_logs() assert len(audit_logs) == 1 log_message = audit_logs[0] assert_common_fields(log_message, profile, "READ", actor_role="ADMIN") actor_log = log_message["audit_event"]["actor"] assert actor_log["service_name"] == service.name assert "client_id" in actor_log assert actor_log["client_id"] == service_client_id.client_id
def test_staff_user_with_group_access_can_query_profiles( user_gql_client, profile, group, service): ServiceConnectionFactory(profile=profile, service=service) user = user_gql_client.user user.groups.add(group) assign_perm("can_view_profiles", group, service) # serviceType is included in query just to ensure that it has NO affect query = """ { profiles(serviceType: GODCHILDREN_OF_CULTURE) { edges { node { firstName } } } } """ expected_data = { "profiles": { "edges": [{ "node": { "firstName": profile.first_name } }] } } executed = user_gql_client.execute(query, service=service) assert executed["data"] == expected_data
def download_verified_personal_information_with_loa( loa, user_gql_client, service, mocker ): profile = VerifiedPersonalInformationFactory( profile__user=user_gql_client.user ).profile mocker.patch.object(TunnistamoTokenExchange, "fetch_api_tokens", return_value=None) ServiceConnectionFactory(profile=profile, service=service) token_payload = { "loa": loa, } executed = user_gql_client.execute( DOWNLOAD_MY_PROFILE_MUTATION, service=service, auth_token_payload=token_payload ) full_dump = json.loads(executed["data"]["downloadMyProfile"]) profile_dump = next( child for child in full_dump["children"] if child["key"] == "PROFILE" ) vpi_dump = next( child for child in profile_dump["children"] if child["key"] == "VERIFIEDPERSONALINFORMATION" ) return vpi_dump
def test_normal_user_can_create_temporary_read_access_token_for_profile( self, user_gql_client, service, with_serviceconnection): profile = ProfileFactory(user=user_gql_client.user) if with_serviceconnection: ServiceConnectionFactory(profile=profile, service=service) executed = user_gql_client.execute(self.query, service=service) if with_serviceconnection: token_data = executed["data"][ "createMyProfileTemporaryReadAccessToken"][ "temporaryReadAccessToken"] # Check that an UUID can be parsed from the token uuid.UUID(token_data["token"]) actual_expiration_time = datetime.fromisoformat( token_data["expiresAt"]) expected_expiration_time = timezone.now() + timedelta(days=2) assert_almost_equal(actual_expiration_time, expected_expiration_time, timedelta(seconds=1)) else: assert_match_error_code(executed, "PERMISSION_DENIED_ERROR") assert executed["data"][ "createMyProfileTemporaryReadAccessToken"] is None
def test_staff_user_can_query_a_profile_connected_to_service_he_is_admin_of( user_gql_client, profile, group, service): ServiceConnectionFactory(profile=profile, service=service) user = user_gql_client.user user.groups.add(group) assign_perm("can_view_profiles", group, service) # serviceType is included in query just to ensure that it has NO affect t = Template(""" { profile(id: "${id}", serviceType: GODCHILDREN_OF_CULTURE) { firstName lastName } } """) query = t.substitute(id=relay.Node.to_global_id(ProfileNode._meta.name, profile.id), ) expected_data = { "profile": { "firstName": profile.first_name, "lastName": profile.last_name } } executed = user_gql_client.execute(query, service=service) assert executed["data"] == expected_data
def test_normal_user_cannot_update_a_profile_using_update_profile_mutation( user_gql_client, service): profile = ProfileWithPrimaryEmailFactory(first_name="Joe") ServiceConnectionFactory(profile=profile, service=service) t = Template(""" mutation { updateProfile( input: { profile: { id: "${id}", firstName: "${first_name}", } } ) { profile { firstName } } } """) query = t.substitute( id=to_global_id("ProfileNode", profile.pk), first_name="John", ) executed = user_gql_client.execute(query, service=service) assert "errors" in executed assert executed["errors"][0]["message"] == _( "You do not have permission to perform this action.") assert Profile.objects.get(pk=profile.pk).first_name == profile.first_name
def test_staff_user_needs_required_permission_to_access_verified_personal_information( has_needed_permission, amr_claim_value, settings, user_gql_client, profile_with_verified_personal_information, group, service, ): settings.VERIFIED_PERSONAL_INFORMATION_ACCESS_AMR_LIST = [ "authmethod1", "authmethod2", ] ServiceConnectionFactory( profile=profile_with_verified_personal_information, service=service) user = user_gql_client.user user.groups.add(group) assign_perm("can_view_profiles", group, service) if has_needed_permission: assign_perm("can_view_verified_personal_information", group, service) t = Template(""" { profile(id: "${id}") { verifiedPersonalInformation { firstName } } } """) query = t.substitute(id=relay.Node.to_global_id( ProfileNode._meta.name, profile_with_verified_personal_information.id)) token_payload = {"loa": "substantial", "amr": amr_claim_value} executed = user_gql_client.execute(query, auth_token_payload=token_payload, service=service) if (has_needed_permission and amr_claim_value in settings.VERIFIED_PERSONAL_INFORMATION_ACCESS_AMR_LIST): assert "errors" not in executed assert executed["data"] == { "profile": { "verifiedPersonalInformation": { "firstName": profile_with_verified_personal_information. verified_personal_information.first_name } } } else: assert_match_error_code(executed, "PERMISSION_DENIED_ERROR") assert executed["data"] == { "profile": { "verifiedPersonalInformation": None } }
def test_language_argument_overrides_header_language(live_server, profile, service_client_id): service = service_client_id.service _set_service_title_and_description(service) ServiceConnectionFactory(profile=profile, service=service) user = profile.user query = """ { myProfile { serviceConnections { edges { node { service { title(language: EN) description } } } } } } """ result_data, errors = do_graphql_call_as_user( live_server, user, service=service, query=query, extra_request_args={"headers": { "Accept-Language": "fi" }}, ) assert not errors, errors expected_title = service.safe_translation_getter("title", language_code="en") expected_description = service.safe_translation_getter("description", language_code="fi") expected_data = { "myProfile": { "serviceConnections": { "edges": [{ "node": { "service": { "title": expected_title, "description": expected_description, }, } }] } } } assert result_data == expected_data
def test_staff_user_with_group_access_can_query_only_profiles_he_has_access_to( user_gql_client, group, service_factory): user = user_gql_client.user user.groups.add(group) entitled_profile = ProfileFactory() entitled_service = service_factory() ServiceConnectionFactory(profile=entitled_profile, service=entitled_service) assign_perm("can_view_profiles", group, entitled_service) unentitled_profile = ProfileFactory() unentitled_service = service_factory() ServiceConnectionFactory(profile=unentitled_profile, service=unentitled_service) query = """ { profiles { edges { node { firstName } } } } """ executed = user_gql_client.execute(query, service=entitled_service) expected_data = { "profiles": { "edges": [{ "node": { "firstName": entitled_profile.first_name } }] } } assert executed["data"] == expected_data executed = user_gql_client.execute(query, service=unentitled_service) assert "errors" in executed assert executed["errors"][0]["message"] == _( "You do not have permission to perform this action.")
def test_staff_user_can_paginate_profiles(order_by, expected_order, user_gql_client, group, service): for profile in ( ProfileFactory( id=uuid.UUID("22222222-2222-2222-2222-222222222222"), first_name="Clive", last_name="Tester", ), ProfileFactory( id=uuid.UUID("33333333-3333-3333-3333-333333333333"), first_name="Adam", last_name="Tester", ), ProfileFactory( id=uuid.UUID("11111111-1111-1111-1111-111111111111"), first_name="Bryan", last_name="Tester", ), ): ServiceConnectionFactory(profile=profile, service=service) user = user_gql_client.user user.groups.add(group) assign_perm("can_view_profiles", group, service) query = """ query getProfiles($orderBy: String, $after: String) { profiles(orderBy: $orderBy, first: 1, after: $after) { pageInfo { endCursor } edges { node { firstName } } } } """ end_cursor = None for expected_first_name in expected_order: executed = user_gql_client.execute(query, variables={ "orderBy": order_by, "after": end_cursor }, service=service) expected_edges = [{"node": {"firstName": expected_first_name}}] assert "data" in executed assert executed["data"]["profiles"]["edges"] == expected_edges assert "pageInfo" in executed["data"]["profiles"] assert "endCursor" in executed["data"]["profiles"]["pageInfo"] end_cursor = executed["data"]["profiles"]["pageInfo"]["endCursor"]
def test_staff_user_filter_profiles_by_verified_personal_information_permissions( has_needed_permission, amr_claim_value, settings, user_gql_client, group, service): settings.VERIFIED_PERSONAL_INFORMATION_ACCESS_AMR_LIST = [ "authmethod1", "authmethod2", ] vpi = VerifiedPersonalInformationFactory() ServiceConnectionFactory(profile=vpi.profile, service=service) user = user_gql_client.user user.groups.add(group) assign_perm("can_view_profiles", group, service) if has_needed_permission: assign_perm("can_view_verified_personal_information", group, service) gql_field_name = to_graphql_name("national_identification_number") query = query_template.substitute(search_arg_name=gql_field_name) expected_data_no_permission = { "profiles": { "count": 0, "totalCount": 1, "edges": [] } } expected_data_with_permission = { "profiles": { "count": 1, "totalCount": 1, "edges": [{ "node": { "firstName": vpi.profile.first_name } }], } } token_payload = {"amr": amr_claim_value} executed = user_gql_client.execute( query, variables={"searchString": vpi.national_identification_number}, auth_token_payload=token_payload, service=service, ) assert "errors" not in executed if (has_needed_permission and amr_claim_value in settings.VERIFIED_PERSONAL_INFORMATION_ACCESS_AMR_LIST): assert executed["data"] == expected_data_with_permission else: assert executed["data"] == expected_data_no_permission
def test_api_tokens_missing(user_gql_client, service_1, query_or_delete, mocker): """Missing API token for a service connection that has the query/delete scope set, should be an error.""" mocker.patch.object(TunnistamoTokenExchange, "fetch_api_tokens", return_value={}) profile = ProfileFactory(user=user_gql_client.user) ServiceConnectionFactory(profile=profile, service=service_1) if query_or_delete == "query": executed = user_gql_client.execute(DOWNLOAD_MY_PROFILE_MUTATION) else: executed = user_gql_client.execute(DELETE_MY_PROFILE_MUTATION) assert_match_error_code(executed, MISSING_GDPR_API_TOKEN_ERROR)
def test_normal_user_can_query_own_services_gdpr_api_scopes( user_gql_client, service_factory, ): query_scope = "query_scope" delete_scope = "delete_scope" service = service_factory( service_type=ServiceType.BERTH, gdpr_query_scope=query_scope, gdpr_delete_scope=delete_scope, ) profile = ProfileFactory(user=user_gql_client.user) ServiceConnectionFactory(profile=profile, service=service) query = """ { myProfile { serviceConnections { edges { node { service { type name gdprQueryScope gdprDeleteScope } } } } } } """ expected_data = { "myProfile": { "serviceConnections": { "edges": [{ "node": { "service": { "type": service.service_type.name, "name": service.name, "gdprQueryScope": query_scope, "gdprDeleteScope": delete_scope, } } }] } } } executed = user_gql_client.execute(query) assert dict(executed["data"]) == expected_data
def test_service_connections_are_ordered_by_id(user_gql_client): profile = ProfileFactory(user=user_gql_client.user) connections = [ ServiceConnectionFactory(profile=profile, service=ServiceFactory()) for _ in range(3) ] executed = user_gql_client.execute(QUERY) connection_edges = executed["data"]["myProfile"]["serviceConnections"]["edges"] assert len(connection_edges) == len(connections) for edge, connection in zip(connection_edges, connections): assert from_global_id(edge["node"]["id"])[1] == str(connection.id)
def test_remove_service_gdpr_data_no_url(profile, service): service_connection = ServiceConnectionFactory(profile=profile, service=service) with pytest.raises(MissingGDPRUrlException): service_connection.delete_gdpr_data(dry_run=True) with pytest.raises(MissingGDPRUrlException): service_connection.delete_gdpr_data()
def test_staff_user_can_sort_profiles(user_gql_client, group, service): profile_1, profile_2 = ( ProfileFactory(first_name="Adam", last_name="Tester"), ProfileFactory(first_name="Bryan", last_name="Tester"), ) ServiceConnectionFactory(profile=profile_1, service=service) ServiceConnectionFactory(profile=profile_2, service=service) user = user_gql_client.user user.groups.add(group) assign_perm("can_view_profiles", group, service) query = """ query getProfiles { profiles(orderBy: "-firstName") { edges { node { firstName } } } } """ expected_data = { "profiles": { "edges": [{ "node": { "firstName": "Bryan" } }, { "node": { "firstName": "Adam" } }] } } executed = user_gql_client.execute(query, service=service) assert executed["data"] == expected_data