Fix owner card creation number allocation

This commit is contained in:
Dave 2026-09-22 21:59:15 +02:00
parent 9cb25b266e
commit d9f6ac0fad
5 changed files with 58 additions and 2 deletions

View file

@ -0,0 +1,50 @@
import { beforeEach, describe, expect, it, vi } from "vitest";
import { createCard } from "@/app/actions/cards";
import { requireSession } from "@/lib/auth/session";
import { getServerPb } from "@/lib/pb/server";
import { resolveWorkArea } from "@/lib/work-areas/server";
vi.mock("next/cache", () => ({ revalidatePath: vi.fn() }));
vi.mock("@/lib/auth/session", () => ({ requireSession: vi.fn() }));
vi.mock("@/lib/pb/server", () => ({ getServerPb: vi.fn() }));
vi.mock("@/lib/work-areas/server", () => ({ resolveWorkArea: vi.fn() }));
vi.mock("@/app/actions/activity", () => ({ logActivity: vi.fn() }));
describe("owner card creation", () => {
beforeEach(() => {
vi.clearAllMocks();
vi.mocked(requireSession).mockResolvedValue({ userId: "owner-1", role: "owner", clientId: "" });
vi.mocked(resolveWorkArea).mockResolvedValue("area-1");
});
it("assigns the next project card number when creating a card", async () => {
const create = vi.fn(async () => ({ id: "new-card" }));
const cards = {
getFirstListItem: vi.fn(async () => ({ position: 1000 })),
getFullList: vi.fn(async () => [{ number: 0 }, { number: 5 }, { number: 3 }]),
create,
};
vi.mocked(getServerPb).mockResolvedValue({ collection: () => cards } as never);
const result = await createCard({
projectId: "project-1",
columnId: "inbox-1",
title: "Prepare a delivery note",
clientSlug: "mega-infrastructure",
projectSlug: "mega-dashboard",
areaId: "area-1",
});
expect(result).toEqual({ id: "new-card" });
expect(cards.getFullList).toHaveBeenCalledWith({
filter: 'project = "project-1"',
fields: "number",
});
expect(create).toHaveBeenCalledWith(expect.objectContaining({
project: "project-1",
work_area: "area-1",
number: 6,
}));
});
});

View file

@ -58,6 +58,10 @@ export async function createCard(input: {
sort: "-position",
})
.catch(() => null);
const numbered = await pb.collection("cards").getFullList<CardRecord>({
filter: `project = "${input.projectId}"`,
fields: "number",
});
const card = await pb.collection("cards").create<CardRecord>({
project: input.projectId,
@ -71,6 +75,7 @@ export async function createCard(input: {
created_by_role: "owner",
accepted: true,
archived: false,
number: nextCardNumber(numbered.map((existing) => existing.number)),
});
await logActivity({

View file

@ -54,7 +54,7 @@ describe("parseBrief — it is a whitelist, not a parser", () => {
number: 1,
});
const brief = parseBrief(hostile, turns) as Record<string, unknown>;
const brief = parseBrief(hostile, turns) as unknown as Record<string, unknown>;
expect(brief.priority).toBeUndefined();
expect(brief.accepted).toBeUndefined();

View file

@ -42,6 +42,7 @@ function card(overrides: Partial<CardRecord> = {}): CardRecord {
updated: "2026-01-01 00:00:00.000Z",
project: "p1",
column: "col1",
work_area: "",
title: "Refactor billing modal",
description: "",
position: 0,

View file

@ -56,7 +56,7 @@ describe("a hostile brief cannot escalate", () => {
created_by_role: "owner",
});
const brief = parseBrief(hostile, []) as Record<string, unknown>;
const brief = parseBrief(hostile, []) as unknown as Record<string, unknown>;
for (const forbidden of [
"priority",