/**
* `robot_error_reports` の読み取りの `select` が `robot_id` / `robot` を含まないこと(指示書AU 段階0-4。§11 Q6)。
*
* ★先方の DDL では `robot_id` が NULL 可(TeamWorks 第11回 P-8 b)、Prisma のスキーマは NOT NULL のまま★。
* 読まなければ NULL の行が入っても落ちない。経路ごとの「NULL の行を混ぜても落ちない」は
* 各ルートのテスト(`app/api/{clean-logs,prechecks,prechecks/[id]/status,reports}/__tests__/route.test.ts`)。
*/
import { describe, expect, it } from "vitest";
import {
ROBOT_ERROR_COUNT_SELECT,
ROBOT_ERROR_DS_ALERT_SELECT,
ROBOT_ERROR_LIST_KEY_SELECT,
ROBOT_ERROR_LIST_ROW_SELECT,
ROBOT_ERROR_PRECHECK_ROW_SELECT,
ROBOT_ERROR_STATUS_SELECT,
ROBOT_ERROR_TYPE_SELECT,
} from "@/lib/db/robot-error-select";
import { guardNullRobotId } from "./robot-error-null-guard";
const SELECTS: [string, Record<string, unknown>][] = [
["ROBOT_ERROR_COUNT_SELECT", ROBOT_ERROR_COUNT_SELECT],
["ROBOT_ERROR_STATUS_SELECT", ROBOT_ERROR_STATUS_SELECT],
["ROBOT_ERROR_TYPE_SELECT", ROBOT_ERROR_TYPE_SELECT],
["ROBOT_ERROR_PRECHECK_ROW_SELECT", ROBOT_ERROR_PRECHECK_ROW_SELECT],
// エラー一覧(指示書AW D6): 1段目(キーだけ)と2段目・PATCH の前後の読み取り
["ROBOT_ERROR_LIST_KEY_SELECT", ROBOT_ERROR_LIST_KEY_SELECT],
["ROBOT_ERROR_LIST_ROW_SELECT", ROBOT_ERROR_LIST_ROW_SELECT],
// D-Sec アラート(指示書AX 2-3。行は全部 robot_id NULL)
["ROBOT_ERROR_DS_ALERT_SELECT", ROBOT_ERROR_DS_ALERT_SELECT],
];
/** ★可視性(`isVisibleErrorRow()`。削除済みの施設・清掃エリアの行を外す。指示書AX U8)の判定に要る列を読む定数★ */
const VISIBILITY_SELECTS: [string, Record<string, unknown>][] = [
["ROBOT_ERROR_LIST_KEY_SELECT", ROBOT_ERROR_LIST_KEY_SELECT],
["ROBOT_ERROR_LIST_ROW_SELECT", ROBOT_ERROR_LIST_ROW_SELECT],
["ROBOT_ERROR_DS_ALERT_SELECT", ROBOT_ERROR_DS_ALERT_SELECT],
// ★アラート(事前チェック異常)の行も可視性で外す(指示書BB U1・5-4)★
["ROBOT_ERROR_PRECHECK_ROW_SELECT", ROBOT_ERROR_PRECHECK_ROW_SELECT],
];
describe("robot_error_reports の select(指示書AU 段階0-4)", () => {
it.each(SELECTS)("%s は robot_id も robot も含まない", (_name, select) => {
expect(select).not.toHaveProperty("robot_id");
expect(select).not.toHaveProperty("robot");
});
it.each(SELECTS)("%s は紐づけのキー clean_timetable_id を含む", (_name, select) => {
expect(select.clean_timetable_id).toBe(true);
});
it.each(VISIBILITY_SELECTS)(
"★%s は facility / section の deleted_at を含む(指示書AX U8。取り忘れを防ぐ)★",
(_name, select) => {
expect(select).toHaveProperty(["facility", "select", "deleted_at"], true);
expect(select).toHaveProperty(["section", "select", "deleted_at"], true);
}
);
});
describe("error_list_id(エラー内容。指示書BD §5-3)", () => {
it("★ROBOT_ERROR_LIST_ROW_SELECT(一覧の2段目・PATCH の前後の読み取り)は error_list_id を読む★", () => {
expect(ROBOT_ERROR_LIST_ROW_SELECT.error_list_id).toBe(true);
});
it.each(SELECTS.filter(([name]) => name !== "ROBOT_ERROR_LIST_ROW_SELECT"))(
"%s は error_list_id を読まない(エラー一覧の行だけが持つ)",
(_name, select) => {
expect(select).not.toHaveProperty("error_list_id");
}
);
});
describe("guardNullRobotId(スタブで Prisma の失敗を再現する)", () => {
const nullRow = [{ error_id: "E1", robot_id: null }];
it("select なし(全列)で NULL の行を読むと落ちる", () => {
expect(() => guardNullRobotId(nullRow, { where: {} })).toThrow(/robot_id/);
expect(() => guardNullRobotId(nullRow, { include: { facility: true } })).toThrow(/robot_id/);
});
it("select / include に robot_id・robot があると落ちる", () => {
expect(() => guardNullRobotId(nullRow, { select: { robot_id: true } })).toThrow();
expect(() => guardNullRobotId(nullRow, { select: { robot: true } })).toThrow();
expect(() => guardNullRobotId(nullRow, { include: { robot: true } })).toThrow();
});
it("select で絞っていれば落ちない", () => {
expect(() => guardNullRobotId(nullRow, { select: ROBOT_ERROR_PRECHECK_ROW_SELECT })).not.toThrow();
});
it("NULL の行が無ければ select なしでも落ちない(既存のテストを巻き込まない)", () => {
expect(() => guardNullRobotId([{ robot_id: BigInt(1) }], undefined)).not.toThrow();
});
});