/** * Admin workflow API — route tests (CRUD, versioning, RBAC gate, approvals). */ const request = require('supertest'); const { bootCrmDb, seedMinimal, assignAdminRole, mintAdminToken, buildRouteApp, } = require('./helpers/crmDb'); let db; let cleanup; let app; let token; let noPermToken; const sampleGraph = { name: 'Test flow', trigger_type: 'invoice.sent', enabled: false, nodes: [ { node_key: 'n1', type: 'trigger' }, { node_key: 'n2', type: 'action', config: { action: 'noop' } }, ], edges: [{ from_node: 'n1', to_node: 'n2' }], }; beforeAll(async () => { ({ db, cleanup } = await bootCrmDb()); const { adminId } = await seedMinimal(db); await assignAdminRole(db, adminId, 'super_admin'); token = mintAdminToken(adminId); const ins = await db('admin_users').insert({ username: 'norole', email: 'nr@example.com', password_hash: 'x', must_change_password: false, created_at: new Date(), }).returning('id'); noPermToken = mintAdminToken(ins[0]?.id ?? ins[0]); await db('feature_flags').insert({ key: 'workflows', value: true }); app = buildRouteApp('/api/admin/workflows', require('../../src/routes/adminWorkflows')); }); afterAll(async () => { await cleanup(); }); const auth = (t) => ({ Authorization: `Bearer ${t}` }); describe('admin workflows API', () => { let createdId; test('create → 201 with id', async () => { const res = await request(app).post('/api/admin/workflows').set(auth(token)).send(sampleGraph); expect(res.status).toBe(201); expect(res.body.id).toBeGreaterThan(0); createdId = res.body.id; }); test('rejects a graph without exactly one trigger', async () => { const res = await request(app).post('/api/admin/workflows').set(auth(token)) .send({ ...sampleGraph, nodes: [{ node_key: 'x', type: 'action' }], edges: [] }); expect(res.status).toBe(400); }); test('get one returns the graph', async () => { const res = await request(app).get(`/api/admin/workflows/${createdId}`).set(auth(token)); expect(res.status).toBe(200); expect(res.body.nodes).toHaveLength(2); expect(res.body.edges).toHaveLength(1); expect(res.body.version).toBe(1); }); test('list includes it', async () => { const res = await request(app).get('/api/admin/workflows').set(auth(token)); expect(res.status).toBe(200); expect(res.body.some((w) => w.id === createdId)).toBe(true); }); test('update bumps the version', async () => { const res = await request(app).put(`/api/admin/workflows/${createdId}`).set(auth(token)) .send({ ...sampleGraph, name: 'Renamed' }); expect(res.status).toBe(200); expect(res.body.version).toBe(2); const get = await request(app).get(`/api/admin/workflows/${createdId}`).set(auth(token)); expect(get.body.name).toBe('Renamed'); expect(get.body.version).toBe(2); }); test('enable toggle', async () => { const res = await request(app).patch(`/api/admin/workflows/${createdId}/enabled`).set(auth(token)).send({ enabled: true }); expect(res.status).toBe(200); expect(res.body.enabled).toBe(true); }); test('approvals inbox returns an array', async () => { const res = await request(app).get('/api/admin/workflows/approvals').set(auth(token)); expect(res.status).toBe(200); expect(Array.isArray(res.body)).toBe(true); }); test('a role without workflows.manage is forbidden from writing', async () => { const res = await request(app).post('/api/admin/workflows').set(auth(noPermToken)).send(sampleGraph); expect(res.status).toBe(403); }); });