import { describe, it, expect, beforeEach, afterEach } from 'vitest'; import { createTestApp, type TestContext, loginAs, buildMultipart } from '../helpers/setup.ts'; import { createUser } from '../../src/db/users.ts'; import { hashPassword } from '../../src/services/auth.ts'; describe('GET /api/v1/files', () => { let ctx: TestContext; let token: string; beforeEach(async () => { ctx = createTestApp(); const hash = await hashPassword('secret'); createUser(ctx.db, { username: 'alice', passwordHash: hash }); token = await loginAs(ctx.app, 'alice', 'secret'); }); afterEach(async () => { await ctx.app.close(); ctx.cleanup(); }); it('returns empty list for new user', async () => { const res = await ctx.app.inject({ method: 'GET', url: '/api/v1/files', cookies: { token }, }); expect(res.statusCode).toBe(200); expect(res.json().files).toEqual([]); }); it('returns 401 without auth', async () => { const res = await ctx.app.inject({ method: 'GET', url: '/api/v1/files' }); expect(res.statusCode).toBe(401); }); }); describe('POST /api/v1/files', () => { let ctx: TestContext; let token: string; beforeEach(async () => { ctx = createTestApp(); const hash = await hashPassword('secret'); createUser(ctx.db, { username: 'alice', passwordHash: hash }); token = await loginAs(ctx.app, 'alice', 'secret'); }); afterEach(async () => { await ctx.app.close(); ctx.cleanup(); }); it('uploads a file and returns url', async () => { const res = await ctx.app.inject({ method: 'POST', url: '/api/v1/files', cookies: { token }, ...buildMultipart({ file: { filename: 'test.txt', contentType: 'text/plain', data: Buffer.from('hello') } }), }); expect(res.statusCode).toBe(201); const body = res.json(); expect(body.url).toMatch(/\/f\//); expect(body.file.original_name).toBe('test.txt'); }); it('returns 401 without auth', async () => { const res = await ctx.app.inject({ method: 'POST', url: '/api/v1/files' }); expect(res.statusCode).toBe(401); }); }); describe('DELETE /api/v1/files/:id', () => { let ctx: TestContext; let token: string; let fileId: string; beforeEach(async () => { ctx = createTestApp(); const hash = await hashPassword('secret'); createUser(ctx.db, { username: 'alice', passwordHash: hash }); token = await loginAs(ctx.app, 'alice', 'secret'); const uploadRes = await ctx.app.inject({ method: 'POST', url: '/api/v1/files', cookies: { token }, ...buildMultipart({ file: { filename: 'f.txt', contentType: 'text/plain', data: Buffer.from('data') } }), }); fileId = uploadRes.json().file.id; }); afterEach(async () => { await ctx.app.close(); ctx.cleanup(); }); it('deletes an owned file', async () => { const res = await ctx.app.inject({ method: 'DELETE', url: `/api/v1/files/${fileId}`, cookies: { token }, }); expect(res.statusCode).toBe(200); }); it('returns 404 for non-existent file', async () => { const res = await ctx.app.inject({ method: 'DELETE', url: '/api/v1/files/doesnotexist', cookies: { token }, }); expect(res.statusCode).toBe(404); }); it('returns 401 without auth', async () => { const res = await ctx.app.inject({ method: 'DELETE', url: `/api/v1/files/${fileId}` }); expect(res.statusCode).toBe(401); }); });