cc-codeconductor 0.4.3 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (141) hide show
  1. package/README.md +1 -1
  2. package/dist/index.js +2642 -226
  3. package/package.json +1 -1
  4. package/presets/agy/skills/evaluation/SKILL.md +6 -0
  5. package/presets/agy/skills/openspec/SKILL.md +32 -0
  6. package/presets/agy/workflows/cc-openspec.md +62 -0
  7. package/presets/agy/workflows/cc-scorecard.md +17 -0
  8. package/presets/claude/commands/cc/openspec.md +144 -0
  9. package/presets/claude/commands/cc/review.md +13 -2
  10. package/presets/claude/commands/cc/scorecard.md +65 -0
  11. package/presets/claude/skills/evaluation/SKILL.md +42 -0
  12. package/presets/claude/skills/openspec/SKILL.md +54 -0
  13. package/presets/codex/AGENTS.md +57 -0
  14. package/presets/cursor/.cursorignore +15 -0
  15. package/presets/cursor/AGENTS.md +504 -0
  16. package/presets/cursor/agents/architect.md +211 -0
  17. package/presets/cursor/agents/complexity-auditor.md +76 -0
  18. package/presets/cursor/agents/contract-builder.md +75 -0
  19. package/presets/cursor/agents/docs.md +180 -0
  20. package/presets/cursor/agents/goal-planner.md +71 -0
  21. package/presets/cursor/agents/implementer.md +161 -0
  22. package/presets/cursor/agents/orchestrator.md +377 -0
  23. package/presets/cursor/agents/repo-explorer.md +100 -0
  24. package/presets/cursor/agents/reviewer.md +237 -0
  25. package/presets/cursor/agents/security-reviewer.md +113 -0
  26. package/presets/cursor/agents/task-coach.md +145 -0
  27. package/presets/cursor/agents/tester.md +241 -0
  28. package/presets/cursor/commands/cc/api-contract.md +58 -0
  29. package/presets/cursor/commands/cc/db-migration.md +58 -0
  30. package/presets/cursor/commands/cc/feature.md +115 -0
  31. package/presets/cursor/commands/cc/fix.md +121 -0
  32. package/presets/cursor/commands/cc/openspec.md +144 -0
  33. package/presets/cursor/commands/cc/pagespeed.md +103 -0
  34. package/presets/cursor/commands/cc/refactor.md +148 -0
  35. package/presets/cursor/commands/cc/review.md +137 -0
  36. package/presets/cursor/commands/cc/scorecard.md +65 -0
  37. package/presets/cursor/commands/cc/tdd-cycle.md +226 -0
  38. package/presets/cursor/commands/cc/test-plan.md +138 -0
  39. package/presets/cursor/rules/behavioral-discipline.mdc +14 -0
  40. package/presets/cursor/rules/context-budget.mdc +12 -0
  41. package/presets/cursor/rules/orchestration.mdc +12 -0
  42. package/presets/cursor/rules/yagni-stdlib.mdc +11 -0
  43. package/presets/cursor/skills/android/SKILL.md +122 -0
  44. package/presets/cursor/skills/api-versioning/SKILL.md +394 -0
  45. package/presets/cursor/skills/astro/SKILL.md +322 -0
  46. package/presets/cursor/skills/auth-token-inspector/SKILL.md +33 -0
  47. package/presets/cursor/skills/code-review/SKILL.md +208 -0
  48. package/presets/cursor/skills/conductor-setup/SKILL.md +127 -0
  49. package/presets/cursor/skills/django-orm/SKILL.md +463 -0
  50. package/presets/cursor/skills/django-testing/SKILL.md +417 -0
  51. package/presets/cursor/skills/django-uv/SKILL.md +409 -0
  52. package/presets/cursor/skills/drizzle-schema-architect/SKILL.md +54 -0
  53. package/presets/cursor/skills/evaluation/SKILL.md +8 -0
  54. package/presets/cursor/skills/fastapi-pydantic-strict/SKILL.md +46 -0
  55. package/presets/cursor/skills/find-skills/SKILL.md +144 -0
  56. package/presets/cursor/skills/jpa-nplusone-detector/SKILL.md +49 -0
  57. package/presets/cursor/skills/jpa-postgres/SKILL.md +626 -0
  58. package/presets/cursor/skills/laravel-specialist/SKILL.md +267 -0
  59. package/presets/cursor/skills/laravel-specialist/references/eloquent.md +351 -0
  60. package/presets/cursor/skills/laravel-specialist/references/livewire.md +512 -0
  61. package/presets/cursor/skills/laravel-specialist/references/queues.md +423 -0
  62. package/presets/cursor/skills/laravel-specialist/references/routing.md +362 -0
  63. package/presets/cursor/skills/laravel-specialist/references/testing.md +522 -0
  64. package/presets/cursor/skills/livewire-alpine-bridge/SKILL.md +39 -0
  65. package/presets/cursor/skills/multi-agent-orchestration/README.md +144 -0
  66. package/presets/cursor/skills/multi-agent-orchestration/SKILL.md +579 -0
  67. package/presets/cursor/skills/multi-agent-orchestration/examples/framework_implementations.py +362 -0
  68. package/presets/cursor/skills/multi-agent-orchestration/examples/orchestration_patterns.py +411 -0
  69. package/presets/cursor/skills/multi-agent-orchestration/scripts/agent_communication.py +334 -0
  70. package/presets/cursor/skills/multi-agent-orchestration/scripts/benchmarking.py +341 -0
  71. package/presets/cursor/skills/multi-agent-orchestration/scripts/workflow_management.py +334 -0
  72. package/presets/cursor/skills/nextjs-typescript/SKILL.md +394 -0
  73. package/presets/cursor/skills/openspec/SKILL.md +52 -0
  74. package/presets/cursor/skills/pagespeed-insights/SKILL.md +445 -0
  75. package/presets/cursor/skills/pagespeed-insights/reference.md +50 -0
  76. package/presets/cursor/skills/pagespeed-perf/SKILL.md +281 -0
  77. package/presets/cursor/skills/php-pro/SKILL.md +210 -0
  78. package/presets/cursor/skills/php-pro/references/async-patterns.md +412 -0
  79. package/presets/cursor/skills/php-pro/references/laravel-patterns.md +377 -0
  80. package/presets/cursor/skills/php-pro/references/modern-php-features.md +323 -0
  81. package/presets/cursor/skills/php-pro/references/symfony-patterns.md +466 -0
  82. package/presets/cursor/skills/php-pro/references/testing-quality.md +466 -0
  83. package/presets/cursor/skills/python/SKILL.md +613 -0
  84. package/presets/cursor/skills/python-django-stack/SKILL.md +500 -0
  85. package/presets/cursor/skills/python-fastapi-stack/SKILL.md +464 -0
  86. package/presets/cursor/skills/security/SKILL.md +384 -0
  87. package/presets/cursor/skills/seo-analytics-injector/SKILL.md +44 -0
  88. package/presets/cursor/skills/spring-auth-auditor/SKILL.md +33 -0
  89. package/presets/cursor/skills/spring-boot-feature/SKILL.md +566 -0
  90. package/presets/cursor/skills/spring-boot-kotlin/SKILL.md +408 -0
  91. package/presets/cursor/skills/spring-boot-testing-strategy/SKILL.md +479 -0
  92. package/presets/cursor/skills/sqlalchemy/SKILL.md +473 -0
  93. package/presets/cursor/skills/tailwind-responsive-auditor/SKILL.md +32 -0
  94. package/presets/cursor/skills/tdd-mutation-tester/SKILL.md +28 -0
  95. package/presets/cursor/skills/testing-tdd/SKILL.md +592 -0
  96. package/presets/cursor/skills/workflow-orchestration-patterns/SKILL.md +98 -0
  97. package/presets/cursor/skills/workflow-orchestration-patterns/references/details.md +223 -0
  98. package/presets/opencode/agents/architect.md +1 -2
  99. package/presets/opencode/agents/complexity-auditor.md +1 -0
  100. package/presets/opencode/agents/contract-builder.md +93 -0
  101. package/presets/opencode/agents/docs.md +1 -2
  102. package/presets/opencode/agents/goal-planner.md +82 -0
  103. package/presets/opencode/agents/implementer.md +9 -2
  104. package/presets/opencode/agents/orchestrator.md +50 -12
  105. package/presets/opencode/agents/repo-explorer.md +0 -1
  106. package/presets/opencode/agents/reviewer.md +23 -2
  107. package/presets/opencode/agents/security-reviewer.md +129 -0
  108. package/presets/opencode/agents/task-coach.md +1 -2
  109. package/presets/opencode/agents/tester.md +1 -2
  110. package/presets/opencode/commands/cc-openspec.md +61 -0
  111. package/presets/opencode/commands/cc-scorecard.md +16 -0
  112. package/presets/opencode/prompts/v0.5.0/architect.md +222 -0
  113. package/presets/opencode/prompts/v0.5.0/complexity-auditor.md +91 -0
  114. package/presets/opencode/prompts/v0.5.0/contract-builder.md +84 -0
  115. package/presets/opencode/prompts/v0.5.0/docs.md +190 -0
  116. package/presets/opencode/prompts/v0.5.0/goal-planner.md +80 -0
  117. package/presets/opencode/prompts/v0.5.0/implementer.md +171 -0
  118. package/presets/opencode/prompts/v0.5.0/orchestrator.md +388 -0
  119. package/presets/opencode/prompts/v0.5.0/repo-explorer.md +111 -0
  120. package/presets/opencode/prompts/v0.5.0/reviewer.md +248 -0
  121. package/presets/opencode/prompts/v0.5.0/security-reviewer.md +123 -0
  122. package/presets/opencode/prompts/v0.5.0/task-coach.md +156 -0
  123. package/presets/opencode/prompts/v0.5.0/tester.md +252 -0
  124. package/presets/opencode/skills/evaluation/SKILL.md +6 -0
  125. package/presets/opencode/skills/openspec/SKILL.md +50 -0
  126. package/presets/templates/BACKLOG.md +33 -0
  127. package/presets/templates/execution-profile.yml +6 -0
  128. package/presets/templates/model-comparison.md +11 -0
  129. package/presets/templates/regression-checklist.yml +10 -0
  130. package/src/presets/manifests/agy.yml +2 -2
  131. package/src/presets/manifests/claude.yml +2 -2
  132. package/src/presets/manifests/codex.yml +2 -2
  133. package/src/presets/manifests/cursor.yml +19 -3
  134. package/src/presets/manifests/gemini.yml +2 -2
  135. package/src/presets/manifests/opencode.yml +2 -2
  136. package/src/presets/models/agy.yml +21 -0
  137. package/src/presets/models/claude.yml +18 -0
  138. package/src/presets/models/codex.yml +18 -0
  139. package/src/presets/models/cursor.yml +39 -9
  140. package/src/presets/models/gemini.yml +18 -0
  141. package/src/presets/models/opencode.yml +18 -0
@@ -0,0 +1,417 @@
1
+ ---
2
+ id: django-testing
3
+ version: 1.0.0
4
+ name: Django Testing
5
+ description: >
6
+ Testing patterns for multi-tenant Django POS projects using pytest-django,
7
+ RequestFactory, and mocks. No factories, no complex conftest.
8
+ Trigger: When writing or reviewing tests in any apps/ directory.
9
+
10
+ user-invokable: true
11
+ license: MIT
12
+ metadata:
13
+ author: lgzarturo
14
+ category: testing
15
+
16
+ compatibility:
17
+ tools: [claude, codex, gemini, agy, opencode]
18
+ stacks:
19
+ languages: [python]
20
+ frameworks: [django, pytest-django, django-tenants]
21
+
22
+ risk:
23
+ level: low
24
+ can_execute_shell: true
25
+ can_modify_files: true
26
+ requires_network: false
27
+
28
+ inputs:
29
+ - view and service files under apps/
30
+ - existing test files (apps/*/tests/ or apps/*/tests.py)
31
+ - pytest configuration (pyproject.toml)
32
+
33
+ outputs:
34
+ - SimpleTestCase-based unit test classes
35
+ - mock configurations for tenant-app models
36
+ - RequestFactory request builders
37
+ - FakeSession pattern implementations
38
+ - queryset chain mock helpers
39
+ - pytest test files following project conventions
40
+
41
+ quality:
42
+ reviewed_by: codeconductor-core
43
+ version: 0.1.0
44
+ paths:
45
+ - "**/test_*.py"
46
+ - "**/tests/**"
47
+ ---
48
+ ## When to Use
49
+
50
+ - Writing any test in `apps/*/tests/` or `apps/*/tests.py`
51
+ - Deciding which test base class to use
52
+ - Mocking ORM calls for TENANT_APP models
53
+ - Testing views that use custom access decorators
54
+ - Testing APIs, PDFs, and cart operations
55
+
56
+ ## Test Architecture in Multi-Tenant Projects
57
+
58
+ ### The Schema Problem
59
+
60
+ This project uses `django-tenants` with multi-schema PostgreSQL:
61
+
62
+ - **Shared apps** (public schema): `core`, `users`, Django contrib
63
+ - **Tenant apps** (per-store schema): `employees`, `catalog`, `cart`, `orders`,
64
+ `pos`, `storefront`, `analytics`
65
+
66
+ **The test runner uses the public schema**, so tenant app tables DON'T exist in
67
+ tests. This profoundly affects how we write tests.
68
+
69
+ ### Base Class Selection
70
+
71
+ | Condition | Use | Notes |
72
+ | ------------------------------------------- | ------------------------ | --------------------- |
73
+ | No DB access needed | `SimpleTestCase` | No transaction, no DB |
74
+ | Only public schema models (`User`, `Store`) | `TestCase` | Uses public schema |
75
+ | **Any tenant app model** | `SimpleTestCase` + mocks | **ALWAYS** |
76
+
77
+ ```python
78
+ # WRONG — crashes because tables don't exist
79
+ class TestProductAPI(TestCase):
80
+ def test_list_products(self):
81
+ product = Product.objects.create(...) # Table does not exist!
82
+
83
+ # CORRECT — SimpleTestCase with mocks
84
+ class TestProductAPI(SimpleTestCase):
85
+ @patch("apps.catalog.views.Product")
86
+ def test_list_products(self, mock_product):
87
+ mock_product.objects.filter.return_value = MagicMock()
88
+ response = api_catalog_products(request)
89
+ ```
90
+
91
+ ## Mock Patterns
92
+
93
+ ### 1. RequestFactory — Never TestClient
94
+
95
+ `TestClient` runs `TenantMainMiddleware`, which redirects tenant URLs and breaks
96
+ view tests. Use `RequestFactory` and call the view directly:
97
+
98
+ ```python
99
+ from django.test import RequestFactory
100
+
101
+ factory = RequestFactory()
102
+
103
+ # GET request
104
+ request = factory.get("/pos/api/products/")
105
+ request.user = mock_user
106
+
107
+ # POST request with JSON
108
+ request = factory.post(
109
+ "/pos/api/cart/add/",
110
+ data=json.dumps({"product_id": 1, "quantity": 2}),
111
+ content_type="application/json"
112
+ )
113
+ request.user = mock_user
114
+
115
+ # Call the view directly — no middleware
116
+ response = api_search_products(request)
117
+ ```
118
+
119
+ ### 2. Bypass Access Decorators
120
+
121
+ `require_pos_access` and `require_manager_access` check `EmployeeProfile`
122
+ (tenant app). Bypass with `is_superuser`:
123
+
124
+ ```python
125
+ # The decorator does:
126
+ # def require_pos_access(view):
127
+ # def wrapper(request, *args, **kwargs):
128
+ # if not request.user.is_superuser:
129
+ # emp = EmployeeProfile.objects.get(user=request.user)
130
+ # if not emp.has_pos_access:
131
+ # return JsonResponse({"error": "No access"}, status=403)
132
+ # return view(request, *args, **kwargs)
133
+
134
+ mock_user = MagicMock()
135
+ mock_user.is_authenticated = True
136
+ mock_user.is_superuser = True # Skip EmployeeProfile lookup
137
+
138
+ request.user = mock_user
139
+ ```
140
+
141
+ ### 3. FakeSession Pattern
142
+
143
+ Carts and session-dependent views need a writable session dict:
144
+
145
+ ```python
146
+ class FakeSession(dict):
147
+ modified = False
148
+
149
+ def save(self):
150
+ pass
151
+
152
+ request.session = FakeSession()
153
+ request.session["pos_cart"] = {}
154
+ ```
155
+
156
+ ### 4. MagicMock.DoesNotExist Trap
157
+
158
+ `except Model.DoesNotExist` won't catch a plain `MagicMock`. Create a real
159
+ exception class:
160
+
161
+ ```python
162
+ # When mocking a model
163
+ mock_cart = MagicMock()
164
+ mock_cart.DoesNotExist = type("DoesNotExist", (Exception,), {})
165
+
166
+ with pytest.raises(mock_cart.DoesNotExist):
167
+ TemporalCart.objects.get(session_key=...)
168
+ ```
169
+
170
+ ### 5. MagicMock name= Trap
171
+
172
+ `MagicMock(name="X")` sets the internal repr, NOT the `.name` attribute:
173
+
174
+ ```python
175
+ # WRONG — .name will be a MagicMock, not "Papel A4"
176
+ mock = MagicMock(name="Papel A4")
177
+ print(mock.name) # <MagicMock name='Papel A4'>
178
+
179
+ # CORRECT
180
+ mock = MagicMock()
181
+ mock.name = "Papel A4"
182
+ mock.price = "1500.00"
183
+ ```
184
+
185
+ ### 6. Queryset Chain Mocking
186
+
187
+ Mock each chained call explicitly:
188
+
189
+ ```python
190
+ def _setup_product_mock(mock_product_model, product_data=None):
191
+ """Setup complete Product mock with chained queryset."""
192
+ mock_qs = MagicMock()
193
+
194
+ # Each method in the chain returns the same mock
195
+ mock_product_model.objects.filter.return_value = mock_qs
196
+ mock_qs.select_related.return_value = mock_qs
197
+ mock_qs.annotate.return_value = mock_qs
198
+ mock_qs.order_by.return_value = mock_qs
199
+ mock_qs.__getitem__ = lambda self, s: [mock_product] if isinstance(s, slice) else mock_product
200
+ mock_qs.count.return_value = 1
201
+
202
+ return mock_qs
203
+ ```
204
+
205
+ ### 7. Mock Model with Real Data
206
+
207
+ ```python
208
+ def _create_mock_product(product_id=1, name="Test Product", price="1000.00"):
209
+ mock = MagicMock()
210
+ mock.id = product_id
211
+ mock.name = name
212
+ mock.price = price
213
+ mock.is_active = True
214
+ mock.stock = 10
215
+ mock.is_service = False
216
+ mock.category_id = 1
217
+ mock.__str__ = lambda self: self.name
218
+ return mock
219
+ ```
220
+
221
+ ## File Structure
222
+
223
+ ```
224
+ apps/{app}/tests.py # simple apps, one file
225
+ apps/{app}/tests/__init__.py # complex apps
226
+ apps/{app}/tests/test_{feature}.py
227
+ ```
228
+
229
+ ### Section Separators
230
+
231
+ ```python
232
+ # ── TestClassName ────────────────────────────────────────
233
+ ```
234
+
235
+ ### Module Docstring
236
+
237
+ Every test file must explain the multi-tenant constraint:
238
+
239
+ ```python
240
+ """
241
+ Tests for catalog API.
242
+
243
+ NOTE: catalog models are TENANT_APP — they live in per-store schemas.
244
+ The test runner uses the public schema, so these tables don't exist.
245
+ All tests use SimpleTestCase + mocks.
246
+ """
247
+ ```
248
+
249
+ ### Naming Conventions
250
+
251
+ - `_make_{thing}()` — builds a request or object
252
+ - `_build_{thing}()` — constructs data structures
253
+ - `_setup_{thing}_mock()` — configures a mock for reuse
254
+
255
+ ## Test Types
256
+
257
+ ### 1. API/View Tests
258
+
259
+ ```python
260
+ class TestProductSearch(SimpleTestCase):
261
+ """Tests for POS product search."""
262
+
263
+ def _make_request(self, query="", offset=0, limit=20):
264
+ factory = RequestFactory()
265
+ request = factory.get(f"/pos/api/search/?q={query}&offset={offset}&limit={limit}")
266
+ request.user = MagicMock()
267
+ request.user.is_authenticated = True
268
+ request.user.is_superuser = True
269
+ return request
270
+
271
+ @patch("apps.pos.views.Product")
272
+ def test_search_returns_results(self, mock_product):
273
+ mock_product_obj = _create_mock_product(name="Lápices")
274
+ mock_qs = MagicMock()
275
+ mock_qs.select_related.return_value = mock_qs
276
+ mock_qs.annotate.return_value = mock_qs
277
+ mock_qs.filter.return_value = mock_qs
278
+ mock_qs.__getitem__ = lambda self, s: [mock_product_obj] if isinstance(s, slice) else mock_product_obj
279
+ mock_product.objects.filter.return_value = mock_qs
280
+
281
+ request = self._make_request(query="lápices")
282
+ response = api_search_products(request)
283
+
284
+ self.assertEqual(response.status_code, 200)
285
+ data = json.loads(response.content)
286
+ self.assertIn("results", data)
287
+ ```
288
+
289
+ ### 2. Service Tests
290
+
291
+ ```python
292
+ class TestCartOperations(SimpleTestCase):
293
+ """Tests for cart operations."""
294
+
295
+ def _build_cart_data(self, items):
296
+ """Build cart data as it arrives from request."""
297
+ return {str(k): {"quantity": v["quantity"], "price": str(v["price"])}
298
+ for k, v in items.items()}
299
+
300
+ @patch("apps.pos.services.CartService.get_products_from_db")
301
+ def test_calculate_total(self, mock_get_products):
302
+ mock_get_products.return_value = {
303
+ 1: _create_mock_product(price="1000.00"),
304
+ 2: _create_mock_product(price="2500.00"),
305
+ }
306
+
307
+ cart_data = self._build_cart_data({1: {"quantity": 2}, 2: {"quantity": 1}})
308
+ total = CartService.calculate_total(cart_data)
309
+
310
+ self.assertEqual(total, Decimal("4500.00"))
311
+ ```
312
+
313
+ ### 3. PDF Generation Tests
314
+
315
+ ```python
316
+ class TestRestockPDF(SimpleTestCase):
317
+ """Tests for restock PDF generation."""
318
+
319
+ def test_pdf_generates_correct_content(self):
320
+ products = [
321
+ {"name": "Product 1", "sku": "SKU001", "stock": 5, "min_stock": 10},
322
+ {"name": "Product 2", "sku": "SKU002", "stock": 0, "min_stock": 5},
323
+ ]
324
+
325
+ pdf_bytes = generate_restock_pdf("Test Store", products)
326
+
327
+ # Verify it's a valid PDF
328
+ self.assertTrue(pdf_bytes.startswith(b"%PDF"))
329
+ ```
330
+
331
+ ## Testing Commands
332
+
333
+ ```bash
334
+ # Run all tests
335
+ make tests
336
+
337
+ # Run a single file
338
+ uv run pytest apps/pos/tests/test_cart_checkout.py
339
+
340
+ # Run a single test
341
+ uv run pytest apps/pos/tests/test_cart_checkout.py::TestCartCheckout::test_checkout_success -v
342
+
343
+ # Force fresh DB (after schema changes)
344
+ uv run pytest --create-db
345
+
346
+ # With coverage
347
+ make tests-coverage
348
+
349
+ # Run with verbose output
350
+ uv run pytest -v --tb=short
351
+
352
+ # Run only failed tests from last run
353
+ uv run pytest --lf
354
+ ```
355
+
356
+ ## pytest Configuration
357
+
358
+ In `pyproject.toml`:
359
+
360
+ ```toml
361
+ [tool.pytest.ini_options]
362
+ DJANGO_SETTINGS_MODULE = "config.settings"
363
+ python_files = ["test_*.py", "*_test.py"]
364
+ python_classes = ["Test*"]
365
+ python_functions = ["test_*"]
366
+ addopts = "--reuse-db --nomigrations -x"
367
+ testpaths = ["apps"]
368
+ ```
369
+
370
+ ## Common Errors
371
+
372
+ ### Error: "no such table"
373
+
374
+ ```python
375
+ # CAUSE: TestCase for tenant app model
376
+ # SOLUTION: SimpleTestCase + mocks
377
+
378
+ # WRONG
379
+ class TestProduct(TestCase):
380
+ def test_create(self):
381
+ Product.objects.create(name="Test")
382
+
383
+ # CORRECT
384
+ class TestProduct(SimpleTestCase):
385
+ @patch("apps.catalog.models.Product")
386
+ def test_create(self, mock_product):
387
+ mock_product.objects.create.return_value = MagicMock(id=1)
388
+ ```
389
+
390
+ ### Error: "relation does not exist"
391
+
392
+ Same as above — schema mismatch between test and tenant.
393
+
394
+ ### Mocking transaction.atomic
395
+
396
+ ```python
397
+ from unittest.mock import patch, MagicMock
398
+
399
+ @patch("django.db.transaction.atomic")
400
+ def test_order_creation(self, mock_atomic):
401
+ # transaction.atomic as context manager
402
+ mock_context = MagicMock()
403
+ mock_atomic.return_value = mock_context
404
+
405
+ # Code uses "with transaction.atomic()"
406
+ # Mock must support context protocol
407
+ ```
408
+
409
+ ## Resources
410
+
411
+ - **Canonical queryset mock**: `apps/pos/tests/test_search_products.py`
412
+ - **FakeSession + transaction mock**: `apps/pos/tests/test_cart_checkout.py`
413
+ - **DoesNotExist + MagicMock.name traps**:
414
+ `apps/pos/tests/test_temporal_cart.py`
415
+ - **Multi-decorator bypass**: `apps/catalog/tests/test_api_catalog_products.py`
416
+ - **pytest config**: `pyproject.toml` → `[tool.pytest.ini_options]`
417
+ - **pytest-django docs**: <https://pytest-django.readthedocs.io/>