@blackbelt-technology/pi-agent-dashboard 0.5.2 → 0.5.4
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/AGENTS.md +19 -30
- package/README.md +69 -1
- package/docs/architecture.md +89 -165
- package/package.json +11 -7
- package/packages/extension/package.json +2 -2
- package/packages/extension/src/__tests__/bridge-default-model-gate.test.ts +47 -0
- package/packages/extension/src/__tests__/bridge-followup-chat-order.test.ts +215 -0
- package/packages/extension/src/__tests__/bridge-followup-multi-entry.test.ts +202 -0
- package/packages/extension/src/__tests__/bridge-queue-update-forward.test.ts +77 -0
- package/packages/extension/src/__tests__/bridge-retry-ordering.test.ts +148 -0
- package/packages/extension/src/__tests__/bridge-shadow-queue-drain.test.ts +221 -0
- package/packages/extension/src/__tests__/bridge-shadow-queue-gate.test.ts +299 -0
- package/packages/extension/src/__tests__/bridge-shutdown-reset.test.ts +238 -0
- package/packages/extension/src/__tests__/bridge-slash-command-routing.test.ts +127 -31
- package/packages/extension/src/__tests__/command-handler.test.ts +105 -3
- package/packages/extension/src/__tests__/fixtures/usage-limit-error-strings.ts +127 -0
- package/packages/extension/src/__tests__/source-detector.test.ts +15 -0
- package/packages/extension/src/__tests__/usage-limit-orderer.test.ts +12 -0
- package/packages/extension/src/bridge-default-model-gate.ts +32 -0
- package/packages/extension/src/bridge.ts +299 -20
- package/packages/extension/src/command-handler.ts +53 -7
- package/packages/extension/src/dashboard-default-adapter.ts +5 -0
- package/packages/extension/src/prompt-bus.ts +15 -0
- package/packages/extension/src/slash-dispatch.ts +30 -15
- package/packages/extension/src/source-detector.ts +13 -5
- package/packages/extension/src/usage-limit-orderer.ts +18 -1
- package/packages/server/bin/pi-dashboard.mjs +62 -14
- package/packages/server/package.json +9 -5
- package/packages/server/src/__tests__/browser-gateway-register-handler.test.ts +69 -0
- package/packages/server/src/__tests__/cli-env-no-clobber.test.ts +46 -0
- package/packages/server/src/__tests__/cli-no-bootstrap-references.test.ts +69 -0
- package/packages/server/src/__tests__/cli-parse.test.ts +9 -10
- package/packages/server/src/__tests__/cli-version.test.ts +151 -0
- package/packages/server/src/__tests__/directory-service-openspec-enabled.test.ts +9 -0
- package/packages/server/src/__tests__/directory-service-refresh-force.test.ts +9 -0
- package/packages/server/src/__tests__/directory-service-specs-mtime.test.ts +9 -0
- package/packages/server/src/__tests__/directory-service-toctou.test.ts +9 -0
- package/packages/server/src/__tests__/directory-service.test.ts +9 -0
- package/packages/server/src/__tests__/doctor-route.test.ts +53 -0
- package/packages/server/src/__tests__/event-wiring-queue-state.test.ts +156 -0
- package/packages/server/src/__tests__/event-wiring-resume-clear.test.ts +105 -0
- package/packages/server/src/__tests__/health-shape.test.ts +35 -12
- package/packages/server/src/__tests__/installed-package-enricher.test.ts +12 -12
- package/packages/server/src/__tests__/is-activity-event.test.ts +4 -7
- package/packages/server/src/__tests__/package-routes.test.ts +6 -2
- package/packages/server/src/__tests__/pi-changelog-routes.test.ts +10 -13
- package/packages/server/src/__tests__/pi-core-checker.test.ts +2 -2
- package/packages/server/src/__tests__/pi-version-skew.test.ts +3 -2
- package/packages/server/src/__tests__/plugin-activation-routes.test.ts +267 -0
- package/packages/server/src/__tests__/plugin-intent-cache.test.ts +75 -0
- package/packages/server/src/__tests__/preferences-store.test.ts +196 -0
- package/packages/server/src/__tests__/reattach-placement.test.ts +9 -0
- package/packages/server/src/__tests__/recommended-routes.test.ts +2 -2
- package/packages/server/src/__tests__/recovery-server.test.ts +203 -0
- package/packages/server/src/__tests__/session-action-handler-clear-queue.test.ts +153 -0
- package/packages/server/src/__tests__/session-action-handler-headless-reload.test.ts +43 -0
- package/packages/server/src/__tests__/session-order-manager.test.ts +9 -0
- package/packages/server/src/__tests__/session-order-reboot.test.ts +9 -0
- package/packages/server/src/__tests__/session-ordering-integration.test.ts +9 -0
- package/packages/server/src/browser-gateway.ts +83 -5
- package/packages/server/src/browser-handlers/directory-handler.ts +69 -0
- package/packages/server/src/browser-handlers/session-action-handler.ts +89 -0
- package/packages/server/src/browser-handlers/subscription-handler.ts +23 -0
- package/packages/server/src/changelog-parser.ts +1 -1
- package/packages/server/src/cli.ts +68 -250
- package/packages/server/src/event-status-extraction.ts +14 -62
- package/packages/server/src/event-wiring.ts +23 -10
- package/packages/server/src/memory-session-manager.ts +4 -0
- package/packages/server/src/pi-core-checker.ts +1 -1
- package/packages/server/src/pi-dev-version-check.ts +1 -1
- package/packages/server/src/pi-version-skew.ts +24 -46
- package/packages/server/src/plugin-intent-cache.ts +67 -0
- package/packages/server/src/preferences-store.ts +199 -13
- package/packages/server/src/recovery-server.ts +366 -0
- package/packages/server/src/routes/__tests__/manifest-route.test.ts +138 -0
- package/packages/server/src/routes/doctor-routes.ts +26 -21
- package/packages/server/src/routes/manifest-route.ts +162 -0
- package/packages/server/src/routes/openspec-routes.ts +4 -25
- package/packages/server/src/routes/pi-changelog-routes.ts +5 -24
- package/packages/server/src/routes/pi-core-routes.ts +3 -23
- package/packages/server/src/routes/plugin-activation-routes.ts +193 -0
- package/packages/server/src/routes/recommended-routes.ts +21 -0
- package/packages/server/src/routes/system-routes.ts +73 -11
- package/packages/server/src/server.ts +105 -307
- package/packages/server/src/session-api.ts +5 -63
- package/packages/shared/package.json +1 -1
- package/packages/shared/src/__tests__/binary-lookup-resolveJiti.test.ts +28 -0
- package/packages/shared/src/__tests__/binary-lookup-spawn-env.test.ts +61 -0
- package/packages/shared/src/__tests__/binary-lookup.test.ts +16 -0
- package/packages/shared/src/__tests__/bridge-register.test.ts +67 -0
- package/packages/shared/src/__tests__/ci-electron-no-side-effects.test.ts +129 -0
- package/packages/shared/src/__tests__/config.test.ts +40 -0
- package/packages/shared/src/__tests__/dashboard-paths.test.ts +81 -0
- package/packages/shared/src/__tests__/ensure-windows-path.test.ts +112 -0
- package/packages/shared/src/__tests__/intent-types.test.ts +120 -0
- package/packages/shared/src/__tests__/jiti-packages-parity.test.ts +85 -0
- package/packages/shared/src/__tests__/legacy-managed-dir.test.ts +59 -0
- package/packages/shared/src/__tests__/no-direct-child-process.test.ts +12 -0
- package/packages/shared/src/__tests__/no-electron-execpath-spawn.test.ts +149 -0
- package/packages/shared/src/__tests__/no-flow-command-route-claims.test.ts +71 -0
- package/packages/shared/src/__tests__/no-flow-references-in-shell.test.ts +221 -0
- package/packages/shared/src/__tests__/no-managed-dir-reference.test.ts +134 -0
- package/packages/shared/src/__tests__/no-pi-dashboard-version-jiti-gate.test.ts +41 -0
- package/packages/shared/src/__tests__/no-primitive-direct-import.test.ts +235 -0
- package/packages/shared/src/__tests__/no-server-imports-in-resolver.test.ts +53 -0
- package/packages/shared/src/__tests__/node-spawn-jiti-contract.test.ts +54 -101
- package/packages/shared/src/__tests__/node-spawn.test.ts +29 -13
- package/packages/shared/src/__tests__/pi-package-resolver.test.ts +300 -0
- package/packages/shared/src/__tests__/plugin-activation-contracts.test.ts +74 -0
- package/packages/shared/src/__tests__/plugin-bridge-classify-source.test.ts +73 -0
- package/packages/shared/src/__tests__/plugin-bridge-register-extended.test.ts +17 -5
- package/packages/shared/src/__tests__/plugin-bridge-register-packages.test.ts +233 -0
- package/packages/shared/src/__tests__/plugin-bridge-register.test.ts +19 -9
- package/packages/shared/src/__tests__/publish-workflow-contract.test.ts +154 -15
- package/packages/shared/src/__tests__/recommended-extensions.test.ts +28 -10
- package/packages/shared/src/__tests__/resolver-parity-with-scanner.test.ts +76 -0
- package/packages/shared/src/__tests__/server-identity.test.ts +127 -0
- package/packages/shared/src/__tests__/server-launcher.test.ts +35 -0
- package/packages/shared/src/__tests__/source-matching.test.ts +5 -5
- package/packages/shared/src/__tests__/sync-versions-spec.test.ts +76 -0
- package/packages/shared/src/__tests__/tool-registry-definitions.test.ts +50 -2
- package/packages/shared/src/bridge-register.ts +35 -2
- package/packages/shared/src/browser-protocol.ts +176 -2
- package/packages/shared/src/config.ts +12 -0
- package/packages/shared/src/dashboard-paths.ts +69 -0
- package/packages/shared/src/dashboard-plugin/index.ts +2 -0
- package/packages/shared/src/dashboard-plugin/intent-types.ts +93 -0
- package/packages/shared/src/dashboard-plugin/manifest-types.ts +55 -1
- package/packages/shared/src/dashboard-plugin/plugin-status.ts +82 -0
- package/packages/shared/src/dashboard-plugin/slot-props.ts +11 -0
- package/packages/shared/src/dashboard-plugin/slot-types.ts +16 -2
- package/packages/shared/src/dashboard-plugin/ui-primitives.ts +287 -0
- package/packages/shared/src/dashboard-starter.ts +22 -0
- package/packages/shared/src/doctor-core.ts +49 -27
- package/packages/shared/src/launch-source-types.ts +9 -9
- package/packages/shared/src/legacy-managed-dir.ts +97 -0
- package/packages/shared/src/mdns-discovery.ts +4 -1
- package/packages/shared/src/pi-package-resolver.ts +388 -0
- package/packages/shared/src/platform/binary-lookup.ts +27 -3
- package/packages/shared/src/platform/ensure-windows-path.ts +95 -0
- package/packages/shared/src/platform/exec.ts +22 -0
- package/packages/shared/src/platform/node-spawn.ts +42 -41
- package/packages/shared/src/plugin-bridge-register.ts +275 -18
- package/packages/shared/src/protocol.ts +94 -2
- package/packages/shared/src/recommended-extensions.ts +34 -10
- package/packages/shared/src/server-identity.ts +74 -5
- package/packages/shared/src/server-launcher.ts +20 -0
- package/packages/shared/src/source-matching.ts +1 -1
- package/packages/shared/src/tool-registry/__tests__/node-script-toargv-fallback.test.ts +84 -0
- package/packages/shared/src/tool-registry/definitions.ts +91 -7
- package/packages/shared/src/types.ts +12 -8
- package/scripts/maybe-patch-package.cjs +44 -0
- package/packages/server/src/__tests__/bootstrap-install-from-list.test.ts +0 -263
- package/packages/server/src/__tests__/bootstrap-queue.test.ts +0 -120
- package/packages/server/src/__tests__/bootstrap-routes.test.ts +0 -125
- package/packages/server/src/__tests__/bootstrap-state.test.ts +0 -119
- package/packages/server/src/__tests__/cli-bootstrap.test.ts +0 -36
- package/packages/server/src/__tests__/event-status-extraction-flow.test.ts +0 -55
- package/packages/server/src/__tests__/legacy-pi-cleanup.test.ts +0 -149
- package/packages/server/src/__tests__/post-install-openspec-refresh.test.ts +0 -180
- package/packages/server/src/__tests__/post-install-rescan.test.ts +0 -134
- package/packages/server/src/__tests__/system-routes-reextract.test.ts +0 -91
- package/packages/server/src/bootstrap-install-from-list.ts +0 -232
- package/packages/server/src/bootstrap-queue.ts +0 -130
- package/packages/server/src/bootstrap-state.ts +0 -159
- package/packages/server/src/legacy-pi-cleanup.ts +0 -151
- package/packages/server/src/routes/bootstrap-routes.ts +0 -125
- package/packages/shared/src/__tests__/bootstrap/README.md +0 -133
- package/packages/shared/src/__tests__/bootstrap/__snapshots__/cube.test.ts.snap +0 -378
- package/packages/shared/src/__tests__/bootstrap/assertions.ts +0 -136
- package/packages/shared/src/__tests__/bootstrap/cube.test.ts +0 -47
- package/packages/shared/src/__tests__/bootstrap/cube.ts +0 -66
- package/packages/shared/src/__tests__/bootstrap/families/__snapshots__/a-electron.test.ts.snap +0 -84
- package/packages/shared/src/__tests__/bootstrap/families/__snapshots__/b-npm-global.test.ts.snap +0 -90
- package/packages/shared/src/__tests__/bootstrap/families/__snapshots__/c-dev-monorepo.test.ts.snap +0 -34
- package/packages/shared/src/__tests__/bootstrap/families/__snapshots__/d-overrides.test.ts.snap +0 -20
- package/packages/shared/src/__tests__/bootstrap/families/__snapshots__/e-stale-partial.test.ts.snap +0 -62
- package/packages/shared/src/__tests__/bootstrap/families/__snapshots__/f-cwd-variants.test.ts.snap +0 -34
- package/packages/shared/src/__tests__/bootstrap/families/__snapshots__/g-windows-specifics.test.ts.snap +0 -49
- package/packages/shared/src/__tests__/bootstrap/families/__snapshots__/j-path-gui-minimal.test.ts.snap +0 -12
- package/packages/shared/src/__tests__/bootstrap/families/a-electron.test.ts +0 -156
- package/packages/shared/src/__tests__/bootstrap/families/b-npm-global.test.ts +0 -157
- package/packages/shared/src/__tests__/bootstrap/families/c-dev-monorepo.test.ts +0 -102
- package/packages/shared/src/__tests__/bootstrap/families/d-overrides.test.ts +0 -76
- package/packages/shared/src/__tests__/bootstrap/families/e-stale-partial.test.ts +0 -94
- package/packages/shared/src/__tests__/bootstrap/families/f-cwd-variants.test.ts +0 -87
- package/packages/shared/src/__tests__/bootstrap/families/g-windows-specifics.test.ts +0 -143
- package/packages/shared/src/__tests__/bootstrap/families/h-home-drift.test.ts +0 -64
- package/packages/shared/src/__tests__/bootstrap/families/i-malformed-settings.test.ts +0 -77
- package/packages/shared/src/__tests__/bootstrap/families/index.ts +0 -19
- package/packages/shared/src/__tests__/bootstrap/families/j-path-gui-minimal.test.ts +0 -61
- package/packages/shared/src/__tests__/bootstrap/families/k-dashboard-absent.test.ts +0 -50
- package/packages/shared/src/__tests__/bootstrap/families/l-instance-coordination.test.ts +0 -272
- package/packages/shared/src/__tests__/bootstrap/fixtures/dev-monorepo.ts +0 -58
- package/packages/shared/src/__tests__/bootstrap/fixtures/electron-layout.ts +0 -84
- package/packages/shared/src/__tests__/bootstrap/fixtures/index.ts +0 -9
- package/packages/shared/src/__tests__/bootstrap/fixtures/managed-install.ts +0 -85
- package/packages/shared/src/__tests__/bootstrap/fixtures/npm-global-layout.ts +0 -122
- package/packages/shared/src/__tests__/bootstrap/fixtures/pi-versions.ts +0 -36
- package/packages/shared/src/__tests__/bootstrap/fixtures/settings-json.ts +0 -39
- package/packages/shared/src/__tests__/bootstrap/harness.smoke.test.ts +0 -220
- package/packages/shared/src/__tests__/bootstrap/harness.ts +0 -413
- package/packages/shared/src/__tests__/bootstrap/scenarios-skipped.ts +0 -125
- package/packages/shared/src/__tests__/bootstrap/scenarios.ts +0 -132
- package/packages/shared/src/__tests__/bootstrap-install-resolve-npm.test.ts +0 -72
- package/packages/shared/src/__tests__/install-managed-node-bootstrap-order.test.ts +0 -68
- package/packages/shared/src/__tests__/install-managed-node.test.ts +0 -192
- package/packages/shared/src/__tests__/installable-list.test.ts +0 -130
- package/packages/shared/src/__tests__/no-installable-list-in-bridge.test.ts +0 -52
- package/packages/shared/src/bootstrap-install.ts +0 -406
- package/packages/shared/src/installable-list.ts +0 -152
- package/packages/shared/src/launch-source-flag.ts +0 -14
|
@@ -1,125 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Bulk-skip manifest for cells NOT yet covered by a family-test file.
|
|
3
|
-
*
|
|
4
|
-
* This is the "fail-closed by default" scaffold. Every cell in the
|
|
5
|
-
* cube starts SKIPPED with a documented reason. As family-test files
|
|
6
|
-
* are added, they call `register(cell, tag)` and the corresponding
|
|
7
|
-
* skip entry is automatically overridden (because registration takes
|
|
8
|
-
* precedence in the sweep).
|
|
9
|
-
*
|
|
10
|
-
* New cells added to the enumeration (by extending PLATFORMS,
|
|
11
|
-
* DASH_LOCATIONS, etc. in `scenarios.ts`) will fail the cube-sweep
|
|
12
|
-
* test until a decision is made here or in a family file.
|
|
13
|
-
*
|
|
14
|
-
* Call ordering invariant: this module MUST be imported before
|
|
15
|
-
* `sweepCube()` runs. Family-test files can import-and-register after
|
|
16
|
-
* this module runs — registration wins.
|
|
17
|
-
*/
|
|
18
|
-
import { enumerateCube, skip, type ScenarioCell } from "./scenarios.js";
|
|
19
|
-
|
|
20
|
-
/**
|
|
21
|
-
* Classify why a given cell is not interesting / not reachable in
|
|
22
|
-
* practice. Returns null when the cell is plausible and should be
|
|
23
|
-
* covered by a family file — such cells get a generic placeholder
|
|
24
|
-
* skip reason here so the cube sweep passes on day 1; families
|
|
25
|
-
* replace the skip with registration as they land.
|
|
26
|
-
*/
|
|
27
|
-
function skipReasonFor(cell: ScenarioCell): string {
|
|
28
|
-
// ── Combinations that are not real install layouts ────────────────
|
|
29
|
-
|
|
30
|
-
// AppImage tmp mount is Linux-only, and only occurs when dash ===
|
|
31
|
-
// "electron" via an AppImage package.
|
|
32
|
-
if (cell.pi === "appimage-tmp" && (cell.platform !== "linux" || cell.dash !== "electron")) {
|
|
33
|
-
return "appimage-tmp is Linux + electron only";
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
// "dev" dash implies a workspace checkout — only meaningful on
|
|
37
|
-
// developer machines (typically mac/linux). Windows dev happens but
|
|
38
|
-
// is rare; capture later if needed.
|
|
39
|
-
if (cell.dash === "dev" && cell.platform === "win32") {
|
|
40
|
-
return "dev monorepo on Windows — rare; capture if reported";
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
// "home-drift" env only meaningful on Windows (Git Bash sets $HOME
|
|
44
|
-
// differently from os.homedir). On posix, $HOME and os.homedir
|
|
45
|
-
// agree.
|
|
46
|
-
if (cell.env === "home-drift" && cell.platform !== "win32") {
|
|
47
|
-
return "home-drift is a Windows-specific phenomenon";
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
// Malformed settings.json doesn't depend on pi state — the parse
|
|
51
|
-
// failure happens regardless. One test per platform is enough.
|
|
52
|
-
if (cell.settings === "malformed" && cell.pi !== "present-valid") {
|
|
53
|
-
return "malformed settings: one pi state per platform is sufficient";
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
// Appimage + anything other than settings=missing is not a real
|
|
57
|
-
// first-run scenario (appimage is installed fresh).
|
|
58
|
-
if (cell.pi === "appimage-tmp" && cell.settings !== "missing") {
|
|
59
|
-
return "appimage fresh-run implies missing settings.json";
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
// spaces-unicode env is orthogonal to most axes — one scenario per
|
|
63
|
-
// platform proves the invariant. Skip most combinations.
|
|
64
|
-
if (cell.env === "spaces-unicode" && cell.pi !== "present-valid") {
|
|
65
|
-
return "spaces-unicode: covered once per platform via pi=present-valid";
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
// ── Refined skip reasons (post families B-K) ──────────────────────
|
|
69
|
-
|
|
70
|
-
// Dashboard-absent is only interesting when pi is present (K1).
|
|
71
|
-
// Other combinations — pi absent without dashboard, etc. — are
|
|
72
|
-
// pathological and not reachable by any real install mechanic.
|
|
73
|
-
if (cell.dash === "absent" && cell.pi !== "present-valid") {
|
|
74
|
-
return "dashboard-absent only meaningful when pi is present (K1)";
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
// Lock-file and instance-coordination cells land with proposal
|
|
78
|
-
// `single-dashboard-per-home`, which introduces a new axis (lock
|
|
79
|
-
// state) not modelled in this cube. The current cells remain
|
|
80
|
-
// skipped until that proposal extends the enumeration.
|
|
81
|
-
|
|
82
|
-
// TODO(unified-bootstrap-install): add three scenarios once this
|
|
83
|
-
// change's task 13 wires the bootstrap through the home-lock:
|
|
84
|
-
// - B1-post: "npm-g-dash-only + bootstrap install ran→ pi resolves
|
|
85
|
-
// via managed + bridge auto-registered". Requires
|
|
86
|
-
// extending the cube with a `bootstrap?: "pre"|"post"`
|
|
87
|
-
// axis or a new pi state `managed-installed-fresh`.
|
|
88
|
-
// - B4: "pi-dashboard installed via npm-g, extension resolves
|
|
89
|
-
// via node_modules lookup (findBundledExtension
|
|
90
|
-
// Strategy 2)". Requires a new `pi-ext-resolution`
|
|
91
|
-
// axis.
|
|
92
|
-
// - L2: "two simultaneous bootstraps" (requires lock from
|
|
93
|
-
// single-dashboard-per-home to be wired).
|
|
94
|
-
// Reason skipped today: requires lock from single-dashboard-per-home
|
|
95
|
-
// to be wired AND the cube to grow new axes. Track in harness
|
|
96
|
-
// follow-up. See change: unified-bootstrap-install tasks 2.3, 10.2,
|
|
97
|
-
// 11.2, 13.2.
|
|
98
|
-
|
|
99
|
-
// pi=malformed means partial install; resolution failure reason
|
|
100
|
-
// is identical across settings/env axes once pi state is fixed.
|
|
101
|
-
// E2 covers linux + win32; other combinations add no signal.
|
|
102
|
-
if (
|
|
103
|
-
cell.pi === "malformed"
|
|
104
|
-
&& cell.settings !== "empty"
|
|
105
|
-
&& !(cell.platform === "linux" || cell.platform === "win32")
|
|
106
|
-
) {
|
|
107
|
-
return "malformed pi: E2 covers linux+win32 — other settings/env add no signal";
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
// Remaining cells are plausible combinations with no dedicated
|
|
111
|
-
// family test. Documented as deliberate skips rather than gaps.
|
|
112
|
-
return "not yet covered — add family coverage when a bug reports here";
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
/**
|
|
116
|
-
* Apply bulk skips. Called at module load.
|
|
117
|
-
*/
|
|
118
|
-
export function applyBulkSkips(): void {
|
|
119
|
-
for (const cell of enumerateCube()) {
|
|
120
|
-
skip(cell, skipReasonFor(cell));
|
|
121
|
-
}
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
// Apply on import so scenario consumers see the full manifest.
|
|
125
|
-
applyBulkSkips();
|
|
@@ -1,132 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Scenario registry — the authoritative record of which cells are
|
|
3
|
-
* tested, which are explicitly skipped (with a reason), and which
|
|
4
|
-
* are unknown (fail-closed on CI).
|
|
5
|
-
*
|
|
6
|
-
* See openspec/changes/bootstrap-resolution-harness/design.md §5.
|
|
7
|
-
*/
|
|
8
|
-
|
|
9
|
-
/** The canonical axes for the scenario cube. */
|
|
10
|
-
export const PLATFORMS = ["win32", "darwin", "linux"] as const;
|
|
11
|
-
export const DASH_LOCATIONS = ["electron", "npm-g", "dev", "managed", "absent"] as const;
|
|
12
|
-
export const PI_STATES = [
|
|
13
|
-
"absent",
|
|
14
|
-
"present-no-ext",
|
|
15
|
-
"present-stale-ext",
|
|
16
|
-
"present-valid",
|
|
17
|
-
"malformed",
|
|
18
|
-
"appimage-tmp",
|
|
19
|
-
] as const;
|
|
20
|
-
export const SETTINGS_STATES = ["missing", "empty", "valid", "malformed"] as const;
|
|
21
|
-
export const ENV_STATES = ["normal", "spaces-unicode", "home-drift"] as const;
|
|
22
|
-
|
|
23
|
-
export type Platform = (typeof PLATFORMS)[number];
|
|
24
|
-
export type DashLocation = (typeof DASH_LOCATIONS)[number];
|
|
25
|
-
export type PiState = (typeof PI_STATES)[number];
|
|
26
|
-
export type SettingsState = (typeof SETTINGS_STATES)[number];
|
|
27
|
-
export type EnvState = (typeof ENV_STATES)[number];
|
|
28
|
-
|
|
29
|
-
export interface ScenarioCell {
|
|
30
|
-
platform: Platform;
|
|
31
|
-
dash: DashLocation;
|
|
32
|
-
pi: PiState;
|
|
33
|
-
settings: SettingsState;
|
|
34
|
-
env: EnvState;
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
export function cellKey(cell: ScenarioCell): string {
|
|
38
|
-
return [cell.platform, cell.dash, cell.pi, cell.settings, cell.env].join("/");
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
/** Parse a cell-key back into its components. */
|
|
42
|
-
export function parseCellKey(key: string): ScenarioCell | null {
|
|
43
|
-
const parts = key.split("/");
|
|
44
|
-
if (parts.length !== 5) return null;
|
|
45
|
-
const [platform, dash, pi, settings, env] = parts;
|
|
46
|
-
if (!PLATFORMS.includes(platform as Platform)) return null;
|
|
47
|
-
if (!DASH_LOCATIONS.includes(dash as DashLocation)) return null;
|
|
48
|
-
if (!PI_STATES.includes(pi as PiState)) return null;
|
|
49
|
-
if (!SETTINGS_STATES.includes(settings as SettingsState)) return null;
|
|
50
|
-
if (!ENV_STATES.includes(env as EnvState)) return null;
|
|
51
|
-
return {
|
|
52
|
-
platform: platform as Platform,
|
|
53
|
-
dash: dash as DashLocation,
|
|
54
|
-
pi: pi as PiState,
|
|
55
|
-
settings: settings as SettingsState,
|
|
56
|
-
env: env as EnvState,
|
|
57
|
-
};
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
/**
|
|
61
|
-
* Cells with an active test. Entries added by each family-test file
|
|
62
|
-
* via `register(cell, describeSymbol)`.
|
|
63
|
-
*
|
|
64
|
-
* The value is a string tag identifying the test file — useful for
|
|
65
|
-
* the cube-sweep error message ("cell X already covered by Y").
|
|
66
|
-
*/
|
|
67
|
-
export const REGISTERED_SCENARIOS = new Map<string, string>();
|
|
68
|
-
|
|
69
|
-
/**
|
|
70
|
-
* Cells explicitly skipped with a documented reason. New cells added
|
|
71
|
-
* to the enumeration that don't land in REGISTERED or SKIPPED will
|
|
72
|
-
* fail CI (the fail-closed invariant).
|
|
73
|
-
*
|
|
74
|
-
* Format: key → human-readable reason.
|
|
75
|
-
*/
|
|
76
|
-
export const SKIPPED_SCENARIOS = new Map<string, string>();
|
|
77
|
-
|
|
78
|
-
/** Register a tested cell. Last-registered wins (tests may re-register during dev). */
|
|
79
|
-
export function register(cell: ScenarioCell, tag: string): void {
|
|
80
|
-
REGISTERED_SCENARIOS.set(cellKey(cell), tag);
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
/** Skip a cell with a reason. Required before CI accepts the new cell. */
|
|
84
|
-
export function skip(cell: ScenarioCell, reason: string): void {
|
|
85
|
-
if (!reason || reason.trim().length === 0) {
|
|
86
|
-
throw new Error(`SKIPPED_SCENARIOS entry for ${cellKey(cell)} requires a non-empty reason`);
|
|
87
|
-
}
|
|
88
|
-
SKIPPED_SCENARIOS.set(cellKey(cell), reason);
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
/** Mass-skip helper: all cells matching a partial pattern. */
|
|
92
|
-
export function skipPattern(
|
|
93
|
-
partial: Partial<ScenarioCell>,
|
|
94
|
-
reason: string,
|
|
95
|
-
): void {
|
|
96
|
-
for (const cell of enumerateCube()) {
|
|
97
|
-
let match = true;
|
|
98
|
-
for (const k of Object.keys(partial) as (keyof ScenarioCell)[]) {
|
|
99
|
-
if (cell[k] !== partial[k]) {
|
|
100
|
-
match = false;
|
|
101
|
-
break;
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
if (match) skip(cell, reason);
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
/**
|
|
109
|
-
* Enumerate every combination. Order is stable (useful for snapshots
|
|
110
|
-
* and for deterministic error messages).
|
|
111
|
-
*/
|
|
112
|
-
export function enumerateCube(): ScenarioCell[] {
|
|
113
|
-
const out: ScenarioCell[] = [];
|
|
114
|
-
for (const platform of PLATFORMS) {
|
|
115
|
-
for (const dash of DASH_LOCATIONS) {
|
|
116
|
-
for (const pi of PI_STATES) {
|
|
117
|
-
for (const settings of SETTINGS_STATES) {
|
|
118
|
-
for (const env of ENV_STATES) {
|
|
119
|
-
out.push({ platform, dash, pi, settings, env });
|
|
120
|
-
}
|
|
121
|
-
}
|
|
122
|
-
}
|
|
123
|
-
}
|
|
124
|
-
}
|
|
125
|
-
return out;
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
/** Reset state — used only by tests that want to verify the cube logic itself. */
|
|
129
|
-
export function __resetScenariosForTesting(): void {
|
|
130
|
-
REGISTERED_SCENARIOS.clear();
|
|
131
|
-
SKIPPED_SCENARIOS.clear();
|
|
132
|
-
}
|
|
@@ -1,72 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Regression: `resolveNpmArgv` from `bootstrap-install.ts` MUST consult
|
|
3
|
-
* the injected `ToolRegistry` for `npm` before falling back to the
|
|
4
|
-
* platform `npm` / `npm.cmd` PATH binary.
|
|
5
|
-
*
|
|
6
|
-
* The change `embed-managed-node-runtime` adds a managedRuntime
|
|
7
|
-
* strategy to the npm chain in the registry. If a future refactor
|
|
8
|
-
* accidentally bypasses the registry inside `resolveNpmArgv`, the
|
|
9
|
-
* managed Node runtime would stop being preferred for shared bootstrap
|
|
10
|
-
* spawns \u2014 the user-visible regression class this whole change exists
|
|
11
|
-
* to prevent.
|
|
12
|
-
*
|
|
13
|
-
* See change: embed-managed-node-runtime (task 4.2).
|
|
14
|
-
*/
|
|
15
|
-
import { describe, expect, it } from "vitest";
|
|
16
|
-
import { resolveNpmArgv } from "../bootstrap-install.js";
|
|
17
|
-
import type {
|
|
18
|
-
Resolution,
|
|
19
|
-
ToolRegistry,
|
|
20
|
-
} from "../tool-registry/index.js";
|
|
21
|
-
|
|
22
|
-
function fakeRegistry(opts: {
|
|
23
|
-
hasNpm?: boolean;
|
|
24
|
-
resolveResult?: Partial<Resolution>;
|
|
25
|
-
}): ToolRegistry {
|
|
26
|
-
// Only the methods `resolveNpmArgv` actually calls.
|
|
27
|
-
return {
|
|
28
|
-
has: (name: string) => name === "npm" && (opts.hasNpm ?? true),
|
|
29
|
-
resolve: () =>
|
|
30
|
-
({
|
|
31
|
-
name: "npm",
|
|
32
|
-
ok: true,
|
|
33
|
-
path: "/managed/node/bin/npm",
|
|
34
|
-
source: "managed",
|
|
35
|
-
tried: [],
|
|
36
|
-
resolvedAt: Date.now(),
|
|
37
|
-
...opts.resolveResult,
|
|
38
|
-
}) as Resolution,
|
|
39
|
-
} as unknown as ToolRegistry;
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
describe("resolveNpmArgv", () => {
|
|
43
|
-
it("explicit npmArgv wins over registry", () => {
|
|
44
|
-
const argv = resolveNpmArgv({
|
|
45
|
-
npmArgv: ["/explicit/node", "/explicit/npm-cli.js"],
|
|
46
|
-
registry: fakeRegistry({}),
|
|
47
|
-
});
|
|
48
|
-
expect(argv).toEqual(["/explicit/node", "/explicit/npm-cli.js"]);
|
|
49
|
-
});
|
|
50
|
-
|
|
51
|
-
it("uses ToolRegistry.resolve('npm') when no explicit argv", () => {
|
|
52
|
-
const argv = resolveNpmArgv({
|
|
53
|
-
registry: fakeRegistry({
|
|
54
|
-
resolveResult: { ok: true, path: "/managed/node/bin/npm" } as Partial<Resolution>,
|
|
55
|
-
}),
|
|
56
|
-
});
|
|
57
|
-
expect(argv).toEqual(["/managed/node/bin/npm"]);
|
|
58
|
-
});
|
|
59
|
-
|
|
60
|
-
it("falls back to npm/npm.cmd on PATH when registry has no entry", () => {
|
|
61
|
-
const argv = resolveNpmArgv({
|
|
62
|
-
registry: {
|
|
63
|
-
has: () => false,
|
|
64
|
-
resolve: () => {
|
|
65
|
-
throw new Error("should not be called");
|
|
66
|
-
},
|
|
67
|
-
} as unknown as ToolRegistry,
|
|
68
|
-
});
|
|
69
|
-
expect(argv).toHaveLength(1);
|
|
70
|
-
expect(argv[0]).toMatch(/^npm(\.cmd)?$/);
|
|
71
|
-
});
|
|
72
|
-
});
|
|
@@ -1,68 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Pin the call order in `installStandalone` (the Electron entry that
|
|
3
|
-
* the spec calls `installAllTools`):
|
|
4
|
-
*
|
|
5
|
-
* installManagedNode(...) < any sharedBootstrapInstall(...)
|
|
6
|
-
*
|
|
7
|
-
* We can't easily run installStandalone end-to-end here (it's Electron
|
|
8
|
-
* code, requires a packaged resources path, spawns real npm). So this
|
|
9
|
-
* is a script-text test that greps the dependency-installer source for
|
|
10
|
-
* the call sites and asserts their byte offsets are in the right order.
|
|
11
|
-
* If a refactor moves installManagedNode after the first npm install,
|
|
12
|
-
* the regression \u2014 a fresh Windows install having no managed Node when
|
|
13
|
-
* the very first install runs \u2014 cannot land silently.
|
|
14
|
-
*
|
|
15
|
-
* See change: embed-managed-node-runtime (task 4.3).
|
|
16
|
-
*/
|
|
17
|
-
import fs from "node:fs";
|
|
18
|
-
import path from "node:path";
|
|
19
|
-
import url from "node:url";
|
|
20
|
-
import { describe, expect, it } from "vitest";
|
|
21
|
-
|
|
22
|
-
const __dirname = path.dirname(url.fileURLToPath(import.meta.url));
|
|
23
|
-
// packages/shared/src/__tests__/ \u2192 ../../../electron/src/lib/dependency-installer.ts
|
|
24
|
-
const SOURCE_PATH = path.resolve(
|
|
25
|
-
__dirname,
|
|
26
|
-
"..",
|
|
27
|
-
"..",
|
|
28
|
-
"..",
|
|
29
|
-
"electron",
|
|
30
|
-
"src",
|
|
31
|
-
"lib",
|
|
32
|
-
"dependency-installer.ts",
|
|
33
|
-
);
|
|
34
|
-
|
|
35
|
-
describe("installStandalone call order", () => {
|
|
36
|
-
it("installManagedNode runs before sharedBootstrapInstall", () => {
|
|
37
|
-
const text = fs.readFileSync(SOURCE_PATH, "utf-8");
|
|
38
|
-
|
|
39
|
-
// Locate the installStandalone function body.
|
|
40
|
-
const fnIdx = text.indexOf("export async function installStandalone");
|
|
41
|
-
expect(fnIdx).toBeGreaterThan(-1);
|
|
42
|
-
|
|
43
|
-
// Scope the search to the function body — find its closing brace.
|
|
44
|
-
// Cheap bracket counter: starts after the first `{` after fnIdx.
|
|
45
|
-
const bodyStart = text.indexOf("{", fnIdx);
|
|
46
|
-
let depth = 0;
|
|
47
|
-
let bodyEnd = bodyStart;
|
|
48
|
-
for (let i = bodyStart; i < text.length; i++) {
|
|
49
|
-
const ch = text[i];
|
|
50
|
-
if (ch === "{") depth++;
|
|
51
|
-
else if (ch === "}") {
|
|
52
|
-
depth--;
|
|
53
|
-
if (depth === 0) {
|
|
54
|
-
bodyEnd = i;
|
|
55
|
-
break;
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
const body = text.slice(bodyStart, bodyEnd);
|
|
60
|
-
|
|
61
|
-
const managedIdx = body.indexOf("installManagedNode(");
|
|
62
|
-
const sharedIdx = body.indexOf("sharedBootstrapInstall(");
|
|
63
|
-
|
|
64
|
-
expect(managedIdx).toBeGreaterThan(-1);
|
|
65
|
-
expect(sharedIdx).toBeGreaterThan(-1);
|
|
66
|
-
expect(managedIdx).toBeLessThan(sharedIdx);
|
|
67
|
-
});
|
|
68
|
-
});
|
|
@@ -1,192 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Unit tests for `installManagedNode` from `bootstrap-install.ts`.
|
|
3
|
-
*
|
|
4
|
-
* Uses a real tmp HOME (per the global setup-home tripwire) and a
|
|
5
|
-
* real-on-disk fake bundled-node directory. The bundled-Node version
|
|
6
|
-
* is read via the `_readVersion` test seam so we never spawn `node`
|
|
7
|
-
* here.
|
|
8
|
-
*
|
|
9
|
-
* See change: embed-managed-node-runtime (task 2.4).
|
|
10
|
-
*/
|
|
11
|
-
import fs from "node:fs";
|
|
12
|
-
import os from "node:os";
|
|
13
|
-
import path from "node:path";
|
|
14
|
-
import { afterEach, beforeEach, describe, expect, it } from "vitest";
|
|
15
|
-
import { installManagedNode } from "../bootstrap-install.js";
|
|
16
|
-
|
|
17
|
-
const isWin = process.platform === "win32";
|
|
18
|
-
|
|
19
|
-
function makeFakeBundle(dir: string, opts?: { partial?: boolean }): void {
|
|
20
|
-
fs.mkdirSync(dir, { recursive: true });
|
|
21
|
-
if (isWin) {
|
|
22
|
-
fs.writeFileSync(path.join(dir, "node.exe"), "fake-node-binary");
|
|
23
|
-
if (!opts?.partial) {
|
|
24
|
-
fs.writeFileSync(path.join(dir, "npm.cmd"), "@echo off\nnpm");
|
|
25
|
-
fs.writeFileSync(path.join(dir, "npx.cmd"), "@echo off\nnpx");
|
|
26
|
-
}
|
|
27
|
-
fs.mkdirSync(path.join(dir, "node_modules", "npm", "bin"), {
|
|
28
|
-
recursive: true,
|
|
29
|
-
});
|
|
30
|
-
fs.writeFileSync(
|
|
31
|
-
path.join(dir, "node_modules", "npm", "bin", "npm-cli.js"),
|
|
32
|
-
"// npm-cli",
|
|
33
|
-
);
|
|
34
|
-
} else {
|
|
35
|
-
fs.mkdirSync(path.join(dir, "bin"), { recursive: true });
|
|
36
|
-
fs.writeFileSync(path.join(dir, "bin", "node"), "fake-node-binary");
|
|
37
|
-
fs.writeFileSync(path.join(dir, "bin", "npm"), "#!/bin/sh\nnpm");
|
|
38
|
-
fs.writeFileSync(path.join(dir, "bin", "npx"), "#!/bin/sh\nnpx");
|
|
39
|
-
fs.mkdirSync(path.join(dir, "lib", "node_modules", "npm", "bin"), {
|
|
40
|
-
recursive: true,
|
|
41
|
-
});
|
|
42
|
-
fs.writeFileSync(
|
|
43
|
-
path.join(dir, "lib", "node_modules", "npm", "bin", "npm-cli.js"),
|
|
44
|
-
"// npm-cli",
|
|
45
|
-
);
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
describe("installManagedNode", () => {
|
|
50
|
-
let tmpRoot: string;
|
|
51
|
-
let bundledDir: string;
|
|
52
|
-
let managedDir: string;
|
|
53
|
-
|
|
54
|
-
beforeEach(() => {
|
|
55
|
-
tmpRoot = fs.mkdtempSync(path.join(os.tmpdir(), "managed-node-test-"));
|
|
56
|
-
bundledDir = path.join(tmpRoot, "bundled");
|
|
57
|
-
managedDir = path.join(tmpRoot, "managed");
|
|
58
|
-
makeFakeBundle(bundledDir);
|
|
59
|
-
});
|
|
60
|
-
|
|
61
|
-
afterEach(() => {
|
|
62
|
-
fs.rmSync(tmpRoot, { recursive: true, force: true });
|
|
63
|
-
});
|
|
64
|
-
|
|
65
|
-
it("first run: copies tree and writes .version marker", async () => {
|
|
66
|
-
const r = await installManagedNode({
|
|
67
|
-
bundledNodeDir: bundledDir,
|
|
68
|
-
managedDir,
|
|
69
|
-
_readVersion: () => "v22.12.0",
|
|
70
|
-
});
|
|
71
|
-
expect(r.ok).toBe(true);
|
|
72
|
-
expect(r.copied).toBe(true);
|
|
73
|
-
expect(r.version).toBe("v22.12.0");
|
|
74
|
-
|
|
75
|
-
const targetBinary = isWin
|
|
76
|
-
? path.join(managedDir, "node", "node.exe")
|
|
77
|
-
: path.join(managedDir, "node", "bin", "node");
|
|
78
|
-
expect(fs.existsSync(targetBinary)).toBe(true);
|
|
79
|
-
|
|
80
|
-
const marker = fs.readFileSync(
|
|
81
|
-
path.join(managedDir, "node", ".version"),
|
|
82
|
-
"utf-8",
|
|
83
|
-
);
|
|
84
|
-
expect(marker.trim()).toBe("v22.12.0");
|
|
85
|
-
});
|
|
86
|
-
|
|
87
|
-
it("idempotent re-run with matching version is a no-op", async () => {
|
|
88
|
-
await installManagedNode({
|
|
89
|
-
bundledNodeDir: bundledDir,
|
|
90
|
-
managedDir,
|
|
91
|
-
_readVersion: () => "v22.12.0",
|
|
92
|
-
});
|
|
93
|
-
const targetBinary = isWin
|
|
94
|
-
? path.join(managedDir, "node", "node.exe")
|
|
95
|
-
: path.join(managedDir, "node", "bin", "node");
|
|
96
|
-
const mtimeBefore = fs.statSync(targetBinary).mtimeMs;
|
|
97
|
-
|
|
98
|
-
// Wait one tick to ensure mtime would change if we recopied.
|
|
99
|
-
await new Promise((r) => setTimeout(r, 10));
|
|
100
|
-
|
|
101
|
-
const r = await installManagedNode({
|
|
102
|
-
bundledNodeDir: bundledDir,
|
|
103
|
-
managedDir,
|
|
104
|
-
_readVersion: () => "v22.12.0",
|
|
105
|
-
});
|
|
106
|
-
expect(r.ok).toBe(true);
|
|
107
|
-
expect(r.copied).toBe(false);
|
|
108
|
-
expect(r.reason).toMatch(/version matches/);
|
|
109
|
-
const mtimeAfter = fs.statSync(targetBinary).mtimeMs;
|
|
110
|
-
expect(mtimeAfter).toBe(mtimeBefore);
|
|
111
|
-
});
|
|
112
|
-
|
|
113
|
-
it("mismatched version triggers a re-copy", async () => {
|
|
114
|
-
await installManagedNode({
|
|
115
|
-
bundledNodeDir: bundledDir,
|
|
116
|
-
managedDir,
|
|
117
|
-
_readVersion: () => "v22.12.0",
|
|
118
|
-
});
|
|
119
|
-
|
|
120
|
-
// Bundle now reports a newer version.
|
|
121
|
-
const r = await installManagedNode({
|
|
122
|
-
bundledNodeDir: bundledDir,
|
|
123
|
-
managedDir,
|
|
124
|
-
_readVersion: () => "v22.13.0",
|
|
125
|
-
});
|
|
126
|
-
expect(r.ok).toBe(true);
|
|
127
|
-
expect(r.copied).toBe(true);
|
|
128
|
-
expect(r.version).toBe("v22.13.0");
|
|
129
|
-
|
|
130
|
-
const marker = fs.readFileSync(
|
|
131
|
-
path.join(managedDir, "node", ".version"),
|
|
132
|
-
"utf-8",
|
|
133
|
-
);
|
|
134
|
-
expect(marker.trim()).toBe("v22.13.0");
|
|
135
|
-
});
|
|
136
|
-
|
|
137
|
-
it("missing bundled source: no-op without error", async () => {
|
|
138
|
-
const r = await installManagedNode({
|
|
139
|
-
bundledNodeDir: null,
|
|
140
|
-
managedDir,
|
|
141
|
-
});
|
|
142
|
-
expect(r.ok).toBe(true);
|
|
143
|
-
expect(r.copied).toBe(false);
|
|
144
|
-
expect(r.reason).toMatch(/no bundled source/);
|
|
145
|
-
expect(fs.existsSync(path.join(managedDir, "node"))).toBe(false);
|
|
146
|
-
});
|
|
147
|
-
|
|
148
|
-
it("bundled binary missing: no-op (treat as no source)", async () => {
|
|
149
|
-
const emptyBundled = path.join(tmpRoot, "empty");
|
|
150
|
-
fs.mkdirSync(emptyBundled, { recursive: true });
|
|
151
|
-
const r = await installManagedNode({
|
|
152
|
-
bundledNodeDir: emptyBundled,
|
|
153
|
-
managedDir,
|
|
154
|
-
_readVersion: () => null,
|
|
155
|
-
});
|
|
156
|
-
expect(r.ok).toBe(true);
|
|
157
|
-
expect(r.copied).toBe(false);
|
|
158
|
-
expect(r.reason).toMatch(/bundled node binary/);
|
|
159
|
-
});
|
|
160
|
-
|
|
161
|
-
it("dir present but marker missing: re-copies (treats as mismatch)", async () => {
|
|
162
|
-
// Pretend a partial copy left behind a directory with no marker.
|
|
163
|
-
fs.mkdirSync(path.join(managedDir, "node", "leftover"), { recursive: true });
|
|
164
|
-
|
|
165
|
-
const r = await installManagedNode({
|
|
166
|
-
bundledNodeDir: bundledDir,
|
|
167
|
-
managedDir,
|
|
168
|
-
_readVersion: () => "v22.12.0",
|
|
169
|
-
});
|
|
170
|
-
expect(r.ok).toBe(true);
|
|
171
|
-
expect(r.copied).toBe(true);
|
|
172
|
-
// Leftover should be gone after the rm-then-copy.
|
|
173
|
-
expect(
|
|
174
|
-
fs.existsSync(path.join(managedDir, "node", "leftover")),
|
|
175
|
-
).toBe(false);
|
|
176
|
-
expect(
|
|
177
|
-
fs.existsSync(path.join(managedDir, "node", ".version")),
|
|
178
|
-
).toBe(true);
|
|
179
|
-
});
|
|
180
|
-
|
|
181
|
-
it("reports progress through the callback", async () => {
|
|
182
|
-
const events: Array<{ step: string; status: string }> = [];
|
|
183
|
-
await installManagedNode({
|
|
184
|
-
bundledNodeDir: bundledDir,
|
|
185
|
-
managedDir,
|
|
186
|
-
_readVersion: () => "v22.12.0",
|
|
187
|
-
progress: (p) => events.push({ step: p.step, status: p.status }),
|
|
188
|
-
});
|
|
189
|
-
expect(events.some((e) => e.step === "node-runtime" && e.status === "running")).toBe(true);
|
|
190
|
-
expect(events.some((e) => e.step === "node-runtime" && e.status === "done")).toBe(true);
|
|
191
|
-
});
|
|
192
|
-
});
|