feat: Phase 0+1 — repo sync, pino, lint fixes, core components
Phase 0: - Synced latest live-status.js from workspace (9928 bytes) - Fixed 43 lint issues: empty catch blocks, console statements - Added pino dependency - Created src/tool-labels.json with all known tool mappings - make check passes Phase 1 (Core Components): - src/config.js: env-var config with validation, throws on missing required vars - src/logger.js: pino singleton with child loggers, level validation - src/circuit-breaker.js: CLOSED/OPEN/HALF_OPEN state machine with callbacks - src/tool-labels.js: exact/prefix/regex tool->label resolver with external override - src/status-box.js: Mattermost post manager (keepAlive, throttle, retry, circuit breaker) - src/status-formatter.js: pure SessionState->text formatter (nested, compact) - src/health.js: HTTP health endpoint + metrics - src/status-watcher.js: JSONL file watcher (inotify, compaction detection, idle detection) Tests: - test/unit/config.test.js: 7 tests - test/unit/circuit-breaker.test.js: 12 tests - test/unit/logger.test.js: 5 tests - test/unit/status-formatter.test.js: 20 tests - test/unit/tool-labels.test.js: 15 tests All 59 unit tests pass. make check clean.
This commit is contained in:
185
test/unit/tool-labels.test.js
Normal file
185
test/unit/tool-labels.test.js
Normal file
@@ -0,0 +1,185 @@
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* Unit tests for tool-labels.js
|
||||
*/
|
||||
|
||||
const { describe, it, beforeEach, afterEach } = require('node:test');
|
||||
const assert = require('node:assert/strict');
|
||||
const path = require('path');
|
||||
const fs = require('fs');
|
||||
const os = require('os');
|
||||
|
||||
const { loadLabels, resolve, resetLabels } = require('../../src/tool-labels');
|
||||
|
||||
describe('tool-labels.js', () => {
|
||||
beforeEach(() => {
|
||||
resetLabels();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
resetLabels();
|
||||
});
|
||||
|
||||
describe('exact match', () => {
|
||||
it('resolves known tools by exact name', () => {
|
||||
loadLabels(null);
|
||||
assert.equal(resolve('exec'), 'Running command...');
|
||||
assert.equal(resolve('Read'), 'Reading file...');
|
||||
assert.equal(resolve('Write'), 'Writing file...');
|
||||
assert.equal(resolve('Edit'), 'Editing file...');
|
||||
assert.equal(resolve('web_search'), 'Searching the web...');
|
||||
assert.equal(resolve('web_fetch'), 'Fetching URL...');
|
||||
assert.equal(resolve('message'), 'Sending message...');
|
||||
assert.equal(resolve('tts'), 'Generating speech...');
|
||||
assert.equal(resolve('subagents'), 'Managing sub-agents...');
|
||||
assert.equal(resolve('image'), 'Analyzing image...');
|
||||
assert.equal(resolve('process'), 'Managing process...');
|
||||
assert.equal(resolve('browser'), 'Controlling browser...');
|
||||
});
|
||||
});
|
||||
|
||||
describe('prefix match', () => {
|
||||
it('resolves camofox_ tools via prefix', () => {
|
||||
loadLabels(null);
|
||||
assert.equal(resolve('camofox_create_tab'), 'Opening browser tab...'); // exact takes priority
|
||||
assert.equal(resolve('camofox_some_new_tool'), 'Using browser...');
|
||||
});
|
||||
|
||||
it('resolves claude_code_ tools via prefix', () => {
|
||||
loadLabels(null);
|
||||
assert.equal(resolve('claude_code_start'), 'Starting Claude Code task...'); // exact takes priority
|
||||
assert.equal(resolve('claude_code_something_new'), 'Running Claude Code...');
|
||||
});
|
||||
});
|
||||
|
||||
describe('default label', () => {
|
||||
it('returns default for unknown tools', () => {
|
||||
loadLabels(null);
|
||||
assert.equal(resolve('some_unknown_tool'), 'Working...');
|
||||
assert.equal(resolve(''), 'Working...');
|
||||
assert.equal(resolve('xyz'), 'Working...');
|
||||
});
|
||||
});
|
||||
|
||||
describe('external override', () => {
|
||||
let tmpFile;
|
||||
|
||||
beforeEach(() => {
|
||||
tmpFile = path.join(os.tmpdir(), `tool-labels-test-${Date.now()}.json`);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
try {
|
||||
fs.unlinkSync(tmpFile);
|
||||
} catch (_e) {
|
||||
/* ignore */
|
||||
}
|
||||
});
|
||||
|
||||
it('external exact overrides built-in', () => {
|
||||
fs.writeFileSync(
|
||||
tmpFile,
|
||||
JSON.stringify({
|
||||
exact: { exec: 'Custom exec label...' },
|
||||
prefix: {},
|
||||
}),
|
||||
);
|
||||
loadLabels(tmpFile);
|
||||
assert.equal(resolve('exec'), 'Custom exec label...');
|
||||
// Non-overridden built-in still works
|
||||
assert.equal(resolve('Read'), 'Reading file...');
|
||||
});
|
||||
|
||||
it('external prefix adds new prefix', () => {
|
||||
fs.writeFileSync(
|
||||
tmpFile,
|
||||
JSON.stringify({
|
||||
exact: {},
|
||||
prefix: { my_tool_: 'My custom tool...' },
|
||||
}),
|
||||
);
|
||||
loadLabels(tmpFile);
|
||||
assert.equal(resolve('my_tool_do_something'), 'My custom tool...');
|
||||
});
|
||||
|
||||
it('external default overrides built-in default', () => {
|
||||
fs.writeFileSync(
|
||||
tmpFile,
|
||||
JSON.stringify({
|
||||
exact: {},
|
||||
prefix: {},
|
||||
default: 'Custom default...',
|
||||
}),
|
||||
);
|
||||
loadLabels(tmpFile);
|
||||
assert.equal(resolve('completely_unknown'), 'Custom default...');
|
||||
});
|
||||
|
||||
it('handles missing external file gracefully', () => {
|
||||
loadLabels('/nonexistent/path/tool-labels.json');
|
||||
// Should fall back to built-in
|
||||
assert.equal(resolve('exec'), 'Running command...');
|
||||
});
|
||||
|
||||
it('handles malformed external JSON gracefully', () => {
|
||||
fs.writeFileSync(tmpFile, 'not valid json {{{');
|
||||
loadLabels(tmpFile);
|
||||
// Should fall back to built-in
|
||||
assert.equal(resolve('exec'), 'Running command...');
|
||||
});
|
||||
});
|
||||
|
||||
describe('regex match', () => {
|
||||
let tmpFile;
|
||||
|
||||
beforeEach(() => {
|
||||
tmpFile = path.join(os.tmpdir(), `tool-labels-regex-${Date.now()}.json`);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
try {
|
||||
fs.unlinkSync(tmpFile);
|
||||
} catch (_e) {
|
||||
/* ignore */
|
||||
}
|
||||
});
|
||||
|
||||
it('resolves via regex pattern', () => {
|
||||
fs.writeFileSync(
|
||||
tmpFile,
|
||||
JSON.stringify({
|
||||
exact: {},
|
||||
prefix: {},
|
||||
regex: [{ pattern: '/^my_api_/', label: 'Calling API...' }],
|
||||
}),
|
||||
);
|
||||
loadLabels(tmpFile);
|
||||
assert.equal(resolve('my_api_create'), 'Calling API...');
|
||||
assert.equal(resolve('my_api_update'), 'Calling API...');
|
||||
assert.equal(resolve('other_tool'), 'Working...');
|
||||
});
|
||||
|
||||
it('handles invalid regex gracefully', () => {
|
||||
fs.writeFileSync(
|
||||
tmpFile,
|
||||
JSON.stringify({
|
||||
exact: {},
|
||||
prefix: {},
|
||||
regex: [{ pattern: '/[invalid(/', label: 'oops' }],
|
||||
}),
|
||||
);
|
||||
loadLabels(tmpFile);
|
||||
// Invalid regex skipped — returns default
|
||||
assert.equal(resolve('anything'), 'Working...');
|
||||
});
|
||||
});
|
||||
|
||||
describe('auto-load', () => {
|
||||
it('auto-loads built-in labels on first resolve call', () => {
|
||||
// resetLabels was called in beforeEach — no explicit loadLabels call
|
||||
const label = resolve('exec');
|
||||
assert.equal(label, 'Running command...');
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user