import { randomUUID } from 'node:crypto'; import { INestApplication, ValidationPipe, VersioningType } from '@nestjs/common'; import { Test } from '@nestjs/testing'; import { AppModule } from '../../src/app.module'; import { AppConfigService } from '../../src/modules/config/config.service'; import { DevicesController } from '../../src/modules/devices/devices.controller'; import { HealthController } from '../../src/modules/health/health.controller'; import { SyncController } from '../../src/modules/sync/sync.controller'; import { LocalFilesystemStorageService } from '../../src/modules/storage/storage.service'; import { PrismaService } from '../../src/infrastructure/database/prisma.service'; function createPrismaMock() { const devices = new Map(); return { $queryRawUnsafe: jest.fn().mockResolvedValue([{ '?column?': 1 }]), device: { create: jest.fn().mockImplementation(async ({ data }) => { const id = randomUUID(); const record = { id, ...data }; devices.set(id, record); return record; }), findUnique: jest.fn().mockImplementation(async ({ where }) => { return devices.get(where.id) ?? null; }), update: jest.fn().mockImplementation(async ({ where, data }) => { const current = devices.get(where.id); const updated = { ...current, ...data }; devices.set(where.id, updated); return updated; }), }, audioAsset: { findUnique: jest.fn().mockResolvedValue(null), }, uploadSession: { create: jest.fn().mockImplementation(async ({ data }) => { return { id: randomUUID(), ...data, }; }), findUnique: jest.fn().mockResolvedValue(null), }, libraryEvent: { findFirst: jest.fn().mockResolvedValue(null), }, }; } describe('Velody API wiring (e2e)', () => { let app: INestApplication; let healthController: HealthController; let devicesController: DevicesController; let syncController: SyncController; beforeEach(async () => { const prismaMock = createPrismaMock(); const moduleRef = await Test.createTestingModule({ imports: [AppModule], }) .overrideProvider(AppConfigService) .useValue({ appVersion: '0.1.0', maxUploadSizeBytes: 1024 * 1024 * 1024, storageRoot: '/tmp/velody-storage', }) .overrideProvider(LocalFilesystemStorageService) .useValue({ root: '/tmp/velody-storage', checkReadiness: jest.fn().mockResolvedValue({ root: '/tmp/velody-storage', writable: true, }), }) .overrideProvider(PrismaService) .useValue(prismaMock) .compile(); app = moduleRef.createNestApplication(); app.setGlobalPrefix('api'); app.enableVersioning({ type: VersioningType.URI }); app.useGlobalPipes( new ValidationPipe({ whitelist: true, forbidNonWhitelisted: true, transform: true, }), ); await app.init(); healthController = moduleRef.get(HealthController); devicesController = moduleRef.get(DevicesController); syncController = moduleRef.get(SyncController); }); afterEach(async () => { if (app) { await app.close(); } }); it('returns health information', async () => { const response = await healthController.getHealth(); expect(response.database.status).toBe('up'); expect(response.storage.status).toBe('up'); expect(response.version).toBe('0.1.0'); }); it('registers a device', async () => { const response = await devicesController.register({ platform: 'MACOS', deviceName: 'Diya MacBook Pro', appVersion: '0.1.0', }); expect(response.deviceId).toBeDefined(); expect(response.bootstrapToken).toBeDefined(); }); it('accepts device heartbeat', async () => { const registerResponse = await devicesController.register({ platform: 'IPHONE', deviceName: 'Diya iPhone', appVersion: '0.1.0', }); const response = await devicesController.heartbeat({ deviceId: registerResponse.deviceId, appVersion: '0.1.1', }); expect(response.ok).toBe(true); }); it('returns empty sync bootstrap and changes payloads', async () => { const bootstrapResponse = await syncController.bootstrap(); const changesResponse = await syncController.changes({ after: '0' }); expect(bootstrapResponse.tracks).toEqual([]); expect(changesResponse.events).toEqual([]); expect(changesResponse.nextCursor).toBe('0'); }); });