feat(tools): 2.0 phase 5 wave 3b - audio depth (14 tools) (#222)

This commit is contained in:
SnapOtter
2026-06-13 10:19:06 +08:00
parent 5f98b48593
commit 638288e196
81 changed files with 7646 additions and 718 deletions
+146 -1
View File
@@ -136,7 +136,7 @@ vi.mock("sharp", () => ({
// ── Imports ─────────────────────────────────────────────────────────────
import { waitForJob } from "../../../apps/api/src/jobs/enqueue.js";
import { enqueueToolJob, waitForJob } from "../../../apps/api/src/jobs/enqueue.js";
import { isToolInstalled } from "../../../apps/api/src/lib/feature-status.js";
import { validateImageBuffer } from "../../../apps/api/src/lib/file-validation.js";
import type { AnyToolRouteConfig } from "../../../apps/api/src/routes/tool-factory.js";
@@ -575,4 +575,149 @@ describe("createToolRoute", () => {
);
});
});
describe("multipart field recovery", () => {
it("recovers settings from part.fields when the iterator drops trailing fields", async () => {
const app = createMockApp();
const id = uniqueId();
createToolRoute(app as never, makeMockConfig(id));
const handler = app.routes[`/api/v1/tools/${id}`];
const reply = createMockReply();
// Simulate the @fastify/multipart race: the iterator yields only the
// file part; the settings field is present only on part.fields.
const req = {
parts: () => ({
[Symbol.asyncIterator]: async function* () {
yield {
type: "file",
filename: "test.png",
file: (async function* () {
yield Buffer.from("png-data");
})(),
fields: {
settings: { value: '{"x":1}' },
},
};
},
}),
log: { warn: vi.fn(), error: vi.fn(), info: vi.fn() },
};
await handler(req, reply);
// Settings must be recovered as {x:1}, not fall through to defaults ({})
const enqueueCall = vi.mocked(enqueueToolJob).mock.calls[0][0];
expect(enqueueCall.settings).toEqual({ x: 1 });
expect(reply.send).toHaveBeenCalledWith(
expect.objectContaining({ jobId: expect.any(String) }),
);
});
it("does not overwrite settings already collected from the iterator", async () => {
const app = createMockApp();
const id = uniqueId();
createToolRoute(app as never, makeMockConfig(id));
const handler = app.routes[`/api/v1/tools/${id}`];
const reply = createMockReply();
// Both the iterator and part.fields carry settings; the iterator value wins
const req = {
parts: () => ({
[Symbol.asyncIterator]: async function* () {
yield {
type: "file",
filename: "test.png",
file: (async function* () {
yield Buffer.from("png-data");
})(),
fields: {
settings: { value: '{"from":"fields"}' },
},
};
yield {
type: "field",
fieldname: "settings",
value: '{"from":"iterator"}',
file: (async function* () {})(),
fields: {
settings: { value: '{"from":"fields"}' },
},
};
},
}),
log: { warn: vi.fn(), error: vi.fn(), info: vi.fn() },
};
await handler(req, reply);
const enqueueCall = vi.mocked(enqueueToolJob).mock.calls[0][0];
expect(enqueueCall.settings).toEqual({ from: "iterator" });
});
it("recovers fileId and clientJobId from part.fields", async () => {
const app = createMockApp();
const id = uniqueId();
createToolRoute(app as never, makeMockConfig(id));
const handler = app.routes[`/api/v1/tools/${id}`];
const reply = createMockReply();
const req = {
parts: () => ({
[Symbol.asyncIterator]: async function* () {
yield {
type: "file",
filename: "test.png",
file: (async function* () {
yield Buffer.from("png-data");
})(),
fields: {
settings: { value: "{}" },
fileId: { value: "f-123" },
clientJobId: { value: "cj-456" },
},
};
},
}),
log: { warn: vi.fn(), error: vi.fn(), info: vi.fn() },
};
await handler(req, reply);
const enqueueCall = vi.mocked(enqueueToolJob).mock.calls[0][0];
expect(enqueueCall.fileId).toBe("f-123");
expect(enqueueCall.clientJobId).toBe("cj-456");
});
it("handles array-form fields from part.fields", async () => {
const app = createMockApp();
const id = uniqueId();
createToolRoute(app as never, makeMockConfig(id));
const handler = app.routes[`/api/v1/tools/${id}`];
const reply = createMockReply();
const req = {
parts: () => ({
[Symbol.asyncIterator]: async function* () {
yield {
type: "file",
filename: "test.png",
file: (async function* () {
yield Buffer.from("png-data");
})(),
fields: {
settings: [{ value: '{"arr":true}' }],
},
};
},
}),
log: { warn: vi.fn(), error: vi.fn(), info: vi.fn() },
};
await handler(req, reply);
const enqueueCall = vi.mocked(enqueueToolJob).mock.calls[0][0];
expect(enqueueCall.settings).toEqual({ arr: true });
});
});
});