import path from 'node:path'; import { config } from 'dotenv'; import { CATALOG, DEFAULT_CATEGORY_DISCOUNTS, fromYerevan, toYerevan, YEREVAN_DISTRICTS } from '@barev/core'; import { createDb, type Db } from '@barev/db'; import type { Ctx, DomainEvent } from '../src/context.ts'; config({ path: path.resolve(import.meta.dirname, '../../../.env'), quiet: true }); function testUrl() { const u = new URL(process.env.DATABASE_URL!); u.pathname = `/${process.env.TEST_DB_NAME ?? 'barev_test'}`; return u.toString(); } export const db: Db = createDb(testUrl()); export function makeCtx(now: Date) { const events: DomainEvent[] = []; const clock = { now }; const ctx: Ctx = { db, notify: { emit: (e) => void events.push(e) }, now: () => clock.now }; return { ctx, events, clock }; } export async function wipe() { const tables = await db.$queryRaw<{ tablename: string }[]>` SELECT tablename FROM pg_tables WHERE schemaname = 'public' AND tablename <> '_prisma_migrations'`; await db.$executeRawUnsafe(`TRUNCATE ${tables.map((t) => `"public"."${t.tablename}"`).join(', ')} RESTART IDENTITY CASCADE`); } /** Понедельник 2026-10-19, 10:00 по Еревану — «сейчас» в тестах */ export const NOW = fromYerevan(2026, 10, 19, 10 * 60); /** Слот послезавтра (ср) в hh:00 — вне 24-часового порога */ export const slotAt = (dayOffset: number, hour: number) => { const p = toYerevan(new Date(NOW.getTime() + dayOffset * 24 * 3600_000)); return fromYerevan(p.year, p.month, p.day, hour * 60); }; let tgSeq = 1000; export async function fixtures() { // шаблоны переезда (в базе их создаёт миграция; тесты чистят все таблицы) const steps = ['consult', 'airport', 'stay', 'flat', 'registration', 'socialCard', 'bankCard', 'residence']; const docs = ['passport', 'diploma', 'criminalRecord']; await wipe(); await db.relocationItem.createMany({ data: [ ...steps.map((code, i) => ({ kind: 'STEP' as const, code, name: { ru: code }, sortOrder: i, inTurnkey: code !== 'consult' })), ...docs.map((code, i) => ({ kind: 'DOC' as const, code, name: { ru: code }, sortOrder: i, inTurnkey: false })), ], }); for (const [i, code] of YEREVAN_DISTRICTS.entries()) await db.district.create({ data: { code, sortOrder: i } }); const kentron = await db.district.findUniqueOrThrow({ where: { code: 'kentron' } }); const home = await db.category.create({ data: { code: 'HOME', barevPlusDiscountPct: DEFAULT_CATEGORY_DISCOUNTS.HOME } }); const c = CATALOG.find((s) => s.code === 'cleaning')!; const cleaning = await db.service.create({ data: { code: c.code, categoryId: home.id, flow: c.flow, providerRole: c.providerRole, pricing: c.pricing, paramsSchema: c.paramsSchema, slotConfig: c.slotConfig, lateCancelFeePct: 30, checklist: { create: c.checklist.map((x, i) => ({ code: x.code, requiresPhoto: !!x.requiresPhoto, sortOrder: i })) }, }, }); const agency = await db.agency.create({ data: { code: 'DOMIK', name: 'Domik', commissionPct: 10 } }); await db.agency.create({ data: { code: 'CLOSED', name: 'Closed', active: false } }); const schedule = [0, 1, 2, 3, 4, 5, 6].map((weekday) => ({ weekday, startMinute: 9 * 60, endMinute: 20 * 60 })); const mkProvider = async (name: string, languages: ('ru' | 'en' | 'hy')[], companyId?: string) => { const user = await db.user.create({ data: { telegramId: BigInt(++tgSeq), firstName: name } }); return db.provider.create({ data: { name, userId: user.id, role: 'CLEANER', languages, companyId, inviteCode: `INV-${name}`, onShift: true, districts: { create: [{ districtId: kentron.id }] }, schedule: { create: schedule }, }, }); }; const maria = await mkProvider('Maria', ['ru', 'en']); const ani = await mkProvider('Ani', ['hy']); const company = await db.providerCompany.create({ data: { name: 'Chisto', roles: ['CLEANER'], inviteCode: 'CO-1', rateValue: 65 } }); const lusine = await mkProvider('Lusine', ['ru'], company.id); const dispatcher = await db.user.create({ data: { telegramId: BigInt(++tgSeq), firstName: 'Karen' } }); await db.companyDispatcher.create({ data: { companyId: company.id, userId: dispatcher.id } }); return { kentron, cleaning, agency, maria, ani, company, lusine, dispatcher }; } export const tgUser = (id: number, lang = 'ru') => ({ id, first_name: `User${id}`, language_code: lang });