Monorepo skeleton, Prisma schema, core domain and services with tests
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
commit
45d7512d2b
64 files changed
+9483
No files matched your search
@@ -0,0 +1,87 @@
|
||||
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 = '/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() {
|
||||
await wipe();
|
||||
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 });
|
||||
Reference in new issue
Block a user