Newer
Older
client_dwatch / lib / format / __tests__ / robot-model.test.ts
/**
 * `isSupportedModel()` の期待値表(指示書N 1-5)。
 *
 * ★期待値表を先に確定させ、実装より先にこのテストを書いている(CLAUDE.md §9.4)★
 *
 * | #  | 入力(maker, model)      | 期待  | 意図                                   |
 * |----|---------------------------|-------|----------------------------------------|
 * | 1  | ("PUDU", "CC1")           | true  | 唯一の対応機種                         |
 * | 2  | ("PUDU", "MT1")           | false | 既知だが未対応                         |
 * | 3  | ("Keenon", "C30")         | false | 同上                                   |
 * | 4  | ("Keenon", "C40")         | false | 同上                                   |
 * | 5  | ("PUDU", "ZZ9")           | false | レジストリに無い組み合わせ(完全に未知)|
 * | 6  | ("Keenon", "CC1")         | false | メーカーだけ違う                       |
 * | 7  | ("PUDU", "T5")            | false | メーカーだけ一致して機種が違う         |
 * | 8  | ("", "")                  | false | 空文字で落ちない                       |
 * | 9  | ("PUDU", "")              | false | 片方だけ空                             |
 * | 10 | (" PUDU ", " CC1 ")       | true  | 前後空白を許容する                     |
 * | 11 | ("pudu", "cc1")           | true  | 大文字小文字を区別しない               |
 * | 12 | ("PUDU", "CC10")          | false | 部分一致していないことの固定           |
 */
import { describe, expect, it } from "vitest";
import { facilities } from "@/lib/fixtures/facilities";
import { isSupportedModel, stripMakerPrefix } from "@/lib/format/robot-model";

describe("isSupportedModel", () => {
  it("1. PUDU CC1 は対応済み(唯一の対応機種)", () => {
    expect(isSupportedModel("PUDU", "CC1")).toBe(true);
  });

  it("2. PUDU MT1 は既知だが未対応", () => {
    expect(isSupportedModel("PUDU", "MT1")).toBe(false);
  });

  it("3. Keenon C30 は既知だが未対応", () => {
    expect(isSupportedModel("Keenon", "C30")).toBe(false);
  });

  it("4. Keenon C40 は既知だが未対応", () => {
    expect(isSupportedModel("Keenon", "C40")).toBe(false);
  });

  it("5. レジストリに無い組み合わせ(完全に未知)はfalse", () => {
    expect(isSupportedModel("PUDU", "ZZ9")).toBe(false);
  });

  it("6. メーカーだけ違う場合はfalse(機種名だけで判定していないことの固定)", () => {
    expect(isSupportedModel("Keenon", "CC1")).toBe(false);
  });

  it("7. メーカーだけ一致して機種が違う場合はfalse", () => {
    expect(isSupportedModel("PUDU", "T5")).toBe(false);
  });

  it("8. 空文字同士でも落ちない", () => {
    expect(() => isSupportedModel("", "")).not.toThrow();
    expect(isSupportedModel("", "")).toBe(false);
  });

  it("9. 片方だけ空でもfalse", () => {
    expect(isSupportedModel("PUDU", "")).toBe(false);
  });

  it("10. 前後の空白は許容する(trimしてから比較)", () => {
    expect(isSupportedModel(" PUDU ", " CC1 ")).toBe(true);
  });

  it("11. 大文字小文字は区別しない", () => {
    expect(isSupportedModel("pudu", "cc1")).toBe(true);
  });

  it("12. 部分一致していないこと(CC10 はCC1にヒットしない)", () => {
    expect(isSupportedModel("PUDU", "CC10")).toBe(false);
  });
});

// ★fixtures の全ロボットが対応機種(ROBOT_MODELS の supported)であることの回帰テスト(指示書N 4-3)★
// 入れる/入れないは実装者判断でよいとされているが、
// 「fixturesはP1のスコープ通りPUDU CC1のみで揃っている」という段階3の成果を固定するために入れた。
// 将来MT1/C30/C40をfixturesにも境界ケースとして足したくなったら、このテストごと見直すこと
// (見直しであってバグ修正ではない)。
describe("fixtures の対応機種チェック(指示書N 4-3)", () => {
  it("fixtures のロボットはすべて対応機種(ROBOT_MODELS の supported)である — 意図的に未対応機種を足したい場合はこのテストごと見直すこと", () => {
    const unsupported = facilities
      .flatMap((f) => f.robots)
      .filter((r) => !isSupportedModel(r.maker, r.model));
    expect(unsupported).toEqual([]);
  });
});

describe("stripMakerPrefix(ユーザー指示 2026-10-29: メーカー名を重ねない)", () => {
  it.each([
    ["PUDU", "PUDU CC1", "CC1"],
    ["PUDU", "pudu  CC1", "CC1"],
    ["PUDU", "CC1", "CC1"],
    ["PUDU", "PUDUX1", "PUDUX1"],
    ["PUDU", "PUDU", "PUDU"],
    ["PUDU", "PUDU ", "PUDU "],
    ["Keenon", "Keenon C30", "C30"],
    ["99", "X1", "X1"],
  ] as const)("maker=%s model=%s → %s", (maker, model, expected) => {
    expect(stripMakerPrefix(maker, model)).toBe(expected);
  });
});