@pleri/olam-cli 0.1.11 → 0.1.13

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 (112) hide show
  1. package/dist/__tests__/auth-status.test.js +8 -7
  2. package/dist/__tests__/auth-status.test.js.map +1 -1
  3. package/dist/__tests__/help-output.test.d.ts +2 -0
  4. package/dist/__tests__/help-output.test.d.ts.map +1 -0
  5. package/dist/__tests__/help-output.test.js +74 -0
  6. package/dist/__tests__/help-output.test.js.map +1 -0
  7. package/dist/__tests__/image-presence.test.d.ts +2 -0
  8. package/dist/__tests__/image-presence.test.d.ts.map +1 -0
  9. package/dist/__tests__/image-presence.test.js +44 -0
  10. package/dist/__tests__/image-presence.test.js.map +1 -0
  11. package/dist/__tests__/protocol-version.test.d.ts +2 -0
  12. package/dist/__tests__/protocol-version.test.d.ts.map +1 -0
  13. package/dist/__tests__/protocol-version.test.js +170 -0
  14. package/dist/__tests__/protocol-version.test.js.map +1 -0
  15. package/dist/__tests__/registry-allowlist.test.d.ts +2 -0
  16. package/dist/__tests__/registry-allowlist.test.d.ts.map +1 -0
  17. package/dist/__tests__/registry-allowlist.test.js +129 -0
  18. package/dist/__tests__/registry-allowlist.test.js.map +1 -0
  19. package/dist/commands/__tests__/crystallize.test.d.ts +2 -0
  20. package/dist/commands/__tests__/crystallize.test.d.ts.map +1 -0
  21. package/dist/commands/__tests__/crystallize.test.js +133 -0
  22. package/dist/commands/__tests__/crystallize.test.js.map +1 -0
  23. package/dist/commands/__tests__/upgrade.all-three.test.d.ts +19 -0
  24. package/dist/commands/__tests__/upgrade.all-three.test.d.ts.map +1 -0
  25. package/dist/commands/__tests__/upgrade.all-three.test.js +92 -0
  26. package/dist/commands/__tests__/upgrade.all-three.test.js.map +1 -0
  27. package/dist/commands/__tests__/upgrade.history.test.d.ts +15 -0
  28. package/dist/commands/__tests__/upgrade.history.test.d.ts.map +1 -0
  29. package/dist/commands/__tests__/upgrade.history.test.js +199 -0
  30. package/dist/commands/__tests__/upgrade.history.test.js.map +1 -0
  31. package/dist/commands/__tests__/upgrade.lock.test.d.ts +15 -0
  32. package/dist/commands/__tests__/upgrade.lock.test.d.ts.map +1 -0
  33. package/dist/commands/__tests__/upgrade.lock.test.js +253 -0
  34. package/dist/commands/__tests__/upgrade.lock.test.js.map +1 -0
  35. package/dist/commands/__tests__/upgrade.olam-tag.test.d.ts +21 -0
  36. package/dist/commands/__tests__/upgrade.olam-tag.test.d.ts.map +1 -0
  37. package/dist/commands/__tests__/upgrade.olam-tag.test.js +127 -0
  38. package/dist/commands/__tests__/upgrade.olam-tag.test.js.map +1 -0
  39. package/dist/commands/__tests__/upgrade.poll.test.d.ts +14 -0
  40. package/dist/commands/__tests__/upgrade.poll.test.d.ts.map +1 -0
  41. package/dist/commands/__tests__/upgrade.poll.test.js +136 -0
  42. package/dist/commands/__tests__/upgrade.poll.test.js.map +1 -0
  43. package/dist/commands/__tests__/upgrade.recreate.test.d.ts +17 -0
  44. package/dist/commands/__tests__/upgrade.recreate.test.d.ts.map +1 -0
  45. package/dist/commands/__tests__/upgrade.recreate.test.js +95 -0
  46. package/dist/commands/__tests__/upgrade.recreate.test.js.map +1 -0
  47. package/dist/commands/__tests__/upgrade.rollback.test.d.ts +12 -0
  48. package/dist/commands/__tests__/upgrade.rollback.test.d.ts.map +1 -0
  49. package/dist/commands/__tests__/upgrade.rollback.test.js +275 -0
  50. package/dist/commands/__tests__/upgrade.rollback.test.js.map +1 -0
  51. package/dist/commands/__tests__/upgrade.sha-capture.test.d.ts +12 -0
  52. package/dist/commands/__tests__/upgrade.sha-capture.test.d.ts.map +1 -0
  53. package/dist/commands/__tests__/upgrade.sha-capture.test.js +63 -0
  54. package/dist/commands/__tests__/upgrade.sha-capture.test.js.map +1 -0
  55. package/dist/commands/__tests__/upgrade.smoke.test.d.ts +19 -0
  56. package/dist/commands/__tests__/upgrade.smoke.test.d.ts.map +1 -0
  57. package/dist/commands/__tests__/upgrade.smoke.test.js +101 -0
  58. package/dist/commands/__tests__/upgrade.smoke.test.js.map +1 -0
  59. package/dist/commands/__tests__/upgrade.swap.test.d.ts +19 -0
  60. package/dist/commands/__tests__/upgrade.swap.test.d.ts.map +1 -0
  61. package/dist/commands/__tests__/upgrade.swap.test.js +333 -0
  62. package/dist/commands/__tests__/upgrade.swap.test.js.map +1 -0
  63. package/dist/commands/auth-status.d.ts +8 -1
  64. package/dist/commands/auth-status.d.ts.map +1 -1
  65. package/dist/commands/auth-status.js +2 -1
  66. package/dist/commands/auth-status.js.map +1 -1
  67. package/dist/commands/create.d.ts.map +1 -1
  68. package/dist/commands/create.js +31 -0
  69. package/dist/commands/create.js.map +1 -1
  70. package/dist/commands/crystallize.d.ts +11 -1
  71. package/dist/commands/crystallize.d.ts.map +1 -1
  72. package/dist/commands/crystallize.js +32 -8
  73. package/dist/commands/crystallize.js.map +1 -1
  74. package/dist/commands/upgrade-history.d.ts +17 -0
  75. package/dist/commands/upgrade-history.d.ts.map +1 -0
  76. package/dist/commands/upgrade-history.js +40 -0
  77. package/dist/commands/upgrade-history.js.map +1 -0
  78. package/dist/commands/upgrade-lock.d.ts +102 -0
  79. package/dist/commands/upgrade-lock.d.ts.map +1 -0
  80. package/dist/commands/upgrade-lock.js +225 -0
  81. package/dist/commands/upgrade-lock.js.map +1 -0
  82. package/dist/commands/upgrade-log.d.ts +86 -0
  83. package/dist/commands/upgrade-log.d.ts.map +1 -0
  84. package/dist/commands/upgrade-log.js +146 -0
  85. package/dist/commands/upgrade-log.js.map +1 -0
  86. package/dist/commands/upgrade.d.ts +265 -0
  87. package/dist/commands/upgrade.d.ts.map +1 -1
  88. package/dist/commands/upgrade.js +840 -10
  89. package/dist/commands/upgrade.js.map +1 -1
  90. package/dist/exit-codes.d.ts +35 -0
  91. package/dist/exit-codes.d.ts.map +1 -0
  92. package/dist/exit-codes.js +35 -0
  93. package/dist/exit-codes.js.map +1 -0
  94. package/dist/image-presence.d.ts +40 -0
  95. package/dist/image-presence.d.ts.map +1 -0
  96. package/dist/image-presence.js +39 -0
  97. package/dist/image-presence.js.map +1 -0
  98. package/dist/index.js +1058 -168
  99. package/dist/index.js.map +1 -1
  100. package/dist/pleri-config.d.ts +22 -0
  101. package/dist/pleri-config.d.ts.map +1 -0
  102. package/dist/pleri-config.js +42 -0
  103. package/dist/pleri-config.js.map +1 -0
  104. package/dist/protocol-version.d.ts +79 -0
  105. package/dist/protocol-version.d.ts.map +1 -0
  106. package/dist/protocol-version.js +133 -0
  107. package/dist/protocol-version.js.map +1 -0
  108. package/dist/registry-allowlist.d.ts +47 -0
  109. package/dist/registry-allowlist.d.ts.map +1 -0
  110. package/dist/registry-allowlist.js +67 -0
  111. package/dist/registry-allowlist.js.map +1 -0
  112. package/package.json +1 -1
@@ -0,0 +1,133 @@
1
+ // A1 (Phase A — fresh-install self-bootstrap): when PLERI is not configured,
2
+ // `olam crystallize` exits 2 (named constant) with a stderr-only warn so
3
+ // piped scripts can detect "skipped" without parsing stdout.
4
+ import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest';
5
+ import { Command } from 'commander';
6
+ import { registerCrystallize } from '../crystallize.js';
7
+ import { EXIT_PLERI_NOT_CONFIGURED } from '../../exit-codes.js';
8
+ import * as ctxModule from '../../context.js';
9
+ function captureStreams() {
10
+ const sink = { stdout: '', stderr: '' };
11
+ const origStdout = process.stdout.write.bind(process.stdout);
12
+ const origStderr = process.stderr.write.bind(process.stderr);
13
+ process.stdout.write = (chunk) => {
14
+ sink.stdout += typeof chunk === 'string' ? chunk : Buffer.from(chunk).toString();
15
+ return true;
16
+ };
17
+ process.stderr.write = (chunk) => {
18
+ sink.stderr += typeof chunk === 'string' ? chunk : Buffer.from(chunk).toString();
19
+ return true;
20
+ };
21
+ // Save originals on the sink so the caller can restore them.
22
+ sink.restore = () => {
23
+ process.stdout.write = origStdout;
24
+ process.stderr.write = origStderr;
25
+ };
26
+ return sink;
27
+ }
28
+ describe('olam crystallize — PLERI not configured', () => {
29
+ let originalExitCode;
30
+ beforeEach(() => {
31
+ originalExitCode = process.exitCode;
32
+ process.exitCode = 0;
33
+ });
34
+ afterEach(() => {
35
+ process.exitCode = originalExitCode ?? 0;
36
+ vi.restoreAllMocks();
37
+ });
38
+ it('exits with EXIT_PLERI_NOT_CONFIGURED (2) when ctx.pleriClient is null', async () => {
39
+ // Stub loadContext to return a ctx with no pleriClient (PLERI not configured).
40
+ vi.spyOn(ctxModule, 'loadContext').mockResolvedValue({
41
+ ctx: {
42
+ // Minimal shape — only the field crystallize reads at the gate is needed.
43
+ pleriClient: null,
44
+ },
45
+ error: undefined,
46
+ });
47
+ const program = new Command();
48
+ registerCrystallize(program);
49
+ const sink = captureStreams();
50
+ try {
51
+ // commander throws on `process.exit` calls but we set exitCode + return,
52
+ // so this is just an async function call.
53
+ await program.parseAsync(['node', 'olam', 'crystallize', 'fake-world-id']);
54
+ }
55
+ finally {
56
+ sink.restore();
57
+ }
58
+ expect(process.exitCode).toBe(EXIT_PLERI_NOT_CONFIGURED);
59
+ expect(process.exitCode).toBe(2);
60
+ expect(sink.stderr).toContain('crystallize requires PLERI_BASE_URL');
61
+ expect(sink.stdout).toBe('');
62
+ });
63
+ it('keeps stdout empty so piped consumers can distinguish skip from success', async () => {
64
+ vi.spyOn(ctxModule, 'loadContext').mockResolvedValue({
65
+ // Match production shape: pleriClient is `undefined` when not configured
66
+ // (set conditionally inside `if (config.pleri)` in context.ts).
67
+ ctx: {
68
+ pleriClient: undefined,
69
+ },
70
+ error: undefined,
71
+ });
72
+ const program = new Command();
73
+ registerCrystallize(program);
74
+ const sink = captureStreams();
75
+ try {
76
+ await program.parseAsync(['node', 'olam', 'crystallize', 'any-world']);
77
+ }
78
+ finally {
79
+ sink.restore();
80
+ }
81
+ // The contract: stdout is empty on skip; stderr carries the warn.
82
+ expect(sink.stdout.length).toBe(0);
83
+ expect(sink.stderr.length).toBeGreaterThan(0);
84
+ });
85
+ it('exits 2 on a truly fresh install (no .olam/ dir → OlamConfigNotFoundError)', async () => {
86
+ // The "fresh-install" scenario this epic is named after: a user runs
87
+ // `npm i -g @pleri/olam-cli && olam crystallize <world>` on a project
88
+ // with no `.olam/` directory at all. PLERI is by definition not
89
+ // configured; the skip semantics should match the partial-config case.
90
+ const configNotFound = new Error('No olam config found');
91
+ configNotFound.name = 'OlamConfigNotFoundError';
92
+ vi.spyOn(ctxModule, 'loadContext').mockResolvedValue({
93
+ ctx: undefined,
94
+ error: configNotFound,
95
+ });
96
+ const program = new Command();
97
+ registerCrystallize(program);
98
+ const sink = captureStreams();
99
+ try {
100
+ await program.parseAsync(['node', 'olam', 'crystallize', 'fresh-install-world']);
101
+ }
102
+ finally {
103
+ sink.restore();
104
+ }
105
+ expect(process.exitCode).toBe(EXIT_PLERI_NOT_CONFIGURED);
106
+ expect(sink.stderr).toContain('crystallize requires PLERI_BASE_URL');
107
+ expect(sink.stderr).toContain('Olam is not initialised');
108
+ expect(sink.stdout).toBe('');
109
+ });
110
+ it('warn message names the remediation path (config or env var)', async () => {
111
+ vi.spyOn(ctxModule, 'loadContext').mockResolvedValue({
112
+ // Match production shape: pleriClient is `undefined` when not configured
113
+ // (set conditionally inside `if (config.pleri)` in context.ts).
114
+ ctx: {
115
+ pleriClient: undefined,
116
+ },
117
+ error: undefined,
118
+ });
119
+ const program = new Command();
120
+ registerCrystallize(program);
121
+ const sink = captureStreams();
122
+ try {
123
+ await program.parseAsync(['node', 'olam', 'crystallize', 'world']);
124
+ }
125
+ finally {
126
+ sink.restore();
127
+ }
128
+ // Operators looking at the warn line need to know how to enable the feature.
129
+ expect(sink.stderr).toMatch(/PLERI_BASE_URL/);
130
+ expect(sink.stderr).toMatch(/\.olam\/config\.yaml/);
131
+ });
132
+ });
133
+ //# sourceMappingURL=crystallize.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"crystallize.test.js","sourceRoot":"","sources":["../../../src/commands/__tests__/crystallize.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Verifies Phase 2a A4 sequential build orchestration: `olam upgrade` invokes
3
+ * `bash build-auth.sh` → `bash build-devbox.sh` → `bash build-host-cp.sh` in
4
+ * that order with `OLAM_TAG=olam-next` env, and the devbox step uses inherit
5
+ * stdio (live tee) for cold-cache visibility.
6
+ *
7
+ * The CLI source is read as text and parsed for the load-bearing invariants:
8
+ * - Build scripts listed in correct order
9
+ * - OLAM_TAG=olam-next env propagated
10
+ * - Devbox uses tee (inherit stdio) per F13 audit fix
11
+ * - Other two use silent runStep capture
12
+ *
13
+ * Direct integration testing (spawning real `bash build-*.sh`) lives at
14
+ * tests/integration/build-scripts-olam-tag.sh — manual smoke; not in CI.
15
+ *
16
+ * Run: `cd packages/cli && npx vitest run src/commands/__tests__/upgrade.all-three.test.ts`
17
+ */
18
+ export {};
19
+ //# sourceMappingURL=upgrade.all-three.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"upgrade.all-three.test.d.ts","sourceRoot":"","sources":["../../../src/commands/__tests__/upgrade.all-three.test.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG"}
@@ -0,0 +1,92 @@
1
+ /**
2
+ * Verifies Phase 2a A4 sequential build orchestration: `olam upgrade` invokes
3
+ * `bash build-auth.sh` → `bash build-devbox.sh` → `bash build-host-cp.sh` in
4
+ * that order with `OLAM_TAG=olam-next` env, and the devbox step uses inherit
5
+ * stdio (live tee) for cold-cache visibility.
6
+ *
7
+ * The CLI source is read as text and parsed for the load-bearing invariants:
8
+ * - Build scripts listed in correct order
9
+ * - OLAM_TAG=olam-next env propagated
10
+ * - Devbox uses tee (inherit stdio) per F13 audit fix
11
+ * - Other two use silent runStep capture
12
+ *
13
+ * Direct integration testing (spawning real `bash build-*.sh`) lives at
14
+ * tests/integration/build-scripts-olam-tag.sh — manual smoke; not in CI.
15
+ *
16
+ * Run: `cd packages/cli && npx vitest run src/commands/__tests__/upgrade.all-three.test.ts`
17
+ */
18
+ import { describe, it, expect } from 'vitest';
19
+ import * as fs from 'node:fs';
20
+ import * as path from 'node:path';
21
+ function findRepoRoot(start) {
22
+ let dir = start;
23
+ for (let i = 0; i < 10; i++) {
24
+ if (fs.existsSync(path.join(dir, 'packages', 'host-cp', 'compose.yaml')))
25
+ return dir;
26
+ const parent = path.dirname(dir);
27
+ if (parent === dir)
28
+ break;
29
+ dir = parent;
30
+ }
31
+ throw new Error(`Could not find repo root from ${start}`);
32
+ }
33
+ const REPO_ROOT = findRepoRoot(process.cwd());
34
+ const UPGRADE_TS = path.join(REPO_ROOT, 'packages', 'cli', 'src', 'commands', 'upgrade.ts');
35
+ describe('Phase 2a A4 — sequential build orchestration', () => {
36
+ let body;
37
+ it('reads the upgrade.ts source', () => {
38
+ body = fs.readFileSync(UPGRADE_TS, 'utf-8');
39
+ expect(body.length).toBeGreaterThan(1000);
40
+ });
41
+ it('declares OLAM_TAG=olam-next env for the build steps', () => {
42
+ body ??= fs.readFileSync(UPGRADE_TS, 'utf-8');
43
+ expect(body).toMatch(/OLAM_TAG:\s*['"]olam-next['"]/);
44
+ });
45
+ it('lists the three build scripts in correct order (auth → devbox → host-cp) inside buildScripts[]', () => {
46
+ body ??= fs.readFileSync(UPGRADE_TS, 'utf-8');
47
+ // Scope the order check to the buildScripts array literal (other mentions
48
+ // of build-host-cp.sh exist in the file's doc comment + the printed plan).
49
+ const arrayMatch = body.match(/buildScripts:[\s\S]*?=\s*\[[\s\S]*?\];/);
50
+ expect(arrayMatch).not.toBeNull();
51
+ const arrayBody = arrayMatch[0];
52
+ const authIdx = arrayBody.indexOf('build-auth.sh');
53
+ const devboxIdx = arrayBody.indexOf('build-devbox.sh');
54
+ const hostCpIdx = arrayBody.indexOf('build-host-cp.sh');
55
+ expect(authIdx).toBeGreaterThan(0);
56
+ expect(devboxIdx).toBeGreaterThan(authIdx);
57
+ expect(hostCpIdx).toBeGreaterThan(devboxIdx);
58
+ });
59
+ it('marks the devbox step as tee:true (inherit stdio) for cold-cache visibility (F13 fix)', () => {
60
+ body ??= fs.readFileSync(UPGRADE_TS, 'utf-8');
61
+ // The devbox entry in the buildScripts array should have tee: true.
62
+ // Look for the literal "build-devbox.sh" entry and verify the same line
63
+ // (or nearby) sets tee: true.
64
+ const devboxLine = body
65
+ .split('\n')
66
+ .find((line) => line.includes('build-devbox.sh') && line.includes('tee'));
67
+ expect(devboxLine).toBeDefined();
68
+ expect(devboxLine).toMatch(/tee:\s*true/);
69
+ });
70
+ it('marks auth + host-cp steps as tee:false (silent runStep)', () => {
71
+ body ??= fs.readFileSync(UPGRADE_TS, 'utf-8');
72
+ const lines = body.split('\n');
73
+ const authLine = lines.find((line) => line.includes('build-auth.sh') && line.includes('tee'));
74
+ const hostCpLine = lines.find((line) => line.includes('build-host-cp.sh') && line.includes('tee'));
75
+ expect(authLine).toMatch(/tee:\s*false/);
76
+ expect(hostCpLine).toMatch(/tee:\s*false/);
77
+ });
78
+ it('uses spawnSync with stdio: inherit for the tee branch', () => {
79
+ body ??= fs.readFileSync(UPGRADE_TS, 'utf-8');
80
+ // The tee branch should call spawnSync with stdio: 'inherit'.
81
+ expect(body).toMatch(/spawnSync\([\s\S]*?stdio:\s*['"]inherit['"]/);
82
+ });
83
+ it('failure path exits with code 1 on any build script failure', () => {
84
+ body ??= fs.readFileSync(UPGRADE_TS, 'utf-8');
85
+ // The orchestration loop should set process.exitCode = 1 on failure.
86
+ // Look for the buildScripts loop body containing the exitCode = 1 pattern.
87
+ const loopMatch = body.match(/for\s*\(\s*const\s+step\s+of\s+buildScripts[\s\S]*?\n\s*\}\n/);
88
+ expect(loopMatch).not.toBeNull();
89
+ expect(loopMatch[0]).toMatch(/process\.exitCode\s*=\s*1/);
90
+ });
91
+ });
92
+ //# sourceMappingURL=upgrade.all-three.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"upgrade.all-three.test.js","sourceRoot":"","sources":["../../../src/commands/__tests__/upgrade.all-three.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Phase 2c — verifies C1 (~/.olam/upgrade.log JSONL writer) + C2 (--history flag).
3
+ *
4
+ * Tests:
5
+ * - appendUpgradeLog: open-per-write, mkdir defensively
6
+ * - readUpgradeLog: ENOENT → empty; corrupt JSON skipped + stderr warn
7
+ * - formatHistoryTable / formatHistoryJson: render shape
8
+ * - formatDuration: ms / s / m / h
9
+ * - parseHistoryOpts: --history -n <N> --json
10
+ * - handleHistory wired from upgrade.ts
11
+ *
12
+ * Run: `cd packages/cli && npx vitest run src/commands/__tests__/upgrade.history.test.ts`
13
+ */
14
+ export {};
15
+ //# sourceMappingURL=upgrade.history.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"upgrade.history.test.d.ts","sourceRoot":"","sources":["../../../src/commands/__tests__/upgrade.history.test.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG"}
@@ -0,0 +1,199 @@
1
+ /**
2
+ * Phase 2c — verifies C1 (~/.olam/upgrade.log JSONL writer) + C2 (--history flag).
3
+ *
4
+ * Tests:
5
+ * - appendUpgradeLog: open-per-write, mkdir defensively
6
+ * - readUpgradeLog: ENOENT → empty; corrupt JSON skipped + stderr warn
7
+ * - formatHistoryTable / formatHistoryJson: render shape
8
+ * - formatDuration: ms / s / m / h
9
+ * - parseHistoryOpts: --history -n <N> --json
10
+ * - handleHistory wired from upgrade.ts
11
+ *
12
+ * Run: `cd packages/cli && npx vitest run src/commands/__tests__/upgrade.history.test.ts`
13
+ */
14
+ import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest';
15
+ import * as fs from 'node:fs';
16
+ import * as os from 'node:os';
17
+ import * as path from 'node:path';
18
+ import { appendUpgradeLog, readUpgradeLog, formatHistoryTable, formatHistoryJson, formatDuration, } from '../upgrade-log.js';
19
+ import { parseHistoryOpts } from '../upgrade-history.js';
20
+ let tmpHome;
21
+ let tmpLogPath;
22
+ beforeEach(() => {
23
+ tmpHome = fs.mkdtempSync(path.join(os.tmpdir(), 'olam-history-test-'));
24
+ tmpLogPath = path.join(tmpHome, '.olam', 'upgrade.log');
25
+ });
26
+ afterEach(() => {
27
+ try {
28
+ fs.rmSync(tmpHome, { recursive: true, force: true });
29
+ }
30
+ catch {
31
+ /* best effort */
32
+ }
33
+ });
34
+ function makeRow(overrides = {}) {
35
+ return {
36
+ ts: '2026-05-07T10:00:00.000Z',
37
+ started_at: 1000,
38
+ ended_at: 2000,
39
+ sha_target: '1234567890abcdef1234567890abcdef12345678',
40
+ sha_before: { hostCp: 'aaaaaaaa', authService: 'aaaaaaaa', devbox: 'aaaaaaaa' },
41
+ sha_after: { hostCp: '12345678', authService: '12345678', devbox: '12345678' },
42
+ status: 'success',
43
+ failed_step: null,
44
+ durations_ms: { 'git pull': 200, 'bash build-host-cp.sh': 800 },
45
+ ...overrides,
46
+ };
47
+ }
48
+ describe('appendUpgradeLog', () => {
49
+ it('creates ~/.olam/ defensively before append', () => {
50
+ // Note: HOME is set to tmpHome by beforeEach.
51
+ const expectedPath = path.join(tmpHome, '.olam', 'upgrade.log');
52
+ expect(fs.existsSync(path.dirname(expectedPath))).toBe(false);
53
+ appendUpgradeLog(makeRow(), tmpLogPath);
54
+ expect(fs.existsSync(path.dirname(expectedPath))).toBe(true);
55
+ expect(fs.existsSync(expectedPath)).toBe(true);
56
+ });
57
+ it('appends one JSONL row per call (newline-terminated)', () => {
58
+ appendUpgradeLog(makeRow({ ts: '2026-05-07T10:00:00.000Z' }), tmpLogPath);
59
+ appendUpgradeLog(makeRow({ ts: '2026-05-07T10:05:00.000Z' }), tmpLogPath);
60
+ const logPath = path.join(tmpHome, '.olam', 'upgrade.log');
61
+ const raw = fs.readFileSync(logPath, 'utf-8');
62
+ const lines = raw.split('\n').filter((l) => l.length > 0);
63
+ expect(lines.length).toBe(2);
64
+ const parsed1 = JSON.parse(lines[0]);
65
+ const parsed2 = JSON.parse(lines[1]);
66
+ expect(parsed1.ts).toBe('2026-05-07T10:00:00.000Z');
67
+ expect(parsed2.ts).toBe('2026-05-07T10:05:00.000Z');
68
+ });
69
+ it('failure to append is swallowed (does not throw)', () => {
70
+ // Make ~/.olam unwritable: replace with a regular file.
71
+ fs.mkdirSync(tmpHome, { recursive: true });
72
+ fs.writeFileSync(path.join(tmpHome, '.olam'), 'this-is-a-file-not-a-dir');
73
+ // Should not throw; logs warning to stderr (we don't assert on stderr here).
74
+ expect(() => appendUpgradeLog(makeRow(), path.join(tmpHome, '.olam', 'upgrade.log'))).not.toThrow();
75
+ });
76
+ });
77
+ describe('readUpgradeLog', () => {
78
+ it('returns empty array when log file is missing', () => {
79
+ const rows = readUpgradeLog(10, tmpLogPath);
80
+ expect(rows).toEqual([]);
81
+ });
82
+ it('returns empty array when log file is empty', () => {
83
+ fs.mkdirSync(path.join(tmpHome, '.olam'), { recursive: true });
84
+ fs.writeFileSync(path.join(tmpHome, '.olam', 'upgrade.log'), '');
85
+ expect(readUpgradeLog(10, tmpLogPath)).toEqual([]);
86
+ });
87
+ it('reads the most-recent N rows (chronological order, last-N)', () => {
88
+ for (let i = 0; i < 5; i++) {
89
+ appendUpgradeLog(makeRow({ ts: `2026-05-07T${String(i).padStart(2, '0')}:00:00.000Z` }), tmpLogPath);
90
+ }
91
+ const rows = readUpgradeLog(3, tmpLogPath);
92
+ expect(rows.length).toBe(3);
93
+ // Should be the LAST three (chronologically — most recent in the file).
94
+ expect(rows[0].ts).toBe('2026-05-07T02:00:00.000Z');
95
+ expect(rows[1].ts).toBe('2026-05-07T03:00:00.000Z');
96
+ expect(rows[2].ts).toBe('2026-05-07T04:00:00.000Z');
97
+ });
98
+ it('skips corrupt JSON lines + warns to stderr (does not crash)', () => {
99
+ fs.mkdirSync(path.join(tmpHome, '.olam'), { recursive: true });
100
+ const logPath = path.join(tmpHome, '.olam', 'upgrade.log');
101
+ const goodLine = JSON.stringify(makeRow({ ts: '2026-05-07T10:00:00.000Z' }));
102
+ const corrupt = '{ this is not valid JSON';
103
+ fs.writeFileSync(logPath, `${goodLine}\n${corrupt}\n${goodLine}\n`);
104
+ const stderr = vi.spyOn(process.stderr, 'write').mockReturnValue(true);
105
+ const rows = readUpgradeLog(10, tmpLogPath);
106
+ expect(rows.length).toBe(2); // 2 good, 1 skipped corrupt
107
+ expect(stderr).toHaveBeenCalled();
108
+ stderr.mockRestore();
109
+ });
110
+ it('skips rows that lack required fields (defensive shape check)', () => {
111
+ fs.mkdirSync(path.join(tmpHome, '.olam'), { recursive: true });
112
+ const logPath = path.join(tmpHome, '.olam', 'upgrade.log');
113
+ const badShape = JSON.stringify({ random: 'object', without: 'required fields' });
114
+ const good = JSON.stringify(makeRow());
115
+ fs.writeFileSync(logPath, `${badShape}\n${good}\n`);
116
+ const stderr = vi.spyOn(process.stderr, 'write').mockReturnValue(true);
117
+ const rows = readUpgradeLog(10, tmpLogPath);
118
+ expect(rows.length).toBe(1);
119
+ stderr.mockRestore();
120
+ });
121
+ });
122
+ describe('formatHistoryTable', () => {
123
+ it('returns friendly first-run message on empty rows', () => {
124
+ expect(formatHistoryTable([])).toMatch(/No upgrade history yet/);
125
+ });
126
+ it('renders header + rows for a populated log', () => {
127
+ const out = formatHistoryTable([makeRow()]);
128
+ expect(out).toMatch(/TIMESTAMP/);
129
+ expect(out).toMatch(/STATUS/);
130
+ expect(out).toMatch(/DURATION/);
131
+ expect(out).toMatch(/12345678/); // first 8 of sha_target
132
+ expect(out).toMatch(/✓ success/);
133
+ });
134
+ it('marks failed rows with ✗ + step label', () => {
135
+ const out = formatHistoryTable([
136
+ makeRow({ status: 'failed', failed_step: 'bash build-auth.sh' }),
137
+ ]);
138
+ expect(out).toMatch(/✗ failed/);
139
+ expect(out).toContain('bash build-auth.sh');
140
+ });
141
+ it('marks rolled_back rows with ↩', () => {
142
+ const out = formatHistoryTable([makeRow({ status: 'rolled_back' })]);
143
+ expect(out).toMatch(/↩ rolled_back/);
144
+ });
145
+ });
146
+ describe('formatHistoryJson', () => {
147
+ it('returns JSONL (one JSON object per line, no trailing newline)', () => {
148
+ const out = formatHistoryJson([makeRow({ ts: '2026-05-07T10:00:00.000Z' }), makeRow({ ts: '2026-05-07T10:05:00.000Z' })]);
149
+ const lines = out.split('\n');
150
+ expect(lines.length).toBe(2);
151
+ const parsed1 = JSON.parse(lines[0]);
152
+ const parsed2 = JSON.parse(lines[1]);
153
+ expect(parsed1.ts).toBe('2026-05-07T10:00:00.000Z');
154
+ expect(parsed2.ts).toBe('2026-05-07T10:05:00.000Z');
155
+ });
156
+ it('returns empty string on empty rows', () => {
157
+ expect(formatHistoryJson([])).toBe('');
158
+ });
159
+ });
160
+ describe('formatDuration', () => {
161
+ it('renders ms for sub-second', () => {
162
+ expect(formatDuration(500)).toBe('500ms');
163
+ });
164
+ it('renders s for sub-minute', () => {
165
+ expect(formatDuration(45_000)).toBe('45s');
166
+ });
167
+ it('renders m + s for sub-hour', () => {
168
+ expect(formatDuration(125_000)).toBe('2m05s');
169
+ });
170
+ it('renders h + m for hour-plus', () => {
171
+ expect(formatDuration(3_780_000)).toBe('1h03m');
172
+ });
173
+ });
174
+ describe('parseHistoryOpts', () => {
175
+ it('parses -n string number', () => {
176
+ expect(parseHistoryOpts({ n: '5' }).limit).toBe(5);
177
+ });
178
+ it('parses -n numeric', () => {
179
+ expect(parseHistoryOpts({ n: 7 }).limit).toBe(7);
180
+ });
181
+ it('defaults limit to 10 when -n absent', () => {
182
+ expect(parseHistoryOpts({}).limit).toBe(10);
183
+ });
184
+ it('clamps non-positive -n to default 10', () => {
185
+ expect(parseHistoryOpts({ n: '0' }).limit).toBe(10);
186
+ expect(parseHistoryOpts({ n: '-3' }).limit).toBe(10);
187
+ });
188
+ it('parses --json flag', () => {
189
+ expect(parseHistoryOpts({ json: true }).json).toBe(true);
190
+ expect(parseHistoryOpts({ json: false }).json).toBe(false);
191
+ });
192
+ });
193
+ describe('Phase 2c — wired in handleUpgrade', () => {
194
+ it('upgrade.ts dispatches to handleHistory when opts.history', () => {
195
+ const upgradeTs = fs.readFileSync(path.join(__dirname, '..', 'upgrade.ts'), 'utf-8');
196
+ expect(upgradeTs).toMatch(/if\s*\(opts\.history\)\s*\{[\s\S]*?handleHistory\(/);
197
+ });
198
+ });
199
+ //# sourceMappingURL=upgrade.history.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"upgrade.history.test.js","sourceRoot":"","sources":["../../../src/commands/__tests__/upgrade.history.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Unit tests for `olam upgrade` CLI lock (Phase 2a — A1).
3
+ *
4
+ * Verifies the contract from `packages/cli/src/commands/upgrade-lock.ts`:
5
+ * - Atomic create-or-fail via fs.openSync 'wx'
6
+ * - Stale-lock recovery (empty / parse-error / dead-pid / >30min timeout)
7
+ * - PID liveness check via process.kill(pid, 0)
8
+ * - Command-name check via `ps -p <pid> -o comm=`
9
+ * - Idempotent release
10
+ * - Concurrent-acquisition refusal
11
+ *
12
+ * Run: `cd packages/cli && npx vitest run src/commands/__tests__/upgrade.lock.test.ts`
13
+ */
14
+ export {};
15
+ //# sourceMappingURL=upgrade.lock.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"upgrade.lock.test.d.ts","sourceRoot":"","sources":["../../../src/commands/__tests__/upgrade.lock.test.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG"}