import { describe, expect, it } from "vitest";
import { bhS1RowsDefaultOrder } from "@/lib/clean-logs/__tests__/bh-s1-fixture";
import { sortCleanLogs } from "@/lib/clean-logs/sort";
import { CLEAN_TASK_STATUS } from "@/lib/config";
import type { CleanLogItem } from "@/lib/types";
const [R4, R1, R2, R5, R3, R8, R6, R7] = bhS1RowsDefaultOrder();
const DEFAULT_ORDER = [R4, R1, R2, R5, R3, R8, R6, R7];
function keys(items: readonly CleanLogItem[]): string[] {
return items.map((r) => r.row_key);
}
function rankRow(overrides: Partial<CleanLogItem>): CleanLogItem {
const row = bhS1RowsDefaultOrder()[0];
return {
...row,
id: null,
report_id: null,
unscheduled: false,
facility_name: "テスト",
model_name: "CC1",
serial_number: overrides.row_key ?? "S-X",
scheduled_at: "2026-11-01T02:00:00+09:00",
status_1h: null,
status_1h_minutes: null,
status_30m: null,
status_30m_minutes: null,
departed: true,
result_pending: false,
completion_rate: 50,
grade: "○",
consecutive_problem_days: 0,
escalation_level: null,
...overrides,
};
}
describe("sortCleanLogs — 表S1(指示書BH §3)", () => {
it("S1-1: sort null — 既定順のまま", () => {
expect(keys(sortCleanLogs(DEFAULT_ORDER, null, "asc"))).toEqual(keys(DEFAULT_ORDER));
});
it("S1-1b: sort null — 入力順を変えない", () => {
const input = [R8, R1, R7, R2, R6, R3, R5, R4];
expect(keys(sortCleanLogs(input, null, "asc"))).toEqual(keys(input));
});
it("S1-2: scheduled_at asc", () => {
expect(keys(sortCleanLogs(DEFAULT_ORDER, "scheduled_at", "asc"))).toEqual([
"tt-105",
"tt-106",
"tt-107",
"tt-103",
"rp-9001",
"tt-101",
"tt-102",
"tt-104",
]);
});
it("S1-4: completion_rate asc", () => {
expect(keys(sortCleanLogs(DEFAULT_ORDER, "completion_rate", "asc"))).toEqual([
"rp-9001",
"tt-102",
"tt-107",
"tt-105",
"tt-106",
"tt-101",
"tt-103",
"tt-104",
]);
});
it("S1-5: completion_rate desc", () => {
expect(keys(sortCleanLogs(DEFAULT_ORDER, "completion_rate", "desc"))).toEqual([
"tt-101",
"tt-106",
"tt-105",
"tt-107",
"tt-102",
"rp-9001",
"tt-103",
"tt-104",
]);
});
it("S1-6: grade asc", () => {
expect(keys(sortCleanLogs(DEFAULT_ORDER, "grade", "asc"))).toEqual([
"rp-9001",
"tt-103",
"tt-102",
"tt-107",
"tt-105",
"tt-101",
"tt-106",
"tt-104",
]);
});
it("S1-7: grade desc", () => {
expect(keys(sortCleanLogs(DEFAULT_ORDER, "grade", "desc"))).toEqual([
"tt-101",
"tt-106",
"tt-107",
"tt-105",
"tt-102",
"rp-9001",
"tt-103",
"tt-104",
]);
});
it("S1-8: departed asc", () => {
expect(keys(sortCleanLogs(DEFAULT_ORDER, "departed", "asc"))).toEqual([
"tt-103",
"tt-101",
"tt-102",
"rp-9001",
"tt-107",
"tt-105",
"tt-106",
"tt-104",
]);
});
it("S1-9: departed desc", () => {
expect(keys(sortCleanLogs(DEFAULT_ORDER, "departed", "desc"))).toEqual([
"tt-101",
"tt-102",
"rp-9001",
"tt-107",
"tt-105",
"tt-106",
"tt-103",
"tt-104",
]);
});
it("S1-10: task_status asc", () => {
expect(keys(sortCleanLogs(DEFAULT_ORDER, "task_status", "asc"))).toEqual([
"tt-103",
"tt-107",
"tt-102",
"rp-9001",
"tt-106",
"tt-105",
"tt-101",
"tt-104",
]);
});
it("S1-11: task_status desc", () => {
expect(keys(sortCleanLogs(DEFAULT_ORDER, "task_status", "desc"))).toEqual([
"tt-101",
"tt-105",
"rp-9001",
"tt-106",
"tt-102",
"tt-107",
"tt-103",
"tt-104",
]);
});
it("S1-12: model_name asc", () => {
expect(keys(sortCleanLogs(DEFAULT_ORDER, "model_name", "asc"))).toEqual([
"tt-101",
"tt-102",
"rp-9001",
"tt-107",
"tt-105",
"tt-106",
"tt-103",
"tt-104",
]);
});
it("S1-13: model_name desc", () => {
expect(keys(sortCleanLogs(DEFAULT_ORDER, "model_name", "desc"))).toEqual([
"tt-103",
"tt-101",
"tt-102",
"rp-9001",
"tt-107",
"tt-105",
"tt-106",
"tt-104",
]);
});
it("S1-14: serial_number asc", () => {
expect(keys(sortCleanLogs(DEFAULT_ORDER, "serial_number", "asc"))).toEqual([
"tt-102",
"tt-103",
"tt-101",
"rp-9001",
"tt-105",
"tt-106",
"tt-107",
"tt-104",
]);
});
it("S1-15: serial_number desc", () => {
expect(keys(sortCleanLogs(DEFAULT_ORDER, "serial_number", "desc"))).toEqual([
"tt-107",
"tt-106",
"tt-105",
"rp-9001",
"tt-101",
"tt-103",
"tt-102",
"tt-104",
]);
});
it("S1-16: grade asc の slice(3, 6)", () => {
const sorted = sortCleanLogs(DEFAULT_ORDER, "grade", "asc");
expect(sorted.length).toBe(8);
expect(keys(sorted.slice(3, 6))).toEqual(["tt-107", "tt-105", "tt-101"]);
});
it("S1-17: 入力配列を書き換えない", () => {
const input = [...DEFAULT_ORDER];
const before = keys(input);
sortCleanLogs(input, "grade", "asc");
expect(keys(input)).toEqual(before);
});
});
describe("sortCleanLogs — task_status 順位(指示書BH §11 R3)", () => {
const TS_NULL = rankRow({
row_key: "ts-null",
serial_number: "S-null",
departed: false,
task_status: null,
completion_rate: null,
grade: "×",
});
const TS_5 = rankRow({
row_key: "ts-5",
serial_number: "S-5",
task_status: CLEAN_TASK_STATUS.endedAbnormally,
});
const TS_2 = rankRow({
row_key: "ts-2",
serial_number: "S-2",
task_status: CLEAN_TASK_STATUS.interrupted,
});
const TS_3 = rankRow({
row_key: "ts-3",
serial_number: "S-3",
scheduled_at: "2026-11-01T02:00:00+09:00",
task_status: CLEAN_TASK_STATUS.cancelled,
});
const TS_6 = rankRow({
row_key: "ts-6",
serial_number: "S-6",
scheduled_at: "2026-11-02T21:00:00+09:00",
task_status: CLEAN_TASK_STATUS.cancelledByRobot,
});
const TS_7 = rankRow({
row_key: "ts-7",
serial_number: "S-7",
scheduled_at: "2026-11-01T02:00:00+09:00",
task_status: 7,
});
const TS_NEG = rankRow({
row_key: "ts-neg",
serial_number: "S-neg",
scheduled_at: "2026-11-01T02:00:00+09:00",
task_status: -1,
});
const TS_0 = rankRow({
row_key: "ts-0",
serial_number: "S-0",
task_status: CLEAN_TASK_STATUS.notStarted,
});
const TS_1 = rankRow({
row_key: "ts-1",
serial_number: "S-1",
task_status: CLEAN_TASK_STATUS.cleaning,
});
const TS_4 = rankRow({
row_key: "ts-4",
serial_number: "S-4",
task_status: CLEAN_TASK_STATUS.completed,
});
const TS_PENDING = rankRow({
row_key: "ts-pending",
serial_number: "S-pending",
departed: false,
result_pending: true,
task_status: null,
completion_rate: null,
grade: "×",
});
const shuffled = [
TS_PENDING,
TS_4,
TS_1,
TS_0,
TS_NEG,
TS_7,
TS_6,
TS_3,
TS_2,
TS_5,
TS_NULL,
];
const expectedAsc = [
"ts-null",
"ts-5",
"ts-2",
"ts-6",
"ts-3",
"ts-7",
"ts-neg",
"ts-0",
"ts-1",
"ts-4",
"ts-pending",
];
const expectedDesc = [
"ts-4",
"ts-1",
"ts-0",
"ts-7",
"ts-neg",
"ts-6",
"ts-3",
"ts-2",
"ts-5",
"ts-null",
"ts-pending",
];
it("asc: 順位 0・1・負の値を含む", () => {
expect(keys(sortCleanLogs(shuffled, "task_status", "asc"))).toEqual(expectedAsc);
});
it("desc: 同順位は既定順のまま(reverse しない)", () => {
expect(keys(sortCleanLogs(shuffled, "task_status", "desc"))).toEqual(expectedDesc);
});
});