init
This commit is contained in:
72
.opencode/skills/docs-seeker/scripts/tests/run-tests.js
Executable file
72
.opencode/skills/docs-seeker/scripts/tests/run-tests.js
Executable file
@@ -0,0 +1,72 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
/**
|
||||
* Test runner - runs all tests
|
||||
*/
|
||||
|
||||
const { spawn } = require('child_process');
|
||||
const path = require('path');
|
||||
|
||||
const tests = [
|
||||
'test-detect-topic.js',
|
||||
'test-fetch-docs.js',
|
||||
'test-analyze-llms.js',
|
||||
];
|
||||
|
||||
let totalPassed = 0;
|
||||
let totalFailed = 0;
|
||||
|
||||
function runTest(testFile) {
|
||||
return new Promise((resolve, reject) => {
|
||||
console.log(`\n${'='.repeat(60)}`);
|
||||
console.log(`Running: ${testFile}`);
|
||||
console.log('='.repeat(60));
|
||||
|
||||
const testPath = path.join(__dirname, testFile);
|
||||
const proc = spawn('node', [testPath], {
|
||||
stdio: 'inherit',
|
||||
});
|
||||
|
||||
proc.on('close', (code) => {
|
||||
if (code === 0) {
|
||||
resolve();
|
||||
} else {
|
||||
reject(new Error(`Test failed: ${testFile}`));
|
||||
}
|
||||
});
|
||||
|
||||
proc.on('error', reject);
|
||||
});
|
||||
}
|
||||
|
||||
async function runAllTests() {
|
||||
console.log('Running all docs-seeker tests...');
|
||||
|
||||
let failedTests = [];
|
||||
|
||||
for (const test of tests) {
|
||||
try {
|
||||
await runTest(test);
|
||||
} catch (error) {
|
||||
failedTests.push(test);
|
||||
}
|
||||
}
|
||||
|
||||
console.log(`\n${'='.repeat(60)}`);
|
||||
console.log('All Tests Summary');
|
||||
console.log('='.repeat(60));
|
||||
console.log(`Total test files: ${tests.length}`);
|
||||
console.log(`Passed: ${tests.length - failedTests.length}`);
|
||||
console.log(`Failed: ${failedTests.length}`);
|
||||
|
||||
if (failedTests.length > 0) {
|
||||
console.log('\nFailed tests:');
|
||||
failedTests.forEach((test) => console.log(` - ${test}`));
|
||||
process.exit(1);
|
||||
} else {
|
||||
console.log('\n✓ All tests passed!');
|
||||
process.exit(0);
|
||||
}
|
||||
}
|
||||
|
||||
runAllTests();
|
||||
119
.opencode/skills/docs-seeker/scripts/tests/test-analyze-llms.js
Executable file
119
.opencode/skills/docs-seeker/scripts/tests/test-analyze-llms.js
Executable file
@@ -0,0 +1,119 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
/**
|
||||
* Tests for analyze-llms-txt.js
|
||||
*/
|
||||
|
||||
const {
|
||||
analyzeLlmsTxt,
|
||||
parseUrls,
|
||||
groupByPriority,
|
||||
categorizeUrl,
|
||||
suggestAgentDistribution,
|
||||
} = require('../analyze-llms-txt');
|
||||
|
||||
// Test counter
|
||||
let passed = 0;
|
||||
let failed = 0;
|
||||
|
||||
function assert(condition, message) {
|
||||
if (condition) {
|
||||
console.log(`✓ ${message}`);
|
||||
passed++;
|
||||
} else {
|
||||
console.error(`✗ ${message}`);
|
||||
failed++;
|
||||
}
|
||||
}
|
||||
|
||||
function assertEqual(actual, expected, message) {
|
||||
if (actual === expected) {
|
||||
console.log(`✓ ${message}`);
|
||||
passed++;
|
||||
} else {
|
||||
console.error(`✗ ${message}`);
|
||||
console.error(` Expected: ${expected}`);
|
||||
console.error(` Actual: ${actual}`);
|
||||
failed++;
|
||||
}
|
||||
}
|
||||
|
||||
console.log('Running analyze-llms-txt.js tests...\n');
|
||||
|
||||
// Test categorizeUrl
|
||||
console.log('## Testing categorizeUrl()');
|
||||
assertEqual(categorizeUrl('https://docs.example.com/getting-started'), 'critical', 'Categorize getting-started as critical');
|
||||
assertEqual(categorizeUrl('https://docs.example.com/guide/routing'), 'important', 'Categorize routing guide as important');
|
||||
assertEqual(categorizeUrl('https://docs.example.com/advanced/internals'), 'supplementary', 'Categorize internals as supplementary');
|
||||
assertEqual(categorizeUrl('https://docs.example.com/api-reference'), 'important', 'Categorize API reference as important');
|
||||
|
||||
// Test parseUrls
|
||||
console.log('\n## Testing parseUrls()');
|
||||
|
||||
const sampleContent = `# Documentation
|
||||
https://docs.example.com/getting-started
|
||||
https://docs.example.com/guide
|
||||
# Comment line
|
||||
https://docs.example.com/api-reference
|
||||
|
||||
https://docs.example.com/advanced
|
||||
`;
|
||||
|
||||
const urls = parseUrls(sampleContent);
|
||||
assertEqual(urls.length, 4, 'Parse 4 URLs from content');
|
||||
assert(urls[0].includes('getting-started'), 'First URL is getting-started');
|
||||
|
||||
const emptyContent = '';
|
||||
const emptyUrls = parseUrls(emptyContent);
|
||||
assertEqual(emptyUrls.length, 0, 'Empty content returns 0 URLs');
|
||||
|
||||
// Test groupByPriority
|
||||
console.log('\n## Testing groupByPriority()');
|
||||
|
||||
const testUrls = [
|
||||
'https://docs.example.com/getting-started',
|
||||
'https://docs.example.com/guide/routing',
|
||||
'https://docs.example.com/advanced/internals',
|
||||
'https://docs.example.com/installation',
|
||||
];
|
||||
|
||||
const grouped = groupByPriority(testUrls);
|
||||
assert(grouped.critical.length >= 2, 'Has critical URLs');
|
||||
assert(grouped.important.length >= 1, 'Has important URLs');
|
||||
assert(grouped.supplementary.length >= 1, 'Has supplementary URLs');
|
||||
|
||||
// Test suggestAgentDistribution
|
||||
console.log('\n## Testing suggestAgentDistribution()');
|
||||
|
||||
const dist1 = suggestAgentDistribution(2);
|
||||
assertEqual(dist1.agentCount, 1, 'Suggest 1 agent for 2 URLs');
|
||||
assertEqual(dist1.strategy, 'single', 'Strategy is single for few URLs');
|
||||
|
||||
const dist2 = suggestAgentDistribution(8);
|
||||
assert(dist2.agentCount >= 3 && dist2.agentCount <= 5, 'Suggest 3-5 agents for 8 URLs');
|
||||
assertEqual(dist2.strategy, 'parallel', 'Strategy is parallel for medium URLs');
|
||||
|
||||
const dist3 = suggestAgentDistribution(15);
|
||||
assertEqual(dist3.agentCount, 7, 'Suggest 7 agents for 15 URLs');
|
||||
|
||||
const dist4 = suggestAgentDistribution(25);
|
||||
assertEqual(dist4.agentCount, 7, 'Suggest 7 agents for 25 URLs');
|
||||
assertEqual(dist4.strategy, 'phased', 'Strategy is phased for many URLs');
|
||||
assertEqual(dist4.phases, 2, 'Use 2 phases for large sets');
|
||||
|
||||
// Test analyzeLlmsTxt
|
||||
console.log('\n## Testing analyzeLlmsTxt()');
|
||||
|
||||
const analysis = analyzeLlmsTxt(sampleContent);
|
||||
assertEqual(analysis.totalUrls, 4, 'Analysis counts 4 URLs');
|
||||
assert(analysis.grouped, 'Analysis includes grouped URLs');
|
||||
assert(analysis.distribution, 'Analysis includes distribution suggestion');
|
||||
assert(analysis.summary, 'Analysis includes summary');
|
||||
|
||||
// Summary
|
||||
console.log('\n## Test Summary');
|
||||
console.log(`Passed: ${passed}`);
|
||||
console.log(`Failed: ${failed}`);
|
||||
console.log(`Total: ${passed + failed}`);
|
||||
|
||||
process.exit(failed > 0 ? 1 : 0);
|
||||
112
.opencode/skills/docs-seeker/scripts/tests/test-detect-topic.js
Executable file
112
.opencode/skills/docs-seeker/scripts/tests/test-detect-topic.js
Executable file
@@ -0,0 +1,112 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
/**
|
||||
* Tests for detect-topic.js
|
||||
*/
|
||||
|
||||
const { detectTopic, normalizeTopic, normalizeLibrary } = require('../detect-topic');
|
||||
|
||||
// Test counter
|
||||
let passed = 0;
|
||||
let failed = 0;
|
||||
|
||||
function assert(condition, message) {
|
||||
if (condition) {
|
||||
console.log(`✓ ${message}`);
|
||||
passed++;
|
||||
} else {
|
||||
console.error(`✗ ${message}`);
|
||||
failed++;
|
||||
}
|
||||
}
|
||||
|
||||
function assertEqual(actual, expected, message) {
|
||||
if (actual === expected) {
|
||||
console.log(`✓ ${message}`);
|
||||
passed++;
|
||||
} else {
|
||||
console.error(`✗ ${message}`);
|
||||
console.error(` Expected: ${expected}`);
|
||||
console.error(` Actual: ${actual}`);
|
||||
failed++;
|
||||
}
|
||||
}
|
||||
|
||||
console.log('Running detect-topic.js tests...\n');
|
||||
|
||||
// Test normalizeTopic
|
||||
console.log('## Testing normalizeTopic()');
|
||||
assertEqual(normalizeTopic('date picker'), 'date', 'Normalize multi-word topic');
|
||||
assertEqual(normalizeTopic('OAuth'), 'oauth', 'Normalize OAuth');
|
||||
assertEqual(normalizeTopic('Server-Side'), 'server', 'Normalize Server-Side');
|
||||
assertEqual(normalizeTopic('caching'), 'caching', 'Normalize caching');
|
||||
|
||||
// Test normalizeLibrary
|
||||
console.log('\n## Testing normalizeLibrary()');
|
||||
assertEqual(normalizeLibrary('Next.js'), 'next.js', 'Normalize Next.js');
|
||||
assertEqual(normalizeLibrary('shadcn/ui'), 'shadcn/ui', 'Normalize shadcn/ui');
|
||||
assertEqual(normalizeLibrary('Better Auth'), 'better-auth', 'Normalize Better Auth');
|
||||
|
||||
// Test topic-specific queries
|
||||
console.log('\n## Testing topic-specific queries');
|
||||
|
||||
const topicQuery1 = detectTopic('How do I use date picker in shadcn/ui?');
|
||||
assert(topicQuery1 !== null, 'Detect topic-specific query 1');
|
||||
assert(topicQuery1.isTopicSpecific === true, 'Query 1 is topic-specific');
|
||||
assertEqual(topicQuery1.topic, 'date', 'Query 1 topic is "date"');
|
||||
assertEqual(topicQuery1.library, 'shadcn/ui', 'Query 1 library is "shadcn/ui"');
|
||||
|
||||
const topicQuery2 = detectTopic('Next.js caching strategies');
|
||||
assert(topicQuery2 !== null, 'Detect topic-specific query 2');
|
||||
assert(topicQuery2 && topicQuery2.isTopicSpecific === true, 'Query 2 is topic-specific');
|
||||
if (topicQuery2) {
|
||||
assertEqual(topicQuery2.topic, 'caching', 'Query 2 topic is "caching"');
|
||||
assertEqual(topicQuery2.library, 'next.js', 'Query 2 library is "next.js"');
|
||||
}
|
||||
|
||||
const topicQuery3 = detectTopic('Better Auth OAuth setup');
|
||||
assert(topicQuery3 !== null, 'Detect topic-specific query 3');
|
||||
assert(topicQuery3.isTopicSpecific === true, 'Query 3 is topic-specific');
|
||||
|
||||
const topicQuery4 = detectTopic('Using authentication with Better Auth');
|
||||
assert(topicQuery4 !== null, 'Detect topic-specific query 4');
|
||||
assert(topicQuery4.isTopicSpecific === true, 'Query 4 is topic-specific');
|
||||
|
||||
const topicQuery5 = detectTopic('Implement routing in Next.js');
|
||||
assert(topicQuery5 !== null, 'Detect topic-specific query 5');
|
||||
assert(topicQuery5.isTopicSpecific === true, 'Query 5 is topic-specific');
|
||||
|
||||
// Test general queries
|
||||
console.log('\n## Testing general queries');
|
||||
|
||||
const generalQuery1 = detectTopic('Documentation for Next.js');
|
||||
assert(generalQuery1 === null, 'Detect general query 1 (returns null)');
|
||||
|
||||
const generalQuery2 = detectTopic('Astro getting started');
|
||||
assert(generalQuery2 === null, 'Detect general query 2 (returns null)');
|
||||
|
||||
const generalQuery3 = detectTopic('How to use Better Auth');
|
||||
assert(generalQuery3 === null, 'Detect general query 3 (returns null)');
|
||||
|
||||
const generalQuery4 = detectTopic('Next.js API reference');
|
||||
assert(generalQuery4 === null, 'Detect general query 4 (returns null)');
|
||||
|
||||
// Test edge cases
|
||||
console.log('\n## Testing edge cases');
|
||||
|
||||
const edgeCase1 = detectTopic('');
|
||||
assert(edgeCase1 === null, 'Empty string returns null');
|
||||
|
||||
const edgeCase2 = detectTopic(null);
|
||||
assert(edgeCase2 === null, 'Null returns null');
|
||||
|
||||
const edgeCase3 = detectTopic('Random text without pattern');
|
||||
assert(edgeCase3 === null, 'Non-matching query returns null');
|
||||
|
||||
// Summary
|
||||
console.log('\n## Test Summary');
|
||||
console.log(`Passed: ${passed}`);
|
||||
console.log(`Failed: ${failed}`);
|
||||
console.log(`Total: ${passed + failed}`);
|
||||
|
||||
process.exit(failed > 0 ? 1 : 0);
|
||||
84
.opencode/skills/docs-seeker/scripts/tests/test-fetch-docs.js
Executable file
84
.opencode/skills/docs-seeker/scripts/tests/test-fetch-docs.js
Executable file
@@ -0,0 +1,84 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
/**
|
||||
* Tests for fetch-docs.js
|
||||
*/
|
||||
|
||||
const { buildContext7Url, getUrlVariations } = require('../fetch-docs');
|
||||
|
||||
// Test counter
|
||||
let passed = 0;
|
||||
let failed = 0;
|
||||
|
||||
function assert(condition, message) {
|
||||
if (condition) {
|
||||
console.log(`✓ ${message}`);
|
||||
passed++;
|
||||
} else {
|
||||
console.error(`✗ ${message}`);
|
||||
failed++;
|
||||
}
|
||||
}
|
||||
|
||||
function assertEqual(actual, expected, message) {
|
||||
if (actual === expected) {
|
||||
console.log(`✓ ${message}`);
|
||||
passed++;
|
||||
} else {
|
||||
console.error(`✗ ${message}`);
|
||||
console.error(` Expected: ${expected}`);
|
||||
console.error(` Actual: ${actual}`);
|
||||
failed++;
|
||||
}
|
||||
}
|
||||
|
||||
console.log('Running fetch-docs.js tests...\n');
|
||||
|
||||
// Test buildContext7Url
|
||||
console.log('## Testing buildContext7Url()');
|
||||
|
||||
assertEqual(
|
||||
buildContext7Url('vercel/next.js'),
|
||||
'https://context7.com/vercel/next.js/llms.txt',
|
||||
'Build URL for GitHub repo'
|
||||
);
|
||||
|
||||
assertEqual(
|
||||
buildContext7Url('vercel/next.js', 'cache'),
|
||||
'https://context7.com/vercel/next.js/llms.txt?topic=cache',
|
||||
'Build URL with topic parameter'
|
||||
);
|
||||
|
||||
assertEqual(
|
||||
buildContext7Url('shadcn-ui/ui', 'date'),
|
||||
'https://context7.com/shadcn-ui/ui/llms.txt?topic=date',
|
||||
'Build URL for shadcn with topic'
|
||||
);
|
||||
|
||||
// Test getUrlVariations
|
||||
console.log('\n## Testing getUrlVariations()');
|
||||
|
||||
async function testUrlVariations() {
|
||||
const urls1 = await getUrlVariations('next.js', 'cache');
|
||||
assert(urls1.length >= 2, 'Returns multiple URL variations with topic');
|
||||
assert(urls1[0].includes('?topic=cache'), 'First URL has topic parameter');
|
||||
assert(!urls1[1].includes('?topic='), 'Second URL has no topic parameter');
|
||||
|
||||
const urls2 = await getUrlVariations('shadcn/ui');
|
||||
assert(urls2.length >= 1, 'Returns URL variations without topic');
|
||||
assert(!urls2[0].includes('?topic='), 'URL has no topic parameter');
|
||||
|
||||
const urls3 = await getUrlVariations('astro', 'routing');
|
||||
assert(urls3.length >= 2, 'Returns variations for known library');
|
||||
assertEqual(urls3[0], 'https://context7.com/withastro/astro/llms.txt?topic=routing', 'Maps Astro correctly');
|
||||
}
|
||||
|
||||
testUrlVariations().then(() => {
|
||||
// Summary
|
||||
console.log('\n## Test Summary');
|
||||
console.log(`Passed: ${passed}`);
|
||||
console.log(`Failed: ${failed}`);
|
||||
console.log(`Total: ${passed + failed}`);
|
||||
|
||||
process.exit(failed > 0 ? 1 : 0);
|
||||
});
|
||||
Reference in New Issue
Block a user