Files
NextChair/src/services/business/__tests__/business.service.test.ts
T
Stefan Lazic ec5583f972 Phase 6
Owner Administration is complete. Summary of this final stretch of work:

Seed data extended with an inactive staff member, a pending invitation, a redeemed-reward fixture, a service with no eligible staff, and a future-appointment fixture for conflict testing — all idempotent, verified against the dev DB.

Testing brought to 179 unit / 86 integration / 27 E2E tests, all passing together on a freshly seeded, disposable Postgres:

Unit tests for every new pure validator (business hours, categories/services, booking rules, loyalty config, staff creation).
Integration tests covering opening-hours conflicts, last-owner protection, staff deactivation/eligibility conflicts, loyalty redemption's atomic race-guard, and owner-vs-barber/business-isolation authorization — all against a real Postgres.
12 new Playwright owner journeys, which surfaced and fixed two genuine issues along the way: a cookie-clearing race condition in the sign-out-mid-test flow (a real, reproducible timing bug, not just a test artifact), and confirmation that seed fixtures upserted by deterministic ID go stale once "today" has moved on — now handled defensively.
Desktop responsiveness: converted the single-column Business Details, Booking Rules, and Loyalty Configuration forms to a genuine two-column layout at the lg: breakpoint (verified via measured element positions, not just screenshots), confirmed mobile stacking is unaffected.

Final verification: clean tsc, eslint, and next build, plus the full three-suite run against one fresh database as the authoritative pass.
2026-08-03 12:56:55 +01:00

74 lines
2.7 KiB
TypeScript

import { describe, expect, it } from "vitest";
import { Weekday } from "@/generated/prisma/enums";
import { validateBusinessProfileInput, validateOpeningHoursInput } from "../business.service";
describe("validateBusinessProfileInput", () => {
const base = {
name: "Blake Heritage Barbershop",
timezone: "Europe/London",
currency: "GBP",
locale: "en-GB",
};
it("accepts a complete profile", () => {
expect(validateBusinessProfileInput(base).valid).toBe(true);
});
it("rejects an empty name", () => {
const result = validateBusinessProfileInput({ ...base, name: " " });
expect(result.valid).toBe(false);
expect(result.issues[0]?.code).toBe("INVALID_INPUT");
});
it("rejects a missing timezone/currency/locale", () => {
const result = validateBusinessProfileInput({ ...base, timezone: "" });
expect(result.valid).toBe(false);
expect(result.issues[0]?.code).toBe("INVALID_INPUT");
});
});
describe("validateOpeningHoursInput", () => {
it("accepts a day marked closed with no times", () => {
const result = validateOpeningHoursInput([{ dayOfWeek: Weekday.SUNDAY, isClosed: true }]);
expect(result.valid).toBe(true);
});
it("requires open/close times for an open day", () => {
const result = validateOpeningHoursInput([{ dayOfWeek: Weekday.MONDAY, isClosed: false }]);
expect(result.valid).toBe(false);
expect(result.issues[0]?.code).toBe("INVALID_INPUT");
});
it("rejects opening time at or after closing time", () => {
const result = validateOpeningHoursInput([
{ dayOfWeek: Weekday.TUESDAY, isClosed: false, opensAtMinutes: 600, closesAtMinutes: 600 },
]);
expect(result.valid).toBe(false);
expect(result.issues[0]?.code).toBe("INVALID_TIME_RANGE");
});
it("rejects times outside a single day", () => {
const result = validateOpeningHoursInput([
{ dayOfWeek: Weekday.WEDNESDAY, isClosed: false, opensAtMinutes: 0, closesAtMinutes: 25 * 60 },
]);
expect(result.valid).toBe(false);
expect(result.issues[0]?.code).toBe("INVALID_TIME_RANGE");
});
it("rejects a negative opening time", () => {
const result = validateOpeningHoursInput([
{ dayOfWeek: Weekday.THURSDAY, isClosed: false, opensAtMinutes: -30, closesAtMinutes: 600 },
]);
expect(result.valid).toBe(false);
expect(result.issues[0]?.code).toBe("INVALID_TIME_RANGE");
});
it("accepts a valid full week mixing open and closed days", () => {
const result = validateOpeningHoursInput([
{ dayOfWeek: Weekday.MONDAY, isClosed: false, opensAtMinutes: 10 * 60, closesAtMinutes: 19 * 60 },
{ dayOfWeek: Weekday.SUNDAY, isClosed: true },
]);
expect(result.valid).toBe(true);
});
});