diff --git a/netbox/netbox/tests/test_graphql.py b/netbox/netbox/tests/test_graphql.py index 9fc7e7e96..2b236fee4 100644 --- a/netbox/netbox/tests/test_graphql.py +++ b/netbox/netbox/tests/test_graphql.py @@ -46,9 +46,9 @@ class GraphQLTestCase(TestCase): class GraphQLAPITestCase(APITestCase): @override_settings(LOGIN_REQUIRED=True) - def test_graphql_filter_objects(self): + def test_graphql_filter_objects_v1(self): """ - Test the operation of filters for GraphQL API requests. + Test the operation of filters for GraphQL API v1 requests (old format with List[Type]). """ sites = ( Site(name='Site 1', slug='site-1'), @@ -85,7 +85,89 @@ class GraphQLAPITestCase(APITestCase): obj_perm.object_types.add(ObjectType.objects.get_for_model(Location)) obj_perm.object_types.add(ObjectType.objects.get_for_model(Site)) - url = reverse('graphql') + url = reverse('graphql_v1') + + # A valid request should return the filtered list + query = '{location_list(filters: {site_id: "' + str(sites[0].pk) + '"}) {id site {id}}}' + response = self.client.post(url, data={'query': query}, format="json", **self.header) + self.assertHttpStatus(response, status.HTTP_200_OK) + data = json.loads(response.content) + self.assertNotIn('errors', data) + self.assertEqual(len(data['data']['location_list']), 1) + self.assertIsNotNone(data['data']['location_list'][0]['site']) + + # Test OR logic + query = """{ + location_list( filters: { + status: STATUS_PLANNED, + OR: {status: STATUS_STAGING} + }) { + id site {id} + } + }""" + response = self.client.post(url, data={'query': query}, format="json", **self.header) + self.assertHttpStatus(response, status.HTTP_200_OK) + data = json.loads(response.content) + self.assertNotIn('errors', data) + self.assertEqual(len(data['data']['location_list']), 2) + + # An invalid request should return an empty list + query = '{location_list(filters: {site_id: "99999"}) {id site {id}}}' # Invalid site ID + response = self.client.post(url, data={'query': query}, format="json", **self.header) + self.assertHttpStatus(response, status.HTTP_200_OK) + data = json.loads(response.content) + self.assertEqual(len(data['data']['location_list']), 0) + + # Removing the permissions from location should result in an empty locations list + obj_perm.object_types.remove(ObjectType.objects.get_for_model(Location)) + query = '{site(id: ' + str(sites[0].pk) + ') {id locations {id}}}' + response = self.client.post(url, data={'query': query}, format="json", **self.header) + self.assertHttpStatus(response, status.HTTP_200_OK) + data = json.loads(response.content) + self.assertNotIn('errors', data) + self.assertEqual(len(data['data']['site']['locations']), 0) + + @override_settings(LOGIN_REQUIRED=True) + def test_graphql_filter_objects(self): + """ + Test the operation of filters for GraphQL API v2 requests (new format with OffsetPaginated). + """ + sites = ( + Site(name='Site 1', slug='site-1'), + Site(name='Site 2', slug='site-2'), + Site(name='Site 3', slug='site-3'), + ) + Site.objects.bulk_create(sites) + Location.objects.create( + site=sites[0], + name='Location 1', + slug='location-1', + status=LocationStatusChoices.STATUS_PLANNED + ), + Location.objects.create( + site=sites[1], + name='Location 2', + slug='location-2', + status=LocationStatusChoices.STATUS_STAGING + ), + Location.objects.create( + site=sites[1], + name='Location 3', + slug='location-3', + status=LocationStatusChoices.STATUS_ACTIVE + ), + + # Add object-level permission + obj_perm = ObjectPermission( + name='Test permission', + actions=['view'] + ) + obj_perm.save() + obj_perm.users.add(self.user) + obj_perm.object_types.add(ObjectType.objects.get_for_model(Location)) + obj_perm.object_types.add(ObjectType.objects.get_for_model(Site)) + + url = reverse('graphql_v2') # A valid request should return the filtered list query = '{location_list(filters: {site_id: "' + str(sites[0].pk) + '"}) {results {id site {id}} total_count}}' diff --git a/netbox/utilities/testing/api.py b/netbox/utilities/testing/api.py index 29dcf8385..f97c194ef 100644 --- a/netbox/utilities/testing/api.py +++ b/netbox/utilities/testing/api.py @@ -664,7 +664,7 @@ class APIViewTestCases: @override_settings(LOGIN_REQUIRED=True) def test_graphql_list_objects(self): - url = reverse('graphql') + url = reverse('graphql_v2') field_name = f'{self._get_graphql_base_name()}_list' query = self._build_query(field_name) @@ -709,7 +709,7 @@ class APIViewTestCases: if not hasattr(self, 'graphql_filter'): return - url = reverse('graphql') + url = reverse('graphql_v2') field_name = f'{self._get_graphql_base_name()}_list' query = self._build_filtered_query(field_name, **self.graphql_filter)