@pleri/olam-cli 0.1.12 → 0.1.14

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 (86) 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 +849 -13
  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 +1043 -167
  77. package/dist/index.js.map +1 -1
  78. package/dist/protocol-version.d.ts +79 -0
  79. package/dist/protocol-version.d.ts.map +1 -0
  80. package/dist/protocol-version.js +133 -0
  81. package/dist/protocol-version.js.map +1 -0
  82. package/dist/registry-allowlist.d.ts +47 -0
  83. package/dist/registry-allowlist.d.ts.map +1 -0
  84. package/dist/registry-allowlist.js +67 -0
  85. package/dist/registry-allowlist.js.map +1 -0
  86. package/package.json +1 -1
@@ -0,0 +1,127 @@
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
+ import { describe, it, expect } from 'vitest';
21
+ import * as fs from 'node:fs';
22
+ import * as path from 'node:path';
23
+ // Resolve to the repo root regardless of which workspace cwd vitest runs from.
24
+ function findRepoRoot(start) {
25
+ let dir = start;
26
+ for (let i = 0; i < 10; i++) {
27
+ if (fs.existsSync(path.join(dir, 'packages', 'host-cp', 'compose.yaml')))
28
+ return dir;
29
+ const parent = path.dirname(dir);
30
+ if (parent === dir)
31
+ break;
32
+ dir = parent;
33
+ }
34
+ throw new Error(`Could not find repo root from ${start}`);
35
+ }
36
+ const REPO_ROOT = findRepoRoot(process.cwd());
37
+ const SCRIPTS_DIR = path.join(REPO_ROOT, 'packages', 'adapters', 'src', 'docker');
38
+ const SCRIPTS = [
39
+ {
40
+ name: 'build-host-cp.sh',
41
+ path: path.join(SCRIPTS_DIR, 'build-host-cp.sh'),
42
+ canonicalTagPattern: /olam-host-cp:latest/,
43
+ retagSourceVar: 'olam-host-cp:latest',
44
+ },
45
+ {
46
+ name: 'build-auth.sh',
47
+ path: path.join(SCRIPTS_DIR, 'build-auth.sh'),
48
+ canonicalTagPattern: /olam-auth:local/,
49
+ retagSourceVar: 'olam-auth:local',
50
+ },
51
+ {
52
+ name: 'build-devbox.sh',
53
+ path: path.join(SCRIPTS_DIR, 'build-devbox.sh'),
54
+ canonicalTagPattern: /IMAGE_TAG="olam-devbox:latest"/,
55
+ retagSourceVar: '$IMAGE_TAG',
56
+ },
57
+ ];
58
+ describe('build-*.sh — OLAM_TAG opt-in retag (Phase 2a A3)', () => {
59
+ for (const script of SCRIPTS) {
60
+ describe(script.name, () => {
61
+ let body;
62
+ it('script file exists', () => {
63
+ expect(fs.existsSync(script.path)).toBe(true);
64
+ body = fs.readFileSync(script.path, 'utf-8');
65
+ expect(body.length).toBeGreaterThan(100);
66
+ });
67
+ it('preserves the canonical (default) tag for backward-compat', () => {
68
+ body ??= fs.readFileSync(script.path, 'utf-8');
69
+ expect(body).toMatch(script.canonicalTagPattern);
70
+ });
71
+ it('contains the OLAM_TAG conditional block', () => {
72
+ body ??= fs.readFileSync(script.path, 'utf-8');
73
+ // The block guards on `if [ -n "${OLAM_TAG:-}" ]; then`
74
+ expect(body).toMatch(/if\s*\[\s*-n\s+"\$\{OLAM_TAG:-\}"\s*\]\s*;\s*then/);
75
+ });
76
+ it('issues a docker tag command inside (or after the start of) the OLAM_TAG block', () => {
77
+ body ??= fs.readFileSync(script.path, 'utf-8');
78
+ // Find the OLAM_TAG block opening; assert `docker tag` appears
79
+ // somewhere after that (could be inside a nested-conditional fi
80
+ // for the devbox 2-axis case, so we don't try to extract the
81
+ // exact closing fi — that's regex-fragile).
82
+ const blockStart = body.search(/if\s*\[\s*-n\s+"\$\{OLAM_TAG:-\}"\s*\]/);
83
+ expect(blockStart).toBeGreaterThan(0);
84
+ const after = body.slice(blockStart);
85
+ expect(after).toMatch(/docker\s+tag\b/);
86
+ });
87
+ it('OLAM_TAG block appears AFTER the docker build invocation', () => {
88
+ body ??= fs.readFileSync(script.path, 'utf-8');
89
+ const buildIdx = body.search(/docker\s+(?:compose\s+(?:-f\s+\S+\s+)?)?build\b/);
90
+ const olamTagIdx = body.search(/if\s*\[\s*-n\s+"\$\{OLAM_TAG:-\}"\s*\]/);
91
+ expect(buildIdx).toBeGreaterThan(0);
92
+ expect(olamTagIdx).toBeGreaterThan(0);
93
+ expect(olamTagIdx).toBeGreaterThan(buildIdx);
94
+ });
95
+ it('retag source uses the canonical tag (backward-compat invariant)', () => {
96
+ body ??= fs.readFileSync(script.path, 'utf-8');
97
+ const blockStart = body.search(/if\s*\[\s*-n\s+"\$\{OLAM_TAG:-\}"\s*\]/);
98
+ expect(blockStart).toBeGreaterThan(0);
99
+ const after = body.slice(blockStart);
100
+ // Source of `docker tag <source> <dest>` must reference the canonical
101
+ // tag (or its variable, for build-devbox.sh).
102
+ if (script.name === 'build-devbox.sh') {
103
+ expect(after).toMatch(/docker tag\s+"\$IMAGE_TAG"/);
104
+ }
105
+ else {
106
+ expect(after).toContain(script.retagSourceVar);
107
+ }
108
+ });
109
+ });
110
+ }
111
+ });
112
+ describe('build-devbox.sh — OLAM_TAG × OLAM_DEVBOX_PLATFORM 2-axis composition', () => {
113
+ let body;
114
+ it('reads the file', () => {
115
+ body = fs.readFileSync(path.join(SCRIPTS_DIR, 'build-devbox.sh'), 'utf-8');
116
+ });
117
+ it('handles the latest case (no platform suffix → flat OLAM_TAG)', () => {
118
+ body ??= fs.readFileSync(path.join(SCRIPTS_DIR, 'build-devbox.sh'), 'utf-8');
119
+ expect(body).toMatch(/if\s*\[\s*"\$IMAGE_PLATFORM_SUFFIX"\s*=\s*"latest"\s*\]/);
120
+ });
121
+ it('composes <repo>:<platform>-<olam-tag> for non-latest platforms', () => {
122
+ body ??= fs.readFileSync(path.join(SCRIPTS_DIR, 'build-devbox.sh'), 'utf-8');
123
+ // Should include ${IMAGE_PLATFORM_SUFFIX}-${OLAM_TAG} in the else branch.
124
+ expect(body).toMatch(/\$\{IMAGE_PLATFORM_SUFFIX\}-\$\{OLAM_TAG\}/);
125
+ });
126
+ });
127
+ //# sourceMappingURL=upgrade.olam-tag.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"upgrade.olam-tag.test.js","sourceRoot":"","sources":["../../../src/commands/__tests__/upgrade.olam-tag.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Phase 2a A8 — verifies waitForVersionMatch + formatVersionMismatch.
3
+ *
4
+ * Uses vi.spyOn(global, 'fetch') to mock /api/version/status responses
5
+ * and assert the polling loop:
6
+ * - Returns matched=true when all three SHAs equal target.
7
+ * - Returns matched=false on timeout when SHAs never converge.
8
+ * - Tolerates transient fetch failures (host-cp restart blackout).
9
+ * - Returns the LAST snapshot received for diagnostic display.
10
+ *
11
+ * Run: `cd packages/cli && npx vitest run src/commands/__tests__/upgrade.poll.test.ts`
12
+ */
13
+ export {};
14
+ //# sourceMappingURL=upgrade.poll.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"upgrade.poll.test.d.ts","sourceRoot":"","sources":["../../../src/commands/__tests__/upgrade.poll.test.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG"}
@@ -0,0 +1,136 @@
1
+ /**
2
+ * Phase 2a A8 — verifies waitForVersionMatch + formatVersionMismatch.
3
+ *
4
+ * Uses vi.spyOn(global, 'fetch') to mock /api/version/status responses
5
+ * and assert the polling loop:
6
+ * - Returns matched=true when all three SHAs equal target.
7
+ * - Returns matched=false on timeout when SHAs never converge.
8
+ * - Tolerates transient fetch failures (host-cp restart blackout).
9
+ * - Returns the LAST snapshot received for diagnostic display.
10
+ *
11
+ * Run: `cd packages/cli && npx vitest run src/commands/__tests__/upgrade.poll.test.ts`
12
+ */
13
+ import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
14
+ import { waitForVersionMatch, formatVersionMismatch } from '../upgrade.js';
15
+ const TARGET_SHA = 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa';
16
+ const OTHER_SHA = 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb';
17
+ function makeSnapshot(hostCp, authService, devbox) {
18
+ return {
19
+ hostCp: { running: hostCp, latest: TARGET_SHA, upgradeAvailable: hostCp !== TARGET_SHA },
20
+ authService: { running: authService, latest: TARGET_SHA, upgradeAvailable: authService !== TARGET_SHA },
21
+ devbox: { running: devbox, latest: TARGET_SHA, upgradeAvailable: devbox !== TARGET_SHA },
22
+ operatorHead: TARGET_SHA,
23
+ checkedAt: '2026-05-07T00:00:00.000Z',
24
+ };
25
+ }
26
+ function mockFetchResponse(snapshot) {
27
+ return new Response(JSON.stringify(snapshot), {
28
+ status: 200,
29
+ headers: { 'content-type': 'application/json' },
30
+ });
31
+ }
32
+ describe('waitForVersionMatch — happy path', () => {
33
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
34
+ let fetchSpy;
35
+ beforeEach(() => {
36
+ fetchSpy = vi.spyOn(global, 'fetch');
37
+ });
38
+ afterEach(() => {
39
+ fetchSpy.mockRestore();
40
+ });
41
+ it('returns matched=true when all three SHAs already equal target on first poll', async () => {
42
+ const snapshot = makeSnapshot(TARGET_SHA, TARGET_SHA, TARGET_SHA);
43
+ fetchSpy.mockResolvedValue(mockFetchResponse(snapshot));
44
+ const result = await waitForVersionMatch(TARGET_SHA, 5_000, 100);
45
+ expect(result.matched).toBe(true);
46
+ expect(result.snapshot).toEqual(snapshot);
47
+ });
48
+ it('returns matched=true after SHA propagation completes mid-poll', async () => {
49
+ const stale = makeSnapshot(OTHER_SHA, OTHER_SHA, OTHER_SHA);
50
+ const partial = makeSnapshot(TARGET_SHA, OTHER_SHA, OTHER_SHA);
51
+ const fresh = makeSnapshot(TARGET_SHA, TARGET_SHA, TARGET_SHA);
52
+ fetchSpy
53
+ .mockResolvedValueOnce(mockFetchResponse(stale))
54
+ .mockResolvedValueOnce(mockFetchResponse(partial))
55
+ .mockResolvedValueOnce(mockFetchResponse(fresh));
56
+ const result = await waitForVersionMatch(TARGET_SHA, 10_000, 50);
57
+ expect(result.matched).toBe(true);
58
+ expect(result.snapshot).toEqual(fresh);
59
+ });
60
+ });
61
+ describe('waitForVersionMatch — failure paths', () => {
62
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
63
+ let fetchSpy;
64
+ beforeEach(() => {
65
+ fetchSpy = vi.spyOn(global, 'fetch');
66
+ });
67
+ afterEach(() => {
68
+ fetchSpy.mockRestore();
69
+ });
70
+ it('returns matched=false on timeout when SHAs never converge', async () => {
71
+ const stale = makeSnapshot(OTHER_SHA, OTHER_SHA, OTHER_SHA);
72
+ fetchSpy.mockResolvedValue(mockFetchResponse(stale));
73
+ const result = await waitForVersionMatch(TARGET_SHA, 300, 50);
74
+ expect(result.matched).toBe(false);
75
+ expect(result.snapshot).toEqual(stale);
76
+ });
77
+ it('tolerates transient fetch errors (host-cp restart blackout)', async () => {
78
+ const fresh = makeSnapshot(TARGET_SHA, TARGET_SHA, TARGET_SHA);
79
+ fetchSpy
80
+ .mockRejectedValueOnce(new Error('ECONNREFUSED'))
81
+ .mockRejectedValueOnce(new Error('ECONNREFUSED'))
82
+ .mockResolvedValueOnce(mockFetchResponse(fresh));
83
+ const result = await waitForVersionMatch(TARGET_SHA, 5_000, 50);
84
+ expect(result.matched).toBe(true);
85
+ expect(result.snapshot).toEqual(fresh);
86
+ });
87
+ it('returns matched=false with snapshot=null when /api/version/status is never reachable', async () => {
88
+ fetchSpy.mockRejectedValue(new Error('ECONNREFUSED'));
89
+ const result = await waitForVersionMatch(TARGET_SHA, 200, 50);
90
+ expect(result.matched).toBe(false);
91
+ expect(result.snapshot).toBeNull();
92
+ });
93
+ it('returns matched=false when /api/version/status returns non-200', async () => {
94
+ fetchSpy.mockResolvedValue(new Response('Internal Server Error', { status: 500 }));
95
+ const result = await waitForVersionMatch(TARGET_SHA, 200, 50);
96
+ expect(result.matched).toBe(false);
97
+ expect(result.snapshot).toBeNull();
98
+ });
99
+ });
100
+ describe('formatVersionMismatch', () => {
101
+ it('renders 3-line per-component diff with ✓ / ✗ markers', () => {
102
+ const partial = makeSnapshot(TARGET_SHA, OTHER_SHA, OTHER_SHA);
103
+ const out = formatVersionMismatch(TARGET_SHA, partial);
104
+ expect(out).toMatch(/✓ host-cp/);
105
+ expect(out).toMatch(/✗ auth-service/);
106
+ expect(out).toMatch(/✗ devbox/);
107
+ expect(out).toContain(TARGET_SHA.slice(0, 8));
108
+ expect(out).toContain(OTHER_SHA.slice(0, 8));
109
+ });
110
+ it('handles null snapshot gracefully', () => {
111
+ expect(formatVersionMismatch(TARGET_SHA, null)).toMatch(/No \/api\/version\/status response/);
112
+ });
113
+ it('marks all components ✗ when none match target', () => {
114
+ const stale = makeSnapshot(OTHER_SHA, OTHER_SHA, OTHER_SHA);
115
+ const out = formatVersionMismatch(TARGET_SHA, stale);
116
+ expect((out.match(/✗/g) ?? []).length).toBe(3);
117
+ expect(out.match(/✓/g)).toBeNull();
118
+ });
119
+ it('marks all components ✓ when all match target', () => {
120
+ const fresh = makeSnapshot(TARGET_SHA, TARGET_SHA, TARGET_SHA);
121
+ const out = formatVersionMismatch(TARGET_SHA, fresh);
122
+ expect((out.match(/✓/g) ?? []).length).toBe(3);
123
+ expect(out.match(/✗/g)).toBeNull();
124
+ });
125
+ });
126
+ describe('VersionSnapshot type contract', () => {
127
+ it('exports a usable snapshot shape with three components + operatorHead + checkedAt', () => {
128
+ const sample = makeSnapshot(TARGET_SHA, TARGET_SHA, TARGET_SHA);
129
+ expect(sample.hostCp.running).toBe(TARGET_SHA);
130
+ expect(sample.authService.running).toBe(TARGET_SHA);
131
+ expect(sample.devbox.running).toBe(TARGET_SHA);
132
+ expect(sample.operatorHead).toBe(TARGET_SHA);
133
+ expect(sample.checkedAt).toBeTruthy();
134
+ });
135
+ });
136
+ //# sourceMappingURL=upgrade.poll.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"upgrade.poll.test.js","sourceRoot":"","sources":["../../../src/commands/__tests__/upgrade.poll.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Phase 2a A7 — verifies auth-service recreate via AuthContainerController.
3
+ *
4
+ * Source-text invariants (no docker required):
5
+ * - upgrade.ts imports AuthContainerController from @olam/core
6
+ * - recreateAuthService is invoked AFTER the compose recreate succeeds
7
+ * - auth-recreate-fail branch surfaces --rollback hint (per audit A6-002)
8
+ * - waitForAuthHealthLocal mirrors auth-upgrade.ts contract
9
+ *
10
+ * Running the actual recreateAuthService against a real auth-service
11
+ * container is covered by the manual smoke at tests/integration/upgrade-all-three.sh
12
+ * — too heavy for unit tests (requires the full olam-auth image + secret env).
13
+ *
14
+ * Run: `cd packages/cli && npx vitest run src/commands/__tests__/upgrade.recreate.test.ts`
15
+ */
16
+ export {};
17
+ //# sourceMappingURL=upgrade.recreate.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"upgrade.recreate.test.d.ts","sourceRoot":"","sources":["../../../src/commands/__tests__/upgrade.recreate.test.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG"}
@@ -0,0 +1,95 @@
1
+ /**
2
+ * Phase 2a A7 — verifies auth-service recreate via AuthContainerController.
3
+ *
4
+ * Source-text invariants (no docker required):
5
+ * - upgrade.ts imports AuthContainerController from @olam/core
6
+ * - recreateAuthService is invoked AFTER the compose recreate succeeds
7
+ * - auth-recreate-fail branch surfaces --rollback hint (per audit A6-002)
8
+ * - waitForAuthHealthLocal mirrors auth-upgrade.ts contract
9
+ *
10
+ * Running the actual recreateAuthService against a real auth-service
11
+ * container is covered by the manual smoke at tests/integration/upgrade-all-three.sh
12
+ * — too heavy for unit tests (requires the full olam-auth image + secret env).
13
+ *
14
+ * Run: `cd packages/cli && npx vitest run src/commands/__tests__/upgrade.recreate.test.ts`
15
+ */
16
+ import { describe, it, expect } from 'vitest';
17
+ import * as fs from 'node:fs';
18
+ import * as path from 'node:path';
19
+ function findRepoRoot(start) {
20
+ let dir = start;
21
+ for (let i = 0; i < 10; i++) {
22
+ if (fs.existsSync(path.join(dir, 'packages', 'host-cp', 'compose.yaml')))
23
+ return dir;
24
+ const parent = path.dirname(dir);
25
+ if (parent === dir)
26
+ break;
27
+ dir = parent;
28
+ }
29
+ throw new Error(`Could not find repo root from ${start}`);
30
+ }
31
+ const REPO_ROOT = findRepoRoot(process.cwd());
32
+ const UPGRADE_TS = path.join(REPO_ROOT, 'packages', 'cli', 'src', 'commands', 'upgrade.ts');
33
+ describe('Phase 2a A7 — auth-service recreate via AuthContainerController', () => {
34
+ let body;
35
+ it('reads upgrade.ts source', () => {
36
+ body = fs.readFileSync(UPGRADE_TS, 'utf-8');
37
+ expect(body.length).toBeGreaterThan(1000);
38
+ });
39
+ it('imports AuthContainerController from @olam/core', () => {
40
+ body ??= fs.readFileSync(UPGRADE_TS, 'utf-8');
41
+ expect(body).toMatch(/import\s+\{\s*AuthContainerController\s*\}\s*from\s*['"]@olam\/core/);
42
+ });
43
+ it('declares AUTH_HEALTH_URL pointing at the auth-service port', () => {
44
+ body ??= fs.readFileSync(UPGRADE_TS, 'utf-8');
45
+ expect(body).toContain('http://127.0.0.1:9999/health');
46
+ });
47
+ it('defines recreateAuthService helper', () => {
48
+ body ??= fs.readFileSync(UPGRADE_TS, 'utf-8');
49
+ expect(body).toMatch(/async function recreateAuthService\(/);
50
+ });
51
+ it('recreateAuthService stops + removes + starts the container', () => {
52
+ body ??= fs.readFileSync(UPGRADE_TS, 'utf-8');
53
+ // Look for the docker stop, docker rm, controller.start() sequence.
54
+ const fnMatch = body.match(/async function recreateAuthService[\s\S]*?\n\}/);
55
+ expect(fnMatch).not.toBeNull();
56
+ const fnBody = fnMatch[0];
57
+ expect(fnBody).toMatch(/docker.*stop.*olam-auth/);
58
+ expect(fnBody).toMatch(/docker.*rm.*olam-auth/);
59
+ expect(fnBody).toMatch(/new AuthContainerController\(\)/);
60
+ expect(fnBody).toMatch(/\.start\(\)/);
61
+ expect(fnBody).toMatch(/waitForAuthHealthLocal\(/);
62
+ });
63
+ it('integrates auth recreate AFTER the compose recreate of host-cp (in upgrade path, not rollback path)', () => {
64
+ body ??= fs.readFileSync(UPGRADE_TS, 'utf-8');
65
+ // Scope to runUpgradeStepsWithLockHeld body, since handleRollback (B1)
66
+ // also calls recreateAuthService and would confuse a global search.
67
+ const upgradeFnStart = body.indexOf('async function runUpgradeStepsWithLockHeld');
68
+ expect(upgradeFnStart).toBeGreaterThan(0);
69
+ const upgradeBody = body.slice(upgradeFnStart);
70
+ const composeIdx = upgradeBody.indexOf('docker compose up --force-recreate failed');
71
+ const authRecreateCallIdx = upgradeBody.indexOf('recreateAuthService(');
72
+ expect(composeIdx).toBeGreaterThan(0);
73
+ expect(authRecreateCallIdx).toBeGreaterThan(composeIdx);
74
+ });
75
+ it('auth-recreate-fail branch surfaces --rollback hint (audit A6-002, upgrade path)', () => {
76
+ body ??= fs.readFileSync(UPGRADE_TS, 'utf-8');
77
+ // Scope to runUpgradeStepsWithLockHeld body — handleRollback's auth fail
78
+ // branch (B1) does NOT mention --rollback because it IS the rollback path.
79
+ const upgradeFnStart = body.indexOf('async function runUpgradeStepsWithLockHeld');
80
+ const upgradeBody = body.slice(upgradeFnStart);
81
+ const failBranch = upgradeBody.match(/Auth-service recreate failed[\s\S]*?process\.exitCode\s*=\s*1/);
82
+ expect(failBranch).not.toBeNull();
83
+ expect(failBranch[0]).toContain('--rollback');
84
+ });
85
+ it('mirrors auth-upgrade.ts recreate pattern (docker stop → rm → controller.start)', () => {
86
+ const authUpgradeTs = fs.readFileSync(path.join(REPO_ROOT, 'packages', 'cli', 'src', 'commands', 'auth-upgrade.ts'), 'utf-8');
87
+ // auth-upgrade.ts must contain the same primitive operations our helper
88
+ // mirrors. If auth-upgrade.ts ever migrates the pattern (e.g., moves to
89
+ // compose-driven), this test breaks loudly so we stay in sync.
90
+ expect(authUpgradeTs).toMatch(/docker.*stop.*olam-auth/);
91
+ expect(authUpgradeTs).toMatch(/docker.*rm.*olam-auth/);
92
+ expect(authUpgradeTs).toMatch(/new AuthContainerController\(\)/);
93
+ });
94
+ });
95
+ //# sourceMappingURL=upgrade.recreate.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"upgrade.recreate.test.js","sourceRoot":"","sources":["../../../src/commands/__tests__/upgrade.recreate.test.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAC9C,OAAO,KAAK,EAAE,MAAM,SAAS,CAAC;AAC9B,OAAO,KAAK,IAAI,MAAM,WAAW,CAAC;AAElC,SAAS,YAAY,CAAC,KAAa;IACjC,IAAI,GAAG,GAAG,KAAK,CAAC;IAChB,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,EAAE,EAAE,CAAC;QAC5B,IAAI,EAAE,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,UAAU,EAAE,SAAS,EAAE,cAAc,CAAC,CAAC;YAAE,OAAO,GAAG,CAAC;QACrF,MAAM,MAAM,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;QACjC,IAAI,MAAM,KAAK,GAAG;YAAE,MAAM;QAC1B,GAAG,GAAG,MAAM,CAAC;IACf,CAAC;IACD,MAAM,IAAI,KAAK,CAAC,iCAAiC,KAAK,EAAE,CAAC,CAAC;AAC5D,CAAC;AAED,MAAM,SAAS,GAAG,YAAY,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC,CAAC;AAC9C,MAAM,UAAU,GAAG,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,UAAU,EAAE,KAAK,EAAE,KAAK,EAAE,UAAU,EAAE,YAAY,CAAC,CAAC;AAE5F,QAAQ,CAAC,iEAAiE,EAAE,GAAG,EAAE;IAC/E,IAAI,IAAY,CAAC;IAEjB,EAAE,CAAC,yBAAyB,EAAE,GAAG,EAAE;QACjC,IAAI,GAAG,EAAE,CAAC,YAAY,CAAC,UAAU,EAAE,OAAO,CAAC,CAAC;QAC5C,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,eAAe,CAAC,IAAI,CAAC,CAAC;IAC5C,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,iDAAiD,EAAE,GAAG,EAAE;QACzD,IAAI,KAAK,EAAE,CAAC,YAAY,CAAC,UAAU,EAAE,OAAO,CAAC,CAAC;QAC9C,MAAM,CAAC,IAAI,CAAC,CAAC,OAAO,CAAC,qEAAqE,CAAC,CAAC;IAC9F,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,4DAA4D,EAAE,GAAG,EAAE;QACpE,IAAI,KAAK,EAAE,CAAC,YAAY,CAAC,UAAU,EAAE,OAAO,CAAC,CAAC;QAC9C,MAAM,CAAC,IAAI,CAAC,CAAC,SAAS,CAAC,8BAA8B,CAAC,CAAC;IACzD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,oCAAoC,EAAE,GAAG,EAAE;QAC5C,IAAI,KAAK,EAAE,CAAC,YAAY,CAAC,UAAU,EAAE,OAAO,CAAC,CAAC;QAC9C,MAAM,CAAC,IAAI,CAAC,CAAC,OAAO,CAAC,sCAAsC,CAAC,CAAC;IAC/D,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,4DAA4D,EAAE,GAAG,EAAE;QACpE,IAAI,KAAK,EAAE,CAAC,YAAY,CAAC,UAAU,EAAE,OAAO,CAAC,CAAC;QAC9C,oEAAoE;QACpE,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,gDAAgD,CAAC,CAAC;QAC7E,MAAM,CAAC,OAAO,CAAC,CAAC,GAAG,CAAC,QAAQ,EAAE,CAAC;QAC/B,MAAM,MAAM,GAAG,OAAQ,CAAC,CAAC,CAAC,CAAC;QAC3B,MAAM,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC,yBAAyB,CAAC,CAAC;QAClD,MAAM,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC,uBAAuB,CAAC,CAAC;QAChD,MAAM,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC,iCAAiC,CAAC,CAAC;QAC1D,MAAM,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC,aAAa,CAAC,CAAC;QACtC,MAAM,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC,0BAA0B,CAAC,CAAC;IACrD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,qGAAqG,EAAE,GAAG,EAAE;QAC7G,IAAI,KAAK,EAAE,CAAC,YAAY,CAAC,UAAU,EAAE,OAAO,CAAC,CAAC;QAC9C,uEAAuE;QACvE,oEAAoE;QACpE,MAAM,cAAc,GAAG,IAAI,CAAC,OAAO,CAAC,4CAA4C,CAAC,CAAC;QAClF,MAAM,CAAC,cAAc,CAAC,CAAC,eAAe,CAAC,CAAC,CAAC,CAAC;QAC1C,MAAM,WAAW,GAAG,IAAI,CAAC,KAAK,CAAC,cAAc,CAAC,CAAC;QAC/C,MAAM,UAAU,GAAG,WAAW,CAAC,OAAO,CAAC,2CAA2C,CAAC,CAAC;QACpF,MAAM,mBAAmB,GAAG,WAAW,CAAC,OAAO,CAAC,sBAAsB,CAAC,CAAC;QACxE,MAAM,CAAC,UAAU,CAAC,CAAC,eAAe,CAAC,CAAC,CAAC,CAAC;QACtC,MAAM,CAAC,mBAAmB,CAAC,CAAC,eAAe,CAAC,UAAU,CAAC,CAAC;IAC1D,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,iFAAiF,EAAE,GAAG,EAAE;QACzF,IAAI,KAAK,EAAE,CAAC,YAAY,CAAC,UAAU,EAAE,OAAO,CAAC,CAAC;QAC9C,yEAAyE;QACzE,2EAA2E;QAC3E,MAAM,cAAc,GAAG,IAAI,CAAC,OAAO,CAAC,4CAA4C,CAAC,CAAC;QAClF,MAAM,WAAW,GAAG,IAAI,CAAC,KAAK,CAAC,cAAc,CAAC,CAAC;QAC/C,MAAM,UAAU,GAAG,WAAW,CAAC,KAAK,CAAC,+DAA+D,CAAC,CAAC;QACtG,MAAM,CAAC,UAAU,CAAC,CAAC,GAAG,CAAC,QAAQ,EAAE,CAAC;QAClC,MAAM,CAAC,UAAW,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,YAAY,CAAC,CAAC;IACjD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,gFAAgF,EAAE,GAAG,EAAE;QACxF,MAAM,aAAa,GAAG,EAAE,CAAC,YAAY,CACnC,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,UAAU,EAAE,KAAK,EAAE,KAAK,EAAE,UAAU,EAAE,iBAAiB,CAAC,EAC7E,OAAO,CACR,CAAC;QACF,wEAAwE;QACxE,wEAAwE;QACxE,+DAA+D;QAC/D,MAAM,CAAC,aAAa,CAAC,CAAC,OAAO,CAAC,yBAAyB,CAAC,CAAC;QACzD,MAAM,CAAC,aAAa,CAAC,CAAC,OAAO,CAAC,uBAAuB,CAAC,CAAC;QACvD,MAAM,CAAC,aAAa,CAAC,CAAC,OAAO,CAAC,iCAAiC,CAAC,CAAC;IACnE,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Phase 2b — verifies B1 (--rollback) + B2 (--force) + B3 (--no-cache) + B4 (drift refusal).
3
+ *
4
+ * Mix of:
5
+ * - vi.spyOn unit tests for the parseUpgradeOpts shape (B1/B2/B3 flags).
6
+ * - Real docker integration tests for performRollbackSwap + checkRollbackSetExists.
7
+ * - Source-text invariants for the drift refusal at swap boundary (B4).
8
+ *
9
+ * Run: `cd packages/cli && npx vitest run src/commands/__tests__/upgrade.rollback.test.ts`
10
+ */
11
+ export {};
12
+ //# sourceMappingURL=upgrade.rollback.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"upgrade.rollback.test.d.ts","sourceRoot":"","sources":["../../../src/commands/__tests__/upgrade.rollback.test.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG"}