| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639 |
- /**
- * Module import & export verification for VLCode Lite
- * Tests: import succeeds, exports exist, basic function calls work
- */
- import { fileURLToPath } from 'url';
- import path from 'path';
- const __dirname = path.dirname(fileURLToPath(import.meta.url));
- const results = [];
- let passCount = 0, failCount = 0, warnCount = 0;
- function ok(mod, msg) { passCount++; results.push(` ✅ ${mod}: ${msg}`); }
- function fail(mod, msg) { failCount++; results.push(` ❌ ${mod}: ${msg}`); }
- function warn(mod, msg) { warnCount++; results.push(` ⚠️ ${mod}: ${msg}`); }
- // ─── 1. Utils ────────────────────────────────────────────────────────
- async function testUtils() {
- console.log('\n═══ utils/ ═══');
- try {
- const { loadConfig, APP_VERSION } = await import('./src/utils/config.js');
- if (typeof loadConfig !== 'function') throw new Error('loadConfig not a function');
- if (!APP_VERSION) throw new Error('APP_VERSION is empty');
- const cfg = loadConfig(['--web', '--port', '9999']);
- if (cfg.port !== 9999) throw new Error(`port should be 9999, got ${cfg.port}`);
- if (!['cli', 'api-key'].includes(cfg.llmProvider)) {
- throw new Error(`llmProvider should be cli or api-key, got ${cfg.llmProvider}`);
- }
- ok('config.js', `v${APP_VERSION}, loadConfig() works, port=9999, llmProvider=${cfg.llmProvider}`);
- } catch (e) { fail('config.js', e.message); }
- try {
- const { extractZip, extractZipToDir, createZipFromDir } = await import('./src/utils/zip-extract.js');
- if (typeof extractZip !== 'function') throw new Error('extractZip not a function');
- if (typeof extractZipToDir !== 'function') throw new Error('extractZipToDir not a function');
- if (typeof createZipFromDir !== 'function') throw new Error('createZipFromDir not a function');
- const fns = ['extractZip', 'extractZipToDir', 'createZipFromDir'];
- ok('zip-extract.js', `exports: ${fns.join(', ')}`);
- } catch (e) { fail('zip-extract.js', e.message); }
- }
- // ─── 2. Data ─────────────────────────────────────────────────────────
- async function testData() {
- console.log('\n═══ data/ ═══');
- try {
- const { VL_VERSION, THEME_VERSION, PARSEVL_URL } = await import('./src/data/versions.js');
- if (!VL_VERSION || !THEME_VERSION || !PARSEVL_URL) throw new Error('missing constants');
- ok('versions.js', `VL=${VL_VERSION}, Theme=${THEME_VERSION}`);
- } catch (e) { fail('versions.js', e.message); }
- try {
- const { DOCCENTER_API_URL, DOC_REGISTRY, pathFor, docIdFor, resolveDocRef } = await import('./src/data/doc-paths.js');
- if (!DOCCENTER_API_URL) throw new Error('DOCCENTER_API_URL missing');
- if (typeof pathFor !== 'function') throw new Error('pathFor not a function');
- const p = pathFor('vlSyntax');
- if (p !== 1) throw new Error(`pathFor('vlSyntax') should be 1, got ${p}`);
- ok('doc-paths.js', `API=${DOCCENTER_API_URL.substring(0,30)}..., ${Object.keys(DOC_REGISTRY).length} entries`);
- } catch (e) { fail('doc-paths.js', e.message); }
- try {
- const mod = await import('./src/data/default-theme.js');
- ok('default-theme.js', `exports: ${Object.keys(mod).join(', ').substring(0, 60)}`);
- } catch (e) { fail('default-theme.js', e.message); }
- }
- // ─── 3. Core ─────────────────────────────────────────────────────────
- async function testCore() {
- console.log('\n═══ core/ ═══');
- // tool-registry
- try {
- const { ToolRegistry } = await import('./src/core/tool-registry.js');
- const reg = new ToolRegistry();
- reg.register('TestTool', { description: 'test', parameters: {}, execute: async () => 'ok' });
- if (!reg.has('TestTool')) throw new Error('has() failed');
- const result = await reg.execute('TestTool', {});
- if (!result || result.result !== 'ok') throw new Error(`execute returned ${JSON.stringify(result)}`);
- const schemas = reg.getToolSchemas();
- if (schemas.length !== 1) throw new Error(`schemas.length should be 1, got ${schemas.length}`);
- reg.clear();
- if (reg.listTools().length !== 0) throw new Error('clear() failed');
- ok('tool-registry.js', 'register/has/execute/getToolSchemas/clear all work');
- } catch (e) { fail('tool-registry.js', e.message); }
- // context-manager
- try {
- const { ContextManager } = await import('./src/core/context-manager.js');
- const cm = new ContextManager({ maxOutputTokens: 4096 });
- cm.addUserMessage('hello');
- cm.addAssistantMessage('hi there');
- const msgs = cm.getMessages();
- if (msgs.length !== 2) throw new Error(`expected 2 msgs, got ${msgs.length}`);
- const usage = cm.getUsage();
- if (typeof usage.usedTokens !== 'number') throw new Error('getUsage() missing usedTokens');
- ok('context-manager.js', `addUser/addAssistant/getMessages/getUsage work, ${msgs.length} msgs, ${usage.usedTokens} tokens`);
- } catch (e) { fail('context-manager.js', e.message); }
- // session
- try {
- const { SessionManager } = await import('./src/core/session.js');
- const sm = new SessionManager('/tmp/vlcode-lite-test');
- await sm.init();
- const { resumed } = await sm.startOrResume();
- ok('session.js', `init/startOrResume work, resumed=${resumed}`);
- } catch (e) { fail('session.js', e.message); }
- // session-pool
- try {
- const { SessionPool } = await import('./src/core/session-pool.js');
- // SessionPool needs shared modules, just verify import
- ok('session-pool.js', 'import OK (needs shared modules to construct)');
- } catch (e) { fail('session-pool.js', e.message); }
- // llm-provider
- try {
- const { CLIProvider, createProvider, isCLIAvailable } = await import('./src/core/llm-provider.js');
- if (typeof CLIProvider !== 'function') throw new Error('CLIProvider not a class');
- if (typeof createProvider !== 'function') throw new Error('createProvider not a function');
- if (typeof isCLIAvailable !== 'function') throw new Error('isCLIAvailable not a function');
- const available = await isCLIAvailable();
- const provider = createProvider({ model: 'claude-opus-4-6', port: 4002, llmProvider: available ? 'cli' : 'api-key', cliAvailable: available, apiKey: available ? undefined : 'test-key' });
- if (!['cli', 'api-key'].includes(provider.name)) {
- throw new Error(`provider.name should be cli or api-key, got ${provider.name}`);
- }
- ok('llm-provider.js', `Provider OK (${provider.name}), claude CLI available=${available}`);
- } catch (e) { fail('llm-provider.js', e.message); }
- // prompt-assembler
- try {
- const { PromptAssembler } = await import('./src/core/prompt-assembler.js');
- const pa = new PromptAssembler(
- { workDir: '/tmp', model: 'test' },
- { isVLProject: () => false, getSummary: () => ({}), getVLConfig: () => '', getProjectMd: () => '', getRules: () => [] }
- );
- const manual = pa.buildInstructionManual();
- if (!manual || manual.length === 0) throw new Error('buildInstructionManual returned empty');
- ok('prompt-assembler.js', `buildInstructionManual() returns ${manual.length} chars`);
- } catch (e) { fail('prompt-assembler.js', e.message); }
- // orchestrator (import only — needs full wiring)
- try {
- const { AgentOrchestrator } = await import('./src/core/orchestrator.js');
- if (typeof AgentOrchestrator !== 'function') throw new Error('not a class');
- ok('orchestrator.js', 'import OK (needs full wiring to construct)');
- } catch (e) { fail('orchestrator.js', e.message); }
- // cli
- try {
- const { CLIInterface } = await import('./src/core/cli.js');
- if (typeof CLIInterface !== 'function') throw new Error('not a class');
- ok('cli.js', 'import OK');
- } catch (e) { fail('cli.js', e.message); }
- // hooks
- try {
- const { HooksManager } = await import('./src/core/hooks.js');
- const hm = new HooksManager('/tmp/no-hooks');
- if (typeof hm.hasHooks !== 'function') throw new Error('hasHooks not a function');
- if (hm.hasHooks()) throw new Error('should have no hooks');
- ok('hooks.js', 'HooksManager works, hasHooks()=false for empty dir');
- } catch (e) { fail('hooks.js', e.message); }
- }
- // ─── 4. VL ───────────────────────────────────────────────────────────
- async function testVL() {
- console.log('\n═══ vl/ ═══');
- try {
- const { VLProjectContext } = await import('./src/vl/project-context.js');
- const ctx = new VLProjectContext('/tmp/empty-vl-test');
- await ctx.load();
- if (ctx.isVLProject()) throw new Error('empty dir should not be VL project');
- const summary = ctx.getSummary();
- if (summary !== null) throw new Error('getSummary should return null for non-VL project');
- ok('project-context.js', `load/isVLProject/getSummary work, non-VL returns null`);
- } catch (e) { fail('project-context.js', e.message); }
- try {
- const { VLSymbolIndex } = await import('./src/vl/symbol-index.js');
- const { VLProjectContext } = await import('./src/vl/project-context.js');
- const ctx = new VLProjectContext('/tmp/empty-vl-test');
- await ctx.load();
- const si = new VLSymbolIndex(ctx);
- await si.build();
- const stats = si.getStats();
- if (typeof stats.totalSymbols !== 'number') throw new Error('missing totalSymbols');
- ok('symbol-index.js', `build/getStats work, symbols=${stats.totalSymbols}`);
- } catch (e) { fail('symbol-index.js', e.message); }
- try {
- const { SmartContextLoader } = await import('./src/vl/smart-context.js');
- ok('smart-context.js', 'import OK');
- } catch (e) { fail('smart-context.js', e.message); }
- try {
- const { BlueprintContext } = await import('./src/vl/blueprint-context.js');
- const { VLProjectContext } = await import('./src/vl/project-context.js');
- const ctx = new VLProjectContext('/tmp/empty-vl-test');
- await ctx.load();
- const bp = new BlueprintContext(ctx);
- await bp.load();
- if (bp.hasBlueprints()) throw new Error('empty project should have no blueprints');
- ok('blueprint-context.js', 'load/hasBlueprints work');
- } catch (e) { fail('blueprint-context.js', e.message); }
- try {
- const { ImpactAnalyzer } = await import('./src/vl/impact-analyzer.js');
- ok('impact-analyzer.js', 'import OK');
- } catch (e) { fail('impact-analyzer.js', e.message); }
- try {
- const { VLAutoFix } = await import('./src/vl/auto-fix.js');
- ok('auto-fix.js', 'import OK');
- } catch (e) { fail('auto-fix.js', e.message); }
- try {
- const { FileCache } = await import('./src/vl/file-cache.js');
- const fc = new FileCache('/tmp');
- const stats = fc.getStats();
- if (typeof stats.hits !== 'number') throw new Error('getStats missing hits');
- ok('file-cache.js', `FileCache works, hitRate=${stats.hitRate}`);
- } catch (e) { fail('file-cache.js', e.message); }
- try {
- const { FileWatcher } = await import('./src/vl/file-watcher.js');
- ok('file-watcher.js', 'import OK');
- } catch (e) { fail('file-watcher.js', e.message); }
- try {
- const mod = await import('./src/vl/metadata-extractor.js');
- const fns = Object.keys(mod);
- ok('metadata-extractor.js', `exports: ${fns.join(', ')}`);
- } catch (e) { fail('metadata-extractor.js', e.message); }
- try {
- const { computeMetaDiff } = await import('./src/vl/meta-diff.js');
- if (typeof computeMetaDiff !== 'function') throw new Error('not a function');
- const diff = computeMetaDiff({ apps: [], sections: [] }, { apps: [], sections: [] });
- if (!diff.summary && diff.summary !== '') throw new Error('missing summary');
- ok('meta-diff.js', 'computeMetaDiff() works on empty metas');
- } catch (e) { fail('meta-diff.js', e.message); }
- try {
- const { parseSections, sectionDiff, mergeSections, smartMerge } = await import('./src/vl/section-diff.js');
- const sections = parseSections('# Header\nline1\n# Body\nline2');
- if (!(sections instanceof Map)) throw new Error('parseSections should return Map');
- if (sections.size < 2) throw new Error(`expected >=2 sections, got ${sections.size}`);
- const diff = sectionDiff('# A\nfoo', '# A\nbar\n# B\nnew');
- if (!diff.modified && !diff.added) throw new Error('diff should detect changes');
- ok('section-diff.js', `parseSections=${sections.size} sections, sectionDiff/mergeSections/smartMerge exported`);
- } catch (e) { fail('section-diff.js', e.message); }
- try {
- const { GenerationPipeline } = await import('./src/vl/generation-pipeline.js');
- ok('generation-pipeline.js', 'import OK');
- } catch (e) { fail('generation-pipeline.js', e.message); }
- try {
- const { WorkflowGenerationPipeline } = await import('./src/vl/workflow-generation-pipeline.js');
- ok('workflow-generation-pipeline.js', 'import OK');
- } catch (e) { fail('workflow-generation-pipeline.js', e.message); }
- try {
- const { WorkflowExecutor } = await import('./src/vl/workflow-executor.js');
- ok('workflow-executor.js', 'import OK');
- } catch (e) { fail('workflow-executor.js', e.message); }
- try {
- const { generateTestSkeleton } = await import('./src/vl/meta-test-generator.js');
- if (typeof generateTestSkeleton !== 'function') throw new Error('not a function');
- const result = generateTestSkeleton({ apps: [], sections: [], serviceDomains: [] }, {});
- if (!Array.isArray(result.testCases)) throw new Error('should return testCases array');
- ok('meta-test-generator.js', `generateTestSkeleton() returns ${result.testCases.length} cases for empty meta`);
- } catch (e) { fail('meta-test-generator.js', e.message); }
- }
- // ─── 5. Engine ───────────────────────────────────────────────────────
- async function testEngine() {
- console.log('\n═══ engine/ ═══');
- try {
- const { LocalWorkspace } = await import('./src/engine/local-workspace.js');
- const lw = new LocalWorkspace('/tmp/vlcode-lite-ws-test');
- await lw.createFile({ gid: 'test', path: 'hello.txt', content: 'world' });
- const read = await lw.readFile({ gid: 'test', path: 'hello.txt' });
- if (!read.content && read.content !== 'world') throw new Error(`readFile failed: ${JSON.stringify(read)}`);
- await lw.deleteFile({ gid: 'test', path: 'hello.txt' });
- ok('local-workspace.js', 'createFile/readFile/deleteFile work');
- } catch (e) { fail('local-workspace.js', e.message); }
- try {
- const { parseSSEChunk, mapEngineEvent } = await import('./src/engine/sse-parser.js');
- if (typeof parseSSEChunk !== 'function') throw new Error('parseSSEChunk not a function');
- const events = parseSSEChunk('event: test\ndata: {"foo":"bar"}\n\n');
- if (!Array.isArray(events)) throw new Error('should return array');
- ok('sse-parser.js', `parseSSEChunk works, parsed ${events.length} events`);
- } catch (e) { fail('sse-parser.js', e.message); }
- try {
- const { WorkflowEngineManager } = await import('./src/engine/workflow-engine-manager.js');
- ok('workflow-engine-manager.js', 'import OK');
- } catch (e) { fail('workflow-engine-manager.js', e.message); }
- }
- // ─── 6. Cloud / MCP ─────────────────────────────────────────────────
- async function testCloudMcp() {
- console.log('\n═══ cloud/ + mcp/ ═══');
- try {
- const { CloudAPI } = await import('./src/cloud/cloud-api.js');
- const api = new CloudAPI({ cookie: 'test' });
- if (typeof api.login !== 'function') throw new Error('login not a function');
- if (typeof api.listApps !== 'function') throw new Error('listApps not a function');
- if (typeof api.compileWorkspace !== 'function') throw new Error('compileWorkspace not a function');
- if (typeof api.syncPush !== 'function') throw new Error('syncPush not a function');
- ok('cloud-api.js', 'CloudAPI class OK, all key methods present');
- } catch (e) { fail('cloud-api.js', e.message); }
- // MCP server is a stdio process, just verify import
- try {
- // Can't import directly (it starts listening on stdin), just check file exists
- const fs = await import('fs');
- if (!fs.existsSync('./src/mcp/mcp-server.js')) throw new Error('file missing');
- ok('mcp-server.js', 'file exists (stdio process, cannot import test)');
- } catch (e) { fail('mcp-server.js', e.message); }
- }
- // ─── 7. Tools ────────────────────────────────────────────────────────
- async function testTools() {
- console.log('\n═══ tools/ ═══');
- const config = {
- workDir: '/tmp/vlcode-lite-tool-test',
- model: 'claude-opus-4-6',
- port: 3300,
- maxOutputTokens: 32000,
- llmProvider: 'cli',
- workspaceApiUrl: 'https://editor.visuallogic.ai/ih5/editor/workspace',
- autotest: { headless: true, parallelWorkers: 1, maxCases: 3, useWorkflowEngine: true },
- };
- const toolFiles = [
- ['read-file.js', 'createReadFileTool', config],
- ['edit-file.js', 'createEditFileTool', config],
- ['write-file.js', 'createWriteFileTool', config],
- ['bash.js', 'createBashTool', config],
- ['grep.js', 'createGrepTool', config],
- ['glob.js', 'createGlobTool', config],
- ['todo-write.js', 'createTodoWriteTool', null],
- ['sub-agent.js', 'createSubAgentTool', null],
- ['ask-user.js', 'createAskUserTool', null],
- ['tool-search.js', 'createToolSearchTool', null],
- ['memory.js', 'createMemoryTool', config],
- ['doc-center.js', 'createDocCenterTool', config],
- ['component-fetch.js', 'createComponentFetchTool', config],
- ['browser-inspect.js', 'createBrowserInspectTool', config],
- ['vl-compile.js', 'createVLCompileTool', config],
- ['vl-lint.js', 'createVLLintTool', config],
- ['vl-parse.js', 'createVLParseTool', config],
- ['vl-syntax-ref.js', 'createVLSyntaxRefTool', config],
- ['vl-validate.js', 'createVLValidateTool', config],
- ['vl-metadata.js', 'createVLMetadataTool', config],
- ['vl-component-test.js', 'createVLComponentTestTool', config],
- ['vl-generate.js', 'createVLGenerateTool', config],
- ['vl-adjust.js', 'createVLAdjustTool', config],
- ['vl-edit-section.js', 'createVLEditSectionTool', config],
- ['vl-autofix.js', 'createVLAutoFixTool', config],
- ['vl-cascade-edit.js', 'createVLCascadeEditTool', config],
- ['vl-symbols.js', 'createVLSymbolsTool', config],
- ['vl-impact.js', 'createVLImpactTool', config],
- ['meta-diff.js', 'createMetaDiffTool', config],
- ['section-diff.js', 'createSectionDiffTool', config],
- ['vl-meta-test.js', 'createVLMetaTestTool', config],
- ['workflow-run.js', 'createWorkflowRunTool', config],
- ['workspace-api.js', 'createWorkspaceAPITool', config],
- ['autotest-pipeline.js', 'createAutoTestPipelineTool', config],
- ];
- for (const [file, fnName, arg] of toolFiles) {
- try {
- const mod = await import(`./src/tools/${file}`);
- const fn = mod[fnName] || mod.default;
- if (!fn) {
- // Check what's actually exported
- const exports = Object.keys(mod);
- throw new Error(`${fnName} not found. Exports: [${exports.join(', ')}]`);
- }
- if (typeof fn !== 'function') throw new Error(`${fnName} is ${typeof fn}, not function`);
- let tool;
- if (arg === null) {
- tool = fn();
- } else if (file === 'workspace-api.js') {
- const { LocalWorkspace } = await import('./src/engine/local-workspace.js');
- tool = fn(config, new LocalWorkspace('/tmp'));
- } else if (file === 'autotest-pipeline.js') {
- const { VLProjectContext } = await import('./src/vl/project-context.js');
- const ctx = new VLProjectContext('/tmp');
- tool = fn(config, ctx);
- } else {
- tool = fn(arg);
- }
- if (!tool) throw new Error('factory returned null/undefined');
- if (!tool.description) throw new Error('missing description');
- if (!tool.parameters) throw new Error('missing parameters');
- if (typeof tool.execute !== 'function') throw new Error('missing execute function');
- ok(file, `${fnName}() → tool with description + parameters + execute`);
- } catch (e) {
- fail(file, e.message);
- }
- }
- // Test tool registration integration
- try {
- const { ToolRegistry } = await import('./src/core/tool-registry.js');
- const { registerAllTools } = await import('./src/tools/index.js');
- const { VLProjectContext } = await import('./src/vl/project-context.js');
- const { LocalWorkspace } = await import('./src/engine/local-workspace.js');
- const reg = new ToolRegistry();
- const ctx = new VLProjectContext('/tmp');
- await ctx.load();
- registerAllTools(reg, config, ctx, { localWorkspace: new LocalWorkspace('/tmp') });
- const tools = reg.listTools();
- ok('index.js', `registerAllTools() registered ${tools.length} tools: ${tools.join(', ')}`);
- } catch (e) { fail('index.js (registerAllTools)', e.message); }
- }
- // ─── 8. Server ───────────────────────────────────────────────────────
- async function testServer() {
- console.log('\n═══ server/ ═══');
- try {
- const { WebServer } = await import('./src/server/server.js');
- if (typeof WebServer !== 'function') throw new Error('not a class');
- ok('server.js', 'WebServer class import OK');
- } catch (e) { fail('server.js', e.message); }
- try {
- const mod = await import('./src/server/sse.js');
- if (typeof mod.setupSSE !== 'function') throw new Error('setupSSE not a function');
- ok('sse.js', 'setupSSE function OK');
- } catch (e) { fail('sse.js', e.message); }
- try {
- const mod = await import('./src/server/helpers.js');
- const fns = ['getCookie', 'clearCookie', 'saveCookie', 'loadWorkspaces', 'saveWorkspaces',
- 'ensureVLBible', 'hasVLFiles', 'ensureProjectProfile', 'ensureVLCodeHome'];
- const missing = fns.filter(f => typeof mod[f] !== 'function');
- if (missing.length > 0) throw new Error(`missing functions: ${missing.join(', ')}`);
- ok('helpers.js', `all ${fns.length} helper functions exported`);
- } catch (e) { fail('helpers.js', e.message); }
- try {
- const { WebServer } = await import('./src/server/index.js');
- if (typeof WebServer !== 'function') throw new Error('re-export failed');
- ok('index.js', 'WebServer re-export OK');
- } catch (e) { fail('server/index.js', e.message); }
- // Route modules — verify they export setup functions
- const routes = [
- 'chat.js', 'files.js', 'project.js', 'workspace.js', 'compile.js',
- 'workflow.js', 'cloud.js', 'intelligence.js', 'tools.js', 'conversation.js', 'misc.js'
- ];
- const setupFnNames = {
- 'chat.js': 'setupChatRoutes',
- 'files.js': 'setupFileRoutes',
- 'project.js': 'setupProjectRoutes',
- 'workspace.js': 'setupWorkspaceRoutes',
- 'compile.js': 'setupCompileRoutes',
- 'workflow.js': 'setupWorkflowRoutes',
- 'cloud.js': 'setupCloudRoutes',
- 'intelligence.js': 'setupIntelligenceRoutes',
- 'tools.js': 'setupToolRoutes',
- 'conversation.js': 'setupConversationRoutes',
- 'misc.js': 'setupMiscRoutes',
- };
- for (const route of routes) {
- try {
- const mod = await import(`./src/server/routes/${route}`);
- const expectedFn = setupFnNames[route];
- if (expectedFn && typeof mod[expectedFn] !== 'function') {
- throw new Error(`${expectedFn} not found. Exports: [${Object.keys(mod).join(', ')}]`);
- }
- ok(`routes/${route}`, `${expectedFn}() exported`);
- } catch (e) {
- fail(`routes/${route}`, e.message);
- }
- }
- }
- // ─── 9. Autotest sub-modules ─────────────────────────────────────────
- async function testAutotest() {
- console.log('\n═══ tools/autotest/ ═══');
- const files = [
- 'test-generator.js', 'test-runner.js', 'test-evaluator.js',
- 'auto-fixer.js', 'case-parser.js', 'report-generator.js',
- 'workflow-builder.js', 'workflow-runner.js'
- ];
- for (const file of files) {
- try {
- const mod = await import(`./src/tools/autotest/${file}`);
- const exports = Object.keys(mod);
- if (exports.length === 0) throw new Error('no exports');
- ok(`autotest/${file}`, `exports: ${exports.join(', ')}`);
- } catch (e) {
- fail(`autotest/${file}`, e.message);
- }
- }
- }
- // ─── 10. Functional tests (deeper) ──────────────────────────────────
- async function testFunctional() {
- console.log('\n═══ Functional Tests ═══');
- // ReadFile tool — actually read a file
- try {
- const { createReadFileTool } = await import('./src/tools/read-file.js');
- const tool = createReadFileTool({ workDir: '/tmp' });
- const result = await tool.execute({ file_path: path.join(__dirname, 'package.json') });
- const text = result.result || result;
- if (!text.includes('vlcode-lite')) throw new Error('ReadFile did not return package.json content');
- ok('ReadFile.execute', 'successfully reads package.json');
- } catch (e) { fail('ReadFile.execute', e.message); }
- // WriteFile + EditFile + ReadFile round-trip
- try {
- const { createWriteFileTool } = await import('./src/tools/write-file.js');
- const { createEditFileTool } = await import('./src/tools/edit-file.js');
- const { createReadFileTool } = await import('./src/tools/read-file.js');
- const cfg = { workDir: '/tmp' };
- const testFile = '/tmp/vlcode-lite-test-roundtrip.txt';
- const wt = createWriteFileTool(cfg);
- await wt.execute({ file_path: testFile, content: 'hello world' });
- const et = createEditFileTool(cfg);
- await et.execute({ file_path: testFile, old_string: 'hello', new_string: 'goodbye' });
- const rt = createReadFileTool(cfg);
- const readResult = await rt.execute({ file_path: testFile });
- const content = readResult.result || readResult;
- if (!content.includes('goodbye world')) throw new Error(`expected 'goodbye world', got: ${String(content).substring(0, 100)}`);
- ok('Write→Edit→Read roundtrip', 'file operations work correctly');
- } catch (e) { fail('Write→Edit→Read roundtrip', e.message); }
- // Bash tool
- try {
- const { createBashTool } = await import('./src/tools/bash.js');
- const tool = createBashTool({ workDir: '/tmp' });
- const result = await tool.execute({ command: 'echo "vlcode-lite-test"' });
- if (!result.includes('vlcode-lite-test')) throw new Error(`expected echo output, got: ${result}`);
- ok('Bash.execute', 'echo command works');
- } catch (e) { fail('Bash.execute', e.message); }
- // Grep tool
- try {
- const { createGrepTool } = await import('./src/tools/grep.js');
- const tool = createGrepTool({ workDir: __dirname });
- const result = await tool.execute({ pattern: 'vlcode-lite', path: path.join(__dirname, 'package.json') });
- if (!result) throw new Error('grep returned empty');
- ok('Grep.execute', 'finds "vlcode-lite" in package.json');
- } catch (e) { fail('Grep.execute', e.message); }
- // Glob tool
- try {
- const { createGlobTool } = await import('./src/tools/glob.js');
- const tool = createGlobTool({ workDir: __dirname });
- const result = await tool.execute({ pattern: 'src/core/*.js', path: __dirname });
- if (!result.includes('orchestrator.js')) throw new Error(`glob did not find orchestrator.js: ${result.substring(0, 200)}`);
- ok('Glob.execute', 'finds src/core/*.js files');
- } catch (e) { fail('Glob.execute', e.message); }
- // Memory tool
- try {
- const { createMemoryTool } = await import('./src/tools/memory.js');
- const tool = createMemoryTool({ workDir: '/tmp/vlcode-lite-mem-test' });
- await tool.execute({ operation: 'write', scope: 'project', key: 'test-key', value: 'test-value-123' });
- const readResult = await tool.execute({ operation: 'read', scope: 'project', key: 'test-key' });
- if (!readResult.includes('test-value-123')) throw new Error(`memory read failed: ${readResult}`);
- await tool.execute({ operation: 'delete', scope: 'project', key: 'test-key' });
- ok('Memory.execute', 'write/read/delete cycle works');
- } catch (e) { fail('Memory.execute', e.message); }
- // LocalWorkspace advanced
- try {
- const { LocalWorkspace } = await import('./src/engine/local-workspace.js');
- const lw = new LocalWorkspace('/tmp/vlcode-lite-lw-test');
- await lw.writeFiles({ gid: 'test', files: [
- { path: 'a.txt', content: 'aaa' },
- { path: 'sub/b.txt', content: 'bbb' },
- ]});
- const list = await lw.listFile({ gid: 'test' });
- if (!list.files || list.files.length < 2) throw new Error(`listFile should find >=2 files, got ${JSON.stringify(list)}`);
- const reads = await lw.readFiles({ gid: 'test', paths: ['a.txt', 'sub/b.txt'] });
- if (!reads.files || reads.files.length !== 2) throw new Error(`readFiles should return 2, got ${JSON.stringify(reads)}`);
- ok('LocalWorkspace batch', 'writeFiles/listFile/readFiles work');
- } catch (e) { fail('LocalWorkspace batch', e.message); }
- }
- // ─── Run all ─────────────────────────────────────────────────────────
- async function main() {
- console.log('╔══════════════════════════════════════════════════╗');
- console.log('║ VLCode Lite — Module Verification Suite ║');
- console.log('╚══════════════════════════════════════════════════╝');
- await testUtils();
- await testData();
- await testCore();
- await testVL();
- await testEngine();
- await testCloudMcp();
- await testTools();
- await testServer();
- await testAutotest();
- await testFunctional();
- console.log('\n══════════════════════════════════════════════════');
- console.log(` Results: ${passCount} passed, ${failCount} failed, ${warnCount} warnings`);
- console.log('══════════════════════════════════════════════════');
- if (failCount > 0) {
- console.log('\n❌ FAILURES:');
- results.filter(r => r.includes('❌')).forEach(r => console.log(r));
- }
- if (warnCount > 0) {
- console.log('\n⚠️ WARNINGS:');
- results.filter(r => r.includes('⚠️')).forEach(r => console.log(r));
- }
- console.log('\n✅ PASSED:');
- results.filter(r => r.includes('✅')).forEach(r => console.log(r));
- process.exit(failCount > 0 ? 1 : 0);
- }
- main().catch(e => { console.error('FATAL:', e); process.exit(2); });
|