Files
kaidi/frontend/tests/archive-frontend-r4.test.ts
2026-08-17 14:08:40 +08:00

66 lines
2.8 KiB
TypeScript

import { readFileSync } from 'node:fs';
import { describe, expect, it } from 'vitest';
import {
ARCHIVE_API_BASE,
archiveErrorText,
normalizeArchiveFileDetail,
normalizeArchivePackage,
} from '../src/api/archive';
describe('archive R4 frontend contracts', () => {
it('uses the documented plural archive API prefix', () => {
expect(ARCHIVE_API_BASE).toBe('/archives');
});
it('normalizes nullable archive response collections and action allowlists', () => {
const packageView = normalizeArchivePackage({
allowedActions: null,
items: [{ allowedActions: null }],
objectSnapshots: null,
actions: undefined,
} as any);
const detail = normalizeArchiveFileDetail({
file: { allowedActions: null },
versions: null,
borrows: [{ allowedActions: undefined }],
} as any);
expect(packageView).toMatchObject({
allowedActions: [],
items: [{ allowedActions: [] }],
objectSnapshots: [],
actions: [],
});
expect(detail).toMatchObject({ file: { allowedActions: [] }, versions: [], borrows: [{ allowedActions: [] }] });
});
it('renders actionable error classes without dropping server field errors', () => {
expect(archiveErrorText({ status: 401 }, '失败')).toContain('登录会话已失效');
expect(archiveErrorText({ status: 403 }, '失败')).toContain('没有访问');
expect(archiveErrorText({ status: 404 }, '失败')).toContain('不存在');
expect(archiveErrorText({ status: 409 }, '失败')).toContain('数据已发生变化');
expect(archiveErrorText({ status: 422, fieldErrors: { dueAt: '日期无效' } }, '失败')).toContain('日期无效');
expect(archiveErrorText({ status: 503 }, '失败')).toContain('服务处理失败');
});
it('keeps downloaded blob URLs alive until the browser consumes them', () => {
const page = readFileSync(new URL('../src/pages/archives/ArchiveFilesPage.vue', import.meta.url), 'utf8');
expect(page).toContain('window.setTimeout(() => URL.revokeObjectURL(url), 60_000)');
});
it('exposes a recoverable workflow for quarantined archive files', () => {
const api = readFileSync(new URL('../src/api/archive.ts', import.meta.url), 'utf8');
const page = readFileSync(new URL('../src/pages/archives/ArchiveFilesPage.vue', import.meta.url), 'utf8');
const rescanPath = '`$' + '{ARCHIVE_API_BASE}/files/$' + '{publicId}/rescan`';
expect(api).toContain('export function rescanArchiveFile');
expect(api).toContain(rescanPath);
expect(api).toContain("commandHeaders('archive-file-rescan'");
expect(page).toContain("row.allowedActions.includes('RESCAN')");
expect(page).toContain('@click="rescanFile(row)"');
expect(page).toContain("fileFilters.scanStatus = 'QUARANTINED'");
expect(page).toContain('文件已通过安全检查并恢复可用');
});
});