mirror of
https://github.com/netbox-community/netbox.git
synced 2026-02-04 14:26:25 -06:00
19724 add the v2 to graphql testing
This commit is contained in:
@@ -515,10 +515,15 @@ class APIViewTestCases:
|
|||||||
base_name = self.model._meta.verbose_name.lower().replace(' ', '_')
|
base_name = self.model._meta.verbose_name.lower().replace(' ', '_')
|
||||||
return getattr(self, 'graphql_base_name', base_name)
|
return getattr(self, 'graphql_base_name', base_name)
|
||||||
|
|
||||||
def _build_query_with_filter(self, name, filter_string):
|
def _build_query_with_filter(self, name, filter_string, api_version='v2'):
|
||||||
"""
|
"""
|
||||||
Called by either _build_query or _build_filtered_query - construct the actual
|
Called by either _build_query or _build_filtered_query - construct the actual
|
||||||
query given a name and filter string
|
query given a name and filter string
|
||||||
|
|
||||||
|
Args:
|
||||||
|
name: The query field name (e.g., 'device_list')
|
||||||
|
filter_string: Filter parameters string (e.g., '(filters: {id: "1"})')
|
||||||
|
api_version: 'v1' or 'v2' to determine response format
|
||||||
"""
|
"""
|
||||||
type_class = get_graphql_type_for_model(self.model)
|
type_class = get_graphql_type_for_model(self.model)
|
||||||
|
|
||||||
@@ -564,7 +569,8 @@ class APIViewTestCases:
|
|||||||
|
|
||||||
# Check if this is a list query (ends with '_list')
|
# Check if this is a list query (ends with '_list')
|
||||||
if name.endswith('_list'):
|
if name.endswith('_list'):
|
||||||
# Wrap fields in 'results' for paginated queries
|
if api_version == 'v2':
|
||||||
|
# v2: Wrap fields in 'results' for paginated queries
|
||||||
query = f"""
|
query = f"""
|
||||||
{{
|
{{
|
||||||
{name}{filter_string} {{
|
{name}{filter_string} {{
|
||||||
@@ -574,6 +580,15 @@ class APIViewTestCases:
|
|||||||
}}
|
}}
|
||||||
}}
|
}}
|
||||||
"""
|
"""
|
||||||
|
else:
|
||||||
|
# v1: Return direct array (no 'results' wrapper)
|
||||||
|
query = f"""
|
||||||
|
{{
|
||||||
|
{name}{filter_string} {{
|
||||||
|
{fields_string}
|
||||||
|
}}
|
||||||
|
}}
|
||||||
|
"""
|
||||||
else:
|
else:
|
||||||
# Single object query (no pagination)
|
# Single object query (no pagination)
|
||||||
query = f"""
|
query = f"""
|
||||||
@@ -586,9 +601,14 @@ class APIViewTestCases:
|
|||||||
|
|
||||||
return query
|
return query
|
||||||
|
|
||||||
def _build_filtered_query(self, name, **filters):
|
def _build_filtered_query(self, name, api_version='v2', **filters):
|
||||||
"""
|
"""
|
||||||
Create a filtered query: i.e. device_list(filters: {name: {i_contains: "akron"}}){.
|
Create a filtered query: i.e. device_list(filters: {name: {i_contains: "akron"}}){.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
name: The query field name
|
||||||
|
api_version: 'v1' or 'v2' to determine response format
|
||||||
|
**filters: Filter parameters
|
||||||
"""
|
"""
|
||||||
# TODO: This should be extended to support AND, OR multi-lookups
|
# TODO: This should be extended to support AND, OR multi-lookups
|
||||||
if filters:
|
if filters:
|
||||||
@@ -604,11 +624,16 @@ class APIViewTestCases:
|
|||||||
else:
|
else:
|
||||||
filter_string = ''
|
filter_string = ''
|
||||||
|
|
||||||
return self._build_query_with_filter(name, filter_string)
|
return self._build_query_with_filter(name, filter_string, api_version)
|
||||||
|
|
||||||
def _build_query(self, name, **filters):
|
def _build_query(self, name, api_version='v2', **filters):
|
||||||
"""
|
"""
|
||||||
Create a normal query - unfiltered or with a string query: i.e. site(name: "aaa"){.
|
Create a normal query - unfiltered or with a string query: i.e. site(name: "aaa"){.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
name: The query field name
|
||||||
|
api_version: 'v1' or 'v2' to determine response format
|
||||||
|
**filters: Filter parameters
|
||||||
"""
|
"""
|
||||||
if filters:
|
if filters:
|
||||||
filter_string = ', '.join(f'{k}:{v}' for k, v in filters.items())
|
filter_string = ', '.join(f'{k}:{v}' for k, v in filters.items())
|
||||||
@@ -616,7 +641,7 @@ class APIViewTestCases:
|
|||||||
else:
|
else:
|
||||||
filter_string = ''
|
filter_string = ''
|
||||||
|
|
||||||
return self._build_query_with_filter(name, filter_string)
|
return self._build_query_with_filter(name, filter_string, api_version)
|
||||||
|
|
||||||
@override_settings(LOGIN_REQUIRED=True)
|
@override_settings(LOGIN_REQUIRED=True)
|
||||||
def test_graphql_get_object(self):
|
def test_graphql_get_object(self):
|
||||||
@@ -664,9 +689,13 @@ class APIViewTestCases:
|
|||||||
|
|
||||||
@override_settings(LOGIN_REQUIRED=True)
|
@override_settings(LOGIN_REQUIRED=True)
|
||||||
def test_graphql_list_objects(self):
|
def test_graphql_list_objects(self):
|
||||||
url = reverse('graphql_v2')
|
|
||||||
field_name = f'{self._get_graphql_base_name()}_list'
|
field_name = f'{self._get_graphql_base_name()}_list'
|
||||||
query = self._build_query(field_name)
|
|
||||||
|
# Test both GraphQL API versions
|
||||||
|
for api_version, url_name in [('v1', 'graphql_v1'), ('v2', 'graphql_v2')]:
|
||||||
|
with self.subTest(api_version=api_version):
|
||||||
|
url = reverse(url_name)
|
||||||
|
query = self._build_query(field_name, api_version=api_version)
|
||||||
|
|
||||||
# Non-authenticated requests should fail
|
# Non-authenticated requests should fail
|
||||||
header = {
|
header = {
|
||||||
@@ -691,6 +720,12 @@ class APIViewTestCases:
|
|||||||
self.assertHttpStatus(response, status.HTTP_200_OK)
|
self.assertHttpStatus(response, status.HTTP_200_OK)
|
||||||
data = json.loads(response.content)
|
data = json.loads(response.content)
|
||||||
self.assertNotIn('errors', data)
|
self.assertNotIn('errors', data)
|
||||||
|
|
||||||
|
if api_version == 'v1':
|
||||||
|
# v1 returns direct array
|
||||||
|
self.assertEqual(len(data['data'][field_name]), 0)
|
||||||
|
else:
|
||||||
|
# v2 returns paginated response with results
|
||||||
self.assertEqual(len(data['data'][field_name]['results']), 0)
|
self.assertEqual(len(data['data'][field_name]['results']), 0)
|
||||||
|
|
||||||
# Remove permission constraint
|
# Remove permission constraint
|
||||||
@@ -702,16 +737,23 @@ class APIViewTestCases:
|
|||||||
self.assertHttpStatus(response, status.HTTP_200_OK)
|
self.assertHttpStatus(response, status.HTTP_200_OK)
|
||||||
data = json.loads(response.content)
|
data = json.loads(response.content)
|
||||||
self.assertNotIn('errors', data)
|
self.assertNotIn('errors', data)
|
||||||
|
|
||||||
|
if api_version == 'v1':
|
||||||
|
# v1 returns direct array
|
||||||
|
self.assertEqual(len(data['data'][field_name]), self.model.objects.count())
|
||||||
|
else:
|
||||||
|
# v2 returns paginated response with results
|
||||||
self.assertEqual(len(data['data'][field_name]['results']), self.model.objects.count())
|
self.assertEqual(len(data['data'][field_name]['results']), self.model.objects.count())
|
||||||
|
|
||||||
|
# Clean up permission for next iteration
|
||||||
|
obj_perm.delete()
|
||||||
|
|
||||||
@override_settings(LOGIN_REQUIRED=True)
|
@override_settings(LOGIN_REQUIRED=True)
|
||||||
def test_graphql_filter_objects(self):
|
def test_graphql_filter_objects(self):
|
||||||
if not hasattr(self, 'graphql_filter'):
|
if not hasattr(self, 'graphql_filter'):
|
||||||
return
|
return
|
||||||
|
|
||||||
url = reverse('graphql_v2')
|
|
||||||
field_name = f'{self._get_graphql_base_name()}_list'
|
field_name = f'{self._get_graphql_base_name()}_list'
|
||||||
query = self._build_filtered_query(field_name, **self.graphql_filter)
|
|
||||||
|
|
||||||
# Add object-level permission
|
# Add object-level permission
|
||||||
obj_perm = ObjectPermission(
|
obj_perm = ObjectPermission(
|
||||||
@@ -722,12 +764,27 @@ class APIViewTestCases:
|
|||||||
obj_perm.users.add(self.user)
|
obj_perm.users.add(self.user)
|
||||||
obj_perm.object_types.add(ObjectType.objects.get_for_model(self.model))
|
obj_perm.object_types.add(ObjectType.objects.get_for_model(self.model))
|
||||||
|
|
||||||
|
# Test both GraphQL API versions
|
||||||
|
for api_version, url_name in [('v1', 'graphql_v1'), ('v2', 'graphql_v2')]:
|
||||||
|
with self.subTest(api_version=api_version):
|
||||||
|
url = reverse(url_name)
|
||||||
|
query = self._build_filtered_query(field_name, api_version=api_version, **self.graphql_filter)
|
||||||
|
|
||||||
response = self.client.post(url, data={'query': query}, format="json", **self.header)
|
response = self.client.post(url, data={'query': query}, format="json", **self.header)
|
||||||
self.assertHttpStatus(response, status.HTTP_200_OK)
|
self.assertHttpStatus(response, status.HTTP_200_OK)
|
||||||
data = json.loads(response.content)
|
data = json.loads(response.content)
|
||||||
self.assertNotIn('errors', data)
|
self.assertNotIn('errors', data)
|
||||||
|
|
||||||
|
if api_version == 'v1':
|
||||||
|
# v1 returns direct array
|
||||||
|
self.assertGreater(len(data['data'][field_name]), 0)
|
||||||
|
else:
|
||||||
|
# v2 returns paginated response with results
|
||||||
self.assertGreater(len(data['data'][field_name]['results']), 0)
|
self.assertGreater(len(data['data'][field_name]['results']), 0)
|
||||||
|
|
||||||
|
# Clean up permission
|
||||||
|
obj_perm.delete()
|
||||||
|
|
||||||
class APIViewTestCase(
|
class APIViewTestCase(
|
||||||
GetObjectViewTestCase,
|
GetObjectViewTestCase,
|
||||||
ListObjectsViewTestCase,
|
ListObjectsViewTestCase,
|
||||||
|
|||||||
Reference in New Issue
Block a user