src/programs/snap-negative/input-limits.test.ts
Linked repository file from this build.
import { expect, test } from "bun:test";
import { commandSchema } from "@/features/reviews/command-schema";
import { parseDollarsToCents } from "@/features/reviews/computation-grid.helpers";
import { calculate } from "./calculation";
import { validateRequiredInput } from "./validation";
import { createSeed } from "./seed";
import { isCalendarDate, isCalendarMonth, MAX_MONEY_CENTS } from "./input-limits";
import { PREVIOUS_CHECK_VERSION } from "./rule-versions";
test("scenario and roster limits agree at 1 and 20, with zero reserved for unpopulated drafts", () => {
const scenario = createSeed().facts.scenarios.compI;
for (const householdSize of [1, 20]) {
expect(
commandSchema.safeParse({
type: "scenario",
scenario: "compI",
value: { ...scenario, householdSize },
}).success,
).toBe(true);
}
for (const householdSize of [0, 21, 99, 1.5]) {
expect(
commandSchema.safeParse({
type: "scenario",
scenario: "compI",
value: { ...scenario, householdSize },
}).success,
).toBe(false);
}
expect(
commandSchema.safeParse({
type: "scenario",
scenario: "compI",
value: { ...scenario, populated: false, householdSize: 0 },
}).success,
).toBe(true);
for (const size of [0, 1, 20, 21]) {
const value = Array.from({ length: size }, (_, i) => ({
id: String(i),
line: String(i + 1),
name: "",
relationship: "",
}));
expect(commandSchema.safeParse({ type: "household", value }).success).toBe(
size >= 1 && size <= 20,
);
}
});
test("every monetary field enforces the same cents and dollar boundary", () => {
const base = createSeed().facts.scenarios.compI;
for (const cents of [0, MAX_MONEY_CENTS, MAX_MONEY_CENTS + 1, -1, 0.5]) {
const valid = cents === 0 || cents === MAX_MONEY_CENTS;
for (const key of [
"medicalDeductionCents",
"dependentCareCents",
"childSupportCents",
"rentCents",
"taxesInsuranceCents",
"installationCents",
"otherShelterCents",
] as const) {
const value = { ...base, [key]: cents };
expect(commandSchema.safeParse({ type: "scenario", scenario: "compI", value }).success).toBe(
valid,
);
if (valid) expect(() => calculate(value, "2026-09")).not.toThrow();
else expect(() => calculate(value, "2026-09")).toThrow("money");
}
for (const key of ["amountCents", "expensesCents"] as const) {
const value = { ...base, income: [{ ...base.income[0], [key]: cents }] };
expect(commandSchema.safeParse({ type: "scenario", scenario: "compI", value }).success).toBe(
valid,
);
}
}
expect(parseDollarsToCents("1000000.00")).toBe(MAX_MONEY_CENTS);
expect(parseDollarsToCents("1000000.01")).toBeNull();
});
test("income rows stop at 100 in both commands and calculations", () => {
const base = createSeed().facts.scenarios.compI;
for (const count of [100, 101]) {
const value = {
...base,
income: Array.from({ length: count }, (_, i) => ({ ...base.income[0], id: String(i) })),
};
expect(commandSchema.safeParse({ type: "scenario", scenario: "compI", value }).success).toBe(
count === 100,
);
if (count === 101) expect(() => calculate(value, "2026-09")).toThrow("100 income rows");
}
});
test("calendar and single-digit fields reject impossible values while drafts remain optional", () => {
expect(isCalendarDate("2024-02-29")).toBe(true);
for (const date of ["2026-02-29", "2026-04-31", "0000-01-01", "2026-13-01", "9/9/26"])
expect(isCalendarDate(date)).toBe(false);
expect(isCalendarMonth("2026-09")).toBe(true);
expect(isCalendarMonth("2026-13")).toBe(false);
const review = createSeed();
review.facts.answers.dateCompleted = "2026-02-30";
review.facts.answers.homeCountyDigit1 = "12";
expect(validateRequiredInput(review).map((b) => b.field)).toContain("dateCompleted");
expect(validateRequiredInput(review).map((b) => b.field)).toContain("homeCountyDigit1");
review.checkVersion = PREVIOUS_CHECK_VERSION;
expect(validateRequiredInput(review).map((b) => b.field)).not.toContain("dateCompleted");
expect(validateRequiredInput(review).map((b) => b.field)).not.toContain("homeCountyDigit1");
});