import { describe, expect, it } from "vitest";
import { facilityIdsMatchingMasters } from "@/lib/facilities/resolve-by-master";
import type { FacilityGroupRef } from "@/lib/types";
/**
* 指示書AK 段階3 ★§6-1★ — 分類(ホールディングス/屋号)→ 施設ID の解決(★純粋関数の部分★)。
*
* ★★ここは「解決関数そのもの」のテストです★★。
* ★★これだけでは不十分★★ — ★ルートが `facility_ids` を渡し忘れても赤くなりません★
* (それは段階4 の担当。`resolve-by-name.test.ts` の但し書きと同じ)。
*
* ★戻り値の契約は `facilityIdsMatchingName()` と完全に同じ★:
* `null` = 条件なし / `[]` = 0件ヒット / それ以外 = 当たった施設ID。
*/
/**
* ★実在の法人名・屋号を使わないための架空の構成★(`CLAUDE.md` §7 / `docs/fixtures.md`)。
* `lib/fixtures/` の構成をなぞってある(★グループ1 は傘下2社★/★グループ2 は傘下1社★/
* ★屋号4 はホールディングスなし★)。
*/
const REFS: readonly FacilityGroupRef[] = [
{ id: 1, customer_id: 1, group_id: 1 },
{ id: 2, customer_id: 2, group_id: 1 },
{ id: 3, customer_id: 1, group_id: 1 },
{ id: 4, customer_id: 3, group_id: 2 },
// ★ホールディングスに属さない屋号の施設★(`group_id` が `null`)
{ id: 5, customer_id: 4, group_id: null },
];
describe("facilityIdsMatchingMasters(指示書AK 段階3 §6-1)", () => {
it("★★どちらも未指定なら `null`(=分類で絞らない)★★。★`[]` を返さないこと★", () => {
// ★`[]` は「0件ヒット」の意味★ — 何も選んでいないのに一覧が空になる(落とし穴1)
expect(
facilityIdsMatchingMasters(REFS, { groupId: null, customerId: null })
).toBeNull();
});
it("屋号を指定 → その屋号の施設だけ", () => {
expect(
facilityIdsMatchingMasters(REFS, { groupId: null, customerId: 1 })
).toEqual([1, 3]);
});
it("★★存在しない屋号は `[]`(=0件ヒット)★★。★`null` を返さないこと★", () => {
// ★`null` を返すと「条件なし」になり、★0件のはずが全件表示に化ける★(罠1 / 落とし穴1)
expect(
facilityIdsMatchingMasters(REFS, { groupId: null, customerId: 999 })
).toEqual([]);
expect(
facilityIdsMatchingMasters(REFS, { groupId: 999, customerId: null })
).toEqual([]);
});
it("★ホールディングスを指定 → 傘下の複数屋号の施設が**全部**返る★", () => {
// ★屋号1(施設1・3)と屋号2(施設2)の両方★ — 片方だけになっていたら赤くする
expect(
facilityIdsMatchingMasters(REFS, { groupId: 1, customerId: null })
).toEqual([1, 2, 3]);
});
it("★★同時指定は AND(積集合)★★ — 両方を満たす施設だけ", () => {
expect(
facilityIdsMatchingMasters(REFS, { groupId: 1, customerId: 2 })
).toEqual([2]);
});
it("★★噛み合わない同時指定は `[]`★★(★片方を捨てて `[1,2,3]` にしないこと★)", () => {
// 屋号3 はホールディングス2 の傘下なので、ホールディングス1 との AND は0件
expect(
facilityIdsMatchingMasters(REFS, { groupId: 1, customerId: 3 })
).toEqual([]);
});
it("★ホールディングスなしの施設は `groupId` 指定に当たらない★(`group_id: null`)", () => {
// ★API の `group_id` は1以上しか受け付けない★ので「所属なしだけを引く」経路は無い
// (`parseMasterIdParam()` が 0 を 422 にする。指示書AK §4-1 #3)
expect(facilityIdsMatchingMasters(REFS, { groupId: 1, customerId: null })).not.toContain(5);
expect(facilityIdsMatchingMasters(REFS, { groupId: 2, customerId: null })).not.toContain(5);
// ★屋号で指定すれば引ける★
expect(
facilityIdsMatchingMasters(REFS, { groupId: null, customerId: 4 })
).toEqual([5]);
});
it("★論理削除された施設も解決対象に含まれる★(罠7)", () => {
// ★解決用の読み取りが `deleted_at` で絞られていなければ削除済みの施設も `refs` に入る
// (`loadFacilityGroupIndex()`)。ここはそれを前提に解決する★
const withDeleted: FacilityGroupRef[] = [
...REFS,
{ id: 99, customer_id: 1, group_id: 1 },
];
expect(
facilityIdsMatchingMasters(withDeleted, { groupId: null, customerId: 1 })
).toEqual([1, 3, 99]);
});
it("施設マスタが空でも例外にならない", () => {
expect(facilityIdsMatchingMasters([], { groupId: 1, customerId: null })).toEqual([]);
expect(
facilityIdsMatchingMasters([], { groupId: null, customerId: null })
).toBeNull();
});
it("★入力の配列を壊さない(純粋関数)★", () => {
const before = [...REFS];
facilityIdsMatchingMasters(REFS, { groupId: 1, customerId: 1 });
expect(REFS).toEqual(before);
});
});