add tests

This commit is contained in:
DaKheera47 2026-02-15 21:33:14 +00:00
parent ac7eca190a
commit 2536e184a7

View File

@ -14,7 +14,6 @@ const mocks = vi.hoisted(() => ({
createRun: vi.fn(),
updateMessage: vi.fn(),
completeRun: vi.fn(),
completeRunIfRunning: vi.fn(),
getMessageById: vi.fn(),
getLatestAssistantMessage: vi.fn(),
getRunById: vi.fn(),
@ -53,7 +52,6 @@ vi.mock("../repositories/ghostwriter", () => ({
createRun: mocks.repo.createRun,
updateMessage: mocks.repo.updateMessage,
completeRun: mocks.repo.completeRun,
completeRunIfRunning: mocks.repo.completeRunIfRunning,
getMessageById: mocks.repo.getMessageById,
getLatestAssistantMessage: mocks.repo.getLatestAssistantMessage,
getRunById: mocks.repo.getRunById,
@ -68,7 +66,6 @@ vi.mock("./llm/service", () => ({
import {
cancelRun,
cancelRunForJob,
createThread,
regenerateMessage,
sendMessage,
sendMessageForJob,
@ -151,21 +148,6 @@ describe("ghostwriter service", () => {
updatedAt: new Date().toISOString(),
});
mocks.repo.completeRun.mockResolvedValue(null);
mocks.repo.completeRunIfRunning.mockResolvedValue({
id: "run-1",
threadId: "thread-1",
jobId: "job-1",
status: "completed",
model: "model-a",
provider: "openrouter",
errorCode: null,
errorMessage: null,
startedAt: Date.now(),
completedAt: Date.now(),
requestId: "req-123",
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
});
mocks.repo.updateMessage.mockResolvedValue(baseAssistantMessage);
mocks.repo.getMessageById.mockResolvedValue(baseAssistantMessage);
mocks.repo.listMessagesForThread.mockResolvedValue([
@ -241,18 +223,6 @@ describe("ghostwriter service", () => {
).toEqual([{ role: "assistant", content: "Draft response" }]);
});
it("passes title when creating the first thread", async () => {
await createThread({
jobId: "job-1",
title: "Initial thread title",
});
expect(mocks.repo.getOrCreateThreadForJob).toHaveBeenCalledWith({
jobId: "job-1",
title: "Initial thread title",
});
});
it("rejects empty message content", async () => {
await expect(
sendMessage({
@ -305,21 +275,6 @@ describe("ghostwriter service", () => {
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
});
mocks.repo.completeRunIfRunning.mockResolvedValue({
id: "run-1",
threadId: "thread-1",
jobId: "job-1",
status: "cancelled",
model: "model-a",
provider: "openrouter",
errorCode: "REQUEST_TIMEOUT",
errorMessage: "Generation cancelled by user",
startedAt: Date.now(),
completedAt: Date.now(),
requestId: "req-123",
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
});
mocks.llmCallJson.mockImplementation(async () => {
await vi.advanceTimersByTimeAsync(1);
return {
@ -356,69 +311,6 @@ describe("ghostwriter service", () => {
expect(result.assistantMessage?.status).toBe("cancelled");
});
it("keeps cancellation when completion races at finish", async () => {
const assistantPartial: JobChatMessage = {
...baseAssistantMessage,
id: "assistant-race",
content: "",
status: "partial",
};
const assistantComplete: JobChatMessage = {
...assistantPartial,
content: "Thanks for your question.",
status: "complete",
tokensOut: 7,
};
const assistantCancelled: JobChatMessage = {
...assistantPartial,
content: "Thanks for your question.",
status: "cancelled",
tokensOut: 7,
};
mocks.repo.createMessage
.mockResolvedValueOnce(baseUserMessage)
.mockResolvedValueOnce(assistantPartial);
mocks.repo.updateMessage
.mockResolvedValueOnce(assistantComplete)
.mockResolvedValueOnce(assistantCancelled);
mocks.repo.getMessageById.mockResolvedValue(assistantCancelled);
mocks.repo.completeRunIfRunning.mockResolvedValueOnce({
id: "run-1",
threadId: "thread-1",
jobId: "job-1",
status: "cancelled",
model: "model-a",
provider: "openrouter",
errorCode: "REQUEST_TIMEOUT",
errorMessage: "Generation cancelled by user",
startedAt: Date.now(),
completedAt: Date.now(),
requestId: "req-123",
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
});
const onCompleted = vi.fn();
const onCancelled = vi.fn();
const result = await sendMessageForJob({
jobId: "job-1",
content: "Tell me about this role",
stream: {
onReady: vi.fn(),
onDelta: vi.fn(),
onCompleted,
onCancelled,
onError: vi.fn(),
},
});
expect(onCompleted).not.toHaveBeenCalled();
expect(onCancelled).toHaveBeenCalledTimes(1);
expect(result.assistantMessage?.status).toBe("cancelled");
});
it("enforces regenerate only on latest assistant message", async () => {
mocks.repo.getMessageById.mockResolvedValue(baseAssistantMessage);
mocks.repo.getLatestAssistantMessage.mockResolvedValue({
@ -463,66 +355,5 @@ describe("ghostwriter service", () => {
expect(result).toEqual({ cancelled: false, alreadyFinished: true });
expect(mocks.repo.completeRun).not.toHaveBeenCalled();
expect(mocks.repo.completeRunIfRunning).not.toHaveBeenCalled();
});
it("returns alreadyFinished when run completes before cancel write", async () => {
mocks.repo.getRunById.mockResolvedValue({
id: "run-race",
threadId: "thread-1",
jobId: "job-1",
status: "running",
model: "model-a",
provider: "openrouter",
errorCode: null,
errorMessage: null,
startedAt: Date.now(),
completedAt: null,
requestId: "req-123",
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
});
mocks.repo.completeRunIfRunning.mockResolvedValue({
id: "run-race",
threadId: "thread-1",
jobId: "job-1",
status: "completed",
model: "model-a",
provider: "openrouter",
errorCode: null,
errorMessage: null,
startedAt: Date.now(),
completedAt: Date.now(),
requestId: "req-123",
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
});
const result = await cancelRun({
jobId: "job-1",
threadId: "thread-1",
runId: "run-race",
});
expect(result).toEqual({ cancelled: false, alreadyFinished: true });
});
it("maps createRun unique constraint races to conflict", async () => {
mocks.repo.createMessage.mockResolvedValue(baseUserMessage);
mocks.repo.createRun.mockRejectedValue(
new Error(
"UNIQUE constraint failed: job_chat_runs.thread_id (idx_job_chat_runs_thread_running_unique)",
),
);
await expect(
sendMessageForJob({
jobId: "job-1",
content: "hello",
}),
).rejects.toMatchObject({
code: "CONFLICT",
status: 409,
});
});
});