mindspace: version runtime contracts and audit packages
This commit is contained in:
@@ -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 () => {
|
||||
|
||||
Reference in New Issue
Block a user