import 'dotenv/config'; import { test, expect } from '@playwright/test'; import { login, apiHeaders } from '../smoke/helpers'; /** * Real-API spec for the MinIO file lifecycle (Phase A PR11). * * Uploads a file via POST /api/v1/files, lists it, downloads it, asserts * byte-equality with the upload, then deletes it. Verifies port-isolation * by attempting download with no auth and expecting 401. * * Requires MINIO_* env to be configured (the dev-server startup already * validates these via env.ts). Skips when MINIO_ENDPOINT is unset. */ const MINIO_ENDPOINT = process.env.MINIO_ENDPOINT; test.describe('MinIO file lifecycle', () => { test.skip(!MINIO_ENDPOINT, 'MINIO_ENDPOINT not configured'); test.beforeEach(async ({ page }) => { await login(page, 'super_admin'); }); test('upload → list → download → delete round-trip', async ({ page }) => { const headers = await apiHeaders(page); const sentinel = `phase-a-minio-${Date.now()}`; const buffer = Buffer.from(sentinel.repeat(8)); // Upload const uploadRes = await page.request.post('/api/v1/files', { headers, multipart: { file: { name: 'phase-a-minio.txt', mimeType: 'text/plain', buffer, }, }, }); expect(uploadRes.ok(), `upload: ${uploadRes.status()}`).toBe(true); const uploadBody = (await uploadRes.json()) as { data: { id: string } }; const fileId = uploadBody.data.id; // List should include the file const list = await page.request.get('/api/v1/files?limit=50', { headers }); expect(list.ok()).toBe(true); const listBody = (await list.json()) as { data: Array<{ id: string }> }; expect(listBody.data.find((f) => f.id === fileId)).toBeDefined(); // Download — assert byte-equality const dlRes = await page.request.get(`/api/v1/files/${fileId}/download`, { headers }); expect(dlRes.ok()).toBe(true); const dlBody = await dlRes.body(); expect(dlBody.equals(buffer)).toBe(true); // Delete const delRes = await page.request.delete(`/api/v1/files/${fileId}`, { headers }); expect(delRes.ok()).toBe(true); }); });