Implement attachment analysis task flow
This commit is contained in:
141
scripts/validate-attachment-analysis.mjs
Normal file
141
scripts/validate-attachment-analysis.mjs
Normal file
@@ -0,0 +1,141 @@
|
||||
#!/usr/bin/env node
|
||||
import assert from "node:assert/strict";
|
||||
import fs from "node:fs/promises";
|
||||
import os from "node:os";
|
||||
import path from "node:path";
|
||||
import { fileURLToPath } from "node:url";
|
||||
import { createRequire } from "node:module";
|
||||
|
||||
const rootDir = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
|
||||
const runtimeDir = await fs.mkdtemp(path.join(os.tmpdir(), "boss-attachment-analysis-"));
|
||||
const stateFile = path.join(runtimeDir, "data", "boss-state.json");
|
||||
const require = createRequire(import.meta.url);
|
||||
|
||||
process.env.BOSS_RUNTIME_ROOT = runtimeDir;
|
||||
process.env.BOSS_STATE_FILE = stateFile;
|
||||
process.env.BOSS_AUTH_AUTO_LOGIN = "0";
|
||||
|
||||
const { NextRequest } = require("next/server");
|
||||
const authLoginRoute = require(path.join(rootDir, ".next/standalone/.next/server/app/api/auth/login/route.js"));
|
||||
const attachmentsRoute = require(
|
||||
path.join(rootDir, ".next/standalone/.next/server/app/api/v1/projects/[projectId]/attachments/route.js"),
|
||||
);
|
||||
const analyzeRoute = require(
|
||||
path.join(
|
||||
rootDir,
|
||||
".next/standalone/.next/server/app/api/v1/projects/[projectId]/attachments/[attachmentId]/analyze/route.js",
|
||||
),
|
||||
);
|
||||
|
||||
const loginHandler = authLoginRoute.routeModule.userland.POST;
|
||||
const uploadHandler = attachmentsRoute.routeModule.userland.POST;
|
||||
const analyzeHandler = analyzeRoute.routeModule.userland.POST;
|
||||
|
||||
async function invokeRoute(handler, url, init = {}, context) {
|
||||
const request = new NextRequest(url, {
|
||||
method: init.method ?? "GET",
|
||||
headers: init.headers,
|
||||
body: init.body,
|
||||
});
|
||||
return handler(request, context);
|
||||
}
|
||||
|
||||
function parseCookieValue(setCookieHeader, cookieName) {
|
||||
assert.ok(setCookieHeader, "set-cookie header is missing");
|
||||
const match = setCookieHeader.match(new RegExp(`${cookieName}=([^;]+)`));
|
||||
assert.ok(match, `${cookieName} cookie is missing`);
|
||||
return match[1];
|
||||
}
|
||||
|
||||
async function loginAsAdmin() {
|
||||
const response = await invokeRoute(
|
||||
loginHandler,
|
||||
"http://localhost/api/auth/login",
|
||||
{
|
||||
method: "POST",
|
||||
headers: { "content-type": "application/json" },
|
||||
body: JSON.stringify({
|
||||
account: "17600003315",
|
||||
password: "boss123456",
|
||||
method: "password",
|
||||
}),
|
||||
},
|
||||
);
|
||||
assert.equal(response.status, 200, "login should succeed");
|
||||
const payload = await response.json();
|
||||
assert.equal(payload.ok, true, "login payload should be ok");
|
||||
const cookie = parseCookieValue(response.headers.get("set-cookie"), "boss_session");
|
||||
return { cookie, payload };
|
||||
}
|
||||
|
||||
async function uploadAttachment(cookie, projectId, fileName, type, bytes) {
|
||||
const form = new FormData();
|
||||
form.set("file", new File([bytes], fileName, { type }));
|
||||
|
||||
const response = await invokeRoute(
|
||||
uploadHandler,
|
||||
`http://localhost/api/v1/projects/${projectId}/attachments`,
|
||||
{
|
||||
method: "POST",
|
||||
headers: { cookie: `boss_session=${cookie}` },
|
||||
body: form,
|
||||
},
|
||||
{ params: Promise.resolve({ projectId }) },
|
||||
);
|
||||
assert.equal(response.status, 200, `upload ${fileName} should succeed`);
|
||||
return response.json();
|
||||
}
|
||||
|
||||
const { cookie } = await loginAsAdmin();
|
||||
|
||||
const textUpload = await uploadAttachment(
|
||||
cookie,
|
||||
"master-agent",
|
||||
"analysis-note.txt",
|
||||
"text/plain",
|
||||
Buffer.from("text attachment for automatic analysis"),
|
||||
);
|
||||
assert.equal(textUpload.attachment.analysisState, "queued_auto", "text attachment should queue automatically");
|
||||
assert.ok(textUpload.analysisTask, "queued auto attachment should create a master agent task");
|
||||
assert.equal(textUpload.analysisTask.taskType, "attachment_analysis", "queued task type should be attachment_analysis");
|
||||
assert.equal(
|
||||
textUpload.analysisTask.attachmentFileName,
|
||||
"analysis-note.txt",
|
||||
"queued task should carry attachment file name",
|
||||
);
|
||||
|
||||
const manualUpload = await uploadAttachment(
|
||||
cookie,
|
||||
"master-agent",
|
||||
"manual-binary.bin",
|
||||
"application/octet-stream",
|
||||
Buffer.from([0, 1, 2, 3]),
|
||||
);
|
||||
assert.equal(manualUpload.attachment.analysisState, "ready_manual", "binary attachment should be manually analyzable");
|
||||
|
||||
const analyzeResponse = await invokeRoute(
|
||||
analyzeHandler,
|
||||
`http://localhost/api/v1/projects/master-agent/attachments/${manualUpload.attachment.attachmentId}/analyze`,
|
||||
{
|
||||
method: "POST",
|
||||
headers: { cookie: `boss_session=${cookie}` },
|
||||
},
|
||||
{
|
||||
params: Promise.resolve({
|
||||
projectId: "master-agent",
|
||||
attachmentId: manualUpload.attachment.attachmentId,
|
||||
}),
|
||||
},
|
||||
);
|
||||
assert.equal(analyzeResponse.status, 200, "manual analyze should succeed");
|
||||
const analyzePayload = await analyzeResponse.json();
|
||||
assert.ok(analyzePayload.taskId, "manual analyze should return a taskId");
|
||||
assert.ok(analyzePayload.task, "manual analyze should return a task payload");
|
||||
assert.equal(analyzePayload.task.taskType, "attachment_analysis", "manual analyze task should be attachment_analysis");
|
||||
assert.equal(
|
||||
analyzePayload.task.attachmentId,
|
||||
manualUpload.attachment.attachmentId,
|
||||
"manual task should link the attachment",
|
||||
);
|
||||
|
||||
console.log("attachment analysis validation passed");
|
||||
Reference in New Issue
Block a user