open-design/apps/daemon/tests/plugins-verify.test.ts
Cursor Agent b3e8246f85
feat(plugins,daemon): doctor --strict + db vacuum (Phase 4 + 5)
Plan HH1 + HH2.

HH1: `od plugin doctor --strict` flag promotes doctor warnings
to errors. Useful in CI when any drift in resolved-context refs
or atom warnings should fail the build. JSON output includes
{ strict, passed }; exit code 4 distinguishes 'strict failed' from
'doctor errors' (exit 1). Verify config grows a `strict: true`
knob that propagates through verifyPlugin() — committing
.od-verify.json with strict:true gives plugins a one-line CI
'no warnings allowed' policy.

HH2: `od daemon db vacuum`. New POST /api/daemon/db/vacuum route
runs SQLite VACUUM, reports before/after sizes + reclaimed bytes
+ elapsed ms. Useful after large delete batches (snapshot prune,
plugin uninstall) shrink rows but leave space allocated to the
file. Loopback-only via requireLocalDaemonRequest (matches the
existing applied-plugins/export route shape — DB compaction is
operator-only).

CLI:
  od plugin doctor <id> --strict
  od daemon db vacuum

Daemon tests: 1784 \u2192 1787 (+3 cases on plugins-verify
strict-mode: default-pass on warnings, strict-fail on warnings,
strict-still-pass on zero issues). Existing plugins-verify
warnings-pass test stays correct because warnings-only doctor
keeps doctor.ok=true (the strict path explicitly opts in).
Existing plugins-canon / plugins-doctor tests stay byte-equal.

Co-authored-by: Tom Huang <1043269994@qq.com>
2026-05-09 17:47:28 +00:00

180 lines
6.2 KiB
TypeScript

// Plan §3.FF1 — verifyPlugin() pure helper.
import { describe, expect, it } from 'vitest';
import type { DoctorReport } from '../src/plugins/doctor.js';
import type { SimulatePipelineResult } from '../src/plugins/simulate.js';
import { verifyPlugin } from '../src/plugins/verify.js';
const passingDoctor = (): DoctorReport => ({
pluginId: 'p',
ok: true,
issues: [],
freshDigest: 'abc',
});
const failingDoctor = (): DoctorReport => ({
pluginId: 'p',
ok: false,
issues: [{ severity: 'error', code: 'manifest.invalid', message: 'broken' }],
freshDigest: 'abc',
});
const convergedSim = (): SimulatePipelineResult => ({
outcome: 'all-converged',
totalIterations: 3,
stages: [
{ stageId: 'critique', outcome: 'converged', iterations: 3, finalSignals: {} },
],
});
const capHitSim = (): SimulatePipelineResult => ({
outcome: 'cap-hit',
totalIterations: 10,
stages: [
{ stageId: 'critique', outcome: 'cap', iterations: 10, finalSignals: {}, reason: 'never satisfied' },
],
});
describe('verifyPlugin — pass / fail aggregation', () => {
it('passes when every enabled check passes', () => {
const report = verifyPlugin({
config: { enabled: ['doctor', 'simulate'] },
doctor: passingDoctor(),
simulate: convergedSim(),
});
expect(report.passed).toBe(true);
expect(report.outcomes.find((o) => o.check === 'doctor')?.status).toBe('passed');
expect(report.outcomes.find((o) => o.check === 'simulate')?.status).toBe('passed');
});
it('fails when doctor reports errors', () => {
const report = verifyPlugin({
config: { enabled: ['doctor'] },
doctor: failingDoctor(),
});
expect(report.passed).toBe(false);
expect(report.outcomes.find((o) => o.check === 'doctor')?.status).toBe('failed');
});
it('fails when simulate hits the cap', () => {
const report = verifyPlugin({
config: { enabled: ['simulate'] },
simulate: capHitSim(),
});
expect(report.passed).toBe(false);
expect(report.outcomes.find((o) => o.check === 'simulate')?.status).toBe('failed');
});
it("'unsupported' bubbles up as a fail when the report is missing", () => {
const report = verifyPlugin({
config: { enabled: ['doctor'] },
});
expect(report.passed).toBe(false);
expect(report.outcomes.find((o) => o.check === 'doctor')?.status).toBe('unsupported');
});
it("'skipped' for checks not in the enabled set", () => {
const report = verifyPlugin({
config: { enabled: ['doctor'] },
doctor: passingDoctor(),
});
expect(report.passed).toBe(true);
expect(report.outcomes.find((o) => o.check === 'simulate')?.status).toBe('skipped');
expect(report.outcomes.find((o) => o.check === 'canon')?.status).toBe('skipped');
});
});
describe('verifyPlugin — canon byte-equality', () => {
it('passes when canon === canonExpected', () => {
const report = verifyPlugin({
config: { enabled: ['canon'] },
canon: '## Active plugin\n\nfoo',
canonExpected: '## Active plugin\n\nfoo',
});
expect(report.outcomes.find((o) => o.check === 'canon')?.status).toBe('passed');
});
it('fails on mismatch + records both lengths', () => {
const report = verifyPlugin({
config: { enabled: ['canon'] },
canon: 'short',
canonExpected: 'much longer fixture',
});
const outcome = report.outcomes.find((o) => o.check === 'canon');
expect(outcome?.status).toBe('failed');
expect(outcome?.details?.['expectedLength']).toBe(19);
expect(outcome?.details?.['actualLength']).toBe(5);
});
it('skips canon cleanly when no fixture is supplied', () => {
const report = verifyPlugin({
config: { enabled: ['canon'] },
});
const outcome = report.outcomes.find((o) => o.check === 'canon');
expect(outcome?.status).toBe('skipped');
expect(outcome?.summary).toMatch(/no fixture/);
});
});
describe('verifyPlugin — strict mode (Plan §3.HH1)', () => {
it('passes by default when doctor reports warnings + no errors', () => {
const doctor: DoctorReport = {
pluginId: 'p',
ok: true,
issues: [{ severity: 'warning', code: 'a', message: 'x' }],
freshDigest: 'abc',
};
const report = verifyPlugin({ config: { enabled: ['doctor'] }, doctor });
expect(report.passed).toBe(true);
expect(report.outcomes.find((o) => o.check === 'doctor')?.status).toBe('passed');
});
it('FAILS in strict mode when doctor reports any warning', () => {
const doctor: DoctorReport = {
pluginId: 'p',
ok: true,
issues: [{ severity: 'warning', code: 'a', message: 'x' }],
freshDigest: 'abc',
};
const report = verifyPlugin({ config: { enabled: ['doctor'], strict: true }, doctor });
expect(report.passed).toBe(false);
const outcome = report.outcomes.find((o) => o.check === 'doctor');
expect(outcome?.status).toBe('failed');
expect(outcome?.summary).toMatch(/strict mode/);
});
it('still passes in strict mode when doctor reports zero issues', () => {
const report = verifyPlugin({ config: { enabled: ['doctor'], strict: true }, doctor: passingDoctor() });
expect(report.passed).toBe(true);
});
});
describe('verifyPlugin — summaries', () => {
it('doctor summary mentions error + warning counts', () => {
const doctor: DoctorReport = {
pluginId: 'p',
ok: false,
issues: [
{ severity: 'error', code: 'a', message: 'x' },
{ severity: 'error', code: 'b', message: 'y' },
{ severity: 'warning', code: 'c', message: 'z' },
],
freshDigest: 'abc',
};
const report = verifyPlugin({ config: { enabled: ['doctor'] }, doctor });
const outcome = report.outcomes.find((o) => o.check === 'doctor');
expect(outcome?.summary).toMatch(/2 errors/);
expect(outcome?.summary).toMatch(/1 warning/);
});
it('simulate summary mentions outcome + iterations + stages', () => {
const report = verifyPlugin({
config: { enabled: ['simulate'] },
simulate: convergedSim(),
});
const outcome = report.outcomes.find((o) => o.check === 'simulate');
expect(outcome?.summary).toMatch(/all-converged/);
expect(outcome?.summary).toMatch(/3 iterations/);
expect(outcome?.summary).toMatch(/1 stage/);
});
});