cc-codeconductor 0.2.1

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