Implements Phases 1-8 of the TFTSR implementation plan. Rust backend (Tauri 2.x, src-tauri/): - Multi-provider AI: OpenAI-compatible, Anthropic, Gemini, Mistral, Ollama - PII detection engine: 11 regex patterns with overlap resolution - SQLCipher AES-256 encrypted database with 10 versioned migrations - 28 Tauri IPC commands for triage, analysis, document, and system ops - Ollama: hardware probe, model recommendations, pull/delete with events - RCA and blameless post-mortem Markdown document generators - PDF export via printpdf - Audit log: SHA-256 hash of every external data send - Integration stubs for Confluence, ServiceNow, Azure DevOps (v0.2) Frontend (React 18 + TypeScript + Vite, src/): - 9 pages: full triage workflow NewIssue→LogUpload→Triage→Resolution→RCA→Postmortem→History+Settings - 7 components: ChatWindow, TriageProgress, PiiDiffViewer, DocEditor, HardwareReport, ModelSelector, UI primitives - 3 Zustand stores: session, settings (persisted), history - Type-safe tauriCommands.ts matching Rust backend types exactly - 8 IT domain system prompts (Linux, Windows, Network, K8s, DB, Virt, HW, Obs) DevOps: - .woodpecker/test.yml: rustfmt, clippy, cargo test, tsc, vitest on every push - .woodpecker/release.yml: linux/amd64 + linux/arm64 builds, Gogs release upload Verified: - cargo check: zero errors - tsc --noEmit: zero errors - vitest run: 13/13 unit tests passing Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
109 lines
2.8 KiB
JavaScript
109 lines
2.8 KiB
JavaScript
const expect = require('chai').expect;
|
|
const loglevel = require('loglevel');
|
|
const other = require('loglevel-plugin-mock');
|
|
const sinon = require('sinon');
|
|
const prefix = require('../lib/loglevel-plugin-prefix');
|
|
|
|
const spy = sinon.spy();
|
|
|
|
loglevel.enableAll();
|
|
|
|
describe('API', () => {
|
|
it('Methods', () => {
|
|
expect(prefix).to.have.property('apply').with.be.a('function');
|
|
expect(prefix).to.have.property('reg').with.be.a('function');
|
|
expect(prefix).not.to.have.property('noConflict');
|
|
});
|
|
|
|
it('Reg: empty arguments', () => {
|
|
expect(prefix.reg).to.throw(TypeError, 'Argument is not a root logger');
|
|
});
|
|
|
|
it('Reg: incorrect argument', () => {
|
|
expect(() => prefix.reg('logger')).to.throw(TypeError, 'Argument is not a root logger');
|
|
});
|
|
|
|
it('Apply: empty arguments', () => {
|
|
expect(prefix.apply).to.throw(TypeError, 'Argument is not a logger');
|
|
});
|
|
|
|
it('Apply: incorrect argument', () => {
|
|
expect(() => prefix.apply('logger')).to.throw(TypeError, 'Argument is not a logger');
|
|
});
|
|
});
|
|
|
|
describe('Prefix', () => {
|
|
other.apply(loglevel, { method: spy });
|
|
|
|
beforeEach(() => {
|
|
spy.reset();
|
|
});
|
|
|
|
it('Root applying', () => {
|
|
expect(() => prefix.apply(loglevel)).to.not.throw();
|
|
});
|
|
|
|
it('Root reapplyng', () => {
|
|
prefix.apply(loglevel, { template: '%l (%n):' });
|
|
loglevel.info('test');
|
|
expect(spy.calledWith('INFO (root): test')).to.be.true;
|
|
});
|
|
|
|
it('Format', () => {
|
|
prefix.apply(loglevel, {
|
|
format: (level, logger) => `${level} (${logger}):`
|
|
});
|
|
|
|
loglevel.info('test');
|
|
|
|
expect(spy.calledWith('INFO (root): test')).to.be.true;
|
|
});
|
|
|
|
it('Unformat', () => {
|
|
prefix.apply(loglevel, { template: '%l:' });
|
|
|
|
loglevel.info('test');
|
|
|
|
expect(spy.calledWith('INFO: test')).to.be.true;
|
|
});
|
|
|
|
it('The prefix must be combined with the first argument, if it is a string', () => {
|
|
prefix.apply(loglevel, { template: '%l:' });
|
|
|
|
loglevel.info('foo %s', 'bar');
|
|
expect(spy.calledWith('INFO: foo %s', 'bar')).to.be.true;
|
|
});
|
|
|
|
it('All methods of the previous plugin should be called', () => {
|
|
prefix.apply(loglevel);
|
|
|
|
// for warn in apply
|
|
spy.reset();
|
|
|
|
loglevel.trace();
|
|
loglevel.debug();
|
|
loglevel.info();
|
|
loglevel.warn();
|
|
loglevel.error();
|
|
expect(spy.callCount).to.equal(5);
|
|
});
|
|
|
|
it('Child applying', () => {
|
|
const child = loglevel.getLogger('child');
|
|
prefix.apply(child, { template: '%l (%n):' });
|
|
child.info('test');
|
|
expect(spy.calledWith('INFO (child): test')).to.be.true;
|
|
});
|
|
|
|
it('Child reapplyng', () => {
|
|
const child = loglevel.getLogger('child');
|
|
prefix.apply(child, {
|
|
levelFormatter(level) {
|
|
return level;
|
|
}
|
|
});
|
|
child.info('test');
|
|
expect(spy.calledWith('info (child): test')).to.be.true;
|
|
});
|
|
});
|