@crownpeak/dqm-react-component-dev-mcp 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +138 -0
- package/data/.env.example +22 -0
- package/data/.gitattributes +47 -0
- package/data/.glfrc.json +7 -0
- package/data/.husky/pre-commit +5 -0
- package/data/.nvmrc +1 -0
- package/data/CHANGELOG.md +75 -0
- package/data/CODE_OF_CONDUCT.md +129 -0
- package/data/CONTRIBUTING.md +203 -0
- package/data/DOCS-STRUCTURE.md +307 -0
- package/data/I18N.md +292 -0
- package/data/LICENSE +22 -0
- package/data/README.md +315 -0
- package/data/SECURITY.md +125 -0
- package/data/WIKI-DEPLOYMENT.md +348 -0
- package/data/docs/AI-FEATURES.md +610 -0
- package/data/docs/API-REFERENCE.md +1022 -0
- package/data/docs/AUTHENTICATION.md +301 -0
- package/data/docs/BACKEND-API.md +468 -0
- package/data/docs/DEVELOPMENT.md +375 -0
- package/data/docs/EXAMPLES.md +622 -0
- package/data/docs/MCP-SERVER.md +307 -0
- package/data/docs/MIGRATION-GUIDE.md +367 -0
- package/data/docs/NPM-PUBLISH.md +193 -0
- package/data/docs/QUICKSTART.md +206 -0
- package/data/docs/REDIS-SETUP.md +162 -0
- package/data/docs/SERVER.md +228 -0
- package/data/docs/TROUBLESHOOTING.md +657 -0
- package/data/docs/WIDGET-GUIDE.md +638 -0
- package/data/docs/WIKI-HOME.md +58 -0
- package/data/docs/WIKI-SIDEBAR.md +39 -0
- package/data/package.json +171 -0
- package/data/playwright.config.ts +64 -0
- package/data/probe/.cargo/config.toml +10 -0
- package/data/probe/.claude/commands/performance-review.md +15 -0
- package/data/probe/.clinerules +288 -0
- package/data/probe/.dockerignore +57 -0
- package/data/probe/.githooks/post-commit +11 -0
- package/data/probe/.githooks/pre-commit +99 -0
- package/data/probe/.githooks/pre-commit-vow +9 -0
- package/data/probe/.prompts/engineer.md +41 -0
- package/data/probe/.roomodes +28 -0
- package/data/probe/.windsurfrules +0 -0
- package/data/probe/BASH_TOOL_SUMMARY.md +148 -0
- package/data/probe/BENCHMARKING.md +256 -0
- package/data/probe/CLAUDE.md +226 -0
- package/data/probe/CODE_OF_CONDUCT.md +128 -0
- package/data/probe/CONTRIBUTING.md +193 -0
- package/data/probe/Cargo.toml +120 -0
- package/data/probe/Cross.toml +10 -0
- package/data/probe/DOCKER-README.md +224 -0
- package/data/probe/Dockerfile +32 -0
- package/data/probe/ENHANCED_DEBUG_TELEMETRY.md +188 -0
- package/data/probe/LICENSE +201 -0
- package/data/probe/Makefile +210 -0
- package/data/probe/README.md +824 -0
- package/data/probe/SECURITY.md +67 -0
- package/data/probe/WINDOWS-GUIDE.md +294 -0
- package/data/probe/benches/parsing_benchmarks.rs +370 -0
- package/data/probe/benches/search_benchmarks.rs +599 -0
- package/data/probe/benches/simd_benchmarks.rs +372 -0
- package/data/probe/benches/timing_benchmarks.rs +287 -0
- package/data/probe/build-windows.bat +229 -0
- package/data/probe/codex-config/config.toml +6 -0
- package/data/probe/docs/PERFORMANCE_OPTIMIZATION.md +161 -0
- package/data/probe/examples/cache_demo.rs +46 -0
- package/data/probe/examples/chat/.dockerignore +37 -0
- package/data/probe/examples/chat/ChatSessionManager.js +295 -0
- package/data/probe/examples/chat/Dockerfile +98 -0
- package/data/probe/examples/chat/LICENSE +201 -0
- package/data/probe/examples/chat/LOCAL_IMAGE_SUPPORT.md +195 -0
- package/data/probe/examples/chat/MCP_INTEGRATION.md +400 -0
- package/data/probe/examples/chat/README.md +338 -0
- package/data/probe/examples/chat/TRACING.md +226 -0
- package/data/probe/examples/chat/appTracer.js +968 -0
- package/data/probe/examples/chat/auth.js +76 -0
- package/data/probe/examples/chat/bin/probe-chat.js +13 -0
- package/data/probe/examples/chat/build.js +104 -0
- package/data/probe/examples/chat/cancelRequest.js +84 -0
- package/data/probe/examples/chat/demo-agentic-image-flow.js +88 -0
- package/data/probe/examples/chat/demo-local-images.js +128 -0
- package/data/probe/examples/chat/fileSpanExporter.js +181 -0
- package/data/probe/examples/chat/implement/README.md +228 -0
- package/data/probe/examples/chat/implement/backends/AiderBackend.js +750 -0
- package/data/probe/examples/chat/implement/backends/BaseBackend.js +276 -0
- package/data/probe/examples/chat/implement/backends/ClaudeCodeBackend.js +767 -0
- package/data/probe/examples/chat/implement/backends/MockBackend.js +237 -0
- package/data/probe/examples/chat/implement/backends/registry.js +85 -0
- package/data/probe/examples/chat/implement/core/BackendManager.js +567 -0
- package/data/probe/examples/chat/implement/core/ImplementTool.js +354 -0
- package/data/probe/examples/chat/implement/core/config.js +428 -0
- package/data/probe/examples/chat/implement/core/timeouts.js +58 -0
- package/data/probe/examples/chat/implement/core/utils.js +496 -0
- package/data/probe/examples/chat/implement/types/BackendTypes.js +126 -0
- package/data/probe/examples/chat/index.js +669 -0
- package/data/probe/examples/chat/mcpServer.js +341 -0
- package/data/probe/examples/chat/npm/LICENSE +15 -0
- package/data/probe/examples/chat/npm/README.md +168 -0
- package/data/probe/examples/chat/npm/bin/probe-chat.js +156 -0
- package/data/probe/examples/chat/npm/index.js +259 -0
- package/data/probe/examples/chat/npm/package.json +54 -0
- package/data/probe/examples/chat/package.json +102 -0
- package/data/probe/examples/chat/probeChat.js +456 -0
- package/data/probe/examples/chat/probeTool.js +491 -0
- package/data/probe/examples/chat/storage/JsonChatStorage.js +476 -0
- package/data/probe/examples/chat/telemetry.js +281 -0
- package/data/probe/examples/chat/test/integration/chatFlows.test.js +320 -0
- package/data/probe/examples/chat/test/integration/toolCalling.test.js +471 -0
- package/data/probe/examples/chat/test/mocks/mockLLMProvider.js +269 -0
- package/data/probe/examples/chat/test/test-backends.js +90 -0
- package/data/probe/examples/chat/test/testUtils.js +530 -0
- package/data/probe/examples/chat/test/unit/backendTimeout.test.js +161 -0
- package/data/probe/examples/chat/test/unit/packageFiles.test.js +120 -0
- package/data/probe/examples/chat/test/verify-tests.js +118 -0
- package/data/probe/examples/chat/test-agentic-image-loading.js +294 -0
- package/data/probe/examples/chat/test-ai-sdk-telemetry.js +204 -0
- package/data/probe/examples/chat/test-chat-tracing.js +38 -0
- package/data/probe/examples/chat/test-direct-function.js +49 -0
- package/data/probe/examples/chat/test-file-size-validation.js +103 -0
- package/data/probe/examples/chat/test-full-mcp-integration.js +258 -0
- package/data/probe/examples/chat/test-github-context.txt +12 -0
- package/data/probe/examples/chat/test-hierarchy.js +203 -0
- package/data/probe/examples/chat/test-image-spans.js +37 -0
- package/data/probe/examples/chat/test-local-image-reading.js +176 -0
- package/data/probe/examples/chat/test-mcp-integration.js +136 -0
- package/data/probe/examples/chat/test-mcp-probe-server.js +161 -0
- package/data/probe/examples/chat/test-mcp-with-ai.js +279 -0
- package/data/probe/examples/chat/test-multiple-allowed-dirs.js +111 -0
- package/data/probe/examples/chat/test-probe-mcp-server.js +110 -0
- package/data/probe/examples/chat/test-security-validation.js +145 -0
- package/data/probe/examples/chat/test-simple-tracing.js +32 -0
- package/data/probe/examples/chat/test-trace-verification.js +235 -0
- package/data/probe/examples/chat/test-tracing.js +114 -0
- package/data/probe/examples/chat/tokenCounter.js +419 -0
- package/data/probe/examples/chat/tokenUsageDisplay.js +134 -0
- package/data/probe/examples/chat/webServer.js +1103 -0
- package/data/probe/examples/reranker/Cargo.toml +33 -0
- package/data/probe/examples/reranker/DEBUG_OUTPUT_ANALYSIS.md +71 -0
- package/data/probe/examples/reranker/MODELS.md +66 -0
- package/data/probe/examples/reranker/MODEL_COMPARISON.md +60 -0
- package/data/probe/examples/reranker/MULTI_MODEL_ANALYSIS.md +176 -0
- package/data/probe/examples/reranker/PERFORMANCE_SUMMARY.md +156 -0
- package/data/probe/examples/reranker/README.md +347 -0
- package/data/probe/examples/reranker/RUST_BERT_COMPARISON.md +82 -0
- package/data/probe/examples/reranker/TOKENIZATION_GUIDE.md +120 -0
- package/data/probe/examples/reranker/check_rust_tokenizer.py +108 -0
- package/data/probe/examples/reranker/convert_to_torchscript.py +109 -0
- package/data/probe/examples/reranker/debug_scoring.py +189 -0
- package/data/probe/examples/reranker/debug_tokenization.py +154 -0
- package/data/probe/examples/reranker/download_models.sh +73 -0
- package/data/probe/examples/reranker/requirements.txt +13 -0
- package/data/probe/examples/reranker/run_comprehensive_benchmark.sh +83 -0
- package/data/probe/examples/reranker/rust_bert_test/Cargo.toml +12 -0
- package/data/probe/examples/reranker/rust_bert_test/README.md +54 -0
- package/data/probe/examples/reranker/simple_test.py +50 -0
- package/data/probe/examples/reranker/test_all_models.sh +63 -0
- package/data/probe/examples/reranker/test_bert_results.sh +44 -0
- package/data/probe/examples/reranker/test_cross_encoder.py +334 -0
- package/data/probe/examples/reranker/test_cross_encoder.sh +80 -0
- package/data/probe/examples/reranker/test_exact_comparison.py +151 -0
- package/data/probe/examples/reranker/test_parallel_performance.sh +56 -0
- package/data/probe/examples/reranker/test_scores.py +132 -0
- package/data/probe/install.ps1 +508 -0
- package/data/probe/install.sh +460 -0
- package/data/probe/npm/CLONE_METHOD_EXAMPLES.md +596 -0
- package/data/probe/npm/CONTEXT_COMPACTION.md +303 -0
- package/data/probe/npm/DELEGATE_TOOL_README.md +166 -0
- package/data/probe/npm/MAID_INTEGRATION.md +313 -0
- package/data/probe/npm/MCP_INTEGRATION_SUMMARY.md +241 -0
- package/data/probe/npm/README.md +824 -0
- package/data/probe/npm/bin/.gitignore +7 -0
- package/data/probe/npm/bin/.gitkeep +0 -0
- package/data/probe/npm/bin/README.md +12 -0
- package/data/probe/npm/bin/probe +167 -0
- package/data/probe/npm/docs/CLAUDE_CODE_INTEGRATION.md +414 -0
- package/data/probe/npm/docs/CODEX_INTEGRATION.md +502 -0
- package/data/probe/npm/docs/EDIT_CREATE_TOOLS.md +233 -0
- package/data/probe/npm/docs/RETRY_AND_FALLBACK.md +674 -0
- package/data/probe/npm/example-usage.js +335 -0
- package/data/probe/npm/examples/multi-engine-demo.js +117 -0
- package/data/probe/npm/examples/probe-agent-cli.js +113 -0
- package/data/probe/npm/examples/test-agent-edit.js +114 -0
- package/data/probe/npm/examples/test-edit-create.js +120 -0
- package/data/probe/npm/examples/test-edit-direct.js +114 -0
- package/data/probe/npm/index.d.ts +744 -0
- package/data/probe/npm/jest.config.js +52 -0
- package/data/probe/npm/package.json +117 -0
- package/data/probe/npm/scripts/build-agent.cjs +75 -0
- package/data/probe/npm/scripts/build-cjs.js +124 -0
- package/data/probe/npm/scripts/build-mcp.cjs +36 -0
- package/data/probe/npm/scripts/postinstall.js +216 -0
- package/data/probe/npm/test-codex-e2e.js +78 -0
- package/data/probe/npm/test-download-lock.js +109 -0
- package/data/probe/npm/test-grep-security.js +94 -0
- package/data/probe/npm/test-grep-simplified.js +63 -0
- package/data/probe/npm/test-grep.js +51 -0
- package/data/probe/npm/tests/README.md +96 -0
- package/data/probe/npm/tests/agent-compact-history.test.js +174 -0
- package/data/probe/npm/tests/allow-tests-default.test.js +151 -0
- package/data/probe/npm/tests/contextCompactor.test.js +498 -0
- package/data/probe/npm/tests/delegate-config.test.js +353 -0
- package/data/probe/npm/tests/delegate-integration.test.js +348 -0
- package/data/probe/npm/tests/extractor-integration.test.js +162 -0
- package/data/probe/npm/tests/extractor.test.js +317 -0
- package/data/probe/npm/tests/fixtures/sampleDiagrams.js +267 -0
- package/data/probe/npm/tests/integration/claude-code-auto-fallback.spec.js +148 -0
- package/data/probe/npm/tests/integration/claude-code-multi-step.spec.js +127 -0
- package/data/probe/npm/tests/integration/claude-code-tool-events.spec.js +163 -0
- package/data/probe/npm/tests/integration/codex-auto-fallback.spec.js +191 -0
- package/data/probe/npm/tests/integration/codex-tool-events.spec.js +147 -0
- package/data/probe/npm/tests/integration/examplesChatMcp.test.js +402 -0
- package/data/probe/npm/tests/integration/mcpDotenvSupport.test.js +174 -0
- package/data/probe/npm/tests/integration/mcpErrorHandling.test.js +566 -0
- package/data/probe/npm/tests/integration/mcpRobustness.test.js +564 -0
- package/data/probe/npm/tests/integration/mcpStdoutPurity.test.js +355 -0
- package/data/probe/npm/tests/integration/probeAgentMcp.test.js +398 -0
- package/data/probe/npm/tests/integration/retryFallback.test.js +368 -0
- package/data/probe/npm/tests/integration/schema-in-initial-message.test.js +318 -0
- package/data/probe/npm/tests/integration/schema-validation-loop-prevention.test.js +244 -0
- package/data/probe/npm/tests/integration/schemaRetryLogic.test.js +94 -0
- package/data/probe/npm/tests/integration/validationFlow.test.js +329 -0
- package/data/probe/npm/tests/manual/test-codex-basic.js +110 -0
- package/data/probe/npm/tests/mcp/mcpClientManager.test.js +614 -0
- package/data/probe/npm/tests/mcp/mcpConfig.test.js +359 -0
- package/data/probe/npm/tests/mcp/mcpXmlBridge.test.js +436 -0
- package/data/probe/npm/tests/mcp/mockMcpServer.js +510 -0
- package/data/probe/npm/tests/mcp-strict-syntax.test.js +319 -0
- package/data/probe/npm/tests/mermaidQuoteEscaping.test.js +214 -0
- package/data/probe/npm/tests/nestedQuoteFix.test.js +40 -0
- package/data/probe/npm/tests/setup.js +46 -0
- package/data/probe/npm/tests/unit/allowed-tools.test.js +513 -0
- package/data/probe/npm/tests/unit/attempt-completion-closing-tag-in-content.test.js +188 -0
- package/data/probe/npm/tests/unit/attemptCompletionJsonFix.test.js +238 -0
- package/data/probe/npm/tests/unit/attemptCompletionJsonIssue.test.js +128 -0
- package/data/probe/npm/tests/unit/backtickAutoFix.test.js +35 -0
- package/data/probe/npm/tests/unit/bash-probe-agent-integration.test.js +389 -0
- package/data/probe/npm/tests/unit/bash-simple-commands.test.js +324 -0
- package/data/probe/npm/tests/unit/bash-tool-comprehensive.test.js +371 -0
- package/data/probe/npm/tests/unit/bash-tool-integration.test.js +310 -0
- package/data/probe/npm/tests/unit/bash-tool.test.js +341 -0
- package/data/probe/npm/tests/unit/completion-prompt.test.js +379 -0
- package/data/probe/npm/tests/unit/cwd-path-options.test.js +287 -0
- package/data/probe/npm/tests/unit/delegate-limits.test.js +422 -0
- package/data/probe/npm/tests/unit/direct-content-attempt-completion.test.js +235 -0
- package/data/probe/npm/tests/unit/edit-create-tools.test.js +609 -0
- package/data/probe/npm/tests/unit/enhancedMermaidValidation.test.js +577 -0
- package/data/probe/npm/tests/unit/extract-content.test.js +83 -0
- package/data/probe/npm/tests/unit/extract-multiple-targets.test.js +89 -0
- package/data/probe/npm/tests/unit/fallbackManager.test.js +442 -0
- package/data/probe/npm/tests/unit/githubCompatibilityValidation.test.js +258 -0
- package/data/probe/npm/tests/unit/imageConfig.test.js +149 -0
- package/data/probe/npm/tests/unit/imagePathResolution.test.js +345 -0
- package/data/probe/npm/tests/unit/json-fixing-agent.test.js +238 -0
- package/data/probe/npm/tests/unit/json-validation-enhanced-errors.test.js +199 -0
- package/data/probe/npm/tests/unit/jsonValidationInfiniteLoopFix.test.js +228 -0
- package/data/probe/npm/tests/unit/maidIntegration.test.js +139 -0
- package/data/probe/npm/tests/unit/maxIterationsWarning.test.js +195 -0
- package/data/probe/npm/tests/unit/mermaidEdgeLabelFix.test.js +161 -0
- package/data/probe/npm/tests/unit/mermaidHtmlEntities.test.js +76 -0
- package/data/probe/npm/tests/unit/mermaidInfiniteLoopFix.test.js +64 -0
- package/data/probe/npm/tests/unit/mermaidValidation.test.js +723 -0
- package/data/probe/npm/tests/unit/mermaidValidationVisorExample.test.js +309 -0
- package/data/probe/npm/tests/unit/probe-agent-clone-realistic.test.js +643 -0
- package/data/probe/npm/tests/unit/probe-agent-clone.test.js +476 -0
- package/data/probe/npm/tests/unit/probe-agent-delegate.test.js +400 -0
- package/data/probe/npm/tests/unit/probe-agent-model-option.test.js +118 -0
- package/data/probe/npm/tests/unit/probeTool-security.test.js +283 -0
- package/data/probe/npm/tests/unit/readImageTool.test.js +418 -0
- package/data/probe/npm/tests/unit/retryManager.test.js +317 -0
- package/data/probe/npm/tests/unit/schema-aware-reminders.test.js +288 -0
- package/data/probe/npm/tests/unit/schemaDefinitionDetection.test.js +115 -0
- package/data/probe/npm/tests/unit/schemaUtils.test.js +1268 -0
- package/data/probe/npm/tests/unit/simpleTelemetry.test.js +282 -0
- package/data/probe/npm/tests/unit/simplified-attempt-completion.test.js +274 -0
- package/data/probe/npm/tests/unit/single-quote-json-bug.test.js +231 -0
- package/data/probe/npm/tests/unit/subgraphAutoFix.test.js +110 -0
- package/data/probe/npm/tests/unit/system-prompt.test.js +32 -0
- package/data/probe/npm/tests/unit/types-probe-agent-options.test.js +42 -0
- package/data/probe/npm/tests/unit/xmlParsing.test.js +720 -0
- package/data/probe/npm/tsconfig.json +21 -0
- package/data/probe/result1.txt +19 -0
- package/data/probe/result2.txt +26 -0
- package/data/probe/scripts/benchmark.sh +270 -0
- package/data/probe/scripts/cache_memory_analysis.rs +844 -0
- package/data/probe/scripts/claude-hook-wrapper.sh +56 -0
- package/data/probe/site/.env.example +10 -0
- package/data/probe/site/DEPLOYMENT.md +86 -0
- package/data/probe/site/README.md +183 -0
- package/data/probe/site/adding-languages.md +135 -0
- package/data/probe/site/ai-chat.md +427 -0
- package/data/probe/site/ai-integration.md +1488 -0
- package/data/probe/site/blog/agentic-flow-custom-xml-protocol.md +407 -0
- package/data/probe/site/blog/index.md +118 -0
- package/data/probe/site/blog/v0.6.0-release.md +426 -0
- package/data/probe/site/blog.md +8 -0
- package/data/probe/site/changelog.md +200 -0
- package/data/probe/site/cli-mode.md +437 -0
- package/data/probe/site/code-extraction.md +436 -0
- package/data/probe/site/contributing/README.md +9 -0
- package/data/probe/site/contributing/documentation-cross-references.md +215 -0
- package/data/probe/site/contributing/documentation-maintenance.md +275 -0
- package/data/probe/site/contributing/documentation-structure.md +75 -0
- package/data/probe/site/documentation-cross-references.md +215 -0
- package/data/probe/site/documentation-guide.md +132 -0
- package/data/probe/site/documentation-maintenance.md +275 -0
- package/data/probe/site/features.md +147 -0
- package/data/probe/site/how-it-works.md +118 -0
- package/data/probe/site/index.md +175 -0
- package/data/probe/site/index.md.bak +133 -0
- package/data/probe/site/installation.md +235 -0
- package/data/probe/site/integrations/docker.md +248 -0
- package/data/probe/site/integrations/github-actions.md +413 -0
- package/data/probe/site/language-support-overview.md +168 -0
- package/data/probe/site/mcp-integration.md +587 -0
- package/data/probe/site/mcp-server.md +304 -0
- package/data/probe/site/navigation-structure.md +76 -0
- package/data/probe/site/nodejs-sdk.md +798 -0
- package/data/probe/site/output-formats.md +625 -0
- package/data/probe/site/package.json +21 -0
- package/data/probe/site/public/_headers +28 -0
- package/data/probe/site/public/_redirects +11 -0
- package/data/probe/site/quick-start.md +289 -0
- package/data/probe/site/search-functionality.md +291 -0
- package/data/probe/site/search-reference.md +291 -0
- package/data/probe/site/supported-languages.md +215 -0
- package/data/probe/site/use-cases/README.md +8 -0
- package/data/probe/site/use-cases/advanced-cli.md +253 -0
- package/data/probe/site/use-cases/ai-code-editors.md +239 -0
- package/data/probe/site/use-cases/building-ai-tools.md +529 -0
- package/data/probe/site/use-cases/cli-ai-workflows.md +285 -0
- package/data/probe/site/use-cases/deploying-probe-web-interface.md +255 -0
- package/data/probe/site/use-cases/integrating-probe-into-ai-code-editors.md +161 -0
- package/data/probe/site/use-cases/nodejs-sdk.md +596 -0
- package/data/probe/site/use-cases/team-chat.md +350 -0
- package/data/probe/site/web-interface.md +434 -0
- package/data/probe/site/wrangler.toml +9 -0
- package/data/probe/test-api-key.sh +1 -0
- package/data/probe/test-probe-implementation/hello.js +7 -0
- package/data/probe/test_cases/demonstrate_early_termination_issues.sh +176 -0
- package/data/probe/test_cases/early_termination_issues.rs +533 -0
- package/data/probe/test_data/test_nested_struct.go +26 -0
- package/data/probe/tests/README.md +286 -0
- package/data/probe/tests/README_search_determinism_tests.md +116 -0
- package/data/probe/tests/adjacent_comment_test.rs +152 -0
- package/data/probe/tests/apostrophe_handling_tests.rs +132 -0
- package/data/probe/tests/block_filtering_with_ast_tests.rs +669 -0
- package/data/probe/tests/block_merging_tests.rs +396 -0
- package/data/probe/tests/c_outline_format_tests.rs +2179 -0
- package/data/probe/tests/cache_invalidation_issues.rs.disabled +682 -0
- package/data/probe/tests/cache_order_tests.rs +147 -0
- package/data/probe/tests/cache_query_scoping_tests.rs +221 -0
- package/data/probe/tests/cli_tests.rs +680 -0
- package/data/probe/tests/comment_context_integration_test.rs +240 -0
- package/data/probe/tests/common.rs +33 -0
- package/data/probe/tests/complex_block_merging_tests.rs +599 -0
- package/data/probe/tests/complex_query_block_filtering_tests.rs +422 -0
- package/data/probe/tests/control_flow_closing_braces_test.rs +91 -0
- package/data/probe/tests/cpp_outline_format_tests.rs +1507 -0
- package/data/probe/tests/csharp_outline_format_tests.rs +941 -0
- package/data/probe/tests/elastic_query_integration_tests.rs +922 -0
- package/data/probe/tests/extract_command_tests.rs +1848 -0
- package/data/probe/tests/extract_deduplication_tests.rs +146 -0
- package/data/probe/tests/extract_input_file_tests.rs +84 -0
- package/data/probe/tests/extract_prompt_tests.rs +102 -0
- package/data/probe/tests/filename_search_tests.rs +96 -0
- package/data/probe/tests/fixtures/user/AssemblyInfo.cs +3 -0
- package/data/probe/tests/github_extract_tests.rs +234 -0
- package/data/probe/tests/go_comment_test.rs +253 -0
- package/data/probe/tests/go_outline_format_tests.rs +2587 -0
- package/data/probe/tests/go_path_resolver_tests.rs +96 -0
- package/data/probe/tests/html_outline_format_tests.rs +637 -0
- package/data/probe/tests/integration_tests.rs +837 -0
- package/data/probe/tests/ip_whitelist_test.rs +148 -0
- package/data/probe/tests/java_outline_format_tests.rs +1611 -0
- package/data/probe/tests/javascript_extract_tests.rs +315 -0
- package/data/probe/tests/javascript_outline_format_tests.rs +1464 -0
- package/data/probe/tests/json_format_tests.rs +436 -0
- package/data/probe/tests/json_schema_validation_tests.rs +450 -0
- package/data/probe/tests/lib_usage.rs +60 -0
- package/data/probe/tests/line_comment_context_extension_test.rs +459 -0
- package/data/probe/tests/line_map_cache_tests.rs +114 -0
- package/data/probe/tests/markdown_integration_tests.rs +190 -0
- package/data/probe/tests/mocks/test_ip_whitelist.go +11 -0
- package/data/probe/tests/mocks/test_object.js +27 -0
- package/data/probe/tests/mocks/test_struct.go +50 -0
- package/data/probe/tests/multi_keyword_pattern_tests.rs +464 -0
- package/data/probe/tests/multi_language_syntax_integration_tests.rs +218 -0
- package/data/probe/tests/multiple_capture_groups_tests.rs +169 -0
- package/data/probe/tests/negative_compound_word_tests.rs +246 -0
- package/data/probe/tests/nested_symbol_extraction_tests.rs +99 -0
- package/data/probe/tests/outline_cross_file_interference_test.rs +335 -0
- package/data/probe/tests/outline_keyword_preservation_test.rs +67 -0
- package/data/probe/tests/output_format_edge_cases_tests.rs +693 -0
- package/data/probe/tests/parallel_extraction_tests.rs +178 -0
- package/data/probe/tests/parallel_search_tests.rs +355 -0
- package/data/probe/tests/path_resolver_tests.rs +698 -0
- package/data/probe/tests/php_outline_format_extended_tests.rs +928 -0
- package/data/probe/tests/php_outline_format_tests.rs +768 -0
- package/data/probe/tests/property_tests.proptest-regressions +9 -0
- package/data/probe/tests/property_tests.rs +118 -0
- package/data/probe/tests/python_outline_format_tests.rs +1538 -0
- package/data/probe/tests/query_command_json_tests.rs +438 -0
- package/data/probe/tests/query_command_tests.rs +232 -0
- package/data/probe/tests/query_command_xml_tests.rs +569 -0
- package/data/probe/tests/quoted_term_with_negative_keyword_tests.rs +216 -0
- package/data/probe/tests/required_terms_filename_tests.rs +116 -0
- package/data/probe/tests/ruby_outline_format_tests.rs +1011 -0
- package/data/probe/tests/rust_line_comment_context_test.rs +151 -0
- package/data/probe/tests/rust_outline_format_enhanced_tests.rs +725 -0
- package/data/probe/tests/rust_outline_format_tests.rs +843 -0
- package/data/probe/tests/schemas/xml_output_schema.xsd +38 -0
- package/data/probe/tests/search_determinism_tests.rs +451 -0
- package/data/probe/tests/search_hints_tests.rs +253 -0
- package/data/probe/tests/special_character_escaping_tests.rs +417 -0
- package/data/probe/tests/stemming_compound_word_filtering_tests.rs +535 -0
- package/data/probe/tests/strict_elastic_syntax_tests.rs +404 -0
- package/data/probe/tests/swift_outline_format_tests.rs +3319 -0
- package/data/probe/tests/symbols_tests.rs +166 -0
- package/data/probe/tests/test_file.rs +45 -0
- package/data/probe/tests/test_tokenize.rs +28 -0
- package/data/probe/tests/timeout_tests.rs +82 -0
- package/data/probe/tests/tokenization_tests.rs +195 -0
- package/data/probe/tests/tokenized_block_filtering_tests.rs +174 -0
- package/data/probe/tests/typescript_extract_tests.rs +214 -0
- package/data/probe/tests/typescript_outline_format_tests.rs +2188 -0
- package/data/probe/tests/xml_format_tests.rs +568 -0
- package/data/probe/tests/xml_schema_validation_tests.rs +497 -0
- package/data/scripts/postinstall.mjs +9 -0
- package/data/scripts/set-version.js +0 -0
- package/data/scripts/wiki-build.sh +111 -0
- package/data/scripts/wiki-deploy.sh +73 -0
- package/data/serve.json +12 -0
- package/data/test/demo-dynamic.html +134 -0
- package/data/test/demo-esm.html +105 -0
- package/data/test/demo-iife.html +78 -0
- package/data/tsconfig.json +7 -0
- package/data/vite.server.ts +483 -0
- package/data/vitest.config.ts +40 -0
- package/data/wiki/Home.md +58 -0
- package/data/wiki/_Sidebar.md +39 -0
- package/docs-mcp.config.json +20 -0
- package/package.json +56 -0
- package/src/config.js +111 -0
- package/src/index.js +395 -0
|
@@ -0,0 +1,1848 @@
|
|
|
1
|
+
use std::fs;
|
|
2
|
+
use std::path::PathBuf;
|
|
3
|
+
use std::process::Command;
|
|
4
|
+
|
|
5
|
+
// Import the necessary functions from the extract module
|
|
6
|
+
use probe_code::extract::{
|
|
7
|
+
extract_file_paths_from_git_diff, format_and_print_extraction_results, is_git_diff_format,
|
|
8
|
+
process_file_for_extraction,
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
#[test]
|
|
12
|
+
fn test_process_file_for_extraction_full_file() {
|
|
13
|
+
// Create a temporary file for testing
|
|
14
|
+
let temp_dir = tempfile::tempdir().unwrap();
|
|
15
|
+
let file_path = temp_dir.path().join("test_file.txt");
|
|
16
|
+
let content = "Line 1\nLine 2\nLine 3\n";
|
|
17
|
+
fs::write(&file_path, content).unwrap();
|
|
18
|
+
|
|
19
|
+
// Test processing the full file
|
|
20
|
+
let result =
|
|
21
|
+
process_file_for_extraction(&file_path, None, None, None, false, 0, None, false).unwrap();
|
|
22
|
+
|
|
23
|
+
assert_eq!(result.file, file_path.to_string_lossy().to_string());
|
|
24
|
+
assert_eq!(result.lines, (1, 3)); // 3 lines in the content
|
|
25
|
+
assert_eq!(result.node_type, "file");
|
|
26
|
+
assert_eq!(result.code, content);
|
|
27
|
+
|
|
28
|
+
// Test with non-existent file
|
|
29
|
+
let non_existent = temp_dir.path().join("non_existent.txt");
|
|
30
|
+
let err = process_file_for_extraction(&non_existent, None, None, None, false, 0, None, false)
|
|
31
|
+
.unwrap_err();
|
|
32
|
+
assert!(err.to_string().contains("does not exist"));
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
#[test]
|
|
36
|
+
fn test_process_file_for_extraction_with_line() {
|
|
37
|
+
// Create a temporary file with Rust code for testing
|
|
38
|
+
let temp_dir = tempfile::tempdir().unwrap();
|
|
39
|
+
let file_path = temp_dir.path().join("test_file.rs");
|
|
40
|
+
let content = r#"
|
|
41
|
+
fn main() {
|
|
42
|
+
println!("Hello, world!");
|
|
43
|
+
|
|
44
|
+
let x = 42;
|
|
45
|
+
if x > 0 {
|
|
46
|
+
println!("Positive");
|
|
47
|
+
} else {
|
|
48
|
+
println!("Non-positive");
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
struct Point {
|
|
53
|
+
x: i32,
|
|
54
|
+
y: i32,
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
impl Point {
|
|
58
|
+
fn new(x: i32, y: i32) -> Self {
|
|
59
|
+
Self { x, y }
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
"#;
|
|
63
|
+
fs::write(&file_path, content).unwrap();
|
|
64
|
+
|
|
65
|
+
// Test extracting a function
|
|
66
|
+
let result =
|
|
67
|
+
process_file_for_extraction(&file_path, Some(3), None, None, false, 0, None, false)
|
|
68
|
+
.unwrap();
|
|
69
|
+
assert_eq!(result.file, file_path.to_string_lossy().to_string());
|
|
70
|
+
assert!(result.lines.0 <= 3 && result.lines.1 >= 3);
|
|
71
|
+
assert!(result.code.contains("fn main()"));
|
|
72
|
+
assert!(result.code.contains("Hello, world!"));
|
|
73
|
+
|
|
74
|
+
// Test extracting a struct
|
|
75
|
+
let result =
|
|
76
|
+
process_file_for_extraction(&file_path, Some(13), None, None, false, 0, None, false)
|
|
77
|
+
.unwrap();
|
|
78
|
+
assert_eq!(result.file, file_path.to_string_lossy().to_string());
|
|
79
|
+
assert!(result.lines.0 <= 13 && result.lines.1 >= 13);
|
|
80
|
+
assert!(result.code.contains("struct Point"));
|
|
81
|
+
assert!(result.code.contains("x: i32"));
|
|
82
|
+
assert!(result.code.contains("y: i32"));
|
|
83
|
+
|
|
84
|
+
// Test with out-of-bounds line number (should be clamped to valid range)
|
|
85
|
+
let result =
|
|
86
|
+
process_file_for_extraction(&file_path, Some(1000), None, None, false, 0, None, false)
|
|
87
|
+
.unwrap();
|
|
88
|
+
// The line number should be clamped to the maximum valid line
|
|
89
|
+
// Don't check for exact equality, just make sure it's within valid range
|
|
90
|
+
assert!(result.lines.0 <= result.lines.1);
|
|
91
|
+
assert!(result.lines.1 <= content.lines().count());
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
#[test]
|
|
95
|
+
fn test_process_file_for_extraction_fallback() {
|
|
96
|
+
// Create a temporary file with a non-supported extension
|
|
97
|
+
let temp_dir = tempfile::tempdir().unwrap();
|
|
98
|
+
let file_path = temp_dir.path().join("test_file.xyz");
|
|
99
|
+
let mut content = String::from(
|
|
100
|
+
"Line 1\nLine 2\nLine 3\nLine 4\nLine 5\nLine 6\nLine 7\nLine 8\nLine 9\nLine 10\n",
|
|
101
|
+
);
|
|
102
|
+
content.push_str("Line 11\nLine 12\nLine 13\nLine 14\nLine 15\nLine 16\nLine 17\nLine 18\nLine 19\nLine 20\n");
|
|
103
|
+
content.push_str("Line 21\nLine 22\nLine 23\nLine 24\nLine 25\n");
|
|
104
|
+
fs::write(&file_path, content).unwrap();
|
|
105
|
+
|
|
106
|
+
// Test fallback to line-based context with default context lines (10)
|
|
107
|
+
let result =
|
|
108
|
+
process_file_for_extraction(&file_path, Some(15), None, None, false, 10, None, false)
|
|
109
|
+
.unwrap();
|
|
110
|
+
assert_eq!(result.file, file_path.to_string_lossy().to_string());
|
|
111
|
+
assert_eq!(result.node_type, "context");
|
|
112
|
+
|
|
113
|
+
// Should include 10 lines before and after line 15
|
|
114
|
+
let start_line = result.lines.0;
|
|
115
|
+
let end_line = result.lines.1;
|
|
116
|
+
|
|
117
|
+
// Check that the range includes line 15 and has appropriate context
|
|
118
|
+
assert!(start_line <= 15 && end_line >= 15);
|
|
119
|
+
assert!(end_line - start_line >= 10); // At least 10 lines of context
|
|
120
|
+
|
|
121
|
+
// Test with a line at the beginning of the file
|
|
122
|
+
let result =
|
|
123
|
+
process_file_for_extraction(&file_path, Some(2), None, None, false, 10, None, false)
|
|
124
|
+
.unwrap();
|
|
125
|
+
assert!(result.lines.0 <= 2); // Should start at or before line 2
|
|
126
|
+
assert!(result.lines.1 >= 2); // Should include line 2
|
|
127
|
+
|
|
128
|
+
// Test with a line at the end of the file
|
|
129
|
+
let result =
|
|
130
|
+
process_file_for_extraction(&file_path, Some(25), None, None, false, 10, None, false)
|
|
131
|
+
.unwrap();
|
|
132
|
+
assert!(result.lines.0 <= 25); // Should include some lines before line 25
|
|
133
|
+
assert_eq!(result.lines.1, 25); // Can't go beyond the last line
|
|
134
|
+
|
|
135
|
+
// Test with custom context lines
|
|
136
|
+
let result =
|
|
137
|
+
process_file_for_extraction(&file_path, Some(15), None, None, false, 5, None, false)
|
|
138
|
+
.unwrap();
|
|
139
|
+
assert_eq!(result.file, file_path.to_string_lossy().to_string());
|
|
140
|
+
assert_eq!(result.node_type, "context");
|
|
141
|
+
|
|
142
|
+
// Should include 5 lines before and after line 15
|
|
143
|
+
let start_line = result.lines.0;
|
|
144
|
+
let end_line = result.lines.1;
|
|
145
|
+
|
|
146
|
+
// Check that the range includes line 15 and has appropriate context
|
|
147
|
+
assert!(start_line <= 15 && end_line >= 15);
|
|
148
|
+
assert!(end_line - start_line >= 5); // At least 5 lines of context
|
|
149
|
+
assert!(end_line - start_line <= 11); // At most 11 lines (5 before, 5 after, plus the line itself)
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
#[test]
|
|
153
|
+
fn test_format_and_print_extraction_results() {
|
|
154
|
+
// Create a simple search result
|
|
155
|
+
let result = probe_code::models::SearchResult {
|
|
156
|
+
file: "test_file.rs".to_string(),
|
|
157
|
+
lines: (1, 5),
|
|
158
|
+
node_type: "function".to_string(),
|
|
159
|
+
code: "fn test() {\n println!(\"Hello\");\n}".to_string(),
|
|
160
|
+
symbol_signature: None,
|
|
161
|
+
matched_by_filename: None,
|
|
162
|
+
rank: None,
|
|
163
|
+
score: None,
|
|
164
|
+
tfidf_score: None,
|
|
165
|
+
bm25_score: None,
|
|
166
|
+
tfidf_rank: None,
|
|
167
|
+
bm25_rank: None,
|
|
168
|
+
new_score: None,
|
|
169
|
+
hybrid2_rank: None,
|
|
170
|
+
combined_score_rank: None,
|
|
171
|
+
file_unique_terms: None,
|
|
172
|
+
file_total_matches: None,
|
|
173
|
+
file_match_rank: None,
|
|
174
|
+
block_unique_terms: None,
|
|
175
|
+
block_total_matches: None,
|
|
176
|
+
parent_file_id: None,
|
|
177
|
+
block_id: None,
|
|
178
|
+
matched_keywords: None,
|
|
179
|
+
tokenized_content: None,
|
|
180
|
+
parent_context: None,
|
|
181
|
+
matched_lines: None,
|
|
182
|
+
};
|
|
183
|
+
|
|
184
|
+
// Test different formats
|
|
185
|
+
let results = vec![result];
|
|
186
|
+
|
|
187
|
+
// We can't easily test the output directly, but we can at least ensure the function doesn't panic
|
|
188
|
+
format_and_print_extraction_results(&results, "terminal", None, None, None, false).unwrap();
|
|
189
|
+
format_and_print_extraction_results(&results, "markdown", None, None, None, false).unwrap();
|
|
190
|
+
format_and_print_extraction_results(&results, "plain", None, None, None, false).unwrap();
|
|
191
|
+
format_and_print_extraction_results(&results, "json", None, None, None, false).unwrap();
|
|
192
|
+
format_and_print_extraction_results(&results, "xml", None, None, None, false).unwrap();
|
|
193
|
+
|
|
194
|
+
// Test with system prompt and user instructions
|
|
195
|
+
format_and_print_extraction_results(
|
|
196
|
+
&results,
|
|
197
|
+
"terminal",
|
|
198
|
+
None,
|
|
199
|
+
Some("Test system prompt"),
|
|
200
|
+
Some("Test user instructions"),
|
|
201
|
+
false,
|
|
202
|
+
)
|
|
203
|
+
.unwrap();
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
#[test]
|
|
207
|
+
fn test_json_format_extraction_results() {
|
|
208
|
+
use serde_json::Value;
|
|
209
|
+
use std::process::Command;
|
|
210
|
+
use tempfile::TempDir;
|
|
211
|
+
|
|
212
|
+
// Create a temporary file for testing
|
|
213
|
+
let temp_dir = TempDir::new().expect("Failed to create temp dir");
|
|
214
|
+
let file_path = temp_dir.path().join("test_file.rs");
|
|
215
|
+
let content = r#"
|
|
216
|
+
fn test() {
|
|
217
|
+
println!("Hello");
|
|
218
|
+
}
|
|
219
|
+
"#;
|
|
220
|
+
fs::write(&file_path, content).unwrap();
|
|
221
|
+
|
|
222
|
+
// Print the file path for debugging
|
|
223
|
+
println!("File path: {}", file_path.display());
|
|
224
|
+
|
|
225
|
+
// Get the project root directory (where Cargo.toml is)
|
|
226
|
+
let project_dir = std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR"));
|
|
227
|
+
|
|
228
|
+
// Run the extract command with JSON format
|
|
229
|
+
let output = Command::new("cargo")
|
|
230
|
+
.args([
|
|
231
|
+
"run",
|
|
232
|
+
"--manifest-path",
|
|
233
|
+
project_dir.join("Cargo.toml").to_string_lossy().as_ref(),
|
|
234
|
+
"--",
|
|
235
|
+
"extract",
|
|
236
|
+
file_path.to_string_lossy().as_ref(),
|
|
237
|
+
"--format",
|
|
238
|
+
"json",
|
|
239
|
+
"--allow-tests", // Add this flag to ensure test files are included
|
|
240
|
+
"--prompt",
|
|
241
|
+
"engineer",
|
|
242
|
+
"--instructions",
|
|
243
|
+
"Extract the main function",
|
|
244
|
+
])
|
|
245
|
+
.output()
|
|
246
|
+
.expect("Failed to execute command");
|
|
247
|
+
// Print the command output for debugging
|
|
248
|
+
println!(
|
|
249
|
+
"Command stdout: {}",
|
|
250
|
+
String::from_utf8_lossy(&output.stdout)
|
|
251
|
+
);
|
|
252
|
+
println!(
|
|
253
|
+
"Command stderr: {}",
|
|
254
|
+
String::from_utf8_lossy(&output.stderr)
|
|
255
|
+
);
|
|
256
|
+
|
|
257
|
+
// Check that the command executed successfully
|
|
258
|
+
assert!(
|
|
259
|
+
output.status.success(),
|
|
260
|
+
"Command failed with status: {}",
|
|
261
|
+
output.status
|
|
262
|
+
);
|
|
263
|
+
assert!(output.status.success());
|
|
264
|
+
|
|
265
|
+
// Get the output as a string
|
|
266
|
+
let stdout = String::from_utf8_lossy(&output.stdout);
|
|
267
|
+
|
|
268
|
+
// Helper function to extract JSON from command output
|
|
269
|
+
fn extract_json_from_output(output: &str) -> &str {
|
|
270
|
+
// Find the first occurrence of '{'
|
|
271
|
+
if let Some(start_index) = output.find('{') {
|
|
272
|
+
// Return the substring from the first '{' to the end
|
|
273
|
+
&output[start_index..]
|
|
274
|
+
} else {
|
|
275
|
+
// If no '{' is found, return the original string
|
|
276
|
+
output
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
// Print the output for debugging
|
|
281
|
+
println!("Command stdout: {stdout}");
|
|
282
|
+
|
|
283
|
+
// Extract and parse the JSON
|
|
284
|
+
let json_str = extract_json_from_output(&stdout);
|
|
285
|
+
println!("Extracted JSON: {json_str}");
|
|
286
|
+
let json_value: Value = serde_json::from_str(json_str).expect("Failed to parse JSON output");
|
|
287
|
+
|
|
288
|
+
// Validate the structure of the JSON output
|
|
289
|
+
assert!(json_value.is_object(), "JSON output should be an object");
|
|
290
|
+
assert!(
|
|
291
|
+
json_value.get("results").is_some(),
|
|
292
|
+
"JSON output should have a 'results' field"
|
|
293
|
+
);
|
|
294
|
+
assert!(
|
|
295
|
+
json_value.get("summary").is_some(),
|
|
296
|
+
"JSON output should have a 'summary' field"
|
|
297
|
+
);
|
|
298
|
+
|
|
299
|
+
// Validate the results array
|
|
300
|
+
let results_array = json_value.get("results").unwrap().as_array().unwrap();
|
|
301
|
+
assert_eq!(
|
|
302
|
+
results_array.len(),
|
|
303
|
+
1,
|
|
304
|
+
"Results array should have 1 element"
|
|
305
|
+
);
|
|
306
|
+
|
|
307
|
+
// Validate the structure of the result
|
|
308
|
+
let result_obj = &results_array[0];
|
|
309
|
+
assert!(
|
|
310
|
+
result_obj.get("file").is_some(),
|
|
311
|
+
"Result should have a 'file' field"
|
|
312
|
+
);
|
|
313
|
+
assert!(
|
|
314
|
+
result_obj.get("lines").is_some(),
|
|
315
|
+
"Result should have a 'lines' field"
|
|
316
|
+
);
|
|
317
|
+
assert!(
|
|
318
|
+
result_obj.get("node_type").is_some(),
|
|
319
|
+
"Result should have a 'node_type' field"
|
|
320
|
+
);
|
|
321
|
+
assert!(
|
|
322
|
+
result_obj.get("code").is_some(),
|
|
323
|
+
"Result should have a 'code' field"
|
|
324
|
+
);
|
|
325
|
+
|
|
326
|
+
// Validate the values
|
|
327
|
+
assert!(result_obj
|
|
328
|
+
.get("file")
|
|
329
|
+
.unwrap()
|
|
330
|
+
.as_str()
|
|
331
|
+
.unwrap()
|
|
332
|
+
.contains("test_file.rs"));
|
|
333
|
+
assert!(
|
|
334
|
+
result_obj.get("node_type").is_some(),
|
|
335
|
+
"Result should have a node_type field"
|
|
336
|
+
);
|
|
337
|
+
assert!(
|
|
338
|
+
result_obj
|
|
339
|
+
.get("code")
|
|
340
|
+
.unwrap()
|
|
341
|
+
.as_str()
|
|
342
|
+
.unwrap()
|
|
343
|
+
.contains("fn test()")
|
|
344
|
+
&& result_obj
|
|
345
|
+
.get("code")
|
|
346
|
+
.unwrap()
|
|
347
|
+
.as_str()
|
|
348
|
+
.unwrap()
|
|
349
|
+
.contains("println!(\"Hello\")")
|
|
350
|
+
);
|
|
351
|
+
|
|
352
|
+
// Validate the lines field is an array with two elements
|
|
353
|
+
let lines = result_obj.get("lines").unwrap().as_array().unwrap();
|
|
354
|
+
assert_eq!(lines.len(), 2, "Lines should be an array with 2 elements");
|
|
355
|
+
assert!(
|
|
356
|
+
lines[0].as_u64().unwrap() >= 1,
|
|
357
|
+
"Start line should be at least 1"
|
|
358
|
+
);
|
|
359
|
+
assert!(
|
|
360
|
+
lines[1].as_u64().unwrap() >= lines[0].as_u64().unwrap(),
|
|
361
|
+
"End line should be at least start line"
|
|
362
|
+
);
|
|
363
|
+
|
|
364
|
+
// Validate the summary object
|
|
365
|
+
let summary = json_value.get("summary").unwrap();
|
|
366
|
+
assert!(summary.is_object(), "Summary should be an object");
|
|
367
|
+
assert!(
|
|
368
|
+
summary.get("count").is_some(),
|
|
369
|
+
"Summary should have a 'count' field"
|
|
370
|
+
);
|
|
371
|
+
assert!(
|
|
372
|
+
summary.get("total_bytes").is_some(),
|
|
373
|
+
"Summary should have a 'total_bytes' field"
|
|
374
|
+
);
|
|
375
|
+
assert!(
|
|
376
|
+
summary.get("total_tokens").is_some(),
|
|
377
|
+
"Summary should have a 'total_tokens' field"
|
|
378
|
+
);
|
|
379
|
+
|
|
380
|
+
// Validate the count matches the number of results
|
|
381
|
+
assert_eq!(summary.get("count").unwrap().as_u64().unwrap(), 1);
|
|
382
|
+
|
|
383
|
+
// Validate system_prompt and user_instructions are present
|
|
384
|
+
assert!(
|
|
385
|
+
json_value.get("system_prompt").is_some(),
|
|
386
|
+
"JSON output should have a 'system_prompt' field"
|
|
387
|
+
);
|
|
388
|
+
assert!(
|
|
389
|
+
json_value.get("user_instructions").is_some(),
|
|
390
|
+
"JSON output should have a 'user_instructions' field"
|
|
391
|
+
);
|
|
392
|
+
|
|
393
|
+
// Validate the content of system_prompt and user_instructions
|
|
394
|
+
assert!(
|
|
395
|
+
json_value
|
|
396
|
+
.get("user_instructions")
|
|
397
|
+
.unwrap()
|
|
398
|
+
.as_str()
|
|
399
|
+
.unwrap()
|
|
400
|
+
== "Extract the main function",
|
|
401
|
+
"user_instructions should match the provided value"
|
|
402
|
+
);
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
#[test]
|
|
406
|
+
fn test_xml_format_extraction_results() {
|
|
407
|
+
use roxmltree::{Document, Node};
|
|
408
|
+
use std::process::Command;
|
|
409
|
+
use tempfile::TempDir;
|
|
410
|
+
|
|
411
|
+
// Create a temporary file for testing
|
|
412
|
+
let temp_dir = TempDir::new().expect("Failed to create temp dir");
|
|
413
|
+
let file_path = temp_dir.path().join("test_file.rs");
|
|
414
|
+
let content = r#"
|
|
415
|
+
fn test() {
|
|
416
|
+
println!("Hello");
|
|
417
|
+
}
|
|
418
|
+
"#;
|
|
419
|
+
fs::write(&file_path, content).unwrap();
|
|
420
|
+
|
|
421
|
+
// Print the file path for debugging
|
|
422
|
+
println!("File path: {}", file_path.display());
|
|
423
|
+
|
|
424
|
+
// Get the project root directory (where Cargo.toml is)
|
|
425
|
+
let project_dir = std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR"));
|
|
426
|
+
|
|
427
|
+
// Run the extract command with XML format
|
|
428
|
+
let output = Command::new("cargo")
|
|
429
|
+
.args([
|
|
430
|
+
"run",
|
|
431
|
+
"--manifest-path",
|
|
432
|
+
project_dir.join("Cargo.toml").to_string_lossy().as_ref(),
|
|
433
|
+
"--",
|
|
434
|
+
"extract",
|
|
435
|
+
file_path.to_string_lossy().as_ref(),
|
|
436
|
+
"--format",
|
|
437
|
+
"xml",
|
|
438
|
+
"--allow-tests", // Add this flag to ensure test files are included
|
|
439
|
+
"--prompt",
|
|
440
|
+
"engineer",
|
|
441
|
+
"--instructions",
|
|
442
|
+
"Extract the main function",
|
|
443
|
+
])
|
|
444
|
+
.output()
|
|
445
|
+
.expect("Failed to execute command");
|
|
446
|
+
|
|
447
|
+
// Print the command output for debugging
|
|
448
|
+
println!(
|
|
449
|
+
"Command stdout: {}",
|
|
450
|
+
String::from_utf8_lossy(&output.stdout)
|
|
451
|
+
);
|
|
452
|
+
println!(
|
|
453
|
+
"Command stderr: {}",
|
|
454
|
+
String::from_utf8_lossy(&output.stderr)
|
|
455
|
+
);
|
|
456
|
+
|
|
457
|
+
// Check that the command executed successfully
|
|
458
|
+
assert!(
|
|
459
|
+
output.status.success(),
|
|
460
|
+
"Command failed with status: {}",
|
|
461
|
+
output.status
|
|
462
|
+
);
|
|
463
|
+
|
|
464
|
+
// Get the output as a string
|
|
465
|
+
let stdout = String::from_utf8_lossy(&output.stdout);
|
|
466
|
+
|
|
467
|
+
// Helper function to extract XML from command output
|
|
468
|
+
fn extract_xml_from_output(output: &str) -> &str {
|
|
469
|
+
// Find the first occurrence of '<?xml'
|
|
470
|
+
if let Some(start_index) = output.find("<?xml") {
|
|
471
|
+
// Return the substring from the first '<?xml' to the end
|
|
472
|
+
&output[start_index..]
|
|
473
|
+
} else {
|
|
474
|
+
// If no '<?xml' is found, return the original string
|
|
475
|
+
output
|
|
476
|
+
}
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
// Print the output for debugging
|
|
480
|
+
println!("Command stdout: {stdout}");
|
|
481
|
+
|
|
482
|
+
// Extract and parse the XML
|
|
483
|
+
let xml_str = extract_xml_from_output(&stdout);
|
|
484
|
+
println!("Extracted XML: {xml_str}");
|
|
485
|
+
let doc = Document::parse(xml_str).expect("Failed to parse XML output");
|
|
486
|
+
let root = doc.root_element();
|
|
487
|
+
|
|
488
|
+
// Validate the structure of the XML output
|
|
489
|
+
assert_eq!(
|
|
490
|
+
root.tag_name().name(),
|
|
491
|
+
"probe_results",
|
|
492
|
+
"Root element should be 'probe_results'"
|
|
493
|
+
);
|
|
494
|
+
|
|
495
|
+
// Validate that there are result elements
|
|
496
|
+
let results_nodes: Vec<Node> = root
|
|
497
|
+
.children()
|
|
498
|
+
.filter(|n| n.is_element() && n.tag_name().name() == "result")
|
|
499
|
+
.collect();
|
|
500
|
+
assert_eq!(
|
|
501
|
+
results_nodes.len(),
|
|
502
|
+
1,
|
|
503
|
+
"Should have exactly one result element"
|
|
504
|
+
);
|
|
505
|
+
|
|
506
|
+
// Validate the structure of the result
|
|
507
|
+
let result_node = &results_nodes[0];
|
|
508
|
+
|
|
509
|
+
// Check for required elements
|
|
510
|
+
let file = result_node
|
|
511
|
+
.children()
|
|
512
|
+
.find(|n| n.is_element() && n.tag_name().name() == "file");
|
|
513
|
+
assert!(file.is_some(), "Result should have a file element");
|
|
514
|
+
assert!(
|
|
515
|
+
file.unwrap().text().unwrap().contains("test_file.rs"),
|
|
516
|
+
"File element should contain the correct file path"
|
|
517
|
+
);
|
|
518
|
+
|
|
519
|
+
// For a full file extraction, the lines element might not be present
|
|
520
|
+
let node_type = result_node
|
|
521
|
+
.children()
|
|
522
|
+
.find(|n| n.is_element() && n.tag_name().name() == "node_type");
|
|
523
|
+
if let Some(node_type) = node_type {
|
|
524
|
+
if node_type.text().unwrap() != "file" {
|
|
525
|
+
let lines = result_node
|
|
526
|
+
.children()
|
|
527
|
+
.find(|n| n.is_element() && n.tag_name().name() == "lines");
|
|
528
|
+
assert!(lines.is_some(), "Result should have a lines element");
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
// Validate system_prompt and user_instructions are present
|
|
532
|
+
let system_prompt = root
|
|
533
|
+
.children()
|
|
534
|
+
.find(|n| n.is_element() && n.tag_name().name() == "system_prompt");
|
|
535
|
+
assert!(
|
|
536
|
+
system_prompt.is_some(),
|
|
537
|
+
"Should have a system_prompt element"
|
|
538
|
+
);
|
|
539
|
+
|
|
540
|
+
let user_instructions = root
|
|
541
|
+
.children()
|
|
542
|
+
.find(|n| n.is_element() && n.tag_name().name() == "user_instructions");
|
|
543
|
+
assert!(
|
|
544
|
+
user_instructions.is_some(),
|
|
545
|
+
"Should have a user_instructions element"
|
|
546
|
+
);
|
|
547
|
+
|
|
548
|
+
// Validate the content of user_instructions
|
|
549
|
+
assert_eq!(
|
|
550
|
+
user_instructions.unwrap().text().unwrap(),
|
|
551
|
+
"Extract the main function",
|
|
552
|
+
"user_instructions should match the provided value"
|
|
553
|
+
);
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
// The node_type element might not be present for all extraction types
|
|
557
|
+
let _node_type = result_node
|
|
558
|
+
.children()
|
|
559
|
+
.find(|n| n.is_element() && n.tag_name().name() == "node_type");
|
|
560
|
+
|
|
561
|
+
let code = result_node
|
|
562
|
+
.children()
|
|
563
|
+
.find(|n| n.is_element() && n.tag_name().name() == "code");
|
|
564
|
+
assert!(code.is_some(), "Result should have a code element");
|
|
565
|
+
assert!(
|
|
566
|
+
code.unwrap().text().unwrap().contains("fn test()")
|
|
567
|
+
&& code
|
|
568
|
+
.unwrap()
|
|
569
|
+
.text()
|
|
570
|
+
.unwrap()
|
|
571
|
+
.contains("println!(\"Hello\")"),
|
|
572
|
+
"Code element should contain the correct code"
|
|
573
|
+
);
|
|
574
|
+
|
|
575
|
+
// Validate the summary element
|
|
576
|
+
let summary = root
|
|
577
|
+
.children()
|
|
578
|
+
.find(|n| n.is_element() && n.tag_name().name() == "summary");
|
|
579
|
+
assert!(summary.is_some(), "Should have a summary element");
|
|
580
|
+
|
|
581
|
+
if let Some(summary) = summary {
|
|
582
|
+
// Validate the summary contains count, total_bytes, and total_tokens
|
|
583
|
+
let count = summary
|
|
584
|
+
.children()
|
|
585
|
+
.find(|n| n.is_element() && n.tag_name().name() == "count");
|
|
586
|
+
assert!(count.is_some(), "Summary should have a count element");
|
|
587
|
+
assert_eq!(count.unwrap().text().unwrap(), "1", "Count should be 1");
|
|
588
|
+
|
|
589
|
+
let total_bytes = summary
|
|
590
|
+
.children()
|
|
591
|
+
.find(|n| n.is_element() && n.tag_name().name() == "total_bytes");
|
|
592
|
+
assert!(
|
|
593
|
+
total_bytes.is_some(),
|
|
594
|
+
"Summary should have a total_bytes element"
|
|
595
|
+
);
|
|
596
|
+
|
|
597
|
+
let total_tokens = summary
|
|
598
|
+
.children()
|
|
599
|
+
.find(|n| n.is_element() && n.tag_name().name() == "total_tokens");
|
|
600
|
+
assert!(
|
|
601
|
+
total_tokens.is_some(),
|
|
602
|
+
"Summary should have a total_tokens element"
|
|
603
|
+
);
|
|
604
|
+
}
|
|
605
|
+
}
|
|
606
|
+
|
|
607
|
+
#[test]
|
|
608
|
+
fn test_process_file_for_extraction_with_range() {
|
|
609
|
+
// Create a temporary file for testing
|
|
610
|
+
let temp_dir = tempfile::tempdir().unwrap();
|
|
611
|
+
let file_path = temp_dir.path().join("test_file.txt");
|
|
612
|
+
let mut content = String::new();
|
|
613
|
+
for i in 1..=20 {
|
|
614
|
+
content.push_str(&format!("Line {i}\n"));
|
|
615
|
+
}
|
|
616
|
+
fs::write(&file_path, &content).unwrap();
|
|
617
|
+
|
|
618
|
+
// Test extracting a range of lines
|
|
619
|
+
let result =
|
|
620
|
+
process_file_for_extraction(&file_path, Some(1), Some(10), None, false, 0, None, false)
|
|
621
|
+
.unwrap();
|
|
622
|
+
assert_eq!(result.file, file_path.to_string_lossy().to_string());
|
|
623
|
+
assert_eq!(result.lines, (1, 10));
|
|
624
|
+
assert_eq!(result.node_type, "range");
|
|
625
|
+
|
|
626
|
+
// Check that the extracted content contains exactly lines 1-10
|
|
627
|
+
let expected_content = content.lines().take(10).collect::<Vec<_>>().join("\n");
|
|
628
|
+
assert_eq!(result.code, expected_content);
|
|
629
|
+
|
|
630
|
+
// Test with a different range
|
|
631
|
+
let result =
|
|
632
|
+
process_file_for_extraction(&file_path, Some(5), Some(15), None, false, 0, None, false)
|
|
633
|
+
.unwrap();
|
|
634
|
+
assert_eq!(result.lines, (5, 15));
|
|
635
|
+
|
|
636
|
+
// Check that the extracted content contains exactly lines 5-15
|
|
637
|
+
let expected_content = content
|
|
638
|
+
.lines()
|
|
639
|
+
.skip(4)
|
|
640
|
+
.take(11)
|
|
641
|
+
.collect::<Vec<_>>()
|
|
642
|
+
.join("\n");
|
|
643
|
+
assert_eq!(result.code, expected_content);
|
|
644
|
+
|
|
645
|
+
// Test with invalid range (start > end) - should be clamped to valid range
|
|
646
|
+
let result =
|
|
647
|
+
process_file_for_extraction(&file_path, Some(10), Some(5), None, false, 0, None, false)
|
|
648
|
+
.unwrap();
|
|
649
|
+
// The start and end lines should be clamped to valid values
|
|
650
|
+
assert!(result.lines.0 <= result.lines.1);
|
|
651
|
+
assert!(result.lines.1 <= content.lines().count());
|
|
652
|
+
|
|
653
|
+
// Test with out-of-bounds range (should be clamped to valid range)
|
|
654
|
+
let result =
|
|
655
|
+
process_file_for_extraction(&file_path, Some(15), Some(25), None, false, 0, None, false)
|
|
656
|
+
.unwrap();
|
|
657
|
+
// The end line should be clamped to the maximum valid line
|
|
658
|
+
assert!(result.lines.0 <= 15);
|
|
659
|
+
assert!(result.lines.1 <= content.lines().count());
|
|
660
|
+
// The node_type should be "range"
|
|
661
|
+
assert_eq!(result.node_type, "range");
|
|
662
|
+
}
|
|
663
|
+
|
|
664
|
+
#[test]
|
|
665
|
+
fn test_integration_extract_command() {
|
|
666
|
+
// Create a temporary file for testing
|
|
667
|
+
let temp_dir = tempfile::tempdir().unwrap();
|
|
668
|
+
let file_path = temp_dir.path().join("test_file.rs");
|
|
669
|
+
let content = r#"
|
|
670
|
+
fn main() {
|
|
671
|
+
println!("Hello, world!");
|
|
672
|
+
}
|
|
673
|
+
|
|
674
|
+
struct Point {
|
|
675
|
+
x: i32,
|
|
676
|
+
y: i32,
|
|
677
|
+
}
|
|
678
|
+
"#;
|
|
679
|
+
fs::write(&file_path, content).unwrap();
|
|
680
|
+
|
|
681
|
+
// Print the current directory and file path for debugging
|
|
682
|
+
println!("Current directory: {:?}", std::env::current_dir().unwrap());
|
|
683
|
+
println!("File path: {file_path:?}");
|
|
684
|
+
|
|
685
|
+
// Get the project root directory (where Cargo.toml is)
|
|
686
|
+
let project_dir = std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR"));
|
|
687
|
+
println!("Project directory: {project_dir:?}");
|
|
688
|
+
|
|
689
|
+
// Run the extract command using cargo run from the project directory
|
|
690
|
+
let output = Command::new("cargo")
|
|
691
|
+
.args([
|
|
692
|
+
"run",
|
|
693
|
+
"--manifest-path",
|
|
694
|
+
project_dir.join("Cargo.toml").to_string_lossy().as_ref(),
|
|
695
|
+
"--",
|
|
696
|
+
"extract",
|
|
697
|
+
file_path.to_string_lossy().as_ref(),
|
|
698
|
+
"--allow-tests", // Add this flag to ensure test files are included
|
|
699
|
+
])
|
|
700
|
+
.current_dir(&project_dir) // Ensure we're in the project directory
|
|
701
|
+
.output()
|
|
702
|
+
.expect("Failed to execute command");
|
|
703
|
+
|
|
704
|
+
// Print the command output for debugging
|
|
705
|
+
println!(
|
|
706
|
+
"Command stdout: {}",
|
|
707
|
+
String::from_utf8_lossy(&output.stdout)
|
|
708
|
+
);
|
|
709
|
+
println!(
|
|
710
|
+
"Command stderr: {}",
|
|
711
|
+
String::from_utf8_lossy(&output.stderr)
|
|
712
|
+
);
|
|
713
|
+
|
|
714
|
+
// Check that the command executed successfully
|
|
715
|
+
assert!(output.status.success());
|
|
716
|
+
|
|
717
|
+
// Check that the output contains the file content
|
|
718
|
+
let stdout = String::from_utf8_lossy(&output.stdout);
|
|
719
|
+
assert!(stdout.contains("File:"));
|
|
720
|
+
assert!(stdout.contains("fn main()"));
|
|
721
|
+
// Note: We don't check for "struct Point" here anymore since the extract command
|
|
722
|
+
// might only extract the function and not the struct depending on the AST parsing
|
|
723
|
+
|
|
724
|
+
// Get the project root directory (where Cargo.toml is)
|
|
725
|
+
let project_dir = std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR"));
|
|
726
|
+
|
|
727
|
+
// Run with a line number
|
|
728
|
+
let output = Command::new("cargo")
|
|
729
|
+
.args([
|
|
730
|
+
"run",
|
|
731
|
+
"--manifest-path",
|
|
732
|
+
project_dir.join("Cargo.toml").to_string_lossy().as_ref(),
|
|
733
|
+
"--",
|
|
734
|
+
"extract",
|
|
735
|
+
&format!("{}:3", file_path.to_string_lossy()),
|
|
736
|
+
"--allow-tests", // Add this flag to ensure test files are included
|
|
737
|
+
])
|
|
738
|
+
.current_dir(&project_dir) // Ensure we're in the project directory
|
|
739
|
+
.output()
|
|
740
|
+
.expect("Failed to execute command");
|
|
741
|
+
|
|
742
|
+
// Check that the command executed successfully
|
|
743
|
+
assert!(output.status.success());
|
|
744
|
+
|
|
745
|
+
// Check that the output contains the function
|
|
746
|
+
let stdout = String::from_utf8_lossy(&output.stdout);
|
|
747
|
+
assert!(stdout.contains("File:"));
|
|
748
|
+
assert!(stdout.contains("fn main()"));
|
|
749
|
+
assert!(stdout.contains("Hello, world!"));
|
|
750
|
+
|
|
751
|
+
// Get the project root directory (where Cargo.toml is)
|
|
752
|
+
let project_dir = std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR"));
|
|
753
|
+
|
|
754
|
+
// Run with a different format
|
|
755
|
+
let output = Command::new("cargo")
|
|
756
|
+
.args([
|
|
757
|
+
"run",
|
|
758
|
+
"--manifest-path",
|
|
759
|
+
project_dir.join("Cargo.toml").to_string_lossy().as_ref(),
|
|
760
|
+
"--",
|
|
761
|
+
"extract",
|
|
762
|
+
file_path.to_string_lossy().as_ref(),
|
|
763
|
+
"--format",
|
|
764
|
+
"markdown",
|
|
765
|
+
"--allow-tests", // Add this flag to ensure test files are included
|
|
766
|
+
])
|
|
767
|
+
.current_dir(&project_dir) // Ensure we're in the project directory
|
|
768
|
+
.output()
|
|
769
|
+
.expect("Failed to execute command");
|
|
770
|
+
|
|
771
|
+
// Check that the command executed successfully
|
|
772
|
+
assert!(output.status.success());
|
|
773
|
+
|
|
774
|
+
// Check that the output is in markdown format
|
|
775
|
+
let stdout = String::from_utf8_lossy(&output.stdout);
|
|
776
|
+
assert!(stdout.contains("## File:"));
|
|
777
|
+
|
|
778
|
+
// Get the project root directory (where Cargo.toml is)
|
|
779
|
+
let project_dir = std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR"));
|
|
780
|
+
|
|
781
|
+
// Run with a line range
|
|
782
|
+
let output = Command::new("cargo")
|
|
783
|
+
.args([
|
|
784
|
+
"run",
|
|
785
|
+
"--manifest-path",
|
|
786
|
+
project_dir.join("Cargo.toml").to_string_lossy().as_ref(),
|
|
787
|
+
"--",
|
|
788
|
+
"extract",
|
|
789
|
+
&format!("{}:2-7", file_path.to_string_lossy()),
|
|
790
|
+
"--allow-tests", // Add this flag to ensure test files are included
|
|
791
|
+
])
|
|
792
|
+
.current_dir(&project_dir) // Ensure we're in the project directory
|
|
793
|
+
.output()
|
|
794
|
+
.expect("Failed to execute command");
|
|
795
|
+
|
|
796
|
+
// Check that the command executed successfully
|
|
797
|
+
assert!(output.status.success());
|
|
798
|
+
|
|
799
|
+
// Check that the output contains the specified range
|
|
800
|
+
let stdout = String::from_utf8_lossy(&output.stdout);
|
|
801
|
+
assert!(stdout.contains("File:"));
|
|
802
|
+
// The exact format of the line range might vary, so just check for the content
|
|
803
|
+
// We only check for the content that should definitely be in the range
|
|
804
|
+
assert!(
|
|
805
|
+
stdout.contains("fn main()")
|
|
806
|
+
|| stdout.contains("println!(\"Hello, world!\")")
|
|
807
|
+
|| stdout.contains("struct Point")
|
|
808
|
+
);
|
|
809
|
+
}
|
|
810
|
+
|
|
811
|
+
#[test]
|
|
812
|
+
fn test_integration_extract_command_json_format() {
|
|
813
|
+
use serde_json::Value;
|
|
814
|
+
|
|
815
|
+
// Create a temporary file for testing with special characters
|
|
816
|
+
let temp_dir = tempfile::tempdir().unwrap();
|
|
817
|
+
let file_path = temp_dir.path().join("test_file.rs");
|
|
818
|
+
let content = r#"
|
|
819
|
+
fn main() {
|
|
820
|
+
// This contains special characters: "quotes", 'apostrophes', <tags>, &ersands
|
|
821
|
+
println!("Hello, \"world\"!");
|
|
822
|
+
let message = 'A';
|
|
823
|
+
let html = "<div>Content & More</div>";
|
|
824
|
+
}
|
|
825
|
+
"#;
|
|
826
|
+
fs::write(&file_path, content).unwrap();
|
|
827
|
+
|
|
828
|
+
// Get the project root directory (where Cargo.toml is)
|
|
829
|
+
let project_dir = std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR"));
|
|
830
|
+
|
|
831
|
+
// Run the extract command with JSON format
|
|
832
|
+
let output = Command::new("cargo")
|
|
833
|
+
.args([
|
|
834
|
+
"run",
|
|
835
|
+
"--manifest-path",
|
|
836
|
+
project_dir.join("Cargo.toml").to_string_lossy().as_ref(),
|
|
837
|
+
"--",
|
|
838
|
+
"extract",
|
|
839
|
+
file_path.to_string_lossy().as_ref(),
|
|
840
|
+
"--format",
|
|
841
|
+
"json",
|
|
842
|
+
"--allow-tests", // Add this flag to ensure test files are included
|
|
843
|
+
])
|
|
844
|
+
.output()
|
|
845
|
+
.expect("Failed to execute command");
|
|
846
|
+
|
|
847
|
+
// Check that the command executed successfully
|
|
848
|
+
assert!(output.status.success());
|
|
849
|
+
|
|
850
|
+
// Get the output as a string
|
|
851
|
+
let stdout = String::from_utf8_lossy(&output.stdout);
|
|
852
|
+
|
|
853
|
+
// Helper function to extract JSON from command output
|
|
854
|
+
fn extract_json_from_output(output: &str) -> &str {
|
|
855
|
+
// Find the first occurrence of '{'
|
|
856
|
+
if let Some(start_index) = output.find('{') {
|
|
857
|
+
// Return the substring from the first '{' to the end
|
|
858
|
+
&output[start_index..]
|
|
859
|
+
} else {
|
|
860
|
+
// If no '{' is found, return the original string
|
|
861
|
+
output
|
|
862
|
+
}
|
|
863
|
+
}
|
|
864
|
+
|
|
865
|
+
// Extract and parse the JSON
|
|
866
|
+
let json_str = extract_json_from_output(&stdout);
|
|
867
|
+
let json_value: Value = serde_json::from_str(json_str).expect("Failed to parse JSON output");
|
|
868
|
+
|
|
869
|
+
// Validate the structure of the JSON output
|
|
870
|
+
assert!(json_value.is_object(), "JSON output should be an object");
|
|
871
|
+
assert!(
|
|
872
|
+
json_value.get("results").is_some(),
|
|
873
|
+
"JSON output should have a 'results' field"
|
|
874
|
+
);
|
|
875
|
+
assert!(
|
|
876
|
+
json_value.get("summary").is_some(),
|
|
877
|
+
"JSON output should have a 'summary' field"
|
|
878
|
+
);
|
|
879
|
+
|
|
880
|
+
// Validate the results array
|
|
881
|
+
let results = json_value.get("results").unwrap().as_array().unwrap();
|
|
882
|
+
assert!(!results.is_empty(), "Results array should not be empty");
|
|
883
|
+
|
|
884
|
+
// Validate the first result
|
|
885
|
+
let first_result = &results[0];
|
|
886
|
+
assert!(
|
|
887
|
+
first_result.get("file").is_some(),
|
|
888
|
+
"Result should have a 'file' field"
|
|
889
|
+
);
|
|
890
|
+
assert!(
|
|
891
|
+
first_result.get("code").is_some(),
|
|
892
|
+
"Result should have a 'code' field"
|
|
893
|
+
);
|
|
894
|
+
|
|
895
|
+
// Verify that special characters are properly escaped in the JSON
|
|
896
|
+
let code = first_result.get("code").unwrap().as_str().unwrap();
|
|
897
|
+
assert!(
|
|
898
|
+
code.contains("\"quotes\""),
|
|
899
|
+
"Double quotes should be properly escaped in JSON"
|
|
900
|
+
);
|
|
901
|
+
assert!(
|
|
902
|
+
code.contains("'apostrophes'"),
|
|
903
|
+
"Apostrophes should be properly escaped in JSON"
|
|
904
|
+
);
|
|
905
|
+
assert!(
|
|
906
|
+
code.contains("<tags>"),
|
|
907
|
+
"Tags should be properly escaped in JSON"
|
|
908
|
+
);
|
|
909
|
+
assert!(
|
|
910
|
+
code.contains("&ersands"),
|
|
911
|
+
"Ampersands should be properly escaped in JSON"
|
|
912
|
+
);
|
|
913
|
+
|
|
914
|
+
// Get the project root directory (where Cargo.toml is)
|
|
915
|
+
let project_dir = std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR"));
|
|
916
|
+
|
|
917
|
+
// Run with a line number and JSON format
|
|
918
|
+
let output = Command::new("cargo")
|
|
919
|
+
.args([
|
|
920
|
+
"run",
|
|
921
|
+
"--manifest-path",
|
|
922
|
+
project_dir.join("Cargo.toml").to_string_lossy().as_ref(),
|
|
923
|
+
"--",
|
|
924
|
+
"extract",
|
|
925
|
+
&format!("{}:3", file_path.to_string_lossy()),
|
|
926
|
+
"--format",
|
|
927
|
+
"json",
|
|
928
|
+
"--allow-tests", // Add this flag to ensure test files are included
|
|
929
|
+
])
|
|
930
|
+
.current_dir(&project_dir) // Ensure we're in the project directory
|
|
931
|
+
.output()
|
|
932
|
+
.expect("Failed to execute command");
|
|
933
|
+
|
|
934
|
+
// Print the command output for debugging
|
|
935
|
+
println!(
|
|
936
|
+
"Command stdout: {}",
|
|
937
|
+
String::from_utf8_lossy(&output.stdout)
|
|
938
|
+
);
|
|
939
|
+
println!(
|
|
940
|
+
"Command stderr: {}",
|
|
941
|
+
String::from_utf8_lossy(&output.stderr)
|
|
942
|
+
);
|
|
943
|
+
|
|
944
|
+
// Check that the command executed successfully
|
|
945
|
+
assert!(
|
|
946
|
+
output.status.success(),
|
|
947
|
+
"Command failed with status: {}",
|
|
948
|
+
output.status
|
|
949
|
+
);
|
|
950
|
+
|
|
951
|
+
// Parse the JSON output
|
|
952
|
+
let stdout = String::from_utf8_lossy(&output.stdout);
|
|
953
|
+
let json_str = extract_json_from_output(&stdout);
|
|
954
|
+
let json_value: Value = serde_json::from_str(json_str).expect("Failed to parse JSON output");
|
|
955
|
+
|
|
956
|
+
// Validate the structure
|
|
957
|
+
let results = json_value.get("results").unwrap().as_array().unwrap();
|
|
958
|
+
assert!(!results.is_empty(), "Results array should not be empty");
|
|
959
|
+
|
|
960
|
+
// The result should contain the function with line 3
|
|
961
|
+
let first_result = &results[0];
|
|
962
|
+
let code = first_result.get("code").unwrap().as_str().unwrap();
|
|
963
|
+
assert!(
|
|
964
|
+
code.contains("fn main()"),
|
|
965
|
+
"Code should contain the main function"
|
|
966
|
+
);
|
|
967
|
+
assert!(
|
|
968
|
+
code.contains("Hello, \\\"world\\\"!"),
|
|
969
|
+
"Code should contain the println statement with escaped quotes"
|
|
970
|
+
);
|
|
971
|
+
}
|
|
972
|
+
|
|
973
|
+
#[test]
|
|
974
|
+
fn test_is_git_diff_format() {
|
|
975
|
+
// Test with git diff format
|
|
976
|
+
let diff_content = r#"diff --git a/tests/property_tests.rs b/tests/property_tests.rs
|
|
977
|
+
index cb2cb64..3717769 100644
|
|
978
|
+
--- a/tests/property_tests.rs
|
|
979
|
+
+++ b/tests/property_tests.rs
|
|
980
|
+
@@ -45,7 +45,7 @@ proptest! {
|
|
981
|
+
Err(_) => return proptest::test_runner::TestCaseResult::Ok(()), // Skip invalid queries
|
|
982
|
+
};
|
|
983
|
+
|
|
984
|
+
- let patterns = create_structured_patterns(&plan);
|
|
985
|
+
+ let patterns = create_structured_patterns(&plan, false);
|
|
986
|
+
"#;
|
|
987
|
+
assert!(
|
|
988
|
+
is_git_diff_format(diff_content),
|
|
989
|
+
"Should detect git diff format"
|
|
990
|
+
);
|
|
991
|
+
|
|
992
|
+
// Test with whitespace before git diff format
|
|
993
|
+
let diff_content_with_whitespace = r#"
|
|
994
|
+
|
|
995
|
+
diff --git a/tests/property_tests.rs b/tests/property_tests.rs
|
|
996
|
+
index cb2cb64..3717769 100644
|
|
997
|
+
"#;
|
|
998
|
+
assert!(
|
|
999
|
+
is_git_diff_format(diff_content_with_whitespace),
|
|
1000
|
+
"Should detect git diff format with leading whitespace"
|
|
1001
|
+
);
|
|
1002
|
+
|
|
1003
|
+
// Test with non-git diff format
|
|
1004
|
+
let non_diff_content = r#"This is not a git diff format
|
|
1005
|
+
It's just some text
|
|
1006
|
+
"#;
|
|
1007
|
+
assert!(
|
|
1008
|
+
!is_git_diff_format(non_diff_content),
|
|
1009
|
+
"Should not detect git diff format"
|
|
1010
|
+
);
|
|
1011
|
+
}
|
|
1012
|
+
|
|
1013
|
+
#[test]
|
|
1014
|
+
fn test_extract_file_paths_from_git_diff() {
|
|
1015
|
+
// Sample git diff output
|
|
1016
|
+
let diff_content = r#"diff --git a/tests/property_tests.rs b/tests/property_tests.rs
|
|
1017
|
+
index cb2cb64..3717769 100644
|
|
1018
|
+
--- a/tests/property_tests.rs
|
|
1019
|
+
+++ b/tests/property_tests.rs
|
|
1020
|
+
@@ -45,7 +45,7 @@ proptest! {
|
|
1021
|
+
Err(_) => return proptest::test_runner::TestCaseResult::Ok(()), // Skip invalid queries
|
|
1022
|
+
};
|
|
1023
|
+
|
|
1024
|
+
- let patterns = create_structured_patterns(&plan);
|
|
1025
|
+
+ let patterns = create_structured_patterns(&plan, false);
|
|
1026
|
+
|
|
1027
|
+
// Check that we have at least one pattern for each term
|
|
1028
|
+
for (term, &idx) in &plan.term_indices {
|
|
1029
|
+
"#;
|
|
1030
|
+
|
|
1031
|
+
// Parse the git diff
|
|
1032
|
+
let file_paths = extract_file_paths_from_git_diff(diff_content, true);
|
|
1033
|
+
|
|
1034
|
+
// Verify that we extracted the correct file path and line number
|
|
1035
|
+
assert_eq!(file_paths.len(), 1, "Should extract exactly one file path");
|
|
1036
|
+
|
|
1037
|
+
let (path, start_line, end_line, symbol, _specific_lines) = &file_paths[0];
|
|
1038
|
+
assert_eq!(
|
|
1039
|
+
path,
|
|
1040
|
+
&PathBuf::from("tests/property_tests.rs"),
|
|
1041
|
+
"Should extract the correct file path"
|
|
1042
|
+
);
|
|
1043
|
+
assert_eq!(
|
|
1044
|
+
*start_line,
|
|
1045
|
+
Some(48),
|
|
1046
|
+
"Should extract the correct line number"
|
|
1047
|
+
);
|
|
1048
|
+
assert_eq!(*end_line, Some(48), "End line should be 48");
|
|
1049
|
+
assert_eq!(*symbol, None, "Symbol should be None");
|
|
1050
|
+
}
|
|
1051
|
+
|
|
1052
|
+
#[test]
|
|
1053
|
+
fn test_extract_file_paths_from_git_diff_multiple_files() {
|
|
1054
|
+
// Sample git diff output with multiple files
|
|
1055
|
+
let diff_content = r#"diff --git a/tests/property_tests.rs b/tests/property_tests.rs
|
|
1056
|
+
index cb2cb64..3717769 100644
|
|
1057
|
+
--- a/tests/property_tests.rs
|
|
1058
|
+
+++ b/tests/property_tests.rs
|
|
1059
|
+
@@ -45,7 +45,7 @@ proptest! {
|
|
1060
|
+
Err(_) => return proptest::test_runner::TestCaseResult::Ok(()), // Skip invalid queries
|
|
1061
|
+
};
|
|
1062
|
+
|
|
1063
|
+
- let patterns = create_structured_patterns(&plan);
|
|
1064
|
+
+ let patterns = create_structured_patterns(&plan, false);
|
|
1065
|
+
|
|
1066
|
+
// Check that we have at least one pattern for each term
|
|
1067
|
+
for (term, &idx) in &plan.term_indices {
|
|
1068
|
+
diff --git a/tests/tokenization_tests.rs b/tests/tokenization_tests.rs
|
|
1069
|
+
index abcdef1..1234567 100644
|
|
1070
|
+
--- a/tests/tokenization_tests.rs
|
|
1071
|
+
+++ b/tests/tokenization_tests.rs
|
|
1072
|
+
@@ -20,7 +20,7 @@ fn test_tokenize_with_stemming() {
|
|
1073
|
+
let tokens = tokenize_with_stemming("running runs runner");
|
|
1074
|
+
|
|
1075
|
+
- assert_eq!(tokens, vec!["run", "run", "runner"]);
|
|
1076
|
+
+ assert_eq!(tokens, vec!["run", "run", "run"]);
|
|
1077
|
+
}
|
|
1078
|
+
"#;
|
|
1079
|
+
|
|
1080
|
+
// Parse the git diff
|
|
1081
|
+
let file_paths = extract_file_paths_from_git_diff(diff_content, true);
|
|
1082
|
+
|
|
1083
|
+
// Verify that we extracted the correct file paths and line numbers
|
|
1084
|
+
assert_eq!(file_paths.len(), 2, "Should extract exactly two file paths");
|
|
1085
|
+
|
|
1086
|
+
// Sort the results by file path to ensure consistent order for testing
|
|
1087
|
+
let mut sorted_paths = file_paths.clone();
|
|
1088
|
+
sorted_paths.sort_by(|a, b| a.0.cmp(&b.0));
|
|
1089
|
+
|
|
1090
|
+
// Check first file
|
|
1091
|
+
let (path1, start_line1, end_line1, symbol1, _specific_lines1) = &sorted_paths[0];
|
|
1092
|
+
assert_eq!(
|
|
1093
|
+
path1,
|
|
1094
|
+
&PathBuf::from("tests/property_tests.rs"),
|
|
1095
|
+
"Should extract the correct file path"
|
|
1096
|
+
);
|
|
1097
|
+
assert_eq!(
|
|
1098
|
+
*start_line1,
|
|
1099
|
+
Some(48),
|
|
1100
|
+
"Should extract the correct line number"
|
|
1101
|
+
);
|
|
1102
|
+
assert_eq!(*end_line1, Some(48), "End line should be 48");
|
|
1103
|
+
assert_eq!(*symbol1, None, "Symbol should be None");
|
|
1104
|
+
|
|
1105
|
+
// Check second file
|
|
1106
|
+
let (path2, start_line2, end_line2, symbol2, _specific_lines2) = &sorted_paths[1];
|
|
1107
|
+
assert_eq!(
|
|
1108
|
+
path2,
|
|
1109
|
+
&PathBuf::from("tests/tokenization_tests.rs"),
|
|
1110
|
+
"Should extract the correct file path"
|
|
1111
|
+
);
|
|
1112
|
+
assert_eq!(
|
|
1113
|
+
*start_line2,
|
|
1114
|
+
Some(22),
|
|
1115
|
+
"Should extract the correct line number"
|
|
1116
|
+
);
|
|
1117
|
+
assert_eq!(*end_line2, Some(22), "End line should be 22");
|
|
1118
|
+
assert_eq!(*symbol2, None, "Symbol should be None");
|
|
1119
|
+
}
|
|
1120
|
+
|
|
1121
|
+
#[test]
|
|
1122
|
+
fn test_integration_extract_command_with_diff_flag() {
|
|
1123
|
+
// Create a temporary file for testing
|
|
1124
|
+
let temp_dir = tempfile::tempdir().unwrap();
|
|
1125
|
+
let file_path = temp_dir.path().join("test_file.rs");
|
|
1126
|
+
let content = r#"
|
|
1127
|
+
fn main() {
|
|
1128
|
+
println!("Hello, world!");
|
|
1129
|
+
}
|
|
1130
|
+
|
|
1131
|
+
struct Point {
|
|
1132
|
+
x: i32,
|
|
1133
|
+
y: i32,
|
|
1134
|
+
}
|
|
1135
|
+
"#;
|
|
1136
|
+
fs::write(&file_path, content).unwrap();
|
|
1137
|
+
|
|
1138
|
+
// Create a git diff file
|
|
1139
|
+
let diff_path = temp_dir.path().join("test.diff");
|
|
1140
|
+
let diff_content = format!(
|
|
1141
|
+
r#"diff --git a/{0} b/{0}
|
|
1142
|
+
index cb2cb64..3717769 100644
|
|
1143
|
+
--- a/{0}
|
|
1144
|
+
+++ b/{0}
|
|
1145
|
+
@@ -3,1 +3,1 @@ fn main() {{
|
|
1146
|
+
- println!("Hello, world!");
|
|
1147
|
+
+ println!("Hello, universe!");
|
|
1148
|
+
"#,
|
|
1149
|
+
file_path.file_name().unwrap().to_string_lossy()
|
|
1150
|
+
);
|
|
1151
|
+
fs::write(&diff_path, &diff_content).unwrap();
|
|
1152
|
+
|
|
1153
|
+
// Get the project root directory (where Cargo.toml is)
|
|
1154
|
+
let project_dir = std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR"));
|
|
1155
|
+
|
|
1156
|
+
// Run the extract command with diff option
|
|
1157
|
+
let output = Command::new("cargo")
|
|
1158
|
+
.args([
|
|
1159
|
+
"run",
|
|
1160
|
+
"--manifest-path",
|
|
1161
|
+
project_dir.join("Cargo.toml").to_string_lossy().as_ref(),
|
|
1162
|
+
"--",
|
|
1163
|
+
"extract",
|
|
1164
|
+
"--diff",
|
|
1165
|
+
diff_path.to_string_lossy().as_ref(),
|
|
1166
|
+
"--allow-tests", // Add this flag to ensure test files are included
|
|
1167
|
+
])
|
|
1168
|
+
.output()
|
|
1169
|
+
.expect("Failed to execute command");
|
|
1170
|
+
|
|
1171
|
+
// Check that the command executed successfully
|
|
1172
|
+
assert!(output.status.success(), "Command failed to execute");
|
|
1173
|
+
|
|
1174
|
+
let stdout = String::from_utf8_lossy(&output.stdout);
|
|
1175
|
+
println!("Command output: {stdout}");
|
|
1176
|
+
|
|
1177
|
+
// The output should contain information about the extracted file
|
|
1178
|
+
assert!(
|
|
1179
|
+
stdout.contains("Files to extract:"),
|
|
1180
|
+
"Output should contain file list"
|
|
1181
|
+
);
|
|
1182
|
+
assert!(
|
|
1183
|
+
stdout.contains("test_file.rs"),
|
|
1184
|
+
"Output should contain the extracted file name"
|
|
1185
|
+
);
|
|
1186
|
+
}
|
|
1187
|
+
|
|
1188
|
+
#[test]
|
|
1189
|
+
fn test_integration_extract_command_with_auto_diff_detection() {
|
|
1190
|
+
// Create a temporary file for testing
|
|
1191
|
+
let temp_dir = tempfile::tempdir().unwrap();
|
|
1192
|
+
let file_path = temp_dir.path().join("test_file.rs");
|
|
1193
|
+
let content = r#"
|
|
1194
|
+
fn main() {
|
|
1195
|
+
println!("Hello, world!");
|
|
1196
|
+
}
|
|
1197
|
+
|
|
1198
|
+
struct Point {
|
|
1199
|
+
x: i32,
|
|
1200
|
+
y: i32,
|
|
1201
|
+
}
|
|
1202
|
+
"#;
|
|
1203
|
+
fs::write(&file_path, content).unwrap();
|
|
1204
|
+
|
|
1205
|
+
// Create a git diff file
|
|
1206
|
+
let diff_path = temp_dir.path().join("test.diff");
|
|
1207
|
+
let diff_content = format!(
|
|
1208
|
+
r#"diff --git a/{0} b/{0}
|
|
1209
|
+
index cb2cb64..3717769 100644
|
|
1210
|
+
--- a/{0}
|
|
1211
|
+
+++ b/{0}
|
|
1212
|
+
@@ -3,1 +3,1 @@ fn main() {{
|
|
1213
|
+
- println!("Hello, world!");
|
|
1214
|
+
+ println!("Hello, universe!");
|
|
1215
|
+
"#,
|
|
1216
|
+
file_path.file_name().unwrap().to_string_lossy()
|
|
1217
|
+
);
|
|
1218
|
+
fs::write(&diff_path, &diff_content).unwrap();
|
|
1219
|
+
|
|
1220
|
+
// Get the project root directory (where Cargo.toml is)
|
|
1221
|
+
let project_dir = std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR"));
|
|
1222
|
+
|
|
1223
|
+
// Run the extract command WITHOUT the diff flag - it should auto-detect
|
|
1224
|
+
let output = Command::new("cargo")
|
|
1225
|
+
.args([
|
|
1226
|
+
"run",
|
|
1227
|
+
"--manifest-path",
|
|
1228
|
+
project_dir.join("Cargo.toml").to_string_lossy().as_ref(),
|
|
1229
|
+
"--",
|
|
1230
|
+
"extract",
|
|
1231
|
+
diff_path.to_string_lossy().as_ref(),
|
|
1232
|
+
"--allow-tests", // Add this flag to ensure test files are included
|
|
1233
|
+
])
|
|
1234
|
+
.output()
|
|
1235
|
+
.expect("Failed to execute command");
|
|
1236
|
+
|
|
1237
|
+
// Check that the command executed successfully
|
|
1238
|
+
assert!(output.status.success(), "Command failed to execute");
|
|
1239
|
+
|
|
1240
|
+
let stdout = String::from_utf8_lossy(&output.stdout);
|
|
1241
|
+
println!("Command output: {stdout}");
|
|
1242
|
+
|
|
1243
|
+
// The output should contain information about the extracted file
|
|
1244
|
+
assert!(
|
|
1245
|
+
stdout.contains("Files to extract:"),
|
|
1246
|
+
"Output should contain file list"
|
|
1247
|
+
);
|
|
1248
|
+
assert!(
|
|
1249
|
+
stdout.contains("test_file.rs"),
|
|
1250
|
+
"Output should contain the extracted file name"
|
|
1251
|
+
);
|
|
1252
|
+
}
|
|
1253
|
+
|
|
1254
|
+
#[test]
|
|
1255
|
+
fn test_integration_extract_command_xml_format() {
|
|
1256
|
+
use roxmltree::Document;
|
|
1257
|
+
|
|
1258
|
+
// Create a temporary file for testing with special characters
|
|
1259
|
+
let temp_dir = tempfile::tempdir().unwrap();
|
|
1260
|
+
let file_path = temp_dir.path().join("test_file.rs");
|
|
1261
|
+
let content = r#"
|
|
1262
|
+
fn main() {
|
|
1263
|
+
// This contains special characters: "quotes", 'apostrophes', <tags>, &ersands
|
|
1264
|
+
println!("Hello, \"world\"!");
|
|
1265
|
+
let message = 'A';
|
|
1266
|
+
let html = "<div>Content & More</div>";
|
|
1267
|
+
}
|
|
1268
|
+
"#;
|
|
1269
|
+
fs::write(&file_path, content).unwrap();
|
|
1270
|
+
|
|
1271
|
+
// Get the project root directory (where Cargo.toml is)
|
|
1272
|
+
let project_dir = std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR"));
|
|
1273
|
+
|
|
1274
|
+
// Run the extract command with XML format
|
|
1275
|
+
let output = Command::new("cargo")
|
|
1276
|
+
.args([
|
|
1277
|
+
"run",
|
|
1278
|
+
"--manifest-path",
|
|
1279
|
+
project_dir.join("Cargo.toml").to_string_lossy().as_ref(),
|
|
1280
|
+
"--",
|
|
1281
|
+
"extract",
|
|
1282
|
+
file_path.to_string_lossy().as_ref(),
|
|
1283
|
+
"--format",
|
|
1284
|
+
"xml",
|
|
1285
|
+
"--allow-tests", // Add this flag to ensure test files are included
|
|
1286
|
+
])
|
|
1287
|
+
.current_dir(&project_dir) // Ensure we're in the project directory
|
|
1288
|
+
.output()
|
|
1289
|
+
.expect("Failed to execute command");
|
|
1290
|
+
|
|
1291
|
+
// Check that the command executed successfully
|
|
1292
|
+
assert!(output.status.success());
|
|
1293
|
+
|
|
1294
|
+
// Get the output as a string
|
|
1295
|
+
let stdout = String::from_utf8_lossy(&output.stdout);
|
|
1296
|
+
|
|
1297
|
+
// Helper function to extract XML from command output
|
|
1298
|
+
fn extract_xml_from_output(output: &str) -> &str {
|
|
1299
|
+
// Find the first occurrence of '<?xml'
|
|
1300
|
+
if let Some(start_index) = output.find("<?xml") {
|
|
1301
|
+
// Return the substring from the first '<?xml' to the end
|
|
1302
|
+
&output[start_index..]
|
|
1303
|
+
} else {
|
|
1304
|
+
// If no '<?xml' is found, return the original string
|
|
1305
|
+
output
|
|
1306
|
+
}
|
|
1307
|
+
}
|
|
1308
|
+
|
|
1309
|
+
// Extract and parse the XML
|
|
1310
|
+
let xml_str = extract_xml_from_output(&stdout);
|
|
1311
|
+
let doc = Document::parse(xml_str).expect("Failed to parse XML output");
|
|
1312
|
+
let root = doc.root_element();
|
|
1313
|
+
|
|
1314
|
+
// Validate the structure of the XML output
|
|
1315
|
+
assert_eq!(
|
|
1316
|
+
root.tag_name().name(),
|
|
1317
|
+
"probe_results",
|
|
1318
|
+
"Root element should be 'probe_results'"
|
|
1319
|
+
);
|
|
1320
|
+
|
|
1321
|
+
// Validate that there are result elements
|
|
1322
|
+
let results: Vec<_> = root
|
|
1323
|
+
.children()
|
|
1324
|
+
.filter(|n| n.is_element() && n.tag_name().name() == "result")
|
|
1325
|
+
.collect();
|
|
1326
|
+
assert!(
|
|
1327
|
+
!results.is_empty(),
|
|
1328
|
+
"Should have at least one result element"
|
|
1329
|
+
);
|
|
1330
|
+
|
|
1331
|
+
// Validate the first result
|
|
1332
|
+
let first_result = &results[0];
|
|
1333
|
+
|
|
1334
|
+
// Check for required elements
|
|
1335
|
+
let file = first_result
|
|
1336
|
+
.children()
|
|
1337
|
+
.find(|n| n.is_element() && n.tag_name().name() == "file");
|
|
1338
|
+
assert!(file.is_some(), "Result should have a file element");
|
|
1339
|
+
|
|
1340
|
+
let code = first_result
|
|
1341
|
+
.children()
|
|
1342
|
+
.find(|n| n.is_element() && n.tag_name().name() == "code");
|
|
1343
|
+
assert!(code.is_some(), "Result should have a code element");
|
|
1344
|
+
|
|
1345
|
+
// Verify that special characters are properly handled in the XML (should be in CDATA)
|
|
1346
|
+
if let Some(code_elem) = code {
|
|
1347
|
+
let code_text = code_elem.text().unwrap();
|
|
1348
|
+
assert!(
|
|
1349
|
+
code_text.contains("\"quotes\""),
|
|
1350
|
+
"Double quotes should be preserved in CDATA"
|
|
1351
|
+
);
|
|
1352
|
+
assert!(
|
|
1353
|
+
code_text.contains("'apostrophes'"),
|
|
1354
|
+
"Apostrophes should be preserved in CDATA"
|
|
1355
|
+
);
|
|
1356
|
+
assert!(
|
|
1357
|
+
code_text.contains("<tags>"),
|
|
1358
|
+
"Tags should be preserved in CDATA"
|
|
1359
|
+
);
|
|
1360
|
+
assert!(
|
|
1361
|
+
code_text.contains("&ersands"),
|
|
1362
|
+
"Ampersands should be preserved in CDATA"
|
|
1363
|
+
);
|
|
1364
|
+
}
|
|
1365
|
+
|
|
1366
|
+
// Get the project root directory (where Cargo.toml is)
|
|
1367
|
+
let project_dir = std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR"));
|
|
1368
|
+
|
|
1369
|
+
// Run with a line number and XML format
|
|
1370
|
+
let output = Command::new("cargo")
|
|
1371
|
+
.args([
|
|
1372
|
+
"run",
|
|
1373
|
+
"--manifest-path",
|
|
1374
|
+
project_dir.join("Cargo.toml").to_string_lossy().as_ref(),
|
|
1375
|
+
"--",
|
|
1376
|
+
"extract",
|
|
1377
|
+
&format!("{}:3", file_path.to_string_lossy()),
|
|
1378
|
+
"--format",
|
|
1379
|
+
"xml",
|
|
1380
|
+
"--allow-tests", // Add this flag to ensure test files are included
|
|
1381
|
+
])
|
|
1382
|
+
.current_dir(&project_dir) // Ensure we're in the project directory
|
|
1383
|
+
.output()
|
|
1384
|
+
.expect("Failed to execute command");
|
|
1385
|
+
|
|
1386
|
+
// Print the command output for debugging
|
|
1387
|
+
println!(
|
|
1388
|
+
"Command stdout: {}",
|
|
1389
|
+
String::from_utf8_lossy(&output.stdout)
|
|
1390
|
+
);
|
|
1391
|
+
println!(
|
|
1392
|
+
"Command stderr: {}",
|
|
1393
|
+
String::from_utf8_lossy(&output.stderr)
|
|
1394
|
+
);
|
|
1395
|
+
|
|
1396
|
+
// Check that the command executed successfully
|
|
1397
|
+
assert!(
|
|
1398
|
+
output.status.success(),
|
|
1399
|
+
"Command failed with status: {}",
|
|
1400
|
+
output.status
|
|
1401
|
+
);
|
|
1402
|
+
|
|
1403
|
+
// Parse the XML output
|
|
1404
|
+
let stdout = String::from_utf8_lossy(&output.stdout);
|
|
1405
|
+
let xml_str = extract_xml_from_output(&stdout);
|
|
1406
|
+
let doc = Document::parse(xml_str).expect("Failed to parse XML output");
|
|
1407
|
+
let root = doc.root_element();
|
|
1408
|
+
|
|
1409
|
+
// Validate the structure
|
|
1410
|
+
let results: Vec<_> = root
|
|
1411
|
+
.children()
|
|
1412
|
+
.filter(|n| n.is_element() && n.tag_name().name() == "result")
|
|
1413
|
+
.collect();
|
|
1414
|
+
assert!(
|
|
1415
|
+
!results.is_empty(),
|
|
1416
|
+
"Should have at least one result element"
|
|
1417
|
+
);
|
|
1418
|
+
|
|
1419
|
+
// The result should contain the function with line 3
|
|
1420
|
+
let first_result = &results[0];
|
|
1421
|
+
if let Some(code_elem) = first_result
|
|
1422
|
+
.children()
|
|
1423
|
+
.find(|n| n.is_element() && n.tag_name().name() == "code")
|
|
1424
|
+
{
|
|
1425
|
+
let code_text = code_elem.text().unwrap();
|
|
1426
|
+
assert!(
|
|
1427
|
+
code_text.contains("fn main()"),
|
|
1428
|
+
"Code should contain the main function"
|
|
1429
|
+
);
|
|
1430
|
+
assert!(
|
|
1431
|
+
code_text.contains("Hello") && code_text.contains("world"),
|
|
1432
|
+
"Code should contain the println statement"
|
|
1433
|
+
);
|
|
1434
|
+
}
|
|
1435
|
+
}
|
|
1436
|
+
|
|
1437
|
+
#[test]
|
|
1438
|
+
fn test_integration_extract_command_with_multiple_files_diff() {
|
|
1439
|
+
// Create temporary files for testing
|
|
1440
|
+
let temp_dir = tempfile::tempdir().unwrap();
|
|
1441
|
+
|
|
1442
|
+
// Change to the temp directory for this test
|
|
1443
|
+
let original_dir = std::env::current_dir().unwrap();
|
|
1444
|
+
std::env::set_current_dir(&temp_dir).unwrap();
|
|
1445
|
+
|
|
1446
|
+
// Create the test files in the temp directory
|
|
1447
|
+
let file_path1 = PathBuf::from("property_tests.rs");
|
|
1448
|
+
let content1 = r#"
|
|
1449
|
+
fn test_create_structured_patterns() {
|
|
1450
|
+
let plan = create_query_plan("test query", false).unwrap();
|
|
1451
|
+
let patterns = create_structured_patterns(&plan);
|
|
1452
|
+
|
|
1453
|
+
// Check that we have at least one pattern for each term
|
|
1454
|
+
for (term, &idx) in &plan.term_indices {
|
|
1455
|
+
assert!(!patterns[idx].is_empty());
|
|
1456
|
+
}
|
|
1457
|
+
}
|
|
1458
|
+
"#;
|
|
1459
|
+
fs::write(&file_path1, content1).unwrap();
|
|
1460
|
+
|
|
1461
|
+
let file_path2 = PathBuf::from("tokenization_tests.rs");
|
|
1462
|
+
let content2 = r#"
|
|
1463
|
+
fn test_tokenize_with_stemming() {
|
|
1464
|
+
let tokens = tokenize_with_stemming("running runs runner");
|
|
1465
|
+
|
|
1466
|
+
assert_eq!(tokens, vec!["run", "run", "runner"]);
|
|
1467
|
+
}
|
|
1468
|
+
"#;
|
|
1469
|
+
fs::write(&file_path2, content2).unwrap();
|
|
1470
|
+
|
|
1471
|
+
// Run the git diff command to create a real diff
|
|
1472
|
+
let status = Command::new("git")
|
|
1473
|
+
.args(["init"])
|
|
1474
|
+
.status()
|
|
1475
|
+
.expect("Failed to initialize git repo");
|
|
1476
|
+
assert!(status.success(), "Failed to initialize git repo");
|
|
1477
|
+
|
|
1478
|
+
let status = Command::new("git")
|
|
1479
|
+
.args(["config", "user.name", "Test User"])
|
|
1480
|
+
.status()
|
|
1481
|
+
.expect("Failed to configure git");
|
|
1482
|
+
assert!(status.success(), "Failed to configure git");
|
|
1483
|
+
|
|
1484
|
+
let status = Command::new("git")
|
|
1485
|
+
.args(["config", "user.email", "test@example.com"])
|
|
1486
|
+
.status()
|
|
1487
|
+
.expect("Failed to configure git");
|
|
1488
|
+
assert!(status.success(), "Failed to configure git");
|
|
1489
|
+
|
|
1490
|
+
let status = Command::new("git")
|
|
1491
|
+
.args(["add", "."])
|
|
1492
|
+
.status()
|
|
1493
|
+
.expect("Failed to add files to git");
|
|
1494
|
+
assert!(status.success(), "Failed to add files to git");
|
|
1495
|
+
|
|
1496
|
+
let status = Command::new("git")
|
|
1497
|
+
.args(["commit", "-m", "Initial commit"])
|
|
1498
|
+
.status()
|
|
1499
|
+
.expect("Failed to commit files");
|
|
1500
|
+
assert!(status.success(), "Failed to commit files");
|
|
1501
|
+
|
|
1502
|
+
// Modify the files
|
|
1503
|
+
let content1_modified = r#"
|
|
1504
|
+
fn test_create_structured_patterns() {
|
|
1505
|
+
let plan = create_query_plan("test query", false).unwrap();
|
|
1506
|
+
let patterns = create_structured_patterns(&plan, false);
|
|
1507
|
+
|
|
1508
|
+
// Check that we have at least one pattern for each term
|
|
1509
|
+
for (term, &idx) in &plan.term_indices {
|
|
1510
|
+
assert!(!patterns[idx].is_empty());
|
|
1511
|
+
}
|
|
1512
|
+
}
|
|
1513
|
+
"#;
|
|
1514
|
+
fs::write(&file_path1, content1_modified).unwrap();
|
|
1515
|
+
|
|
1516
|
+
let content2_modified = r#"
|
|
1517
|
+
fn test_tokenize_with_stemming() {
|
|
1518
|
+
let tokens = tokenize_with_stemming("running runs runner");
|
|
1519
|
+
|
|
1520
|
+
assert_eq!(tokens, vec!["run", "run", "run"]);
|
|
1521
|
+
}
|
|
1522
|
+
"#;
|
|
1523
|
+
fs::write(&file_path2, content2_modified).unwrap();
|
|
1524
|
+
|
|
1525
|
+
// Create the diff file
|
|
1526
|
+
let diff_output = Command::new("git")
|
|
1527
|
+
.args(["diff", "property_tests.rs", "tokenization_tests.rs"])
|
|
1528
|
+
.output()
|
|
1529
|
+
.expect("Failed to create git diff");
|
|
1530
|
+
|
|
1531
|
+
assert!(diff_output.status.success(), "Failed to create git diff");
|
|
1532
|
+
let diff_content = String::from_utf8_lossy(&diff_output.stdout).to_string();
|
|
1533
|
+
|
|
1534
|
+
// Write the diff to a file
|
|
1535
|
+
let diff_path = PathBuf::from("changes.diff");
|
|
1536
|
+
fs::write(&diff_path, &diff_content).unwrap();
|
|
1537
|
+
|
|
1538
|
+
// Get the project root directory (where Cargo.toml is)
|
|
1539
|
+
let project_dir = std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR"));
|
|
1540
|
+
|
|
1541
|
+
// Run the extract command with the diff containing multiple files
|
|
1542
|
+
let output = Command::new("cargo")
|
|
1543
|
+
.args([
|
|
1544
|
+
"run",
|
|
1545
|
+
"--manifest-path",
|
|
1546
|
+
project_dir.join("Cargo.toml").to_string_lossy().as_ref(),
|
|
1547
|
+
"--",
|
|
1548
|
+
"extract",
|
|
1549
|
+
diff_path.to_string_lossy().as_ref(),
|
|
1550
|
+
"--allow-tests", // Add this flag to ensure test files are included
|
|
1551
|
+
])
|
|
1552
|
+
.output()
|
|
1553
|
+
.expect("Failed to execute command");
|
|
1554
|
+
|
|
1555
|
+
// Restore the original directory
|
|
1556
|
+
std::env::set_current_dir(original_dir).unwrap();
|
|
1557
|
+
|
|
1558
|
+
// Check that the command executed successfully
|
|
1559
|
+
assert!(output.status.success(), "Command failed to execute");
|
|
1560
|
+
|
|
1561
|
+
let stdout = String::from_utf8_lossy(&output.stdout);
|
|
1562
|
+
println!("Command output: {stdout}");
|
|
1563
|
+
|
|
1564
|
+
// The output should contain information about the diff file
|
|
1565
|
+
assert!(
|
|
1566
|
+
stdout.contains("Files to extract:"),
|
|
1567
|
+
"Output should contain file list"
|
|
1568
|
+
);
|
|
1569
|
+
assert!(
|
|
1570
|
+
stdout.contains("changes.diff"),
|
|
1571
|
+
"Output should contain the diff file name"
|
|
1572
|
+
);
|
|
1573
|
+
|
|
1574
|
+
// Verify that the diff content contains both files
|
|
1575
|
+
assert!(
|
|
1576
|
+
stdout.contains("property_tests.rs"),
|
|
1577
|
+
"Output should contain the first file name in diff content"
|
|
1578
|
+
);
|
|
1579
|
+
assert!(
|
|
1580
|
+
stdout.contains("tokenization_tests.rs"),
|
|
1581
|
+
"Output should contain the second file name in diff content"
|
|
1582
|
+
);
|
|
1583
|
+
|
|
1584
|
+
// When processing a diff file directly, we only get one "File:" entry for the diff itself
|
|
1585
|
+
let file_count = stdout.matches("File:").count();
|
|
1586
|
+
assert_eq!(file_count, 1, "Should process the diff file");
|
|
1587
|
+
}
|
|
1588
|
+
|
|
1589
|
+
#[test]
|
|
1590
|
+
fn test_keep_input_option_with_stdin() {
|
|
1591
|
+
use std::io::Write;
|
|
1592
|
+
use std::process::{Command, Stdio};
|
|
1593
|
+
|
|
1594
|
+
// Get the project root directory (where Cargo.toml is)
|
|
1595
|
+
let project_dir = std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR"));
|
|
1596
|
+
|
|
1597
|
+
// Create a temporary file with some content
|
|
1598
|
+
let temp_dir = tempfile::tempdir().unwrap();
|
|
1599
|
+
let file_path = temp_dir.path().join("test_file.rs");
|
|
1600
|
+
let content = r#"
|
|
1601
|
+
fn main() {
|
|
1602
|
+
println!("Hello, world!");
|
|
1603
|
+
}
|
|
1604
|
+
"#;
|
|
1605
|
+
fs::write(&file_path, content).unwrap();
|
|
1606
|
+
|
|
1607
|
+
// Create input content that references the file
|
|
1608
|
+
let input_content = format!("{}", file_path.to_string_lossy());
|
|
1609
|
+
|
|
1610
|
+
// Run the extract command with stdin input and keep_input flag
|
|
1611
|
+
let mut child = Command::new("cargo")
|
|
1612
|
+
.args([
|
|
1613
|
+
"run",
|
|
1614
|
+
"--manifest-path",
|
|
1615
|
+
project_dir.join("Cargo.toml").to_string_lossy().as_ref(),
|
|
1616
|
+
"--",
|
|
1617
|
+
"extract",
|
|
1618
|
+
"--keep-input",
|
|
1619
|
+
"--allow-tests", // Add this flag to ensure test files are included
|
|
1620
|
+
])
|
|
1621
|
+
.stdin(Stdio::piped())
|
|
1622
|
+
.stdout(Stdio::piped())
|
|
1623
|
+
.spawn()
|
|
1624
|
+
.expect("Failed to spawn command");
|
|
1625
|
+
|
|
1626
|
+
// Write to stdin
|
|
1627
|
+
{
|
|
1628
|
+
let stdin = child.stdin.as_mut().expect("Failed to open stdin");
|
|
1629
|
+
stdin
|
|
1630
|
+
.write_all(input_content.as_bytes())
|
|
1631
|
+
.expect("Failed to write to stdin");
|
|
1632
|
+
}
|
|
1633
|
+
|
|
1634
|
+
// Get the output
|
|
1635
|
+
let output = child.wait_with_output().expect("Failed to read stdout");
|
|
1636
|
+
|
|
1637
|
+
// Check that the command executed successfully
|
|
1638
|
+
assert!(output.status.success(), "Command failed to execute");
|
|
1639
|
+
|
|
1640
|
+
// Get the output as a string
|
|
1641
|
+
let stdout = String::from_utf8_lossy(&output.stdout);
|
|
1642
|
+
|
|
1643
|
+
// The output should contain the original input
|
|
1644
|
+
assert!(
|
|
1645
|
+
stdout.contains("Original Input:"),
|
|
1646
|
+
"Output should contain 'Original Input:' section"
|
|
1647
|
+
);
|
|
1648
|
+
assert!(
|
|
1649
|
+
stdout.contains(&input_content),
|
|
1650
|
+
"Output should contain the original input content"
|
|
1651
|
+
);
|
|
1652
|
+
}
|
|
1653
|
+
|
|
1654
|
+
#[test]
|
|
1655
|
+
fn test_keep_input_option_with_clipboard() {
|
|
1656
|
+
use arboard::Clipboard;
|
|
1657
|
+
use std::process::Command;
|
|
1658
|
+
|
|
1659
|
+
// Skip this test if clipboard access is not available
|
|
1660
|
+
let clipboard_result = Clipboard::new();
|
|
1661
|
+
if clipboard_result.is_err() {
|
|
1662
|
+
println!("Skipping clipboard test as clipboard access is not available");
|
|
1663
|
+
return;
|
|
1664
|
+
}
|
|
1665
|
+
|
|
1666
|
+
// Get the project root directory (where Cargo.toml is)
|
|
1667
|
+
let project_dir = std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR"));
|
|
1668
|
+
|
|
1669
|
+
// Create a temporary file with some content
|
|
1670
|
+
let temp_dir = tempfile::tempdir().unwrap();
|
|
1671
|
+
let file_path = temp_dir.path().join("test_file.rs");
|
|
1672
|
+
let content = r#"
|
|
1673
|
+
fn main() {
|
|
1674
|
+
println!("Hello, world!");
|
|
1675
|
+
}
|
|
1676
|
+
"#;
|
|
1677
|
+
fs::write(&file_path, content).unwrap();
|
|
1678
|
+
|
|
1679
|
+
// Create input content that references the file
|
|
1680
|
+
let input_content = format!("{}", file_path.to_string_lossy());
|
|
1681
|
+
|
|
1682
|
+
// Set clipboard content
|
|
1683
|
+
let mut clipboard = Clipboard::new().unwrap();
|
|
1684
|
+
clipboard.set_text(&input_content).unwrap();
|
|
1685
|
+
|
|
1686
|
+
// Run the extract command with clipboard input and keep_input flag
|
|
1687
|
+
let output = Command::new("cargo")
|
|
1688
|
+
.args([
|
|
1689
|
+
"run",
|
|
1690
|
+
"--manifest-path",
|
|
1691
|
+
project_dir.join("Cargo.toml").to_string_lossy().as_ref(),
|
|
1692
|
+
"--",
|
|
1693
|
+
"extract",
|
|
1694
|
+
"--from-clipboard",
|
|
1695
|
+
"--keep-input",
|
|
1696
|
+
"--allow-tests", // Add this flag to ensure test files are included
|
|
1697
|
+
])
|
|
1698
|
+
.output()
|
|
1699
|
+
.expect("Failed to execute command");
|
|
1700
|
+
|
|
1701
|
+
// Check that the command executed successfully
|
|
1702
|
+
assert!(output.status.success(), "Command failed to execute");
|
|
1703
|
+
|
|
1704
|
+
// Get the output as a string
|
|
1705
|
+
let stdout = String::from_utf8_lossy(&output.stdout);
|
|
1706
|
+
|
|
1707
|
+
// The output should contain the original input
|
|
1708
|
+
assert!(
|
|
1709
|
+
stdout.contains("Original Input:"),
|
|
1710
|
+
"Output should contain 'Original Input:' section"
|
|
1711
|
+
);
|
|
1712
|
+
assert!(
|
|
1713
|
+
stdout.contains(&input_content),
|
|
1714
|
+
"Output should contain the original input content"
|
|
1715
|
+
);
|
|
1716
|
+
}
|
|
1717
|
+
|
|
1718
|
+
#[test]
|
|
1719
|
+
fn test_extract_unsupported_file_type_symbol() {
|
|
1720
|
+
use tempfile::TempDir;
|
|
1721
|
+
|
|
1722
|
+
// Create a temporary Terraform file (unsupported by tree-sitter)
|
|
1723
|
+
let temp_dir = TempDir::new().expect("Failed to create temp dir");
|
|
1724
|
+
let file_path = temp_dir.path().join("main.tf");
|
|
1725
|
+
let content = r#"# Terraform configuration
|
|
1726
|
+
resource "aws_instance" "example" {
|
|
1727
|
+
ami = "ami-0c55b159cbfafe1f0"
|
|
1728
|
+
instance_type = "t2.micro"
|
|
1729
|
+
|
|
1730
|
+
tags = {
|
|
1731
|
+
Name = "ExampleInstance"
|
|
1732
|
+
}
|
|
1733
|
+
}
|
|
1734
|
+
|
|
1735
|
+
output "instance_id" {
|
|
1736
|
+
value = aws_instance.example.id
|
|
1737
|
+
}
|
|
1738
|
+
"#;
|
|
1739
|
+
fs::write(&file_path, content).unwrap();
|
|
1740
|
+
|
|
1741
|
+
// Test extracting a symbol from an unsupported file type
|
|
1742
|
+
// Should return the full file content as fallback
|
|
1743
|
+
let result = process_file_for_extraction(
|
|
1744
|
+
&file_path,
|
|
1745
|
+
None, // start_line
|
|
1746
|
+
None, // end_line
|
|
1747
|
+
Some("aws_instance"), // symbol
|
|
1748
|
+
false, // allow_tests
|
|
1749
|
+
0, // context_lines
|
|
1750
|
+
None, // specific_line_numbers
|
|
1751
|
+
false, // symbols
|
|
1752
|
+
)
|
|
1753
|
+
.unwrap();
|
|
1754
|
+
|
|
1755
|
+
// Should return full file content as fallback
|
|
1756
|
+
assert_eq!(
|
|
1757
|
+
result.node_type, "file",
|
|
1758
|
+
"Should return file type for unsupported language"
|
|
1759
|
+
);
|
|
1760
|
+
assert_eq!(result.code, content, "Should return full file content");
|
|
1761
|
+
assert_eq!(result.lines, (1, 13), "Should return all lines");
|
|
1762
|
+
}
|
|
1763
|
+
|
|
1764
|
+
#[test]
|
|
1765
|
+
fn test_extract_unsupported_file_type_lines() {
|
|
1766
|
+
use tempfile::TempDir;
|
|
1767
|
+
|
|
1768
|
+
// Create a temporary YAML file (another unsupported type)
|
|
1769
|
+
let temp_dir = TempDir::new().expect("Failed to create temp dir");
|
|
1770
|
+
let file_path = temp_dir.path().join("config.yml");
|
|
1771
|
+
let content = r#"version: '3'
|
|
1772
|
+
services:
|
|
1773
|
+
web:
|
|
1774
|
+
image: nginx:latest
|
|
1775
|
+
ports:
|
|
1776
|
+
- "80:80"
|
|
1777
|
+
database:
|
|
1778
|
+
image: postgres:13
|
|
1779
|
+
environment:
|
|
1780
|
+
POSTGRES_PASSWORD: secret
|
|
1781
|
+
"#;
|
|
1782
|
+
fs::write(&file_path, content).unwrap();
|
|
1783
|
+
|
|
1784
|
+
// Test extracting specific lines from an unsupported file type
|
|
1785
|
+
let result = process_file_for_extraction(
|
|
1786
|
+
&file_path,
|
|
1787
|
+
Some(3), // start_line
|
|
1788
|
+
Some(6), // end_line
|
|
1789
|
+
None, // symbol
|
|
1790
|
+
false, // allow_tests
|
|
1791
|
+
0, // context_lines
|
|
1792
|
+
None, // specific_line_numbers
|
|
1793
|
+
false, // symbols
|
|
1794
|
+
)
|
|
1795
|
+
.unwrap();
|
|
1796
|
+
|
|
1797
|
+
// Should return the requested lines
|
|
1798
|
+
assert_eq!(result.lines, (3, 6), "Should return requested line range");
|
|
1799
|
+
assert!(result.code.contains("web:"), "Should contain web service");
|
|
1800
|
+
assert!(
|
|
1801
|
+
result.code.contains("image: nginx"),
|
|
1802
|
+
"Should contain nginx image"
|
|
1803
|
+
);
|
|
1804
|
+
assert!(result.code.contains("80:80"), "Should contain port mapping");
|
|
1805
|
+
}
|
|
1806
|
+
|
|
1807
|
+
#[test]
|
|
1808
|
+
fn test_extract_cli_unsupported_file_type() {
|
|
1809
|
+
use tempfile::TempDir;
|
|
1810
|
+
|
|
1811
|
+
let temp_dir = TempDir::new().expect("Failed to create temp dir");
|
|
1812
|
+
let file_path = temp_dir.path().join("data.jsonl");
|
|
1813
|
+
let content = r#"{"id": 1, "name": "Alice", "age": 30}
|
|
1814
|
+
{"id": 2, "name": "Bob", "age": 25}
|
|
1815
|
+
{"id": 3, "name": "Charlie", "age": 35}
|
|
1816
|
+
"#;
|
|
1817
|
+
fs::write(&file_path, content).unwrap();
|
|
1818
|
+
|
|
1819
|
+
let project_dir = std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR"));
|
|
1820
|
+
|
|
1821
|
+
// Run the extract command on an unsupported file type
|
|
1822
|
+
let output = Command::new("cargo")
|
|
1823
|
+
.args([
|
|
1824
|
+
"run",
|
|
1825
|
+
"--manifest-path",
|
|
1826
|
+
project_dir.join("Cargo.toml").to_string_lossy().as_ref(),
|
|
1827
|
+
"--",
|
|
1828
|
+
"extract",
|
|
1829
|
+
&format!("{}:2", file_path.to_string_lossy()),
|
|
1830
|
+
])
|
|
1831
|
+
.output()
|
|
1832
|
+
.expect("Failed to execute command");
|
|
1833
|
+
|
|
1834
|
+
// Should succeed even with unsupported file type
|
|
1835
|
+
assert!(
|
|
1836
|
+
output.status.success(),
|
|
1837
|
+
"Command should succeed for unsupported file type. Stderr: {}",
|
|
1838
|
+
String::from_utf8_lossy(&output.stderr)
|
|
1839
|
+
);
|
|
1840
|
+
|
|
1841
|
+
let stdout = String::from_utf8_lossy(&output.stdout);
|
|
1842
|
+
|
|
1843
|
+
// Should contain the requested line
|
|
1844
|
+
assert!(
|
|
1845
|
+
stdout.contains("Bob"),
|
|
1846
|
+
"Output should contain the second line with Bob"
|
|
1847
|
+
);
|
|
1848
|
+
}
|