fix(uploads): serialize status filters and resize oversized covers
ci / module boundaries / boundaries (push) Successful in 9s
ci / contract / contract (сгенерированный код совпадает с openapi.yaml) (push) Successful in 1m37s
ci / api / api (push) Successful in 58s
ci / mobile / mobile (Metro-бандл Android) (push) Successful in 1m22s
ci / db / db (push) Successful in 6m19s
ci / ts / ts (packages + web) (push) Failing after 27m2s

This commit is contained in:
mrleo1nid committed 2026-10-29 12:14:49 +03:00
1 parent ca2a50b515
commit e78cab6ce6
11 files changed
+152 -17

No files matched your search

+29 -1
View File
@@ -38,6 +38,15 @@ async function setup(page: Page, scenario: 'queue' | 'template' | 'mine') {
const req = new Request(input, init);
const url = new URL(req.url);
if (scenario === 'queue' && url.pathname === '/api/v1/admin/uploads') return json({ items: [files[0], missing], next_cursor: null });
if (scenario === 'queue' && url.pathname.startsWith('/api/v1/admin/uploads/batches/') && req.method === 'PATCH') {
const body = await req.json();
const bytes = Uint8Array.from(atob(body.cover_base64), (char) => char.charCodeAt(0));
const image = await createImageBitmap(new Blob([bytes], { type: 'image/jpeg' }));
document.documentElement.dataset.coverDimensions = `${image.width}x${image.height}`;
document.documentElement.dataset.coverBytes = String(bytes.length);
image.close();
return json({ items: [files[0], missing] });
}
if (scenario === 'queue' && url.pathname === '/api/v1/admin/uploads/files/missing' && req.method === 'PATCH') {
const body = await req.json();
Object.assign(missing, { metadata: body.metadata, target_source_id: source.id, target_source_name: source.name, target_rel_path: 'Artist/Album/02 - Fixed.flac', status_reason: null });
@@ -59,11 +68,12 @@ async function setup(page: Page, scenario: 'queue' | 'template' | 'mine') {
if (url.pathname === `/api/v1/admin/sources/${source.id}`) return json(source);
}
if (scenario === 'mine' && url.pathname === '/api/v1/uploads/mine') {
if (url.searchParams.getAll('status').length > 1) return json({ code: 'common.bad_request', status: 400 }, 400);
if (url.searchParams.has('batch')) {
document.documentElement.dataset.batchPages = String(Number(document.documentElement.dataset.batchPages ?? 0) + 1);
return json({ items: [url.searchParams.has('cursor') ? files[2] : files[0]], next_cursor: url.searchParams.has('cursor') ? null : 'next' });
}
const statuses = url.searchParams.getAll('status');
const statuses = url.searchParams.get('status')?.split(',') ?? [];
const visible = [files[0], files[1], files[3], files[4]].filter((f) => !statuses.length || statuses.includes(f.status));
return json({ items: visible, next_cursor: 'more' });
}
@@ -253,3 +263,21 @@ test('starting another queue preview pauses the previous track', async ({ page }
await page.getByRole('button', { name: 'Свернуть пачку', exact: true }).click();
await expect.poll(() => first.evaluate((el: HTMLAudioElement) => el.paused)).toBe(true);
});
test('oversized album cover is reduced before saving the batch', async ({ page }) => {
await setup(page, 'queue');
const png = await page.evaluate(() => {
const canvas = document.createElement('canvas');
canvas.width = 3000; canvas.height = 1800;
canvas.getContext('2d')!.fillRect(0, 0, canvas.width, canvas.height);
return canvas.toDataURL('image/png').split(',')[1]!;
});
// A valid image with trailing padding reproduces an oversized input without a large fixture in Git.
const largeCover = Buffer.concat([Buffer.from(png, 'base64'), Buffer.alloc(12 * 1024 * 1024)]);
await page.getByRole('button', { name: 'На всю пачку', exact: true }).click();
const dialog = page.getByRole('dialog');
await dialog.locator('input[type=file]').setInputFiles({ name: 'Cover.png', mimeType: 'image/png', buffer: largeCover });
await dialog.getByRole('button', { name: 'Сохранить', exact: true }).click();
await expect(page.locator('html')).toHaveAttribute('data-cover-dimensions', '2048x1229');
expect(Number(await page.locator('html').getAttribute('data-cover-bytes'))).toBeLessThan(8 * 1024 * 1024);
});