Переглянути джерело

Closes #22305: Allow test cases to declare a stable query-count key prefix (#22306)

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
bctiemann 1 день тому
батько
коміт
1c3ddcd97a

+ 10 - 2
netbox/utilities/testing/query_counts.py

@@ -86,7 +86,14 @@ def assert_expected_query_count(test_case, name):
     Assert that the wrapped block performs the number of SQL queries recorded
     in the per-app baseline file (`<app>/tests/query_counts.json`).
 
-    The baseline key is `<model_name>:<name>`, derived from `test_case.model`.
+    The baseline key is `<model_label>:<name>`. By default `<model_label>` is
+    derived from `test_case.model._meta.model_name`. Test cases that use
+    runtime-generated models with unstable names (e.g. names derived from a
+    database primary-key sequence) can declare a ``query_count_model_label``
+    class attribute to provide a stable, human-assigned label instead:
+
+        class MyViewTestCase(ViewTestCases.PrimaryObjectViewTestCase):
+            query_count_model_label = 'my-stable-label'
 
     When the `UPDATE_QUERY_COUNTS` environment variable is set, the assertion
     is skipped and the observed count is written back to the baseline file
@@ -94,7 +101,8 @@ def assert_expected_query_count(test_case, name):
     """
     model = test_case.model
     app_label = model._meta.app_label
-    model_name = model._meta.model_name
+    label = getattr(test_case, 'query_count_model_label', None)
+    model_name = label if label is not None else model._meta.model_name
     key = f'{model_name}:{name}'
 
     if _is_update_mode():

+ 84 - 0
netbox/utilities/tests/test_query_counts.py

@@ -0,0 +1,84 @@
+import os
+
+from django.test import TestCase
+
+import utilities.testing.query_counts as qc_mod
+from utilities.testing.query_counts import assert_expected_query_count
+
+
+class _FakeModel:
+    """Minimal stand-in for a Django model class with _meta."""
+
+    class _meta:
+        app_label = 'utilities'
+        model_name = 'fakemodel'
+
+
+class _BaseTestCase:
+    """Shared base for fake test-case objects passed to assert_expected_query_count."""
+    model = _FakeModel
+
+
+class AssertExpectedQueryCountLabelTestCase(TestCase):
+    """
+    Verify that assert_expected_query_count uses query_count_model_label when
+    set on the test case and falls back to model._meta.model_name otherwise.
+    """
+
+    def _make_test_case(self, label_value=None, set_attr=False):
+        """Return a minimal fake test case object."""
+        tc = type('FakeTestCase', (_BaseTestCase,), {'fail': self.fail})()
+        if set_attr:
+            tc.query_count_model_label = label_value
+        return tc
+
+    def _recorded_key(self, test_case):
+        """
+        Drive assert_expected_query_count in UPDATE mode and return the key it
+        wrote.  We patch _record_update to capture the key without touching the
+        filesystem.
+        """
+        captured = {}
+
+        original = qc_mod._record_update
+
+        def fake_record(app_label, key, count):
+            captured['key'] = key
+
+        original_parallel = qc_mod._is_parallel_test_run
+        qc_mod._record_update = fake_record
+        qc_mod._is_parallel_test_run = lambda: False
+        try:
+            old_env = os.environ.get(qc_mod.UPDATE_ENV_VAR)
+            os.environ[qc_mod.UPDATE_ENV_VAR] = '1'
+            try:
+                with assert_expected_query_count(test_case, 'test_name'):
+                    pass
+            finally:
+                if old_env is None:
+                    del os.environ[qc_mod.UPDATE_ENV_VAR]
+                else:
+                    os.environ[qc_mod.UPDATE_ENV_VAR] = old_env
+        finally:
+            qc_mod._record_update = original
+            qc_mod._is_parallel_test_run = original_parallel
+
+        return captured['key']
+
+    def test_default_uses_model_name(self):
+        """Without query_count_model_label the key prefix is model._meta.model_name."""
+        tc = self._make_test_case()
+        key = self._recorded_key(tc)
+        self.assertEqual(key, 'fakemodel:test_name')
+
+    def test_custom_label_overrides_model_name(self):
+        """A set query_count_model_label is used as the key prefix."""
+        tc = self._make_test_case(label_value='my-stable-label', set_attr=True)
+        key = self._recorded_key(tc)
+        self.assertEqual(key, 'my-stable-label:test_name')
+
+    def test_empty_string_label_is_used_as_prefix(self):
+        """An empty-string query_count_model_label is used as-is, not treated as falsy."""
+        tc = self._make_test_case(label_value='', set_attr=True)
+        key = self._recorded_key(tc)
+        self.assertEqual(key, ':test_name')