=== NEW === - studio/ — MCPEngine Studio scaffold (Next.js monorepo, build plan) - docs/FACTORY-V2.md — Factory v2 architecture doc - docs/CALENDLY_MCP_BUILD_SUMMARY.md — Calendly MCP build report === UPDATED SERVERS === - fieldedge: Added jobs-tools, UI build script, main entry update - lightspeed: Updated main + server entry points - squarespace: Added collection-browser + page-manager apps - toast: Added main + server entry points === INFRA === - infra/command-center/state.json — Updated pipeline state - infra/command-center/FACTORY-V2.md — Factory v2 operator playbook
84 lines
2.7 KiB
TypeScript
84 lines
2.7 KiB
TypeScript
// POST /api/test — Stream multi-layer QA test execution via SSE
|
|
|
|
import { NextRequest } from 'next/server';
|
|
import { runTests } from '@mcpengine/ai-pipeline/services/tester';
|
|
import { createSSEResponse } from '@mcpengine/ai-pipeline/streaming/sse';
|
|
import type { TestLayer } from '@mcpengine/ai-pipeline/types';
|
|
|
|
export const runtime = 'nodejs';
|
|
export const maxDuration = 180;
|
|
|
|
const VALID_LAYERS: TestLayer[] = [
|
|
'protocol', 'static', 'visual', 'functional', 'performance', 'security',
|
|
];
|
|
|
|
/**
|
|
* Load server code for a project.
|
|
* In production, this reads from DB / generated bundle storage.
|
|
*/
|
|
async function loadServerCode(projectId: string): Promise<string | null> {
|
|
// TODO: Replace with actual DB lookup
|
|
// Should return the concatenated server source code for testing
|
|
return null;
|
|
}
|
|
|
|
export async function POST(req: NextRequest) {
|
|
try {
|
|
const body = await req.json();
|
|
const { projectId, layers, serverCode } = body as {
|
|
projectId: string;
|
|
layers: TestLayer[];
|
|
serverCode?: string; // Allow inline for development
|
|
};
|
|
|
|
if (!projectId) {
|
|
return new Response(
|
|
JSON.stringify({ error: 'projectId is required' }),
|
|
{ status: 400, headers: { 'Content-Type': 'application/json' } }
|
|
);
|
|
}
|
|
|
|
if (!layers || !Array.isArray(layers) || layers.length === 0) {
|
|
return new Response(
|
|
JSON.stringify({ error: 'layers array is required and must not be empty' }),
|
|
{ status: 400, headers: { 'Content-Type': 'application/json' } }
|
|
);
|
|
}
|
|
|
|
// Validate layers
|
|
const invalidLayers = layers.filter((l) => !VALID_LAYERS.includes(l));
|
|
if (invalidLayers.length > 0) {
|
|
return new Response(
|
|
JSON.stringify({
|
|
error: `Invalid test layers: ${invalidLayers.join(', ')}. Valid: ${VALID_LAYERS.join(', ')}`,
|
|
}),
|
|
{ status: 400, headers: { 'Content-Type': 'application/json' } }
|
|
);
|
|
}
|
|
|
|
let resolvedCode: string;
|
|
|
|
if (serverCode) {
|
|
resolvedCode = serverCode;
|
|
} else {
|
|
const code = await loadServerCode(projectId);
|
|
if (!code) {
|
|
return new Response(
|
|
JSON.stringify({ error: `No server code found for project ${projectId}. Generate first.` }),
|
|
{ status: 404, headers: { 'Content-Type': 'application/json' } }
|
|
);
|
|
}
|
|
resolvedCode = code;
|
|
}
|
|
|
|
const generator = runTests(resolvedCode, layers);
|
|
return createSSEResponse(generator);
|
|
} catch (error) {
|
|
const message = error instanceof Error ? error.message : String(error);
|
|
return new Response(
|
|
JSON.stringify({ error: `Internal error: ${message}` }),
|
|
{ status: 500, headers: { 'Content-Type': 'application/json' } }
|
|
);
|
|
}
|
|
}
|