evOo-local/apps/api/src/bot-update.test.ts
cynarAI 6c7eed7580 Initial clean evOo-local from f00577b
Fresh history cut from cynarAI/evoo antigravity-handoff/project-timeline
(Voice First + project timeline). Brand docs/titles as evOo; keep @rakazo/*
packages as tech debt. Excludes node_modules, caches, secrets, voice blobs.
2026-09-20 17:15:16 +02:00

142 lines
4.1 KiB
TypeScript

import { describe, expect, it, vi } from "vitest";
vi.mock("@rakazo/db", () => ({
appendEventInTransaction: vi.fn(),
}));
import { botProfileLabelsChanged, commitBotUpdate } from "./bot-update.js";
describe("botProfileLabelsChanged", () => {
it("is true only when name, title, or description is present", () => {
expect(botProfileLabelsChanged({})).toBe(false);
expect(botProfileLabelsChanged({ name: "SEO" })).toBe(true);
expect(botProfileLabelsChanged({ title: "Strategist" })).toBe(true);
expect(botProfileLabelsChanged({ description: "Helps with SEO" })).toBe(true);
});
});
describe("commitBotUpdate", () => {
it("writes the bot row and bot.updated in one transaction when labels change", async () => {
const updated = {
id: "bot-1",
name: "SEO Strategist",
title: "SEO Strategist",
description: "Helps with keyword research",
};
const botUpdate = vi.fn().mockResolvedValue(updated);
const tx = { bot: { update: botUpdate } };
const transaction = vi.fn(async (run: (client: typeof tx) => Promise<unknown>) => run(tx));
const notify = vi.fn().mockResolvedValue(undefined);
const appendEvent = vi.fn().mockResolvedValue({ seq: 9 });
const prisma = {
$transaction: transaction,
bot: { update: vi.fn() },
};
await expect(
commitBotUpdate(
{
prisma: prisma as never,
notify,
spaceId: "space-1",
threadId: "thread-1",
botId: "bot-1",
data: { name: "SEO Strategist", title: "SEO Strategist" },
emitBotUpdated: true,
},
appendEvent,
),
).resolves.toEqual(updated);
expect(transaction).toHaveBeenCalledOnce();
expect(botUpdate).toHaveBeenCalledOnce();
expect(appendEvent).toHaveBeenCalledWith(tx, {
spaceId: "space-1",
threadId: "thread-1",
botId: "bot-1",
type: "bot.updated",
payload: {
botId: "bot-1",
name: "SEO Strategist",
title: "SEO Strategist",
description: "Helps with keyword research",
},
});
expect(notify).toHaveBeenCalledWith("thread-1", 9);
expect(prisma.bot.update).not.toHaveBeenCalled();
});
it("fails the whole update when the durable event write fails", async () => {
const appendEvent = vi.fn().mockRejectedValue(new Error("event store unavailable"));
const transaction = vi.fn(async (run: (client: unknown) => Promise<unknown>) =>
run({
bot: {
update: vi.fn().mockResolvedValue({
id: "bot-1",
name: "SEO",
title: "SEO",
description: "",
}),
},
}),
);
const notify = vi.fn();
const prisma = {
$transaction: transaction,
bot: { update: vi.fn() },
};
await expect(
commitBotUpdate(
{
prisma: prisma as never,
notify,
spaceId: "space-1",
threadId: "thread-1",
botId: "bot-1",
data: { name: "SEO" },
emitBotUpdated: true,
},
appendEvent,
),
).rejects.toThrow("event store unavailable");
expect(notify).not.toHaveBeenCalled();
expect(prisma.bot.update).not.toHaveBeenCalled();
});
it("updates without an event when profile labels are unchanged", async () => {
const update = vi.fn().mockResolvedValue({
id: "bot-1",
name: "Chief",
title: "Chief",
description: "",
});
const prisma = {
$transaction: vi.fn(),
bot: { update },
};
const notify = vi.fn();
const appendEvent = vi.fn();
await expect(
commitBotUpdate(
{
prisma: prisma as never,
notify,
spaceId: "space-1",
threadId: "thread-1",
botId: "bot-1",
data: { pinned: true },
emitBotUpdated: false,
},
appendEvent,
),
).resolves.toMatchObject({ id: "bot-1" });
expect(prisma.$transaction).not.toHaveBeenCalled();
expect(notify).not.toHaveBeenCalled();
expect(appendEvent).not.toHaveBeenCalled();
expect(update).toHaveBeenCalledOnce();
});
});