@lumenflow/cli 2.20.1 → 2.21.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (111) hide show
  1. package/README.md +8 -4
  2. package/dist/hooks/enforcement-checks.js +120 -0
  3. package/dist/hooks/enforcement-checks.js.map +1 -1
  4. package/dist/init-lane-validation.js +141 -0
  5. package/dist/init-lane-validation.js.map +1 -0
  6. package/dist/init-templates.js +36 -8
  7. package/dist/init-templates.js.map +1 -1
  8. package/dist/init.js +27 -58
  9. package/dist/init.js.map +1 -1
  10. package/dist/initiative-create.js +35 -4
  11. package/dist/initiative-create.js.map +1 -1
  12. package/dist/lane-lifecycle-process.js +364 -0
  13. package/dist/lane-lifecycle-process.js.map +1 -0
  14. package/dist/lane-lock.js +41 -0
  15. package/dist/lane-lock.js.map +1 -0
  16. package/dist/lane-setup.js +55 -0
  17. package/dist/lane-setup.js.map +1 -0
  18. package/dist/lane-status.js +38 -0
  19. package/dist/lane-status.js.map +1 -0
  20. package/dist/lane-validate.js +43 -0
  21. package/dist/lane-validate.js.map +1 -0
  22. package/dist/onboarding-smoke-test.js +17 -0
  23. package/dist/onboarding-smoke-test.js.map +1 -1
  24. package/dist/public-manifest.js +28 -0
  25. package/dist/public-manifest.js.map +1 -1
  26. package/dist/wu-claim-cloud.js +16 -0
  27. package/dist/wu-claim-cloud.js.map +1 -1
  28. package/dist/wu-claim.js +12 -2
  29. package/dist/wu-claim.js.map +1 -1
  30. package/dist/wu-create-content.js +8 -2
  31. package/dist/wu-create-content.js.map +1 -1
  32. package/dist/wu-create-validation.js +5 -3
  33. package/dist/wu-create-validation.js.map +1 -1
  34. package/dist/wu-create.js +21 -1
  35. package/dist/wu-create.js.map +1 -1
  36. package/dist/wu-done.js +57 -8
  37. package/dist/wu-done.js.map +1 -1
  38. package/dist/wu-prep.js +22 -0
  39. package/dist/wu-prep.js.map +1 -1
  40. package/package.json +15 -11
  41. package/dist/__tests__/agent-log-issue.test.js +0 -56
  42. package/dist/__tests__/agent-spawn-coordination.test.js +0 -451
  43. package/dist/__tests__/backlog-prune.test.js +0 -478
  44. package/dist/__tests__/cli-entry-point.test.js +0 -160
  45. package/dist/__tests__/cli-subprocess.test.js +0 -89
  46. package/dist/__tests__/commands/integrate.test.js +0 -165
  47. package/dist/__tests__/commands.test.js +0 -271
  48. package/dist/__tests__/deps-operations.test.js +0 -206
  49. package/dist/__tests__/doctor.test.js +0 -510
  50. package/dist/__tests__/file-operations.test.js +0 -906
  51. package/dist/__tests__/flow-report.test.js +0 -24
  52. package/dist/__tests__/gates-config.test.js +0 -303
  53. package/dist/__tests__/gates-integration-tests.test.js +0 -112
  54. package/dist/__tests__/git-operations.test.js +0 -668
  55. package/dist/__tests__/guard-main-branch.test.js +0 -79
  56. package/dist/__tests__/guards-validation.test.js +0 -416
  57. package/dist/__tests__/hooks/enforcement.test.js +0 -279
  58. package/dist/__tests__/init-config-lanes.test.js +0 -131
  59. package/dist/__tests__/init-docs-structure.test.js +0 -152
  60. package/dist/__tests__/init-greenfield.test.js +0 -247
  61. package/dist/__tests__/init-lane-inference.test.js +0 -125
  62. package/dist/__tests__/init-onboarding-docs.test.js +0 -132
  63. package/dist/__tests__/init-quick-ref.test.js +0 -144
  64. package/dist/__tests__/init-scripts.test.js +0 -207
  65. package/dist/__tests__/init-template-portability.test.js +0 -96
  66. package/dist/__tests__/init.test.js +0 -968
  67. package/dist/__tests__/initiative-add-wu.test.js +0 -490
  68. package/dist/__tests__/initiative-e2e.test.js +0 -442
  69. package/dist/__tests__/initiative-plan-replacement.test.js +0 -161
  70. package/dist/__tests__/initiative-plan.test.js +0 -340
  71. package/dist/__tests__/initiative-remove-wu.test.js +0 -458
  72. package/dist/__tests__/lumenflow-upgrade.test.js +0 -260
  73. package/dist/__tests__/mem-cleanup-execution.test.js +0 -19
  74. package/dist/__tests__/memory-integration.test.js +0 -333
  75. package/dist/__tests__/merge-block.test.js +0 -220
  76. package/dist/__tests__/metrics-cli.test.js +0 -619
  77. package/dist/__tests__/metrics-snapshot.test.js +0 -24
  78. package/dist/__tests__/no-beacon-references-docs.test.js +0 -30
  79. package/dist/__tests__/no-beacon-references.test.js +0 -39
  80. package/dist/__tests__/onboarding-smoke-test.test.js +0 -211
  81. package/dist/__tests__/path-centralization-cli.test.js +0 -234
  82. package/dist/__tests__/plan-create.test.js +0 -126
  83. package/dist/__tests__/plan-edit.test.js +0 -157
  84. package/dist/__tests__/plan-link.test.js +0 -239
  85. package/dist/__tests__/plan-promote.test.js +0 -181
  86. package/dist/__tests__/release.test.js +0 -372
  87. package/dist/__tests__/rotate-progress.test.js +0 -127
  88. package/dist/__tests__/safe-git.test.js +0 -190
  89. package/dist/__tests__/session-coordinator.test.js +0 -109
  90. package/dist/__tests__/state-bootstrap.test.js +0 -432
  91. package/dist/__tests__/state-doctor.test.js +0 -328
  92. package/dist/__tests__/sync-templates.test.js +0 -255
  93. package/dist/__tests__/templates-sync.test.js +0 -219
  94. package/dist/__tests__/trace-gen.test.js +0 -115
  95. package/dist/__tests__/wu-create-required-fields.test.js +0 -143
  96. package/dist/__tests__/wu-create-strict.test.js +0 -118
  97. package/dist/__tests__/wu-create.test.js +0 -121
  98. package/dist/__tests__/wu-done-auto-cleanup.test.js +0 -135
  99. package/dist/__tests__/wu-done-docs-only-policy.test.js +0 -20
  100. package/dist/__tests__/wu-done-staging-whitelist.test.js +0 -35
  101. package/dist/__tests__/wu-done.test.js +0 -36
  102. package/dist/__tests__/wu-edit-strict.test.js +0 -109
  103. package/dist/__tests__/wu-edit.test.js +0 -119
  104. package/dist/__tests__/wu-lifecycle-integration.test.js +0 -388
  105. package/dist/__tests__/wu-prep-default-exec.test.js +0 -35
  106. package/dist/__tests__/wu-prep.test.js +0 -140
  107. package/dist/__tests__/wu-proto.test.js +0 -97
  108. package/dist/__tests__/wu-validate-strict.test.js +0 -113
  109. package/dist/__tests__/wu-validate.test.js +0 -36
  110. package/dist/spawn-list.js +0 -143
  111. package/dist/spawn-list.js.map +0 -1
@@ -1,372 +0,0 @@
1
- /**
2
- * Tests for release command
3
- *
4
- * Verifies that the release command:
5
- * - Validates version format (semver)
6
- * - Bumps all @lumenflow/* package versions
7
- * - Uses micro-worktree isolation for version commit
8
- * - Builds all packages
9
- * - Publishes to npm with proper auth
10
- * - Creates git tag vX.Y.Z
11
- *
12
- * WU-1074: Add release command for npm publishing
13
- */
14
- import { describe, it, expect, beforeEach, afterEach } from 'vitest';
15
- import { existsSync, mkdirSync, writeFileSync, rmSync } from 'node:fs';
16
- import { join } from 'node:path';
17
- import { tmpdir } from 'node:os';
18
- // Import functions under test
19
- import { validateSemver, findPackageJsonPaths, updatePackageVersions, buildCommitMessage, buildTagName, } from '../release.js';
20
- describe('release command', () => {
21
- describe('validateSemver', () => {
22
- it('should accept valid semver versions', () => {
23
- expect(validateSemver('1.0.0')).toBe(true);
24
- expect(validateSemver('1.2.3')).toBe(true);
25
- expect(validateSemver('10.20.30')).toBe(true);
26
- expect(validateSemver('0.0.1')).toBe(true);
27
- });
28
- it('should accept semver with pre-release identifiers', () => {
29
- expect(validateSemver('1.0.0-alpha')).toBe(true);
30
- expect(validateSemver('1.0.0-beta.1')).toBe(true);
31
- expect(validateSemver('1.0.0-rc.1')).toBe(true);
32
- });
33
- it('should reject invalid versions', () => {
34
- expect(validateSemver('1')).toBe(false);
35
- expect(validateSemver('1.0')).toBe(false);
36
- expect(validateSemver('v1.0.0')).toBe(false);
37
- expect(validateSemver('1.0.0.0')).toBe(false);
38
- expect(validateSemver('abc')).toBe(false);
39
- expect(validateSemver('')).toBe(false);
40
- });
41
- });
42
- describe('findPackageJsonPaths', () => {
43
- let testDir;
44
- beforeEach(() => {
45
- // Create temp directory for test packages
46
- testDir = join(tmpdir(), `release-test-${Date.now()}`);
47
- mkdirSync(testDir, { recursive: true });
48
- });
49
- afterEach(() => {
50
- // Clean up temp directory
51
- rmSync(testDir, { recursive: true, force: true });
52
- });
53
- it('should find all @lumenflow/* package.json files', () => {
54
- // Create mock package structure
55
- const packagesDir = join(testDir, 'packages/@lumenflow');
56
- mkdirSync(join(packagesDir, 'core'), { recursive: true });
57
- mkdirSync(join(packagesDir, 'cli'), { recursive: true });
58
- mkdirSync(join(packagesDir, 'memory'), { recursive: true });
59
- writeFileSync(join(packagesDir, 'core/package.json'), JSON.stringify({ name: '@lumenflow/core', version: '1.0.0' }));
60
- writeFileSync(join(packagesDir, 'cli/package.json'), JSON.stringify({ name: '@lumenflow/cli', version: '1.0.0' }));
61
- writeFileSync(join(packagesDir, 'memory/package.json'), JSON.stringify({ name: '@lumenflow/memory', version: '1.0.0' }));
62
- const paths = findPackageJsonPaths(testDir);
63
- expect(paths).toHaveLength(3);
64
- expect(paths).toContain(join(packagesDir, 'core/package.json'));
65
- expect(paths).toContain(join(packagesDir, 'cli/package.json'));
66
- expect(paths).toContain(join(packagesDir, 'memory/package.json'));
67
- });
68
- it('should not include packages with private: true', () => {
69
- // Create mock package structure with private package
70
- const packagesDir = join(testDir, 'packages/@lumenflow');
71
- mkdirSync(join(packagesDir, 'core'), { recursive: true });
72
- mkdirSync(join(packagesDir, 'internal'), { recursive: true });
73
- writeFileSync(join(packagesDir, 'core/package.json'), JSON.stringify({ name: '@lumenflow/core', version: '1.0.0' }));
74
- writeFileSync(join(packagesDir, 'internal/package.json'), JSON.stringify({ name: '@lumenflow/internal', version: '1.0.0', private: true }));
75
- const paths = findPackageJsonPaths(testDir);
76
- expect(paths).toHaveLength(1);
77
- expect(paths).toContain(join(packagesDir, 'core/package.json'));
78
- });
79
- });
80
- describe('updatePackageVersions', () => {
81
- let testDir;
82
- beforeEach(() => {
83
- testDir = join(tmpdir(), `release-test-${Date.now()}`);
84
- mkdirSync(testDir, { recursive: true });
85
- });
86
- afterEach(() => {
87
- rmSync(testDir, { recursive: true, force: true });
88
- });
89
- it('should update version in all package.json files', async () => {
90
- // Create mock package.json
91
- const packagePath = join(testDir, 'package.json');
92
- writeFileSync(packagePath, JSON.stringify({
93
- name: '@lumenflow/core',
94
- version: '1.0.0',
95
- description: 'Core package',
96
- }, null, 2));
97
- await updatePackageVersions([packagePath], '1.2.3');
98
- // Read back and verify
99
- const content = JSON.parse(await import('node:fs/promises').then((fs) => fs.readFile(packagePath, 'utf-8')));
100
- expect(content.version).toBe('1.2.3');
101
- expect(content.name).toBe('@lumenflow/core'); // Other fields preserved
102
- });
103
- it('should preserve JSON formatting', async () => {
104
- // Create mock package.json with specific formatting
105
- const packagePath = join(testDir, 'package.json');
106
- writeFileSync(packagePath, JSON.stringify({ name: '@lumenflow/core', version: '1.0.0' }, null, 2) + '\n');
107
- await updatePackageVersions([packagePath], '1.2.3');
108
- // Read back raw content and check formatting
109
- const content = await import('node:fs/promises').then((fs) => fs.readFile(packagePath, 'utf-8'));
110
- expect(content).toMatch(/{\n {2}"name"/); // Preserve 2-space indent
111
- });
112
- });
113
- describe('buildCommitMessage', () => {
114
- it('should build correct commit message for version bump', () => {
115
- const message = buildCommitMessage('1.3.0');
116
- expect(message).toBe('chore: bump all packages to v1.3.0');
117
- });
118
- });
119
- describe('buildTagName', () => {
120
- it('should build correct tag name', () => {
121
- expect(buildTagName('1.3.0')).toBe('v1.3.0');
122
- expect(buildTagName('1.0.0-beta.1')).toBe('v1.0.0-beta.1');
123
- });
124
- });
125
- });
126
- describe('release command integration', () => {
127
- // These tests verify the command exists and parses arguments correctly
128
- // They don't actually run the full release process
129
- it('should export main function from release.ts', async () => {
130
- // This test verifies the module structure exists
131
- const module = await import('../release.js');
132
- expect(module).toBeDefined();
133
- expect(typeof module.validateSemver).toBe('function');
134
- expect(typeof module.findPackageJsonPaths).toBe('function');
135
- expect(typeof module.updatePackageVersions).toBe('function');
136
- });
137
- });
138
- /**
139
- * WU-1296: Tests for release flow trunk protection compatibility
140
- *
141
- * Verifies:
142
- * - RELEASE_WU_TOOL constant is exported for pre-push hook bypass
143
- * - withReleaseEnv helper sets LUMENFLOW_WU_TOOL=release during execution
144
- * - Environment is properly restored after execution (including on error)
145
- */
146
- describe('WU-1296: release flow trunk protection compatibility', () => {
147
- it('should export RELEASE_WU_TOOL constant for pre-push hook bypass', async () => {
148
- const { RELEASE_WU_TOOL } = await import('../release.js');
149
- expect(RELEASE_WU_TOOL).toBe('release');
150
- });
151
- it('should export withReleaseEnv helper for setting LUMENFLOW_WU_TOOL', async () => {
152
- const { withReleaseEnv } = await import('../release.js');
153
- expect(typeof withReleaseEnv).toBe('function');
154
- });
155
- it('withReleaseEnv should set and restore LUMENFLOW_WU_TOOL', async () => {
156
- const { withReleaseEnv } = await import('../release.js');
157
- // Save original value
158
- const originalValue = process.env.LUMENFLOW_WU_TOOL;
159
- let capturedValue;
160
- await withReleaseEnv(async () => {
161
- capturedValue = process.env.LUMENFLOW_WU_TOOL;
162
- });
163
- expect(capturedValue).toBe('release');
164
- expect(process.env.LUMENFLOW_WU_TOOL).toBe(originalValue);
165
- });
166
- it('withReleaseEnv should restore LUMENFLOW_WU_TOOL even on error', async () => {
167
- const { withReleaseEnv } = await import('../release.js');
168
- // Save original value
169
- const originalValue = process.env.LUMENFLOW_WU_TOOL;
170
- try {
171
- await withReleaseEnv(async () => {
172
- throw new Error('Test error');
173
- });
174
- }
175
- catch {
176
- // Expected to throw
177
- }
178
- expect(process.env.LUMENFLOW_WU_TOOL).toBe(originalValue);
179
- });
180
- it('withReleaseEnv should preserve existing LUMENFLOW_WU_TOOL value', async () => {
181
- const { withReleaseEnv } = await import('../release.js');
182
- // Set a specific value before running
183
- const testValue = 'wu-done';
184
- process.env.LUMENFLOW_WU_TOOL = testValue;
185
- try {
186
- let capturedValue;
187
- await withReleaseEnv(async () => {
188
- capturedValue = process.env.LUMENFLOW_WU_TOOL;
189
- });
190
- expect(capturedValue).toBe('release');
191
- expect(process.env.LUMENFLOW_WU_TOOL).toBe(testValue);
192
- }
193
- finally {
194
- // Cleanup
195
- delete process.env.LUMENFLOW_WU_TOOL;
196
- }
197
- });
198
- });
199
- /**
200
- * WU-1077: Tests for release script bug fixes
201
- *
202
- * Verifies:
203
- * - hasNpmAuth() detects auth from ~/.npmrc not just env vars
204
- * - Changeset pre mode is detected and exited in micro-worktree
205
- * - Tag push bypasses pre-push hooks via LUMENFLOW_FORCE
206
- */
207
- describe('WU-1077: release script bug fixes', () => {
208
- describe('hasNpmAuth - ~/.npmrc detection', () => {
209
- let testDir;
210
- let originalUserConfig;
211
- let originalNpmToken;
212
- let originalNodeAuthToken;
213
- beforeEach(() => {
214
- testDir = join(tmpdir(), `release-npmrc-test-${Date.now()}`);
215
- mkdirSync(testDir, { recursive: true });
216
- originalUserConfig = process.env.NPM_CONFIG_USERCONFIG;
217
- originalNpmToken = process.env.NPM_TOKEN;
218
- originalNodeAuthToken = process.env.NODE_AUTH_TOKEN;
219
- process.env.NPM_CONFIG_USERCONFIG = join(testDir, 'user.npmrc');
220
- writeFileSync(process.env.NPM_CONFIG_USERCONFIG, '');
221
- delete process.env.NPM_TOKEN;
222
- delete process.env.NODE_AUTH_TOKEN;
223
- });
224
- afterEach(() => {
225
- if (originalUserConfig === undefined) {
226
- delete process.env.NPM_CONFIG_USERCONFIG;
227
- }
228
- else {
229
- process.env.NPM_CONFIG_USERCONFIG = originalUserConfig;
230
- }
231
- if (originalNpmToken === undefined) {
232
- delete process.env.NPM_TOKEN;
233
- }
234
- else {
235
- process.env.NPM_TOKEN = originalNpmToken;
236
- }
237
- if (originalNodeAuthToken === undefined) {
238
- delete process.env.NODE_AUTH_TOKEN;
239
- }
240
- else {
241
- process.env.NODE_AUTH_TOKEN = originalNodeAuthToken;
242
- }
243
- rmSync(testDir, { recursive: true, force: true });
244
- });
245
- it('should detect auth from ~/.npmrc authToken line', async () => {
246
- // Import the function we're testing
247
- const { hasNpmAuth } = await import('../release.js');
248
- // Create a mock .npmrc with auth token
249
- const npmrcPath = join(testDir, '.npmrc');
250
- writeFileSync(npmrcPath, '//registry.npmjs.org/:_authToken=npm_testToken123\n');
251
- // Test that it detects auth from the file
252
- const result = hasNpmAuth(npmrcPath);
253
- expect(result).toBe(true);
254
- });
255
- it('should return false when ~/.npmrc has no auth token', async () => {
256
- const { hasNpmAuth } = await import('../release.js');
257
- // Create a mock .npmrc without auth token
258
- const npmrcPath = join(testDir, '.npmrc');
259
- writeFileSync(npmrcPath, 'registry=https://registry.npmjs.org\n');
260
- const result = hasNpmAuth(npmrcPath);
261
- expect(result).toBe(false);
262
- });
263
- it('should return false when ~/.npmrc does not exist', async () => {
264
- const { hasNpmAuth } = await import('../release.js');
265
- // Non-existent path
266
- const npmrcPath = join(testDir, 'nonexistent', '.npmrc');
267
- const result = hasNpmAuth(npmrcPath);
268
- expect(result).toBe(false);
269
- });
270
- it('should still detect auth from NPM_TOKEN env var', async () => {
271
- const { hasNpmAuth } = await import('../release.js');
272
- // Set env var
273
- const originalNpmToken = process.env.NPM_TOKEN;
274
- process.env.NPM_TOKEN = 'test_token';
275
- try {
276
- // No npmrc file provided, should check env var
277
- const result = hasNpmAuth();
278
- expect(result).toBe(true);
279
- }
280
- finally {
281
- // Restore
282
- if (originalNpmToken === undefined) {
283
- delete process.env.NPM_TOKEN;
284
- }
285
- else {
286
- process.env.NPM_TOKEN = originalNpmToken;
287
- }
288
- }
289
- });
290
- });
291
- describe('isInChangesetPreMode', () => {
292
- let testDir;
293
- beforeEach(() => {
294
- testDir = join(tmpdir(), `release-pre-test-${Date.now()}`);
295
- mkdirSync(testDir, { recursive: true });
296
- });
297
- afterEach(() => {
298
- rmSync(testDir, { recursive: true, force: true });
299
- });
300
- it('should return true when .changeset/pre.json exists', async () => {
301
- const { isInChangesetPreMode } = await import('../release.js');
302
- // Create .changeset directory and pre.json
303
- const changesetDir = join(testDir, '.changeset');
304
- mkdirSync(changesetDir, { recursive: true });
305
- writeFileSync(join(changesetDir, 'pre.json'), JSON.stringify({
306
- mode: 'pre',
307
- tag: 'next',
308
- initialVersions: {},
309
- changesets: [],
310
- }));
311
- const result = isInChangesetPreMode(testDir);
312
- expect(result).toBe(true);
313
- });
314
- it('should return false when .changeset/pre.json does not exist', async () => {
315
- const { isInChangesetPreMode } = await import('../release.js');
316
- // Create .changeset directory without pre.json
317
- const changesetDir = join(testDir, '.changeset');
318
- mkdirSync(changesetDir, { recursive: true });
319
- writeFileSync(join(changesetDir, 'config.json'), JSON.stringify({ access: 'public' }));
320
- const result = isInChangesetPreMode(testDir);
321
- expect(result).toBe(false);
322
- });
323
- it('should return false when .changeset directory does not exist', async () => {
324
- const { isInChangesetPreMode } = await import('../release.js');
325
- const result = isInChangesetPreMode(testDir);
326
- expect(result).toBe(false);
327
- });
328
- });
329
- describe('exitChangesetPreMode', () => {
330
- let testDir;
331
- beforeEach(() => {
332
- testDir = join(tmpdir(), `release-exit-pre-test-${Date.now()}`);
333
- mkdirSync(testDir, { recursive: true });
334
- });
335
- afterEach(() => {
336
- rmSync(testDir, { recursive: true, force: true });
337
- });
338
- it('should delete .changeset/pre.json to exit pre mode', async () => {
339
- const { exitChangesetPreMode, isInChangesetPreMode } = await import('../release.js');
340
- // Create .changeset directory and pre.json
341
- const changesetDir = join(testDir, '.changeset');
342
- mkdirSync(changesetDir, { recursive: true });
343
- const preJsonPath = join(changesetDir, 'pre.json');
344
- writeFileSync(preJsonPath, JSON.stringify({
345
- mode: 'pre',
346
- tag: 'next',
347
- initialVersions: {},
348
- changesets: [],
349
- }));
350
- // Verify pre mode is active
351
- expect(isInChangesetPreMode(testDir)).toBe(true);
352
- // Exit pre mode
353
- exitChangesetPreMode(testDir);
354
- // Verify pre mode is no longer active
355
- expect(isInChangesetPreMode(testDir)).toBe(false);
356
- expect(existsSync(preJsonPath)).toBe(false);
357
- });
358
- it('should not throw when .changeset/pre.json does not exist', async () => {
359
- const { exitChangesetPreMode } = await import('../release.js');
360
- // No pre.json file exists
361
- expect(() => exitChangesetPreMode(testDir)).not.toThrow();
362
- });
363
- });
364
- describe('pushTagWithForce', () => {
365
- it('should export pushTagWithForce function', async () => {
366
- const { pushTagWithForce } = await import('../release.js');
367
- expect(typeof pushTagWithForce).toBe('function');
368
- });
369
- // Integration test would require git setup - functional verification
370
- // is done by checking the function uses LUMENFLOW_FORCE env var
371
- });
372
- });
@@ -1,127 +0,0 @@
1
- #!/usr/bin/env node
2
- /**
3
- * Tests for rotate-progress CLI command
4
- *
5
- * WU-1112: INIT-003 Phase 6 - Migrate remaining Tier 1 tools
6
- *
7
- * Rotate progress moves completed WUs from status.md In Progress
8
- * section to Completed section, keeping the file tidy.
9
- */
10
- import { describe, it, expect, vi, beforeEach } from 'vitest';
11
- // Import functions under test
12
- import { parseRotateArgs, findCompletedWUs, buildRotatedContent, } from '../rotate-progress.js';
13
- describe('rotate-progress', () => {
14
- beforeEach(() => {
15
- vi.clearAllMocks();
16
- });
17
- describe('parseRotateArgs', () => {
18
- it('should parse --dry-run flag', () => {
19
- const args = parseRotateArgs(['node', 'rotate-progress.js', '--dry-run']);
20
- expect(args.dryRun).toBe(true);
21
- });
22
- it('should parse --help flag', () => {
23
- const args = parseRotateArgs(['node', 'rotate-progress.js', '--help']);
24
- expect(args.help).toBe(true);
25
- });
26
- it('should parse --limit flag', () => {
27
- const args = parseRotateArgs(['node', 'rotate-progress.js', '--limit', '10']);
28
- expect(args.limit).toBe(10);
29
- });
30
- it('should default dryRun to false', () => {
31
- const args = parseRotateArgs(['node', 'rotate-progress.js']);
32
- expect(args.dryRun).toBeFalsy();
33
- });
34
- it('should default limit to undefined', () => {
35
- const args = parseRotateArgs(['node', 'rotate-progress.js']);
36
- expect(args.limit).toBeUndefined();
37
- });
38
- });
39
- describe('findCompletedWUs', () => {
40
- it('should find WUs with done status in In Progress section', () => {
41
- const statusContent = `## In Progress
42
- - WU-1001 - Feature A
43
- - WU-1002 - Feature B (done)
44
-
45
- ## Completed
46
- - WU-1000 - Old feature (2024-01-01)
47
- `;
48
- const wuStatuses = new Map([
49
- ['WU-1001', 'in_progress'],
50
- ['WU-1002', 'done'],
51
- ]);
52
- const completed = findCompletedWUs(statusContent, wuStatuses);
53
- expect(completed).toContain('WU-1002');
54
- expect(completed).not.toContain('WU-1001');
55
- });
56
- it('should return empty array when no completed WUs found', () => {
57
- const statusContent = `## In Progress
58
- - WU-1001 - Feature A
59
-
60
- ## Completed
61
- `;
62
- const wuStatuses = new Map([['WU-1001', 'in_progress']]);
63
- const completed = findCompletedWUs(statusContent, wuStatuses);
64
- expect(completed).toEqual([]);
65
- });
66
- it('should handle multiple completed WUs', () => {
67
- const statusContent = `## In Progress
68
- - WU-1001 - Feature A
69
- - WU-1002 - Feature B
70
- - WU-1003 - Feature C
71
-
72
- ## Completed
73
- `;
74
- const wuStatuses = new Map([
75
- ['WU-1001', 'done'],
76
- ['WU-1002', 'done'],
77
- ['WU-1003', 'in_progress'],
78
- ]);
79
- const completed = findCompletedWUs(statusContent, wuStatuses);
80
- expect(completed).toContain('WU-1001');
81
- expect(completed).toContain('WU-1002');
82
- expect(completed).not.toContain('WU-1003');
83
- });
84
- });
85
- describe('buildRotatedContent', () => {
86
- it('should move completed WUs to Completed section', () => {
87
- const statusContent = `## In Progress
88
- - WU-1001 - Feature A
89
- - WU-1002 - Feature B
90
-
91
- ## Completed
92
- - WU-1000 - Old feature (2024-01-01)
93
- `;
94
- const completedWUs = ['WU-1002'];
95
- const result = buildRotatedContent(statusContent, completedWUs);
96
- // WU-1002 should be removed from In Progress
97
- expect(result).not.toContain('## In Progress\n- WU-1001 - Feature A\n- WU-1002 - Feature B');
98
- // WU-1001 should still be in In Progress
99
- expect(result).toContain('WU-1001 - Feature A');
100
- // WU-1002 should be in Completed
101
- expect(result).toContain('WU-1002');
102
- });
103
- it('should preserve existing completed entries', () => {
104
- const statusContent = `## In Progress
105
- - WU-1001 - Feature A
106
-
107
- ## Completed
108
- - WU-1000 - Old feature (2024-01-01)
109
- `;
110
- const completedWUs = ['WU-1001'];
111
- const result = buildRotatedContent(statusContent, completedWUs);
112
- // Existing completed entry should remain
113
- expect(result).toContain('WU-1000 - Old feature');
114
- });
115
- it('should add date stamp to newly completed WUs', () => {
116
- const statusContent = `## In Progress
117
- - WU-1001 - Feature A
118
-
119
- ## Completed
120
- `;
121
- const completedWUs = ['WU-1001'];
122
- const result = buildRotatedContent(statusContent, completedWUs);
123
- // Should have a date stamp (YYYY-MM-DD format)
124
- expect(result).toMatch(/WU-1001.*\(\d{4}-\d{2}-\d{2}\)/);
125
- });
126
- });
127
- });