test: clear music generate broad matchers

This commit is contained in:
Peter Steinberger
2026-05-10 17:24:27 +01:00
parent af9fdb5095
commit 679fe96469
+117 -110
View File
@@ -124,6 +124,8 @@ function expectMusicGenerateTool(
function resetMusicGenerateMocks() {
vi.restoreAllMocks();
vi.spyOn(musicGenerationRuntime, "listRuntimeMusicGenerationProviders").mockReturnValue([]);
musicGenerationRuntimeMocks.generateMusic.mockReset();
mediaStoreMocks.saveMediaBuffer.mockReset();
taskRuntimeInternalMocks.listTasksForOwnerKey.mockReset();
taskRuntimeInternalMocks.listTasksForOwnerKey.mockReturnValue([]);
taskExecutorMocks.createRunningTaskRun.mockReset();
@@ -132,6 +134,43 @@ function resetMusicGenerateMocks() {
taskExecutorMocks.recordTaskRunProgressByRunId.mockReset();
}
function detailsOf(result: { details?: unknown }): Record<string, unknown> {
expect(typeof result.details).toBe("object");
expect(result.details).not.toBeNull();
return result.details as Record<string, unknown>;
}
function generateMusicOptions(
callIndex = musicGenerationRuntimeMocks.generateMusic.mock.calls.length - 1,
): Record<string, unknown> {
const options = musicGenerationRuntimeMocks.generateMusic.mock.calls[callIndex]?.[0];
expect(typeof options).toBe("object");
expect(options).not.toBeNull();
return options as Record<string, unknown>;
}
function taskProgressCall(callIndex = 0): Record<string, unknown> {
const call = taskExecutorMocks.recordTaskRunProgressByRunId.mock.calls[callIndex]?.[0];
expect(typeof call).toBe("object");
expect(call).not.toBeNull();
return call as Record<string, unknown>;
}
function taskCompleteCall(callIndex = 0): Record<string, unknown> {
const call = taskExecutorMocks.completeTaskRunByRunId.mock.calls[callIndex]?.[0];
expect(typeof call).toBe("object");
expect(call).not.toBeNull();
return call as Record<string, unknown>;
}
function wakeCompletionCall(callIndex = 0): Record<string, unknown> {
const call =
musicGenerateBackgroundMocks.wakeMusicGenerationTaskCompletion.mock.calls[callIndex]?.[0];
expect(typeof call).toBe("object");
expect(call).not.toBeNull();
return call as Record<string, unknown>;
}
describe("createMusicGenerateTool", () => {
beforeEach(resetMusicGenerateMocks);
@@ -222,24 +261,16 @@ describe("createMusicGenerateTool", () => {
throw new Error("expected music_generate tool");
}
await expect(
tool.execute("call-1", {
prompt: "night-drive synthwave",
instrumental: true,
}),
).resolves.toMatchObject({
details: {
instrumental: true,
provider: "google",
paths: ["/tmp/generated-night-drive.mp3"],
},
const result = await tool.execute("call-1", {
prompt: "night-drive synthwave",
instrumental: true,
});
const details = detailsOf(result);
expect(details.instrumental).toBe(true);
expect(details.provider).toBe("google");
expect(details.paths).toEqual(["/tmp/generated-night-drive.mp3"]);
expect(listProviders).not.toHaveBeenCalled();
expect(musicGenerationRuntime.generateMusic).toHaveBeenCalledWith(
expect.objectContaining({
autoProviderFallback: false,
}),
);
expect(generateMusicOptions().autoProviderFallback).toBe(false);
});
it("generates tracks, saves them, and emits MEDIA paths without a session-backed detach", async () => {
@@ -308,18 +339,17 @@ describe("createMusicGenerateTool", () => {
expect(text).toContain("Generated 1 track with google/lyria-3-clip-preview.");
expect(text).toContain("Lyrics returned.");
expect(text).toContain("MEDIA:/tmp/generated-night-drive.mp3");
expect(result.details).toMatchObject({
provider: "google",
model: "lyria-3-clip-preview",
count: 1,
instrumental: true,
lyrics: ["wake the city up"],
media: {
mediaUrls: ["/tmp/generated-night-drive.mp3"],
},
paths: ["/tmp/generated-night-drive.mp3"],
metadata: { taskId: "music-task-1" },
});
const details = detailsOf(result);
expect(details.provider).toBe("google");
expect(details.model).toBe("lyria-3-clip-preview");
expect(details.count).toBe(1);
expect(details.instrumental).toBe(true);
expect(details.lyrics).toEqual(["wake the city up"]);
expect((details.media as { mediaUrls?: unknown }).mediaUrls).toEqual([
"/tmp/generated-night-drive.mp3",
]);
expect(details.paths).toEqual(["/tmp/generated-night-drive.mp3"]);
expect(details.metadata).toEqual({ taskId: "music-task-1" });
expect(taskExecutorMocks.createRunningTaskRun).not.toHaveBeenCalled();
expect(taskExecutorMocks.completeTaskRunByRunId).not.toHaveBeenCalled();
});
@@ -364,21 +394,17 @@ describe("createMusicGenerateTool", () => {
});
const text = (result.content?.[0] as { text: string } | undefined)?.text ?? "";
expect(generateSpy).toHaveBeenCalledWith(
expect.objectContaining({
autoProviderFallback: false,
timeoutMs: 10_000,
}),
);
expect(generateSpy).toHaveBeenCalledTimes(1);
expect(generateMusicOptions().autoProviderFallback).toBe(false);
expect(generateMusicOptions().timeoutMs).toBe(10_000);
expect(text).toContain("Timeout normalized: requested 1000ms; used 10000ms.");
expect(result.details).toMatchObject({
timeoutMs: 10_000,
requestedTimeoutMs: 1000,
timeoutNormalization: {
requested: 1000,
applied: 10_000,
minimum: 10_000,
},
const details = detailsOf(result);
expect(details.timeoutMs).toBe(10_000);
expect(details.requestedTimeoutMs).toBe(1000);
expect(details.timeoutNormalization).toEqual({
requested: 1000,
applied: 10_000,
minimum: 10_000,
});
});
@@ -451,51 +477,33 @@ describe("createMusicGenerateTool", () => {
expect(text).toContain("Background task started for music generation (task-123).");
expect(text).toContain("Do not call music_generate again for this request.");
expect(text).toContain("Timeout normalized: requested 1000ms; used 10000ms.");
expect(result.details).toMatchObject({
async: true,
status: "started",
task: {
taskId: "task-123",
},
instrumental: true,
timeoutMs: 10_000,
requestedTimeoutMs: 1000,
timeoutNormalization: {
requested: 1000,
applied: 10_000,
minimum: 10_000,
},
const details = detailsOf(result);
expect(details.async).toBe(true);
expect(details.status).toBe("started");
expect((details.task as { taskId?: unknown }).taskId).toBe("task-123");
expect(details.instrumental).toBe(true);
expect(details.timeoutMs).toBe(10_000);
expect(details.requestedTimeoutMs).toBe(1000);
expect(details.timeoutNormalization).toEqual({
requested: 1000,
applied: 10_000,
minimum: 10_000,
});
if (!scheduledWork) {
throw new Error("expected scheduled music generation work");
}
await scheduledWork();
expect(musicGenerationRuntime.generateMusic).toHaveBeenCalledWith(
expect.objectContaining({
autoProviderFallback: false,
timeoutMs: 10_000,
}),
);
expect(taskExecutorMocks.recordTaskRunProgressByRunId).toHaveBeenCalledWith(
expect.objectContaining({
runId: expect.stringMatching(/^tool:music_generate:/),
progressSummary: "Generating music",
}),
);
expect(taskExecutorMocks.completeTaskRunByRunId).toHaveBeenCalledWith(
expect.objectContaining({
runId: expect.stringMatching(/^tool:music_generate:/),
}),
);
expect(wakeSpy).toHaveBeenCalledWith(
expect.objectContaining({
handle: expect.objectContaining({
taskId: "task-123",
}),
status: "ok",
result: expect.stringContaining("MEDIA:/tmp/generated-night-drive.mp3"),
}),
);
expect(generateMusicOptions().autoProviderFallback).toBe(false);
expect(generateMusicOptions().timeoutMs).toBe(10_000);
const progress = taskProgressCall();
expect(String(progress.runId)).toMatch(/^tool:music_generate:/);
expect(progress.progressSummary).toBe("Generating music");
expect(String(taskCompleteCall().runId)).toMatch(/^tool:music_generate:/);
expect(wakeSpy).toHaveBeenCalledTimes(1);
const wake = wakeCompletionCall();
expect((wake.handle as { taskId?: unknown }).taskId).toBe("task-123");
expect(wake.status).toBe("ok");
expect(wake.result).toContain("MEDIA:/tmp/generated-night-drive.mp3");
});
it("lists provider capabilities", async () => {
@@ -608,19 +616,17 @@ describe("createMusicGenerateTool", () => {
expect(text).toContain(
"Warning: Ignored unsupported overrides for google/lyria-3-clip-preview: durationSeconds=30, format=wav.",
);
expect(result).toMatchObject({
details: {
instrumental: true,
warning:
"Ignored unsupported overrides for google/lyria-3-clip-preview: durationSeconds=30, format=wav.",
ignoredOverrides: [
{ key: "durationSeconds", value: 30 },
{ key: "format", value: "wav" },
],
},
});
expect(result.details).not.toHaveProperty("durationSeconds");
expect(result.details).not.toHaveProperty("format");
const details = detailsOf(result);
expect(details.instrumental).toBe(true);
expect(details.warning).toBe(
"Ignored unsupported overrides for google/lyria-3-clip-preview: durationSeconds=30, format=wav.",
);
expect(details.ignoredOverrides).toEqual([
{ key: "durationSeconds", value: 30 },
{ key: "format", value: "wav" },
]);
expect(details).not.toHaveProperty("durationSeconds");
expect(details).not.toHaveProperty("format");
});
it("surfaces normalized durations from runtime metadata", async () => {
@@ -674,14 +680,13 @@ describe("createMusicGenerateTool", () => {
const text = (result.content?.[0] as { text: string } | undefined)?.text ?? "";
expect(text).toContain("Duration normalized: requested 45s; used 30s.");
expect(result.details).toMatchObject({
durationSeconds: 30,
requestedDurationSeconds: 45,
normalization: {
durationSeconds: {
requested: 45,
applied: 30,
},
const details = detailsOf(result);
expect(details.durationSeconds).toBe(30);
expect(details.requestedDurationSeconds).toBe(45);
expect(details.normalization).toEqual({
durationSeconds: {
requested: 45,
applied: 30,
},
});
});
@@ -737,12 +742,14 @@ describe("createMusicGenerateTool", () => {
image: "http://198.18.0.153/reference.png",
});
expect(webMedia.loadWebMedia).toHaveBeenCalledWith(
"http://198.18.0.153/reference.png",
expect.objectContaining({
requestInit: expect.objectContaining({ signal: expect.any(AbortSignal) }),
ssrfPolicy: { allowRfc2544BenchmarkRange: true },
}),
);
expect(webMedia.loadWebMedia).toHaveBeenCalledTimes(1);
const loadCall = vi.mocked(webMedia.loadWebMedia).mock.calls[0];
expect(loadCall?.[0]).toBe("http://198.18.0.153/reference.png");
const loadOptions = loadCall?.[1] as {
requestInit?: { signal?: unknown };
ssrfPolicy?: unknown;
};
expect(loadOptions.requestInit?.signal).toBeInstanceOf(AbortSignal);
expect(loadOptions.ssrfPolicy).toEqual({ allowRfc2544BenchmarkRange: true });
});
});