Files
msd-core/tests/vscode-extension-reachability.test.cjs
Jakub Zych a9a7a328e6 refactor: hard-fork GSD -> MSD (Make Software Done)
Mechanical rename produced by scripts/msd-rename.cjs: gsd/Gsd/GSD -> msd/Msd/MSD
across contents and paths, upstream package/repo coordinates -> @golem15/msd-core
and golem15com/msd-core. Deep links into upstream history, sibling upstream
packages, the GSD-2 import feature, CHANGELOG.md and .changeset/ are kept as-is.

Hand edits on top: MSD block-letter banner and logos, LICENSE copyright line,
package/plugin identity, regenerated lockfile, install-tree fixtures, derived
registries and benchmark baseline; migration checksum baseline re-locked
(MSD keeps its own install state, so no install had applied the old sums);
sort-order and regex-escaped expectations in tests adjusted.
2026-10-06 01:47:40 +02:00

205 lines
10 KiB
JavaScript

'use strict';
/**
* VS Code extension reachability test — ADR-1239 Phase D / #1942, upgraded #2103.
*
* Proves the VS Code extension is keystone-WIRED: the msd.invoke handler
* dispatches through msd-tools.cjs (subprocess-reuse — the shared
* `dispatchMsdCommand` in msd-core/bin/lib/shell-command-projection.cjs) and
* returns REAL output, not just a registration on a stub. The handler is
* exported separately from activate() so it is testable WITHOUT a VS Code host.
*
* The original cut called `createHub()` with NO args — no hub factory in the
* tree fully populates a hub, so every dispatch silently answered
* UnknownCommand. The prior version of this test only asserted the result was
* "a JSON object" — a VACUOUS assertion that passed whether or not dispatch
* actually worked. Dispatch is now exercised with a real read-only
* family/subcommand (progress/json) against a real temp project, matching the
* sibling tests/pi-extension-reachability.test.cjs pattern — no fake
* dispatcher injected, because the whole point of "reachability" is that the
* real engine is reached.
*/
const { test } = require('node:test');
const assert = require('node:assert/strict');
const Module = require('node:module');
const path = require('node:path');
const fs = require('node:fs');
const extension = require('../vscode/extension.js');
const { activate, dispatchMsdCommand, resolveEngineRoot, resolveWorkspaceCwd } = extension;
const { createTempDir, cleanup } = require('./helpers.cjs');
const shellCommandProjection = require('../msd-core/bin/lib/shell-command-projection.cjs');
test('the extension exports activate + dispatchMsdCommand + resolveEngineRoot', () => {
assert.equal(typeof activate, 'function');
assert.equal(typeof dispatchMsdCommand, 'function');
assert.equal(typeof resolveEngineRoot, 'function');
});
test('REACHABILITY: dispatchMsdCommand dispatches a real family/subcommand through msd-tools.cjs and returns REAL output (keystone wired, not UnknownCommand)', async () => {
const dir = createTempDir();
try {
// #3217 (ADR-3180 §7.6 rule 4): a free-form ROADMAP.md (no version
// token) is COMPLETE scope for windowing (§7.1) — without this, a
// bare temp dir has no ROADMAP.md at all (UNREADABLE) and `percent`
// is withheld (null), breaking this reachability proxy.
fs.mkdirSync(path.join(dir, '.planning'), { recursive: true });
fs.writeFileSync(path.join(dir, '.planning', 'ROADMAP.md'), '# Roadmap\n');
const result = await dispatchMsdCommand({ family: 'progress', subcommand: 'json', cwd: dir });
assert.equal(typeof result, 'string', 'returns a string result');
const parsed = JSON.parse(result);
assert.ok(parsed !== null && typeof parsed === 'object', 'dispatch produced a result object');
assert.equal(parsed.ok, true, `expected ok:true (real dispatch), got: ${result}`);
assert.equal(typeof parsed.stdout, 'string');
assert.ok(parsed.stdout.length > 0, 'stdout must be non-empty');
const inner = JSON.parse(parsed.stdout);
assert.equal(typeof inner.percent, 'number',
'the real progress command ran (proves the engine was reached — not UnknownCommand)');
assert.equal(parsed.code, 0);
} finally {
cleanup(dir);
}
});
test('REACHABILITY: an unknown family surfaces ok:false without throwing (not a silent UnknownCommand success)', async () => {
const dir = createTempDir();
try {
const result = await dispatchMsdCommand({ family: 'no-such-family-8675309', cwd: dir });
const parsed = JSON.parse(result);
assert.equal(parsed.ok, false);
assert.match(parsed.stderr, /no-such-family-8675309|Unknown command/);
} finally {
cleanup(dir);
}
});
test('dispatchMsdCommand works with default args (no args → msd-tools.cjs --help, a real working default)', async () => {
const result = await dispatchMsdCommand();
assert.equal(typeof result, 'string');
const parsed = JSON.parse(result); // must be valid JSON
assert.equal(parsed.ok, true, `expected the --help default to be ok:true, got: ${result}`);
assert.match(parsed.stdout, /Usage: msd-tools/, 'the --help default produced real usage output');
});
test('resolveEngineRoot finds the msd-core/ dir from the extension location', () => {
const root = resolveEngineRoot(__dirname + '/../vscode');
const fs = require('fs');
assert.ok(fs.existsSync(require('path').join(root, 'msd-core')),
'resolveEngineRoot finds a dir containing msd-core/');
});
test('the extension manifest declares the msd.invoke command', () => {
const pkg = require('../vscode/package.json');
assert.ok(pkg.contributes && pkg.contributes.commands, 'manifest has commands');
const cmd = pkg.contributes.commands.find((c) => c.command === 'msd.invoke');
assert.ok(cmd, 'manifest declares msd.invoke');
assert.ok(pkg.engines && pkg.engines.vscode, 'manifest declares VS Code engine');
assert.equal(pkg.main, './extension.js', 'manifest main points to extension.js');
});
// ── #2103 FIX (adversarial review, MAJOR): all three desktop dispatch
// surfaces previously called dispatchMsdCommand WITHOUT a cwd, silently
// defaulting to the extension host's own process.cwd() instead of the user's
// project — meaning MSD ran against the wrong directory. resolveWorkspaceCwd
// resolves vscode.workspace.workspaceFolders[0].uri.fsPath, computed fresh
// per-invocation (never cached at activate() time). ─────────────────────────
test('resolveWorkspaceCwd resolves workspaceFolders[0].uri.fsPath when a workspace is open', () => {
const mockVscode = { workspace: { workspaceFolders: [{ uri: { fsPath: '/tmp/some-workspace' } }] } };
assert.equal(resolveWorkspaceCwd(mockVscode), '/tmp/some-workspace');
});
test('resolveWorkspaceCwd falls back to process.cwd() when no workspace folder is open (fail-soft, never throws)', () => {
assert.doesNotThrow(() => {
assert.equal(resolveWorkspaceCwd({ workspace: { workspaceFolders: [] } }), process.cwd());
assert.equal(resolveWorkspaceCwd({ workspace: {} }), process.cwd());
assert.equal(resolveWorkspaceCwd({}), process.cwd());
});
});
test('#2103 FIX: all three desktop dispatch surfaces (msd.invoke command, @msd chat participant, LM tool invoke) thread the resolved workspace folder as cwd — not process.cwd()', async () => {
// Spy on the SHARED subprocess-shim dispatchMsdCommand (the one every
// surface ultimately calls via extension.js's own dispatchMsdCommand) so we
// observe the actual `cwd` each surface passes, without needing a real
// msd-tools.cjs project rooted at the mock workspace path.
const originalShimDispatch = shellCommandProjection.dispatchMsdCommand;
const calls = [];
shellCommandProjection.dispatchMsdCommand = (args) => {
calls.push(args);
return originalShimDispatch(args);
};
const registeredCommands = {};
let chatHandler = null;
const toolImpls = [];
const mockVscode = {
commands: {
registerCommand(id, handler) { registeredCommands[id] = handler; return { dispose() {} }; },
},
chat: {
createChatParticipant(id, handler) { chatHandler = handler; return { id, dispose() {} }; },
},
lm: {
registerTool(name, impl) { toolImpls.push({ name, impl }); return { dispose() {} }; },
},
workspace: {
workspaceFolders: [{ uri: { fsPath: '/tmp/mock-msd-workspace' } }],
getConfiguration: () => ({ get: () => undefined }),
},
LanguageModelTextPart: class { constructor(t) { this.text = t; } },
LanguageModelToolResult: class { constructor(parts) { this.parts = parts; } },
};
const originalLoad = Module._load;
Module._load = function (request, ...rest) {
if (request === 'vscode') return mockVscode;
return originalLoad.call(this, request, ...rest);
};
const extensionPath = path.join(__dirname, '..', 'vscode', 'extension.js');
const hostBindingPath = path.join(__dirname, '..', 'vscode', 'host-binding.js');
try {
delete require.cache[extensionPath];
delete require.cache[hostBindingPath];
const freshExtension = require(extensionPath);
const context = {
subscriptions: [],
globalState: { get: () => undefined, update: () => Promise.resolve() },
};
freshExtension.activate(context);
// Surface 1: msd.invoke command handler.
calls.length = 0;
await registeredCommands['msd.invoke']({ family: 'progress', subcommand: 'json' });
assert.equal(calls.length, 1, 'msd.invoke handler must dispatch exactly once');
assert.equal(calls[0].cwd, '/tmp/mock-msd-workspace', 'msd.invoke must thread the workspace folder as cwd');
// Surface 1b: an explicit args.cwd still takes precedence over the resolved workspace.
calls.length = 0;
await registeredCommands['msd.invoke']({ family: 'progress', subcommand: 'json', cwd: '/explicit/override' });
assert.equal(calls[0].cwd, '/explicit/override', 'an explicit cwd argument must still override the resolved workspace folder');
// Surface 2: @msd chat participant handler.
calls.length = 0;
let markdownOut = '';
await chatHandler({ prompt: 'progress json' }, {}, { markdown: (t) => { markdownOut += t; } }, {});
assert.equal(calls.length, 1, 'the chat participant handler must dispatch exactly once');
assert.equal(calls[0].cwd, '/tmp/mock-msd-workspace', 'the chat participant must thread the workspace folder as cwd');
assert.ok(markdownOut.length > 0);
// Surface 3: Language Model Tool invoke().
calls.length = 0;
const progressTool = toolImpls.find((t) => t.name === 'msd_progress');
assert.ok(progressTool, 'msd_progress tool must be registered');
await progressTool.impl.invoke({ input: {} }, {});
assert.equal(calls.length, 1, 'the LM tool invoke() must dispatch exactly once');
assert.equal(calls[0].cwd, '/tmp/mock-msd-workspace', 'the LM tool invoke() must thread the workspace folder as cwd');
} finally {
Module._load = originalLoad;
shellCommandProjection.dispatchMsdCommand = originalShimDispatch;
delete require.cache[extensionPath];
delete require.cache[hostBindingPath];
}
});