TEST - CODEX AGENTS: real Tasker e2e smoke harness
This commit is contained in:
parent
9f402074f2
commit
418914fefd
|
|
@ -12,6 +12,7 @@
|
||||||
"check": "tsc --noEmit -p tsconfig.json",
|
"check": "tsc --noEmit -p tsconfig.json",
|
||||||
"migrate": "tsx src/scripts/migrate.ts",
|
"migrate": "tsx src/scripts/migrate.ts",
|
||||||
"migrate:dist": "node dist/scripts/migrate.js",
|
"migrate:dist": "node dist/scripts/migrate.js",
|
||||||
|
"smoke:e2e": "tsx src/scripts/smoke-e2e.ts",
|
||||||
"smoke:gateway": "tsx src/scripts/smoke-gateway.ts",
|
"smoke:gateway": "tsx src/scripts/smoke-gateway.ts",
|
||||||
"start": "node dist/server.js"
|
"start": "node dist/server.js"
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,182 @@
|
||||||
|
import { Pool } from "pg";
|
||||||
|
|
||||||
|
import { buildApp } from "../app.js";
|
||||||
|
import { loadConfig } from "../config.js";
|
||||||
|
import { runMigrations } from "../db/migrations.js";
|
||||||
|
|
||||||
|
const config = loadConfig();
|
||||||
|
const workspaceSlug = readRequiredEnv("SMOKE_WORKSPACE_SLUG");
|
||||||
|
const projectId = readRequiredEnv("SMOKE_PROJECT_ID");
|
||||||
|
|
||||||
|
if (!config.DATABASE_URL) {
|
||||||
|
throw new Error("DATABASE_URL is required for e2e smoke test.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!config.NODEDC_INTERNAL_ACCESS_TOKEN) {
|
||||||
|
throw new Error("NODEDC_INTERNAL_ACCESS_TOKEN is required for e2e smoke test.");
|
||||||
|
}
|
||||||
|
|
||||||
|
const migrationPool = new Pool({ connectionString: config.DATABASE_URL });
|
||||||
|
await runMigrations(migrationPool);
|
||||||
|
await migrationPool.end();
|
||||||
|
|
||||||
|
const app = await buildApp({
|
||||||
|
...config,
|
||||||
|
LOG_LEVEL: process.env.LOG_LEVEL === "debug" ? "debug" : "silent",
|
||||||
|
});
|
||||||
|
|
||||||
|
try {
|
||||||
|
const suffix = Date.now().toString(36);
|
||||||
|
const agentId = await createAgent(suffix);
|
||||||
|
await upsertGrant(agentId);
|
||||||
|
const token = await createToken(agentId);
|
||||||
|
const authHeaders = { Authorization: `Bearer ${token}` };
|
||||||
|
|
||||||
|
const projects = await requestJson("GET", "/api/v1/tools/projects", authHeaders);
|
||||||
|
const context = await requestJson(
|
||||||
|
"GET",
|
||||||
|
`/api/v1/tools/projects/${projectId}/context?workspace_slug=${encodeURIComponent(workspaceSlug)}`,
|
||||||
|
authHeaders
|
||||||
|
);
|
||||||
|
const issue = await requestJson("POST", "/api/v1/tools/issues", authHeaders, {
|
||||||
|
project_id: projectId,
|
||||||
|
workspace_slug: workspaceSlug,
|
||||||
|
title: `NODE.DC Codex API smoke ${suffix}`,
|
||||||
|
description: "Created by Agent Gateway e2e smoke test.",
|
||||||
|
priority: "medium",
|
||||||
|
structured_blocks: [
|
||||||
|
{
|
||||||
|
id: "current-architecture",
|
||||||
|
type: "text",
|
||||||
|
title: "Текущая архитектура",
|
||||||
|
body: "Gateway создал карточку через реальный Tasker internal adapter.",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "checker-1",
|
||||||
|
type: "checker",
|
||||||
|
title: "Чекер smoke",
|
||||||
|
items: [
|
||||||
|
{
|
||||||
|
id: "auth",
|
||||||
|
text: "Agent token accepted",
|
||||||
|
checked: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "tasker",
|
||||||
|
text: "Tasker adapter created issue",
|
||||||
|
checked: true,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
const issueId = issue.issue.id as string;
|
||||||
|
await requestJson("POST", `/api/v1/tools/issues/${issueId}/comments`, authHeaders, {
|
||||||
|
project_id: projectId,
|
||||||
|
workspace_slug: workspaceSlug,
|
||||||
|
body: "Smoke comment from Agent Gateway.",
|
||||||
|
});
|
||||||
|
|
||||||
|
const states = Array.isArray(context.states) ? context.states : [];
|
||||||
|
const targetState = states.find((state) => typeof state?.id === "string");
|
||||||
|
if (targetState) {
|
||||||
|
await requestJson("POST", `/api/v1/tools/issues/${issueId}/move`, authHeaders, {
|
||||||
|
project_id: projectId,
|
||||||
|
workspace_slug: workspaceSlug,
|
||||||
|
state_id: targetState.id,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(
|
||||||
|
JSON.stringify(
|
||||||
|
{
|
||||||
|
ok: true,
|
||||||
|
tasker_url: config.NODEDC_TASKER_INTERNAL_URL,
|
||||||
|
workspace_slug: workspaceSlug,
|
||||||
|
project_id: projectId,
|
||||||
|
visible_projects: Array.isArray(projects.projects) ? projects.projects.length : null,
|
||||||
|
issue_id: issueId,
|
||||||
|
moved: Boolean(targetState),
|
||||||
|
},
|
||||||
|
null,
|
||||||
|
2
|
||||||
|
)
|
||||||
|
);
|
||||||
|
} finally {
|
||||||
|
await app.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
async function createAgent(suffix: string): Promise<string> {
|
||||||
|
const payload = await requestJson("POST", "/api/v1/agents", undefined, {
|
||||||
|
owner_user_id: `e2e-owner-${suffix}`,
|
||||||
|
owner_email: `e2e-${suffix}@example.test`,
|
||||||
|
display_name: `E2E Codex ${suffix}`,
|
||||||
|
});
|
||||||
|
|
||||||
|
return payload.agent.id as string;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function upsertGrant(agentId: string): Promise<void> {
|
||||||
|
await requestJson("POST", `/api/v1/agents/${agentId}/grants`, undefined, {
|
||||||
|
workspace_slug: workspaceSlug,
|
||||||
|
project_id: projectId,
|
||||||
|
scopes: [
|
||||||
|
"workspace:read",
|
||||||
|
"project:read",
|
||||||
|
"issue:read",
|
||||||
|
"issue:create",
|
||||||
|
"issue:update",
|
||||||
|
"issue:move",
|
||||||
|
"issue:comment",
|
||||||
|
"issue:label",
|
||||||
|
"issue:assign",
|
||||||
|
"issue:structured_blocks:write",
|
||||||
|
],
|
||||||
|
mode: "voluntary",
|
||||||
|
created_by_user_id: "e2e-smoke-admin",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async function createToken(agentId: string): Promise<string> {
|
||||||
|
const payload = await requestJson("POST", `/api/v1/agents/${agentId}/tokens`, undefined, {
|
||||||
|
name: "E2E smoke token",
|
||||||
|
});
|
||||||
|
|
||||||
|
return payload.token as string;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function requestJson(
|
||||||
|
method: "GET" | "POST",
|
||||||
|
url: string,
|
||||||
|
headers?: Record<string, string>,
|
||||||
|
payload?: unknown
|
||||||
|
): Promise<Record<string, any>> {
|
||||||
|
const injectOptions: any = {
|
||||||
|
method,
|
||||||
|
url,
|
||||||
|
headers,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (payload !== undefined) {
|
||||||
|
injectOptions.payload = payload;
|
||||||
|
}
|
||||||
|
|
||||||
|
const response = await app.inject(injectOptions);
|
||||||
|
|
||||||
|
const body = response.body ? JSON.parse(response.body) : {};
|
||||||
|
|
||||||
|
if (response.statusCode < 200 || response.statusCode >= 300) {
|
||||||
|
throw new Error(`HTTP ${response.statusCode} for ${method} ${url}: ${JSON.stringify(body)}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
return body;
|
||||||
|
}
|
||||||
|
|
||||||
|
function readRequiredEnv(key: string): string {
|
||||||
|
const value = process.env[key]?.trim();
|
||||||
|
if (!value) {
|
||||||
|
throw new Error(`${key} is required for e2e smoke test.`);
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue