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,195 @@
/**
* Trust Model Last-Mile Wiring Tests
*
* Validates that:
* 1. InstallAuditStore is instantiated and wired at runtime
* 2. Install operations record audit entries in the .mind DB
* 3. Existing non-trust approvals remain backward-compatible
*/
import { describe, it, expect, beforeAll, afterAll } from 'vitest';
import fs from 'node:fs';
import path from 'node:path';
import os from 'node:os';
import Database from 'better-sqlite3';
import { MindDB } from '@waggle/core';
import { buildLocalServer } from '../../src/local/index.js';
import type { FastifyInstance } from 'fastify';
import { injectWithAuth } from '../test-utils.js';
describe('Trust Model Runtime Wiring', () => {
let server: FastifyInstance;
let tmpDir: string;
beforeAll(async () => {
tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'waggle-trust-wire-'));
// Create personal.mind
const personalPath = path.join(tmpDir, 'personal.mind');
const mind = new MindDB(personalPath);
mind.close();
// Create skills dir with marker to prevent auto-install of starter skills
const skillsDir = path.join(tmpDir, 'skills');
fs.mkdirSync(skillsDir, { recursive: true });
fs.writeFileSync(path.join(skillsDir, '.starter-installed'), 'test');
server = await buildLocalServer({ dataDir: tmpDir });
});
afterAll(async () => {
await server.close();
fs.rmSync(tmpDir, { recursive: true, force: true });
});
describe('Audit store wiring', () => {
it('install_audit table exists after server startup', () => {
const personalPath = path.join(tmpDir, 'personal.mind');
const db = new Database(personalPath);
const table = db.prepare(
"SELECT name FROM sqlite_master WHERE type='table' AND name='install_audit'",
).get() as { name: string } | undefined;
db.close();
expect(table).toBeDefined();
expect(table!.name).toBe('install_audit');
});
it('installing a starter skill creates audit entries', async () => {
// Remove skill if auto-installed, then install fresh
await injectWithAuth(server, { method: 'DELETE', url: '/api/skills/brainstorm' });
const res = await injectWithAuth(server, {
method: 'POST',
url: '/api/skills/starter-pack/brainstorm',
});
expect(res.statusCode).toBe(200);
// Check audit trail — read through the server's own auditStore (the
// writer connection). A fresh better-sqlite3 reader can miss WAL rows the
// server hasn't checkpointed, which is flaky across platforms/boot timing.
const entries = server.auditStore.getByCapability('brainstorm');
// API route records 1 installed entry (agent tool path records approved + installed)
expect(entries.length).toBeGreaterThanOrEqual(1);
expect(entries.some(e => e.action === 'installed')).toBe(true);
// Verify trust metadata is populated
const installed = entries.find(e => e.action === 'installed')!;
expect(installed.risk_level).toBe('low');
expect(installed.trust_source).toBe('starter_pack');
expect(installed.approval_class).toBe('standard');
expect(installed.initiator).toBe('user');
expect(installed.detail).toContain('Install Center');
});
it('failed install records audit entry', async () => {
const res = await injectWithAuth(server, {
method: 'POST',
url: '/api/skills/starter-pack/nonexistent-xyz',
});
expect(res.statusCode).toBe(404);
// The API route handler returns 404, but the skill-tools install_capability
// path records failures. Check if any failed entries exist.
// Read through the server's writer connection (avoids the cross-connection
// WAL visibility race a fresh reader hits).
const total = server.auditStore.getAll().length;
// At least the brainstorm entry from the previous test should exist
expect(total).toBeGreaterThanOrEqual(1);
});
it('duplicate install does not create new audit entries', async () => {
const personalPath = path.join(tmpDir, 'personal.mind');
const db1 = new Database(personalPath);
const before = (db1.prepare("SELECT count(*) as c FROM install_audit").get() as { c: number }).c;
db1.close();
// Try to install already-installed skill
const res = await injectWithAuth(server, {
method: 'POST',
url: '/api/skills/starter-pack/brainstorm',
});
expect(res.statusCode).toBe(409);
const db2 = new Database(personalPath);
const after = (db2.prepare("SELECT count(*) as c FROM install_audit").get() as { c: number }).c;
db2.close();
// Server route handles duplicate before reaching skill-tools,
// so no new audit entries should be created via the API route
expect(after).toBe(before);
});
});
describe('Audit REST endpoint', () => {
it('GET /api/audit/installs returns recent install events', async () => {
// brainstorm was already installed in the previous test block
const res = await injectWithAuth(server, {
method: 'GET',
url: '/api/audit/installs',
});
expect(res.statusCode).toBe(200);
const body = JSON.parse(res.body);
expect(body.entries).toBeDefined();
expect(body.entries.length).toBeGreaterThan(0);
expect(body.entries[0].capabilityName).toBeDefined();
expect(body.entries[0].riskLevel).toBeDefined();
expect(body.entries[0].action).toBeDefined();
expect(body.entries[0].trustSource).toBeDefined();
expect(body.entries[0].timestamp).toBeDefined();
});
it('respects limit parameter', async () => {
const res = await injectWithAuth(server, {
method: 'GET',
url: '/api/audit/installs?limit=1',
});
expect(res.statusCode).toBe(200);
const body = JSON.parse(res.body);
expect(body.entries.length).toBeLessThanOrEqual(1);
});
it('caps limit at 100', async () => {
const res = await injectWithAuth(server, {
method: 'GET',
url: '/api/audit/installs?limit=999',
});
expect(res.statusCode).toBe(200);
// Just verify it doesn't error — the cap is internal
const body = JSON.parse(res.body);
expect(body.entries).toBeDefined();
});
});
describe('Backward compatibility', () => {
it('starter-pack catalog still works', async () => {
const res = await injectWithAuth(server, {
method: 'GET',
url: '/api/skills/starter-pack/catalog',
});
expect(res.statusCode).toBe(200);
const body = JSON.parse(res.body);
expect(body.skills).toBeDefined();
expect(body.skills.length).toBeGreaterThan(0);
});
it('capabilities status endpoint still works', async () => {
const res = await injectWithAuth(server, {
method: 'GET',
url: '/api/capabilities/status',
});
expect(res.statusCode).toBe(200);
const body = JSON.parse(res.body);
expect(body.skills).toBeDefined();
});
it('health endpoint still works', async () => {
const res = await injectWithAuth(server, {
method: 'GET',
url: '/health',
});
expect(res.statusCode).toBe(200);
const body = JSON.parse(res.body);
expect(body.mode).toBe('local');
});
});
});