135 lines
4.6 KiB
TypeScript
135 lines
4.6 KiB
TypeScript
import { test, expect } from '@playwright/test'
|
|
import { API_KEY_HEADER } from './helpers'
|
|
|
|
test.describe('Exec Approval Allowlist API', () => {
|
|
// ── GET /api/exec-approvals?action=allowlist ──
|
|
|
|
test('GET allowlist returns agents map and hash', async ({ request }) => {
|
|
const res = await request.get('/api/exec-approvals?action=allowlist', {
|
|
headers: API_KEY_HEADER,
|
|
})
|
|
expect(res.status()).toBe(200)
|
|
const body = await res.json()
|
|
expect(body).toHaveProperty('agents')
|
|
expect(typeof body.agents).toBe('object')
|
|
expect(body).toHaveProperty('hash')
|
|
expect(typeof body.hash).toBe('string')
|
|
})
|
|
|
|
// ── PUT /api/exec-approvals (save allowlist) ──
|
|
|
|
test('PUT save-allowlist persists and round-trips', async ({ request }) => {
|
|
// Read current state to get hash
|
|
const getRes = await request.get('/api/exec-approvals?action=allowlist', {
|
|
headers: API_KEY_HEADER,
|
|
})
|
|
expect(getRes.status()).toBe(200)
|
|
const current = await getRes.json()
|
|
|
|
// Save with a test agent pattern
|
|
const testAgent = `e2e-test-agent-${Date.now()}`
|
|
const putRes = await request.put('/api/exec-approvals', {
|
|
headers: API_KEY_HEADER,
|
|
data: {
|
|
agents: { [testAgent]: [{ pattern: 'echo *' }] },
|
|
hash: current.hash,
|
|
},
|
|
})
|
|
expect(putRes.status()).toBe(200)
|
|
const putBody = await putRes.json()
|
|
expect(putBody.ok).toBe(true)
|
|
expect(putBody.hash).toBeDefined()
|
|
|
|
// Read back and verify
|
|
const verifyRes = await request.get('/api/exec-approvals?action=allowlist', {
|
|
headers: API_KEY_HEADER,
|
|
})
|
|
expect(verifyRes.status()).toBe(200)
|
|
const verifyBody = await verifyRes.json()
|
|
expect(verifyBody.agents[testAgent]).toBeDefined()
|
|
expect(verifyBody.agents[testAgent]).toEqual([{ pattern: 'echo *' }])
|
|
|
|
// Clean up: remove the test agent by saving empty patterns
|
|
await request.put('/api/exec-approvals', {
|
|
headers: API_KEY_HEADER,
|
|
data: {
|
|
agents: { [testAgent]: [] },
|
|
hash: verifyBody.hash,
|
|
},
|
|
})
|
|
})
|
|
|
|
test('PUT save-allowlist with stale hash returns 409', async ({ request }) => {
|
|
const res = await request.put('/api/exec-approvals', {
|
|
headers: API_KEY_HEADER,
|
|
data: {
|
|
agents: { 'conflict-agent': [{ pattern: 'test' }] },
|
|
hash: 'stale-hash-value-that-does-not-match',
|
|
},
|
|
})
|
|
// 409 if the file exists and hash mismatches; 200 if file doesn't exist (no conflict possible)
|
|
if (res.status() === 409) {
|
|
const body = await res.json()
|
|
expect(body.code).toBe('CONFLICT')
|
|
} else {
|
|
// File didn't exist, so no conflict — clean up
|
|
expect(res.status()).toBe(200)
|
|
const body = await res.json()
|
|
// Re-read and clean up the test entry
|
|
const getRes = await request.get('/api/exec-approvals?action=allowlist', {
|
|
headers: API_KEY_HEADER,
|
|
})
|
|
const current = await getRes.json()
|
|
await request.put('/api/exec-approvals', {
|
|
headers: API_KEY_HEADER,
|
|
data: {
|
|
agents: { 'conflict-agent': [] },
|
|
hash: current.hash,
|
|
},
|
|
})
|
|
}
|
|
})
|
|
|
|
test('PUT save-allowlist rejects missing agents field', async ({ request }) => {
|
|
const res = await request.put('/api/exec-approvals', {
|
|
headers: API_KEY_HEADER,
|
|
data: {},
|
|
})
|
|
expect(res.status()).toBe(400)
|
|
const body = await res.json()
|
|
expect(body.error).toContain('agents')
|
|
})
|
|
|
|
// ── GET /api/exec-approvals (pending approvals) ──
|
|
|
|
test('GET pending approvals returns array or empty on gateway unavailable', async ({ request }) => {
|
|
const res = await request.get('/api/exec-approvals', { headers: API_KEY_HEADER })
|
|
expect(res.status()).toBe(200)
|
|
const body = await res.json()
|
|
// Returns { approvals: [] } when gateway is unavailable
|
|
expect(body).toHaveProperty('approvals')
|
|
expect(Array.isArray(body.approvals)).toBe(true)
|
|
})
|
|
|
|
// ── Auth ──────────────────────────────────────
|
|
|
|
test('GET without auth is rejected', async ({ request }) => {
|
|
const res = await request.get('/api/exec-approvals?action=allowlist')
|
|
expect(res.status()).toBe(401)
|
|
})
|
|
|
|
test('PUT without auth is rejected', async ({ request }) => {
|
|
const res = await request.put('/api/exec-approvals', {
|
|
data: { agents: {} },
|
|
})
|
|
expect(res.status()).toBe(401)
|
|
})
|
|
|
|
test('POST without auth is rejected', async ({ request }) => {
|
|
const res = await request.post('/api/exec-approvals', {
|
|
data: { id: 'test', action: 'approve' },
|
|
})
|
|
expect(res.status()).toBe(401)
|
|
})
|
|
})
|