refactor(api): extract country procedures

This commit is contained in:
2026-03-31 20:25:11 +02:00
parent e375d634f6
commit a22dee6d25
4 changed files with 618 additions and 215 deletions
@@ -0,0 +1,156 @@
import { beforeEach, describe, expect, it, vi } from "vitest";
const { createAuditEntry } = vi.hoisted(() => ({
createAuditEntry: vi.fn(),
}));
vi.mock("../lib/audit.js", () => ({
createAuditEntry,
}));
import {
createCountry,
deleteMetroCity,
updateCountry,
} from "../router/country-procedure-support.js";
function createContext(db: Record<string, unknown>) {
return {
db: db as never,
dbUser: { id: "user_admin" } as never,
};
}
describe("country procedure support", () => {
beforeEach(() => {
createAuditEntry.mockReset();
});
it("creates a country after checking code uniqueness", async () => {
const findUnique = vi.fn().mockResolvedValue(null);
const create = vi.fn().mockResolvedValue({
id: "country_de",
code: "DE",
name: "Germany",
metroCities: [],
});
const result = await createCountry(
createContext({
country: { findUnique, create },
}),
{
code: "DE",
name: "Germany",
dailyWorkingHours: 8,
},
);
expect(findUnique).toHaveBeenCalledWith({
where: { code: "DE" },
});
expect(create).toHaveBeenCalledWith({
data: {
code: "DE",
name: "Germany",
dailyWorkingHours: 8,
},
include: { metroCities: true },
});
expect(result.id).toBe("country_de");
expect(createAuditEntry).toHaveBeenCalledWith(
expect.objectContaining({
entityType: "Country",
action: "CREATE",
entityId: "country_de",
userId: "user_admin",
}),
);
});
it("rechecks code uniqueness only when the code changes", async () => {
const findUnique = vi
.fn()
.mockResolvedValueOnce({
id: "country_de",
code: "DE",
name: "Germany",
})
.mockResolvedValueOnce(null);
const update = vi.fn().mockResolvedValue({
id: "country_de",
code: "DEU",
name: "Germany",
metroCities: [],
});
const result = await updateCountry(
createContext({
country: { findUnique, update },
}),
{
id: "country_de",
data: {
code: "DEU",
},
},
);
expect(findUnique).toHaveBeenNthCalledWith(1, {
where: { id: "country_de" },
});
expect(findUnique).toHaveBeenNthCalledWith(2, {
where: { code: "DEU" },
});
expect(update).toHaveBeenCalledWith({
where: { id: "country_de" },
data: { code: "DEU" },
include: { metroCities: true },
});
expect(result.code).toBe("DEU");
expect(createAuditEntry).toHaveBeenCalledWith(
expect.objectContaining({
entityType: "Country",
action: "UPDATE",
before: expect.objectContaining({ code: "DE" }),
after: expect.objectContaining({ code: "DEU" }),
}),
);
});
it("deletes a metro city only after the resource-count guard passes", async () => {
const findUnique = vi.fn().mockResolvedValue({
id: "city_berlin",
name: "Berlin",
_count: { resources: 0 },
});
const remove = vi.fn().mockResolvedValue({ id: "city_berlin" });
const result = await deleteMetroCity(
createContext({
metroCity: { findUnique, delete: remove },
}),
{ id: "city_berlin" },
);
expect(findUnique).toHaveBeenCalledWith({
where: { id: "city_berlin" },
include: { _count: { select: { resources: true } } },
});
expect(remove).toHaveBeenCalledWith({
where: { id: "city_berlin" },
});
expect(result).toEqual({
success: true,
id: "city_berlin",
name: "Berlin",
});
expect(createAuditEntry).toHaveBeenCalledWith(
expect.objectContaining({
entityType: "MetroCity",
action: "DELETE",
entityId: "city_berlin",
}),
);
});
});
@@ -0,0 +1,152 @@
import { PermissionKey, SystemRole } from "@capakraken/shared";
import { beforeEach, describe, expect, it, vi } from "vitest";
const { createAuditEntry } = vi.hoisted(() => ({
createAuditEntry: vi.fn(),
}));
vi.mock("../lib/audit.js", () => ({
createAuditEntry,
}));
import { countryRouter } from "../router/country.js";
import { createCallerFactory } from "../trpc.js";
const createCaller = createCallerFactory(countryRouter);
function createProtectedCaller(
db: Record<string, unknown>,
options: {
role?: SystemRole;
granted?: PermissionKey[];
} = {},
) {
const { role = SystemRole.USER, granted = [] } = options;
return createCaller({
session: {
user: { email: "user@example.com", name: "User", image: null },
expires: "2099-01-01T00:00:00.000Z",
},
db: db as never,
dbUser: {
id: role === SystemRole.ADMIN ? "user_admin" : "user_1",
systemRole: role,
permissionOverrides: granted.length > 0 ? { granted } : null,
},
});
}
describe("country router", () => {
beforeEach(() => {
createAuditEntry.mockReset();
});
it("lists countries through the protected router", async () => {
const findMany = vi.fn().mockResolvedValue([
{ id: "country_de", code: "DE", name: "Germany", metroCities: [] },
]);
const caller = createProtectedCaller({
country: { findMany },
});
const result = await caller.list({ isActive: true });
expect(findMany).toHaveBeenCalledWith({
where: { isActive: true },
include: { metroCities: { orderBy: { name: "asc" } } },
orderBy: { name: "asc" },
});
expect(result).toHaveLength(1);
});
it("allows detailed country reads with resource-overview permission", async () => {
const findFirst = vi.fn().mockResolvedValue({
id: "country_de",
code: "DE",
name: "Germany",
isActive: true,
dailyWorkingHours: 8,
scheduleRules: null,
metroCities: [{ id: "city_berlin", name: "Berlin", countryId: "country_de" }],
_count: { resources: 4 },
});
const caller = createProtectedCaller({
country: {
findUnique: vi.fn().mockResolvedValue(null),
findFirst,
},
}, {
granted: [PermissionKey.VIEW_ALL_RESOURCES],
});
const result = await caller.getByIdentifier({ identifier: "DE" });
expect(findFirst).toHaveBeenCalledWith({
where: { code: { equals: "DE", mode: "insensitive" } },
include: {
metroCities: { orderBy: { name: "asc" } },
_count: { select: { resources: true } },
},
});
expect(result._count.resources).toBe(4);
});
it("creates and updates countries through the admin router", async () => {
const findUnique = vi
.fn()
.mockResolvedValueOnce(null)
.mockResolvedValueOnce({
id: "country_de",
code: "DE",
name: "Germany",
});
const create = vi.fn().mockResolvedValue({
id: "country_de",
code: "DE",
name: "Germany",
metroCities: [],
});
const update = vi.fn().mockResolvedValue({
id: "country_de",
code: "DE",
name: "Deutschland",
metroCities: [],
});
const caller = createProtectedCaller({
country: { findUnique, create, update },
}, {
role: SystemRole.ADMIN,
});
const created = await caller.create({
code: "DE",
name: "Germany",
dailyWorkingHours: 8,
});
const updated = await caller.update({
id: "country_de",
data: {
name: "Deutschland",
},
});
expect(create).toHaveBeenCalledWith({
data: {
code: "DE",
name: "Germany",
dailyWorkingHours: 8,
},
include: { metroCities: true },
});
expect(update).toHaveBeenCalledWith({
where: { id: "country_de" },
data: { name: "Deutschland" },
include: { metroCities: true },
});
expect(created.id).toBe("country_de");
expect(updated.name).toBe("Deutschland");
expect(createAuditEntry).toHaveBeenCalledTimes(2);
});
});