@pleri/olam-cli 0.1.12 → 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 (85) hide show
  1. package/dist/__tests__/image-presence.test.d.ts +2 -0
  2. package/dist/__tests__/image-presence.test.d.ts.map +1 -0
  3. package/dist/__tests__/image-presence.test.js +44 -0
  4. package/dist/__tests__/image-presence.test.js.map +1 -0
  5. package/dist/__tests__/protocol-version.test.d.ts +2 -0
  6. package/dist/__tests__/protocol-version.test.d.ts.map +1 -0
  7. package/dist/__tests__/protocol-version.test.js +170 -0
  8. package/dist/__tests__/protocol-version.test.js.map +1 -0
  9. package/dist/__tests__/registry-allowlist.test.d.ts +2 -0
  10. package/dist/__tests__/registry-allowlist.test.d.ts.map +1 -0
  11. package/dist/__tests__/registry-allowlist.test.js +129 -0
  12. package/dist/__tests__/registry-allowlist.test.js.map +1 -0
  13. package/dist/commands/__tests__/upgrade.all-three.test.d.ts +19 -0
  14. package/dist/commands/__tests__/upgrade.all-three.test.d.ts.map +1 -0
  15. package/dist/commands/__tests__/upgrade.all-three.test.js +92 -0
  16. package/dist/commands/__tests__/upgrade.all-three.test.js.map +1 -0
  17. package/dist/commands/__tests__/upgrade.history.test.d.ts +15 -0
  18. package/dist/commands/__tests__/upgrade.history.test.d.ts.map +1 -0
  19. package/dist/commands/__tests__/upgrade.history.test.js +199 -0
  20. package/dist/commands/__tests__/upgrade.history.test.js.map +1 -0
  21. package/dist/commands/__tests__/upgrade.lock.test.d.ts +15 -0
  22. package/dist/commands/__tests__/upgrade.lock.test.d.ts.map +1 -0
  23. package/dist/commands/__tests__/upgrade.lock.test.js +253 -0
  24. package/dist/commands/__tests__/upgrade.lock.test.js.map +1 -0
  25. package/dist/commands/__tests__/upgrade.olam-tag.test.d.ts +21 -0
  26. package/dist/commands/__tests__/upgrade.olam-tag.test.d.ts.map +1 -0
  27. package/dist/commands/__tests__/upgrade.olam-tag.test.js +127 -0
  28. package/dist/commands/__tests__/upgrade.olam-tag.test.js.map +1 -0
  29. package/dist/commands/__tests__/upgrade.poll.test.d.ts +14 -0
  30. package/dist/commands/__tests__/upgrade.poll.test.d.ts.map +1 -0
  31. package/dist/commands/__tests__/upgrade.poll.test.js +136 -0
  32. package/dist/commands/__tests__/upgrade.poll.test.js.map +1 -0
  33. package/dist/commands/__tests__/upgrade.recreate.test.d.ts +17 -0
  34. package/dist/commands/__tests__/upgrade.recreate.test.d.ts.map +1 -0
  35. package/dist/commands/__tests__/upgrade.recreate.test.js +95 -0
  36. package/dist/commands/__tests__/upgrade.recreate.test.js.map +1 -0
  37. package/dist/commands/__tests__/upgrade.rollback.test.d.ts +12 -0
  38. package/dist/commands/__tests__/upgrade.rollback.test.d.ts.map +1 -0
  39. package/dist/commands/__tests__/upgrade.rollback.test.js +275 -0
  40. package/dist/commands/__tests__/upgrade.rollback.test.js.map +1 -0
  41. package/dist/commands/__tests__/upgrade.sha-capture.test.d.ts +12 -0
  42. package/dist/commands/__tests__/upgrade.sha-capture.test.d.ts.map +1 -0
  43. package/dist/commands/__tests__/upgrade.sha-capture.test.js +63 -0
  44. package/dist/commands/__tests__/upgrade.sha-capture.test.js.map +1 -0
  45. package/dist/commands/__tests__/upgrade.smoke.test.d.ts +19 -0
  46. package/dist/commands/__tests__/upgrade.smoke.test.d.ts.map +1 -0
  47. package/dist/commands/__tests__/upgrade.smoke.test.js +101 -0
  48. package/dist/commands/__tests__/upgrade.smoke.test.js.map +1 -0
  49. package/dist/commands/__tests__/upgrade.swap.test.d.ts +19 -0
  50. package/dist/commands/__tests__/upgrade.swap.test.d.ts.map +1 -0
  51. package/dist/commands/__tests__/upgrade.swap.test.js +333 -0
  52. package/dist/commands/__tests__/upgrade.swap.test.js.map +1 -0
  53. package/dist/commands/create.d.ts.map +1 -1
  54. package/dist/commands/create.js +31 -0
  55. package/dist/commands/create.js.map +1 -1
  56. package/dist/commands/upgrade-history.d.ts +17 -0
  57. package/dist/commands/upgrade-history.d.ts.map +1 -0
  58. package/dist/commands/upgrade-history.js +40 -0
  59. package/dist/commands/upgrade-history.js.map +1 -0
  60. package/dist/commands/upgrade-lock.d.ts +102 -0
  61. package/dist/commands/upgrade-lock.d.ts.map +1 -0
  62. package/dist/commands/upgrade-lock.js +225 -0
  63. package/dist/commands/upgrade-lock.js.map +1 -0
  64. package/dist/commands/upgrade-log.d.ts +86 -0
  65. package/dist/commands/upgrade-log.d.ts.map +1 -0
  66. package/dist/commands/upgrade-log.js +146 -0
  67. package/dist/commands/upgrade-log.js.map +1 -0
  68. package/dist/commands/upgrade.d.ts +265 -0
  69. package/dist/commands/upgrade.d.ts.map +1 -1
  70. package/dist/commands/upgrade.js +840 -10
  71. package/dist/commands/upgrade.js.map +1 -1
  72. package/dist/image-presence.d.ts +40 -0
  73. package/dist/image-presence.d.ts.map +1 -0
  74. package/dist/image-presence.js +39 -0
  75. package/dist/image-presence.js.map +1 -0
  76. package/dist/index.js +1015 -163
  77. package/dist/protocol-version.d.ts +79 -0
  78. package/dist/protocol-version.d.ts.map +1 -0
  79. package/dist/protocol-version.js +133 -0
  80. package/dist/protocol-version.js.map +1 -0
  81. package/dist/registry-allowlist.d.ts +47 -0
  82. package/dist/registry-allowlist.d.ts.map +1 -0
  83. package/dist/registry-allowlist.js +67 -0
  84. package/dist/registry-allowlist.js.map +1 -0
  85. package/package.json +1 -1
@@ -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"}
@@ -0,0 +1,253 @@
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
+ import { describe, it, expect, beforeEach, afterEach } 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 { acquireLock, releaseLock, readLockFile, isStaleLock, isPidAlive, isOlamUpgradeCommand, formatRefusalMessage, STALE_LOCK_TIMEOUT_MS, PS_UNAVAILABLE, } from '../upgrade-lock.js';
19
+ let tmpDir;
20
+ let tmpLockPath;
21
+ beforeEach(() => {
22
+ tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'olam-lock-test-'));
23
+ tmpLockPath = path.join(tmpDir, 'upgrade.lock');
24
+ });
25
+ afterEach(() => {
26
+ try {
27
+ fs.rmSync(tmpDir, { recursive: true, force: true });
28
+ }
29
+ catch {
30
+ // best effort
31
+ }
32
+ });
33
+ describe('acquireLock — happy path', () => {
34
+ it('acquires a lock when none exists', () => {
35
+ const r = acquireLock(tmpLockPath);
36
+ expect(r.acquired).toBe(true);
37
+ if (r.acquired) {
38
+ expect(r.lockPath).toBe(tmpLockPath);
39
+ }
40
+ expect(fs.existsSync(tmpLockPath)).toBe(true);
41
+ });
42
+ it('writes pid + startTs to the lock file', () => {
43
+ const before = Date.now();
44
+ const r = acquireLock(tmpLockPath);
45
+ const after = Date.now();
46
+ expect(r.acquired).toBe(true);
47
+ const content = readLockFile(tmpLockPath);
48
+ expect(content).not.toBeNull();
49
+ expect(content.pid).toBe(process.pid);
50
+ expect(content.startTs).toBeGreaterThanOrEqual(before);
51
+ expect(content.startTs).toBeLessThanOrEqual(after);
52
+ });
53
+ it('creates the parent directory if missing', () => {
54
+ const nestedDir = path.join(tmpDir, 'nested', 'deeper');
55
+ const nestedLock = path.join(nestedDir, 'upgrade.lock');
56
+ expect(fs.existsSync(nestedDir)).toBe(false);
57
+ const r = acquireLock(nestedLock);
58
+ expect(r.acquired).toBe(true);
59
+ expect(fs.existsSync(nestedLock)).toBe(true);
60
+ });
61
+ });
62
+ describe('acquireLock — stale recovery', () => {
63
+ it('treats empty file as stale and retries', () => {
64
+ fs.writeFileSync(tmpLockPath, '');
65
+ const r = acquireLock(tmpLockPath);
66
+ expect(r.acquired).toBe(true);
67
+ const content = readLockFile(tmpLockPath);
68
+ expect(content.pid).toBe(process.pid);
69
+ });
70
+ it('treats parse-error file as stale and retries', () => {
71
+ fs.writeFileSync(tmpLockPath, 'not valid json {{{ broken');
72
+ const r = acquireLock(tmpLockPath);
73
+ expect(r.acquired).toBe(true);
74
+ });
75
+ it('treats shape-mismatch file as stale and retries', () => {
76
+ fs.writeFileSync(tmpLockPath, JSON.stringify({ wrong: 'shape' }));
77
+ const r = acquireLock(tmpLockPath);
78
+ expect(r.acquired).toBe(true);
79
+ });
80
+ it('treats dead-pid lock as stale and retries', () => {
81
+ const deadPid = 99999999;
82
+ fs.writeFileSync(tmpLockPath, JSON.stringify({ pid: deadPid, startTs: Date.now() }));
83
+ const r = acquireLock(tmpLockPath);
84
+ expect(r.acquired).toBe(true);
85
+ });
86
+ it('treats lock older than STALE_LOCK_TIMEOUT_MS as stale', () => {
87
+ const oldTs = Date.now() - STALE_LOCK_TIMEOUT_MS - 1000;
88
+ fs.writeFileSync(tmpLockPath, JSON.stringify({ pid: process.pid, startTs: oldTs }));
89
+ const r = acquireLock(tmpLockPath);
90
+ expect(r.acquired).toBe(true);
91
+ });
92
+ });
93
+ describe('acquireLock — refusal on live lock', () => {
94
+ it('refuses when an alive olam-process holds the lock', () => {
95
+ fs.writeFileSync(tmpLockPath, JSON.stringify({ pid: process.pid, startTs: Date.now() }));
96
+ const r = acquireLock(tmpLockPath);
97
+ expect(r.acquired).toBe(false);
98
+ if (!r.acquired) {
99
+ expect(r.reason).toBe('live');
100
+ expect(r.existingPid).toBe(process.pid);
101
+ }
102
+ });
103
+ });
104
+ describe('releaseLock', () => {
105
+ it('removes an existing lock', () => {
106
+ fs.writeFileSync(tmpLockPath, JSON.stringify({ pid: process.pid, startTs: Date.now() }));
107
+ releaseLock(tmpLockPath);
108
+ expect(fs.existsSync(tmpLockPath)).toBe(false);
109
+ });
110
+ it('is idempotent (no error on missing lock)', () => {
111
+ expect(() => releaseLock(tmpLockPath)).not.toThrow();
112
+ });
113
+ });
114
+ describe('isStaleLock', () => {
115
+ it('null content → stale', () => {
116
+ expect(isStaleLock(null)).toBe(true);
117
+ });
118
+ it('content older than 30 min → stale', () => {
119
+ const old = Date.now() - STALE_LOCK_TIMEOUT_MS - 1;
120
+ expect(isStaleLock({ pid: process.pid, startTs: old })).toBe(true);
121
+ });
122
+ it('content with dead pid → stale', () => {
123
+ expect(isStaleLock({ pid: 99999999, startTs: Date.now() })).toBe(true);
124
+ });
125
+ it('fresh content with own pid → live (not stale)', () => {
126
+ expect(isStaleLock({ pid: process.pid, startTs: Date.now() })).toBe(false);
127
+ });
128
+ });
129
+ describe('isPidAlive', () => {
130
+ it('returns true for own pid', () => {
131
+ expect(isPidAlive(process.pid)).toBe(true);
132
+ });
133
+ it('returns false for unused high pid', () => {
134
+ expect(isPidAlive(99999999)).toBe(false);
135
+ });
136
+ });
137
+ describe('isOlamUpgradeCommand', () => {
138
+ it('accepts node', () => {
139
+ expect(isOlamUpgradeCommand('node')).toBe(true);
140
+ });
141
+ it('accepts /usr/local/bin/node (path-prefixed)', () => {
142
+ expect(isOlamUpgradeCommand('/usr/local/bin/node')).toBe(true);
143
+ });
144
+ it('accepts olam', () => {
145
+ expect(isOlamUpgradeCommand('olam')).toBe(true);
146
+ });
147
+ it('accepts olam-cli', () => {
148
+ expect(isOlamUpgradeCommand('olam-cli')).toBe(true);
149
+ });
150
+ it('accepts node with worker-pool suffix (e.g. vitest)', () => {
151
+ expect(isOlamUpgradeCommand('node (vitest 1)')).toBe(true);
152
+ expect(isOlamUpgradeCommand('node (vitest pool 3)')).toBe(true);
153
+ });
154
+ it('rejects unrelated commands', () => {
155
+ expect(isOlamUpgradeCommand('bash')).toBe(false);
156
+ expect(isOlamUpgradeCommand('zsh')).toBe(false);
157
+ expect(isOlamUpgradeCommand('python3')).toBe(false);
158
+ });
159
+ it('rejects null', () => {
160
+ expect(isOlamUpgradeCommand(null)).toBe(false);
161
+ });
162
+ it('rejects empty string', () => {
163
+ expect(isOlamUpgradeCommand('')).toBe(false);
164
+ });
165
+ });
166
+ describe('formatRefusalMessage', () => {
167
+ it('includes pid when known', () => {
168
+ const msg = formatRefusalMessage({
169
+ acquired: false,
170
+ reason: 'live',
171
+ existingPid: 12345,
172
+ existingStartTs: Date.now(),
173
+ }, '/tmp/test.lock');
174
+ expect(msg).toContain('12345');
175
+ });
176
+ it('mentions --history hint and rm path', () => {
177
+ const msg = formatRefusalMessage({
178
+ acquired: false,
179
+ reason: 'live',
180
+ existingPid: 12345,
181
+ existingStartTs: Date.now(),
182
+ }, '/tmp/test.lock');
183
+ expect(msg).toContain('--history');
184
+ expect(msg).toContain('rm /tmp/test.lock');
185
+ });
186
+ it('omits pid string when not known', () => {
187
+ const msg = formatRefusalMessage({
188
+ acquired: false,
189
+ reason: 'race',
190
+ }, '/tmp/test.lock');
191
+ expect(msg).toContain('Upgrade in progress.');
192
+ expect(msg).not.toContain('pid');
193
+ });
194
+ });
195
+ describe('parallel acquisition (live-lock refusal)', () => {
196
+ it('first acquires; second refuses with existingPid populated', () => {
197
+ const r1 = acquireLock(tmpLockPath);
198
+ expect(r1.acquired).toBe(true);
199
+ const r2 = acquireLock(tmpLockPath);
200
+ expect(r2.acquired).toBe(false);
201
+ if (!r2.acquired) {
202
+ expect(r2.reason).toBe('live');
203
+ expect(r2.existingPid).toBe(process.pid);
204
+ }
205
+ releaseLock(tmpLockPath);
206
+ });
207
+ it('after release, a new acquire succeeds', () => {
208
+ const r1 = acquireLock(tmpLockPath);
209
+ expect(r1.acquired).toBe(true);
210
+ releaseLock(tmpLockPath);
211
+ const r2 = acquireLock(tmpLockPath);
212
+ expect(r2.acquired).toBe(true);
213
+ });
214
+ });
215
+ describe('isStaleLock — ps-unavailable fail-live (audit A1-003)', () => {
216
+ it('treats PS_UNAVAILABLE comm as live (NOT stale) when pid is alive', () => {
217
+ // Simulate: process is alive but `ps` couldn't tell us its name (binary missing,
218
+ // container without procfs, fork pressure). Per audit A1-003, the safe default
219
+ // is "assume live, refuse to recover" — better than silently deleting a live lock.
220
+ const liveAlivePid = process.pid;
221
+ // We can't easily mock getPidCommand here without DI; verify isOlamUpgradeCommand
222
+ // returns false for PS_UNAVAILABLE so callers branching on it work correctly.
223
+ expect(isOlamUpgradeCommand(PS_UNAVAILABLE)).toBe(false);
224
+ });
225
+ it('exposes PS_UNAVAILABLE sentinel as a string constant', () => {
226
+ expect(typeof PS_UNAVAILABLE).toBe('string');
227
+ expect(PS_UNAVAILABLE.length).toBeGreaterThan(0);
228
+ });
229
+ });
230
+ describe('STALE_LOCK_TIMEOUT_MS — bounds false-refusal blast-radius (audit A1-004)', () => {
231
+ it('is set to 5 minutes (shortened from 30 min per audit recommendation)', () => {
232
+ expect(STALE_LOCK_TIMEOUT_MS).toBe(5 * 60 * 1000);
233
+ });
234
+ });
235
+ describe('readLockFile', () => {
236
+ it('returns null on missing file', () => {
237
+ expect(readLockFile(tmpLockPath)).toBeNull();
238
+ });
239
+ it('returns null on empty file', () => {
240
+ fs.writeFileSync(tmpLockPath, '');
241
+ expect(readLockFile(tmpLockPath)).toBeNull();
242
+ });
243
+ it('returns null on invalid JSON', () => {
244
+ fs.writeFileSync(tmpLockPath, 'not json');
245
+ expect(readLockFile(tmpLockPath)).toBeNull();
246
+ });
247
+ it('returns parsed content on valid file', () => {
248
+ const content = { pid: 12345, startTs: 1700000000000 };
249
+ fs.writeFileSync(tmpLockPath, JSON.stringify(content));
250
+ expect(readLockFile(tmpLockPath)).toEqual(content);
251
+ });
252
+ });
253
+ //# sourceMappingURL=upgrade.lock.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"upgrade.lock.test.js","sourceRoot":"","sources":["../../../src/commands/__tests__/upgrade.lock.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,21 @@
1
+ /**
2
+ * Verifies that build-{host-cp,auth,devbox}.sh accept an OLAM_TAG env var
3
+ * that retags the freshly-built image with an additional alias (Phase 2a — A3).
4
+ *
5
+ * The CLI's atomic-swap workflow (A6) needs all three scripts to produce
6
+ * `<image>:olam-next` tags during the staged-build window so canonical tags
7
+ * (`:latest` for host-cp/devbox, `:local` for auth) stay untouched until the
8
+ * final 6-tag swap.
9
+ *
10
+ * Default unset → no retag → backward-compat with manual `bash build-*.sh`
11
+ * invocation.
12
+ *
13
+ * This test reads each script as TEXT and asserts the retag block is present
14
+ * + correctly placed (after the docker build). A real docker integration test
15
+ * lives at tests/integration/build-scripts-olam-tag.sh (manual smoke; not run
16
+ * in CI without a docker daemon).
17
+ *
18
+ * Run: `cd packages/cli && npx vitest run src/commands/__tests__/upgrade.olam-tag.test.ts`
19
+ */
20
+ export {};
21
+ //# sourceMappingURL=upgrade.olam-tag.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"upgrade.olam-tag.test.d.ts","sourceRoot":"","sources":["../../../src/commands/__tests__/upgrade.olam-tag.test.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG"}