/** * Core.invoke 与 Provider 契约测试(任务 A2.2 / A2.3 / A2.4)。 * * 覆盖实施设计 5 要求的六件事:Schema 校验、幂等重放、错误映射、超时与结果未知、 * 执行记录可查、契约测试套件本身能拦住坏 Provider。 */ import assert from 'node:assert/strict'; import { test } from 'node:test'; import { runProviderContract } from '@aps/provider-sdk/contract-suite'; import { ProviderError } from '@aps/provider-sdk'; import { createFakeProvider } from '@aps/provider-sdk/testing'; const subject = { userId: 'u-1', tenantId: 't-1' }; async function startPlatform(devProvider = true) { if (devProvider) process.env.APS_DEV_PROVIDER = '1'; else delete process.env.APS_DEV_PROVIDER; const { startServer } = await import('../server/dist/index.js'); const server = await startServer({ host: '127.0.0.1', port: 0, version: 'test' }); return server; } test('未注册能力返回 invalid_argument,且不产生成功记录', async () => { const provider = createFakeProvider(); const server = await startPlatform(false); try { await server.service.registerProvider(provider); const receipt = await server.service.invoke({ subject, sessionId: 's-test', capabilityId: 'fake.__nope__', params: {}, }); assert.equal(receipt.status, 'failed'); assert.equal(receipt.errorKind, 'invalid_argument'); } finally { await server.close(); } }); test('正常调用返回 succeeded,执行记录可查且带参数摘要', async () => { const provider = createFakeProvider(); const server = await startPlatform(false); try { await server.service.registerProvider(provider); const receipt = await server.service.invoke({ subject, sessionId: 's-test', capabilityId: 'fake.echo', params: { text: '你好' }, }); assert.equal(receipt.status, 'succeeded'); assert.deepEqual(receipt.result, { echo: '你好' }); const record = server.service.core.executions.get(receipt.executionId); assert.ok(record, '执行记录应可查'); assert.equal(record.capabilityId, 'fake.echo'); assert.equal(record.providerId, 'fake'); assert.match(record.paramsDigest, /^sha256:/, '只存参数摘要,不存明文'); assert.equal(record.status, 'succeeded'); } finally { await server.close(); } }); test('同一幂等键重试只执行一次,返回同一 executionId', async () => { const provider = createFakeProvider(); const server = await startPlatform(false); try { await server.service.registerProvider(provider); const request = { subject, sessionId: 's-test', capabilityId: 'fake.write', params: { value: 1 }, idempotencyKey: 'idem-core-0001', }; const first = await server.service.invoke(request); const second = await server.service.invoke(request); assert.equal(first.executionId, second.executionId, '重试应返回同一执行'); assert.equal(provider.stats.calls['fake.write'], 1, 'Provider 只应被调用一次'); } finally { await server.close(); } }); test('超时归一为 unknown_result(结果未知,不等于失败确认)', async () => { const provider = createFakeProvider(); const server = await startPlatform(false); try { await server.service.registerProvider(provider); const receipt = await server.service.invoke({ subject, sessionId: 's-test', capabilityId: 'fake.slow', params: { ms: 500 }, timeoutMs: 50, }); assert.equal(receipt.status, 'failed'); assert.equal(receipt.errorKind, 'unknown_result'); const record = server.service.core.executions.get(receipt.executionId); assert.equal(record?.status, 'failed'); } finally { await server.close(); } }); test('Provider 声明的错误分类原样透传', async () => { const provider = createFakeProvider(); const server = await startPlatform(false); try { await server.service.registerProvider(provider); const receipt = await server.service.invoke({ subject, sessionId: 's-test', capabilityId: 'fake.declare-error', params: { kind: 'forbidden' }, }); assert.equal(receipt.status, 'failed'); assert.equal(receipt.errorKind, 'forbidden'); } finally { await server.close(); } }); test('裸异常归一为 unknown_result,不进脏分类', async () => { const provider = createFakeProvider(); const server = await startPlatform(false); try { await server.service.registerProvider(provider); const receipt = await server.service.invoke({ subject, sessionId: 's-test', capabilityId: 'fake.crash', params: {}, }); assert.equal(receipt.status, 'failed'); assert.equal(receipt.errorKind, 'unknown_result'); } finally { await server.close(); } }); test('契约套件:假 Provider 全绿', async () => { const report = await runProviderContract(createFakeProvider()); assert.equal(report.ok, true, JSON.stringify(report.checks.filter((check) => !check.ok), null, 2)); assert.equal(report.providerId, 'fake'); assert.ok(report.checks.length >= 7, `检查项应有若干条,实际 ${report.checks.length}`); }); test('契约套件:坏 Provider 被拦下(负例)', async () => { const broken = { async describe() { return { id: 'broken', displayName: '坏 Provider', version: '0.0.0', platformVersion: 1, capabilities: [ { id: 'Broken_Capability', description: '这个描述故意写得非常长'.repeat(5), effect: 'execute', mode: 'batch', inputSchema: null, outputSchema: null, idempotency: 'none', errors: ['nonsense'], }, ], }; }, async invoke() { throw new Error('不该被调用'); }, async health() { return { status: 'ok', checkedAt: Date.now() }; }, }; const report = await runProviderContract(broken); assert.equal(report.ok, false, '坏 Provider 不应通过契约测试'); const failed = report.checks.filter((check) => !check.ok); assert.ok(failed.length >= 2, `应有多项失败,实际:${failed.map((item) => item.name).join('、')}`); const capabilityDetail = failed.find((item) => item.name.includes('声明合法'))?.detail ?? ''; for (const expected of ['id 命名不合规', 'effect 非法', 'mode 非法', '缺 inputSchema', 'errors 含非法分类']) { assert.ok(capabilityDetail.includes(expected), `应报告「${expected}」,实际:${capabilityDetail}`); } assert.ok(failed.some((item) => item.name === '未知能力必须报错'), '裸异常不算合规的未知能力报错'); }); test('HTTP:注册 Provider 后能力清单非空,执行记录端点可查回执', async () => { const server = await startPlatform(true); try { const capabilities = await (await fetch(`${server.url}/v1/capabilities`)).json(); assert.ok(capabilities.capabilities.length >= 5, '假 Provider 应暴露 5 个能力'); const first = capabilities.capabilities[0]; assert.equal(typeof first.providerId, 'string'); assert.ok(first.providerId.length > 0, '能力条目应带 providerId'); const receipt = await server.service.invoke({ subject, sessionId: 's-http', capabilityId: 'fake.echo', params: { text: '通过 HTTP 查回执' }, }); const response = await fetch(`${server.url}/v1/executions/${receipt.executionId}`); assert.equal(response.status, 200, '执行记录端点应返回 200'); const body = await response.json(); assert.equal(body.executionId, receipt.executionId); assert.equal(body.status, 'succeeded'); const missing = await fetch(`${server.url}/v1/executions/exec-none`); assert.equal(missing.status, 404, '未知执行记录应 404'); const missingBody = await missing.json(); assert.equal(missingBody.error.code, 'not_found'); } finally { delete process.env.APS_DEV_PROVIDER; await server.close(); } });