edsger 0.45.0 → 0.46.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/dist/commands/workflow/executors/phase-executor.js +3 -1
- package/dist/commands/workflow/phase-orchestrator.js +1 -2
- package/dist/phases/app-store-generation/index.js +1 -2
- package/dist/phases/branch-planning/index.js +1 -2
- package/dist/phases/bug-fixing/analyzer.js +1 -2
- package/dist/phases/code-implementation/index.js +1 -2
- package/dist/phases/code-refine/index.js +1 -2
- package/dist/phases/code-review/index.js +1 -2
- package/dist/phases/code-testing/analyzer.js +1 -2
- package/dist/phases/feature-analysis/index.js +1 -2
- package/dist/phases/functional-testing/analyzer.js +1 -2
- package/dist/phases/growth-analysis/index.js +1 -2
- package/dist/phases/pr-execution/index.js +1 -0
- package/dist/phases/pr-splitting/index.js +1 -2
- package/dist/phases/run-sheet/index.js +7 -7
- package/dist/phases/run-sheet/render.js +3 -1
- package/dist/phases/smoke-test/agent.js +2 -4
- package/dist/phases/smoke-test/index.js +11 -6
- package/dist/phases/technical-design/index.js +1 -2
- package/dist/phases/test-cases-analysis/index.js +1 -2
- package/dist/phases/user-stories-analysis/index.js +1 -2
- package/package.json +3 -3
- package/tsconfig.build.json +4 -0
- package/tsconfig.json +3 -9
- package/dist/api/__tests__/app-store.test.d.ts +0 -7
- package/dist/api/__tests__/app-store.test.js +0 -60
- package/dist/api/__tests__/intelligence.test.d.ts +0 -11
- package/dist/api/__tests__/intelligence.test.js +0 -315
- package/dist/api/features/__tests__/feature-utils.test.d.ts +0 -4
- package/dist/api/features/__tests__/feature-utils.test.js +0 -370
- package/dist/api/features/__tests__/status-updater.test.d.ts +0 -4
- package/dist/api/features/__tests__/status-updater.test.js +0 -88
- package/dist/commands/build/__tests__/build.test.d.ts +0 -5
- package/dist/commands/build/__tests__/build.test.js +0 -206
- package/dist/commands/build/__tests__/detect-project.test.d.ts +0 -6
- package/dist/commands/build/__tests__/detect-project.test.js +0 -160
- package/dist/commands/build/__tests__/run-build.test.d.ts +0 -6
- package/dist/commands/build/__tests__/run-build.test.js +0 -433
- package/dist/commands/intelligence/__tests__/command.test.d.ts +0 -4
- package/dist/commands/intelligence/__tests__/command.test.js +0 -48
- package/dist/commands/workflow/core/__tests__/feature-filter.test.d.ts +0 -5
- package/dist/commands/workflow/core/__tests__/feature-filter.test.js +0 -316
- package/dist/commands/workflow/core/__tests__/pipeline-evaluator.test.d.ts +0 -4
- package/dist/commands/workflow/core/__tests__/pipeline-evaluator.test.js +0 -397
- package/dist/commands/workflow/core/__tests__/state-manager.test.d.ts +0 -4
- package/dist/commands/workflow/core/__tests__/state-manager.test.js +0 -384
- package/dist/config/__tests__/config.test.d.ts +0 -4
- package/dist/config/__tests__/config.test.js +0 -286
- package/dist/config/__tests__/feature-status.test.d.ts +0 -4
- package/dist/config/__tests__/feature-status.test.js +0 -111
- package/dist/errors/__tests__/index.test.d.ts +0 -4
- package/dist/errors/__tests__/index.test.js +0 -349
- package/dist/phases/app-store-generation/__tests__/agent.test.d.ts +0 -5
- package/dist/phases/app-store-generation/__tests__/agent.test.js +0 -142
- package/dist/phases/app-store-generation/__tests__/context.test.d.ts +0 -4
- package/dist/phases/app-store-generation/__tests__/context.test.js +0 -284
- package/dist/phases/app-store-generation/__tests__/prompts.test.d.ts +0 -4
- package/dist/phases/app-store-generation/__tests__/prompts.test.js +0 -122
- package/dist/phases/app-store-generation/__tests__/screenshot-composer.test.d.ts +0 -5
- package/dist/phases/app-store-generation/__tests__/screenshot-composer.test.js +0 -826
- package/dist/phases/code-review/__tests__/diff-utils.test.d.ts +0 -1
- package/dist/phases/code-review/__tests__/diff-utils.test.js +0 -101
- package/dist/phases/intelligence-analysis/__tests__/context.test.d.ts +0 -4
- package/dist/phases/intelligence-analysis/__tests__/context.test.js +0 -192
- package/dist/phases/intelligence-analysis/__tests__/matching.test.d.ts +0 -13
- package/dist/phases/intelligence-analysis/__tests__/matching.test.js +0 -154
- package/dist/phases/intelligence-analysis/__tests__/orchestration.test.d.ts +0 -5
- package/dist/phases/intelligence-analysis/__tests__/orchestration.test.js +0 -378
- package/dist/phases/intelligence-analysis/__tests__/prompts.test.d.ts +0 -4
- package/dist/phases/intelligence-analysis/__tests__/prompts.test.js +0 -33
- package/dist/phases/pr-execution/__tests__/file-assigner.test.d.ts +0 -1
- package/dist/phases/pr-execution/__tests__/file-assigner.test.js +0 -303
- package/dist/phases/pr-resolve/__tests__/checklist-learner.test.d.ts +0 -1
- package/dist/phases/pr-resolve/__tests__/checklist-learner.test.js +0 -157
- package/dist/phases/pr-resolve/__tests__/prompts.test.d.ts +0 -1
- package/dist/phases/pr-resolve/__tests__/prompts.test.js +0 -116
- package/dist/phases/pr-resolve/__tests__/resolve-mapping.test.d.ts +0 -1
- package/dist/phases/pr-resolve/__tests__/resolve-mapping.test.js +0 -138
- package/dist/phases/pr-resolve/__tests__/types.test.d.ts +0 -1
- package/dist/phases/pr-resolve/__tests__/types.test.js +0 -43
- package/dist/phases/pr-resolve/__tests__/workspace.test.d.ts +0 -1
- package/dist/phases/pr-resolve/__tests__/workspace.test.js +0 -111
- package/dist/phases/pr-review/__tests__/prompts.test.d.ts +0 -1
- package/dist/phases/pr-review/__tests__/prompts.test.js +0 -49
- package/dist/phases/pr-review/__tests__/review-comments.test.d.ts +0 -1
- package/dist/phases/pr-review/__tests__/review-comments.test.js +0 -110
- package/dist/phases/pr-shared/__tests__/agent-utils.test.d.ts +0 -1
- package/dist/phases/pr-shared/__tests__/agent-utils.test.js +0 -91
- package/dist/phases/pr-shared/__tests__/context.test.d.ts +0 -1
- package/dist/phases/pr-shared/__tests__/context.test.js +0 -94
- package/dist/phases/pr-splitting/__tests__/import-dep-validator.test.d.ts +0 -1
- package/dist/phases/pr-splitting/__tests__/import-dep-validator.test.js +0 -331
- package/dist/phases/release-sync/__tests__/github.test.d.ts +0 -9
- package/dist/phases/release-sync/__tests__/github.test.js +0 -123
- package/dist/phases/release-sync/__tests__/snapshot.test.d.ts +0 -8
- package/dist/phases/release-sync/__tests__/snapshot.test.js +0 -93
- package/dist/phases/smoke-test/__tests__/agent.test.d.ts +0 -4
- package/dist/phases/smoke-test/__tests__/agent.test.js +0 -85
- package/dist/services/coaching/__tests__/coaching-agent.test.d.ts +0 -1
- package/dist/services/coaching/__tests__/coaching-agent.test.js +0 -74
- package/dist/services/coaching/__tests__/coaching-loop.test.d.ts +0 -1
- package/dist/services/coaching/__tests__/coaching-loop.test.js +0 -59
- package/dist/services/coaching/__tests__/self-rating.test.d.ts +0 -1
- package/dist/services/coaching/__tests__/self-rating.test.js +0 -188
- package/dist/services/phase-hooks/__tests__/bindings-fetcher.test.d.ts +0 -1
- package/dist/services/phase-hooks/__tests__/bindings-fetcher.test.js +0 -122
- package/dist/services/phase-hooks/__tests__/hook-executor.test.d.ts +0 -1
- package/dist/services/phase-hooks/__tests__/hook-executor.test.js +0 -321
- package/dist/services/phase-hooks/__tests__/hook-runner.test.d.ts +0 -1
- package/dist/services/phase-hooks/__tests__/hook-runner.test.js +0 -261
- package/dist/services/phase-hooks/__tests__/plugin-loader.test.d.ts +0 -1
- package/dist/services/phase-hooks/__tests__/plugin-loader.test.js +0 -158
- package/dist/services/video/__tests__/video-pipeline.test.d.ts +0 -6
- package/dist/services/video/__tests__/video-pipeline.test.js +0 -249
- package/dist/workspace/__tests__/workspace-manager.test.d.ts +0 -7
- package/dist/workspace/__tests__/workspace-manager.test.js +0 -52
|
@@ -1,370 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Unit tests for feature utility functions
|
|
3
|
-
*/
|
|
4
|
-
import assert from 'node:assert';
|
|
5
|
-
import { describe, it } from 'node:test';
|
|
6
|
-
import { filterFeaturesByStatus, sortFeaturesByUpdatedAt, } from '../feature-utils.js';
|
|
7
|
-
void describe('Feature Utils', () => {
|
|
8
|
-
void describe('sortFeaturesByUpdatedAt', () => {
|
|
9
|
-
void it('should sort features by updated_at in ascending order (oldest first)', () => {
|
|
10
|
-
const features = [
|
|
11
|
-
{
|
|
12
|
-
id: '1',
|
|
13
|
-
name: 'Feature 1',
|
|
14
|
-
status: 'ready_for_ai',
|
|
15
|
-
product_id: 'test-product',
|
|
16
|
-
updated_at: '2024-01-15T10:00:00Z',
|
|
17
|
-
},
|
|
18
|
-
{
|
|
19
|
-
id: '2',
|
|
20
|
-
name: 'Feature 2',
|
|
21
|
-
status: 'ready_for_ai',
|
|
22
|
-
product_id: 'test-product',
|
|
23
|
-
updated_at: '2024-01-10T10:00:00Z',
|
|
24
|
-
},
|
|
25
|
-
{
|
|
26
|
-
id: '3',
|
|
27
|
-
name: 'Feature 3',
|
|
28
|
-
status: 'ready_for_ai',
|
|
29
|
-
product_id: 'test-product',
|
|
30
|
-
updated_at: '2024-01-20T10:00:00Z',
|
|
31
|
-
},
|
|
32
|
-
];
|
|
33
|
-
const sorted = sortFeaturesByUpdatedAt(features);
|
|
34
|
-
// Should be sorted oldest first: Feature 2, Feature 1, Feature 3
|
|
35
|
-
assert.strictEqual(sorted[0].id, '2', 'First should be Feature 2 (oldest)');
|
|
36
|
-
assert.strictEqual(sorted[1].id, '1', 'Second should be Feature 1');
|
|
37
|
-
assert.strictEqual(sorted[2].id, '3', 'Third should be Feature 3 (newest)');
|
|
38
|
-
});
|
|
39
|
-
void it('should handle features with missing updated_at dates', () => {
|
|
40
|
-
const features = [
|
|
41
|
-
{
|
|
42
|
-
id: '1',
|
|
43
|
-
name: 'Feature 1',
|
|
44
|
-
status: 'ready_for_ai',
|
|
45
|
-
product_id: 'test-product',
|
|
46
|
-
updated_at: '2024-01-15T10:00:00Z',
|
|
47
|
-
},
|
|
48
|
-
{
|
|
49
|
-
id: '2',
|
|
50
|
-
name: 'Feature 2',
|
|
51
|
-
status: 'ready_for_ai',
|
|
52
|
-
product_id: 'test-product',
|
|
53
|
-
// Missing updated_at
|
|
54
|
-
},
|
|
55
|
-
{
|
|
56
|
-
id: '3',
|
|
57
|
-
name: 'Feature 3',
|
|
58
|
-
status: 'ready_for_ai',
|
|
59
|
-
product_id: 'test-product',
|
|
60
|
-
updated_at: '2024-01-10T10:00:00Z',
|
|
61
|
-
},
|
|
62
|
-
];
|
|
63
|
-
const sorted = sortFeaturesByUpdatedAt(features);
|
|
64
|
-
// Feature without updated_at should be treated as epoch (1970-01-01)
|
|
65
|
-
// and sorted first
|
|
66
|
-
assert.strictEqual(sorted[0].id, '2', 'Feature without date should come first');
|
|
67
|
-
assert.strictEqual(sorted[1].id, '3', 'Feature 3 should be second (older date)');
|
|
68
|
-
assert.strictEqual(sorted[2].id, '1', 'Feature 1 should be third (newer date)');
|
|
69
|
-
});
|
|
70
|
-
void it('should handle empty array', () => {
|
|
71
|
-
const features = [];
|
|
72
|
-
const sorted = sortFeaturesByUpdatedAt(features);
|
|
73
|
-
assert.strictEqual(sorted.length, 0, 'Empty array should remain empty');
|
|
74
|
-
});
|
|
75
|
-
void it('should handle single feature', () => {
|
|
76
|
-
const features = [
|
|
77
|
-
{
|
|
78
|
-
id: '1',
|
|
79
|
-
name: 'Feature 1',
|
|
80
|
-
status: 'ready_for_ai',
|
|
81
|
-
product_id: 'test-product',
|
|
82
|
-
updated_at: '2024-01-15T10:00:00Z',
|
|
83
|
-
},
|
|
84
|
-
];
|
|
85
|
-
const sorted = sortFeaturesByUpdatedAt(features);
|
|
86
|
-
assert.strictEqual(sorted.length, 1, 'Should return single feature');
|
|
87
|
-
assert.strictEqual(sorted[0].id, '1', 'Should be the same feature');
|
|
88
|
-
});
|
|
89
|
-
void it('should create a new array and not mutate the original', () => {
|
|
90
|
-
const features = [
|
|
91
|
-
{
|
|
92
|
-
id: '1',
|
|
93
|
-
name: 'Feature 1',
|
|
94
|
-
status: 'ready_for_ai',
|
|
95
|
-
product_id: 'test-product',
|
|
96
|
-
updated_at: '2024-01-15T10:00:00Z',
|
|
97
|
-
},
|
|
98
|
-
{
|
|
99
|
-
id: '2',
|
|
100
|
-
name: 'Feature 2',
|
|
101
|
-
status: 'ready_for_ai',
|
|
102
|
-
product_id: 'test-product',
|
|
103
|
-
updated_at: '2024-01-10T10:00:00Z',
|
|
104
|
-
},
|
|
105
|
-
];
|
|
106
|
-
const originalOrder = features.map((f) => f.id);
|
|
107
|
-
const sorted = sortFeaturesByUpdatedAt(features);
|
|
108
|
-
const originalAfterSort = features.map((f) => f.id);
|
|
109
|
-
// Original array should be unchanged
|
|
110
|
-
assert.deepStrictEqual(originalAfterSort, originalOrder, 'Original array should not be mutated');
|
|
111
|
-
// Sorted array should be different
|
|
112
|
-
assert.notDeepStrictEqual(sorted.map((f) => f.id), originalOrder, 'Sorted array should have different order');
|
|
113
|
-
});
|
|
114
|
-
void it('should handle features with same updated_at timestamps consistently', () => {
|
|
115
|
-
const sameTimestamp = '2024-01-15T10:00:00Z';
|
|
116
|
-
const features = [
|
|
117
|
-
{
|
|
118
|
-
id: '1',
|
|
119
|
-
name: 'Feature 1',
|
|
120
|
-
status: 'ready_for_ai',
|
|
121
|
-
product_id: 'test-product',
|
|
122
|
-
updated_at: sameTimestamp,
|
|
123
|
-
},
|
|
124
|
-
{
|
|
125
|
-
id: '2',
|
|
126
|
-
name: 'Feature 2',
|
|
127
|
-
status: 'ready_for_ai',
|
|
128
|
-
product_id: 'test-product',
|
|
129
|
-
updated_at: sameTimestamp,
|
|
130
|
-
},
|
|
131
|
-
{
|
|
132
|
-
id: '3',
|
|
133
|
-
name: 'Feature 3',
|
|
134
|
-
status: 'ready_for_ai',
|
|
135
|
-
product_id: 'test-product',
|
|
136
|
-
updated_at: sameTimestamp,
|
|
137
|
-
},
|
|
138
|
-
];
|
|
139
|
-
const sorted = sortFeaturesByUpdatedAt(features);
|
|
140
|
-
assert.strictEqual(sorted.length, 3, 'Should return all features');
|
|
141
|
-
// Order should be stable (same as original for equal timestamps)
|
|
142
|
-
// Note: JavaScript sort is stable in modern engines
|
|
143
|
-
});
|
|
144
|
-
void it('should handle various date formats correctly', () => {
|
|
145
|
-
const features = [
|
|
146
|
-
{
|
|
147
|
-
id: '1',
|
|
148
|
-
name: 'Feature 1',
|
|
149
|
-
status: 'ready_for_ai',
|
|
150
|
-
product_id: 'test-product',
|
|
151
|
-
updated_at: '2024-01-15', // Date only
|
|
152
|
-
},
|
|
153
|
-
{
|
|
154
|
-
id: '2',
|
|
155
|
-
name: 'Feature 2',
|
|
156
|
-
status: 'ready_for_ai',
|
|
157
|
-
product_id: 'test-product',
|
|
158
|
-
updated_at: '2024-01-10T10:00:00.000Z', // Full ISO format
|
|
159
|
-
},
|
|
160
|
-
{
|
|
161
|
-
id: '3',
|
|
162
|
-
name: 'Feature 3',
|
|
163
|
-
status: 'ready_for_ai',
|
|
164
|
-
product_id: 'test-product',
|
|
165
|
-
updated_at: '2024-01-20T15:30:00Z', // ISO with time
|
|
166
|
-
},
|
|
167
|
-
];
|
|
168
|
-
const sorted = sortFeaturesByUpdatedAt(features);
|
|
169
|
-
// Should handle different date formats and sort correctly
|
|
170
|
-
assert.strictEqual(sorted[0].id, '2', 'Feature 2 should be first (oldest)');
|
|
171
|
-
assert.strictEqual(sorted[1].id, '1', 'Feature 1 should be second');
|
|
172
|
-
assert.strictEqual(sorted[2].id, '3', 'Feature 3 should be third (newest)');
|
|
173
|
-
});
|
|
174
|
-
});
|
|
175
|
-
void describe('filterFeaturesByStatus', () => {
|
|
176
|
-
void it('should filter features by exact status match', () => {
|
|
177
|
-
const features = [
|
|
178
|
-
{
|
|
179
|
-
id: '1',
|
|
180
|
-
name: 'Feature 1',
|
|
181
|
-
status: 'ready_for_ai',
|
|
182
|
-
product_id: 'test-product',
|
|
183
|
-
},
|
|
184
|
-
{
|
|
185
|
-
id: '2',
|
|
186
|
-
name: 'Feature 2',
|
|
187
|
-
status: 'feature_analysis',
|
|
188
|
-
product_id: 'test-product',
|
|
189
|
-
},
|
|
190
|
-
{
|
|
191
|
-
id: '3',
|
|
192
|
-
name: 'Feature 3',
|
|
193
|
-
status: 'ready_for_ai',
|
|
194
|
-
product_id: 'test-product',
|
|
195
|
-
},
|
|
196
|
-
];
|
|
197
|
-
const filtered = filterFeaturesByStatus(features, 'ready_for_ai');
|
|
198
|
-
assert.strictEqual(filtered.length, 2, 'Should return 2 ready_for_ai features');
|
|
199
|
-
assert.strictEqual(filtered[0].id, '1', 'Should include Feature 1');
|
|
200
|
-
assert.strictEqual(filtered[1].id, '3', 'Should include Feature 3');
|
|
201
|
-
});
|
|
202
|
-
void it('should return empty array when no features match status', () => {
|
|
203
|
-
const features = [
|
|
204
|
-
{
|
|
205
|
-
id: '1',
|
|
206
|
-
name: 'Feature 1',
|
|
207
|
-
status: 'feature_analysis',
|
|
208
|
-
product_id: 'test-product',
|
|
209
|
-
},
|
|
210
|
-
{
|
|
211
|
-
id: '2',
|
|
212
|
-
name: 'Feature 2',
|
|
213
|
-
status: 'code_implementation',
|
|
214
|
-
product_id: 'test-product',
|
|
215
|
-
},
|
|
216
|
-
];
|
|
217
|
-
const filtered = filterFeaturesByStatus(features, 'ready_for_ai');
|
|
218
|
-
assert.strictEqual(filtered.length, 0, 'Should return empty array');
|
|
219
|
-
});
|
|
220
|
-
void it('should handle empty input array', () => {
|
|
221
|
-
const features = [];
|
|
222
|
-
const filtered = filterFeaturesByStatus(features, 'ready_for_ai');
|
|
223
|
-
assert.strictEqual(filtered.length, 0, 'Should return empty array');
|
|
224
|
-
});
|
|
225
|
-
});
|
|
226
|
-
void describe('Integration Tests - Workflow Processing Order', () => {
|
|
227
|
-
void it('should process features in oldest-first order for workflow execution', () => {
|
|
228
|
-
// This test simulates the expected behavior for workflow processing
|
|
229
|
-
const simulatedWorkflowFeatures = [
|
|
230
|
-
{
|
|
231
|
-
id: 'urgent-feature',
|
|
232
|
-
name: 'Urgent Feature',
|
|
233
|
-
status: 'ready_for_ai',
|
|
234
|
-
product_id: 'test-product',
|
|
235
|
-
updated_at: '2024-01-01T08:00:00Z', // Very old - should be processed first
|
|
236
|
-
},
|
|
237
|
-
{
|
|
238
|
-
id: 'recent-feature',
|
|
239
|
-
name: 'Recent Feature',
|
|
240
|
-
status: 'ready_for_ai',
|
|
241
|
-
product_id: 'test-product',
|
|
242
|
-
updated_at: '2024-01-20T10:00:00Z', // Recent - should be processed last
|
|
243
|
-
},
|
|
244
|
-
{
|
|
245
|
-
id: 'medium-feature',
|
|
246
|
-
name: 'Medium Feature',
|
|
247
|
-
status: 'ready_for_ai',
|
|
248
|
-
product_id: 'test-product',
|
|
249
|
-
updated_at: '2024-01-10T12:00:00Z', // Medium age - should be processed second
|
|
250
|
-
},
|
|
251
|
-
];
|
|
252
|
-
const processingOrder = sortFeaturesByUpdatedAt(simulatedWorkflowFeatures);
|
|
253
|
-
// Verify processing order matches expected workflow behavior
|
|
254
|
-
assert.strictEqual(processingOrder[0].id, 'urgent-feature', 'Oldest feature should be processed first in workflow');
|
|
255
|
-
assert.strictEqual(processingOrder[1].id, 'medium-feature', 'Medium-age feature should be processed second in workflow');
|
|
256
|
-
assert.strictEqual(processingOrder[2].id, 'recent-feature', 'Newest feature should be processed last in workflow');
|
|
257
|
-
// Verify timestamps are in correct ascending order
|
|
258
|
-
const timestamps = processingOrder.map((f) => new Date(f.updated_at || 0).getTime());
|
|
259
|
-
for (let i = 1; i < timestamps.length; i++) {
|
|
260
|
-
assert.ok(timestamps[i] >= timestamps[i - 1], `Feature ${i} should have timestamp >= feature ${i - 1}`);
|
|
261
|
-
}
|
|
262
|
-
});
|
|
263
|
-
void it('should maintain consistent ordering across multiple calls', () => {
|
|
264
|
-
const features = [
|
|
265
|
-
{
|
|
266
|
-
id: '1',
|
|
267
|
-
name: 'Feature 1',
|
|
268
|
-
status: 'ready_for_ai',
|
|
269
|
-
product_id: 'test-product',
|
|
270
|
-
updated_at: '2024-01-15T10:00:00Z',
|
|
271
|
-
},
|
|
272
|
-
{
|
|
273
|
-
id: '2',
|
|
274
|
-
name: 'Feature 2',
|
|
275
|
-
status: 'ready_for_ai',
|
|
276
|
-
product_id: 'test-product',
|
|
277
|
-
updated_at: '2024-01-10T10:00:00Z',
|
|
278
|
-
},
|
|
279
|
-
{
|
|
280
|
-
id: '3',
|
|
281
|
-
name: 'Feature 3',
|
|
282
|
-
status: 'ready_for_ai',
|
|
283
|
-
product_id: 'test-product',
|
|
284
|
-
updated_at: '2024-01-20T10:00:00Z',
|
|
285
|
-
},
|
|
286
|
-
];
|
|
287
|
-
// Sort multiple times to ensure consistency
|
|
288
|
-
const sorted1 = sortFeaturesByUpdatedAt(features);
|
|
289
|
-
const sorted2 = sortFeaturesByUpdatedAt(features);
|
|
290
|
-
const sorted3 = sortFeaturesByUpdatedAt(features);
|
|
291
|
-
// All should produce the same order
|
|
292
|
-
assert.deepStrictEqual(sorted1.map((f) => f.id), sorted2.map((f) => f.id), 'First and second sort should produce same order');
|
|
293
|
-
assert.deepStrictEqual(sorted2.map((f) => f.id), sorted3.map((f) => f.id), 'Second and third sort should produce same order');
|
|
294
|
-
});
|
|
295
|
-
void it('should verify oldest-first ordering is critical for fair workflow processing', () => {
|
|
296
|
-
// Test that demonstrates why oldest-first ordering is important
|
|
297
|
-
const featuresWithVariousAges = [
|
|
298
|
-
{
|
|
299
|
-
id: 'very-old-bug',
|
|
300
|
-
name: 'Critical Bug Fix',
|
|
301
|
-
status: 'ready_for_ai',
|
|
302
|
-
product_id: 'test-product',
|
|
303
|
-
updated_at: '2024-01-01T00:00:00Z', // Very old, should be highest priority
|
|
304
|
-
},
|
|
305
|
-
{
|
|
306
|
-
id: 'new-enhancement',
|
|
307
|
-
name: 'Nice to Have Feature',
|
|
308
|
-
status: 'ready_for_ai',
|
|
309
|
-
product_id: 'test-product',
|
|
310
|
-
updated_at: '2024-01-25T00:00:00Z', // Recent, should be lowest priority
|
|
311
|
-
},
|
|
312
|
-
{
|
|
313
|
-
id: 'medium-priority',
|
|
314
|
-
name: 'Important Feature',
|
|
315
|
-
status: 'ready_for_ai',
|
|
316
|
-
product_id: 'test-product',
|
|
317
|
-
updated_at: '2024-01-15T00:00:00Z', // Medium age, should be middle priority
|
|
318
|
-
},
|
|
319
|
-
];
|
|
320
|
-
const orderedFeatures = sortFeaturesByUpdatedAt(featuresWithVariousAges);
|
|
321
|
-
// The oldest feature (potentially a bug that's been waiting) should be processed first
|
|
322
|
-
assert.strictEqual(orderedFeatures[0].id, 'very-old-bug');
|
|
323
|
-
assert.strictEqual(orderedFeatures[1].id, 'medium-priority');
|
|
324
|
-
assert.strictEqual(orderedFeatures[2].id, 'new-enhancement');
|
|
325
|
-
// Verify the business logic: older items get processed before newer ones
|
|
326
|
-
// This ensures fairness and prevents newer features from always jumping ahead
|
|
327
|
-
const ages = orderedFeatures.map((f) => new Date(f.updated_at ?? '').getTime());
|
|
328
|
-
assert.ok(ages[0] <= ages[1] && ages[1] <= ages[2], 'Features should be processed in age order (oldest first)');
|
|
329
|
-
});
|
|
330
|
-
});
|
|
331
|
-
void describe('getReadyForAIFeatures integration behavior', () => {
|
|
332
|
-
void it('should use sortFeaturesByUpdatedAt to ensure oldest-first processing', () => {
|
|
333
|
-
// This test verifies the integration between getReadyForAIFeatures and sortFeaturesByUpdatedAt
|
|
334
|
-
// We can test this indirectly by testing the sorting function that getReadyForAIFeatures uses
|
|
335
|
-
const mockApiResponse = [
|
|
336
|
-
{
|
|
337
|
-
id: 'feat-1',
|
|
338
|
-
name: 'Feature Added Last Week',
|
|
339
|
-
status: 'ready_for_ai',
|
|
340
|
-
product_id: 'product-123',
|
|
341
|
-
updated_at: '2024-01-18T10:00:00Z',
|
|
342
|
-
},
|
|
343
|
-
{
|
|
344
|
-
id: 'feat-2',
|
|
345
|
-
name: 'Feature Added Yesterday',
|
|
346
|
-
status: 'ready_for_ai',
|
|
347
|
-
product_id: 'product-123',
|
|
348
|
-
updated_at: '2024-01-24T15:30:00Z',
|
|
349
|
-
},
|
|
350
|
-
{
|
|
351
|
-
id: 'feat-3',
|
|
352
|
-
name: 'Feature Added Last Month',
|
|
353
|
-
status: 'ready_for_ai',
|
|
354
|
-
product_id: 'product-123',
|
|
355
|
-
updated_at: '2024-01-05T08:00:00Z',
|
|
356
|
-
},
|
|
357
|
-
];
|
|
358
|
-
// This simulates what getReadyForAIFeatures does internally
|
|
359
|
-
const sortedForWorkflow = sortFeaturesByUpdatedAt(mockApiResponse);
|
|
360
|
-
// Verify oldest-first ordering that getReadyForAIFeatures should provide
|
|
361
|
-
assert.strictEqual(sortedForWorkflow[0].id, 'feat-3', 'Oldest feature should be first');
|
|
362
|
-
assert.strictEqual(sortedForWorkflow[1].id, 'feat-1', 'Second oldest should be second');
|
|
363
|
-
assert.strictEqual(sortedForWorkflow[2].id, 'feat-2', 'Newest feature should be last');
|
|
364
|
-
// Verify the function returns the features in the correct order for workflow processing
|
|
365
|
-
const expectedOrder = ['feat-3', 'feat-1', 'feat-2'];
|
|
366
|
-
const actualOrder = sortedForWorkflow.map((f) => f.id);
|
|
367
|
-
assert.deepStrictEqual(actualOrder, expectedOrder, 'Features should be ordered for fair workflow processing');
|
|
368
|
-
});
|
|
369
|
-
});
|
|
370
|
-
});
|
|
@@ -1,88 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Unit tests for status progression logic
|
|
3
|
-
*/
|
|
4
|
-
import assert from 'node:assert';
|
|
5
|
-
import { describe, it } from 'node:test';
|
|
6
|
-
import { STATUS_PROGRESSION_ORDER } from '../../../config/feature-status.js';
|
|
7
|
-
import { isForwardProgression } from '../status-updater.js';
|
|
8
|
-
void describe('Status Progression Logic', () => {
|
|
9
|
-
void describe('isForwardProgression', () => {
|
|
10
|
-
void it('should allow forward progression', () => {
|
|
11
|
-
assert.strictEqual(isForwardProgression('backlog', 'ready_for_ai'), true, 'Should allow progression from backlog to ready_for_ai');
|
|
12
|
-
assert.strictEqual(isForwardProgression('feature_analysis', 'technical_design'), true, 'Should allow progression from feature_analysis to technical_design');
|
|
13
|
-
assert.strictEqual(isForwardProgression('code_implementation', 'shipped'), true, 'Should allow progression from code_implementation to shipped');
|
|
14
|
-
});
|
|
15
|
-
void it('should allow staying at same status', () => {
|
|
16
|
-
assert.strictEqual(isForwardProgression('backlog', 'backlog'), true, 'Should allow staying at backlog status');
|
|
17
|
-
assert.strictEqual(isForwardProgression('feature_analysis', 'feature_analysis'), true, 'Should allow staying at feature_analysis status');
|
|
18
|
-
assert.strictEqual(isForwardProgression('shipped', 'shipped'), true, 'Should allow staying at shipped status');
|
|
19
|
-
});
|
|
20
|
-
void it('should prevent backward progression', () => {
|
|
21
|
-
assert.strictEqual(isForwardProgression('ready_for_ai', 'backlog'), false, 'Should prevent regression from ready_for_ai to backlog');
|
|
22
|
-
assert.strictEqual(isForwardProgression('technical_design', 'feature_analysis'), false, 'Should prevent regression from technical_design to feature_analysis');
|
|
23
|
-
assert.strictEqual(isForwardProgression('shipped', 'code_implementation'), false, 'Should prevent regression from shipped to code_implementation');
|
|
24
|
-
});
|
|
25
|
-
void it('should handle edge cases correctly', () => {
|
|
26
|
-
// First status to last status
|
|
27
|
-
assert.strictEqual(isForwardProgression('backlog', 'shipped'), true, 'Should allow progression from first to last status');
|
|
28
|
-
// Last status to first status
|
|
29
|
-
assert.strictEqual(isForwardProgression('shipped', 'backlog'), false, 'Should prevent regression from last to first status');
|
|
30
|
-
});
|
|
31
|
-
void it('should handle testing workflow correctly', () => {
|
|
32
|
-
// Allow progression through testing states
|
|
33
|
-
assert.strictEqual(isForwardProgression('functional_testing', 'testing_in_progress'), true, 'Should allow progression to testing_in_progress');
|
|
34
|
-
assert.strictEqual(isForwardProgression('testing_in_progress', 'testing_passed'), true, 'Should allow progression to testing_passed');
|
|
35
|
-
// Allow retry after failed testing
|
|
36
|
-
assert.strictEqual(isForwardProgression('testing_in_progress', 'testing_failed'), true, 'Should allow marking tests as failed');
|
|
37
|
-
// Prevent going back to testing_in_progress from passed
|
|
38
|
-
assert.strictEqual(isForwardProgression('testing_passed', 'testing_in_progress'), false, 'Should prevent going back from testing_passed to testing_in_progress');
|
|
39
|
-
});
|
|
40
|
-
});
|
|
41
|
-
void describe('STATUS_PROGRESSION_ORDER', () => {
|
|
42
|
-
void it('should contain all expected statuses', () => {
|
|
43
|
-
const expectedStatuses = [
|
|
44
|
-
'backlog',
|
|
45
|
-
'ready_for_ai',
|
|
46
|
-
'feature_analysis',
|
|
47
|
-
'feature_analysis_verification',
|
|
48
|
-
'technical_design',
|
|
49
|
-
'technical_design_verification',
|
|
50
|
-
'branch_planning',
|
|
51
|
-
'branch_planning_verification',
|
|
52
|
-
'code_implementation',
|
|
53
|
-
'code_implementation_verification',
|
|
54
|
-
'code_refine',
|
|
55
|
-
'code_refine_verification',
|
|
56
|
-
'bug_fixing',
|
|
57
|
-
'code_review',
|
|
58
|
-
'functional_testing',
|
|
59
|
-
'testing_in_progress',
|
|
60
|
-
'testing_passed',
|
|
61
|
-
'testing_failed',
|
|
62
|
-
'ready_for_review',
|
|
63
|
-
'shipped',
|
|
64
|
-
];
|
|
65
|
-
assert.strictEqual(STATUS_PROGRESSION_ORDER.length, expectedStatuses.length, 'Should contain the correct number of statuses');
|
|
66
|
-
for (const status of expectedStatuses) {
|
|
67
|
-
assert.ok(STATUS_PROGRESSION_ORDER.includes(status), `Should include status: ${status}`);
|
|
68
|
-
}
|
|
69
|
-
});
|
|
70
|
-
void it('should have correct ordering for key workflow phases', () => {
|
|
71
|
-
const backlogIndex = STATUS_PROGRESSION_ORDER.indexOf('backlog');
|
|
72
|
-
const readyForDevIndex = STATUS_PROGRESSION_ORDER.indexOf('ready_for_ai');
|
|
73
|
-
const featureAnalysisIndex = STATUS_PROGRESSION_ORDER.indexOf('feature_analysis');
|
|
74
|
-
const technicalDesignIndex = STATUS_PROGRESSION_ORDER.indexOf('technical_design');
|
|
75
|
-
const codeImplementationIndex = STATUS_PROGRESSION_ORDER.indexOf('code_implementation');
|
|
76
|
-
const shippedIndex = STATUS_PROGRESSION_ORDER.indexOf('shipped');
|
|
77
|
-
assert.ok(backlogIndex < readyForDevIndex, 'backlog should come before ready_for_ai');
|
|
78
|
-
assert.ok(readyForDevIndex < featureAnalysisIndex, 'ready_for_ai should come before feature_analysis');
|
|
79
|
-
assert.ok(featureAnalysisIndex < technicalDesignIndex, 'feature_analysis should come before technical_design');
|
|
80
|
-
assert.ok(technicalDesignIndex < codeImplementationIndex, 'technical_design should come before code_implementation');
|
|
81
|
-
assert.ok(codeImplementationIndex < shippedIndex, 'code_implementation should come before shipped');
|
|
82
|
-
});
|
|
83
|
-
void it('should not have duplicate statuses', () => {
|
|
84
|
-
const uniqueStatuses = new Set(STATUS_PROGRESSION_ORDER);
|
|
85
|
-
assert.strictEqual(uniqueStatuses.size, STATUS_PROGRESSION_ORDER.length, 'Should not contain duplicate statuses');
|
|
86
|
-
});
|
|
87
|
-
});
|
|
88
|
-
});
|