|
| 1 | +import json |
| 2 | + |
1 | 3 | import pytest |
2 | 4 | from django.test import Client |
3 | 5 |
|
4 | 6 | from ...settings import graphene_settings |
5 | 7 | from ...tests.test_types import with_local_registry |
6 | 8 | from .. import GraphQLTestCase |
| 9 | +from ..testing import GraphQLTestMixin |
7 | 10 |
|
8 | 11 |
|
9 | 12 | @with_local_registry |
@@ -52,3 +55,147 @@ def test_graphql_test_case_imports_endpoint(): |
52 | 55 | """ |
53 | 56 |
|
54 | 57 | assert GraphQLTestCase.GRAPHQL_URL == graphene_settings.TESTING_ENDPOINT |
| 58 | + |
| 59 | + |
| 60 | +class _FakeHttpResponse: |
| 61 | + """Minimal stand-in for ``django.http.HttpResponse``. |
| 62 | +
|
| 63 | + The ``assertResponse*`` helpers only touch ``.status_code`` and |
| 64 | + ``.content``, so we don't need the full Django response machinery to |
| 65 | + exercise them. Building this stand-in keeps the alias tests free of |
| 66 | + any dependency on routing, schemas, or the test client. |
| 67 | + """ |
| 68 | + |
| 69 | + def __init__(self, content_dict, status_code=200): |
| 70 | + self.content = json.dumps(content_dict).encode("utf-8") |
| 71 | + self.status_code = status_code |
| 72 | + |
| 73 | + |
| 74 | +class TestAssertionAliases: |
| 75 | + """Coverage for the snake_case aliases in :mod:`graphene_django.utils.testing`. |
| 76 | +
|
| 77 | + The PR adds ``assert_response_no_errors`` / |
| 78 | + ``assert_response_has_errors`` as additive aliases for the existing |
| 79 | + camelCase ``assertResponseNoErrors`` / ``assertResponseHasErrors``. |
| 80 | + These tests pin three properties of that contract: |
| 81 | +
|
| 82 | + 1. The aliases must point at the **same callable** as the |
| 83 | + camelCase originals (no copy-paste re-implementation that could |
| 84 | + drift out of sync over time). |
| 85 | + 2. The aliases must work end-to-end on a "no errors" response. |
| 86 | + 3. The aliases must work end-to-end on a "with errors" response. |
| 87 | +
|
| 88 | + The end-to-end checks build a tiny ``GraphQLTestCase`` subclass and |
| 89 | + invoke each alias against a synthesised :class:`_FakeHttpResponse`, |
| 90 | + so neither GraphiQL routing nor a real schema needs to be involved. |
| 91 | + """ |
| 92 | + |
| 93 | + def test_alias_is_identical_to_camel_case_original(self): |
| 94 | + """ |
| 95 | + Name: alias identity check |
| 96 | + Description: ``assert_response_no_errors`` and |
| 97 | + ``assert_response_has_errors`` must be the **same** function |
| 98 | + objects as their camelCase counterparts so behaviour cannot |
| 99 | + drift between spellings. |
| 100 | + Assumptions: Both spellings are defined on |
| 101 | + :class:`GraphQLTestMixin`. |
| 102 | + Expectations: ``is`` identity holds for both pairs. |
| 103 | + """ |
| 104 | + assert ( |
| 105 | + GraphQLTestMixin.assert_response_no_errors |
| 106 | + is GraphQLTestMixin.assertResponseNoErrors |
| 107 | + ) |
| 108 | + assert ( |
| 109 | + GraphQLTestMixin.assert_response_has_errors |
| 110 | + is GraphQLTestMixin.assertResponseHasErrors |
| 111 | + ) |
| 112 | + |
| 113 | + def test_assert_response_no_errors_alias_passes_for_clean_response(self): |
| 114 | + """ |
| 115 | + Name: snake_case no-errors helper, clean response |
| 116 | + Description: ``assert_response_no_errors`` should silently accept a |
| 117 | + 200 response whose JSON body has no ``errors`` key, exactly |
| 118 | + like its camelCase counterpart. |
| 119 | + Assumptions: A bare ``{"data": {...}}`` body with status 200 is the |
| 120 | + canonical "clean" GraphQL response. |
| 121 | + Expectations: The call returns without raising. |
| 122 | + """ |
| 123 | + |
| 124 | + class _TC(GraphQLTestCase): |
| 125 | + GRAPHQL_SCHEMA = True |
| 126 | + |
| 127 | + def runTest(self): |
| 128 | + pass |
| 129 | + |
| 130 | + tc = _TC() |
| 131 | + resp = _FakeHttpResponse({"data": {"hello": "world"}}, status_code=200) |
| 132 | + tc.assert_response_no_errors(resp) |
| 133 | + |
| 134 | + def test_assert_response_no_errors_alias_fails_when_errors_present(self): |
| 135 | + """ |
| 136 | + Name: snake_case no-errors helper, errors present |
| 137 | + Description: ``assert_response_no_errors`` should fail when the |
| 138 | + response contains an ``errors`` key, exactly like its |
| 139 | + camelCase counterpart. |
| 140 | + Assumptions: ``unittest`` assertion failures raise |
| 141 | + :class:`AssertionError`. |
| 142 | + Expectations: ``AssertionError`` is raised. |
| 143 | + """ |
| 144 | + |
| 145 | + class _TC(GraphQLTestCase): |
| 146 | + GRAPHQL_SCHEMA = True |
| 147 | + |
| 148 | + def runTest(self): |
| 149 | + pass |
| 150 | + |
| 151 | + tc = _TC() |
| 152 | + resp = _FakeHttpResponse( |
| 153 | + {"errors": [{"message": "boom"}]}, status_code=200 |
| 154 | + ) |
| 155 | + with pytest.raises(AssertionError): |
| 156 | + tc.assert_response_no_errors(resp) |
| 157 | + |
| 158 | + def test_assert_response_has_errors_alias_passes_when_errors_present(self): |
| 159 | + """ |
| 160 | + Name: snake_case has-errors helper, errors present |
| 161 | + Description: ``assert_response_has_errors`` should silently accept |
| 162 | + a response whose body has an ``errors`` key, regardless of |
| 163 | + status code (GraphQL returns 200 even on errors). |
| 164 | + Assumptions: An ``{"errors": [...]}``-shaped response represents |
| 165 | + a failing GraphQL query. |
| 166 | + Expectations: The call returns without raising. |
| 167 | + """ |
| 168 | + |
| 169 | + class _TC(GraphQLTestCase): |
| 170 | + GRAPHQL_SCHEMA = True |
| 171 | + |
| 172 | + def runTest(self): |
| 173 | + pass |
| 174 | + |
| 175 | + tc = _TC() |
| 176 | + resp = _FakeHttpResponse( |
| 177 | + {"errors": [{"message": "boom"}]}, status_code=200 |
| 178 | + ) |
| 179 | + tc.assert_response_has_errors(resp) |
| 180 | + |
| 181 | + def test_assert_response_has_errors_alias_fails_for_clean_response(self): |
| 182 | + """ |
| 183 | + Name: snake_case has-errors helper, clean response |
| 184 | + Description: ``assert_response_has_errors`` should fail when the |
| 185 | + response has no ``errors`` key, exactly like its camelCase |
| 186 | + counterpart. |
| 187 | + Assumptions: ``unittest`` assertion failures raise |
| 188 | + :class:`AssertionError`. |
| 189 | + Expectations: ``AssertionError`` is raised. |
| 190 | + """ |
| 191 | + |
| 192 | + class _TC(GraphQLTestCase): |
| 193 | + GRAPHQL_SCHEMA = True |
| 194 | + |
| 195 | + def runTest(self): |
| 196 | + pass |
| 197 | + |
| 198 | + tc = _TC() |
| 199 | + resp = _FakeHttpResponse({"data": {"hello": "world"}}, status_code=200) |
| 200 | + with pytest.raises(AssertionError): |
| 201 | + tc.assert_response_has_errors(resp) |
0 commit comments