import { describe, it, expect, beforeEach, afterEach } from 'vitest'; import express from 'express'; import request from 'supertest'; import { mkdtempSync, rmSync } from 'fs'; import { join } from 'path'; import { tmpdir } from 'os'; import { createUserFolderApi } from './user-folder-api.js'; // --------------------------------------------------------------------------- // Helpers // --------------------------------------------------------------------------- function makeApp(userId: string, userFolderRoot: string): express.Application { const app = express(); app.use((req, _res, next) => { (req as any).user = { id: userId, role: 'user' }; next(); }); app.use('/api/users/me', createUserFolderApi({ userFolderRoot })); return app; } // --------------------------------------------------------------------------- // Tests // --------------------------------------------------------------------------- describe('User Folder API — AGENTS.md routes', () => { let tmpRoot: string; let app: express.Application; const USER_A = 'user-a'; beforeEach(() => { tmpRoot = mkdtempSync(join(tmpdir(), 'agents-md-test-')); app = makeApp(USER_A, tmpRoot); }); afterEach(() => { rmSync(tmpRoot, { recursive: true, force: true }); }); // ── GET /agents-md ──────────────────────────────────────────────────────── it('GET /agents-md returns exists=false when file is missing', async () => { const res = await request(app).get('/api/users/me/agents-md'); expect(res.status).toBe(200); expect(res.body).toEqual({ exists: false, content: '' }); }); // ── PUT /agents-md ──────────────────────────────────────────────────────── it('PUT /agents-md writes content; subsequent GET reflects it', async () => { const text = '# My Instructions\n\nAlways be concise.\n'; const putRes = await request(app) .put('/api/users/me/agents-md') .set('Content-Type', 'text/plain') .send(text); expect(putRes.status).toBe(200); expect(putRes.body.ok).toBe(true); expect(typeof putRes.body.bytes).toBe('number'); const getRes = await request(app).get('/api/users/me/agents-md'); expect(getRes.status).toBe(200); expect(getRes.body.exists).toBe(true); expect(getRes.body.content).toBe(text); }); it('PUT /agents-md with oversized body returns 413', async () => { // 64 KB + 1 byte — exceeds USER_AGENTS_MAX_BYTES const oversized = 'x'.repeat(64 * 1024 + 1); const res = await request(app) .put('/api/users/me/agents-md') .set('Content-Type', 'text/plain') .send(oversized); expect(res.status).toBe(413); expect(res.body.error).toMatch(/exceeds/); }); // ── DELETE /agents-md ───────────────────────────────────────────────────── it('DELETE /agents-md removes the file', async () => { // First write something await request(app) .put('/api/users/me/agents-md') .set('Content-Type', 'text/plain') .send('# Hello\n'); // Confirm it exists const beforeGet = await request(app).get('/api/users/me/agents-md'); expect(beforeGet.body.exists).toBe(true); // Delete it const delRes = await request(app).delete('/api/users/me/agents-md'); expect(delRes.status).toBe(200); expect(delRes.body.ok).toBe(true); expect(delRes.body.existed).toBe(true); // Confirm it's gone const afterGet = await request(app).get('/api/users/me/agents-md'); expect(afterGet.body.exists).toBe(false); }); it('DELETE /agents-md when file does not exist returns ok=true, existed=false', async () => { const res = await request(app).delete('/api/users/me/agents-md'); expect(res.status).toBe(200); expect(res.body.ok).toBe(true); expect(res.body.existed).toBe(false); }); });