import { describe, it, expect, beforeAll, beforeEach } from 'vitest'; import request from 'supertest'; import { setup } from './helpers.js'; import * as spacesRepo from '../../lib/db/repos/spaces.js'; import * as agentsRepo from '../../lib/db/repos/agents.js'; import * as pendingChanges from '../../lib/db/repos/pending_changes.js'; let app, ownerHeaders, space; const owner = { kind: 'user', id: null }; async function mintAgent(slug, caps, scopes = {}) { const a = await agentsRepo.create({ slug, name: slug, kind: 'claude', model: 'sonnet', capabilities: caps, scopes }, owner); const { token } = await agentsRepo.createToken(a.id, 'test'); return { agent: a, headers: { Authorization: `Bearer ${token}` } }; } beforeAll(async () => { ({ app, ownerHeaders } = await setup()); }); beforeEach(async () => { space = await spacesRepo.create({ slug: `s-${Date.now()}-${Math.random().toString(36).slice(2,5)}`, name: 'S' }, owner); }); describe('capability enforcement on writes', () => { it('agent at allow tier writes through (201)', async () => { const { headers } = await mintAgent(`allow-${Date.now()}`, { read: true, write: true }, { page: true }); const res = await request(app) .post(`/api/spaces/${space.id}/pages`).set(headers) .send({ slug: 'a', title: 'A', body_md: 'hi' }); expect(res.status).toBe(201); }); it('agent at suggest tier diverts to pending_changes (202)', async () => { const { agent, headers } = await mintAgent(`sug-${Date.now()}`, { read: true, suggest: true }); const res = await request(app) .post(`/api/spaces/${space.id}/pages`).set(headers) .send({ slug: 'p', title: 'P', body_md: 'draft' }); expect(res.status).toBe(202); expect(res.body.pending).toBe(true); expect(res.body.change_id).toBeDefined(); const change = await pendingChanges.getById(res.body.change_id); expect(change.agent_id).toBe(agent.id); expect(change.entity_type).toBe('page'); expect(change.action).toBe('create'); expect(change.payload.title).toBe('P'); }); it('agent at deny tier → 403', async () => { const { headers } = await mintAgent(`deny-${Date.now()}`, { read: true }); const res = await request(app) .post(`/api/spaces/${space.id}/pages`).set(headers) .send({ slug: 'p', title: 'P' }); expect(res.status).toBe(403); }); it('owner still writes through', async () => { const res = await request(app) .post(`/api/spaces/${space.id}/pages`).set(ownerHeaders) .send({ slug: 'owner', title: 'Owner' }); expect(res.status).toBe(201); }); });