mindspace: version runtime contracts and audit packages

This commit is contained in:
john
2026-07-27 17:43:38 +08:00
parent e94052ff24
commit 70264a4c4c
23 changed files with 1448 additions and 20 deletions
+88 -2
View File
@@ -3,7 +3,13 @@ import test from 'node:test';
import {
createMindSpaceRemoteServerAdapter,
mindspaceRemoteServerAdapterInternals,
validateMindSpaceRemoteContract,
} from './mindspace-remote-server-adapter.mjs';
import {
MINDSPACE_SERVER_ADAPTER_BINDINGS,
MINDSPACE_SERVER_ADAPTER_CONTRACT_VERSION,
MINDSPACE_SERVER_ADAPTER_REQUIRED_CAPABILITIES,
} from './mindspace-server-adapter-contract.mjs';
test('buildRemoteOperationUrl normalizes endpoint and encodes operation path', () => {
assert.equal(
@@ -17,13 +23,93 @@ test('buildRemoteOperationUrl normalizes endpoint and encodes operation path', (
);
});
test('createMindSpaceRemoteServerAdapter validates endpoint at startup', () => {
test('createMindSpaceRemoteServerAdapter validates endpoint at startup', async () => {
const adapter = createMindSpaceRemoteServerAdapter({
fetchFn: async () => {
throw new Error('should not fetch');
},
});
assert.throws(() => adapter.assertReady(), /MINDSPACE_REMOTE_BASE_URL/);
await assert.rejects(() => adapter.assertReady(), /MINDSPACE_REMOTE_BASE_URL/);
});
test('createMindSpaceRemoteServerAdapter validates remote contract before startup succeeds', async () => {
const calls = [];
const adapter = createMindSpaceRemoteServerAdapter({
endpoint: 'https://mindspace.example.com/',
authToken: 'secret-token',
fetchFn: async (url, init) => {
calls.push({ url, init });
return {
ok: true,
status: 200,
async text() {
return JSON.stringify({
contractVersion:
MINDSPACE_SERVER_ADAPTER_CONTRACT_VERSION,
bindings: MINDSPACE_SERVER_ADAPTER_BINDINGS,
requiredCapabilities:
MINDSPACE_SERVER_ADAPTER_REQUIRED_CAPABILITIES,
buildId: 'mindspace-test',
gitSha: 'abc123',
});
},
};
},
logger: { log() {}, warn() {}, error() {} },
});
await assert.doesNotReject(() => adapter.assertReady());
assert.equal(
calls[0].url,
'https://mindspace.example.com/mindspace/v1/contract',
);
assert.equal(calls[0].init.method, 'GET');
assert.equal(
calls[0].init.headers.Authorization,
'Bearer secret-token',
);
});
test('createMindSpaceRemoteServerAdapter rejects stale remote contracts', async () => {
const adapter = createMindSpaceRemoteServerAdapter({
endpoint: 'https://mindspace.example.com/',
fetchFn: async () => ({
ok: true,
status: 200,
async text() {
return JSON.stringify({
contractVersion: 1,
bindings: {
assetService: ['renderAssetPreview'],
},
requiredCapabilities: [],
});
},
}),
logger: { log() {}, warn() {}, error() {} },
});
await assert.rejects(
() => adapter.assertReady(),
(error) =>
error?.code === 'mindspace_contract_incompatible' &&
/workspacePublicationDeliveryService/.test(error.message),
);
});
test('validateMindSpaceRemoteContract reports missing capabilities and methods', () => {
assert.throws(
() =>
validateMindSpaceRemoteContract({
contractVersion:
MINDSPACE_SERVER_ADAPTER_CONTRACT_VERSION,
bindings: {
chatSaveService: ['createSharedHtml'],
},
requiredCapabilities: ['chat-save-authority'],
}),
/missing method chatSaveService\.materializeWorkspaceHtml/,
);
});
test('createMindSpaceRemoteServerAdapter proxies server binding methods through fetch transport', async () => {