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.
- package/README.md +240 -32
- package/cheatsheet.md +407 -0
- package/dist/cli/commands/db.d.ts +10 -0
- package/dist/cli/commands/db.d.ts.map +1 -0
- package/dist/cli/commands/db.js +240 -0
- package/dist/cli/commands/db.js.map +1 -0
- package/dist/cli/commands/doctor.d.ts +18 -0
- package/dist/cli/commands/doctor.d.ts.map +1 -0
- package/dist/cli/commands/doctor.js +255 -0
- package/dist/cli/commands/doctor.js.map +1 -0
- package/dist/cli/commands/index.d.ts +2 -0
- package/dist/cli/commands/index.d.ts.map +1 -1
- package/dist/cli/commands/index.js +2 -0
- package/dist/cli/commands/index.js.map +1 -1
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/index.js +3 -1
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/interactive.d.ts.map +1 -1
- package/dist/cli/interactive.js +96 -0
- package/dist/cli/interactive.js.map +1 -1
- package/dist/generators/admin-wizard.d.ts +25 -0
- package/dist/generators/admin-wizard.d.ts.map +1 -0
- package/dist/generators/admin-wizard.js +123 -0
- package/dist/generators/admin-wizard.js.map +1 -0
- package/dist/generators/all.d.ts.map +1 -1
- package/dist/generators/all.js +10 -3
- package/dist/generators/all.js.map +1 -1
- package/dist/generators/database.d.ts +58 -0
- package/dist/generators/database.d.ts.map +1 -0
- package/dist/generators/database.js +229 -0
- package/dist/generators/database.js.map +1 -0
- package/dist/generators/fullstack.d.ts.map +1 -1
- package/dist/generators/fullstack.js +23 -7
- package/dist/generators/fullstack.js.map +1 -1
- package/dist/generators/index.d.ts +2 -0
- package/dist/generators/index.d.ts.map +1 -1
- package/dist/generators/index.js +2 -0
- package/dist/generators/index.js.map +1 -1
- package/dist/generators/templates/admin-wizard-python.d.ts +32 -0
- package/dist/generators/templates/admin-wizard-python.d.ts.map +1 -0
- package/dist/generators/templates/admin-wizard-python.js +425 -0
- package/dist/generators/templates/admin-wizard-python.js.map +1 -0
- package/dist/generators/templates/admin-wizard-react.d.ts +48 -0
- package/dist/generators/templates/admin-wizard-react.d.ts.map +1 -0
- package/dist/generators/templates/admin-wizard-react.js +554 -0
- package/dist/generators/templates/admin-wizard-react.js.map +1 -0
- package/dist/generators/templates/database-docker.d.ts +23 -0
- package/dist/generators/templates/database-docker.d.ts.map +1 -0
- package/dist/generators/templates/database-docker.js +221 -0
- package/dist/generators/templates/database-docker.js.map +1 -0
- package/dist/generators/templates/database-python.d.ts +54 -0
- package/dist/generators/templates/database-python.d.ts.map +1 -0
- package/dist/generators/templates/database-python.js +723 -0
- package/dist/generators/templates/database-python.js.map +1 -0
- package/dist/generators/templates/database-typescript.d.ts +34 -0
- package/dist/generators/templates/database-typescript.d.ts.map +1 -0
- package/dist/generators/templates/database-typescript.js +232 -0
- package/dist/generators/templates/database-typescript.js.map +1 -0
- package/dist/generators/templates/fullstack.d.ts.map +1 -1
- package/dist/generators/templates/fullstack.js +29 -0
- package/dist/generators/templates/fullstack.js.map +1 -1
- package/dist/generators/templates/index.d.ts +5 -0
- package/dist/generators/templates/index.d.ts.map +1 -1
- package/dist/generators/templates/index.js +5 -0
- package/dist/generators/templates/index.js.map +1 -1
- package/dist/state/index.d.ts +10 -0
- package/dist/state/index.d.ts.map +1 -1
- package/dist/state/index.js +22 -0
- package/dist/state/index.js.map +1 -1
- package/dist/types/consensus.d.ts +3 -0
- package/dist/types/consensus.d.ts.map +1 -1
- package/dist/types/consensus.js +1 -0
- package/dist/types/consensus.js.map +1 -1
- package/dist/types/database-runtime.d.ts +86 -0
- package/dist/types/database-runtime.d.ts.map +1 -0
- package/dist/types/database-runtime.js +61 -0
- package/dist/types/database-runtime.js.map +1 -0
- package/dist/types/database.d.ts +85 -0
- package/dist/types/database.d.ts.map +1 -0
- package/dist/types/database.js +71 -0
- package/dist/types/database.js.map +1 -0
- package/dist/types/index.d.ts +3 -0
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/index.js +6 -0
- package/dist/types/index.js.map +1 -1
- package/dist/types/tester.d.ts +138 -0
- package/dist/types/tester.d.ts.map +1 -0
- package/dist/types/tester.js +110 -0
- package/dist/types/tester.js.map +1 -0
- package/dist/types/workflow.d.ts +166 -0
- package/dist/types/workflow.d.ts.map +1 -1
- package/dist/types/workflow.js +14 -0
- package/dist/types/workflow.js.map +1 -1
- package/dist/workflow/db-setup-runner.d.ts +63 -0
- package/dist/workflow/db-setup-runner.d.ts.map +1 -0
- package/dist/workflow/db-setup-runner.js +336 -0
- package/dist/workflow/db-setup-runner.js.map +1 -0
- package/dist/workflow/db-state-machine.d.ts +30 -0
- package/dist/workflow/db-state-machine.d.ts.map +1 -0
- package/dist/workflow/db-state-machine.js +51 -0
- package/dist/workflow/db-state-machine.js.map +1 -0
- package/dist/workflow/execution-mode.js +2 -2
- package/dist/workflow/execution-mode.js.map +1 -1
- package/dist/workflow/index.d.ts +3 -0
- package/dist/workflow/index.d.ts.map +1 -1
- package/dist/workflow/index.js +3 -0
- package/dist/workflow/index.js.map +1 -1
- package/dist/workflow/task-workflow.d.ts +5 -0
- package/dist/workflow/task-workflow.d.ts.map +1 -1
- package/dist/workflow/task-workflow.js +172 -6
- package/dist/workflow/task-workflow.js.map +1 -1
- package/dist/workflow/tester.d.ts +120 -0
- package/dist/workflow/tester.d.ts.map +1 -0
- package/dist/workflow/tester.js +589 -0
- package/dist/workflow/tester.js.map +1 -0
- package/dist/workflow/workflow-logger.d.ts +1 -1
- package/dist/workflow/workflow-logger.d.ts.map +1 -1
- package/dist/workflow/workflow-logger.js.map +1 -1
- package/package.json +1 -1
- package/src/cli/commands/db.ts +281 -0
- package/src/cli/commands/doctor.ts +273 -0
- package/src/cli/commands/index.ts +2 -0
- package/src/cli/index.ts +4 -0
- package/src/cli/interactive.ts +102 -0
- package/src/generators/admin-wizard.ts +146 -0
- package/src/generators/all.ts +10 -3
- package/src/generators/database.ts +286 -0
- package/src/generators/fullstack.ts +26 -9
- package/src/generators/index.ts +12 -0
- package/src/generators/templates/admin-wizard-python.ts +431 -0
- package/src/generators/templates/admin-wizard-react.ts +560 -0
- package/src/generators/templates/database-docker.ts +227 -0
- package/src/generators/templates/database-python.ts +734 -0
- package/src/generators/templates/database-typescript.ts +238 -0
- package/src/generators/templates/fullstack.ts +29 -0
- package/src/generators/templates/index.ts +5 -0
- package/src/state/index.ts +29 -0
- package/src/types/consensus.ts +3 -0
- package/src/types/database-runtime.ts +69 -0
- package/src/types/database.ts +84 -0
- package/src/types/index.ts +50 -0
- package/src/types/tester.ts +136 -0
- package/src/types/workflow.ts +31 -0
- package/src/workflow/db-setup-runner.ts +391 -0
- package/src/workflow/db-state-machine.ts +58 -0
- package/src/workflow/execution-mode.ts +2 -2
- package/src/workflow/index.ts +3 -0
- package/src/workflow/task-workflow.ts +227 -5
- package/src/workflow/tester.ts +723 -0
- package/src/workflow/workflow-logger.ts +2 -0
- package/tests/generators/admin-wizard-orchestrator.test.ts +64 -0
- package/tests/generators/admin-wizard-templates.test.ts +366 -0
- package/tests/generators/cross-phase-integration.test.ts +383 -0
- package/tests/generators/database.test.ts +456 -0
- package/tests/generators/fe-be-db-integration.test.ts +613 -0
- package/tests/types/database-runtime.test.ts +158 -0
- package/tests/types/database.test.ts +187 -0
- package/tests/types/tester.test.ts +174 -0
- package/tests/workflow/db-setup-runner.test.ts +211 -0
- package/tests/workflow/db-state-machine.test.ts +117 -0
- package/tests/workflow/tester.test.ts +401 -0
|
@@ -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
|
+
});
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests for Tester (QA) type schemas
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { describe, it, expect } from 'vitest';
|
|
6
|
+
import {
|
|
7
|
+
TestVerdictSchema,
|
|
8
|
+
TestCommandSchema,
|
|
9
|
+
TestCaseSchema,
|
|
10
|
+
TestPlanOutputSchema,
|
|
11
|
+
TestRunReviewSchema,
|
|
12
|
+
FixStepSchema,
|
|
13
|
+
TestFixPlanSchema,
|
|
14
|
+
} from '../../src/types/tester.js';
|
|
15
|
+
|
|
16
|
+
describe('TestVerdictSchema', () => {
|
|
17
|
+
it('should accept valid verdicts', () => {
|
|
18
|
+
expect(TestVerdictSchema.parse('PASS')).toBe('PASS');
|
|
19
|
+
expect(TestVerdictSchema.parse('PASS_WITH_NOTES')).toBe('PASS_WITH_NOTES');
|
|
20
|
+
expect(TestVerdictSchema.parse('FAIL')).toBe('FAIL');
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
it('should reject invalid verdict strings', () => {
|
|
24
|
+
expect(() => TestVerdictSchema.parse('pass')).toThrow();
|
|
25
|
+
expect(() => TestVerdictSchema.parse('UNKNOWN')).toThrow();
|
|
26
|
+
expect(() => TestVerdictSchema.parse('')).toThrow();
|
|
27
|
+
});
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
describe('TestCommandSchema', () => {
|
|
31
|
+
it('should accept a valid command', () => {
|
|
32
|
+
const cmd = { command: 'npm test', purpose: 'Run unit tests', required: true };
|
|
33
|
+
expect(TestCommandSchema.parse(cmd)).toEqual(cmd);
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
it('should accept command with optional cwd', () => {
|
|
37
|
+
const cmd = { command: 'pytest', cwd: 'backend/', purpose: 'Run backend tests', required: false };
|
|
38
|
+
expect(TestCommandSchema.parse(cmd)).toEqual(cmd);
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
it('should reject empty command string', () => {
|
|
42
|
+
expect(() => TestCommandSchema.parse({ command: '', purpose: 'test', required: true })).toThrow();
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
it('should reject missing required fields', () => {
|
|
46
|
+
expect(() => TestCommandSchema.parse({ command: 'npm test' })).toThrow();
|
|
47
|
+
expect(() => TestCommandSchema.parse({ purpose: 'test', required: true })).toThrow();
|
|
48
|
+
});
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
describe('TestCaseSchema', () => {
|
|
52
|
+
const validCase = {
|
|
53
|
+
id: 'TC-1',
|
|
54
|
+
category: 'unit',
|
|
55
|
+
description: 'Test user login',
|
|
56
|
+
acceptanceCriteria: 'Returns 200 with valid credentials',
|
|
57
|
+
evidenceRequired: 'Test output showing assertion passed',
|
|
58
|
+
priority: 'critical' as const,
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
it('should accept a valid test case', () => {
|
|
62
|
+
expect(TestCaseSchema.parse(validCase)).toEqual(validCase);
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
it('should reject invalid priority values', () => {
|
|
66
|
+
expect(() => TestCaseSchema.parse({ ...validCase, priority: 'urgent' })).toThrow();
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
it('should reject empty id', () => {
|
|
70
|
+
expect(() => TestCaseSchema.parse({ ...validCase, id: '' })).toThrow();
|
|
71
|
+
});
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
describe('TestPlanOutputSchema', () => {
|
|
75
|
+
const validPlan = {
|
|
76
|
+
summary: 'Tests login feature risks',
|
|
77
|
+
scope: ['backend'] as const,
|
|
78
|
+
testMatrix: [{
|
|
79
|
+
id: 'TC-1', category: 'unit', description: 'Login test',
|
|
80
|
+
acceptanceCriteria: 'passes', evidenceRequired: 'output', priority: 'high' as const,
|
|
81
|
+
}],
|
|
82
|
+
commands: [{ command: 'pytest', purpose: 'Run tests', required: true }],
|
|
83
|
+
riskFocus: ['Authentication bypass'],
|
|
84
|
+
evidenceRequired: ['test output'],
|
|
85
|
+
minimumVerification: ['build check'],
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
it('should accept a valid test plan', () => {
|
|
89
|
+
const result = TestPlanOutputSchema.parse(validPlan);
|
|
90
|
+
expect(result.summary).toBe('Tests login feature risks');
|
|
91
|
+
expect(result.commands).toHaveLength(1);
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
it('should accept plan with noTestsRationale', () => {
|
|
95
|
+
const plan = { ...validPlan, noTestsRationale: 'Config-only change, no code logic' };
|
|
96
|
+
expect(TestPlanOutputSchema.parse(plan).noTestsRationale).toBe('Config-only change, no code logic');
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
it('should reject empty commands array', () => {
|
|
100
|
+
expect(() => TestPlanOutputSchema.parse({ ...validPlan, commands: [] })).toThrow();
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
it('should reject empty scope array', () => {
|
|
104
|
+
expect(() => TestPlanOutputSchema.parse({ ...validPlan, scope: [] })).toThrow();
|
|
105
|
+
});
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
describe('TestRunReviewSchema', () => {
|
|
109
|
+
const validReview = {
|
|
110
|
+
verdict: 'PASS' as const,
|
|
111
|
+
summary: 'All tests passed',
|
|
112
|
+
evidenceReviewed: ['test output'],
|
|
113
|
+
failures: [],
|
|
114
|
+
gaps: [],
|
|
115
|
+
recommendations: [],
|
|
116
|
+
requiresConsensus: false,
|
|
117
|
+
};
|
|
118
|
+
|
|
119
|
+
it('should accept a valid PASS review', () => {
|
|
120
|
+
expect(TestRunReviewSchema.parse(validReview).verdict).toBe('PASS');
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
it('should accept a FAIL review with failures', () => {
|
|
124
|
+
const review = {
|
|
125
|
+
...validReview,
|
|
126
|
+
verdict: 'FAIL' as const,
|
|
127
|
+
failures: ['Login test failed'],
|
|
128
|
+
requiresConsensus: true,
|
|
129
|
+
};
|
|
130
|
+
expect(TestRunReviewSchema.parse(review).requiresConsensus).toBe(true);
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
it('should reject missing verdict', () => {
|
|
134
|
+
const { verdict, ...rest } = validReview;
|
|
135
|
+
expect(() => TestRunReviewSchema.parse(rest)).toThrow();
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
it('should reject empty evidenceReviewed', () => {
|
|
139
|
+
expect(() => TestRunReviewSchema.parse({ ...validReview, evidenceReviewed: [] })).toThrow();
|
|
140
|
+
});
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
describe('TestFixPlanSchema', () => {
|
|
144
|
+
const validFix = {
|
|
145
|
+
failedCriteria: ['Login returns 200'],
|
|
146
|
+
rootCauseAnalysis: 'Password hashing function is not async',
|
|
147
|
+
fixSteps: [{ file: 'src/auth.ts', change: 'Add await', reason: 'Async hash' }],
|
|
148
|
+
regressionRisks: ['May affect session handling'],
|
|
149
|
+
retestStrategy: 'Re-run login test suite',
|
|
150
|
+
};
|
|
151
|
+
|
|
152
|
+
it('should accept a valid fix plan', () => {
|
|
153
|
+
expect(TestFixPlanSchema.parse(validFix).rootCauseAnalysis).toContain('async');
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
it('should reject empty fixSteps', () => {
|
|
157
|
+
expect(() => TestFixPlanSchema.parse({ ...validFix, fixSteps: [] })).toThrow();
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
it('should reject empty failedCriteria', () => {
|
|
161
|
+
expect(() => TestFixPlanSchema.parse({ ...validFix, failedCriteria: [] })).toThrow();
|
|
162
|
+
});
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
describe('FixStepSchema', () => {
|
|
166
|
+
it('should accept valid fix step', () => {
|
|
167
|
+
const step = { file: 'src/index.ts', change: 'Fix import', reason: 'Missing module' };
|
|
168
|
+
expect(FixStepSchema.parse(step)).toEqual(step);
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
it('should reject empty file', () => {
|
|
172
|
+
expect(() => FixStepSchema.parse({ file: '', change: 'Fix', reason: 'Bug' })).toThrow();
|
|
173
|
+
});
|
|
174
|
+
});
|