velody/backend/test/e2e/app.e2e-spec.ts
2026-06-14 09:43:41 +02:00

1926 lines
58 KiB
TypeScript

import { randomUUID, createHash } from 'node:crypto';
import { mkdir, mkdtemp, readFile, rm, writeFile } from 'node:fs/promises';
import { tmpdir } from 'node:os';
import { dirname, join } from 'node:path';
import { Readable } from 'node:stream';
import {
ForbiddenException,
NotFoundException,
UnauthorizedException,
ValidationPipe,
VersioningType,
} from '@nestjs/common';
import type { NestExpressApplication } from '@nestjs/platform-express';
import { Test } from '@nestjs/testing';
import { API_JSON_BODY_LIMIT } from '../../src/app.factory';
import { AppModule } from '../../src/app.module';
import { RequestContextService } from '../../src/infrastructure/request-context/request-context.service';
import { AssetsController } from '../../src/modules/assets/assets.controller';
import { AssetDownloadQueryDto } from '../../src/modules/assets/assets.dto';
import { DeviceAuthService } from '../../src/modules/auth/device-auth.service';
import { ArtworkController } from '../../src/modules/artwork/artwork.controller';
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 { LibraryController } from '../../src/modules/library/library.controller';
import { LibraryTracksQueryDto } from '../../src/modules/library/library.dto';
import { SyncController } from '../../src/modules/sync/sync.controller';
import { UploadsController } from '../../src/modules/uploads/uploads.controller';
import { UploadsService } from '../../src/modules/uploads/uploads.service';
import { PrismaService } from '../../src/infrastructure/database/prisma.service';
function sampleMp3Bytes(seed: string): Buffer {
return Buffer.concat([
Buffer.from('ID3', 'ascii'),
Buffer.from([0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x21]),
Buffer.from(seed, 'utf8'),
]);
}
function sha256Hex(data: Buffer): string {
return createHash('sha256').update(data).digest('hex');
}
function applySelect<T extends Record<string, any>>(
record: T | null,
select?: Record<string, boolean>,
) {
if (!record) {
return null;
}
if (!select) {
return record;
}
return Object.fromEntries(
Object.entries(select)
.filter(([, enabled]) => enabled)
.map(([key]) => [key, record[key]]),
);
}
function createUploadRequest(data: Buffer): any {
const request = Readable.from([data]) as any;
request.headers = {
'content-type': 'audio/mpeg',
'content-length': String(data.length),
};
return request;
}
async function streamToBuffer(stream: NodeJS.ReadableStream): Promise<Buffer> {
const chunks: Buffer[] = [];
for await (const chunkValue of stream) {
chunks.push(
Buffer.isBuffer(chunkValue) ? chunkValue : Buffer.from(chunkValue),
);
}
return Buffer.concat(chunks);
}
function createPrismaMock() {
const users = new Map<string, any>();
const devices = new Map<string, any>();
const tracks = new Map<string, any>();
const audioAssets = new Map<string, any>();
const artworkAssets = new Map<string, any>();
const uploadSessions = new Map<string, any>();
const libraryEvents = new Map<bigint, any>();
let nextLibraryEventId = 1n;
const defaultUser = {
id: randomUUID(),
slug: 'default-owner',
displayName: 'Default Owner',
isDefault: true,
createdAt: new Date(),
updatedAt: new Date(),
};
users.set(defaultUser.id, defaultUser);
const prismaMock: any = {
$queryRawUnsafe: jest.fn().mockResolvedValue([{ '?column?': 1 }]),
$transaction: jest.fn().mockImplementation(async (callback: any) => callback(prismaMock)),
user: {
upsert: jest.fn().mockResolvedValue(defaultUser),
},
device: {
create: jest.fn().mockImplementation(async ({ data }) => {
const record = {
id: randomUUID(),
createdAt: new Date(),
updatedAt: new Date(),
...data,
};
devices.set(record.id, record);
return record;
}),
findUnique: jest.fn().mockImplementation(async ({ where, select }) => {
if (where.id) {
return applySelect(devices.get(where.id) ?? null, select);
}
if (where.tokenHash) {
const matchingDevice =
[...devices.values()].find(
(device) => device.tokenHash === where.tokenHash,
) ?? null;
return applySelect(matchingDevice, select);
}
return null;
}),
update: jest.fn().mockImplementation(async ({ where, data }) => {
const current = devices.get(where.id);
const updated = {
...current,
...data,
updatedAt: new Date(),
};
devices.set(where.id, updated);
return updated;
}),
},
track: {
findMany: jest.fn().mockImplementation(async ({ where }) => {
return [...tracks.values()]
.filter((track) => {
const userMatches = where?.userId ? track.userId === where.userId : true;
const statusMatches = where?.status ? track.status === where.status : true;
const assetMatches = where?.primaryAudioAssetId?.not
? track.primaryAudioAssetId != null
: true;
return userMatches && statusMatches && assetMatches;
})
.sort((lhs, rhs) => lhs.createdAt.getTime() - rhs.createdAt.getTime())
.map((track) => ({
...track,
primaryAudioAsset: track.primaryAudioAssetId
? audioAssets.get(track.primaryAudioAssetId) ?? null
: null,
artworkAsset: track.artworkAssetId
? artworkAssets.get(track.artworkAssetId) ?? null
: null,
}));
}),
findUnique: jest.fn().mockImplementation(async ({ where }) => {
return tracks.get(where.id) ?? null;
}),
create: jest.fn().mockImplementation(async ({ data }) => {
const now = new Date();
const record = {
id: randomUUID(),
primaryAudioAssetId: null,
artworkAssetId: null,
albumArtist: null,
genre: null,
discNumber: null,
trackNumber: null,
year: null,
deletedAt: null,
createdAt: now,
updatedAt: now,
...data,
};
tracks.set(record.id, record);
return record;
}),
update: jest.fn().mockImplementation(async ({ where, data }) => {
const current = tracks.get(where.id);
const updated = {
...current,
...data,
updatedAt: new Date(),
};
tracks.set(where.id, updated);
return updated;
}),
},
audioAsset: {
findUnique: jest.fn().mockImplementation(async ({ where }) => {
if (where.id) {
return audioAssets.get(where.id) ?? null;
}
const composite = where.userId_sha256;
if (!composite) {
return null;
}
return (
[...audioAssets.values()].find(
(asset) =>
asset.userId === composite.userId &&
asset.sha256 === composite.sha256,
) ?? null
);
}),
create: jest.fn().mockImplementation(async ({ data }) => {
const record = {
id: randomUUID(),
bitRateKbps: null,
sampleRateHz: null,
channels: null,
createdAt: new Date(),
...data,
};
audioAssets.set(record.id, record);
return record;
}),
update: jest.fn().mockImplementation(async ({ where, data }) => {
const current = audioAssets.get(where.id);
const updated = {
...current,
...data,
};
audioAssets.set(where.id, updated);
return updated;
}),
},
artworkAsset: {
findUnique: jest.fn().mockImplementation(async ({ where }) => {
return artworkAssets.get(where.id) ?? null;
}),
findFirst: jest.fn().mockImplementation(async ({ where }) => {
return (
[...artworkAssets.values()].find(
(asset) =>
asset.userId === where.userId &&
asset.sha256 === where.sha256,
) ?? null
);
}),
create: jest.fn().mockImplementation(async ({ data }) => {
const record = {
id: randomUUID(),
createdAt: new Date(),
...data,
};
artworkAssets.set(record.id, record);
return record;
}),
update: jest.fn().mockImplementation(async ({ where, data }) => {
const current = artworkAssets.get(where.id);
const updated = {
...current,
...data,
};
artworkAssets.set(where.id, updated);
return updated;
}),
},
uploadSession: {
create: jest.fn().mockImplementation(async ({ data }) => {
const now = new Date();
const record = {
createdAt: now,
updatedAt: now,
completedAt: null,
finalizedAt: null,
trackId: null,
audioAssetId: null,
...data,
};
uploadSessions.set(record.id, record);
return record;
}),
findUnique: jest.fn().mockImplementation(async ({ where }) => {
return uploadSessions.get(where.id) ?? null;
}),
update: jest.fn().mockImplementation(async ({ where, data }) => {
const current = uploadSessions.get(where.id);
const updated = {
...current,
...data,
updatedAt: new Date(),
};
uploadSessions.set(where.id, updated);
return updated;
}),
},
libraryEvent: {
create: jest.fn().mockImplementation(async ({ data }) => {
const record = {
id: nextLibraryEventId,
payloadVersion: 1,
createdAt: new Date(),
...data,
};
libraryEvents.set(record.id, record);
nextLibraryEventId += 1n;
return record;
}),
findFirst: jest.fn().mockImplementation(async ({ where }) => {
const filteredEvents = [...libraryEvents.values()].filter((event) =>
where?.userId ? event.userId === where.userId : true,
);
return filteredEvents.sort((lhs, rhs) => Number(rhs.id - lhs.id))[0] ?? null;
}),
},
};
return {
prismaMock,
state: {
defaultUser,
devices,
tracks,
audioAssets,
artworkAssets,
uploadSessions,
libraryEvents,
},
};
}
describe('Velody API wiring (e2e)', () => {
let app: NestExpressApplication;
let prismaMock: ReturnType<typeof createPrismaMock>['prismaMock'];
let assetsController: AssetsController;
let artworkController: ArtworkController;
let healthController: HealthController;
let devicesController: DevicesController;
let libraryController: LibraryController;
let syncController: SyncController;
let uploadsController: UploadsController;
let uploadsService: UploadsService;
let requestContextService: RequestContextService;
let deviceAuthService: DeviceAuthService;
let prismaState: ReturnType<typeof createPrismaMock>['state'];
let storageRoot: string;
async function registerDeviceWithAuthorization(
body: {
platform: 'MACOS' | 'IPHONE';
deviceName: string;
appVersion: string;
},
authorizationHeader: string,
) {
return requestContextService.run(async () => {
await deviceAuthService.authenticateAuthorizationHeader(
authorizationHeader,
);
return devicesController.register(body);
});
}
function seedDevice(params: {
userId: string;
deviceAccessToken?: string;
deviceId?: string;
deviceName?: string;
appVersion?: string;
platform?: 'MACOS' | 'IPHONE';
tokenRevokedAt?: Date | null;
}) {
const deviceId = params.deviceId ?? randomUUID();
const deviceAccessToken = params.deviceAccessToken;
prismaState.devices.set(deviceId, {
id: deviceId,
userId: params.userId,
platform: params.platform ?? 'IPHONE',
deviceName: params.deviceName ?? 'Seeded Device',
appVersion: params.appVersion ?? '0.1.0',
installTokenHash: `seeded-install-${deviceId}`,
tokenHash: deviceAccessToken
? sha256Hex(Buffer.from(deviceAccessToken, 'utf8'))
: undefined,
tokenCreatedAt: deviceAccessToken ? new Date() : undefined,
tokenRevokedAt: params.tokenRevokedAt ?? undefined,
lastSeenAt: new Date(),
createdAt: new Date(),
updatedAt: new Date(),
});
return {
deviceId,
deviceAccessToken,
};
}
async function runAsDevice<T>(
deviceAccessToken: string,
callback: () => Promise<T>,
): Promise<T> {
return requestContextService.run(async () => {
await deviceAuthService.authenticateAuthorizationHeader(
`Bearer ${deviceAccessToken}`,
);
return callback();
});
}
beforeEach(async () => {
const prismaSetup = createPrismaMock();
prismaMock = prismaSetup.prismaMock;
const { state } = prismaSetup;
prismaState = state;
storageRoot = await mkdtemp(join(tmpdir(), 'velody-e2e-'));
const moduleRef = await Test.createTestingModule({
imports: [AppModule],
})
.overrideProvider(AppConfigService)
.useValue({
appVersion: '0.1.0',
maxUploadSizeBytes: 1024 * 1024 * 1024,
storageRoot,
})
.overrideProvider(PrismaService)
.useValue(prismaMock)
.compile();
app = moduleRef.createNestApplication({ bodyParser: false });
app.useBodyParser('json', { limit: API_JSON_BODY_LIMIT });
app.useBodyParser('urlencoded', {
extended: true,
limit: API_JSON_BODY_LIMIT,
});
app.setGlobalPrefix('api');
app.enableVersioning({ type: VersioningType.URI });
app.useGlobalPipes(
new ValidationPipe({
whitelist: true,
forbidNonWhitelisted: true,
transform: true,
}),
);
await app.init();
assetsController = moduleRef.get(AssetsController);
artworkController = moduleRef.get(ArtworkController);
healthController = moduleRef.get(HealthController);
devicesController = moduleRef.get(DevicesController);
libraryController = moduleRef.get(LibraryController);
syncController = moduleRef.get(SyncController);
uploadsController = moduleRef.get(UploadsController);
uploadsService = moduleRef.get(UploadsService);
requestContextService = moduleRef.get(RequestContextService);
deviceAuthService = moduleRef.get(DeviceAuthService);
});
afterEach(async () => {
if (app) {
await app.close();
}
await rm(storageRoot, { recursive: true, force: true });
});
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('creates the bootstrap default owner during application startup', () => {
expect(prismaMock.user.upsert).toHaveBeenCalledWith({
where: {
slug: 'default-owner',
},
update: {
displayName: 'Default Owner',
isDefault: true,
},
create: {
slug: 'default-owner',
displayName: 'Default Owner',
isDefault: true,
},
});
});
it('registers a device and accepts heartbeat', async () => {
const registerResponse = await devicesController.register({
platform: 'MACOS',
deviceName: 'Diya MacBook Pro',
appVersion: '0.1.0',
});
expect(registerResponse.deviceId).toBeDefined();
expect(registerResponse.deviceAccessToken).toBeDefined();
expect(registerResponse.bootstrapToken).toBeDefined();
expect(prismaState.devices.get(registerResponse.deviceId)?.userId).toBe(
prismaState.defaultUser.id,
);
expect(prismaState.devices.get(registerResponse.deviceId)?.tokenHash).toBe(
sha256Hex(Buffer.from(registerResponse.deviceAccessToken, 'utf8')),
);
expect(prismaState.devices.get(registerResponse.deviceId)?.tokenCreatedAt).toEqual(
expect.any(Date),
);
const heartbeatResponse = await runAsDevice(
registerResponse.deviceAccessToken,
() =>
devicesController.heartbeat({
deviceId: registerResponse.deviceId,
appVersion: '0.1.1',
}),
);
expect(heartbeatResponse.ok).toBe(true);
expect(prismaState.devices.get(registerResponse.deviceId)?.appVersion).toBe(
'0.1.1',
);
});
it('registers a linked device under the authenticated device owner when Authorization is present', async () => {
const linkedOwnerId = randomUUID();
const existingDevice = seedDevice({
userId: linkedOwnerId,
deviceAccessToken: 'linked-owner-access-token',
deviceName: 'Existing Linked Owner Device',
platform: 'MACOS',
});
const response = await registerDeviceWithAuthorization(
{
platform: 'IPHONE',
deviceName: 'Linked iPhone',
appVersion: '0.1.0',
},
`Bearer ${existingDevice.deviceAccessToken}`,
);
expect(response.deviceId).toBeDefined();
expect(response.deviceAccessToken).toBeDefined();
expect(prismaState.devices.get(response.deviceId)?.userId).toBe(
linkedOwnerId,
);
expect(prismaState.devices.get(response.deviceId)?.userId).not.toBe(
prismaState.defaultUser.id,
);
});
it('returns 401 when register receives an invalid Authorization header', async () => {
await expect(
registerDeviceWithAuthorization(
{
platform: 'IPHONE',
deviceName: 'Rejected iPhone',
appVersion: '0.1.0',
},
'Bearer invalid-device-token',
),
).rejects.toBeInstanceOf(UnauthorizedException);
});
it('rejects linked registration when the authenticating device token has been revoked', async () => {
const revokedDevice = seedDevice({
userId: randomUUID(),
deviceAccessToken: 'revoked-link-token',
tokenRevokedAt: new Date('2026-06-09T10:00:00.000Z'),
deviceName: 'Revoked Device',
});
await expect(
registerDeviceWithAuthorization(
{
platform: 'MACOS',
deviceName: 'Blocked Mac',
appVersion: '0.1.0',
},
`Bearer ${revokedDevice.deviceAccessToken}`,
),
).rejects.toBeInstanceOf(UnauthorizedException);
});
it('returns 401 when heartbeat is requested without Authorization', async () => {
await expect(
devicesController.heartbeat({
deviceId: randomUUID(),
appVersion: '0.1.1',
}),
).rejects.toBeInstanceOf(UnauthorizedException);
});
it('returns 401 when sync bootstrap is requested without Authorization', async () => {
await expect(syncController.bootstrap()).rejects.toBeInstanceOf(
UnauthorizedException,
);
});
it('returns 401 when upload prepare is requested without Authorization', async () => {
const bytes = sampleMp3Bytes('missing-upload-auth');
await expect(
uploadsController.prepare({
deviceId: randomUUID(),
sha256: sha256Hex(bytes),
originalFilename: 'missing-upload-auth.mp3',
sizeBytes: bytes.length,
}),
).rejects.toBeInstanceOf(UnauthorizedException);
});
it('returns 401 when asset download is requested without Authorization', async () => {
await expect(
assetsController.download(
randomUUID(),
{ deviceId: randomUUID() },
{ setHeader() {} } as any,
),
).rejects.toBeInstanceOf(UnauthorizedException);
});
it('returns 401 when artwork download is requested without Authorization', async () => {
await expect(
artworkController.download(
randomUUID(),
{ deviceId: randomUUID() },
{ setHeader() {} } as any,
),
).rejects.toBeInstanceOf(UnauthorizedException);
});
it('returns 401 when a protected route receives an invalid Authorization header', async () => {
await requestContextService.run(async () => {
await expect(
deviceAuthService.authenticateAuthorizationHeader(
'Bearer invalid-device-token',
),
).rejects.toBeInstanceOf(UnauthorizedException);
});
});
it('uses the authenticated device for heartbeat even when the body deviceId is spoofed', async () => {
const ownerDevice = await devicesController.register({
platform: 'MACOS',
deviceName: 'Owner Mac',
appVersion: '0.1.0',
});
const foreignDeviceId = randomUUID();
prismaState.devices.set(foreignDeviceId, {
id: foreignDeviceId,
userId: randomUUID(),
platform: 'MACOS',
deviceName: 'Foreign Mac',
appVersion: '0.1.0',
installTokenHash: 'foreign-device-hash',
lastSeenAt: new Date(),
createdAt: new Date(),
updatedAt: new Date(),
});
const response = await runAsDevice(ownerDevice.deviceAccessToken, () =>
devicesController.heartbeat({
deviceId: foreignDeviceId,
appVersion: '0.1.1',
}),
);
expect(response.ok).toBe(true);
expect(prismaState.devices.get(ownerDevice.deviceId)?.appVersion).toBe(
'0.1.1',
);
expect(prismaState.devices.get(foreignDeviceId)?.appVersion).toBe('0.1.0');
});
it('returns sync bootstrap and changes payloads', async () => {
const device = await devicesController.register({
platform: 'IPHONE',
deviceName: 'Sync iPhone',
appVersion: '0.1.0',
});
const bootstrapResponse = await runAsDevice(device.deviceAccessToken, () =>
syncController.bootstrap(),
);
const changesResponse = await runAsDevice(device.deviceAccessToken, () =>
syncController.changes({ after: '0' }),
);
expect(bootstrapResponse.tracks).toEqual([]);
expect(changesResponse.events).toEqual([]);
expect(changesResponse.nextCursor).toBe('0');
});
it('sync bootstrap and changes do not expose foreign-owner data', async () => {
const device = await devicesController.register({
platform: 'IPHONE',
deviceName: 'Scoped Sync iPhone',
appVersion: '0.1.0',
});
const foreignUserId = randomUUID();
const foreignTrackId = randomUUID();
prismaState.tracks.set(foreignTrackId, {
id: foreignTrackId,
userId: foreignUserId,
primaryAudioAssetId: null,
artworkAssetId: null,
title: 'Foreign Bootstrap Track',
artist: 'Elsewhere',
album: null,
albumArtist: null,
genre: null,
discNumber: null,
trackNumber: null,
year: null,
durationMs: 180000,
status: 'ACTIVE',
deletedAt: null,
createdAt: new Date('2026-05-29T08:00:00.000Z'),
updatedAt: new Date('2026-05-29T08:01:00.000Z'),
});
prismaState.libraryEvents.set(1n, {
id: 1n,
userId: foreignUserId,
entityType: 'TRACK',
entityId: foreignTrackId,
action: 'CREATED',
payloadVersion: 1,
createdAt: new Date('2026-05-29T08:02:00.000Z'),
});
const bootstrapResponse = await runAsDevice(device.deviceAccessToken, () =>
syncController.bootstrap(),
);
const changesResponse = await runAsDevice(device.deviceAccessToken, () =>
syncController.changes({ after: '0' }),
);
expect(bootstrapResponse.tracks).toEqual([]);
expect(changesResponse.events).toEqual([]);
expect(changesResponse.nextCursor).toBe('0');
});
it('downloads audio asset bytes for the owning device user', async () => {
const registerResponse = await devicesController.register({
platform: 'IPHONE',
deviceName: 'Playback iPhone',
appVersion: '0.1.0',
});
const assetId = randomUUID();
const trackId = randomUUID();
const bytes = sampleMp3Bytes('owner-download');
const storageKey = join(
'users',
prismaState.defaultUser.id,
'audio',
'owner-download.mp3',
);
prismaState.audioAssets.set(assetId, {
id: assetId,
userId: prismaState.defaultUser.id,
trackId,
sha256: sha256Hex(bytes),
storageKey,
originalFilename: 'owner-download.mp3',
mimeType: 'audio/mpeg',
fileExtension: 'mp3',
fileSizeBytes: BigInt(bytes.length),
durationMs: 180000,
sourceDeviceId: registerResponse.deviceId,
createdAt: new Date('2026-05-29T08:00:00.000Z'),
});
const filePath = join(storageRoot, storageKey);
await mkdir(dirname(filePath), { recursive: true });
await writeFile(filePath, bytes);
const headers = new Map<string, string>();
const responseMock = {
setHeader(name: string, value: string) {
headers.set(name.toLowerCase(), String(value));
},
} as any;
const streamable = await runAsDevice(registerResponse.deviceAccessToken, () =>
assetsController.download(
assetId,
{ deviceId: registerResponse.deviceId },
responseMock,
),
);
const downloadedBytes = await streamToBuffer(streamable.getStream());
expect(downloadedBytes.equals(bytes)).toBe(true);
expect(headers.get('content-type')).toBe('audio/mpeg');
expect(headers.get('content-length')).toBe(String(bytes.length));
});
it('rejects unauthorized asset download requests for another user asset', async () => {
const registerResponse = await devicesController.register({
platform: 'IPHONE',
deviceName: 'Playback iPhone',
appVersion: '0.1.0',
});
const assetId = randomUUID();
const otherUserId = randomUUID();
prismaState.audioAssets.set(assetId, {
id: assetId,
userId: otherUserId,
trackId: randomUUID(),
sha256: 'sha-other',
storageKey: join('users', otherUserId, 'audio', 'other.mp3'),
originalFilename: 'other.mp3',
mimeType: 'audio/mpeg',
fileExtension: 'mp3',
fileSizeBytes: BigInt(10),
durationMs: 180000,
sourceDeviceId: randomUUID(),
createdAt: new Date('2026-05-29T08:00:00.000Z'),
});
await expect(
runAsDevice(registerResponse.deviceAccessToken, () =>
assetsController.download(
assetId,
{ deviceId: registerResponse.deviceId },
{ setHeader() {} } as any,
),
),
).rejects.toBeInstanceOf(ForbiddenException);
});
it('handles missing audio asset files cleanly', async () => {
const registerResponse = await devicesController.register({
platform: 'IPHONE',
deviceName: 'Playback iPhone',
appVersion: '0.1.0',
});
const assetId = randomUUID();
prismaState.audioAssets.set(assetId, {
id: assetId,
userId: prismaState.defaultUser.id,
trackId: randomUUID(),
sha256: 'sha-missing-file',
storageKey: join(
'users',
prismaState.defaultUser.id,
'audio',
'missing-file.mp3',
),
originalFilename: 'missing-file.mp3',
mimeType: 'audio/mpeg',
fileExtension: 'mp3',
fileSizeBytes: BigInt(10),
durationMs: 180000,
sourceDeviceId: registerResponse.deviceId,
createdAt: new Date('2026-05-29T08:00:00.000Z'),
});
await expect(
runAsDevice(registerResponse.deviceAccessToken, () =>
assetsController.download(
assetId,
{ deviceId: registerResponse.deviceId },
{ setHeader() {} } as any,
),
),
).rejects.toBeInstanceOf(NotFoundException);
});
it('downloads artwork bytes for the owning device user with the stored image mime type', async () => {
const registerResponse = await devicesController.register({
platform: 'IPHONE',
deviceName: 'Artwork iPhone',
appVersion: '0.1.0',
});
const artworkId = randomUUID();
const trackId = randomUUID();
const bytes = Buffer.from(
'iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVQIW2P8z8DwHwAFgwJ/lBi4NwAAAABJRU5ErkJggg==',
'base64',
);
const storageKey = join(
'users',
prismaState.defaultUser.id,
'artwork',
`${sha256Hex(bytes)}.png`,
);
prismaState.artworkAssets.set(artworkId, {
id: artworkId,
userId: prismaState.defaultUser.id,
sha256: sha256Hex(bytes),
storageKey,
mimeType: 'image/png',
width: 1,
height: 1,
fileSizeBytes: BigInt(bytes.length),
createdAt: new Date('2026-05-29T08:00:00.000Z'),
tracks: [{ userId: prismaState.defaultUser.id }],
});
prismaState.tracks.set(trackId, {
id: trackId,
userId: prismaState.defaultUser.id,
primaryAudioAssetId: null,
artworkAssetId: artworkId,
title: 'Artwork Track',
artist: 'Velody',
album: null,
albumArtist: null,
genre: null,
discNumber: null,
trackNumber: null,
year: null,
durationMs: null,
status: 'ACTIVE',
deletedAt: null,
createdAt: new Date('2026-05-29T08:00:00.000Z'),
updatedAt: new Date('2026-05-29T08:02:00.000Z'),
});
const filePath = join(storageRoot, storageKey);
await mkdir(dirname(filePath), { recursive: true });
await writeFile(filePath, bytes);
const headers = new Map<string, string>();
const responseMock = {
setHeader(name: string, value: string) {
headers.set(name.toLowerCase(), String(value));
},
} as any;
const streamable = await runAsDevice(registerResponse.deviceAccessToken, () =>
artworkController.download(
artworkId,
{ deviceId: registerResponse.deviceId },
responseMock,
),
);
const downloadedBytes = await streamToBuffer(streamable.getStream());
expect(downloadedBytes.equals(bytes)).toBe(true);
expect(headers.get('content-type')).toBe('image/png');
expect(headers.get('content-length')).toBe(String(bytes.length));
});
it('rejects artwork download requests for another user artwork', async () => {
const registerResponse = await devicesController.register({
platform: 'IPHONE',
deviceName: 'Artwork iPhone',
appVersion: '0.1.0',
});
const artworkId = randomUUID();
const otherUserId = randomUUID();
prismaState.artworkAssets.set(artworkId, {
id: artworkId,
userId: otherUserId,
sha256: 'sha-other-artwork',
storageKey: join('users', otherUserId, 'artwork', 'sha-other-artwork.png'),
mimeType: 'image/png',
width: 1,
height: 1,
fileSizeBytes: BigInt(10),
createdAt: new Date('2026-05-29T08:00:00.000Z'),
});
await expect(
runAsDevice(registerResponse.deviceAccessToken, () =>
artworkController.download(
artworkId,
{ deviceId: registerResponse.deviceId },
{ setHeader() {} } as any,
),
),
).rejects.toBeInstanceOf(ForbiddenException);
});
it('returns not found when the requested artwork file is missing', async () => {
const registerResponse = await devicesController.register({
platform: 'IPHONE',
deviceName: 'Artwork iPhone',
appVersion: '0.1.0',
});
const artworkId = randomUUID();
prismaState.artworkAssets.set(artworkId, {
id: artworkId,
userId: prismaState.defaultUser.id,
sha256: 'sha-missing-artwork',
storageKey: join(
'users',
prismaState.defaultUser.id,
'artwork',
'sha-missing-artwork.png',
),
mimeType: 'image/png',
width: 1,
height: 1,
fileSizeBytes: BigInt(10),
createdAt: new Date('2026-05-29T08:00:00.000Z'),
tracks: [{ userId: prismaState.defaultUser.id }],
});
await expect(
runAsDevice(registerResponse.deviceAccessToken, () =>
artworkController.download(
artworkId,
{ deviceId: registerResponse.deviceId },
{ setHeader() {} } as any,
),
),
).rejects.toBeInstanceOf(NotFoundException);
});
it('rejects an invalid asset download device id query', async () => {
const validationPipe = new ValidationPipe({
whitelist: true,
forbidNonWhitelisted: true,
transform: true,
});
await expect(
validationPipe.transform(
{ deviceId: 'not-a-uuid' },
{
type: 'query',
metatype: AssetDownloadQueryDto,
data: '',
},
),
).rejects.toMatchObject({
response: {
message: expect.arrayContaining(['deviceId must be a UUID']),
},
});
});
it('returns remote library metadata for the requesting device owner', async () => {
const primaryDevice = await devicesController.register({
platform: 'IPHONE',
deviceName: 'iPhone',
appVersion: '0.1.0',
});
const secondUserId = randomUUID();
const secondaryDeviceId = randomUUID();
const primaryTrackId = randomUUID();
const primaryAssetId = randomUUID();
const primaryArtworkId = randomUUID();
const placeholderTrackId = randomUUID();
const placeholderAssetId = randomUUID();
const secondaryTrackId = randomUUID();
const secondaryAssetId = randomUUID();
prismaState.devices.set(secondaryDeviceId, {
id: secondaryDeviceId,
userId: secondUserId,
platform: 'IPHONE',
deviceName: 'Other iPhone',
appVersion: '0.1.0',
installTokenHash: 'other-device-hash',
lastSeenAt: new Date(),
createdAt: new Date(),
updatedAt: new Date(),
});
prismaState.audioAssets.set(primaryAssetId, {
id: primaryAssetId,
userId: prismaState.defaultUser.id,
trackId: primaryTrackId,
sha256: 'sha-default',
storageKey: 'users/default/audio/sha-default.mp3',
originalFilename: 'default.mp3',
mimeType: 'audio/mpeg',
fileExtension: 'mp3',
fileSizeBytes: BigInt(42),
durationMs: 245000,
sourceDeviceId: primaryDevice.deviceId,
createdAt: new Date('2026-05-29T08:00:00.000Z'),
});
prismaState.artworkAssets.set(primaryArtworkId, {
id: primaryArtworkId,
userId: prismaState.defaultUser.id,
sha256: 'artwork-sha-default',
storageKey: `users/${prismaState.defaultUser.id}/artwork/artwork-sha-default.png`,
mimeType: 'image/png',
width: 512,
height: 512,
fileSizeBytes: BigInt(128),
createdAt: new Date('2026-05-29T08:00:30.000Z'),
tracks: [{ userId: prismaState.defaultUser.id }],
});
prismaState.audioAssets.set(placeholderAssetId, {
id: placeholderAssetId,
userId: prismaState.defaultUser.id,
trackId: placeholderTrackId,
sha256: 'sha-no-artwork',
storageKey: 'users/default/audio/sha-no-artwork.mp3',
originalFilename: 'no-artwork.mp3',
mimeType: 'audio/mpeg',
fileExtension: 'mp3',
fileSizeBytes: BigInt(64),
durationMs: 201000,
sourceDeviceId: primaryDevice.deviceId,
createdAt: new Date('2026-05-29T08:00:45.000Z'),
});
prismaState.audioAssets.set(secondaryAssetId, {
id: secondaryAssetId,
userId: secondUserId,
trackId: secondaryTrackId,
sha256: 'sha-other-user',
storageKey: 'users/other/audio/sha-other-user.mp3',
originalFilename: 'other.mp3',
mimeType: 'audio/mpeg',
fileExtension: 'mp3',
fileSizeBytes: BigInt(24),
durationMs: 180000,
sourceDeviceId: secondaryDeviceId,
createdAt: new Date('2026-05-29T08:01:00.000Z'),
});
prismaState.tracks.set(primaryTrackId, {
id: primaryTrackId,
userId: prismaState.defaultUser.id,
primaryAudioAssetId: primaryAssetId,
artworkAssetId: primaryArtworkId,
title: 'Default User Track',
artist: 'Velody',
album: null,
albumArtist: null,
genre: null,
discNumber: null,
trackNumber: null,
year: null,
durationMs: 245000,
status: 'ACTIVE',
deletedAt: null,
createdAt: new Date('2026-05-29T08:00:00.000Z'),
updatedAt: new Date('2026-05-29T08:02:00.000Z'),
});
prismaState.tracks.set(secondaryTrackId, {
id: secondaryTrackId,
userId: secondUserId,
primaryAudioAssetId: secondaryAssetId,
artworkAssetId: null,
title: 'Other User Track',
artist: 'Elsewhere',
album: null,
albumArtist: null,
genre: null,
discNumber: null,
trackNumber: null,
year: null,
durationMs: 180000,
status: 'ACTIVE',
deletedAt: null,
createdAt: new Date('2026-05-29T08:01:00.000Z'),
updatedAt: new Date('2026-05-29T08:03:00.000Z'),
});
prismaState.tracks.set(placeholderTrackId, {
id: placeholderTrackId,
userId: prismaState.defaultUser.id,
primaryAudioAssetId: placeholderAssetId,
artworkAssetId: null,
title: 'No Artwork Track',
artist: 'Velody',
album: null,
albumArtist: null,
genre: null,
discNumber: null,
trackNumber: null,
year: null,
durationMs: 201000,
status: 'ACTIVE',
deletedAt: null,
createdAt: new Date('2026-05-29T08:00:45.000Z'),
updatedAt: new Date('2026-05-29T08:02:30.000Z'),
});
const response = await runAsDevice(primaryDevice.deviceAccessToken, () =>
libraryController.getTracks({
deviceId: primaryDevice.deviceId,
}),
);
expect(response).toEqual({
tracks: [
{
trackId: primaryTrackId,
title: 'Default User Track',
artist: 'Velody',
durationSeconds: 245,
sha256: 'sha-default',
assetId: primaryAssetId,
createdAt: '2026-05-29T08:00:00.000Z',
updatedAt: '2026-05-29T08:02:00.000Z',
artwork: {
artworkId: primaryArtworkId,
sha256: 'artwork-sha-default',
mimeType: 'image/png',
width: 512,
height: 512,
},
},
{
trackId: placeholderTrackId,
title: 'No Artwork Track',
artist: 'Velody',
durationSeconds: 201,
sha256: 'sha-no-artwork',
assetId: placeholderAssetId,
createdAt: '2026-05-29T08:00:45.000Z',
updatedAt: '2026-05-29T08:02:30.000Z',
artwork: null,
},
],
});
});
it('rejects an invalid remote library device id query', async () => {
const validationPipe = new ValidationPipe({
whitelist: true,
forbidNonWhitelisted: true,
transform: true,
});
await expect(
validationPipe.transform(
{ deviceId: 'not-a-uuid' },
{
type: 'query',
metatype: LibraryTracksQueryDto,
data: '',
},
),
).rejects.toMatchObject({
response: {
message: expect.arrayContaining(['deviceId must be a UUID']),
},
});
});
it('authenticates remote library requests with a valid device token and ignores spoofed device ids', async () => {
const ownerDevice = await devicesController.register({
platform: 'IPHONE',
deviceName: 'Auth iPhone',
appVersion: '0.1.0',
});
const foreignUserId = randomUUID();
const foreignDeviceId = randomUUID();
const ownerTrackId = randomUUID();
const ownerAssetId = randomUUID();
const foreignTrackId = randomUUID();
const foreignAssetId = randomUUID();
prismaState.devices.set(foreignDeviceId, {
id: foreignDeviceId,
userId: foreignUserId,
platform: 'IPHONE',
deviceName: 'Spoofed Foreign iPhone',
appVersion: '0.1.0',
installTokenHash: 'spoofed-device-hash',
tokenHash: sha256Hex(Buffer.from('foreign-device-token', 'utf8')),
tokenCreatedAt: new Date(),
lastSeenAt: new Date(),
createdAt: new Date(),
updatedAt: new Date(),
});
prismaState.audioAssets.set(ownerAssetId, {
id: ownerAssetId,
userId: prismaState.defaultUser.id,
trackId: ownerTrackId,
sha256: 'owner-library-sha',
storageKey: 'users/default/audio/owner-library-sha.mp3',
originalFilename: 'owner-library.mp3',
mimeType: 'audio/mpeg',
fileExtension: 'mp3',
fileSizeBytes: BigInt(42),
durationMs: 183000,
sourceDeviceId: ownerDevice.deviceId,
createdAt: new Date('2026-05-29T08:00:00.000Z'),
});
prismaState.audioAssets.set(foreignAssetId, {
id: foreignAssetId,
userId: foreignUserId,
trackId: foreignTrackId,
sha256: 'foreign-library-sha',
storageKey: 'users/foreign/audio/foreign-library-sha.mp3',
originalFilename: 'foreign-library.mp3',
mimeType: 'audio/mpeg',
fileExtension: 'mp3',
fileSizeBytes: BigInt(42),
durationMs: 204000,
sourceDeviceId: foreignDeviceId,
createdAt: new Date('2026-05-29T08:01:00.000Z'),
});
prismaState.tracks.set(ownerTrackId, {
id: ownerTrackId,
userId: prismaState.defaultUser.id,
primaryAudioAssetId: ownerAssetId,
artworkAssetId: null,
title: 'Authenticated Owner Track',
artist: 'Velody',
album: null,
albumArtist: null,
genre: null,
discNumber: null,
trackNumber: null,
year: null,
durationMs: 183000,
status: 'ACTIVE',
deletedAt: null,
createdAt: new Date('2026-05-29T08:00:00.000Z'),
updatedAt: new Date('2026-05-29T08:02:00.000Z'),
});
prismaState.tracks.set(foreignTrackId, {
id: foreignTrackId,
userId: foreignUserId,
primaryAudioAssetId: foreignAssetId,
artworkAssetId: null,
title: 'Foreign Track',
artist: 'Elsewhere',
album: null,
albumArtist: null,
genre: null,
discNumber: null,
trackNumber: null,
year: null,
durationMs: 204000,
status: 'ACTIVE',
deletedAt: null,
createdAt: new Date('2026-05-29T08:03:00.000Z'),
updatedAt: new Date('2026-05-29T08:04:00.000Z'),
});
await requestContextService.run(async () => {
await deviceAuthService.authenticateAuthorizationHeader(
`Bearer ${ownerDevice.deviceAccessToken}`,
);
await expect(
libraryController.getTracks({
deviceId: foreignDeviceId,
}),
).resolves.toEqual({
tracks: [
expect.objectContaining({
trackId: ownerTrackId,
title: 'Authenticated Owner Track',
assetId: ownerAssetId,
}),
],
});
});
});
it('lets linked devices under the same identity see the same library', async () => {
const identityUserId = randomUUID();
const primaryDevice = seedDevice({
userId: identityUserId,
deviceAccessToken: 'shared-library-primary-token',
deviceName: 'Identity Mac',
platform: 'MACOS',
});
const linkResponse = await registerDeviceWithAuthorization(
{
platform: 'IPHONE',
deviceName: 'Identity iPhone',
appVersion: '0.1.0',
},
`Bearer ${primaryDevice.deviceAccessToken}`,
);
const linkedDeviceToken = linkResponse.deviceAccessToken;
const linkedDeviceId = linkResponse.deviceId;
const trackId = randomUUID();
const assetId = randomUUID();
prismaState.audioAssets.set(assetId, {
id: assetId,
userId: identityUserId,
trackId,
sha256: 'shared-library-sha',
storageKey: `users/${identityUserId}/audio/shared-library-sha.mp3`,
originalFilename: 'shared-library.mp3',
mimeType: 'audio/mpeg',
fileExtension: 'mp3',
fileSizeBytes: BigInt(42),
durationMs: 198000,
sourceDeviceId: primaryDevice.deviceId,
createdAt: new Date('2026-05-29T08:00:00.000Z'),
});
prismaState.tracks.set(trackId, {
id: trackId,
userId: identityUserId,
primaryAudioAssetId: assetId,
artworkAssetId: null,
title: 'Shared Library Track',
artist: 'Velody',
album: null,
albumArtist: null,
genre: null,
discNumber: null,
trackNumber: null,
year: null,
durationMs: 198000,
status: 'ACTIVE',
deletedAt: null,
createdAt: new Date('2026-05-29T08:00:00.000Z'),
updatedAt: new Date('2026-05-29T08:02:00.000Z'),
});
const primaryLibrary = await runAsDevice(
primaryDevice.deviceAccessToken!,
() => libraryController.getTracks({}),
);
const linkedLibrary = await runAsDevice(linkedDeviceToken, () =>
libraryController.getTracks({
deviceId: linkedDeviceId,
}),
);
expect(primaryLibrary.tracks).toEqual([
expect.objectContaining({
trackId,
assetId,
title: 'Shared Library Track',
}),
]);
expect(linkedLibrary.tracks).toEqual(primaryLibrary.tracks);
});
it('returns 401 when library is requested without Authorization even if deviceId is supplied', async () => {
const ownerDevice = await devicesController.register({
platform: 'IPHONE',
deviceName: 'Legacy iPhone',
appVersion: '0.1.0',
});
await expect(
libraryController.getTracks({
deviceId: ownerDevice.deviceId,
}),
).rejects.toBeInstanceOf(UnauthorizedException);
});
it('rejects invalid or revoked device tokens even when a legacy device id is supplied', async () => {
const ownerDevice = await devicesController.register({
platform: 'IPHONE',
deviceName: 'Rejected iPhone',
appVersion: '0.1.0',
});
await requestContextService.run(async () => {
await expect(
deviceAuthService.authenticateAuthorizationHeader(
'Bearer invalid-device-token',
),
).rejects.toBeInstanceOf(UnauthorizedException);
});
prismaState.devices.set(ownerDevice.deviceId, {
...prismaState.devices.get(ownerDevice.deviceId),
tokenRevokedAt: new Date('2026-06-09T10:00:00.000Z'),
});
await requestContextService.run(async () => {
await expect(
deviceAuthService.authenticateAuthorizationHeader(
`Bearer ${ownerDevice.deviceAccessToken}`,
),
).rejects.toBeInstanceOf(UnauthorizedException);
});
});
it('rejects cross-owner asset and artwork downloads for authenticated tokens', async () => {
const ownerDevice = await devicesController.register({
platform: 'IPHONE',
deviceName: 'Owner iPhone',
appVersion: '0.1.0',
});
const foreignUserId = randomUUID();
const foreignDeviceId = randomUUID();
const foreignAssetId = randomUUID();
const foreignArtworkId = randomUUID();
prismaState.devices.set(foreignDeviceId, {
id: foreignDeviceId,
userId: foreignUserId,
platform: 'IPHONE',
deviceName: 'Foreign iPhone',
appVersion: '0.1.0',
installTokenHash: 'foreign-install-hash',
tokenHash: sha256Hex(Buffer.from('foreign-access-token', 'utf8')),
tokenCreatedAt: new Date(),
lastSeenAt: new Date(),
createdAt: new Date(),
updatedAt: new Date(),
});
prismaState.audioAssets.set(foreignAssetId, {
id: foreignAssetId,
userId: foreignUserId,
trackId: randomUUID(),
sha256: 'foreign-asset-sha',
storageKey: 'users/foreign/audio/foreign-asset-sha.mp3',
originalFilename: 'foreign-asset.mp3',
mimeType: 'audio/mpeg',
fileExtension: 'mp3',
fileSizeBytes: BigInt(64),
durationMs: 190000,
sourceDeviceId: foreignDeviceId,
createdAt: new Date(),
});
prismaState.artworkAssets.set(foreignArtworkId, {
id: foreignArtworkId,
userId: foreignUserId,
sha256: 'foreign-artwork-sha',
storageKey: 'users/foreign/artwork/foreign-artwork-sha.png',
mimeType: 'image/png',
width: 512,
height: 512,
fileSizeBytes: BigInt(64),
createdAt: new Date(),
tracks: [],
});
await requestContextService.run(async () => {
await deviceAuthService.authenticateAuthorizationHeader(
`Bearer ${ownerDevice.deviceAccessToken}`,
);
await expect(
assetsController.download(
foreignAssetId,
{},
{ setHeader() {} } as any,
),
).rejects.toBeInstanceOf(ForbiddenException);
await expect(
artworkController.download(
foreignArtworkId,
{},
{ setHeader() {} } as any,
),
).rejects.toBeInstanceOf(ForbiddenException);
});
});
it('updates tokenLastUsedAt during authenticated heartbeat', async () => {
const ownerDevice = await devicesController.register({
platform: 'MACOS',
deviceName: 'Heartbeat Mac',
appVersion: '0.1.0',
});
expect(prismaState.devices.get(ownerDevice.deviceId)?.tokenLastUsedAt).toBeUndefined();
await requestContextService.run(async () => {
await deviceAuthService.authenticateAuthorizationHeader(
`Bearer ${ownerDevice.deviceAccessToken}`,
);
const response = await devicesController.heartbeat({
appVersion: '0.1.1',
});
expect(response.ok).toBe(true);
});
expect(prismaState.devices.get(ownerDevice.deviceId)?.tokenLastUsedAt).toEqual(
expect.any(Date),
);
});
it('makes an upload from one device visible to another linked device under the same owner', async () => {
const identityUserId = randomUUID();
const primaryDevice = seedDevice({
userId: identityUserId,
deviceAccessToken: 'linked-upload-primary-token',
deviceName: 'Upload Mac',
platform: 'MACOS',
});
const linkResponse = await registerDeviceWithAuthorization(
{
platform: 'IPHONE',
deviceName: 'Upload iPhone',
appVersion: '0.1.0',
},
`Bearer ${primaryDevice.deviceAccessToken}`,
);
const linkedDeviceToken = linkResponse.deviceAccessToken;
const bytes = sampleMp3Bytes('linked-upload');
const sha256 = sha256Hex(bytes);
const prepareResponse = await runAsDevice(
primaryDevice.deviceAccessToken!,
() =>
uploadsController.prepare({
sha256,
originalFilename: 'linked-upload.mp3',
sizeBytes: bytes.length,
}),
);
expect(prepareResponse.status).toBe('upload_required');
expect(prepareResponse.uploadId).toBeDefined();
await runAsDevice(primaryDevice.deviceAccessToken!, () =>
uploadsController.uploadFile(
prepareResponse.uploadId!,
createUploadRequest(bytes),
),
);
const finalizeResponse = await runAsDevice(
primaryDevice.deviceAccessToken!,
() =>
uploadsController.finalize(prepareResponse.uploadId!, {
title: 'Linked Upload Track',
artist: 'Velody',
durationMs: 123000,
}),
);
const linkedLibrary = await runAsDevice(linkedDeviceToken, () =>
libraryController.getTracks({}),
);
expect(linkedLibrary.tracks).toEqual([
expect.objectContaining({
trackId: finalizeResponse.trackId,
assetId: finalizeResponse.assetId,
title: 'Linked Upload Track',
}),
]);
});
it('supports the MP3 upload pipeline through the Nest app wiring', async () => {
const registerResponse = await devicesController.register({
platform: 'MACOS',
deviceName: 'Upload Mac',
appVersion: '0.1.0',
});
const bytes = sampleMp3Bytes('e2e-upload');
const sha256 = sha256Hex(bytes);
const prepareResponse = await runAsDevice(registerResponse.deviceAccessToken, () =>
uploadsController.prepare({
deviceId: registerResponse.deviceId,
sha256,
originalFilename: 'e2e-upload.mp3',
sizeBytes: bytes.length,
}),
);
expect(prepareResponse.status).toBe('upload_required');
const uploadResponse = await runAsDevice(registerResponse.deviceAccessToken, () =>
uploadsService.uploadFile(
prepareResponse.uploadId!,
createUploadRequest(bytes),
),
);
expect(uploadResponse.status).toBe('COMPLETED');
const finalizeResponse = await runAsDevice(
registerResponse.deviceAccessToken,
() =>
uploadsController.finalize(prepareResponse.uploadId!, {
title: 'Uploaded Track',
artist: 'Velody',
album: 'Milestone 6',
durationMs: 222000,
}),
);
expect(finalizeResponse.trackId).toBeDefined();
expect(finalizeResponse.assetId).toBeDefined();
const storedBytes = await readFile(
join(storageRoot, 'users', prismaState.defaultUser.id, 'audio', `${sha256}.mp3`),
);
expect(storedBytes.equals(bytes)).toBe(true);
const duplicatePrepare = await runAsDevice(
registerResponse.deviceAccessToken,
() =>
uploadsController.prepare({
deviceId: registerResponse.deviceId,
sha256,
originalFilename: 'e2e-upload.mp3',
sizeBytes: bytes.length,
}),
);
expect(duplicatePrepare.status).toBe('exists');
expect(duplicatePrepare.uploadId).toBeDefined();
expect(prismaState.audioAssets.size).toBe(1);
expect(prismaState.libraryEvents.size).toBe(1);
});
it('supports upload finalize with embedded artwork and exposes remote artwork metadata', async () => {
const registerResponse = await devicesController.register({
platform: 'MACOS',
deviceName: 'Artwork Upload Mac',
appVersion: '0.1.0',
});
const bytes = sampleMp3Bytes('e2e-upload-artwork');
const artworkBytes = Buffer.from(
'iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVQIW2P8z8DwHwAFgwJ/lBi4NwAAAABJRU5ErkJggg==',
'base64',
);
const sha256 = sha256Hex(bytes);
const artworkSha256 = sha256Hex(artworkBytes);
const prepareResponse = await runAsDevice(registerResponse.deviceAccessToken, () =>
uploadsController.prepare({
deviceId: registerResponse.deviceId,
sha256,
originalFilename: 'e2e-upload-artwork.mp3',
sizeBytes: bytes.length,
}),
);
expect(prepareResponse.status).toBe('upload_required');
const uploadResponse = await runAsDevice(registerResponse.deviceAccessToken, () =>
uploadsService.uploadFile(
prepareResponse.uploadId!,
createUploadRequest(bytes),
),
);
expect(uploadResponse.status).toBe('COMPLETED');
const finalizeResponse = await runAsDevice(
registerResponse.deviceAccessToken,
() =>
uploadsController.finalize(prepareResponse.uploadId!, {
title: 'Uploaded Artwork Track',
artist: 'Velody',
album: 'Milestone 8.1',
durationMs: 222000,
artwork: {
dataBase64: artworkBytes.toString('base64'),
sha256: artworkSha256,
mimeType: 'image/png',
width: 1,
height: 1,
},
}),
);
expect(finalizeResponse.trackId).toBeDefined();
expect(prismaState.artworkAssets.size).toBe(1);
const remoteLibrary = await runAsDevice(registerResponse.deviceAccessToken, () =>
libraryController.getTracks({
deviceId: registerResponse.deviceId,
}),
);
expect(remoteLibrary.tracks).toEqual([
expect.objectContaining({
trackId: finalizeResponse.trackId,
artwork: {
artworkId: expect.any(String),
sha256: artworkSha256,
mimeType: 'image/png',
width: 1,
height: 1,
},
}),
]);
const artworkAsset = [...prismaState.artworkAssets.values()][0];
const storedArtworkBytes = await readFile(
join(
storageRoot,
'users',
prismaState.defaultUser.id,
'artwork',
`${artworkSha256}.png`,
),
);
expect(storedArtworkBytes.equals(artworkBytes)).toBe(true);
const headers = new Map<string, string>();
const responseMock = {
setHeader(name: string, value: string) {
headers.set(name.toLowerCase(), String(value));
},
} as any;
const streamable = await runAsDevice(registerResponse.deviceAccessToken, () =>
artworkController.download(
artworkAsset.id,
{ deviceId: registerResponse.deviceId },
responseMock,
),
);
const downloadedArtworkBytes = await streamToBuffer(streamable.getStream());
expect(downloadedArtworkBytes.equals(artworkBytes)).toBe(true);
expect(headers.get('content-type')).toBe('image/png');
});
it('attaches artwork through the deduped prepare exists path and returns non-null remote artwork metadata', async () => {
const registerResponse = await devicesController.register({
platform: 'MACOS',
deviceName: 'Deduped Artwork Upload Mac',
appVersion: '0.1.0',
});
const bytes = sampleMp3Bytes('e2e-deduped-artwork');
const artworkBytes = Buffer.from(
'iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVQIW2P8z8DwHwAFgwJ/lBi4NwAAAABJRU5ErkJggg==',
'base64',
);
const sha256 = sha256Hex(bytes);
const artworkSha256 = sha256Hex(artworkBytes);
const firstPrepare = await runAsDevice(registerResponse.deviceAccessToken, () =>
uploadsController.prepare({
deviceId: registerResponse.deviceId,
sha256,
originalFilename: 'e2e-deduped-artwork.mp3',
sizeBytes: bytes.length,
}),
);
expect(firstPrepare.status).toBe('upload_required');
const uploadResponse = await runAsDevice(registerResponse.deviceAccessToken, () =>
uploadsService.uploadFile(
firstPrepare.uploadId!,
createUploadRequest(bytes),
),
);
expect(uploadResponse.status).toBe('COMPLETED');
const firstFinalize = await runAsDevice(
registerResponse.deviceAccessToken,
() =>
uploadsController.finalize(firstPrepare.uploadId!, {
title: 'Deduped Artwork Track',
artist: 'Velody',
album: 'Milestone 8.1',
durationMs: 222000,
}),
);
const secondPrepare = await runAsDevice(
registerResponse.deviceAccessToken,
() =>
uploadsController.prepare({
deviceId: registerResponse.deviceId,
sha256,
originalFilename: 'e2e-deduped-artwork.mp3',
sizeBytes: bytes.length,
}),
);
expect(secondPrepare.status).toBe('exists');
expect(secondPrepare.uploadId).toBeDefined();
expect(prismaState.audioAssets.size).toBe(1);
const secondFinalize = await runAsDevice(
registerResponse.deviceAccessToken,
() =>
uploadsController.finalize(secondPrepare.uploadId!, {
title: 'Deduped Artwork Track',
artist: 'Velody',
album: 'Milestone 8.1',
durationMs: 222000,
artwork: {
dataBase64: artworkBytes.toString('base64'),
sha256: artworkSha256,
mimeType: 'image/png',
width: 1,
height: 1,
},
}),
);
expect(secondFinalize.trackId).toBe(firstFinalize.trackId);
expect(secondFinalize.assetId).toBe(firstFinalize.assetId);
expect(prismaState.artworkAssets.size).toBe(1);
const remoteLibrary = await runAsDevice(registerResponse.deviceAccessToken, () =>
libraryController.getTracks({
deviceId: registerResponse.deviceId,
}),
);
expect(remoteLibrary.tracks).toEqual([
expect.objectContaining({
trackId: firstFinalize.trackId,
artwork: {
artworkId: expect.any(String),
sha256: artworkSha256,
mimeType: 'image/png',
width: 1,
height: 1,
},
}),
]);
});
});