* feat(#1680): ADR-1239 Phase C-1 — declarative embedding adapter + minimal HostIntegrationInterface [AC1] Phase 3 slice 1 (AC1). Names + bounds today's projection path behind the common HostIntegrationInterface that both declarative + imperative adapters will satisfy. - src/embedding-adapter.cts: minimal HostIntegrationInterface (kind + runtime + install/uninstall) + ADAPTER_KINDS. The full 6-point binding surface (command/dispatch/model/hooks/state/artifact) is DEFERRED until the imperative adapter (AC2) fixes the shape — ADR-1239 lists the wire-shape as an open question; freezing it now risks rework across Phases 3-6. - src/adapter-declarative.cts: createDeclarativeAdapter({runtime}) factory. Delegates in-process to install-engine installRuntimeArtifacts / uninstallRuntimeArtifacts (the SAME engine functions bin/install.js uses), so output is byte-identical to today's install (gated by golden-install-parity). Module-ref call style = monkeypatch-friendly for tests. Lossy by design: projects files, does not drive the loop (that's the imperative adapter, AC2). - tests/adapter-declarative-equivalence.test.cjs: kind classification (all 16 runtimes), install/uninstall delegation with exact args (the byte-identity link), fail-closed construction (missing/invalid runtime throws). Purely additive — no install.js/install-engine changes. Unblocks AC2 (imperative adapter) + AC3/AC4 (model/hook/state seams) as follow-up slices. * chore(changeset): add Changed fragment for declarative embedding adapter (#1680) * fix(lint): ignore tsc-emitted embedding-adapter/adapter-declarative .cjs (ADR-457) The new src/embedding-adapter.cts + src/adapter-declarative.cts modules' emitted gsd-core/bin/lib/*.cjs artifacts must join the ADR-457 ignores list (lint the src/*.cts source, not the emitted .cjs). Without this, the .cjs is linted under js.recommended where @typescript-eslint/no-require-imports is undefined, so the verbatim-copied eslint-disable directive surfaces as 'Definition for rule not found' — failing the lint-tests CI job. Also restores the clean line-level disable in adapter-declarative.cts (valid in the .cts source context where the rule IS defined). * fix(docs): add adapter-declarative + embedding-adapter to INVENTORY-MANIFEST cli_modules The new ADR-1239 Phase C-1 modules' built .cjs artifacts must be registered in docs/INVENTORY-MANIFEST.json's cli_modules array or the 'docs/INVENTORY-MANIFEST.json matches the filesystem' drift test fails on CI. Mirrors the existing sorted entries.
103 lines
4.7 KiB
JavaScript
103 lines
4.7 KiB
JavaScript
'use strict';
|
|
/**
|
|
* Equivalence test for the declarative embedding adapter (ADR-1239 Phase C-1,
|
|
* AC1 / #1680).
|
|
*
|
|
* The declarative adapter NAMES + BOUNDS today's projection path behind
|
|
* `HostIntegrationInterface`. This test pins the three load-bearing properties:
|
|
*
|
|
* 1. KIND — every runtime's adapter classifies as `kind: 'declarative'` and
|
|
* echoes its runtime id.
|
|
* 2. DELEGATION (the byte-identity link) — `install`/`uninstall` delegate
|
|
* IN-PROCESS to install-engine's `installRuntimeArtifacts` /
|
|
* `uninstallRuntimeArtifacts` with the EXACT args passed through. Because
|
|
* the adapter calls the SAME engine functions `bin/install.js` uses, its
|
|
* output is byte-identical to today's install — the 16-runtime byte
|
|
* identity is gated by `tests/golden-install-parity.test.cjs` (which
|
|
* exercises these engine functions end-to-end). Asserting the delegation
|
|
* link here proves the adapter never diverges from the reference path.
|
|
* 3. FAIL-CLOSED CONSTRUCTION — missing/invalid runtime throws (no silent
|
|
* default adapter).
|
|
*
|
|
* Behavioral tests only; delegation verified via module-ref monkeypatch (the
|
|
* install-engine.cts:31-38 stub-compatible pattern — Node module cache shares
|
|
* the one module object, so patching it here is seen by the adapter).
|
|
*/
|
|
|
|
const { test } = require('node:test');
|
|
const assert = require('node:assert/strict');
|
|
const { createDeclarativeAdapter } = require('../gsd-core/bin/lib/adapter-declarative.cjs');
|
|
const installEngine = require('../gsd-core/bin/lib/install-engine.cjs');
|
|
const registry = require('../gsd-core/bin/lib/capability-registry.cjs');
|
|
|
|
const RUNTIMES = Object.keys(registry.runtimes);
|
|
|
|
test('declarative adapter: kind === "declarative" + runtime echoed, for all 16 runtimes', () => {
|
|
assert.ok(RUNTIMES.length >= 16, `expected ≥16 runtimes in registry, got ${RUNTIMES.length}`);
|
|
for (const r of RUNTIMES) {
|
|
const adapter = createDeclarativeAdapter({ runtime: r });
|
|
assert.strictEqual(adapter.kind, 'declarative', `${r}: kind must be 'declarative'`);
|
|
assert.strictEqual(adapter.runtime, r, `${r}: adapter must echo the runtime id`);
|
|
assert.strictEqual(typeof adapter.install, 'function', `${r}: install must be a function`);
|
|
assert.strictEqual(typeof adapter.uninstall, 'function', `${r}: uninstall must be a function`);
|
|
}
|
|
});
|
|
|
|
test('declarative adapter.install delegates in-process to installRuntimeArtifacts with exact args (byte-identity link)', () => {
|
|
const original = installEngine.installRuntimeArtifacts;
|
|
try {
|
|
for (const r of RUNTIMES) {
|
|
const adapter = createDeclarativeAdapter({ runtime: r });
|
|
let captured = null;
|
|
installEngine.installRuntimeArtifacts = function (...args) { captured = args; return undefined; };
|
|
const resolveAttribution = () => 'attr-' + r;
|
|
const intent = {
|
|
configDir: '/tmp/adapter-eq/' + r,
|
|
scope: 'global',
|
|
resolvedProfile: { profile: r },
|
|
resolveAttribution,
|
|
};
|
|
assert.doesNotThrow(() => adapter.install(intent), `${r}: install threw`);
|
|
assert.ok(captured, `${r}: install did not delegate to installRuntimeArtifacts`);
|
|
assert.deepStrictEqual(
|
|
captured,
|
|
[r, '/tmp/adapter-eq/' + r, 'global', { profile: r }, resolveAttribution],
|
|
`${r}: install delegation args diverged from the engine signature`,
|
|
);
|
|
}
|
|
} finally {
|
|
installEngine.installRuntimeArtifacts = original;
|
|
}
|
|
});
|
|
|
|
test('declarative adapter.uninstall delegates in-process to uninstallRuntimeArtifacts with exact args', () => {
|
|
const original = installEngine.uninstallRuntimeArtifacts;
|
|
try {
|
|
for (const r of RUNTIMES) {
|
|
const adapter = createDeclarativeAdapter({ runtime: r });
|
|
let captured = null;
|
|
installEngine.uninstallRuntimeArtifacts = function (...args) { captured = args; return undefined; };
|
|
assert.doesNotThrow(() => adapter.uninstall({ configDir: '/tmp/adapter-eq/' + r, scope: 'local' }), `${r}: uninstall threw`);
|
|
assert.ok(captured, `${r}: uninstall did not delegate to uninstallRuntimeArtifacts`);
|
|
assert.deepStrictEqual(
|
|
captured,
|
|
[r, '/tmp/adapter-eq/' + r, 'local'],
|
|
`${r}: uninstall delegation args diverged from the engine signature`,
|
|
);
|
|
}
|
|
} finally {
|
|
installEngine.uninstallRuntimeArtifacts = original;
|
|
}
|
|
});
|
|
|
|
test('createDeclarativeAdapter: throws on missing/invalid runtime (fail-closed construction)', () => {
|
|
for (const bad of ['', undefined, null]) {
|
|
assert.throws(
|
|
() => createDeclarativeAdapter({ runtime: bad }),
|
|
TypeError,
|
|
`runtime=${JSON.stringify(bad)} must throw TypeError`,
|
|
);
|
|
}
|
|
assert.throws(() => createDeclarativeAdapter({}), TypeError, 'missing runtime must throw TypeError');
|
|
});
|