src/features/reviews/search-fields.test.ts
Linked repository file from this build.
import { expect, test } from "bun:test";
import { createSeed } from "@/programs/snap-negative/seed";
import {
fieldRawValues,
fieldDisplayValues,
getSearchField,
searchFields,
searchPath,
validSearchField,
} from "./search-fields";
import { parseSearchSorts, searchFiltersSchema, searchSortsSchema } from "./search-filters";
test("registry includes empty optional fields and nested repeated metadata", () => {
const keys = new Set(searchFields.map((field) => field.key));
for (const key of [
"reviewNumber",
"supervisorApproved.at",
"caoResponse.rootCause",
"facts.final.result.selfEmploymentLossCents",
"policy.errorThreshold",
"facts.answers.narrative",
"facts.scenarios.compII.applicationDay",
"facts.final.inputs.educationGrantCents",
"facts.final.result.monthlyBenefitCents",
"documents[].name",
"activity[].policyDecision",
"validation.findings[].refused.reason",
])
expect(keys.has(key)).toBe(true);
expect(getSearchField("reviewNumber").kind).toBe("number");
expect(getSearchField("examiner").choices).toHaveLength(8);
expect(getSearchField("supervisor").choices).toHaveLength(2);
expect(getSearchField("notes[].actor.staffId").choices).toHaveLength(14);
expect(getSearchField("facts.scenarios.compI.rentCents")).toMatchObject({
kind: "number",
unit: "cents",
});
expect(getSearchField("facts.scenarios.compI.rentCents").label).toContain("(cents)");
expect(getSearchField("examiner").choices?.find((choice) => choice.value === "101")?.label).toBe(
"Alex Examiner",
);
});
test("dynamic map paths safely encode punctuation, Unicode, and empty keys", () => {
const review = createSeed();
// Records saved before actor slimming still hold preferences, and search indexes them.
const legacyActor = {
name: "Reader",
role: "examiner" as const,
preferences: { score: 3, "a.b[]": 7, "": false, 日本語: "present", 'quote"key': "safe" },
};
review.notes = [
{
id: "note",
body: "Evidence",
createdAt: "2026-09-09T12:00:00Z",
actor: legacyActor,
},
];
for (const [key, expected] of [
["notes[].actor.preferences.a~1b~2~3", 7],
["notes[].actor.preferences.~4", false],
["notes[].actor.preferences.日本語", "present"],
['notes[].actor.preferences.quote"key', "safe"],
] as const) {
expect(validSearchField(key)).toBe(true);
expect(fieldRawValues(review, key)).toEqual([expected]);
}
expect(searchPath('notes[].actor.preferences.quote"key')).toBe(
'$."notes"[*]."actor"."preferences"."quote\\"key"',
);
expect(validSearchField("notReview.secret")).toBe(false);
expect(
searchFiltersSchema.safeParse([
{ field: "notes[].actor.preferences.score", mode: "between", from: "2", through: "9" },
]).success,
).toBe(true);
});
test("typed point/ranges validate bounds, preserve empty choices and support exclusion", () => {
for (const row of [
{ field: "facts.scenarios.compI.rentCents", mode: "between", from: "10", through: "2" },
{ field: "facts.noticeDate", mode: "exact", term: "2026-02-30" },
{ field: "reviewNumber", mode: "exact", term: "NaN" },
{ field: "sampleMonth", from: "2026-13", through: "" },
])
expect(searchFiltersSchema.safeParse([row]).success).toBe(false);
expect(
searchFiltersSchema.safeParse([{ field: "outcome", mode: "exact", term: "", exclude: true }])
.success,
).toBe(true);
const review = createSeed();
review.examiner = "101";
expect(fieldDisplayValues(review, "examiner")).toEqual(["Alex Examiner"]);
review.facts.scenarios.compI.rentCents = 12345;
expect(fieldDisplayValues(review, "facts.scenarios.compI.rentCents")).toEqual(["$123.45"]);
});
test("timestamp ranges compare instants across timezone offsets", () => {
const row = {
field: "notes[].createdAt",
mode: "between",
from: "2026-09-09T08:00:00-04:00",
through: "2026-09-09T11:00:00Z",
};
expect(searchFiltersSchema.safeParse([row]).success).toBe(false);
expect(searchFiltersSchema.safeParse([{ ...row, through: "2026-09-09T12:00:00Z" }]).success).toBe(
true,
);
});
test("ordered sorts keep priority and reject repeated fields", () => {
const sorts = [
{ field: "sampleMonth", direction: "desc" as const },
{ field: "reviewNumber", direction: "asc" as const },
];
expect(parseSearchSorts(JSON.stringify(sorts))).toEqual(sorts);
expect(parseSearchSorts(null, "clientName", "asc")).toEqual([
{ field: "clientName", direction: "asc" },
]);
expect(parseSearchSorts(null)).toEqual([]);
expect(searchSortsSchema.safeParse([...sorts, sorts[0]]).success).toBe(false);
expect(
searchSortsSchema.safeParse([{ field: "notReview.secret", direction: "asc" }]).success,
).toBe(false);
expect(
searchSortsSchema.safeParse([{ field: "sampleMonth", direction: "sideways" }]).success,
).toBe(false);
});