Files
RedditBot/tests/media-library.test.ts

72 lines
3.1 KiB
TypeScript

import fs from 'node:fs/promises';
import os from 'node:os';
import path from 'node:path';
import sharp from 'sharp';
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
import { MediaLibraryRepository } from '../src/database/media-library-repository.js';
import { MediaLibraryConflictError, MediaLibraryService } from '../src/services/media-library.js';
import { createTestDb } from './testUtils.js';
describe('MediaLibraryService', () => {
let directory: string;
beforeEach(async () => {
directory = await fs.mkdtemp(path.join(os.tmpdir(), 'redditbot-library-'));
});
afterEach(async () => {
await fs.rm(directory, { recursive: true, force: true });
});
it('re-encodes uploads without metadata and stores editable AI classifications', async () => {
const db = createTestDb();
const repository = new MediaLibraryRepository(db);
const classifyLibraryImage = vi.fn().mockResolvedValue({
tags: ['selfie', 'topless'], safety: 'nsfw', description: 'A self portrait.', confidence: 0.92,
});
const service = new MediaLibraryService(repository, { classifyLibraryImage }, logger(), directory, () => 1234);
const source = await sharp({ create: { width: 8, height: 6, channels: 3, background: '#cc8844' } })
.withMetadata({ orientation: 6 })
.jpeg()
.toBuffer();
const asset = await service.upload({
bytes: source,
contentType: 'image/jpeg',
originalFileName: '../private portrait.jpg',
consentConfirmed: true,
});
expect(asset).toMatchObject({
tags: ['selfie', 'topless'], safety: 'nsfw', classificationStatus: 'ready', enabled: true,
consentConfirmedAt: 1234,
});
expect(classifyLibraryImage).toHaveBeenCalledOnce();
const outputMetadata = await sharp(service.resolvePath(asset)).metadata();
expect(outputMetadata.exif).toBeUndefined();
expect(outputMetadata.orientation).toBeUndefined();
expect(service.update(asset.id, { tags: ['selfie'], safety: 'sfw', enabled: false }))
.toMatchObject({ tags: ['selfie'], safety: 'sfw', enabled: false });
db.close();
});
it('keeps failed or low-confidence classifications disabled and rejects duplicate content', async () => {
const db = createTestDb();
const repository = new MediaLibraryRepository(db);
const source = await sharp({ create: { width: 4, height: 4, channels: 3, background: '#123456' } }).png().toBuffer();
const classifier = { classifyLibraryImage: vi.fn().mockRejectedValue(new Error('refused')) };
const service = new MediaLibraryService(repository, classifier, logger(), directory);
const first = await service.upload({ bytes: source, contentType: 'image/png', originalFileName: 'one.png', consentConfirmed: true });
expect(first).toMatchObject({ classificationStatus: 'failed', safety: 'unknown', tags: [], enabled: false });
await expect(service.upload({ bytes: source, contentType: 'image/png', originalFileName: 'two.png', consentConfirmed: true }))
.rejects.toBeInstanceOf(MediaLibraryConflictError);
db.close();
});
});
function logger() {
return { warn: vi.fn(), info: vi.fn(), debug: vi.fn(), error: vi.fn() } as never;
}