fix artifact download response contract

This commit is contained in:
-Puter
2026-07-16 13:19:06 +05:30
parent 794fe29d8d
commit 5a519f6b52
3 changed files with 60 additions and 18 deletions
+41 -16
View File
@@ -1,17 +1,38 @@
import assert from "node:assert/strict";
import { createServer } from "node:http";
import type { AddressInfo } from "node:net";
import { serviceRoutes } from "../src/routes/services.js";
const originalFetch = globalThis.fetch;
const requests: Request[] = [];
const artifactTypes: Record<string, { mime: string; bytes: number[] }> = {
session_dual_wav: { mime: "audio/wav", bytes: [0x52, 0x49, 0x46, 0x46] },
session_mixed_mp3: { mime: "audio/mpeg", bytes: [0x49, 0x44, 0x33, 0x04] },
session_video: { mime: "video/webm", bytes: [0x1a, 0x45, 0xdf, 0xa3] },
session_video_mp4: { mime: "video/mp4", bytes: [0x00, 0x00, 0x00, 0x18] },
};
const storageServer = createServer((request, response) => {
const artifactType = request.url?.replace(/^\/stored\//, "") ?? "";
const artifact = artifactTypes[artifactType];
if (!artifact) { response.writeHead(404); response.end(); return; }
response.writeHead(200, { "content-type": artifact.mime, "content-length": artifact.bytes.length });
response.end(Buffer.from(artifact.bytes));
});
await new Promise<void>((resolve) => storageServer.listen(0, "127.0.0.1", resolve));
const storagePort = (storageServer.address() as AddressInfo).port;
const storageBaseUrl = `http://127.0.0.1:${storagePort}`;
globalThis.fetch = (async (input, init) => {
const request = new Request(input, init);
requests.push(request);
const path = new URL(request.url).pathname;
if (path.endsWith("/api/v1/artifacts/sess-1/session_video")) {
return new Response(JSON.stringify({ url: "http://s3.test/obj", mime_type: "video/webm" }), { headers: { "content-type": "application/json" } });
const metadataMatch = path.match(/\/api\/v1\/artifacts\/sess-1\/([^/]+)$/);
if (metadataMatch && artifactTypes[metadataMatch[1]]) {
const artifact = artifactTypes[metadataMatch[1]];
return new Response(JSON.stringify({ url: `${storageBaseUrl}/stored/${metadataMatch[1]}`, mime_type: artifact.mime }), { headers: { "content-type": "application/json" } });
}
if (path === "/obj") return new Response(new Uint8Array([1, 2, 3]), { headers: { "content-type": "video/webm", "content-length": "3" } });
if (path.startsWith("/stored/")) return originalFetch(input, init);
if (path.endsWith("/api/v1/sessions/sess-1/video/upload-url")) {
return new Response(JSON.stringify({ url: "http://s3.test/put", mime_type: "video/webm" }), { headers: { "content-type": "application/json" } });
}
@@ -21,20 +42,23 @@ globalThis.fetch = (async (input, init) => {
const app = serviceRoutes({ skipAuth: true });
try {
const artifacts = [
["session_dual_wav", "audio/wav", [0x52, 0x49, 0x46, 0x46], "wav"],
["session_mixed_mp3", "audio/mpeg", [0x49, 0x44, 0x33, 0x04], "mp3"],
["session_video", "video/webm", [0x1a, 0x45, 0xdf, 0xa3], "webm"],
["session_video_mp4", "video/mp4", [0x00, 0x00, 0x00, 0x18], "mp4"],
] as const;
for (const product of ["interview", "roleplay"] as const) {
const download = await app.request(`http://backend.test/${product}/artifacts/sess-1/session_video/download`);
assert.equal(download.status, 200);
const downloadedBytes = new Uint8Array(await download.arrayBuffer());
assert.deepEqual(Array.from(downloadedBytes), [1, 2, 3]);
assert.equal(download.headers.get("content-type"), "video/webm");
assert.equal(new TextDecoder().decode(downloadedBytes).includes("s3.test"), false);
const uploadBody = new Uint8Array([9, 8, 7]);
const upload = await app.request(`http://backend.test/${product}/sessions/sess-1/video/upload`, {
method: "PUT",
headers: { "content-type": "video/webm" },
body: uploadBody,
});
for (const [artifactType, mime, expectedBytes, extension] of artifacts) {
const download = await app.request(`http://backend.test/${product}/artifacts/sess-1/${artifactType}/download`);
assert.equal(download.status, 200);
const downloadedBytes = new Uint8Array(await download.arrayBuffer());
assert.deepEqual(Array.from(downloadedBytes), expectedBytes);
assert.equal(download.headers.get("content-type"), mime);
assert.match(download.headers.get("content-disposition") ?? "", new RegExp(`^attachment; filename="${artifactType}\\.${extension}"$`));
assert.ok(downloadedBytes.byteLength > 0);
}
const upload = await app.request(`http://backend.test/${product}/sessions/sess-1/video/upload`, { method: "PUT", headers: { "content-type": "video/webm" }, body: new Uint8Array([9, 8, 7]) });
assert.equal(upload.status, 204);
const put = requests.at(-1)!;
assert.equal(put.method, "PUT");
@@ -43,6 +67,7 @@ try {
}
} finally {
globalThis.fetch = originalFetch;
storageServer.close();
}
console.log("artifact transport gateway tests passed");
@@ -97,6 +97,7 @@ if (!databaseAvailable) {
category: "mission" as const,
occurredAt: new Date(Date.now() + index),
dedupeKey: `${userId}:curator.task.completed:${task.id}`,
correlation: { task_id: task.id },
payload: { taskId: task.id },
})));
+18 -2
View File
@@ -788,6 +788,20 @@ async function proxySocialRequest(req: Request, rest: string, userId: string) {
});
}
function artifactExtension(mimeType: string | undefined): string {
const normalized = mimeType?.split(";", 1)[0].trim().toLowerCase();
const extensions: Record<string, string> = {
"audio/mpeg": "mp3",
"audio/mp3": "mp3",
"audio/wav": "wav",
"audio/x-wav": "wav",
"video/webm": "webm",
"video/mp4": "mp4",
"application/json": "json",
};
return extensions[normalized ?? ""] ?? "bin";
}
async function streamServiceArtifact(input: { service: "interview" | "roleplay"; sessionId: string; artifactType: string; userId: string; request: Request }) {
const metadata = input.service === "interview"
? await interviewService.artifact(input.sessionId, input.artifactType, input.userId)
@@ -806,8 +820,10 @@ async function streamServiceArtifact(input: { service: "interview" | "roleplay";
const value = upstream.headers.get(name);
if (value) responseHeaders.set(name, value);
}
if (!responseHeaders.has("content-type") && isRecord(metadata) && typeof metadata.mime_type === "string") responseHeaders.set("content-type", metadata.mime_type);
responseHeaders.set("content-disposition", `inline; filename="${input.artifactType}"`);
const mimeType = responseHeaders.get("content-type") ?? (isRecord(metadata) ? getString(metadata.mime_type) : undefined);
if (mimeType && !responseHeaders.has("content-type")) responseHeaders.set("content-type", mimeType);
const filename = `${input.artifactType}.${artifactExtension(mimeType)}`;
responseHeaders.set("content-disposition", `attachment; filename="${filename}"`);
responseHeaders.set("cache-control", "private, max-age=300");
return new Response(upstream.body, { status: upstream.status, headers: responseHeaders });
}