1b264d715d
- Add coatType, temperamentScore, temperamentFlags, medicalAlerts, preferredCuts - Fixes TypeScript error on factories.ts:89 where extended profile fields were missing - GRO-1346 Co-Authored-By: Paperclip <noreply@paperclip.ing>
163 lines
5.0 KiB
TypeScript
163 lines
5.0 KiB
TypeScript
/**
|
|
* Test factories — build typed in-memory entities for unit tests.
|
|
*
|
|
* Each factory returns a fully-populated object with valid defaults.
|
|
* Pass an overrides object to customise specific fields.
|
|
*
|
|
* IDs are generated with a deterministic counter so tests produce stable,
|
|
* readable values (e.g. "staff-1", "client-2") without needing crypto.
|
|
*
|
|
* Usage:
|
|
* import { buildStaff, buildClient, buildPet } from "./db/factories";
|
|
*
|
|
* const manager = buildStaff({ role: "manager" });
|
|
* const client = buildClient({ name: "Alice Smith" });
|
|
* const pet = buildPet({ clientId: client.id });
|
|
*/
|
|
|
|
import type { staff, clients, pets, services, appointments } from "./schema.js";
|
|
|
|
// ── Counter-based ID factory ─────────────────────────────────────────────────
|
|
|
|
const counters: Record<string, number> = {};
|
|
|
|
function nextId(prefix: string): string {
|
|
counters[prefix] = (counters[prefix] ?? 0) + 1;
|
|
return `${prefix}-${counters[prefix]}`;
|
|
}
|
|
|
|
/** Reset all counters. Call in beforeEach() to keep tests independent. */
|
|
export function resetFactoryCounters(): void {
|
|
for (const key of Object.keys(counters)) {
|
|
delete counters[key];
|
|
}
|
|
}
|
|
|
|
// ── Type aliases ─────────────────────────────────────────────────────────────
|
|
|
|
export type StaffRow = typeof staff.$inferSelect;
|
|
export type ClientRow = typeof clients.$inferSelect;
|
|
export type PetRow = typeof pets.$inferSelect;
|
|
export type ServiceRow = typeof services.$inferSelect;
|
|
export type AppointmentRow = typeof appointments.$inferSelect;
|
|
|
|
// ── Factories ────────────────────────────────────────────────────────────────
|
|
|
|
export function buildStaff(overrides: Partial<StaffRow> = {}): StaffRow {
|
|
const id = nextId("staff");
|
|
return {
|
|
id,
|
|
name: `Staff Member ${id}`,
|
|
email: `${id}@groombook.test`,
|
|
oidcSub: `oidc-${id}`,
|
|
userId: null,
|
|
role: "groomer",
|
|
isSuperUser: false,
|
|
active: true,
|
|
icalToken: null,
|
|
createdAt: new Date("2025-01-01T00:00:00Z"),
|
|
updatedAt: new Date("2025-01-01T00:00:00Z"),
|
|
...overrides,
|
|
};
|
|
}
|
|
|
|
export function buildClient(overrides: Partial<ClientRow> = {}): ClientRow {
|
|
const id = nextId("client");
|
|
return {
|
|
id,
|
|
name: `Client ${id}`,
|
|
email: `${id}@example.com`,
|
|
phone: "555-0100",
|
|
address: "1 Main St, Springfield, CA 90000",
|
|
notes: null,
|
|
emailOptOut: false,
|
|
smsOptIn: false,
|
|
smsConsentDate: null,
|
|
smsOptOutDate: null,
|
|
smsConsentText: null,
|
|
stripeCustomerId: null,
|
|
status: "active",
|
|
disabledAt: null,
|
|
createdAt: new Date("2025-01-01T00:00:00Z"),
|
|
updatedAt: new Date("2025-01-01T00:00:00Z"),
|
|
...overrides,
|
|
};
|
|
}
|
|
|
|
export function buildPet(overrides: Partial<PetRow> & { clientId: string }): PetRow {
|
|
const id = nextId("pet");
|
|
const defaults: PetRow = {
|
|
id,
|
|
clientId: overrides.clientId,
|
|
name: `Pet ${id}`,
|
|
species: "Dog",
|
|
breed: "Mixed Breed",
|
|
weightKg: "15.00",
|
|
dateOfBirth: new Date("2020-06-15T00:00:00Z"),
|
|
healthAlerts: null,
|
|
groomingNotes: null,
|
|
cutStyle: null,
|
|
shampooPreference: null,
|
|
specialCareNotes: null,
|
|
customFields: {},
|
|
photoKey: null,
|
|
photoUploadedAt: null,
|
|
image: null,
|
|
coatType: null,
|
|
temperamentScore: null,
|
|
temperamentFlags: [],
|
|
medicalAlerts: [],
|
|
preferredCuts: [],
|
|
createdAt: new Date("2025-01-01T00:00:00Z"),
|
|
updatedAt: new Date("2025-01-01T00:00:00Z"),
|
|
};
|
|
return { ...defaults, ...overrides };
|
|
}
|
|
|
|
export function buildService(overrides: Partial<ServiceRow> = {}): ServiceRow {
|
|
const id = nextId("service");
|
|
return {
|
|
id,
|
|
name: `Service ${id}`,
|
|
description: "A grooming service",
|
|
basePriceCents: 6500,
|
|
durationMinutes: 60,
|
|
active: true,
|
|
createdAt: new Date("2025-01-01T00:00:00Z"),
|
|
updatedAt: new Date("2025-01-01T00:00:00Z"),
|
|
...overrides,
|
|
};
|
|
}
|
|
|
|
export function buildAppointment(
|
|
overrides: Partial<AppointmentRow> & { clientId: string; petId: string; serviceId: string; staffId: string }
|
|
): AppointmentRow {
|
|
const id = nextId("appointment");
|
|
const startTime = new Date("2025-06-01T10:00:00Z");
|
|
const endTime = new Date("2025-06-01T11:00:00Z");
|
|
const defaults: AppointmentRow = {
|
|
id,
|
|
clientId: overrides.clientId,
|
|
petId: overrides.petId,
|
|
serviceId: overrides.serviceId,
|
|
staffId: overrides.staffId,
|
|
batherStaffId: null,
|
|
seriesId: null,
|
|
seriesIndex: null,
|
|
groupId: null,
|
|
status: "scheduled",
|
|
startTime,
|
|
endTime,
|
|
notes: null,
|
|
priceCents: null,
|
|
confirmationStatus: "pending",
|
|
confirmedAt: null,
|
|
cancelledAt: null,
|
|
confirmationToken: null,
|
|
customerNotes: null,
|
|
createdAt: new Date("2025-01-01T00:00:00Z"),
|
|
updatedAt: new Date("2025-01-01T00:00:00Z"),
|
|
};
|
|
return { ...defaults, ...overrides };
|
|
}
|