mirror of
https://github.com/Sea-Haven-Industries/sh-mcp.git
synced 2026-10-01 10:33:16 +00:00
71 lines
2 KiB
TypeScript
71 lines
2 KiB
TypeScript
|
|
import { describe, it, expect } from 'vitest';
|
||
|
|
|
||
|
|
import {
|
||
|
|
ConsoleAuditLogger,
|
||
|
|
MemoryAuditLogger,
|
||
|
|
NoopAuditLogger,
|
||
|
|
hashArgs,
|
||
|
|
type AuditRecord,
|
||
|
|
} from './audit.js';
|
||
|
|
|
||
|
|
const sample: AuditRecord = {
|
||
|
|
sub: 'u@seahavenind.com',
|
||
|
|
tool: 'lookup_payment',
|
||
|
|
argsHash: 'abc',
|
||
|
|
decision: 'allow',
|
||
|
|
result: 'ok',
|
||
|
|
ts: '2026-06-24T00:00:00.000Z',
|
||
|
|
};
|
||
|
|
|
||
|
|
describe('hashArgs', () => {
|
||
|
|
it('produces a stable 64-char hex digest', () => {
|
||
|
|
const h = hashArgs({ vendor: 'Acme', amount: 5 });
|
||
|
|
expect(h).toMatch(/^[0-9a-f]{64}$/);
|
||
|
|
});
|
||
|
|
|
||
|
|
it('is order-insensitive for object keys (canonicalized)', () => {
|
||
|
|
expect(hashArgs({ a: 1, b: 2 })).toBe(hashArgs({ b: 2, a: 1 }));
|
||
|
|
});
|
||
|
|
|
||
|
|
it('differs when values differ and never embeds the raw value', () => {
|
||
|
|
const h1 = hashArgs({ secret: 'TOPSECRET' });
|
||
|
|
const h2 = hashArgs({ secret: 'other' });
|
||
|
|
expect(h1).not.toBe(h2);
|
||
|
|
expect(h1).not.toContain('TOPSECRET');
|
||
|
|
});
|
||
|
|
});
|
||
|
|
|
||
|
|
describe('MemoryAuditLogger', () => {
|
||
|
|
it('captures records in order', () => {
|
||
|
|
const log = new MemoryAuditLogger();
|
||
|
|
log.log(sample);
|
||
|
|
log.log({ ...sample, tool: 'second' });
|
||
|
|
expect(log.records.map((r) => r.tool)).toEqual(['lookup_payment', 'second']);
|
||
|
|
});
|
||
|
|
});
|
||
|
|
|
||
|
|
describe('NoopAuditLogger', () => {
|
||
|
|
it('accepts records without throwing', () => {
|
||
|
|
expect(() => new NoopAuditLogger().log(sample)).not.toThrow();
|
||
|
|
});
|
||
|
|
});
|
||
|
|
|
||
|
|
describe('ConsoleAuditLogger', () => {
|
||
|
|
it('writes one structured JSON line tagged as audit', () => {
|
||
|
|
const lines: string[] = [];
|
||
|
|
const orig = console.log;
|
||
|
|
// eslint-disable-next-line no-console
|
||
|
|
console.log = (msg?: unknown) => void lines.push(String(msg));
|
||
|
|
try {
|
||
|
|
new ConsoleAuditLogger().log(sample);
|
||
|
|
} finally {
|
||
|
|
// eslint-disable-next-line no-console
|
||
|
|
console.log = orig;
|
||
|
|
}
|
||
|
|
expect(lines).toHaveLength(1);
|
||
|
|
const parsed = JSON.parse(lines[0]!) as Record<string, unknown>;
|
||
|
|
expect(parsed['kind']).toBe('audit');
|
||
|
|
expect(parsed['tool']).toBe('lookup_payment');
|
||
|
|
});
|
||
|
|
});
|