Part of #869 Mos (id-11) Gate-16 merge: independent APPROVE @e75e3238 (8/8), author id2 != approver id11, clean mosaic-coder author, CI green wp1987. Co-authored-by: jason.woltje <jason@diversecanvas.com> Co-committed-by: jason.woltje <jason@diversecanvas.com>
197 lines
6.4 KiB
TypeScript
197 lines
6.4 KiB
TypeScript
import { describe, expect, it } from 'vitest';
|
||
import {
|
||
detectEnforcementHooksWired,
|
||
runLeaseEnforcementDoctorCheck,
|
||
} from './lease-doctor-check.js';
|
||
|
||
/**
|
||
* Red-first tests for issue #869 Point-1 C5 — the `mosaic doctor`
|
||
* lease-enforcement surfacing check.
|
||
*
|
||
* Root cause under test: enforcement hooks (`mutator-gate.py`,
|
||
* `receipt-observer-client.py`) can be wired into `~/.claude/settings.json`
|
||
* on a host where C1's `leaseEnforcementActivatable()` is false and/or C3's
|
||
* `checkBrokerSupervisorHealth()` reports unhealthy. That combination fails
|
||
* closed correctly, but must be surfaced LOUDLY by `mosaic doctor` rather
|
||
* than silently passing — this test suite exercises the three primary
|
||
* branches (wired+not-activatable, wired+healthy, not-wired) plus the
|
||
* broker-unhealthy variant.
|
||
*
|
||
* Every dependency is injected — no real `~/.claude/settings.json` and no
|
||
* real broker are ever touched.
|
||
*/
|
||
|
||
const WIRED_SETTINGS_JSON = JSON.stringify({
|
||
hooks: {
|
||
PreToolUse: [
|
||
{
|
||
matcher: '.*',
|
||
hooks: [
|
||
{
|
||
type: 'command',
|
||
command: 'python3 ~/.config/mosaic/tools/lease-broker/mutator-gate.py --runtime claude',
|
||
},
|
||
],
|
||
},
|
||
],
|
||
Stop: [
|
||
{
|
||
hooks: [
|
||
{
|
||
type: 'command',
|
||
command:
|
||
'python3 ~/.config/mosaic/tools/lease-broker/receipt-observer-client.py --runtime claude --latest-entry',
|
||
},
|
||
],
|
||
},
|
||
],
|
||
},
|
||
});
|
||
|
||
const UNWIRED_SETTINGS_JSON = JSON.stringify({
|
||
hooks: {
|
||
PostToolUse: [
|
||
{
|
||
matcher: 'Edit|MultiEdit|Write',
|
||
hooks: [{ type: 'command', command: '~/.config/mosaic/tools/qa/qa-hook-stdin.sh' }],
|
||
},
|
||
],
|
||
},
|
||
});
|
||
|
||
describe('detectEnforcementHooksWired', () => {
|
||
it('detects the mutator-gate + receipt-observer markers when wired', () => {
|
||
const result = detectEnforcementHooksWired(JSON.parse(WIRED_SETTINGS_JSON));
|
||
expect(result.wired).toBe(true);
|
||
expect(result.matchedMarkers).toEqual(
|
||
expect.arrayContaining(['mutator-gate.py', 'receipt-observer-client.py']),
|
||
);
|
||
});
|
||
|
||
it('reports not wired when no enforcement markers are present', () => {
|
||
const result = detectEnforcementHooksWired(JSON.parse(UNWIRED_SETTINGS_JSON));
|
||
expect(result.wired).toBe(false);
|
||
expect(result.matchedMarkers).toEqual([]);
|
||
});
|
||
|
||
it('reports not wired for an empty settings object', () => {
|
||
expect(detectEnforcementHooksWired({}).wired).toBe(false);
|
||
});
|
||
|
||
it('detects wiring from just ONE marker (partial wiring is still dangerous)', () => {
|
||
const onlyMutatorGate = JSON.stringify({
|
||
hooks: {
|
||
PreToolUse: [
|
||
{
|
||
hooks: [{ type: 'command', command: 'python3 .../mutator-gate.py --runtime claude' }],
|
||
},
|
||
],
|
||
},
|
||
});
|
||
const result = detectEnforcementHooksWired(JSON.parse(onlyMutatorGate));
|
||
expect(result.wired).toBe(true);
|
||
expect(result.matchedMarkers).toEqual(['mutator-gate.py']);
|
||
});
|
||
});
|
||
|
||
describe('runLeaseEnforcementDoctorCheck', () => {
|
||
it('RED: wired + not-activatable ⇒ LOUD error (not a silent pass)', async () => {
|
||
const result = await runLeaseEnforcementDoctorCheck({
|
||
readSettingsRaw: () => WIRED_SETTINGS_JSON,
|
||
isActivatable: () => false,
|
||
isBrokerHealthy: async () => true,
|
||
});
|
||
|
||
expect(result.status).toBe('error');
|
||
expect(result.wired).toBe(true);
|
||
expect(result.activatable).toBe(false);
|
||
expect(result.message).toMatch(/activation absent/);
|
||
expect(result.message).toMatch(/#869/);
|
||
expect(result.message.toLowerCase()).toMatch(/brick/);
|
||
});
|
||
|
||
it('wired + activatable + broker-unhealthy ⇒ LOUD error', async () => {
|
||
const result = await runLeaseEnforcementDoctorCheck({
|
||
readSettingsRaw: () => WIRED_SETTINGS_JSON,
|
||
isActivatable: () => true,
|
||
isBrokerHealthy: async () => false,
|
||
});
|
||
|
||
expect(result.status).toBe('error');
|
||
expect(result.wired).toBe(true);
|
||
expect(result.brokerHealthy).toBe(false);
|
||
expect(result.message).toMatch(/broker not healthy/);
|
||
});
|
||
|
||
it('wired + not-activatable + broker-unhealthy ⇒ LOUD error citing both reasons', async () => {
|
||
const result = await runLeaseEnforcementDoctorCheck({
|
||
readSettingsRaw: () => WIRED_SETTINGS_JSON,
|
||
isActivatable: () => false,
|
||
isBrokerHealthy: async () => false,
|
||
});
|
||
|
||
expect(result.status).toBe('error');
|
||
expect(result.message).toMatch(/activation absent/);
|
||
expect(result.message).toMatch(/broker not healthy/);
|
||
});
|
||
|
||
it('GREEN: wired + activatable + broker-healthy ⇒ ok', async () => {
|
||
const result = await runLeaseEnforcementDoctorCheck({
|
||
readSettingsRaw: () => WIRED_SETTINGS_JSON,
|
||
isActivatable: () => true,
|
||
isBrokerHealthy: async () => true,
|
||
});
|
||
|
||
expect(result.status).toBe('ok');
|
||
expect(result.wired).toBe(true);
|
||
expect(result.activatable).toBe(true);
|
||
expect(result.brokerHealthy).toBe(true);
|
||
});
|
||
|
||
it('GREEN: not-wired ⇒ ok, no false alarm (activation/broker never probed)', async () => {
|
||
let activatableCalled = false;
|
||
let brokerCalled = false;
|
||
|
||
const result = await runLeaseEnforcementDoctorCheck({
|
||
readSettingsRaw: () => UNWIRED_SETTINGS_JSON,
|
||
isActivatable: () => {
|
||
activatableCalled = true;
|
||
return false;
|
||
},
|
||
isBrokerHealthy: async () => {
|
||
brokerCalled = true;
|
||
return false;
|
||
},
|
||
});
|
||
|
||
expect(result.status).toBe('ok');
|
||
expect(result.wired).toBe(false);
|
||
expect(result.activatable).toBeNull();
|
||
expect(result.brokerHealthy).toBeNull();
|
||
// Not wired must short-circuit — never even consult activation/broker.
|
||
expect(activatableCalled).toBe(false);
|
||
expect(brokerCalled).toBe(false);
|
||
});
|
||
|
||
it('GREEN: settings.json absent ⇒ ok (never touches a real file — readSettingsRaw is injected)', async () => {
|
||
const result = await runLeaseEnforcementDoctorCheck({
|
||
readSettingsRaw: () => null,
|
||
isActivatable: () => false,
|
||
isBrokerHealthy: async () => false,
|
||
});
|
||
|
||
expect(result.status).toBe('ok');
|
||
expect(result.wired).toBe(false);
|
||
});
|
||
|
||
it('GREEN: malformed settings.json ⇒ ok (parse errors are not this card’s failure class)', async () => {
|
||
const result = await runLeaseEnforcementDoctorCheck({
|
||
readSettingsRaw: () => '{ not valid json',
|
||
isActivatable: () => false,
|
||
isBrokerHealthy: async () => false,
|
||
});
|
||
|
||
expect(result.status).toBe('ok');
|
||
});
|
||
});
|