popeye-cli 1.7.0 → 1.9.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 (174) hide show
  1. package/README.md +148 -7
  2. package/cheatsheet.md +440 -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 +3 -0
  12. package/dist/cli/commands/index.d.ts.map +1 -1
  13. package/dist/cli/commands/index.js +3 -0
  14. package/dist/cli/commands/index.js.map +1 -1
  15. package/dist/cli/commands/review.d.ts +31 -0
  16. package/dist/cli/commands/review.d.ts.map +1 -0
  17. package/dist/cli/commands/review.js +156 -0
  18. package/dist/cli/commands/review.js.map +1 -0
  19. package/dist/cli/index.d.ts.map +1 -1
  20. package/dist/cli/index.js +4 -1
  21. package/dist/cli/index.js.map +1 -1
  22. package/dist/cli/interactive.d.ts.map +1 -1
  23. package/dist/cli/interactive.js +218 -61
  24. package/dist/cli/interactive.js.map +1 -1
  25. package/dist/generators/admin-wizard.d.ts +25 -0
  26. package/dist/generators/admin-wizard.d.ts.map +1 -0
  27. package/dist/generators/admin-wizard.js +123 -0
  28. package/dist/generators/admin-wizard.js.map +1 -0
  29. package/dist/generators/all.d.ts.map +1 -1
  30. package/dist/generators/all.js +10 -3
  31. package/dist/generators/all.js.map +1 -1
  32. package/dist/generators/database.d.ts +58 -0
  33. package/dist/generators/database.d.ts.map +1 -0
  34. package/dist/generators/database.js +229 -0
  35. package/dist/generators/database.js.map +1 -0
  36. package/dist/generators/fullstack.d.ts.map +1 -1
  37. package/dist/generators/fullstack.js +23 -7
  38. package/dist/generators/fullstack.js.map +1 -1
  39. package/dist/generators/index.d.ts +2 -0
  40. package/dist/generators/index.d.ts.map +1 -1
  41. package/dist/generators/index.js +2 -0
  42. package/dist/generators/index.js.map +1 -1
  43. package/dist/generators/templates/admin-wizard-python.d.ts +32 -0
  44. package/dist/generators/templates/admin-wizard-python.d.ts.map +1 -0
  45. package/dist/generators/templates/admin-wizard-python.js +425 -0
  46. package/dist/generators/templates/admin-wizard-python.js.map +1 -0
  47. package/dist/generators/templates/admin-wizard-react.d.ts +48 -0
  48. package/dist/generators/templates/admin-wizard-react.d.ts.map +1 -0
  49. package/dist/generators/templates/admin-wizard-react.js +554 -0
  50. package/dist/generators/templates/admin-wizard-react.js.map +1 -0
  51. package/dist/generators/templates/database-docker.d.ts +23 -0
  52. package/dist/generators/templates/database-docker.d.ts.map +1 -0
  53. package/dist/generators/templates/database-docker.js +221 -0
  54. package/dist/generators/templates/database-docker.js.map +1 -0
  55. package/dist/generators/templates/database-python.d.ts +54 -0
  56. package/dist/generators/templates/database-python.d.ts.map +1 -0
  57. package/dist/generators/templates/database-python.js +723 -0
  58. package/dist/generators/templates/database-python.js.map +1 -0
  59. package/dist/generators/templates/database-typescript.d.ts +34 -0
  60. package/dist/generators/templates/database-typescript.d.ts.map +1 -0
  61. package/dist/generators/templates/database-typescript.js +232 -0
  62. package/dist/generators/templates/database-typescript.js.map +1 -0
  63. package/dist/generators/templates/fullstack.d.ts.map +1 -1
  64. package/dist/generators/templates/fullstack.js +29 -0
  65. package/dist/generators/templates/fullstack.js.map +1 -1
  66. package/dist/generators/templates/index.d.ts +5 -0
  67. package/dist/generators/templates/index.d.ts.map +1 -1
  68. package/dist/generators/templates/index.js +5 -0
  69. package/dist/generators/templates/index.js.map +1 -1
  70. package/dist/state/index.d.ts +10 -0
  71. package/dist/state/index.d.ts.map +1 -1
  72. package/dist/state/index.js +21 -0
  73. package/dist/state/index.js.map +1 -1
  74. package/dist/types/audit.d.ts +623 -0
  75. package/dist/types/audit.d.ts.map +1 -0
  76. package/dist/types/audit.js +240 -0
  77. package/dist/types/audit.js.map +1 -0
  78. package/dist/types/database-runtime.d.ts +86 -0
  79. package/dist/types/database-runtime.d.ts.map +1 -0
  80. package/dist/types/database-runtime.js +61 -0
  81. package/dist/types/database-runtime.js.map +1 -0
  82. package/dist/types/database.d.ts +85 -0
  83. package/dist/types/database.d.ts.map +1 -0
  84. package/dist/types/database.js +71 -0
  85. package/dist/types/database.js.map +1 -0
  86. package/dist/types/index.d.ts +2 -0
  87. package/dist/types/index.d.ts.map +1 -1
  88. package/dist/types/index.js +4 -0
  89. package/dist/types/index.js.map +1 -1
  90. package/dist/types/workflow.d.ts +36 -0
  91. package/dist/types/workflow.d.ts.map +1 -1
  92. package/dist/types/workflow.js +7 -0
  93. package/dist/types/workflow.js.map +1 -1
  94. package/dist/workflow/audit-analyzer.d.ts +58 -0
  95. package/dist/workflow/audit-analyzer.d.ts.map +1 -0
  96. package/dist/workflow/audit-analyzer.js +420 -0
  97. package/dist/workflow/audit-analyzer.js.map +1 -0
  98. package/dist/workflow/audit-mode.d.ts +28 -0
  99. package/dist/workflow/audit-mode.d.ts.map +1 -0
  100. package/dist/workflow/audit-mode.js +169 -0
  101. package/dist/workflow/audit-mode.js.map +1 -0
  102. package/dist/workflow/audit-recovery.d.ts +61 -0
  103. package/dist/workflow/audit-recovery.d.ts.map +1 -0
  104. package/dist/workflow/audit-recovery.js +242 -0
  105. package/dist/workflow/audit-recovery.js.map +1 -0
  106. package/dist/workflow/audit-reporter.d.ts +65 -0
  107. package/dist/workflow/audit-reporter.d.ts.map +1 -0
  108. package/dist/workflow/audit-reporter.js +301 -0
  109. package/dist/workflow/audit-reporter.js.map +1 -0
  110. package/dist/workflow/audit-scanner.d.ts +87 -0
  111. package/dist/workflow/audit-scanner.d.ts.map +1 -0
  112. package/dist/workflow/audit-scanner.js +768 -0
  113. package/dist/workflow/audit-scanner.js.map +1 -0
  114. package/dist/workflow/db-setup-runner.d.ts +63 -0
  115. package/dist/workflow/db-setup-runner.d.ts.map +1 -0
  116. package/dist/workflow/db-setup-runner.js +336 -0
  117. package/dist/workflow/db-setup-runner.js.map +1 -0
  118. package/dist/workflow/db-state-machine.d.ts +30 -0
  119. package/dist/workflow/db-state-machine.d.ts.map +1 -0
  120. package/dist/workflow/db-state-machine.js +51 -0
  121. package/dist/workflow/db-state-machine.js.map +1 -0
  122. package/dist/workflow/index.d.ts +7 -0
  123. package/dist/workflow/index.d.ts.map +1 -1
  124. package/dist/workflow/index.js +7 -0
  125. package/dist/workflow/index.js.map +1 -1
  126. package/package.json +1 -1
  127. package/src/cli/commands/db.ts +281 -0
  128. package/src/cli/commands/doctor.ts +273 -0
  129. package/src/cli/commands/index.ts +3 -0
  130. package/src/cli/commands/review.ts +187 -0
  131. package/src/cli/index.ts +6 -0
  132. package/src/cli/interactive.ts +174 -4
  133. package/src/generators/admin-wizard.ts +146 -0
  134. package/src/generators/all.ts +10 -3
  135. package/src/generators/database.ts +286 -0
  136. package/src/generators/fullstack.ts +26 -9
  137. package/src/generators/index.ts +12 -0
  138. package/src/generators/templates/admin-wizard-python.ts +431 -0
  139. package/src/generators/templates/admin-wizard-react.ts +560 -0
  140. package/src/generators/templates/database-docker.ts +227 -0
  141. package/src/generators/templates/database-python.ts +734 -0
  142. package/src/generators/templates/database-typescript.ts +238 -0
  143. package/src/generators/templates/fullstack.ts +29 -0
  144. package/src/generators/templates/index.ts +5 -0
  145. package/src/state/index.ts +28 -0
  146. package/src/types/audit.ts +294 -0
  147. package/src/types/database-runtime.ts +69 -0
  148. package/src/types/database.ts +84 -0
  149. package/src/types/index.ts +29 -0
  150. package/src/types/workflow.ts +20 -0
  151. package/src/workflow/audit-analyzer.ts +491 -0
  152. package/src/workflow/audit-mode.ts +240 -0
  153. package/src/workflow/audit-recovery.ts +284 -0
  154. package/src/workflow/audit-reporter.ts +370 -0
  155. package/src/workflow/audit-scanner.ts +873 -0
  156. package/src/workflow/db-setup-runner.ts +391 -0
  157. package/src/workflow/db-state-machine.ts +58 -0
  158. package/src/workflow/index.ts +7 -0
  159. package/tests/cli/commands/review.test.ts +52 -0
  160. package/tests/generators/admin-wizard-orchestrator.test.ts +64 -0
  161. package/tests/generators/admin-wizard-templates.test.ts +366 -0
  162. package/tests/generators/cross-phase-integration.test.ts +383 -0
  163. package/tests/generators/database.test.ts +456 -0
  164. package/tests/generators/fe-be-db-integration.test.ts +613 -0
  165. package/tests/types/audit.test.ts +250 -0
  166. package/tests/types/database-runtime.test.ts +158 -0
  167. package/tests/types/database.test.ts +187 -0
  168. package/tests/workflow/audit-analyzer.test.ts +281 -0
  169. package/tests/workflow/audit-mode.test.ts +114 -0
  170. package/tests/workflow/audit-recovery.test.ts +237 -0
  171. package/tests/workflow/audit-reporter.test.ts +254 -0
  172. package/tests/workflow/audit-scanner.test.ts +270 -0
  173. package/tests/workflow/db-setup-runner.test.ts +211 -0
  174. package/tests/workflow/db-state-machine.test.ts +117 -0
@@ -0,0 +1,250 @@
1
+ /**
2
+ * Tests for audit type schemas.
3
+ */
4
+ import { describe, it, expect } from 'vitest';
5
+ import {
6
+ AuditSeveritySchema,
7
+ AuditCategorySchema,
8
+ ComponentKindSchema,
9
+ AuditEvidenceSchema,
10
+ DependencyManifestSchema,
11
+ FileEntrySchema,
12
+ ComponentScanSchema,
13
+ WiringMismatchSchema,
14
+ WiringMatrixSchema,
15
+ SearchMetadataSchema,
16
+ AuditFindingSchema,
17
+ ProjectSummaryReportSchema,
18
+ ProjectAuditReportSchema,
19
+ RecoveryTaskSchema,
20
+ RecoveryMilestoneSchema,
21
+ RecoveryPlanSchema,
22
+ AuditModeOptionsSchema,
23
+ } from '../../src/types/audit.js';
24
+
25
+ describe('AuditSeveritySchema', () => {
26
+ it('should accept valid severity values', () => {
27
+ expect(AuditSeveritySchema.parse('critical')).toBe('critical');
28
+ expect(AuditSeveritySchema.parse('major')).toBe('major');
29
+ expect(AuditSeveritySchema.parse('minor')).toBe('minor');
30
+ expect(AuditSeveritySchema.parse('info')).toBe('info');
31
+ });
32
+
33
+ it('should reject invalid values', () => {
34
+ expect(() => AuditSeveritySchema.parse('high')).toThrow();
35
+ expect(() => AuditSeveritySchema.parse('')).toThrow();
36
+ });
37
+ });
38
+
39
+ describe('AuditCategorySchema', () => {
40
+ it('should accept all valid categories', () => {
41
+ const categories = [
42
+ 'feature-completeness',
43
+ 'integration-wiring',
44
+ 'test-coverage',
45
+ 'config-deployment',
46
+ 'dependency-sanity',
47
+ 'consistency',
48
+ 'security',
49
+ 'documentation',
50
+ ];
51
+ for (const cat of categories) {
52
+ expect(AuditCategorySchema.parse(cat)).toBe(cat);
53
+ }
54
+ });
55
+
56
+ it('should reject invalid category', () => {
57
+ expect(() => AuditCategorySchema.parse('performance')).toThrow();
58
+ });
59
+ });
60
+
61
+ describe('ComponentKindSchema', () => {
62
+ it('should accept valid kinds', () => {
63
+ expect(ComponentKindSchema.parse('frontend')).toBe('frontend');
64
+ expect(ComponentKindSchema.parse('backend')).toBe('backend');
65
+ expect(ComponentKindSchema.parse('website')).toBe('website');
66
+ expect(ComponentKindSchema.parse('shared')).toBe('shared');
67
+ expect(ComponentKindSchema.parse('infra')).toBe('infra');
68
+ });
69
+
70
+ it('should reject unknown kind', () => {
71
+ expect(() => ComponentKindSchema.parse('mobile')).toThrow();
72
+ });
73
+ });
74
+
75
+ describe('ComponentScanSchema', () => {
76
+ const validComponent = {
77
+ kind: 'frontend',
78
+ rootDir: 'apps/frontend',
79
+ language: 'typescript',
80
+ framework: 'react',
81
+ entryPoints: ['src/main.tsx'],
82
+ routeFiles: ['src/App.tsx'],
83
+ testFiles: [{ path: 'tests/App.test.tsx', lines: 50 }],
84
+ sourceFiles: [{ path: 'src/main.tsx', lines: 20, extension: '.tsx' }],
85
+ dependencyManifests: [
86
+ { file: 'package.json', type: 'package.json', dependencies: { react: '^18.0.0' } },
87
+ ],
88
+ };
89
+
90
+ it('should validate a complete component scan', () => {
91
+ const result = ComponentScanSchema.parse(validComponent);
92
+ expect(result.kind).toBe('frontend');
93
+ expect(result.rootDir).toBe('apps/frontend');
94
+ expect(result.sourceFiles).toHaveLength(1);
95
+ });
96
+
97
+ it('should accept minimal component scan', () => {
98
+ const minimal = {
99
+ kind: 'backend',
100
+ rootDir: '.',
101
+ language: 'python',
102
+ entryPoints: [],
103
+ routeFiles: [],
104
+ testFiles: [],
105
+ sourceFiles: [],
106
+ dependencyManifests: [],
107
+ };
108
+ const result = ComponentScanSchema.parse(minimal);
109
+ expect(result.framework).toBeUndefined();
110
+ });
111
+
112
+ it('should reject invalid language', () => {
113
+ expect(() =>
114
+ ComponentScanSchema.parse({ ...validComponent, language: 'rust' })
115
+ ).toThrow();
116
+ });
117
+ });
118
+
119
+ describe('WiringMatrixSchema', () => {
120
+ it('should validate a complete wiring matrix', () => {
121
+ const wiring = {
122
+ frontendApiBaseEnvKeys: ['VITE_API_URL'],
123
+ frontendApiBaseResolved: 'http://localhost:3000',
124
+ backendCorsOrigins: ['http://localhost:5173'],
125
+ backendApiPrefix: '/api',
126
+ potentialMismatches: [
127
+ {
128
+ type: 'cors-origin-mismatch',
129
+ details: 'Frontend origin not in CORS list',
130
+ evidence: [{ file: '.env.example', snippet: 'VITE_API_URL=http://localhost:3000' }],
131
+ },
132
+ ],
133
+ };
134
+ const result = WiringMatrixSchema.parse(wiring);
135
+ expect(result.potentialMismatches).toHaveLength(1);
136
+ });
137
+
138
+ it('should accept empty mismatches', () => {
139
+ const wiring = {
140
+ frontendApiBaseEnvKeys: [],
141
+ potentialMismatches: [],
142
+ };
143
+ const result = WiringMatrixSchema.parse(wiring);
144
+ expect(result.potentialMismatches).toHaveLength(0);
145
+ });
146
+ });
147
+
148
+ describe('SearchMetadataSchema', () => {
149
+ it('should validate search metadata', () => {
150
+ const meta = {
151
+ serenaUsed: true,
152
+ serenaRetries: 1,
153
+ serenaErrors: ['timeout'],
154
+ fallbackUsed: true,
155
+ fallbackTool: 'grep',
156
+ searchQueries: ['find_symbol UserService'],
157
+ };
158
+ const result = SearchMetadataSchema.parse(meta);
159
+ expect(result.serenaUsed).toBe(true);
160
+ expect(result.serenaRetries).toBe(1);
161
+ });
162
+
163
+ it('should require all fields', () => {
164
+ expect(() => SearchMetadataSchema.parse({ serenaUsed: true })).toThrow();
165
+ });
166
+ });
167
+
168
+ describe('AuditFindingSchema', () => {
169
+ const validFinding = {
170
+ id: 'AUD-001',
171
+ category: 'test-coverage',
172
+ severity: 'major',
173
+ title: 'No tests for auth module',
174
+ description: 'The authentication module has zero test files.',
175
+ evidence: [{ file: 'src/auth/login.ts', description: 'No test file found' }],
176
+ recommendation: 'Add unit tests for auth handlers',
177
+ autoFixable: false,
178
+ };
179
+
180
+ it('should validate a complete finding', () => {
181
+ const result = AuditFindingSchema.parse(validFinding);
182
+ expect(result.id).toBe('AUD-001');
183
+ expect(result.autoFixable).toBe(false);
184
+ });
185
+
186
+ it('should reject missing required fields', () => {
187
+ const { recommendation, ...incomplete } = validFinding;
188
+ expect(() => AuditFindingSchema.parse(incomplete)).toThrow();
189
+ });
190
+
191
+ it('should reject invalid severity', () => {
192
+ expect(() =>
193
+ AuditFindingSchema.parse({ ...validFinding, severity: 'high' })
194
+ ).toThrow();
195
+ });
196
+ });
197
+
198
+ describe('RecoveryTaskSchema', () => {
199
+ it('should require appTarget', () => {
200
+ const task = {
201
+ name: 'Fix CORS config',
202
+ description: 'Update backend CORS settings',
203
+ findingIds: ['AUD-003'],
204
+ acceptanceCriteria: ['CORS allows frontend origin'],
205
+ appTarget: 'backend',
206
+ };
207
+ const result = RecoveryTaskSchema.parse(task);
208
+ expect(result.appTarget).toBe('backend');
209
+ });
210
+
211
+ it('should reject missing appTarget', () => {
212
+ const task = {
213
+ name: 'Fix something',
214
+ description: 'A task',
215
+ findingIds: ['AUD-001'],
216
+ acceptanceCriteria: ['Fixed'],
217
+ };
218
+ expect(() => RecoveryTaskSchema.parse(task)).toThrow();
219
+ });
220
+ });
221
+
222
+ describe('AuditModeOptionsSchema', () => {
223
+ it('should apply defaults', () => {
224
+ const result = AuditModeOptionsSchema.parse({ projectDir: '/tmp/proj' });
225
+ expect(result.depth).toBe(2);
226
+ expect(result.runTests).toBe(true);
227
+ expect(result.strict).toBe(false);
228
+ expect(result.format).toBe('both');
229
+ expect(result.autoRecover).toBe(true);
230
+ expect(result.target).toBe('all');
231
+ });
232
+
233
+ it('should accept overrides', () => {
234
+ const result = AuditModeOptionsSchema.parse({
235
+ projectDir: '/tmp/proj',
236
+ depth: 3,
237
+ strict: true,
238
+ target: 'frontend',
239
+ });
240
+ expect(result.depth).toBe(3);
241
+ expect(result.strict).toBe(true);
242
+ expect(result.target).toBe('frontend');
243
+ });
244
+
245
+ it('should reject depth out of range', () => {
246
+ expect(() =>
247
+ AuditModeOptionsSchema.parse({ projectDir: '/tmp/proj', depth: 5 })
248
+ ).toThrow();
249
+ });
250
+ });
@@ -0,0 +1,158 @@
1
+ /**
2
+ * Tests for database runtime types and schemas
3
+ */
4
+
5
+ import { describe, it, expect } from 'vitest';
6
+ import {
7
+ SetupStepResultSchema,
8
+ SetupResultSchema,
9
+ ReadinessCheckSchema,
10
+ ReadinessResultSchema,
11
+ } from '../../src/types/database-runtime.js';
12
+
13
+ describe('SetupStepResultSchema', () => {
14
+ it('should accept a valid successful step result', () => {
15
+ const result = SetupStepResultSchema.safeParse({
16
+ step: 'check_connection',
17
+ success: true,
18
+ message: 'Database connection verified',
19
+ durationMs: 150,
20
+ });
21
+ expect(result.success).toBe(true);
22
+ });
23
+
24
+ it('should accept a failed step result with error', () => {
25
+ const result = SetupStepResultSchema.safeParse({
26
+ step: 'apply_migrations',
27
+ success: false,
28
+ message: 'Migration failed',
29
+ durationMs: 3000,
30
+ error: 'alembic upgrade head returned exit code 1',
31
+ });
32
+ expect(result.success).toBe(true);
33
+ });
34
+
35
+ it('should reject invalid step names', () => {
36
+ const result = SetupStepResultSchema.safeParse({
37
+ step: 'invalid_step',
38
+ success: true,
39
+ message: 'ok',
40
+ durationMs: 0,
41
+ });
42
+ expect(result.success).toBe(false);
43
+ });
44
+
45
+ it('should reject missing required fields', () => {
46
+ const result = SetupStepResultSchema.safeParse({
47
+ step: 'check_connection',
48
+ success: true,
49
+ });
50
+ expect(result.success).toBe(false);
51
+ });
52
+ });
53
+
54
+ describe('SetupResultSchema', () => {
55
+ it('should accept a successful pipeline result', () => {
56
+ const result = SetupResultSchema.safeParse({
57
+ success: true,
58
+ steps: [
59
+ { step: 'check_connection', success: true, message: 'ok', durationMs: 100 },
60
+ { step: 'apply_migrations', success: true, message: 'ok', durationMs: 200 },
61
+ ],
62
+ totalDurationMs: 300,
63
+ finalStatus: 'ready',
64
+ });
65
+ expect(result.success).toBe(true);
66
+ });
67
+
68
+ it('should accept a failed pipeline result with error', () => {
69
+ const result = SetupResultSchema.safeParse({
70
+ success: false,
71
+ steps: [
72
+ { step: 'check_connection', success: false, message: 'fail', durationMs: 50, error: 'timeout' },
73
+ ],
74
+ totalDurationMs: 50,
75
+ finalStatus: 'error',
76
+ error: 'timeout',
77
+ });
78
+ expect(result.success).toBe(true);
79
+ });
80
+
81
+ it('should accept empty steps array', () => {
82
+ const result = SetupResultSchema.safeParse({
83
+ success: false,
84
+ steps: [],
85
+ totalDurationMs: 0,
86
+ finalStatus: 'error',
87
+ });
88
+ expect(result.success).toBe(true);
89
+ });
90
+
91
+ it('should reject invalid finalStatus', () => {
92
+ const result = SetupResultSchema.safeParse({
93
+ success: true,
94
+ steps: [],
95
+ totalDurationMs: 0,
96
+ finalStatus: 'complete',
97
+ });
98
+ expect(result.success).toBe(false);
99
+ });
100
+ });
101
+
102
+ describe('ReadinessCheckSchema', () => {
103
+ it('should accept all severity levels', () => {
104
+ for (const severity of ['critical', 'warning', 'info'] as const) {
105
+ const result = ReadinessCheckSchema.safeParse({
106
+ name: 'Test Check',
107
+ passed: true,
108
+ message: 'Check passed',
109
+ severity,
110
+ });
111
+ expect(result.success).toBe(true);
112
+ }
113
+ });
114
+
115
+ it('should reject invalid severity', () => {
116
+ const result = ReadinessCheckSchema.safeParse({
117
+ name: 'Test',
118
+ passed: true,
119
+ message: 'ok',
120
+ severity: 'error',
121
+ });
122
+ expect(result.success).toBe(false);
123
+ });
124
+ });
125
+
126
+ describe('ReadinessResultSchema', () => {
127
+ it('should accept a healthy result with checks', () => {
128
+ const result = ReadinessResultSchema.safeParse({
129
+ healthy: true,
130
+ checks: [
131
+ { name: 'DB Connection', passed: true, message: 'Connected', severity: 'critical' },
132
+ { name: 'pgvector', passed: true, message: 'Available', severity: 'warning' },
133
+ ],
134
+ timestamp: '2024-01-01T00:00:00Z',
135
+ });
136
+ expect(result.success).toBe(true);
137
+ });
138
+
139
+ it('should accept an unhealthy result', () => {
140
+ const result = ReadinessResultSchema.safeParse({
141
+ healthy: false,
142
+ checks: [
143
+ { name: 'DB Connection', passed: false, message: 'Cannot connect', severity: 'critical' },
144
+ ],
145
+ timestamp: '2024-01-01T00:00:00Z',
146
+ });
147
+ expect(result.success).toBe(true);
148
+ });
149
+
150
+ it('should accept empty checks array', () => {
151
+ const result = ReadinessResultSchema.safeParse({
152
+ healthy: true,
153
+ checks: [],
154
+ timestamp: '2024-01-01T00:00:00Z',
155
+ });
156
+ expect(result.success).toBe(true);
157
+ });
158
+ });
@@ -0,0 +1,187 @@
1
+ /**
2
+ * Tests for database types and schemas
3
+ */
4
+
5
+ import { describe, it, expect } from 'vitest';
6
+ import {
7
+ DbStatusSchema,
8
+ DbModeSchema,
9
+ DbProviderSchema,
10
+ BackendOrmSchema,
11
+ DbSetupStepSchema,
12
+ DbConfigSchema,
13
+ DEFAULT_DB_CONFIG,
14
+ } from '../../src/types/database.js';
15
+
16
+ describe('DbStatusSchema', () => {
17
+ it('should accept all valid status values', () => {
18
+ const validStatuses = ['unconfigured', 'configured', 'applying', 'ready', 'error'];
19
+ for (const status of validStatuses) {
20
+ expect(DbStatusSchema.safeParse(status).success).toBe(true);
21
+ }
22
+ });
23
+
24
+ it('should reject invalid status values', () => {
25
+ expect(DbStatusSchema.safeParse('pending').success).toBe(false);
26
+ expect(DbStatusSchema.safeParse('active').success).toBe(false);
27
+ expect(DbStatusSchema.safeParse('').success).toBe(false);
28
+ expect(DbStatusSchema.safeParse(123).success).toBe(false);
29
+ });
30
+ });
31
+
32
+ describe('DbModeSchema', () => {
33
+ it('should accept valid modes', () => {
34
+ expect(DbModeSchema.safeParse('local_docker').success).toBe(true);
35
+ expect(DbModeSchema.safeParse('managed').success).toBe(true);
36
+ });
37
+
38
+ it('should reject unconfigured and unknown values', () => {
39
+ expect(DbModeSchema.safeParse('unconfigured').success).toBe(false);
40
+ expect(DbModeSchema.safeParse('cloud').success).toBe(false);
41
+ expect(DbModeSchema.safeParse('').success).toBe(false);
42
+ });
43
+ });
44
+
45
+ describe('DbProviderSchema', () => {
46
+ it('should accept valid providers', () => {
47
+ expect(DbProviderSchema.safeParse('neon').success).toBe(true);
48
+ expect(DbProviderSchema.safeParse('supabase').success).toBe(true);
49
+ expect(DbProviderSchema.safeParse('other').success).toBe(true);
50
+ });
51
+
52
+ it('should reject unknown providers', () => {
53
+ expect(DbProviderSchema.safeParse('aws').success).toBe(false);
54
+ });
55
+ });
56
+
57
+ describe('BackendOrmSchema', () => {
58
+ it('should accept valid ORM values', () => {
59
+ expect(BackendOrmSchema.safeParse('sqlalchemy').success).toBe(true);
60
+ expect(BackendOrmSchema.safeParse('prisma').success).toBe(true);
61
+ expect(BackendOrmSchema.safeParse('drizzle').success).toBe(true);
62
+ });
63
+
64
+ it('should reject unknown ORM values', () => {
65
+ expect(BackendOrmSchema.safeParse('typeorm').success).toBe(false);
66
+ });
67
+ });
68
+
69
+ describe('DbConfigSchema', () => {
70
+ it('should accept a full config with all fields', () => {
71
+ const config = {
72
+ designed: true,
73
+ mode: 'local_docker',
74
+ vectorRequired: true,
75
+ status: 'ready',
76
+ lastError: undefined,
77
+ migrationsApplied: 3,
78
+ readinessCheckedAt: '2024-01-01T00:00:00Z',
79
+ };
80
+ const result = DbConfigSchema.safeParse(config);
81
+ expect(result.success).toBe(true);
82
+ });
83
+
84
+ it('should accept a minimal config without optional mode', () => {
85
+ const config = {
86
+ designed: true,
87
+ vectorRequired: true,
88
+ status: 'unconfigured',
89
+ migrationsApplied: 0,
90
+ };
91
+ const result = DbConfigSchema.safeParse(config);
92
+ expect(result.success).toBe(true);
93
+ if (result.success) {
94
+ expect(result.data.mode).toBeUndefined();
95
+ }
96
+ });
97
+
98
+ it('should allow mode set with status unconfigured (valid during transitions)', () => {
99
+ const config = {
100
+ designed: true,
101
+ mode: 'managed',
102
+ vectorRequired: false,
103
+ status: 'unconfigured',
104
+ migrationsApplied: 0,
105
+ };
106
+ const result = DbConfigSchema.safeParse(config);
107
+ expect(result.success).toBe(true);
108
+ });
109
+
110
+ it('should reject config with invalid status', () => {
111
+ const config = {
112
+ designed: true,
113
+ vectorRequired: true,
114
+ status: 'broken',
115
+ migrationsApplied: 0,
116
+ };
117
+ expect(DbConfigSchema.safeParse(config).success).toBe(false);
118
+ });
119
+
120
+ it('should reject config missing required fields', () => {
121
+ const config = { designed: true };
122
+ expect(DbConfigSchema.safeParse(config).success).toBe(false);
123
+ });
124
+ });
125
+
126
+ describe('DEFAULT_DB_CONFIG', () => {
127
+ it('should have correct initial values', () => {
128
+ expect(DEFAULT_DB_CONFIG.designed).toBe(true);
129
+ expect(DEFAULT_DB_CONFIG.status).toBe('unconfigured');
130
+ expect(DEFAULT_DB_CONFIG.vectorRequired).toBe(true);
131
+ expect(DEFAULT_DB_CONFIG.migrationsApplied).toBe(0);
132
+ });
133
+
134
+ it('should not have mode set (absent until user configures)', () => {
135
+ expect(DEFAULT_DB_CONFIG.mode).toBeUndefined();
136
+ });
137
+
138
+ it('should parse through DbConfigSchema successfully', () => {
139
+ const result = DbConfigSchema.safeParse(DEFAULT_DB_CONFIG);
140
+ expect(result.success).toBe(true);
141
+ });
142
+ });
143
+
144
+ describe('DbSetupStepSchema', () => {
145
+ it('should accept all setup steps', () => {
146
+ const steps = [
147
+ 'check_connection',
148
+ 'ensure_extensions',
149
+ 'apply_migrations',
150
+ 'seed_minimal',
151
+ 'readiness_tests',
152
+ 'mark_ready',
153
+ ];
154
+ for (const step of steps) {
155
+ expect(DbSetupStepSchema.safeParse(step).success).toBe(true);
156
+ }
157
+ });
158
+ });
159
+
160
+ describe('ProjectStateSchema backward compatibility', () => {
161
+ it('should parse existing state without dbConfig field', async () => {
162
+ // Import ProjectStateSchema to verify backward compat
163
+ const { ProjectStateSchema } = await import('../../src/types/workflow.js');
164
+
165
+ const existingState = {
166
+ id: 'test-id',
167
+ name: 'test-project',
168
+ idea: 'Build something',
169
+ language: 'fullstack',
170
+ openaiModel: 'gpt-4o',
171
+ phase: 'plan',
172
+ status: 'pending',
173
+ milestones: [],
174
+ currentMilestone: null,
175
+ currentTask: null,
176
+ consensusHistory: [],
177
+ createdAt: '2024-01-01T00:00:00Z',
178
+ updatedAt: '2024-01-01T00:00:00Z',
179
+ };
180
+
181
+ const result = ProjectStateSchema.safeParse(existingState);
182
+ expect(result.success).toBe(true);
183
+ if (result.success) {
184
+ expect(result.data.dbConfig).toBeUndefined();
185
+ }
186
+ });
187
+ });