import { randomUUID } from 'crypto'; import { newDb } from 'pg-mem'; import { DataSource } from 'typeorm'; import { Vehicle } from './entities/vehicle.entity'; import { VehiclesService } from './vehicles.service'; const TENANT = '11111111-1111-1111-1111-111111111111'; const DRIVER = '22222222-2222-2222-2222-222222222222'; describe('VehiclesService (docs/17 — C1)', () => { let ds: DataSource; let svc: VehiclesService; beforeEach(async () => { const db = newDb({ autoCreateForeignKeyIndices: true }); db.public.registerFunction({ name: 'version', returns: 'text' as any, implementation: () => 'pg-mem' }); db.public.registerFunction({ name: 'current_database', returns: 'text' as any, implementation: () => 'tripz', }); db.registerExtension('uuid-ossp', (schema) => schema.registerFunction({ name: 'uuid_generate_v4', returns: 'uuid' as any, implementation: () => randomUUID(), impure: true, }), ); await db.public.none(`CREATE EXTENSION "uuid-ossp"`); ds = (await db.adapters.createTypeormDataSource({ type: 'postgres', entities: [Vehicle], entityPrefix: 'tripz_', })) as DataSource; await ds.initialize(); await ds.synchronize(); svc = new VehiclesService(ds.getRepository(Vehicle)); }); afterEach(async () => { if (ds?.isInitialized) await ds.destroy(); }); it('أول مركبة تصير الافتراضية تلقائياً', async () => { const v = await svc.create(TENANT, DRIVER, { make: 'Toyota', model: 'Corolla' }); expect(v.is_default).toBe(true); expect(v.status).toBe('pending'); }); it('المركبة الثانية ليست افتراضية إلا بطلب صريح', async () => { await svc.create(TENANT, DRIVER, { make: 'Toyota' }); const second = await svc.create(TENANT, DRIVER, { make: 'Kia' }); expect(second.is_default).toBe(false); }); it('تعيين افتراضي جديد يلغي القديم — واحدة افتراضية فقط دائماً', async () => { const first = await svc.create(TENANT, DRIVER, { make: 'Toyota' }); const second = await svc.create(TENANT, DRIVER, { make: 'Kia' }); await svc.setDefault(TENANT, DRIVER, second.id); const list = await svc.list(TENANT, DRIVER); const defaults = list.filter((v) => v.is_default); expect(defaults).toHaveLength(1); expect(defaults[0].id).toBe(second.id); expect((await svc.getDefault(TENANT, DRIVER))!.id).toBe(second.id); }); it('حذف الافتراضية يرقّي مركبة أخرى — لا يبقى السائق بلا افتراضية', async () => { const first = await svc.create(TENANT, DRIVER, { make: 'Toyota' }); await svc.create(TENANT, DRIVER, { make: 'Kia' }); await svc.remove(TENANT, DRIVER, first.id); const def = await svc.getDefault(TENANT, DRIVER); expect(def).not.toBeNull(); expect(def!.is_default).toBe(true); }); it('اللوحة والـVIN مشفَّران at-rest، والباقي واضح', async () => { const v = await svc.create(TENANT, DRIVER, { plate: 'ABC-123', vin: '1HGCM82633A004352', make: 'Honda', color_hex: '#FF0000', }); const raw = await ds.query(`SELECT plate, vin, make, color_hex FROM tripz_vehicles WHERE id = $1`, [v.id]); expect(raw[0].plate).toMatch(/^v1:/); // مشفّر expect(raw[0].vin).toMatch(/^v1:/); expect(raw[0].make).toBe('Honda'); // غير حسّاس — واضح expect(raw[0].color_hex).toBe('#FF0000'); // لكنه يُقرأ مفكوكاً عبر الـORM const read = await svc.list(TENANT, DRIVER); expect(read[0].plate).toBe('ABC-123'); expect(read[0].vin).toBe('1HGCM82633A004352'); }); it('المستأجرون معزولون', async () => { await svc.create(TENANT, DRIVER, { make: 'Toyota' }); expect(await svc.list('33333333-3333-3333-3333-333333333333', DRIVER)).toHaveLength(0); }); });