// Shared fixture of the lane L4 tests (scripts/test-einsatz-*.ts): two zz test tenants with // users, customer, site, team and work orders. Not a test itself (the runner only picks up // scripts/test-*.ts at top level). import { Prisma } from "@prisma/client"; import { prisma, dbForTenant } from "../../src/server/db"; import { ROLE_DEFS, type RoleKey } from "../../src/server/rbac"; import type { ServiceCtx } from "../../src/server/services/context"; export let failures = 0; export const ok = (cond: boolean, msg: string) => { console.log(`${cond ? "✓" : "✗ FEHLER"} ${msg}`); if (!cond) failures++; }; /** Expects `fn` to throw a ServiceError with the given code. */ export async function expectCode(fn: () => Promise, code: string, msg: string) { try { await fn(); ok(false, `${msg} — kein Fehler (erwartet ${code})`); } catch (err) { const actual = (err as { code?: string }).code; ok(actual === code, `${msg}${actual === code ? "" : ` — Code ${actual ?? (err as Error).message}`}`); } } export function ctxFor(tenantId: string, userId: string, role: RoleKey): ServiceCtx { return { db: dbForTenant(tenantId), tenantId, userId, permissions: new Set(ROLE_DEFS[role].permissions) }; } async function cleanupSlug(slug: string, emailDomain: string) { const tenant = await prisma.tenant.findUnique({ where: { slug }, select: { id: true } }); if (tenant) { const where = { tenantId: tenant.id }; await prisma.auditLog.deleteMany({ where }); await prisma.syncOperation.deleteMany({ where }); await prisma.photo.deleteMany({ where }); await prisma.voiceNote.deleteMany({ where }); await prisma.document.deleteMany({ where }); await prisma.workOrder.deleteMany({ where }); await prisma.teamMember.deleteMany({ where }); await prisma.team.deleteMany({ where }); await prisma.site.deleteMany({ where }); await prisma.contact.deleteMany({ where }); await prisma.customer.deleteMany({ where }); await prisma.numberSequence.deleteMany({ where }); await prisma.userRole.deleteMany({ where: { user: { tenantId: tenant.id } } }); await prisma.user.deleteMany({ where }); await prisma.role.deleteMany({ where }); await prisma.tenant.delete({ where: { id: tenant.id } }); } await prisma.identity.deleteMany({ where: { email: { endsWith: emailDomain }, memberships: { none: {} } } }); } async function user(tenantId: string, email: string, name: string) { const identity = await prisma.identity.upsert({ where: { email }, update: {}, create: { email, passwordHash: "x" } }); return prisma.user.create({ data: { tenantId, identityId: identity.id, email, name } }); } export type Fixture = Awaited>; export async function createFixture(prefix: string) { const slugA = `zz-${prefix}-a`; const slugB = `zz-${prefix}-b`; const domain = `@zz-${prefix}.test`; await cleanupSlug(slugA, domain); await cleanupSlug(slugB, domain); const tenantA = await prisma.tenant.create({ data: { name: `L4 ${prefix} A`, slug: slugA } }); const tenantB = await prisma.tenant.create({ data: { name: `L4 ${prefix} B`, slug: slugB } }); const tech = await user(tenantA.id, `tech${domain}`, "Tech A"); const lead = await user(tenantA.id, `lead${domain}`, "Lead A"); const outsider = await user(tenantA.id, `outsider${domain}`, "Outsider A"); const techB = await user(tenantB.id, `techb${domain}`, "Tech B"); const customerA = await prisma.customer.create({ data: { tenantId: tenantA.id, companyName: "Kunde A GmbH", city: "Hamburg" } }); const siteA = await prisma.site.create({ data: { tenantId: tenantA.id, customerId: customerA.id, name: "Halle 1", street: "Hafenstraße", houseNumber: "1", postalCode: "20457", city: "Hamburg", accessNotes: "Schlüssel beim Pförtner" } }); const team = await prisma.team.create({ data: { tenantId: tenantA.id, name: `Team ${prefix}`, leaderUserId: lead.id } }); const now = new Date(); const orderA = await prisma.workOrder.create({ data: { tenantId: tenantA.id, number: `A-${prefix}-1`, customerId: customerA.id, siteId: siteA.id, title: "Heizung montieren", status: "assigned", plannedStart: new Date(now.getTime() - 60 * 60 * 1000), plannedEnd: new Date(now.getTime() + 60 * 60 * 1000), teamLeadUserId: lead.id, assignees: { create: [{ tenantId: tenantA.id, userId: tech.id }] }, checklistItems: { create: [{ tenantId: tenantA.id, key: "safe", label: "Arbeitsbereich abgesichert", required: true }] }, photoRequirements: { create: [{ tenantId: tenantA.id, key: "typenschild", label: "Typenschild" }] }, materialPlans: { create: [{ tenantId: tenantA.id, name: "Kupferrohr 15 mm", plannedQuantity: new Prisma.Decimal(10), unit: "m" }] }, }, include: { checklistItems: true, photoRequirements: true, materialPlans: true }, }); const customerB = await prisma.customer.create({ data: { tenantId: tenantB.id, companyName: "Kunde B AG" } }); const orderB = await prisma.workOrder.create({ data: { tenantId: tenantB.id, number: `A-${prefix}-B1`, customerId: customerB.id, title: "Wartung B", status: "assigned", assignees: { create: [{ tenantId: tenantB.id, userId: techB.id }] }, }, }); return { tenantA, tenantB, tech, lead, outsider, techB, team, siteA, orderA, orderB, ctxTech: ctxFor(tenantA.id, tech.id, "technician"), ctxLead: ctxFor(tenantA.id, lead.id, "team-lead"), ctxOutsider: ctxFor(tenantA.id, outsider.id, "technician"), ctxB: ctxFor(tenantB.id, techB.id, "technician"), cleanup: async () => { await cleanupSlug(slugA, domain); await cleanupSlug(slugB, domain); }, }; }