Compose-ready workspace: packages/zappier (rate card, portal, Stripe), packages/verae-zapier-middleware (timestamp + NATS), packages/verae-zapier (CLI app), vendor/zapier-platform, and research/zapier vendor corpus. Gate 0 structure checks pass. Product code and research are not yet wired.
165 lines
4.6 KiB
JavaScript
165 lines
4.6 KiB
JavaScript
/**
|
|
* GATE 1 — Debug facility unit tests.
|
|
* Must pass before Phase 2.
|
|
*/
|
|
|
|
import { describe, it, beforeEach, afterEach } from 'node:test';
|
|
import assert from 'node:assert/strict';
|
|
|
|
import {
|
|
parseDebugVeraeEnv,
|
|
loadDebugConfig,
|
|
shouldLog,
|
|
createDebugger,
|
|
setDebugTestSink,
|
|
redact,
|
|
withTrace,
|
|
getTraceId,
|
|
generateTraceId,
|
|
} from '../../src/debug/index.js';
|
|
|
|
describe('parseDebugVeraeEnv', () => {
|
|
it('disables when unset or empty', () => {
|
|
assert.equal(parseDebugVeraeEnv(undefined).enabled, false);
|
|
assert.equal(parseDebugVeraeEnv('').enabled, false);
|
|
assert.equal(parseDebugVeraeEnv('off').enabled, false);
|
|
});
|
|
|
|
it('enables all namespaces for 1 or *', () => {
|
|
const a = parseDebugVeraeEnv('1');
|
|
assert.equal(a.enabled, true);
|
|
assert.equal(a.namespaces, null);
|
|
|
|
const b = parseDebugVeraeEnv('*');
|
|
assert.equal(b.enabled, true);
|
|
assert.equal(b.namespaces, null);
|
|
});
|
|
|
|
it('parses comma-separated namespaces', () => {
|
|
const { enabled, namespaces } = parseDebugVeraeEnv('auth, NATS, jobs');
|
|
assert.equal(enabled, true);
|
|
assert.ok(namespaces.has('auth'));
|
|
assert.ok(namespaces.has('nats'));
|
|
assert.ok(namespaces.has('jobs'));
|
|
assert.equal(namespaces.has('webhooks'), false);
|
|
});
|
|
});
|
|
|
|
describe('shouldLog', () => {
|
|
it('respects level thresholds', () => {
|
|
const config = {
|
|
enabled: true,
|
|
namespaces: null,
|
|
level: 'warn',
|
|
filePath: null,
|
|
};
|
|
assert.equal(shouldLog(config, 'app', 'debug'), false);
|
|
assert.equal(shouldLog(config, 'app', 'warn'), true);
|
|
assert.equal(shouldLog(config, 'app', 'error'), true);
|
|
});
|
|
|
|
it('filters by namespace', () => {
|
|
const config = {
|
|
enabled: true,
|
|
namespaces: new Set(['auth']),
|
|
level: 'debug',
|
|
filePath: null,
|
|
};
|
|
assert.equal(shouldLog(config, 'auth', 'debug'), true);
|
|
assert.equal(shouldLog(config, 'nats', 'debug'), false);
|
|
});
|
|
});
|
|
|
|
describe('redact', () => {
|
|
it('redacts sensitive keys and token-like strings', () => {
|
|
const out = redact({
|
|
password: 'secret',
|
|
apiKey: 'zmw_abc123def456ghi789jkl',
|
|
jobId: 'keep-me',
|
|
authorization: 'Bearer eyJhbGciOiJIUzI1NiJ9.aaa.bbb',
|
|
nested: { veraeToken: 'zmt_payload.sig' },
|
|
});
|
|
|
|
assert.equal(out.password, '[REDACTED]');
|
|
assert.equal(out.apiKey, '[REDACTED]');
|
|
assert.equal(out.jobId, 'keep-me');
|
|
assert.equal(out.authorization, '[REDACTED]');
|
|
assert.equal(out.nested.veraeToken, '[REDACTED]');
|
|
});
|
|
});
|
|
|
|
describe('createDebugger', () => {
|
|
/** @type {string[]} */
|
|
let lines;
|
|
const prev = { ...process.env };
|
|
|
|
beforeEach(() => {
|
|
lines = [];
|
|
setDebugTestSink((line) => lines.push(line));
|
|
});
|
|
|
|
afterEach(() => {
|
|
setDebugTestSink(null);
|
|
for (const key of Object.keys(process.env)) {
|
|
if (!(key in prev)) delete process.env[key];
|
|
}
|
|
Object.assign(process.env, prev);
|
|
});
|
|
|
|
it('is silent when DEBUG_VERAE is unset', () => {
|
|
delete process.env.DEBUG_VERAE;
|
|
const log = createDebugger('auth');
|
|
log.debug('should not appear', { x: 1 });
|
|
assert.equal(lines.length, 0);
|
|
});
|
|
|
|
it('emits only selected namespaces', () => {
|
|
process.env.DEBUG_VERAE = 'auth';
|
|
createDebugger('auth').debug('auth-line');
|
|
createDebugger('nats').debug('nats-line');
|
|
assert.equal(lines.length, 1);
|
|
assert.match(lines[0], /auth-line/);
|
|
assert.doesNotMatch(lines[0], /nats-line/);
|
|
});
|
|
|
|
it('redacts secrets in meta', () => {
|
|
process.env.DEBUG_VERAE = 'auth';
|
|
createDebugger('auth').debug('login', {
|
|
password: 'hunter2',
|
|
token: 'zmw_supersecretvaluehere12',
|
|
});
|
|
assert.equal(lines.length, 1);
|
|
assert.doesNotMatch(lines[0], /hunter2/);
|
|
assert.doesNotMatch(lines[0], /zmw_supersecret/);
|
|
assert.match(lines[0], /REDACTED/);
|
|
});
|
|
});
|
|
|
|
describe('withTrace', () => {
|
|
it('propagates traceId to nested async work', async () => {
|
|
const outerId = generateTraceId();
|
|
let innerId = null;
|
|
|
|
await withTrace({ traceId: outerId, span: 'outer' }, async () => {
|
|
assert.equal(getTraceId(), outerId);
|
|
await withTrace({ span: 'inner' }, async () => {
|
|
innerId = getTraceId();
|
|
});
|
|
});
|
|
|
|
assert.equal(innerId, outerId);
|
|
assert.equal(getTraceId(), null);
|
|
});
|
|
});
|
|
|
|
describe('loadDebugConfig', () => {
|
|
it('reads level and file from env', () => {
|
|
process.env.DEBUG_VERAE = '1';
|
|
process.env.DEBUG_VERAE_LEVEL = 'error';
|
|
process.env.DEBUG_VERAE_FILE = '/tmp/verae-debug.log';
|
|
const cfg = loadDebugConfig(process.env);
|
|
assert.equal(cfg.enabled, true);
|
|
assert.equal(cfg.level, 'error');
|
|
assert.equal(cfg.filePath, '/tmp/verae-debug.log');
|
|
});
|
|
});
|