103 lines
3.3 KiB
TypeScript
103 lines
3.3 KiB
TypeScript
|
|
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
|
||
|
|
|
||
|
|
/**
|
||
|
|
* The doctor's zoom check (#467) samples one photo that zoom serves through
|
||
|
|
* Immich's full-size rendition. A rendition in a format the zoom route refuses
|
||
|
|
* (WebP) is not "fine" — the doctor used to say OK because one existed.
|
||
|
|
*/
|
||
|
|
|
||
|
|
vi.mock('@/lib/admin/auth', () => ({
|
||
|
|
isAdminEnabled: () => true,
|
||
|
|
isAdminAuthenticated: async () => true,
|
||
|
|
COOKIE_NAME: 'folio_admin_session',
|
||
|
|
}));
|
||
|
|
|
||
|
|
const ALBUM = '11111111-1111-1111-1111-111111111111';
|
||
|
|
const config = {
|
||
|
|
needsCredentials: false,
|
||
|
|
trustedProxyHops: 0,
|
||
|
|
authSecret: 'x'.repeat(64),
|
||
|
|
albums: [ALBUM],
|
||
|
|
standaloneAlbums: [ALBUM],
|
||
|
|
standaloneAlbumZoom: {},
|
||
|
|
zoom: true,
|
||
|
|
albumOverrides: {},
|
||
|
|
albumDownloads: {},
|
||
|
|
subpages: [],
|
||
|
|
albumPasswords: {},
|
||
|
|
sitePassword: '',
|
||
|
|
immich: { apiUrl: 'https://immich.example/api', apiKey: 'key' },
|
||
|
|
immichTimeoutMs: 1000,
|
||
|
|
legal: { enabled: false, name: '', address: '', zipCity: '', country: '' },
|
||
|
|
contact: { enabled: false, retentionDays: 90 },
|
||
|
|
privacy: { enabled: true },
|
||
|
|
};
|
||
|
|
|
||
|
|
vi.mock('@/lib/config', () => ({
|
||
|
|
getConfig: () => config,
|
||
|
|
slugify: (name: string) => name.toLowerCase().replace(/[^a-z0-9]+/g, '-'),
|
||
|
|
}));
|
||
|
|
vi.mock('@/lib/env', () => ({ env: { AUTH_SECRET: 'x'.repeat(64) } }));
|
||
|
|
vi.mock('@/lib/admin/journal-service', () => ({ listJournalEntries: async () => [] }));
|
||
|
|
vi.mock('@/lib/admin/yaml-service', () => ({ readSettingsYaml: async () => null }));
|
||
|
|
|
||
|
|
const immichMock = vi.hoisted(() => ({ getAlbum: vi.fn(), streamFullsize: vi.fn() }));
|
||
|
|
vi.mock('@/lib/immich', () => ({ immich: immichMock }));
|
||
|
|
|
||
|
|
import { GET } from '../doctor/route';
|
||
|
|
import { NextRequest } from 'next/server';
|
||
|
|
|
||
|
|
const zoomFinding = async () => {
|
||
|
|
const body = await (await GET(new NextRequest('http://localhost/api/admin/doctor'))).json();
|
||
|
|
return body.findings.find((f: { id: string }) => f.id === 'zoom-renditions');
|
||
|
|
};
|
||
|
|
|
||
|
|
const rendition = (contentType: string) => ({
|
||
|
|
stream: new ReadableStream(),
|
||
|
|
contentType,
|
||
|
|
contentLength: '10',
|
||
|
|
});
|
||
|
|
|
||
|
|
beforeEach(() => {
|
||
|
|
vi.stubGlobal(
|
||
|
|
'fetch',
|
||
|
|
vi.fn(async (url: string) =>
|
||
|
|
url.includes('/albums')
|
||
|
|
? ({ ok: true, json: async () => [{ id: ALBUM, albumName: 'Trip' }] } as Response)
|
||
|
|
: ({ ok: true, json: async () => ({}) } as Response),
|
||
|
|
),
|
||
|
|
);
|
||
|
|
immichMock.getAlbum.mockResolvedValue({
|
||
|
|
id: ALBUM,
|
||
|
|
albumName: 'Trip',
|
||
|
|
assets: [{ id: 'heic', type: 'IMAGE', originalMimeType: 'image/heic' }],
|
||
|
|
});
|
||
|
|
});
|
||
|
|
|
||
|
|
afterEach(() => {
|
||
|
|
vi.unstubAllGlobals();
|
||
|
|
vi.clearAllMocks();
|
||
|
|
});
|
||
|
|
|
||
|
|
describe('GET /api/admin/doctor — zoom renditions', () => {
|
||
|
|
it('is OK for a JPEG rendition', async () => {
|
||
|
|
immichMock.streamFullsize.mockResolvedValue(rendition('image/jpeg'));
|
||
|
|
expect((await zoomFinding())?.level).toBe('ok');
|
||
|
|
expect(immichMock.streamFullsize).toHaveBeenCalledWith('heic');
|
||
|
|
});
|
||
|
|
|
||
|
|
it('warns when Immich has none', async () => {
|
||
|
|
immichMock.streamFullsize.mockResolvedValue(null);
|
||
|
|
const finding = await zoomFinding();
|
||
|
|
expect(finding?.level).toBe('warn');
|
||
|
|
expect(finding?.title).toMatch(/no full-size preview/);
|
||
|
|
});
|
||
|
|
|
||
|
|
it('warns, asking for JPEG, when the rendition is WebP (review of #830)', async () => {
|
||
|
|
immichMock.streamFullsize.mockResolvedValue(rendition('image/webp'));
|
||
|
|
const finding = await zoomFinding();
|
||
|
|
expect(finding?.level).toBe('warn');
|
||
|
|
expect(finding?.title).toMatch(/not JPEG/);
|
||
|
|
});
|
||
|
|
});
|