popeye-cli 1.6.0 → 1.8.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 (161) hide show
  1. package/README.md +240 -32
  2. package/cheatsheet.md +407 -0
  3. package/dist/cli/commands/db.d.ts +10 -0
  4. package/dist/cli/commands/db.d.ts.map +1 -0
  5. package/dist/cli/commands/db.js +240 -0
  6. package/dist/cli/commands/db.js.map +1 -0
  7. package/dist/cli/commands/doctor.d.ts +18 -0
  8. package/dist/cli/commands/doctor.d.ts.map +1 -0
  9. package/dist/cli/commands/doctor.js +255 -0
  10. package/dist/cli/commands/doctor.js.map +1 -0
  11. package/dist/cli/commands/index.d.ts +2 -0
  12. package/dist/cli/commands/index.d.ts.map +1 -1
  13. package/dist/cli/commands/index.js +2 -0
  14. package/dist/cli/commands/index.js.map +1 -1
  15. package/dist/cli/index.d.ts.map +1 -1
  16. package/dist/cli/index.js +3 -1
  17. package/dist/cli/index.js.map +1 -1
  18. package/dist/cli/interactive.d.ts.map +1 -1
  19. package/dist/cli/interactive.js +96 -0
  20. package/dist/cli/interactive.js.map +1 -1
  21. package/dist/generators/admin-wizard.d.ts +25 -0
  22. package/dist/generators/admin-wizard.d.ts.map +1 -0
  23. package/dist/generators/admin-wizard.js +123 -0
  24. package/dist/generators/admin-wizard.js.map +1 -0
  25. package/dist/generators/all.d.ts.map +1 -1
  26. package/dist/generators/all.js +10 -3
  27. package/dist/generators/all.js.map +1 -1
  28. package/dist/generators/database.d.ts +58 -0
  29. package/dist/generators/database.d.ts.map +1 -0
  30. package/dist/generators/database.js +229 -0
  31. package/dist/generators/database.js.map +1 -0
  32. package/dist/generators/fullstack.d.ts.map +1 -1
  33. package/dist/generators/fullstack.js +23 -7
  34. package/dist/generators/fullstack.js.map +1 -1
  35. package/dist/generators/index.d.ts +2 -0
  36. package/dist/generators/index.d.ts.map +1 -1
  37. package/dist/generators/index.js +2 -0
  38. package/dist/generators/index.js.map +1 -1
  39. package/dist/generators/templates/admin-wizard-python.d.ts +32 -0
  40. package/dist/generators/templates/admin-wizard-python.d.ts.map +1 -0
  41. package/dist/generators/templates/admin-wizard-python.js +425 -0
  42. package/dist/generators/templates/admin-wizard-python.js.map +1 -0
  43. package/dist/generators/templates/admin-wizard-react.d.ts +48 -0
  44. package/dist/generators/templates/admin-wizard-react.d.ts.map +1 -0
  45. package/dist/generators/templates/admin-wizard-react.js +554 -0
  46. package/dist/generators/templates/admin-wizard-react.js.map +1 -0
  47. package/dist/generators/templates/database-docker.d.ts +23 -0
  48. package/dist/generators/templates/database-docker.d.ts.map +1 -0
  49. package/dist/generators/templates/database-docker.js +221 -0
  50. package/dist/generators/templates/database-docker.js.map +1 -0
  51. package/dist/generators/templates/database-python.d.ts +54 -0
  52. package/dist/generators/templates/database-python.d.ts.map +1 -0
  53. package/dist/generators/templates/database-python.js +723 -0
  54. package/dist/generators/templates/database-python.js.map +1 -0
  55. package/dist/generators/templates/database-typescript.d.ts +34 -0
  56. package/dist/generators/templates/database-typescript.d.ts.map +1 -0
  57. package/dist/generators/templates/database-typescript.js +232 -0
  58. package/dist/generators/templates/database-typescript.js.map +1 -0
  59. package/dist/generators/templates/fullstack.d.ts.map +1 -1
  60. package/dist/generators/templates/fullstack.js +29 -0
  61. package/dist/generators/templates/fullstack.js.map +1 -1
  62. package/dist/generators/templates/index.d.ts +5 -0
  63. package/dist/generators/templates/index.d.ts.map +1 -1
  64. package/dist/generators/templates/index.js +5 -0
  65. package/dist/generators/templates/index.js.map +1 -1
  66. package/dist/state/index.d.ts +10 -0
  67. package/dist/state/index.d.ts.map +1 -1
  68. package/dist/state/index.js +22 -0
  69. package/dist/state/index.js.map +1 -1
  70. package/dist/types/consensus.d.ts +3 -0
  71. package/dist/types/consensus.d.ts.map +1 -1
  72. package/dist/types/consensus.js +1 -0
  73. package/dist/types/consensus.js.map +1 -1
  74. package/dist/types/database-runtime.d.ts +86 -0
  75. package/dist/types/database-runtime.d.ts.map +1 -0
  76. package/dist/types/database-runtime.js +61 -0
  77. package/dist/types/database-runtime.js.map +1 -0
  78. package/dist/types/database.d.ts +85 -0
  79. package/dist/types/database.d.ts.map +1 -0
  80. package/dist/types/database.js +71 -0
  81. package/dist/types/database.js.map +1 -0
  82. package/dist/types/index.d.ts +3 -0
  83. package/dist/types/index.d.ts.map +1 -1
  84. package/dist/types/index.js +6 -0
  85. package/dist/types/index.js.map +1 -1
  86. package/dist/types/tester.d.ts +138 -0
  87. package/dist/types/tester.d.ts.map +1 -0
  88. package/dist/types/tester.js +110 -0
  89. package/dist/types/tester.js.map +1 -0
  90. package/dist/types/workflow.d.ts +166 -0
  91. package/dist/types/workflow.d.ts.map +1 -1
  92. package/dist/types/workflow.js +14 -0
  93. package/dist/types/workflow.js.map +1 -1
  94. package/dist/workflow/db-setup-runner.d.ts +63 -0
  95. package/dist/workflow/db-setup-runner.d.ts.map +1 -0
  96. package/dist/workflow/db-setup-runner.js +336 -0
  97. package/dist/workflow/db-setup-runner.js.map +1 -0
  98. package/dist/workflow/db-state-machine.d.ts +30 -0
  99. package/dist/workflow/db-state-machine.d.ts.map +1 -0
  100. package/dist/workflow/db-state-machine.js +51 -0
  101. package/dist/workflow/db-state-machine.js.map +1 -0
  102. package/dist/workflow/execution-mode.js +2 -2
  103. package/dist/workflow/execution-mode.js.map +1 -1
  104. package/dist/workflow/index.d.ts +3 -0
  105. package/dist/workflow/index.d.ts.map +1 -1
  106. package/dist/workflow/index.js +3 -0
  107. package/dist/workflow/index.js.map +1 -1
  108. package/dist/workflow/task-workflow.d.ts +5 -0
  109. package/dist/workflow/task-workflow.d.ts.map +1 -1
  110. package/dist/workflow/task-workflow.js +172 -6
  111. package/dist/workflow/task-workflow.js.map +1 -1
  112. package/dist/workflow/tester.d.ts +120 -0
  113. package/dist/workflow/tester.d.ts.map +1 -0
  114. package/dist/workflow/tester.js +589 -0
  115. package/dist/workflow/tester.js.map +1 -0
  116. package/dist/workflow/workflow-logger.d.ts +1 -1
  117. package/dist/workflow/workflow-logger.d.ts.map +1 -1
  118. package/dist/workflow/workflow-logger.js.map +1 -1
  119. package/package.json +1 -1
  120. package/src/cli/commands/db.ts +281 -0
  121. package/src/cli/commands/doctor.ts +273 -0
  122. package/src/cli/commands/index.ts +2 -0
  123. package/src/cli/index.ts +4 -0
  124. package/src/cli/interactive.ts +102 -0
  125. package/src/generators/admin-wizard.ts +146 -0
  126. package/src/generators/all.ts +10 -3
  127. package/src/generators/database.ts +286 -0
  128. package/src/generators/fullstack.ts +26 -9
  129. package/src/generators/index.ts +12 -0
  130. package/src/generators/templates/admin-wizard-python.ts +431 -0
  131. package/src/generators/templates/admin-wizard-react.ts +560 -0
  132. package/src/generators/templates/database-docker.ts +227 -0
  133. package/src/generators/templates/database-python.ts +734 -0
  134. package/src/generators/templates/database-typescript.ts +238 -0
  135. package/src/generators/templates/fullstack.ts +29 -0
  136. package/src/generators/templates/index.ts +5 -0
  137. package/src/state/index.ts +29 -0
  138. package/src/types/consensus.ts +3 -0
  139. package/src/types/database-runtime.ts +69 -0
  140. package/src/types/database.ts +84 -0
  141. package/src/types/index.ts +50 -0
  142. package/src/types/tester.ts +136 -0
  143. package/src/types/workflow.ts +31 -0
  144. package/src/workflow/db-setup-runner.ts +391 -0
  145. package/src/workflow/db-state-machine.ts +58 -0
  146. package/src/workflow/execution-mode.ts +2 -2
  147. package/src/workflow/index.ts +3 -0
  148. package/src/workflow/task-workflow.ts +227 -5
  149. package/src/workflow/tester.ts +723 -0
  150. package/src/workflow/workflow-logger.ts +2 -0
  151. package/tests/generators/admin-wizard-orchestrator.test.ts +64 -0
  152. package/tests/generators/admin-wizard-templates.test.ts +366 -0
  153. package/tests/generators/cross-phase-integration.test.ts +383 -0
  154. package/tests/generators/database.test.ts +456 -0
  155. package/tests/generators/fe-be-db-integration.test.ts +613 -0
  156. package/tests/types/database-runtime.test.ts +158 -0
  157. package/tests/types/database.test.ts +187 -0
  158. package/tests/types/tester.test.ts +174 -0
  159. package/tests/workflow/db-setup-runner.test.ts +211 -0
  160. package/tests/workflow/db-state-machine.test.ts +117 -0
  161. package/tests/workflow/tester.test.ts +401 -0
@@ -0,0 +1,383 @@
1
+ /**
2
+ * Cross-phase integration tests
3
+ * Verifies that Phase 1 (DB types/templates), Phase 2 (state machine/runner),
4
+ * and Phase 3 (admin wizard) work together correctly with no disconnections.
5
+ */
6
+
7
+ import { describe, it, expect } from 'vitest';
8
+
9
+ // Phase 1: types + templates
10
+ import { DbStatusSchema, DbSetupStepSchema } from '../../src/types/database.js';
11
+ import { SetupResultSchema } from '../../src/types/database-runtime.js';
12
+ import { getDatabaseFiles } from '../../src/generators/database.js';
13
+ import { generateDbEnvExample } from '../../src/generators/templates/database-docker.js';
14
+
15
+ // Phase 2: state machine
16
+ import {
17
+ canTransition,
18
+ getAvailableTransitions,
19
+ } from '../../src/workflow/db-state-machine.js';
20
+
21
+ // Phase 3: admin wizard
22
+ import { getAdminWizardFiles } from '../../src/generators/admin-wizard.js';
23
+ import {
24
+ generateFastAPIMainWithAdmin,
25
+ generateAdminDbRoutes,
26
+ } from '../../src/generators/templates/admin-wizard-python.js';
27
+ import {
28
+ generateAppTsxWithAdmin,
29
+ generateDbStatusBanner,
30
+ } from '../../src/generators/templates/admin-wizard-react.js';
31
+
32
+ // Composite: fullstack + all file lists
33
+ import { getFullstackProjectFiles } from '../../src/generators/fullstack.js';
34
+ import { getAllProjectFiles } from '../../src/generators/all.js';
35
+
36
+ const TEST_PACKAGE = 'my_project';
37
+ const TEST_PROJECT = 'my-project';
38
+
39
+ // ============================================================
40
+ // Phase 1 ↔ Phase 3: Generated backend wiring
41
+ // ============================================================
42
+
43
+ describe('Phase 1 ↔ Phase 3: Backend router wiring', () => {
44
+ const mainPy = generateFastAPIMainWithAdmin(TEST_PROJECT, TEST_PACKAGE);
45
+
46
+ it('should include Phase 1 health_db_router in main.py', () => {
47
+ expect(mainPy).toContain('health_db_router');
48
+ expect(mainPy).toContain(
49
+ `from ${TEST_PACKAGE}.routes.health_db import router as health_db_router`
50
+ );
51
+ });
52
+
53
+ it('should include Phase 3 admin_db_router in main.py', () => {
54
+ expect(mainPy).toContain('admin_db_router');
55
+ expect(mainPy).toContain(
56
+ `from ${TEST_PACKAGE}.routes.admin_db import router as admin_db_router`
57
+ );
58
+ });
59
+
60
+ it('should wire both routers via app.include_router()', () => {
61
+ expect(mainPy).toContain('app.include_router(health_db_router)');
62
+ expect(mainPy).toContain('app.include_router(admin_db_router)');
63
+ });
64
+
65
+ it('should preserve base endpoints alongside both routers', () => {
66
+ expect(mainPy).toContain('@app.get("/")');
67
+ expect(mainPy).toContain('@app.get("/health")');
68
+ });
69
+ });
70
+
71
+ describe('Phase 1 ↔ Phase 3: Admin routes reference Phase 1 concepts', () => {
72
+ const adminRoutes = generateAdminDbRoutes(TEST_PACKAGE);
73
+
74
+ it('should reference alembic for migrations', () => {
75
+ expect(adminRoutes).toContain('alembic upgrade head');
76
+ });
77
+
78
+ it('should reference asyncpg for DB connectivity', () => {
79
+ expect(adminRoutes).toContain('asyncpg.connect');
80
+ });
81
+
82
+ it('should check alembic_version table for migration status', () => {
83
+ expect(adminRoutes).toContain('alembic_version');
84
+ });
85
+ });
86
+
87
+ // ============================================================
88
+ // Phase 1 ↔ Phase 3: Environment variable alignment
89
+ // ============================================================
90
+
91
+ describe('Phase 1 ↔ Phase 3: Environment variable alignment', () => {
92
+ const envExample = generateDbEnvExample(TEST_PROJECT);
93
+
94
+ it('should include Phase 1 DATABASE_URL', () => {
95
+ expect(envExample).toContain('DATABASE_URL=postgresql+asyncpg://');
96
+ });
97
+
98
+ it('should include Phase 1 POSTGRES vars', () => {
99
+ expect(envExample).toContain('POSTGRES_USER=postgres');
100
+ expect(envExample).toContain('POSTGRES_PASSWORD=postgres');
101
+ expect(envExample).toContain('POSTGRES_DB=');
102
+ });
103
+
104
+ it('should include Phase 1 vector support flag', () => {
105
+ expect(envExample).toContain('DB_VECTOR_REQUIRED=true');
106
+ });
107
+
108
+ it('should include Phase 3 ADMIN_SETUP_TOKEN', () => {
109
+ expect(envExample).toContain('ADMIN_SETUP_TOKEN=change-me-to-a-random-string');
110
+ });
111
+
112
+ it('should have both Database and Admin sections', () => {
113
+ expect(envExample).toContain('# Database');
114
+ expect(envExample).toContain('# Admin Wizard');
115
+ });
116
+ });
117
+
118
+ // ============================================================
119
+ // Phase 2 ↔ Phase 3: Status value alignment
120
+ // ============================================================
121
+
122
+ describe('Phase 2 ↔ Phase 3: Admin wizard status values match DbStatusSchema', () => {
123
+ const validStatuses = DbStatusSchema.options;
124
+ const adminRoutes = generateAdminDbRoutes(TEST_PACKAGE);
125
+
126
+ it('DbStatusSchema should have all 5 lifecycle states', () => {
127
+ expect(validStatuses).toContain('unconfigured');
128
+ expect(validStatuses).toContain('configured');
129
+ expect(validStatuses).toContain('applying');
130
+ expect(validStatuses).toContain('ready');
131
+ expect(validStatuses).toContain('error');
132
+ });
133
+
134
+ it('admin GET /status should only use valid DbStatus values', () => {
135
+ // Extract all status = "..." assignments from the generated Python code
136
+ const statusAssignments = adminRoutes.match(/status\s*=\s*"([^"]+)"/g) || [];
137
+ const assignedValues = statusAssignments.map((s) =>
138
+ s.match(/"([^"]+)"/)?.[1]
139
+ ).filter(Boolean);
140
+
141
+ for (const value of assignedValues) {
142
+ expect(
143
+ validStatuses.includes(value as typeof validStatuses[number]),
144
+ `Admin route status "${value}" is not in DbStatusSchema: [${validStatuses.join(', ')}]`
145
+ ).toBe(true);
146
+ }
147
+ });
148
+
149
+ it('admin POST /apply should return valid final status values', () => {
150
+ // /apply endpoint returns "ready" or "error" as final_status
151
+ expect(adminRoutes).toContain('final_status = "ready" if');
152
+ expect(adminRoutes).toContain('else "error"');
153
+ expect(validStatuses).toContain('ready');
154
+ expect(validStatuses).toContain('error');
155
+ });
156
+
157
+ it('admin GET /status default should be "unconfigured"', () => {
158
+ expect(adminRoutes).toContain('status = "unconfigured"');
159
+ expect(validStatuses).toContain('unconfigured');
160
+ });
161
+
162
+ it('should NOT contain any non-schema status values', () => {
163
+ // Ensure we never use invented statuses like "pending_migration"
164
+ expect(adminRoutes).not.toContain('"pending_migration"');
165
+ expect(adminRoutes).not.toContain('"pending"');
166
+ expect(adminRoutes).not.toContain('"migrating"');
167
+ });
168
+ });
169
+
170
+ describe('Phase 2 ↔ Phase 3: State transitions match admin wizard flow', () => {
171
+ it('configured -> applying should be valid (admin /apply triggers this)', () => {
172
+ expect(canTransition('configured', 'applying')).toBe(true);
173
+ });
174
+
175
+ it('applying -> ready should be valid (successful /apply result)', () => {
176
+ expect(canTransition('applying', 'ready')).toBe(true);
177
+ });
178
+
179
+ it('applying -> error should be valid (failed /apply result)', () => {
180
+ expect(canTransition('applying', 'error')).toBe(true);
181
+ });
182
+
183
+ it('error -> configured should be valid (admin /retry resets to configured)', () => {
184
+ expect(canTransition('error', 'configured')).toBe(true);
185
+ });
186
+
187
+ it('unconfigured -> configured should be valid (initial setup)', () => {
188
+ expect(canTransition('unconfigured', 'configured')).toBe(true);
189
+ });
190
+
191
+ it('ready -> configured should be valid (reconfiguration)', () => {
192
+ expect(canTransition('ready', 'configured')).toBe(true);
193
+ });
194
+
195
+ it('applying should only go to ready or error', () => {
196
+ const targets = getAvailableTransitions('applying');
197
+ expect(targets).toEqual(['ready', 'error']);
198
+ });
199
+ });
200
+
201
+ describe('Phase 2 ↔ Phase 3: React banner uses valid statuses', () => {
202
+ const banner = generateDbStatusBanner();
203
+
204
+ it('should check for "ready" status to hide banner', () => {
205
+ expect(banner).toContain("status === 'ready'");
206
+ });
207
+
208
+ it('should check for "error" status to show error message', () => {
209
+ expect(banner).toContain("status === 'error'");
210
+ });
211
+ });
212
+
213
+ // ============================================================
214
+ // Phase 1 ↔ Phase 2: Schema consistency
215
+ // ============================================================
216
+
217
+ describe('Phase 1 ↔ Phase 2: Schema types used in runtime', () => {
218
+ it('SetupResultSchema.finalStatus should use DbStatusSchema', () => {
219
+ // Verify SetupResult validates with a valid DbStatus
220
+ const result = SetupResultSchema.safeParse({
221
+ success: true,
222
+ steps: [],
223
+ totalDurationMs: 100,
224
+ finalStatus: 'ready',
225
+ });
226
+ expect(result.success).toBe(true);
227
+ });
228
+
229
+ it('SetupResultSchema should reject invalid finalStatus', () => {
230
+ const result = SetupResultSchema.safeParse({
231
+ success: true,
232
+ steps: [],
233
+ totalDurationMs: 100,
234
+ finalStatus: 'pending_migration',
235
+ });
236
+ expect(result.success).toBe(false);
237
+ });
238
+
239
+ it('DbSetupStepSchema should have all 6 pipeline steps', () => {
240
+ const steps = DbSetupStepSchema.options;
241
+ expect(steps).toHaveLength(6);
242
+ expect(steps).toContain('check_connection');
243
+ expect(steps).toContain('ensure_extensions');
244
+ expect(steps).toContain('apply_migrations');
245
+ expect(steps).toContain('seed_minimal');
246
+ expect(steps).toContain('readiness_tests');
247
+ expect(steps).toContain('mark_ready');
248
+ });
249
+ });
250
+
251
+ // ============================================================
252
+ // All phases combined: File list completeness
253
+ // ============================================================
254
+
255
+ describe('All phases: getFullstackProjectFiles includes all layers', () => {
256
+ const files = getFullstackProjectFiles(TEST_PROJECT);
257
+ const dbFiles = getDatabaseFiles(TEST_PACKAGE, 'sqlalchemy');
258
+ const adminFiles = getAdminWizardFiles(TEST_PACKAGE);
259
+
260
+ it('should include all Phase 1 database files', () => {
261
+ for (const f of dbFiles) {
262
+ expect(files, `Missing Phase 1 file: ${f}`).toContain(f);
263
+ }
264
+ });
265
+
266
+ it('should include all Phase 3 admin wizard files', () => {
267
+ for (const f of adminFiles) {
268
+ expect(files, `Missing Phase 3 file: ${f}`).toContain(f);
269
+ }
270
+ });
271
+
272
+ it('should include Phase 1 health route', () => {
273
+ expect(files).toContain(`apps/backend/src/${TEST_PACKAGE}/routes/health_db.py`);
274
+ });
275
+
276
+ it('should include Phase 3 admin route', () => {
277
+ expect(files).toContain(`apps/backend/src/${TEST_PACKAGE}/routes/admin_db.py`);
278
+ });
279
+
280
+ it('should include Phase 1 alembic config', () => {
281
+ expect(files).toContain('apps/backend/alembic.ini');
282
+ });
283
+
284
+ it('should include Phase 3 admin middleware', () => {
285
+ expect(files).toContain(`apps/backend/src/${TEST_PACKAGE}/middleware/admin_auth.py`);
286
+ });
287
+ });
288
+
289
+ describe('All phases: getAllProjectFiles includes all layers', () => {
290
+ const files = getAllProjectFiles(TEST_PROJECT);
291
+ const dbFiles = getDatabaseFiles(TEST_PACKAGE, 'sqlalchemy');
292
+ const adminFiles = getAdminWizardFiles(TEST_PACKAGE);
293
+
294
+ it('should include all Phase 1 database files', () => {
295
+ for (const f of dbFiles) {
296
+ expect(files, `Missing Phase 1 file in all: ${f}`).toContain(f);
297
+ }
298
+ });
299
+
300
+ it('should include all Phase 3 admin wizard files', () => {
301
+ for (const f of adminFiles) {
302
+ expect(files, `Missing Phase 3 file in all: ${f}`).toContain(f);
303
+ }
304
+ });
305
+
306
+ it('should include website files (unique to all projects)', () => {
307
+ expect(files).toContain('apps/website/package.json');
308
+ expect(files).toContain('apps/website/src/app/page.tsx');
309
+ });
310
+
311
+ it('should include shared packages (unique to all projects)', () => {
312
+ expect(files).toContain('packages/design-tokens/package.json');
313
+ expect(files).toContain('packages/ui/package.json');
314
+ });
315
+ });
316
+
317
+ // ============================================================
318
+ // All phases: Frontend wiring
319
+ // ============================================================
320
+
321
+ describe('All phases: Generated App.tsx integrates admin wizard', () => {
322
+ const appTsx = generateAppTsxWithAdmin(TEST_PROJECT);
323
+
324
+ it('should import DbStatusBanner (Phase 3)', () => {
325
+ expect(appTsx).toContain('DbStatusBanner');
326
+ });
327
+
328
+ it('should import DbSetupStepper (Phase 3)', () => {
329
+ expect(appTsx).toContain('DbSetupStepper');
330
+ });
331
+
332
+ it('should manage showWizard state for overlay', () => {
333
+ expect(appTsx).toContain('showWizard');
334
+ expect(appTsx).toContain('setShowWizard');
335
+ });
336
+
337
+ it('should render the banner and stepper', () => {
338
+ expect(appTsx).toContain('<DbStatusBanner');
339
+ expect(appTsx).toContain('<DbSetupStepper');
340
+ });
341
+
342
+ it('should still include the project name', () => {
343
+ expect(appTsx).toContain(TEST_PROJECT);
344
+ });
345
+ });
346
+
347
+ // ============================================================
348
+ // Cross-phase: No duplicate/conflicting file paths
349
+ // ============================================================
350
+
351
+ describe('Cross-phase: No file path conflicts between layers', () => {
352
+ const dbFiles = getDatabaseFiles(TEST_PACKAGE, 'sqlalchemy');
353
+ const adminFiles = getAdminWizardFiles(TEST_PACKAGE);
354
+
355
+ it('should have no overlapping files between Phase 1 and Phase 3', () => {
356
+ const overlap = dbFiles.filter((f) => adminFiles.includes(f));
357
+ expect(overlap, `Overlapping files: ${overlap.join(', ')}`).toHaveLength(0);
358
+ });
359
+
360
+ it('Phase 1 files should be in database/ and migrations/', () => {
361
+ const dbBackendFiles = dbFiles.filter((f) => f.startsWith('apps/backend/'));
362
+ for (const f of dbBackendFiles) {
363
+ const hasExpectedPath =
364
+ f.includes('/database/') ||
365
+ f.includes('/routes/health_db') ||
366
+ f.includes('/startup.py') ||
367
+ f.includes('alembic') ||
368
+ f.includes('migrations/') ||
369
+ f.includes('conftest_db');
370
+ expect(hasExpectedPath, `Unexpected Phase 1 path: ${f}`).toBe(true);
371
+ }
372
+ });
373
+
374
+ it('Phase 3 files should be in middleware/ and admin/', () => {
375
+ for (const f of adminFiles) {
376
+ const hasExpectedPath =
377
+ f.includes('/middleware/') ||
378
+ f.includes('/routes/admin_db') ||
379
+ f.includes('/admin/');
380
+ expect(hasExpectedPath, `Unexpected Phase 3 path: ${f}`).toBe(true);
381
+ }
382
+ });
383
+ });