tpmjs/packages/executor-test/src/tests/core.ts
Ajax Davis 32c6e097ed feat(executor): formalize Executor Protocol v1.0 with compliance testing
- Add EXECUTOR_SPECIFICATION.md with formal v1.0 protocol spec
- Add executor-openapi.yaml (OpenAPI 3.0 specification)
- Create @tpmjs/executor-test compliance test package (15 tests)
- Update Railway executor to v1.0 compliance (15/15 tests pass)
- Update Unsandbox executor to v1.0 compliance (15/15 tests pass)
- Update Vercel executor to v1.0 compliance
- Add /info endpoint with capability advertisement to all executors
- Add structured error codes (PACKAGE_NOT_FOUND, TOOL_NOT_FOUND, etc.)
- Add protocolVersion and implementationVersion to /health responses
- Add X-TPMJS-Protocol-Version header support
- Add EXECUTOR_COMPLIANCE.md with test results documentation
2026-02-04 02:09:08 +10:00

417 lines
10 KiB
TypeScript

import type { ExecuteToolResponse, HealthResponse, TestResult, TestSuite } from '../types.js';
async function testHealthReturns200(baseUrl: string, _apiKey?: string): Promise<TestResult> {
const start = Date.now();
const name = 'GET /health returns 200';
try {
const response = await fetch(`${baseUrl}/health`, {
method: 'GET',
headers: {
'X-TPMJS-Protocol-Version': '1.0',
},
});
const durationMs = Date.now() - start;
if (response.status === 200) {
return { name, passed: true, durationMs };
}
return {
name,
passed: false,
message: `Expected status 200, got ${response.status}`,
durationMs,
};
} catch (error) {
return {
name,
passed: false,
message: `Request failed: ${error instanceof Error ? error.message : 'Unknown error'}`,
durationMs: Date.now() - start,
};
}
}
async function testHealthIncludesProtocolVersion(
baseUrl: string,
_apiKey?: string
): Promise<TestResult> {
const start = Date.now();
const name = 'GET /health includes protocolVersion';
try {
const response = await fetch(`${baseUrl}/health`, {
method: 'GET',
headers: {
'X-TPMJS-Protocol-Version': '1.0',
},
});
const data = (await response.json()) as HealthResponse;
const durationMs = Date.now() - start;
if (data.protocolVersion) {
return { name, passed: true, durationMs };
}
return {
name,
passed: false,
message: 'Response missing protocolVersion field',
durationMs,
};
} catch (error) {
return {
name,
passed: false,
message: `Request failed: ${error instanceof Error ? error.message : 'Unknown error'}`,
durationMs: Date.now() - start,
};
}
}
async function testHealthIncludesImplementationVersion(
baseUrl: string,
_apiKey?: string
): Promise<TestResult> {
const start = Date.now();
const name = 'GET /health includes implementationVersion';
try {
const response = await fetch(`${baseUrl}/health`, {
method: 'GET',
headers: {
'X-TPMJS-Protocol-Version': '1.0',
},
});
const data = (await response.json()) as HealthResponse;
const durationMs = Date.now() - start;
if (data.implementationVersion) {
return { name, passed: true, durationMs };
}
return {
name,
passed: false,
message: 'Response missing implementationVersion field',
durationMs,
};
} catch (error) {
return {
name,
passed: false,
message: `Request failed: ${error instanceof Error ? error.message : 'Unknown error'}`,
durationMs: Date.now() - start,
};
}
}
async function testExecuteToolAcceptsValidRequest(
baseUrl: string,
apiKey?: string
): Promise<TestResult> {
const start = Date.now();
const name = 'POST /execute-tool accepts valid request';
try {
const headers: Record<string, string> = {
'Content-Type': 'application/json',
'X-TPMJS-Protocol-Version': '1.0',
};
if (apiKey) {
headers['Authorization'] = `Bearer ${apiKey}`;
}
const response = await fetch(`${baseUrl}/execute-tool`, {
method: 'POST',
headers,
body: JSON.stringify({
packageName: '@anthropic-ai/sdk',
name: 'default',
params: {},
}),
});
const durationMs = Date.now() - start;
// We expect 200 even if the tool fails
if (response.status === 200) {
return { name, passed: true, durationMs };
}
// 401 is acceptable if auth is required and not provided
if (response.status === 401 && !apiKey) {
return {
name,
passed: true,
message: 'Authentication required (expected)',
durationMs,
};
}
return {
name,
passed: false,
message: `Expected status 200, got ${response.status}`,
durationMs,
};
} catch (error) {
return {
name,
passed: false,
message: `Request failed: ${error instanceof Error ? error.message : 'Unknown error'}`,
durationMs: Date.now() - start,
};
}
}
async function testExecuteToolReturnsStructuredResponse(
baseUrl: string,
apiKey?: string
): Promise<TestResult> {
const start = Date.now();
const name = 'POST /execute-tool returns structured response';
try {
const headers: Record<string, string> = {
'Content-Type': 'application/json',
'X-TPMJS-Protocol-Version': '1.0',
};
if (apiKey) {
headers['Authorization'] = `Bearer ${apiKey}`;
}
const response = await fetch(`${baseUrl}/execute-tool`, {
method: 'POST',
headers,
body: JSON.stringify({
packageName: '@anthropic-ai/sdk',
name: 'default',
params: {},
}),
});
const durationMs = Date.now() - start;
// Skip if auth required
if (response.status === 401 && !apiKey) {
return {
name,
passed: true,
message: 'Skipped: Authentication required',
durationMs,
};
}
const data = (await response.json()) as ExecuteToolResponse;
// Check required fields
if (typeof data.success !== 'boolean') {
return {
name,
passed: false,
message: 'Response missing "success" boolean field',
durationMs,
};
}
if (typeof data.executionTimeMs !== 'number') {
return {
name,
passed: false,
message: 'Response missing "executionTimeMs" number field',
durationMs,
};
}
if (data.success && data.output === undefined) {
return {
name,
passed: false,
message: 'Successful response missing "output" field',
durationMs,
};
}
if (!data.success && !data.error) {
return {
name,
passed: false,
message: 'Error response missing "error" field',
durationMs,
};
}
return { name, passed: true, durationMs };
} catch (error) {
return {
name,
passed: false,
message: `Request failed: ${error instanceof Error ? error.message : 'Unknown error'}`,
durationMs: Date.now() - start,
};
}
}
async function testExecuteToolReturnsErrorForInvalidTool(
baseUrl: string,
apiKey?: string
): Promise<TestResult> {
const start = Date.now();
const name = 'POST /execute-tool returns error for invalid tool';
try {
const headers: Record<string, string> = {
'Content-Type': 'application/json',
'X-TPMJS-Protocol-Version': '1.0',
};
if (apiKey) {
headers['Authorization'] = `Bearer ${apiKey}`;
}
const response = await fetch(`${baseUrl}/execute-tool`, {
method: 'POST',
headers,
body: JSON.stringify({
packageName: '@tpmjs/nonexistent-package-12345',
name: 'nonexistentTool',
params: {},
}),
});
const durationMs = Date.now() - start;
// Skip if auth required
if (response.status === 401 && !apiKey) {
return {
name,
passed: true,
message: 'Skipped: Authentication required',
durationMs,
};
}
const data = (await response.json()) as ExecuteToolResponse;
if (data.success === false && data.error?.code) {
return { name, passed: true, durationMs };
}
return {
name,
passed: false,
message: 'Expected error response with code for nonexistent package',
durationMs,
};
} catch (error) {
return {
name,
passed: false,
message: `Request failed: ${error instanceof Error ? error.message : 'Unknown error'}`,
durationMs: Date.now() - start,
};
}
}
async function testCORSHeaders(baseUrl: string, _apiKey?: string): Promise<TestResult> {
const start = Date.now();
const name = 'CORS headers present';
try {
const response = await fetch(`${baseUrl}/health`, {
method: 'GET',
});
const durationMs = Date.now() - start;
const allowOrigin = response.headers.get('access-control-allow-origin');
if (allowOrigin) {
return { name, passed: true, durationMs };
}
return {
name,
passed: false,
message: 'Missing Access-Control-Allow-Origin header',
durationMs,
};
} catch (error) {
return {
name,
passed: false,
message: `Request failed: ${error instanceof Error ? error.message : 'Unknown error'}`,
durationMs: Date.now() - start,
};
}
}
async function testOPTIONSPreflight(baseUrl: string, _apiKey?: string): Promise<TestResult> {
const start = Date.now();
const name = 'OPTIONS preflight works';
try {
const response = await fetch(`${baseUrl}/execute-tool`, {
method: 'OPTIONS',
});
const durationMs = Date.now() - start;
if (response.status === 200 || response.status === 204) {
const allowMethods = response.headers.get('access-control-allow-methods');
const allowHeaders = response.headers.get('access-control-allow-headers');
if (allowMethods && allowHeaders) {
return { name, passed: true, durationMs };
}
return {
name,
passed: false,
message: 'Missing CORS preflight headers',
durationMs,
};
}
return {
name,
passed: false,
message: `Expected status 200 or 204, got ${response.status}`,
durationMs,
};
} catch (error) {
return {
name,
passed: false,
message: `Request failed: ${error instanceof Error ? error.message : 'Unknown error'}`,
durationMs: Date.now() - start,
};
}
}
export async function runCoreTests(baseUrl: string, apiKey?: string): Promise<TestSuite> {
const results: TestResult[] = [];
// Run tests sequentially to avoid overwhelming the executor
results.push(await testHealthReturns200(baseUrl, apiKey));
results.push(await testHealthIncludesProtocolVersion(baseUrl, apiKey));
results.push(await testHealthIncludesImplementationVersion(baseUrl, apiKey));
results.push(await testExecuteToolAcceptsValidRequest(baseUrl, apiKey));
results.push(await testExecuteToolReturnsStructuredResponse(baseUrl, apiKey));
results.push(await testExecuteToolReturnsErrorForInvalidTool(baseUrl, apiKey));
results.push(await testCORSHeaders(baseUrl, apiKey));
results.push(await testOPTIONSPreflight(baseUrl, apiKey));
return {
name: 'Core Requirements',
level: 'core',
results,
};
}