Files
Claude-Code-Workflow/ccw/tests/smart-search-enrich.test.js
catlog22 b27d8a9570 feat: Add CLAUDE.md freshness tracking and update reminders
- Add SQLite table and CRUD methods for tracking update history
- Create freshness calculation service based on git file changes
- Add API endpoints for freshness data, marking updates, and history
- Display freshness badges in file tree (green/yellow/red indicators)
- Show freshness gauge and details in metadata panel
- Auto-mark files as updated after CLI sync
- Add English and Chinese i18n translations

Freshness algorithm: 100 - min((changedFilesCount / 20) * 100, 100)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-20 16:14:46 +08:00

258 lines
8.5 KiB
JavaScript

/**
* Tests for smart_search with enrich parameter
*
* Tests the following:
* - enrich parameter is passed to codex-lens
* - relationship data is parsed from response
* - SemanticMatch interface with relationships field
*/
import { describe, it, before, mock } from 'node:test';
import assert from 'node:assert';
import { dirname, join } from 'path';
import { fileURLToPath } from 'url';
const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);
// Import the smart-search module (exports schema, not smartSearchTool)
const smartSearchPath = new URL('../dist/tools/smart-search.js', import.meta.url).href;
describe('Smart Search Enrich Parameter', async () => {
let smartSearchModule;
before(async () => {
try {
smartSearchModule = await import(smartSearchPath);
} catch (err) {
console.log('Note: smart-search module import skipped:', err.message);
}
});
describe('Parameter Schema', () => {
it('should have enrich parameter in schema', async () => {
if (!smartSearchModule) {
console.log('Skipping: smart-search module not available');
return;
}
const { schema } = smartSearchModule;
assert.ok(schema, 'Should export schema');
// Schema uses inputSchema (MCP standard), not parameters
const params = schema.inputSchema || schema.parameters;
assert.ok(params, 'Should have inputSchema or parameters');
const props = params.properties;
assert.ok(props.enrich, 'Should have enrich parameter');
assert.strictEqual(props.enrich.type, 'boolean', 'enrich should be boolean');
assert.strictEqual(props.enrich.default, false, 'enrich should default to false');
});
it('should describe enrich parameter purpose', async () => {
if (!smartSearchModule) {
console.log('Skipping: smart-search module not available');
return;
}
const { schema } = smartSearchModule;
const params = schema.inputSchema || schema.parameters;
const enrichDesc = params.properties.enrich?.description || '';
// Description should mention relationships or graph
const mentionsRelationships = enrichDesc.toLowerCase().includes('relationship') ||
enrichDesc.toLowerCase().includes('graph') ||
enrichDesc.toLowerCase().includes('enrich');
assert.ok(mentionsRelationships, 'enrich description should mention relationships/graph');
});
});
describe('SemanticMatch Interface', () => {
it('should handle results with relationships field', async () => {
if (!smartSearchModule) {
console.log('Skipping: smart-search module not available');
return;
}
// Create a mock result with relationships
const mockResult = {
file: 'test.py',
score: 0.95,
content: 'def main(): pass',
symbol: 'main',
relationships: [
{
type: 'calls',
direction: 'outgoing',
target: 'helper',
file: 'test.py',
line: 5
},
{
type: 'called_by',
direction: 'incoming',
source: 'entrypoint',
file: 'app.py',
line: 10
}
]
};
// Verify structure
assert.ok(Array.isArray(mockResult.relationships), 'relationships should be array');
assert.strictEqual(mockResult.relationships.length, 2, 'should have 2 relationships');
const outgoing = mockResult.relationships[0];
assert.strictEqual(outgoing.type, 'calls');
assert.strictEqual(outgoing.direction, 'outgoing');
assert.ok(outgoing.target, 'outgoing should have target');
const incoming = mockResult.relationships[1];
assert.strictEqual(incoming.type, 'called_by');
assert.strictEqual(incoming.direction, 'incoming');
assert.ok(incoming.source, 'incoming should have source');
});
});
describe('RelationshipInfo Structure', () => {
it('should validate relationship info structure', () => {
// Test the expected structure of RelationshipInfo
const validRelationship = {
type: 'calls',
direction: 'outgoing',
target: 'some_function',
file: 'module.py',
line: 42
};
assert.ok(['calls', 'imports', 'extends', 'called_by', 'imported_by', 'extended_by']
.includes(validRelationship.type), 'type should be valid relationship type');
assert.ok(['outgoing', 'incoming'].includes(validRelationship.direction),
'direction should be outgoing or incoming');
assert.ok(typeof validRelationship.file === 'string', 'file should be string');
});
it('should allow optional line number', () => {
const withLine = {
type: 'calls',
direction: 'outgoing',
target: 'func',
file: 'test.py',
line: 10
};
const withoutLine = {
type: 'imports',
direction: 'outgoing',
target: 'os',
file: 'test.py'
// line is optional
};
assert.strictEqual(withLine.line, 10);
assert.strictEqual(withoutLine.line, undefined);
});
});
});
describe('Smart Search Tool Definition', async () => {
let smartSearchModule;
before(async () => {
try {
smartSearchModule = await import(smartSearchPath);
} catch (err) {
console.log('Note: smart-search module not available');
}
});
it('should have correct tool name', () => {
if (!smartSearchModule) {
console.log('Skipping: smart-search module not available');
return;
}
assert.strictEqual(smartSearchModule.schema.name, 'smart_search');
});
it('should have all required parameters', () => {
if (!smartSearchModule) {
console.log('Skipping: smart-search module not available');
return;
}
const params = smartSearchModule.schema.inputSchema || smartSearchModule.schema.parameters;
const props = params.properties;
// Core parameters
assert.ok(props.action, 'Should have action parameter');
assert.ok(props.query, 'Should have query parameter');
assert.ok(props.path, 'Should have path parameter');
// Search parameters
assert.ok(props.mode, 'Should have mode parameter');
assert.ok(props.maxResults || props.limit, 'Should have maxResults/limit parameter');
// New enrich parameter
assert.ok(props.enrich, 'Should have enrich parameter');
});
it('should support search modes', () => {
if (!smartSearchModule) {
console.log('Skipping: smart-search module not available');
return;
}
const params = smartSearchModule.schema.inputSchema || smartSearchModule.schema.parameters;
const modeEnum = params.properties.mode?.enum;
assert.ok(modeEnum, 'Should have mode enum');
assert.ok(modeEnum.includes('auto'), 'Should support auto mode');
assert.ok(modeEnum.includes('hybrid'), 'Should support hybrid mode');
assert.ok(modeEnum.includes('exact'), 'Should support exact mode');
});
});
describe('Enrich Flag Integration', async () => {
let codexLensModule;
before(async () => {
try {
const codexLensPath = new URL('../dist/tools/codex-lens.js', import.meta.url).href;
codexLensModule = await import(codexLensPath);
} catch (err) {
console.log('Note: codex-lens module not available');
}
});
it('codex-lens should support enrich parameter', () => {
if (!codexLensModule) {
console.log('Skipping: codex-lens module not available');
return;
}
// Use schema export (primary) or codexLensTool (backward-compatible)
const toolDef = codexLensModule.schema || codexLensModule.codexLensTool;
assert.ok(toolDef, 'Should have schema or codexLensTool export');
// Schema uses inputSchema (MCP standard), codexLensTool uses parameters
const params = toolDef.inputSchema || toolDef.parameters;
const props = params.properties;
assert.ok(props.enrich, 'should have enrich parameter');
assert.strictEqual(props.enrich.type, 'boolean', 'enrich should be boolean');
});
it('should pass enrich flag to command line', async () => {
if (!codexLensModule) {
console.log('Skipping: codex-lens module not available');
return;
}
// Check if executeCodexLens function is exported
const { executeCodexLens } = codexLensModule;
if (executeCodexLens) {
// The function should be available for passing enrich parameter
assert.ok(typeof executeCodexLens === 'function', 'executeCodexLens should be a function');
}
});
});