moving
Some checks failed
Installer Smoke / installer-smoke (push) Has been cancelled

This commit is contained in:
Oleg Maslov
2026-09-02 10:10:29 +02:00
commit 0c3e2ead3b
3841 changed files with 970576 additions and 0 deletions

View File

@@ -0,0 +1,235 @@
/**
* Compliance Template Routes Tests (M-03)
*
* Covers the 5-endpoint CRUD surface:
* GET /api/compliance/templates
* GET /api/compliance/templates/:id
* POST /api/compliance/templates
* PATCH /api/compliance/templates/:id
* DELETE /api/compliance/templates/:id
*/
import { describe, it, expect, beforeAll, afterAll } from 'vitest';
import fs from 'node:fs';
import path from 'node:path';
import os from 'node:os';
import type { FastifyInstance } from 'fastify';
import { MindDB, SessionStore, FrameStore } from '@waggle/core';
import { buildLocalServer } from '../../src/local/index.js';
import { injectWithAuth } from '../test-utils.js';
const ALL_ON = {
interactions: true,
oversight: true,
models: true,
provenance: true,
riskAssessment: true,
fria: true,
};
const ALL_OFF = {
interactions: false,
oversight: false,
models: false,
provenance: false,
riskAssessment: false,
fria: false,
};
describe('Compliance Template Routes (M-03)', () => {
let server: FastifyInstance;
let tmpDir: string;
beforeAll(async () => {
tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'waggle-compliance-templates-test-'));
const mind = new MindDB(path.join(tmpDir, 'personal.mind'));
const sessions = new SessionStore(mind);
const frames = new FrameStore(mind);
const s = sessions.create('template-test-seed');
frames.createIFrame(s.gop_id, 'seed', 'normal');
mind.close();
server = await buildLocalServer({ dataDir: tmpDir });
});
afterAll(async () => {
await server.close();
fs.rmSync(tmpDir, { recursive: true, force: true });
});
describe('POST /api/compliance/templates', () => {
it('creates a template with 201 + returns the full row', async () => {
const res = await injectWithAuth(server, {
method: 'POST',
url: '/api/compliance/templates',
payload: {
name: 'Test template',
description: 'unit test',
sections: ALL_ON,
riskClassification: 'high-risk',
orgName: 'Acme',
footerText: 'Confidential',
},
});
expect(res.statusCode).toBe(201);
const body = res.json();
expect(body.template.id).toBeGreaterThan(0);
expect(body.template.name).toBe('Test template');
expect(body.template.sections).toEqual(ALL_ON);
expect(body.template.riskClassification).toBe('high-risk');
});
it('400s on missing name', async () => {
const res = await injectWithAuth(server, {
method: 'POST',
url: '/api/compliance/templates',
payload: { sections: ALL_OFF },
});
expect(res.statusCode).toBe(400);
});
it('400s on missing sections', async () => {
const res = await injectWithAuth(server, {
method: 'POST',
url: '/api/compliance/templates',
payload: { name: 'no sections' },
});
expect(res.statusCode).toBe(400);
});
it('400s on invalid risk classification', async () => {
const res = await injectWithAuth(server, {
method: 'POST',
url: '/api/compliance/templates',
payload: { name: 'bad risk', sections: ALL_OFF, riskClassification: 'catastrophic' },
});
expect(res.statusCode).toBe(400);
});
});
describe('GET /api/compliance/templates', () => {
it('returns the list of all templates', async () => {
const res = await injectWithAuth(server, {
method: 'GET',
url: '/api/compliance/templates',
});
expect(res.statusCode).toBe(200);
const body = res.json();
expect(Array.isArray(body.templates)).toBe(true);
expect(body.templates.length).toBeGreaterThan(0);
});
});
describe('GET /api/compliance/templates/:id', () => {
it('returns a specific template', async () => {
const create = await injectWithAuth(server, {
method: 'POST',
url: '/api/compliance/templates',
payload: { name: 'fetch-me', sections: ALL_OFF },
});
const id = create.json().template.id;
const res = await injectWithAuth(server, {
method: 'GET',
url: `/api/compliance/templates/${id}`,
});
expect(res.statusCode).toBe(200);
expect(res.json().template.id).toBe(id);
});
it('404s on missing id', async () => {
const res = await injectWithAuth(server, {
method: 'GET',
url: '/api/compliance/templates/999999',
});
expect(res.statusCode).toBe(404);
});
it('400s on non-numeric id', async () => {
const res = await injectWithAuth(server, {
method: 'GET',
url: '/api/compliance/templates/not-a-number',
});
expect(res.statusCode).toBe(400);
});
});
describe('PATCH /api/compliance/templates/:id', () => {
it('updates partial fields and returns the new row', async () => {
const create = await injectWithAuth(server, {
method: 'POST',
url: '/api/compliance/templates',
payload: { name: 'orig', description: 'old', sections: ALL_OFF, orgName: 'X' },
});
const id = create.json().template.id;
const patch = await injectWithAuth(server, {
method: 'PATCH',
url: `/api/compliance/templates/${id}`,
payload: { name: 'renamed', orgName: null },
});
expect(patch.statusCode).toBe(200);
const body = patch.json();
expect(body.template.name).toBe('renamed');
expect(body.template.description).toBe('old'); // preserved
expect(body.template.orgName).toBeNull(); // cleared
});
it('404s on missing id', async () => {
const res = await injectWithAuth(server, {
method: 'PATCH',
url: '/api/compliance/templates/999999',
payload: { name: 'x' },
});
expect(res.statusCode).toBe(404);
});
it('400s on invalid body shape', async () => {
const create = await injectWithAuth(server, {
method: 'POST',
url: '/api/compliance/templates',
payload: { name: 'for-bad-patch', sections: ALL_OFF },
});
const id = create.json().template.id;
const res = await injectWithAuth(server, {
method: 'PATCH',
url: `/api/compliance/templates/${id}`,
payload: { sections: { interactions: true } }, // missing other 5 flags
});
expect(res.statusCode).toBe(400);
});
});
describe('DELETE /api/compliance/templates/:id', () => {
it('deletes an existing template + returns { deleted: true }', async () => {
const create = await injectWithAuth(server, {
method: 'POST',
url: '/api/compliance/templates',
payload: { name: 'doomed', sections: ALL_OFF },
});
const id = create.json().template.id;
const del = await injectWithAuth(server, {
method: 'DELETE',
url: `/api/compliance/templates/${id}`,
});
expect(del.statusCode).toBe(200);
expect(del.json().deleted).toBe(true);
const refetch = await injectWithAuth(server, {
method: 'GET',
url: `/api/compliance/templates/${id}`,
});
expect(refetch.statusCode).toBe(404);
});
it('404s on missing id', async () => {
const res = await injectWithAuth(server, {
method: 'DELETE',
url: '/api/compliance/templates/999999',
});
expect(res.statusCode).toBe(404);
});
});
});