@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.
- package/README.md +8 -4
- package/dist/hooks/enforcement-checks.js +120 -0
- package/dist/hooks/enforcement-checks.js.map +1 -1
- package/dist/init-lane-validation.js +141 -0
- package/dist/init-lane-validation.js.map +1 -0
- package/dist/init-templates.js +36 -8
- package/dist/init-templates.js.map +1 -1
- package/dist/init.js +27 -58
- package/dist/init.js.map +1 -1
- package/dist/initiative-create.js +35 -4
- package/dist/initiative-create.js.map +1 -1
- package/dist/lane-lifecycle-process.js +364 -0
- package/dist/lane-lifecycle-process.js.map +1 -0
- package/dist/lane-lock.js +41 -0
- package/dist/lane-lock.js.map +1 -0
- package/dist/lane-setup.js +55 -0
- package/dist/lane-setup.js.map +1 -0
- package/dist/lane-status.js +38 -0
- package/dist/lane-status.js.map +1 -0
- package/dist/lane-validate.js +43 -0
- package/dist/lane-validate.js.map +1 -0
- package/dist/onboarding-smoke-test.js +17 -0
- package/dist/onboarding-smoke-test.js.map +1 -1
- package/dist/public-manifest.js +28 -0
- package/dist/public-manifest.js.map +1 -1
- package/dist/wu-claim-cloud.js +16 -0
- package/dist/wu-claim-cloud.js.map +1 -1
- package/dist/wu-claim.js +12 -2
- package/dist/wu-claim.js.map +1 -1
- package/dist/wu-create-content.js +8 -2
- package/dist/wu-create-content.js.map +1 -1
- package/dist/wu-create-validation.js +5 -3
- package/dist/wu-create-validation.js.map +1 -1
- package/dist/wu-create.js +21 -1
- package/dist/wu-create.js.map +1 -1
- package/dist/wu-done.js +57 -8
- package/dist/wu-done.js.map +1 -1
- package/dist/wu-prep.js +22 -0
- package/dist/wu-prep.js.map +1 -1
- package/package.json +15 -11
- package/dist/__tests__/agent-log-issue.test.js +0 -56
- package/dist/__tests__/agent-spawn-coordination.test.js +0 -451
- package/dist/__tests__/backlog-prune.test.js +0 -478
- package/dist/__tests__/cli-entry-point.test.js +0 -160
- package/dist/__tests__/cli-subprocess.test.js +0 -89
- package/dist/__tests__/commands/integrate.test.js +0 -165
- package/dist/__tests__/commands.test.js +0 -271
- package/dist/__tests__/deps-operations.test.js +0 -206
- package/dist/__tests__/doctor.test.js +0 -510
- package/dist/__tests__/file-operations.test.js +0 -906
- package/dist/__tests__/flow-report.test.js +0 -24
- package/dist/__tests__/gates-config.test.js +0 -303
- package/dist/__tests__/gates-integration-tests.test.js +0 -112
- package/dist/__tests__/git-operations.test.js +0 -668
- package/dist/__tests__/guard-main-branch.test.js +0 -79
- package/dist/__tests__/guards-validation.test.js +0 -416
- package/dist/__tests__/hooks/enforcement.test.js +0 -279
- package/dist/__tests__/init-config-lanes.test.js +0 -131
- package/dist/__tests__/init-docs-structure.test.js +0 -152
- package/dist/__tests__/init-greenfield.test.js +0 -247
- package/dist/__tests__/init-lane-inference.test.js +0 -125
- package/dist/__tests__/init-onboarding-docs.test.js +0 -132
- package/dist/__tests__/init-quick-ref.test.js +0 -144
- package/dist/__tests__/init-scripts.test.js +0 -207
- package/dist/__tests__/init-template-portability.test.js +0 -96
- package/dist/__tests__/init.test.js +0 -968
- package/dist/__tests__/initiative-add-wu.test.js +0 -490
- package/dist/__tests__/initiative-e2e.test.js +0 -442
- package/dist/__tests__/initiative-plan-replacement.test.js +0 -161
- package/dist/__tests__/initiative-plan.test.js +0 -340
- package/dist/__tests__/initiative-remove-wu.test.js +0 -458
- package/dist/__tests__/lumenflow-upgrade.test.js +0 -260
- package/dist/__tests__/mem-cleanup-execution.test.js +0 -19
- package/dist/__tests__/memory-integration.test.js +0 -333
- package/dist/__tests__/merge-block.test.js +0 -220
- package/dist/__tests__/metrics-cli.test.js +0 -619
- package/dist/__tests__/metrics-snapshot.test.js +0 -24
- package/dist/__tests__/no-beacon-references-docs.test.js +0 -30
- package/dist/__tests__/no-beacon-references.test.js +0 -39
- package/dist/__tests__/onboarding-smoke-test.test.js +0 -211
- package/dist/__tests__/path-centralization-cli.test.js +0 -234
- package/dist/__tests__/plan-create.test.js +0 -126
- package/dist/__tests__/plan-edit.test.js +0 -157
- package/dist/__tests__/plan-link.test.js +0 -239
- package/dist/__tests__/plan-promote.test.js +0 -181
- package/dist/__tests__/release.test.js +0 -372
- package/dist/__tests__/rotate-progress.test.js +0 -127
- package/dist/__tests__/safe-git.test.js +0 -190
- package/dist/__tests__/session-coordinator.test.js +0 -109
- package/dist/__tests__/state-bootstrap.test.js +0 -432
- package/dist/__tests__/state-doctor.test.js +0 -328
- package/dist/__tests__/sync-templates.test.js +0 -255
- package/dist/__tests__/templates-sync.test.js +0 -219
- package/dist/__tests__/trace-gen.test.js +0 -115
- package/dist/__tests__/wu-create-required-fields.test.js +0 -143
- package/dist/__tests__/wu-create-strict.test.js +0 -118
- package/dist/__tests__/wu-create.test.js +0 -121
- package/dist/__tests__/wu-done-auto-cleanup.test.js +0 -135
- package/dist/__tests__/wu-done-docs-only-policy.test.js +0 -20
- package/dist/__tests__/wu-done-staging-whitelist.test.js +0 -35
- package/dist/__tests__/wu-done.test.js +0 -36
- package/dist/__tests__/wu-edit-strict.test.js +0 -109
- package/dist/__tests__/wu-edit.test.js +0 -119
- package/dist/__tests__/wu-lifecycle-integration.test.js +0 -388
- package/dist/__tests__/wu-prep-default-exec.test.js +0 -35
- package/dist/__tests__/wu-prep.test.js +0 -140
- package/dist/__tests__/wu-proto.test.js +0 -97
- package/dist/__tests__/wu-validate-strict.test.js +0 -113
- package/dist/__tests__/wu-validate.test.js +0 -36
- package/dist/spawn-list.js +0 -143
- 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
|
-
});
|