147 lines
4.5 KiB
TypeScript
147 lines
4.5 KiB
TypeScript
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<string, any>();
|
|
|
|
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');
|
|
});
|
|
});
|