Newer
Older
client_dwatch / lib / ds-alerts / __tests__ / edit.test.ts
/**
 * `lib/ds-alerts/edit.ts` の期待値表(指示書AX §3 表3。D15)
 *
 * ★表3 は指示書AX で導き、監督が正としたもの(監督判断 2026-10-19。§11 Q5)。変えないこと★
 *
 * | #  | 入力 | 期待 |
 * |----|------|------|
 * | S1 | `row`(unresolved・note null)、`updated`(同じ id・resolved・responded_at / resolved_at あり・note「確認済み」) | `row` の4項目だけ差し替え。他は `row` のまま |
 * | S2 | `updated.id !== row.id` | 例外 |
 * | S3 | summary {2,1,1}、1行が unresolved → resolved | {1,1,2} |
 * | S4 | 備考だけ変えた(状態は同じ) | summary は変わらない |
 * | S5 | 2行(unresolved → in_progress、in_progress → unresolved) | {2,1,1} のまま |
 * | S6 | `updated` が空の Map | 入力と同じ値(入力のオブジェクトを書き換えない) |
 */
import { describe, expect, it } from "vitest";
import { adjustDsAlertSummary, dsAlertWithResolution } from "@/lib/ds-alerts/edit";
import type { DsAlertSummary } from "@/lib/ds-alerts/query";
import type { DsAlert, ErrorReportItem } from "@/lib/types";

function alert(overrides: Partial<DsAlert> & Pick<DsAlert, "id">): DsAlert {
  return {
    alert_type: "disconnected",
    device_serial: "DFAS010001",
    facility_id: 3,
    facility_name: "テスト施設C",
    section_name: "1F売り場",
    detected_at: "2026-10-19T01:30:00+09:00",
    resolution_status: "unresolved",
    responded_at: null,
    resolved_at: null,
    note: null,
    ...overrides,
  };
}

/** PATCH /errors/{error_id}/status の応答。★DsAlert に無い項目はわざと row と違う値にする★(差し替えないことを見る) */
function updatedItem(overrides: Partial<ErrorReportItem> & Pick<ErrorReportItem, "id">): ErrorReportItem {
  return {
    detected_at: "2026-10-01T00:00:00+09:00",
    facility_id: 99,
    facility_name: "別の施設",
    serial_number: "OTHER",
    error_type: "D-S_CONTROL_FAILURE",
    error_level: "CRITICAL",
    clean_timetable_id: null,
    schedule_link: "none",
    precheck_minutes: null,
    resolution_status: "unresolved",
    responded_at: null,
    resolved_at: null,
    note: null,
    // エラー内容(指示書BD §3-2)。★DsAlert には無い項目★(D-Sec の行は選べない)
    error_list_id: null,
    error_list: null,
    error_list_selectable: false,
    ...overrides,
  };
}

describe("dsAlertWithResolution(表3 S1・S2)", () => {
  it("S1: 4項目だけ差し替え、他は row のまま", () => {
    const row = alert({ id: "1000004" });
    const updated = updatedItem({
      id: "1000004",
      resolution_status: "resolved",
      responded_at: "2026-10-19T09:00:00+09:00",
      resolved_at: "2026-10-19T09:30:00+09:00",
      note: "確認済み",
    });
    expect(dsAlertWithResolution(row, updated)).toEqual({
      ...row,
      resolution_status: "resolved",
      responded_at: "2026-10-19T09:00:00+09:00",
      resolved_at: "2026-10-19T09:30:00+09:00",
      note: "確認済み",
    });
  });

  it("S1: 入力の row を書き換えない", () => {
    const row = alert({ id: "1" });
    const snapshot = { ...row };
    dsAlertWithResolution(row, updatedItem({ id: "1", resolution_status: "resolved" }));
    expect(row).toEqual(snapshot);
  });

  it("★S2: id が違えば例外(呼び出しの誤り)★", () => {
    expect(() => dsAlertWithResolution(alert({ id: "1" }), updatedItem({ id: "2" }))).toThrow();
  });
});

describe("adjustDsAlertSummary(表3 S3〜S6)", () => {
  const SUMMARY: DsAlertSummary = { unresolved: 2, in_progress: 1, resolved: 1 };
  const BEFORE: DsAlert[] = [
    alert({ id: "a", resolution_status: "unresolved" }),
    alert({ id: "b", resolution_status: "unresolved" }),
    alert({ id: "c", resolution_status: "in_progress" }),
    alert({ id: "d", resolution_status: "resolved" }),
  ];

  it("S3: 1行が unresolved → resolved → {1,1,2}", () => {
    const updated = new Map([["a", { ...BEFORE[0], resolution_status: "resolved" as const }]]);
    expect(adjustDsAlertSummary(SUMMARY, BEFORE, updated)).toEqual({ unresolved: 1, in_progress: 1, resolved: 2 });
  });

  it("S4: 備考だけ変えた(状態は同じ)→ 変わらない", () => {
    const updated = new Map([["b", { ...BEFORE[1], note: "備考だけ" }]]);
    expect(adjustDsAlertSummary(SUMMARY, BEFORE, updated)).toEqual(SUMMARY);
  });

  it("S5: unresolved → in_progress と in_progress → unresolved → {2,1,1} のまま", () => {
    const updated = new Map([
      ["a", { ...BEFORE[0], resolution_status: "in_progress" as const }],
      ["c", { ...BEFORE[2], resolution_status: "unresolved" as const }],
    ]);
    expect(adjustDsAlertSummary(SUMMARY, BEFORE, updated)).toEqual({ unresolved: 2, in_progress: 1, resolved: 1 });
  });

  it("★S6: updated が空 → 入力と同じ値・入力のオブジェクトを書き換えない★", () => {
    const summary = { ...SUMMARY };
    const result = adjustDsAlertSummary(summary, BEFORE, new Map());
    expect(result).toEqual(SUMMARY);
    expect(summary).toEqual(SUMMARY);
  });

  it("★入力の summary を書き換えない(状態が変わったとき)★", () => {
    const summary = { ...SUMMARY };
    adjustDsAlertSummary(summary, BEFORE, new Map([["a", { ...BEFORE[0], resolution_status: "resolved" as const }]]));
    expect(summary).toEqual(SUMMARY);
  });
});