Add consent-gated media exchange library
This commit is contained in:
1 parent
b8e4a1bb88
commit
bf474dfe10
42 files changed
+2533
-9
No files matched your search
@@ -0,0 +1,71 @@
|
||||
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;
|
||||
}
|
||||
Reference in new issue
Block a user