import { describe, expect, it } from "vitest";
import {
loadFacilityGroupIndex,
type FacilityGroupRow,
type FacilityGroupSourcePrisma,
} from "@/lib/db/facility-group-source";
/**
* 指示書AK 段階3 ★§6-3 / 罠7 / 落とし穴3・14★ —
* 分類(ホールディングス/屋号)の解決用の読み取り。
*
* ★★`deleted_at` で絞らない★★: `cleanLogs()` は論理削除された施設のログも返している
* (`include: { facility: true }` に条件が無い)。ここで絞ると
* ★屋号で絞った瞬間にそのログが静かに消える★(★件数が減るだけで例外も出ない★)。
* ★`loadFacilityNameIndex()` と扱いを揃えてある★。
*
* ★★ローカルDBでは検出できません★★ — 実測で `facilities` 12件のうち論理削除は0件。
* ★だから合成データで固定する★(`facility-name-source.test.ts` と同じ理由)。
*/
/** `findMany()` に渡された引数を控えるスタブ(★DBに接続しない★) */
function stubPrisma(rows: readonly FacilityGroupRow[]): {
prisma: FacilityGroupSourcePrisma;
calls: unknown[];
} {
const calls: unknown[] = [];
const prisma: FacilityGroupSourcePrisma = {
facility: {
async findMany(args) {
calls.push(args);
return [...rows];
},
},
};
return { prisma, calls };
}
function row(id: number, customerId: number, groupId: number): FacilityGroupRow {
return {
id: BigInt(id),
customer_id: BigInt(customerId),
customer: { group_id: BigInt(groupId) },
};
}
describe("loadFacilityGroupIndex(指示書AK 段階3 §6-3)", () => {
it("★★`where` を一切付けない★★(= `deleted_at` で絞らない。罠7 の唯一の検出手段)", async () => {
const { prisma, calls } = stubPrisma([]);
await loadFacilityGroupIndex(prisma);
for (const args of calls) {
expect(Object.keys(args as object)).toEqual(["select"]);
}
});
it("★★クエリは1本だけ★★(施設ごとにループで撃たないこと。落とし穴14)", async () => {
const { prisma, calls } = stubPrisma([row(1, 1, 1), row(2, 2, 1), row(3, 3, 0)]);
await loadFacilityGroupIndex(prisma);
expect(calls).toHaveLength(1);
});
it("読む列は `id` / `customer_id` / 屋号の `group_id` だけ(★これ以上増やさない★)", async () => {
const { prisma, calls } = stubPrisma([]);
await loadFacilityGroupIndex(prisma);
const select = (calls[0] as { select: Record<string, unknown> }).select;
expect(Object.keys(select).sort()).toEqual(["customer", "customer_id", "id"]);
expect(select.customer).toEqual({ select: { group_id: true } });
});
it("★論理削除された施設も返す★(返す行を間引かないこと)", async () => {
const { prisma } = stubPrisma([row(1, 1, 1), row(2, 1, 1)]);
const rows = await loadFacilityGroupIndex(prisma);
expect(rows.map((r) => r.id)).toEqual([1, 2]);
});
it("★BigInt を `number` に直して返す★(`NextResponse.json` が BigInt で例外を投げるため)", async () => {
const { prisma } = stubPrisma([row(12, 4, 2)]);
const rows = await loadFacilityGroupIndex(prisma);
expect(rows).toEqual([{ id: 12, customer_id: 4, group_id: 2 }]);
expect(typeof rows[0].id).toBe("number");
expect(typeof rows[0].customer_id).toBe("number");
});
it("★★`customers.group_id = 0` は `null` に変換する★★(D-011 / 落とし穴3)", async () => {
// ★0 は「ホールディングスなし」のセンチネル★ — そのまま出すと
// 「グループ0」という存在しないホールディングスに当たってしまう
const { prisma } = stubPrisma([row(5, 4, 0)]);
const rows = await loadFacilityGroupIndex(prisma);
expect(rows[0].group_id).toBeNull();
});
});