Browse Source

test(22-01): add API route tests for parameters field

Add tests verifying the parameters field (UI settings) is passed to
provider APIs:
- Replicate: parameters passed to prediction input
- Replicate: dynamicInputs take precedence when merging
- fal.ai: parameters passed to request body
- fal.ai: dynamicInputs take precedence when merging

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
handoff-20260429-1057
shrimbly 6 months ago
parent
commit
0f3f9e50e1
  1. 233
      src/app/api/generate/__tests__/route.test.ts

233
src/app/api/generate/__tests__/route.test.ts

@ -1418,6 +1418,131 @@ describe("/api/generate route", () => {
}) })
); );
}); });
it("should pass parameters to prediction input", async () => {
// Model info fetch
mockFetch.mockResolvedValueOnce({
ok: true,
json: () => Promise.resolve({
latest_version: { id: "version123", openapi_schema: {} },
}),
});
// Create prediction
mockFetch.mockResolvedValueOnce({
ok: true,
json: () => Promise.resolve({
id: "prediction123",
status: "succeeded",
output: ["https://replicate.delivery/output.png"],
}),
});
// Fetch output media
mockFetch.mockResolvedValueOnce({
ok: true,
headers: new Headers({ "content-type": "image/png" }),
arrayBuffer: () => Promise.resolve(new ArrayBuffer(1024)),
});
const request = createMockPostRequest(
{
prompt: "Test prompt",
selectedModel: {
provider: "replicate",
modelId: "stability-ai/sdxl",
displayName: "SDXL",
},
parameters: {
seed: 42,
num_inference_steps: 30,
guidance_scale: 7.5,
},
},
{ "X-Replicate-API-Key": "test-replicate-key" }
);
const response = await POST(request);
const data = await response.json();
expect(response.status).toBe(200);
expect(data.success).toBe(true);
// Verify parameters were passed to prediction
const createPredictionCall = mockFetch.mock.calls[1];
const requestBody = JSON.parse(createPredictionCall[1].body);
expect(requestBody.input).toEqual(
expect.objectContaining({
seed: 42,
num_inference_steps: 30,
guidance_scale: 7.5,
})
);
});
it("should merge parameters with dynamicInputs (dynamicInputs take precedence)", async () => {
// Model info fetch
mockFetch.mockResolvedValueOnce({
ok: true,
json: () => Promise.resolve({
latest_version: { id: "version123", openapi_schema: {} },
}),
});
// Create prediction
mockFetch.mockResolvedValueOnce({
ok: true,
json: () => Promise.resolve({
id: "prediction123",
status: "succeeded",
output: ["https://replicate.delivery/output.png"],
}),
});
// Fetch output media
mockFetch.mockResolvedValueOnce({
ok: true,
headers: new Headers({ "content-type": "image/png" }),
arrayBuffer: () => Promise.resolve(new ArrayBuffer(1024)),
});
const request = createMockPostRequest(
{
prompt: "",
selectedModel: {
provider: "replicate",
modelId: "stability-ai/sdxl",
displayName: "SDXL",
},
parameters: {
seed: 42,
guidance_scale: 7.5,
},
dynamicInputs: {
prompt: "Dynamic prompt",
guidance_scale: "10.0", // Should override parameters
},
},
{ "X-Replicate-API-Key": "test-replicate-key" }
);
const response = await POST(request);
const data = await response.json();
expect(response.status).toBe(200);
expect(data.success).toBe(true);
// Verify dynamicInputs override parameters
const createPredictionCall = mockFetch.mock.calls[1];
const requestBody = JSON.parse(createPredictionCall[1].body);
expect(requestBody.input).toEqual(
expect.objectContaining({
seed: 42,
prompt: "Dynamic prompt",
guidance_scale: "10.0", // dynamicInputs value
})
);
});
}); });
describe("fal.ai provider", () => { describe("fal.ai provider", () => {
@ -1923,6 +2048,114 @@ describe("/api/generate route", () => {
); );
}); });
it("should pass parameters to fal.ai request body", async () => {
// fal.run API call
mockFetch.mockResolvedValueOnce({
ok: true,
json: () => Promise.resolve({
images: [{ url: "https://fal.media/output.png" }],
}),
});
// Fetch output media
mockFetch.mockResolvedValueOnce({
ok: true,
headers: new Headers({ "content-type": "image/png" }),
arrayBuffer: () => Promise.resolve(new ArrayBuffer(1024)),
});
const request = createMockPostRequest(
{
prompt: "",
selectedModel: {
provider: "fal",
modelId: "fal-ai/flux/schnell",
displayName: "Flux Schnell",
},
dynamicInputs: {
prompt: "Test prompt",
},
parameters: {
seed: 12345,
num_inference_steps: 28,
guidance_scale: 3.5,
},
},
{ "X-Fal-API-Key": "test-fal-key" }
);
const response = await POST(request);
const data = await response.json();
expect(response.status).toBe(200);
expect(data.success).toBe(true);
// Verify parameters were passed to fal.ai
const falCall = mockFetch.mock.calls[0];
const requestBody = JSON.parse(falCall[1].body);
expect(requestBody).toEqual(
expect.objectContaining({
seed: 12345,
num_inference_steps: 28,
guidance_scale: 3.5,
})
);
});
it("should merge parameters with dynamicInputs (dynamicInputs take precedence)", async () => {
// fal.run API call
mockFetch.mockResolvedValueOnce({
ok: true,
json: () => Promise.resolve({
images: [{ url: "https://fal.media/output.png" }],
}),
});
// Fetch output media
mockFetch.mockResolvedValueOnce({
ok: true,
headers: new Headers({ "content-type": "image/png" }),
arrayBuffer: () => Promise.resolve(new ArrayBuffer(1024)),
});
const request = createMockPostRequest(
{
prompt: "",
selectedModel: {
provider: "fal",
modelId: "fal-ai/flux/schnell",
displayName: "Flux Schnell",
},
parameters: {
seed: 42,
num_inference_steps: 25,
},
dynamicInputs: {
prompt: "Dynamic prompt",
num_inference_steps: "30", // Should override parameters
},
},
{ "X-Fal-API-Key": "test-fal-key" }
);
const response = await POST(request);
const data = await response.json();
expect(response.status).toBe(200);
expect(data.success).toBe(true);
// Verify dynamicInputs override parameters
const falCall = mockFetch.mock.calls[0];
const requestBody = JSON.parse(falCall[1].body);
expect(requestBody).toEqual(
expect.objectContaining({
seed: 42,
prompt: "Dynamic prompt",
num_inference_steps: "30", // dynamicInputs value
})
);
});
it("should handle error response with error.message format", async () => { it("should handle error response with error.message format", async () => {
// Schema fetch (for input mapping when no dynamicInputs) // Schema fetch (for input mapping when no dynamicInputs)
mockFetch.mockResolvedValueOnce({ mockFetch.mockResolvedValueOnce({

Loading…
Cancel
Save