@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,1268 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Unit tests for schemaUtils module
|
|
3
|
+
* Tests JSON and Mermaid validation functionality
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { describe, test, expect, beforeEach } from '@jest/globals';
|
|
7
|
+
import {
|
|
8
|
+
cleanSchemaResponse,
|
|
9
|
+
validateJsonResponse,
|
|
10
|
+
validateXmlResponse,
|
|
11
|
+
processSchemaResponse,
|
|
12
|
+
isJsonSchema,
|
|
13
|
+
createJsonCorrectionPrompt,
|
|
14
|
+
isMermaidSchema,
|
|
15
|
+
extractMermaidFromMarkdown,
|
|
16
|
+
validateMermaidDiagram,
|
|
17
|
+
validateMermaidResponse,
|
|
18
|
+
createMermaidCorrectionPrompt,
|
|
19
|
+
generateExampleFromSchema
|
|
20
|
+
} from '../../src/agent/schemaUtils.js';
|
|
21
|
+
|
|
22
|
+
describe('Schema Utilities', () => {
|
|
23
|
+
describe('cleanSchemaResponse', () => {
|
|
24
|
+
test('should handle null/undefined input', () => {
|
|
25
|
+
expect(cleanSchemaResponse(null)).toBeNull();
|
|
26
|
+
expect(cleanSchemaResponse(undefined)).toBeUndefined();
|
|
27
|
+
expect(cleanSchemaResponse('')).toBe('');
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
test('should handle non-string input', () => {
|
|
31
|
+
expect(cleanSchemaResponse(123)).toBe(123);
|
|
32
|
+
expect(cleanSchemaResponse({})).toEqual({});
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
test('should extract JSON from markdown code blocks when response starts with {', () => {
|
|
36
|
+
const input = '```json\n{"test": "value"}\n```';
|
|
37
|
+
const expected = '{"test": "value"}';
|
|
38
|
+
expect(cleanSchemaResponse(input)).toBe(expected);
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
test('should extract JSON from markdown code blocks when response starts with [', () => {
|
|
42
|
+
const input = '```json\n[{"test": "value"}]\n```';
|
|
43
|
+
const expected = '[{"test": "value"}]';
|
|
44
|
+
expect(cleanSchemaResponse(input)).toBe(expected);
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
test('should extract JSON boundaries correctly with multiple brackets', () => {
|
|
48
|
+
const input = '```json\n{"nested": {"array": [1, 2, 3]}}\n```';
|
|
49
|
+
const expected = '{"nested": {"array": [1, 2, 3]}}';
|
|
50
|
+
expect(cleanSchemaResponse(input)).toBe(expected);
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
test('should return original input when not starting with JSON brackets', () => {
|
|
54
|
+
const input = '```xml\n<root>test</root>\n```';
|
|
55
|
+
expect(cleanSchemaResponse(input)).toBe(input); // Returns unchanged
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
test('should return original input for non-JSON backtick content', () => {
|
|
59
|
+
const input = '`some text content`';
|
|
60
|
+
expect(cleanSchemaResponse(input)).toBe(input); // Returns unchanged
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
test('should handle JSON with surrounding whitespace and markdown', () => {
|
|
64
|
+
const input = ' ```json\n{"test": "value"}\n``` ';
|
|
65
|
+
const expected = '{"test": "value"}';
|
|
66
|
+
expect(cleanSchemaResponse(input)).toBe(expected);
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
test('should handle direct JSON input without markdown', () => {
|
|
70
|
+
const input = '{"test": "value"}';
|
|
71
|
+
const expected = '{"test": "value"}';
|
|
72
|
+
expect(cleanSchemaResponse(input)).toBe(expected);
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
test('should handle array JSON input without markdown', () => {
|
|
76
|
+
const input = '[1, 2, 3]';
|
|
77
|
+
const expected = '[1, 2, 3]';
|
|
78
|
+
expect(cleanSchemaResponse(input)).toBe(expected);
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
test('should not extract JSON from text with surrounding content', () => {
|
|
82
|
+
const input = 'This is some text with {"json": "inside"}';
|
|
83
|
+
// Should return original since JSON has text before/after it
|
|
84
|
+
// This prevents false positives like extracting {{ pr.title }} from markdown
|
|
85
|
+
expect(cleanSchemaResponse(input)).toBe(input);
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
test('should return original for text with too much content before JSON', () => {
|
|
89
|
+
const input = 'Line 1\nLine 2\nLine 3\nLine 4\nMany lines of text that should prevent extraction {"json": "inside"}';
|
|
90
|
+
// Should return original since there are too many lines before the JSON
|
|
91
|
+
expect(cleanSchemaResponse(input)).toBe(input);
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
test('should handle empty JSON object', () => {
|
|
95
|
+
const input = '{}';
|
|
96
|
+
const expected = '{}';
|
|
97
|
+
expect(cleanSchemaResponse(input)).toBe(expected);
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
test('should handle empty JSON array', () => {
|
|
101
|
+
const input = '[]';
|
|
102
|
+
const expected = '[]';
|
|
103
|
+
expect(cleanSchemaResponse(input)).toBe(expected);
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
// New tests for enhanced JSON detection after code blocks
|
|
107
|
+
test('should extract JSON from code blocks with various patterns', () => {
|
|
108
|
+
const testCases = [
|
|
109
|
+
{
|
|
110
|
+
input: '```json\n{"test": "value"}\n```',
|
|
111
|
+
expected: '{"test": "value"}',
|
|
112
|
+
description: 'standard json code block'
|
|
113
|
+
},
|
|
114
|
+
{
|
|
115
|
+
input: '```\n{"test": "value"}\n```',
|
|
116
|
+
expected: '{"test": "value"}',
|
|
117
|
+
description: 'code block without language specifier'
|
|
118
|
+
},
|
|
119
|
+
{
|
|
120
|
+
input: '`{"test": "value"}`',
|
|
121
|
+
expected: '{"test": "value"}',
|
|
122
|
+
description: 'single backtick JSON'
|
|
123
|
+
}
|
|
124
|
+
];
|
|
125
|
+
|
|
126
|
+
testCases.forEach(({ input, expected, description }) => {
|
|
127
|
+
expect(cleanSchemaResponse(input)).toBe(expected);
|
|
128
|
+
});
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
test('should handle code blocks with immediate JSON start', () => {
|
|
132
|
+
const input = '```json\n{';
|
|
133
|
+
const remaining = '"test": "value", "nested": {"array": [1, 2, 3]}}';
|
|
134
|
+
const fullInput = input + remaining;
|
|
135
|
+
|
|
136
|
+
expect(cleanSchemaResponse(fullInput)).toBe('{' + remaining);
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
test('should handle code blocks with array JSON', () => {
|
|
140
|
+
const input = '```json\n[{"item": 1}, {"item": 2}]```';
|
|
141
|
+
const expected = '[{"item": 1}, {"item": 2}]';
|
|
142
|
+
expect(cleanSchemaResponse(input)).toBe(expected);
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
test('should extract JSON with proper bracket counting', () => {
|
|
146
|
+
const input = '```json\n{"outer": {"inner": {"deep": [1, 2, {"nested": true}]}}}\n```';
|
|
147
|
+
const expected = '{"outer": {"inner": {"deep": [1, 2, {"nested": true}]}}}';
|
|
148
|
+
expect(cleanSchemaResponse(input)).toBe(expected);
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
test('should handle code blocks with whitespace after marker', () => {
|
|
152
|
+
const input = '```json \n {"test": "value"} \n```';
|
|
153
|
+
const expected = '{"test": "value"}';
|
|
154
|
+
expect(cleanSchemaResponse(input)).toBe(expected);
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
test('should handle incomplete code blocks gracefully', () => {
|
|
158
|
+
const input = '```json\n{"test": "incomplete"';
|
|
159
|
+
// Should fall back to boundary detection
|
|
160
|
+
expect(cleanSchemaResponse(input)).toBe(input);
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
test('should prioritize code block extraction over boundary detection', () => {
|
|
164
|
+
const input = 'Some text {"not": "this"} ```json\n{"extract": "this"}\n```';
|
|
165
|
+
const expected = '{"extract": "this"}';
|
|
166
|
+
expect(cleanSchemaResponse(input)).toBe(expected);
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
test('should handle mixed bracket types in code blocks', () => {
|
|
170
|
+
const input = '```json\n[{"objects": [1, 2]}, {"more": {"nested": true}}]\n```';
|
|
171
|
+
const expected = '[{"objects": [1, 2]}, {"more": {"nested": true}}]';
|
|
172
|
+
expect(cleanSchemaResponse(input)).toBe(expected);
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
test('should not extract JSON when embedded in surrounding text', () => {
|
|
176
|
+
const input = 'Here is some JSON: {"test": "value"} that should be extracted';
|
|
177
|
+
// Should return original since JSON has text before and after it
|
|
178
|
+
// This prevents extracting fragments like {{ pr.title }} from content
|
|
179
|
+
expect(cleanSchemaResponse(input)).toBe(input);
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
test('should not extract JSON when text precedes it', () => {
|
|
183
|
+
const input = 'Result:\n{"test": "value"}';
|
|
184
|
+
// Should return original since there's text before the JSON
|
|
185
|
+
expect(cleanSchemaResponse(input)).toBe(input);
|
|
186
|
+
});
|
|
187
|
+
|
|
188
|
+
test('should extract JSON from code block after correction prompt (mermaid-style fix)', () => {
|
|
189
|
+
// This is the exact pattern we see when LLM responds to correction prompts
|
|
190
|
+
// with ```json blocks instead of raw JSON
|
|
191
|
+
const input = '```json\n{\n "issues": [\n {\n "file": "test.js",\n "line": 1\n }\n ]\n}\n```';
|
|
192
|
+
const result = cleanSchemaResponse(input);
|
|
193
|
+
|
|
194
|
+
// Should extract the JSON content without the code block markers
|
|
195
|
+
expect(result).not.toContain('```');
|
|
196
|
+
expect(result).toContain('"issues"');
|
|
197
|
+
|
|
198
|
+
// Verify it can be parsed
|
|
199
|
+
expect(() => JSON.parse(result)).not.toThrow();
|
|
200
|
+
const parsed = JSON.parse(result);
|
|
201
|
+
expect(parsed.issues).toBeDefined();
|
|
202
|
+
expect(Array.isArray(parsed.issues)).toBe(true);
|
|
203
|
+
});
|
|
204
|
+
|
|
205
|
+
test('should extract multiline JSON from ```json blocks', () => {
|
|
206
|
+
const input = '```json\n{\n "key": "value",\n "nested": {\n "array": [1, 2, 3]\n }\n}\n```';
|
|
207
|
+
const result = cleanSchemaResponse(input);
|
|
208
|
+
|
|
209
|
+
expect(result).not.toContain('```');
|
|
210
|
+
const parsed = JSON.parse(result);
|
|
211
|
+
expect(parsed.key).toBe('value');
|
|
212
|
+
expect(parsed.nested.array).toEqual([1, 2, 3]);
|
|
213
|
+
});
|
|
214
|
+
});
|
|
215
|
+
|
|
216
|
+
describe('validateJsonResponse', () => {
|
|
217
|
+
test('should validate correct JSON', () => {
|
|
218
|
+
const result = validateJsonResponse('{"test": "value"}');
|
|
219
|
+
expect(result.isValid).toBe(true);
|
|
220
|
+
expect(result.parsed).toEqual({ test: "value" });
|
|
221
|
+
});
|
|
222
|
+
|
|
223
|
+
test('should validate JSON arrays', () => {
|
|
224
|
+
const result = validateJsonResponse('[1, 2, 3]');
|
|
225
|
+
expect(result.isValid).toBe(true);
|
|
226
|
+
expect(result.parsed).toEqual([1, 2, 3]);
|
|
227
|
+
});
|
|
228
|
+
|
|
229
|
+
test('should validate primitive JSON values', () => {
|
|
230
|
+
expect(validateJsonResponse('null').isValid).toBe(true);
|
|
231
|
+
expect(validateJsonResponse('42').isValid).toBe(true);
|
|
232
|
+
expect(validateJsonResponse('"string"').isValid).toBe(true);
|
|
233
|
+
expect(validateJsonResponse('true').isValid).toBe(true);
|
|
234
|
+
});
|
|
235
|
+
|
|
236
|
+
test('should reject invalid JSON', () => {
|
|
237
|
+
const result = validateJsonResponse('{"test": value}'); // Missing quotes
|
|
238
|
+
expect(result.isValid).toBe(false);
|
|
239
|
+
expect(result.error).toBeDefined();
|
|
240
|
+
});
|
|
241
|
+
|
|
242
|
+
test('should reject incomplete JSON', () => {
|
|
243
|
+
const result = validateJsonResponse('{"test":');
|
|
244
|
+
expect(result.isValid).toBe(false);
|
|
245
|
+
expect(result.error).toBeDefined();
|
|
246
|
+
});
|
|
247
|
+
|
|
248
|
+
test('should handle empty input', () => {
|
|
249
|
+
const result = validateJsonResponse('');
|
|
250
|
+
expect(result.isValid).toBe(false);
|
|
251
|
+
expect(result.error).toBeDefined();
|
|
252
|
+
});
|
|
253
|
+
|
|
254
|
+
test('should handle complex nested JSON', () => {
|
|
255
|
+
const complex = '{"nested": {"array": [1, {"deep": true}], "null": null}}';
|
|
256
|
+
const result = validateJsonResponse(complex);
|
|
257
|
+
expect(result.isValid).toBe(true);
|
|
258
|
+
expect(result.parsed.nested.array[1].deep).toBe(true);
|
|
259
|
+
});
|
|
260
|
+
|
|
261
|
+
// Schema validation tests
|
|
262
|
+
test('should validate JSON against schema with required fields', () => {
|
|
263
|
+
const schema = {
|
|
264
|
+
type: 'object',
|
|
265
|
+
properties: {
|
|
266
|
+
name: { type: 'string' },
|
|
267
|
+
age: { type: 'number' }
|
|
268
|
+
},
|
|
269
|
+
required: ['name', 'age']
|
|
270
|
+
};
|
|
271
|
+
|
|
272
|
+
const validJson = '{"name": "John", "age": 30}';
|
|
273
|
+
const result = validateJsonResponse(validJson, { schema });
|
|
274
|
+
expect(result.isValid).toBe(true);
|
|
275
|
+
expect(result.parsed).toEqual({ name: 'John', age: 30 });
|
|
276
|
+
});
|
|
277
|
+
|
|
278
|
+
test('should reject JSON missing required fields', () => {
|
|
279
|
+
const schema = {
|
|
280
|
+
type: 'object',
|
|
281
|
+
properties: {
|
|
282
|
+
name: { type: 'string' },
|
|
283
|
+
age: { type: 'number' }
|
|
284
|
+
},
|
|
285
|
+
required: ['name', 'age']
|
|
286
|
+
};
|
|
287
|
+
|
|
288
|
+
const invalidJson = '{"name": "John"}';
|
|
289
|
+
const result = validateJsonResponse(invalidJson, { schema });
|
|
290
|
+
expect(result.isValid).toBe(false);
|
|
291
|
+
expect(result.error).toBe('Schema validation failed');
|
|
292
|
+
expect(result.schemaErrors).toBeDefined();
|
|
293
|
+
expect(result.formattedErrors).toBeDefined();
|
|
294
|
+
expect(result.formattedErrors.some(e => e.includes('age'))).toBe(true);
|
|
295
|
+
});
|
|
296
|
+
|
|
297
|
+
test('should reject JSON with wrong field types', () => {
|
|
298
|
+
const schema = {
|
|
299
|
+
type: 'object',
|
|
300
|
+
properties: {
|
|
301
|
+
name: { type: 'string' },
|
|
302
|
+
age: { type: 'number' }
|
|
303
|
+
}
|
|
304
|
+
};
|
|
305
|
+
|
|
306
|
+
const invalidJson = '{"name": "John", "age": "thirty"}';
|
|
307
|
+
const result = validateJsonResponse(invalidJson, { schema });
|
|
308
|
+
expect(result.isValid).toBe(false);
|
|
309
|
+
expect(result.error).toBe('Schema validation failed');
|
|
310
|
+
expect(result.formattedErrors.some(e => e.includes('number') || e.includes('age'))).toBe(true);
|
|
311
|
+
});
|
|
312
|
+
|
|
313
|
+
test('should reject JSON with additional properties when not allowed', () => {
|
|
314
|
+
const schema = {
|
|
315
|
+
type: 'object',
|
|
316
|
+
properties: {
|
|
317
|
+
name: { type: 'string' }
|
|
318
|
+
},
|
|
319
|
+
additionalProperties: false
|
|
320
|
+
};
|
|
321
|
+
|
|
322
|
+
const invalidJson = '{"name": "John", "extra": "field"}';
|
|
323
|
+
const result = validateJsonResponse(invalidJson, { schema });
|
|
324
|
+
expect(result.isValid).toBe(false);
|
|
325
|
+
expect(result.error).toBe('Schema validation failed');
|
|
326
|
+
expect(result.formattedErrors.some(e => e.includes('extra'))).toBe(true);
|
|
327
|
+
});
|
|
328
|
+
|
|
329
|
+
test('should allow JSON with additional properties when allowed', () => {
|
|
330
|
+
const schema = {
|
|
331
|
+
type: 'object',
|
|
332
|
+
properties: {
|
|
333
|
+
name: { type: 'string' }
|
|
334
|
+
},
|
|
335
|
+
additionalProperties: true
|
|
336
|
+
};
|
|
337
|
+
|
|
338
|
+
const validJson = '{"name": "John", "extra": "field"}';
|
|
339
|
+
const result = validateJsonResponse(validJson, { schema });
|
|
340
|
+
expect(result.isValid).toBe(true);
|
|
341
|
+
expect(result.parsed).toEqual({ name: 'John', extra: 'field' });
|
|
342
|
+
});
|
|
343
|
+
|
|
344
|
+
test('should validate arrays against schema', () => {
|
|
345
|
+
const schema = {
|
|
346
|
+
type: 'array',
|
|
347
|
+
items: {
|
|
348
|
+
type: 'object',
|
|
349
|
+
properties: {
|
|
350
|
+
id: { type: 'number' },
|
|
351
|
+
name: { type: 'string' }
|
|
352
|
+
},
|
|
353
|
+
required: ['id']
|
|
354
|
+
}
|
|
355
|
+
};
|
|
356
|
+
|
|
357
|
+
const validJson = '[{"id": 1, "name": "Item 1"}, {"id": 2}]';
|
|
358
|
+
const result = validateJsonResponse(validJson, { schema });
|
|
359
|
+
expect(result.isValid).toBe(true);
|
|
360
|
+
});
|
|
361
|
+
|
|
362
|
+
test('should reject invalid array items', () => {
|
|
363
|
+
const schema = {
|
|
364
|
+
type: 'array',
|
|
365
|
+
items: {
|
|
366
|
+
type: 'object',
|
|
367
|
+
properties: {
|
|
368
|
+
id: { type: 'number' }
|
|
369
|
+
},
|
|
370
|
+
required: ['id']
|
|
371
|
+
}
|
|
372
|
+
};
|
|
373
|
+
|
|
374
|
+
const invalidJson = '[{"id": 1}, {"name": "missing id"}]';
|
|
375
|
+
const result = validateJsonResponse(invalidJson, { schema });
|
|
376
|
+
expect(result.isValid).toBe(false);
|
|
377
|
+
expect(result.schemaErrors).toBeDefined();
|
|
378
|
+
});
|
|
379
|
+
|
|
380
|
+
test('should accept schema as string', () => {
|
|
381
|
+
const schemaString = '{"type": "object", "properties": {"name": {"type": "string"}}, "required": ["name"]}';
|
|
382
|
+
const validJson = '{"name": "John"}';
|
|
383
|
+
const result = validateJsonResponse(validJson, { schema: schemaString });
|
|
384
|
+
expect(result.isValid).toBe(true);
|
|
385
|
+
});
|
|
386
|
+
|
|
387
|
+
test('should handle invalid schema gracefully', () => {
|
|
388
|
+
const invalidSchema = 'not valid json';
|
|
389
|
+
const validJson = '{"name": "John"}';
|
|
390
|
+
const result = validateJsonResponse(validJson, { schema: invalidSchema });
|
|
391
|
+
expect(result.isValid).toBe(false);
|
|
392
|
+
expect(result.error).toBe('Invalid schema provided');
|
|
393
|
+
expect(result.schemaError).toBeDefined();
|
|
394
|
+
});
|
|
395
|
+
|
|
396
|
+
test('should validate nested object schemas', () => {
|
|
397
|
+
const schema = {
|
|
398
|
+
type: 'object',
|
|
399
|
+
properties: {
|
|
400
|
+
user: {
|
|
401
|
+
type: 'object',
|
|
402
|
+
properties: {
|
|
403
|
+
name: { type: 'string' },
|
|
404
|
+
email: { type: 'string' }
|
|
405
|
+
},
|
|
406
|
+
required: ['name'],
|
|
407
|
+
additionalProperties: false
|
|
408
|
+
}
|
|
409
|
+
},
|
|
410
|
+
required: ['user']
|
|
411
|
+
};
|
|
412
|
+
|
|
413
|
+
const validJson = '{"user": {"name": "John", "email": "john@example.com"}}';
|
|
414
|
+
const result = validateJsonResponse(validJson, { schema });
|
|
415
|
+
expect(result.isValid).toBe(true);
|
|
416
|
+
});
|
|
417
|
+
|
|
418
|
+
test('should provide detailed error messages for nested violations', () => {
|
|
419
|
+
const schema = {
|
|
420
|
+
type: 'object',
|
|
421
|
+
properties: {
|
|
422
|
+
user: {
|
|
423
|
+
type: 'object',
|
|
424
|
+
properties: {
|
|
425
|
+
name: { type: 'string' }
|
|
426
|
+
},
|
|
427
|
+
required: ['name'],
|
|
428
|
+
additionalProperties: false
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
};
|
|
432
|
+
|
|
433
|
+
const invalidJson = '{"user": {"age": 30}}';
|
|
434
|
+
const result = validateJsonResponse(invalidJson, { schema });
|
|
435
|
+
expect(result.isValid).toBe(false);
|
|
436
|
+
expect(result.formattedErrors).toBeDefined();
|
|
437
|
+
expect(result.errorSummary).toContain('Schema validation failed');
|
|
438
|
+
});
|
|
439
|
+
|
|
440
|
+
test('should work without schema (backward compatibility)', () => {
|
|
441
|
+
const validJson = '{"name": "John", "extra": "field"}';
|
|
442
|
+
const result = validateJsonResponse(validJson);
|
|
443
|
+
expect(result.isValid).toBe(true);
|
|
444
|
+
expect(result.parsed).toEqual({ name: 'John', extra: 'field' });
|
|
445
|
+
});
|
|
446
|
+
|
|
447
|
+
// Strict schema mode tests (automatic additionalProperties enforcement)
|
|
448
|
+
test('should automatically enforce additionalProperties on nested objects in strict mode', () => {
|
|
449
|
+
const schema = {
|
|
450
|
+
type: 'object',
|
|
451
|
+
properties: {
|
|
452
|
+
user: {
|
|
453
|
+
type: 'object',
|
|
454
|
+
properties: {
|
|
455
|
+
name: { type: 'string' }
|
|
456
|
+
}
|
|
457
|
+
// No additionalProperties specified - should be auto-added
|
|
458
|
+
}
|
|
459
|
+
}
|
|
460
|
+
// No additionalProperties on root either - should be auto-added
|
|
461
|
+
};
|
|
462
|
+
|
|
463
|
+
const invalidJson = '{"user": {"name": "John", "age": 30}}';
|
|
464
|
+
const result = validateJsonResponse(invalidJson, { schema, strictSchema: true });
|
|
465
|
+
|
|
466
|
+
expect(result.isValid).toBe(false);
|
|
467
|
+
expect(result.error).toBe('Schema validation failed');
|
|
468
|
+
// Should reject the extra 'age' field in nested object
|
|
469
|
+
expect(result.formattedErrors.some(e => e.includes('age'))).toBe(true);
|
|
470
|
+
});
|
|
471
|
+
|
|
472
|
+
test('should allow disabling strict mode to permit additional properties', () => {
|
|
473
|
+
const schema = {
|
|
474
|
+
type: 'object',
|
|
475
|
+
properties: {
|
|
476
|
+
user: {
|
|
477
|
+
type: 'object',
|
|
478
|
+
properties: {
|
|
479
|
+
name: { type: 'string' }
|
|
480
|
+
}
|
|
481
|
+
}
|
|
482
|
+
}
|
|
483
|
+
};
|
|
484
|
+
|
|
485
|
+
const jsonWithExtra = '{"user": {"name": "John", "age": 30}}';
|
|
486
|
+
const result = validateJsonResponse(jsonWithExtra, { schema, strictSchema: false });
|
|
487
|
+
|
|
488
|
+
// Should allow extra fields when strictSchema is disabled
|
|
489
|
+
expect(result.isValid).toBe(true);
|
|
490
|
+
expect(result.parsed.user.age).toBe(30);
|
|
491
|
+
});
|
|
492
|
+
|
|
493
|
+
test('should respect explicit additionalProperties: true even in strict mode', () => {
|
|
494
|
+
const schema = {
|
|
495
|
+
type: 'object',
|
|
496
|
+
properties: {
|
|
497
|
+
user: {
|
|
498
|
+
type: 'object',
|
|
499
|
+
properties: {
|
|
500
|
+
name: { type: 'string' }
|
|
501
|
+
},
|
|
502
|
+
additionalProperties: true // Explicitly allow
|
|
503
|
+
}
|
|
504
|
+
}
|
|
505
|
+
};
|
|
506
|
+
|
|
507
|
+
const jsonWithExtra = '{"user": {"name": "John", "age": 30}}';
|
|
508
|
+
const result = validateJsonResponse(jsonWithExtra, { schema, strictSchema: true });
|
|
509
|
+
|
|
510
|
+
// Should respect explicit additionalProperties: true
|
|
511
|
+
expect(result.isValid).toBe(true);
|
|
512
|
+
expect(result.parsed.user.age).toBe(30);
|
|
513
|
+
});
|
|
514
|
+
|
|
515
|
+
test('should enforce strict mode on deeply nested objects', () => {
|
|
516
|
+
const schema = {
|
|
517
|
+
type: 'object',
|
|
518
|
+
properties: {
|
|
519
|
+
level1: {
|
|
520
|
+
type: 'object',
|
|
521
|
+
properties: {
|
|
522
|
+
level2: {
|
|
523
|
+
type: 'object',
|
|
524
|
+
properties: {
|
|
525
|
+
level3: {
|
|
526
|
+
type: 'object',
|
|
527
|
+
properties: {
|
|
528
|
+
name: { type: 'string' }
|
|
529
|
+
}
|
|
530
|
+
}
|
|
531
|
+
}
|
|
532
|
+
}
|
|
533
|
+
}
|
|
534
|
+
}
|
|
535
|
+
}
|
|
536
|
+
};
|
|
537
|
+
|
|
538
|
+
const invalidJson = '{"level1": {"level2": {"level3": {"name": "test", "extra": "field"}}}}';
|
|
539
|
+
const result = validateJsonResponse(invalidJson, { schema, strictSchema: true });
|
|
540
|
+
|
|
541
|
+
expect(result.isValid).toBe(false);
|
|
542
|
+
expect(result.formattedErrors.some(e => e.includes('extra'))).toBe(true);
|
|
543
|
+
});
|
|
544
|
+
|
|
545
|
+
test('should enforce strict mode on array items', () => {
|
|
546
|
+
const schema = {
|
|
547
|
+
type: 'object',
|
|
548
|
+
properties: {
|
|
549
|
+
items: {
|
|
550
|
+
type: 'array',
|
|
551
|
+
items: {
|
|
552
|
+
type: 'object',
|
|
553
|
+
properties: {
|
|
554
|
+
id: { type: 'number' }
|
|
555
|
+
}
|
|
556
|
+
// No additionalProperties - should be enforced
|
|
557
|
+
}
|
|
558
|
+
}
|
|
559
|
+
}
|
|
560
|
+
};
|
|
561
|
+
|
|
562
|
+
const invalidJson = '{"items": [{"id": 1, "extra": "field"}]}';
|
|
563
|
+
const result = validateJsonResponse(invalidJson, { schema, strictSchema: true });
|
|
564
|
+
|
|
565
|
+
expect(result.isValid).toBe(false);
|
|
566
|
+
expect(result.formattedErrors.some(e => e.includes('extra'))).toBe(true);
|
|
567
|
+
});
|
|
568
|
+
|
|
569
|
+
test('strict mode should be enabled by default', () => {
|
|
570
|
+
const schema = {
|
|
571
|
+
type: 'object',
|
|
572
|
+
properties: {
|
|
573
|
+
name: { type: 'string' }
|
|
574
|
+
}
|
|
575
|
+
};
|
|
576
|
+
|
|
577
|
+
const invalidJson = '{"name": "John", "extra": "field"}';
|
|
578
|
+
// Don't specify strictSchema - should default to true
|
|
579
|
+
const result = validateJsonResponse(invalidJson, { schema });
|
|
580
|
+
|
|
581
|
+
expect(result.isValid).toBe(false);
|
|
582
|
+
expect(result.formattedErrors.some(e => e.includes('extra'))).toBe(true);
|
|
583
|
+
});
|
|
584
|
+
|
|
585
|
+
test('should enforce strict mode with oneOf schemas', () => {
|
|
586
|
+
const schema = {
|
|
587
|
+
type: 'object',
|
|
588
|
+
properties: {
|
|
589
|
+
data: {
|
|
590
|
+
oneOf: [
|
|
591
|
+
{
|
|
592
|
+
type: 'object',
|
|
593
|
+
properties: {
|
|
594
|
+
type: { type: 'string', const: 'user' },
|
|
595
|
+
name: { type: 'string' }
|
|
596
|
+
},
|
|
597
|
+
required: ['type', 'name']
|
|
598
|
+
},
|
|
599
|
+
{
|
|
600
|
+
type: 'object',
|
|
601
|
+
properties: {
|
|
602
|
+
type: { type: 'string', const: 'product' },
|
|
603
|
+
price: { type: 'number' }
|
|
604
|
+
},
|
|
605
|
+
required: ['type', 'price']
|
|
606
|
+
}
|
|
607
|
+
]
|
|
608
|
+
}
|
|
609
|
+
}
|
|
610
|
+
};
|
|
611
|
+
|
|
612
|
+
const invalidJson = '{"data": {"type": "user", "name": "John", "extra": "field"}}';
|
|
613
|
+
const result = validateJsonResponse(invalidJson, { schema, strictSchema: true });
|
|
614
|
+
|
|
615
|
+
expect(result.isValid).toBe(false);
|
|
616
|
+
expect(result.formattedErrors.some(e => e.includes('extra'))).toBe(true);
|
|
617
|
+
});
|
|
618
|
+
|
|
619
|
+
test('should enforce strict mode with anyOf schemas', () => {
|
|
620
|
+
const schema = {
|
|
621
|
+
type: 'object',
|
|
622
|
+
properties: {
|
|
623
|
+
value: {
|
|
624
|
+
anyOf: [
|
|
625
|
+
{
|
|
626
|
+
type: 'object',
|
|
627
|
+
properties: {
|
|
628
|
+
num: { type: 'number' }
|
|
629
|
+
}
|
|
630
|
+
},
|
|
631
|
+
{
|
|
632
|
+
type: 'object',
|
|
633
|
+
properties: {
|
|
634
|
+
str: { type: 'string' }
|
|
635
|
+
}
|
|
636
|
+
}
|
|
637
|
+
]
|
|
638
|
+
}
|
|
639
|
+
}
|
|
640
|
+
};
|
|
641
|
+
|
|
642
|
+
const invalidJson = '{"value": {"num": 42, "extra": "field"}}';
|
|
643
|
+
const result = validateJsonResponse(invalidJson, { schema, strictSchema: true });
|
|
644
|
+
|
|
645
|
+
expect(result.isValid).toBe(false);
|
|
646
|
+
expect(result.formattedErrors.some(e => e.includes('extra'))).toBe(true);
|
|
647
|
+
});
|
|
648
|
+
|
|
649
|
+
test('should enforce strict mode with allOf schemas', () => {
|
|
650
|
+
const schema = {
|
|
651
|
+
type: 'object',
|
|
652
|
+
properties: {
|
|
653
|
+
entity: {
|
|
654
|
+
allOf: [
|
|
655
|
+
{
|
|
656
|
+
type: 'object',
|
|
657
|
+
properties: {
|
|
658
|
+
id: { type: 'number' }
|
|
659
|
+
}
|
|
660
|
+
},
|
|
661
|
+
{
|
|
662
|
+
type: 'object',
|
|
663
|
+
properties: {
|
|
664
|
+
name: { type: 'string' }
|
|
665
|
+
}
|
|
666
|
+
}
|
|
667
|
+
]
|
|
668
|
+
}
|
|
669
|
+
}
|
|
670
|
+
};
|
|
671
|
+
|
|
672
|
+
const invalidJson = '{"entity": {"id": 1, "name": "Test", "extra": "field"}}';
|
|
673
|
+
const result = validateJsonResponse(invalidJson, { schema, strictSchema: true });
|
|
674
|
+
|
|
675
|
+
expect(result.isValid).toBe(false);
|
|
676
|
+
expect(result.formattedErrors.some(e => e.includes('extra'))).toBe(true);
|
|
677
|
+
});
|
|
678
|
+
|
|
679
|
+
test('should enforce strict mode with schema definitions', () => {
|
|
680
|
+
const schema = {
|
|
681
|
+
type: 'object',
|
|
682
|
+
definitions: {
|
|
683
|
+
address: {
|
|
684
|
+
type: 'object',
|
|
685
|
+
properties: {
|
|
686
|
+
street: { type: 'string' },
|
|
687
|
+
city: { type: 'string' }
|
|
688
|
+
}
|
|
689
|
+
}
|
|
690
|
+
},
|
|
691
|
+
properties: {
|
|
692
|
+
home: { $ref: '#/definitions/address' }
|
|
693
|
+
}
|
|
694
|
+
};
|
|
695
|
+
|
|
696
|
+
const invalidJson = '{"home": {"street": "123 Main St", "city": "NYC", "extra": "field"}}';
|
|
697
|
+
const result = validateJsonResponse(invalidJson, { schema, strictSchema: true });
|
|
698
|
+
|
|
699
|
+
expect(result.isValid).toBe(false);
|
|
700
|
+
expect(result.formattedErrors.some(e => e.includes('extra'))).toBe(true);
|
|
701
|
+
});
|
|
702
|
+
|
|
703
|
+
test('should enforce strict mode with $defs (JSON Schema 2019-09)', () => {
|
|
704
|
+
const schema = {
|
|
705
|
+
type: 'object',
|
|
706
|
+
$defs: {
|
|
707
|
+
person: {
|
|
708
|
+
type: 'object',
|
|
709
|
+
properties: {
|
|
710
|
+
name: { type: 'string' },
|
|
711
|
+
age: { type: 'number' }
|
|
712
|
+
}
|
|
713
|
+
}
|
|
714
|
+
},
|
|
715
|
+
properties: {
|
|
716
|
+
employee: { $ref: '#/$defs/person' }
|
|
717
|
+
}
|
|
718
|
+
};
|
|
719
|
+
|
|
720
|
+
const invalidJson = '{"employee": {"name": "John", "age": 30, "extra": "field"}}';
|
|
721
|
+
const result = validateJsonResponse(invalidJson, { schema, strictSchema: true });
|
|
722
|
+
|
|
723
|
+
expect(result.isValid).toBe(false);
|
|
724
|
+
expect(result.formattedErrors.some(e => e.includes('extra'))).toBe(true);
|
|
725
|
+
});
|
|
726
|
+
|
|
727
|
+
test('should enforce strict mode on array items with tuple validation', () => {
|
|
728
|
+
const schema = {
|
|
729
|
+
type: 'object',
|
|
730
|
+
properties: {
|
|
731
|
+
coordinates: {
|
|
732
|
+
type: 'array',
|
|
733
|
+
items: [
|
|
734
|
+
{
|
|
735
|
+
type: 'object',
|
|
736
|
+
properties: {
|
|
737
|
+
x: { type: 'number' }
|
|
738
|
+
}
|
|
739
|
+
},
|
|
740
|
+
{
|
|
741
|
+
type: 'object',
|
|
742
|
+
properties: {
|
|
743
|
+
y: { type: 'number' }
|
|
744
|
+
}
|
|
745
|
+
}
|
|
746
|
+
]
|
|
747
|
+
}
|
|
748
|
+
}
|
|
749
|
+
};
|
|
750
|
+
|
|
751
|
+
const invalidJson = '{"coordinates": [{"x": 10, "extra": "bad"}, {"y": 20}]}';
|
|
752
|
+
const result = validateJsonResponse(invalidJson, { schema, strictSchema: true });
|
|
753
|
+
|
|
754
|
+
expect(result.isValid).toBe(false);
|
|
755
|
+
expect(result.formattedErrors.some(e => e.includes('extra'))).toBe(true);
|
|
756
|
+
});
|
|
757
|
+
|
|
758
|
+
test('should handle schemas without type specified', () => {
|
|
759
|
+
const schema = {
|
|
760
|
+
properties: {
|
|
761
|
+
user: {
|
|
762
|
+
properties: {
|
|
763
|
+
name: { type: 'string' }
|
|
764
|
+
}
|
|
765
|
+
// No type: 'object' specified, but has properties
|
|
766
|
+
}
|
|
767
|
+
}
|
|
768
|
+
};
|
|
769
|
+
|
|
770
|
+
const invalidJson = '{"user": {"name": "John", "extra": "field"}}';
|
|
771
|
+
const result = validateJsonResponse(invalidJson, { schema, strictSchema: true });
|
|
772
|
+
|
|
773
|
+
// Without explicit type: 'object', strict mode won't add additionalProperties
|
|
774
|
+
// This is expected behavior - schemas should be well-formed
|
|
775
|
+
expect(result.isValid).toBe(true);
|
|
776
|
+
});
|
|
777
|
+
|
|
778
|
+
test('should not modify schemas with explicit additionalProperties: false', () => {
|
|
779
|
+
const schema = {
|
|
780
|
+
type: 'object',
|
|
781
|
+
properties: {
|
|
782
|
+
data: {
|
|
783
|
+
type: 'object',
|
|
784
|
+
properties: {
|
|
785
|
+
id: { type: 'number' }
|
|
786
|
+
},
|
|
787
|
+
additionalProperties: false // Already set
|
|
788
|
+
}
|
|
789
|
+
},
|
|
790
|
+
additionalProperties: false // Already set
|
|
791
|
+
};
|
|
792
|
+
|
|
793
|
+
const invalidJson = '{"data": {"id": 1, "extra": "field"}}';
|
|
794
|
+
const result = validateJsonResponse(invalidJson, { schema, strictSchema: true });
|
|
795
|
+
|
|
796
|
+
expect(result.isValid).toBe(false);
|
|
797
|
+
expect(result.formattedErrors.some(e => e.includes('extra'))).toBe(true);
|
|
798
|
+
});
|
|
799
|
+
|
|
800
|
+
// Enhanced error message tests
|
|
801
|
+
test('should provide crisp error messages with dot notation paths', () => {
|
|
802
|
+
const schema = {
|
|
803
|
+
type: 'object',
|
|
804
|
+
properties: {
|
|
805
|
+
user: {
|
|
806
|
+
type: 'object',
|
|
807
|
+
properties: {
|
|
808
|
+
profile: {
|
|
809
|
+
type: 'object',
|
|
810
|
+
properties: {
|
|
811
|
+
name: { type: 'string' }
|
|
812
|
+
},
|
|
813
|
+
required: ['name']
|
|
814
|
+
}
|
|
815
|
+
}
|
|
816
|
+
}
|
|
817
|
+
}
|
|
818
|
+
};
|
|
819
|
+
|
|
820
|
+
const invalidJson = '{"user": {"profile": {}}}';
|
|
821
|
+
const result = validateJsonResponse(invalidJson, { schema });
|
|
822
|
+
|
|
823
|
+
expect(result.isValid).toBe(false);
|
|
824
|
+
// Should use dot notation, not slashes
|
|
825
|
+
expect(result.formattedErrors[0]).toContain("at 'user.profile'");
|
|
826
|
+
expect(result.formattedErrors[0]).not.toContain('/user/profile');
|
|
827
|
+
// Should have actionable suggestion
|
|
828
|
+
expect(result.formattedErrors[0]).toContain("Missing required field 'name'");
|
|
829
|
+
expect(result.formattedErrors[0]).toContain("Add 'name' to this object");
|
|
830
|
+
});
|
|
831
|
+
|
|
832
|
+
test('should show actual values in type errors', () => {
|
|
833
|
+
const schema = {
|
|
834
|
+
type: 'object',
|
|
835
|
+
properties: {
|
|
836
|
+
age: { type: 'number' }
|
|
837
|
+
}
|
|
838
|
+
};
|
|
839
|
+
|
|
840
|
+
const invalidJson = '{"age": "thirty"}';
|
|
841
|
+
const result = validateJsonResponse(invalidJson, { schema });
|
|
842
|
+
|
|
843
|
+
expect(result.isValid).toBe(false);
|
|
844
|
+
expect(result.formattedErrors[0]).toContain('Wrong type');
|
|
845
|
+
expect(result.formattedErrors[0]).toContain('expected number');
|
|
846
|
+
expect(result.formattedErrors[0]).toContain('got string');
|
|
847
|
+
expect(result.formattedErrors[0]).toContain('value: "thirty"');
|
|
848
|
+
expect(result.formattedErrors[0]).toContain('Change value to number type');
|
|
849
|
+
});
|
|
850
|
+
|
|
851
|
+
test('should provide actionable suggestions for additional properties', () => {
|
|
852
|
+
const schema = {
|
|
853
|
+
type: 'object',
|
|
854
|
+
properties: {
|
|
855
|
+
name: { type: 'string' }
|
|
856
|
+
},
|
|
857
|
+
additionalProperties: false
|
|
858
|
+
};
|
|
859
|
+
|
|
860
|
+
const invalidJson = '{"name": "John", "extra": "field"}';
|
|
861
|
+
const result = validateJsonResponse(invalidJson, { schema });
|
|
862
|
+
|
|
863
|
+
expect(result.isValid).toBe(false);
|
|
864
|
+
expect(result.formattedErrors[0]).toContain("Extra field 'extra' is not allowed");
|
|
865
|
+
expect(result.formattedErrors[0]).toContain("Remove 'extra' or add it to the schema");
|
|
866
|
+
});
|
|
867
|
+
|
|
868
|
+
test('should show allowed values for enum violations', () => {
|
|
869
|
+
const schema = {
|
|
870
|
+
type: 'object',
|
|
871
|
+
properties: {
|
|
872
|
+
role: { type: 'string', enum: ['admin', 'user', 'guest'] }
|
|
873
|
+
}
|
|
874
|
+
};
|
|
875
|
+
|
|
876
|
+
const invalidJson = '{"role": "superadmin"}';
|
|
877
|
+
const result = validateJsonResponse(invalidJson, { schema });
|
|
878
|
+
|
|
879
|
+
expect(result.isValid).toBe(false);
|
|
880
|
+
expect(result.formattedErrors[0]).toContain('Invalid value "superadmin"');
|
|
881
|
+
expect(result.formattedErrors[0]).toContain('Allowed: "admin", "user", "guest"');
|
|
882
|
+
expect(result.formattedErrors[0]).toContain('Use one of the allowed values');
|
|
883
|
+
});
|
|
884
|
+
|
|
885
|
+
test('should show constraint details for range violations', () => {
|
|
886
|
+
const schema = {
|
|
887
|
+
type: 'object',
|
|
888
|
+
properties: {
|
|
889
|
+
age: { type: 'number', minimum: 0, maximum: 150 }
|
|
890
|
+
}
|
|
891
|
+
};
|
|
892
|
+
|
|
893
|
+
const invalidJson = '{"age": 200}';
|
|
894
|
+
const result = validateJsonResponse(invalidJson, { schema });
|
|
895
|
+
|
|
896
|
+
expect(result.isValid).toBe(false);
|
|
897
|
+
expect(result.formattedErrors[0]).toContain('at \'age\'');
|
|
898
|
+
expect(result.formattedErrors[0]).toContain('Value 200');
|
|
899
|
+
expect(result.formattedErrors[0]).toContain('150');
|
|
900
|
+
expect(result.formattedErrors[0]).toContain('Adjust value to meet constraint');
|
|
901
|
+
});
|
|
902
|
+
|
|
903
|
+
test('should show current length for string length violations', () => {
|
|
904
|
+
const schema = {
|
|
905
|
+
type: 'object',
|
|
906
|
+
properties: {
|
|
907
|
+
username: { type: 'string', minLength: 3, maxLength: 20 }
|
|
908
|
+
}
|
|
909
|
+
};
|
|
910
|
+
|
|
911
|
+
const invalidJson = '{"username": "ab"}';
|
|
912
|
+
const result = validateJsonResponse(invalidJson, { schema });
|
|
913
|
+
|
|
914
|
+
expect(result.isValid).toBe(false);
|
|
915
|
+
expect(result.formattedErrors[0]).toContain('String length');
|
|
916
|
+
expect(result.formattedErrors[0]).toContain('current: 2');
|
|
917
|
+
expect(result.formattedErrors[0]).toContain('minLength: 3');
|
|
918
|
+
expect(result.formattedErrors[0]).toContain('Adjust string length');
|
|
919
|
+
});
|
|
920
|
+
|
|
921
|
+
test('should handle root-level errors clearly', () => {
|
|
922
|
+
const schema = {
|
|
923
|
+
type: 'object',
|
|
924
|
+
properties: {
|
|
925
|
+
name: { type: 'string' }
|
|
926
|
+
},
|
|
927
|
+
additionalProperties: false
|
|
928
|
+
};
|
|
929
|
+
|
|
930
|
+
const invalidJson = '{"extra": "field"}';
|
|
931
|
+
const result = validateJsonResponse(invalidJson, { schema });
|
|
932
|
+
|
|
933
|
+
expect(result.isValid).toBe(false);
|
|
934
|
+
expect(result.formattedErrors[0]).toContain('at \'<root>\'');
|
|
935
|
+
expect(result.formattedErrors[0]).toContain("Extra field 'extra' is not allowed");
|
|
936
|
+
});
|
|
937
|
+
});
|
|
938
|
+
|
|
939
|
+
describe('validateXmlResponse', () => {
|
|
940
|
+
test('should validate basic XML', () => {
|
|
941
|
+
const result = validateXmlResponse('<root>test</root>');
|
|
942
|
+
expect(result.isValid).toBe(true);
|
|
943
|
+
});
|
|
944
|
+
|
|
945
|
+
test('should validate XML with attributes', () => {
|
|
946
|
+
const result = validateXmlResponse('<root attr="value">test</root>');
|
|
947
|
+
expect(result.isValid).toBe(true);
|
|
948
|
+
});
|
|
949
|
+
|
|
950
|
+
test('should validate self-closing tags', () => {
|
|
951
|
+
const result = validateXmlResponse('<root><item/></root>');
|
|
952
|
+
expect(result.isValid).toBe(true);
|
|
953
|
+
});
|
|
954
|
+
|
|
955
|
+
test('should reject non-XML content', () => {
|
|
956
|
+
const result = validateXmlResponse('just plain text');
|
|
957
|
+
expect(result.isValid).toBe(false);
|
|
958
|
+
expect(result.error).toBe('No XML tags found');
|
|
959
|
+
});
|
|
960
|
+
|
|
961
|
+
test('should reject empty input', () => {
|
|
962
|
+
const result = validateXmlResponse('');
|
|
963
|
+
expect(result.isValid).toBe(false);
|
|
964
|
+
expect(result.error).toBe('No XML tags found');
|
|
965
|
+
});
|
|
966
|
+
});
|
|
967
|
+
|
|
968
|
+
describe('isJsonSchema', () => {
|
|
969
|
+
test('should detect JSON object schemas', () => {
|
|
970
|
+
expect(isJsonSchema('{"type": "object"}')).toBe(true);
|
|
971
|
+
expect(isJsonSchema('{ "properties": {} }')).toBe(true);
|
|
972
|
+
expect(isJsonSchema('{"test": "value"}')).toBe(true);
|
|
973
|
+
});
|
|
974
|
+
|
|
975
|
+
test('should detect JSON array schemas', () => {
|
|
976
|
+
expect(isJsonSchema('[{"name": "string"}]')).toBe(true);
|
|
977
|
+
expect(isJsonSchema('[]')).toBe(true);
|
|
978
|
+
});
|
|
979
|
+
|
|
980
|
+
test('should detect JSON content-type indicators', () => {
|
|
981
|
+
expect(isJsonSchema('application/json')).toBe(true);
|
|
982
|
+
expect(isJsonSchema('Response should be JSON format')).toBe(true);
|
|
983
|
+
expect(isJsonSchema('return as json')).toBe(true);
|
|
984
|
+
});
|
|
985
|
+
|
|
986
|
+
test('should handle mixed case', () => {
|
|
987
|
+
expect(isJsonSchema('{"Type": "Object"}')).toBe(true);
|
|
988
|
+
expect(isJsonSchema('APPLICATION/JSON')).toBe(true);
|
|
989
|
+
});
|
|
990
|
+
|
|
991
|
+
test('should reject non-JSON schemas', () => {
|
|
992
|
+
expect(isJsonSchema('<schema></schema>')).toBe(false);
|
|
993
|
+
expect(isJsonSchema('plain text schema')).toBe(false);
|
|
994
|
+
expect(isJsonSchema('')).toBe(false);
|
|
995
|
+
expect(isJsonSchema(null)).toBe(false);
|
|
996
|
+
expect(isJsonSchema(undefined)).toBe(false);
|
|
997
|
+
});
|
|
998
|
+
});
|
|
999
|
+
|
|
1000
|
+
describe('createJsonCorrectionPrompt', () => {
|
|
1001
|
+
test('should create basic correction prompt for first retry (retryCount 0)', () => {
|
|
1002
|
+
const invalidResponse = '{"test": value}';
|
|
1003
|
+
const schema = '{"test": "string"}';
|
|
1004
|
+
const error = 'Unexpected token v in JSON';
|
|
1005
|
+
|
|
1006
|
+
const prompt = createJsonCorrectionPrompt(invalidResponse, schema, error, 0);
|
|
1007
|
+
|
|
1008
|
+
expect(prompt).toContain(invalidResponse);
|
|
1009
|
+
expect(prompt).toContain(schema);
|
|
1010
|
+
expect(prompt).toContain(error);
|
|
1011
|
+
expect(prompt).toContain('CRITICAL JSON ERROR:');
|
|
1012
|
+
expect(prompt).toContain('Return ONLY the corrected JSON');
|
|
1013
|
+
});
|
|
1014
|
+
|
|
1015
|
+
test('should create more urgent prompt for second retry (retryCount 1)', () => {
|
|
1016
|
+
const invalidResponse = '{"test": value}';
|
|
1017
|
+
const schema = '{"test": "string"}';
|
|
1018
|
+
const error = 'Unexpected token v in JSON';
|
|
1019
|
+
|
|
1020
|
+
const prompt = createJsonCorrectionPrompt(invalidResponse, schema, error, 1);
|
|
1021
|
+
|
|
1022
|
+
expect(prompt).toContain('URGENT - JSON PARSING FAILED:');
|
|
1023
|
+
expect(prompt).toContain('second chance');
|
|
1024
|
+
expect(prompt).toContain('ABSOLUTELY NO explanatory text');
|
|
1025
|
+
});
|
|
1026
|
+
|
|
1027
|
+
test('should create final attempt prompt for third retry (retryCount 2)', () => {
|
|
1028
|
+
const invalidResponse = '{"test": value}';
|
|
1029
|
+
const schema = '{"test": "string"}';
|
|
1030
|
+
const error = 'Unexpected token v in JSON';
|
|
1031
|
+
|
|
1032
|
+
const prompt = createJsonCorrectionPrompt(invalidResponse, schema, error, 2);
|
|
1033
|
+
|
|
1034
|
+
expect(prompt).toContain('FINAL ATTEMPT - CRITICAL JSON ERROR:');
|
|
1035
|
+
expect(prompt).toContain('final retry');
|
|
1036
|
+
expect(prompt).toContain('EXAMPLE:');
|
|
1037
|
+
expect(prompt).toContain('NOT:');
|
|
1038
|
+
});
|
|
1039
|
+
|
|
1040
|
+
test('should cap at highest strength level for retryCount > 2', () => {
|
|
1041
|
+
const invalidResponse = '{"test": value}';
|
|
1042
|
+
const schema = '{"test": "string"}';
|
|
1043
|
+
const error = 'Unexpected token v in JSON';
|
|
1044
|
+
|
|
1045
|
+
const prompt = createJsonCorrectionPrompt(invalidResponse, schema, error, 5);
|
|
1046
|
+
|
|
1047
|
+
expect(prompt).toContain('FINAL ATTEMPT - CRITICAL JSON ERROR:');
|
|
1048
|
+
});
|
|
1049
|
+
|
|
1050
|
+
test('should truncate long invalid responses', () => {
|
|
1051
|
+
const longResponse = 'Hello '.repeat(200) + '{"test": value}';
|
|
1052
|
+
const schema = '{"test": "string"}';
|
|
1053
|
+
const error = 'Unexpected token v';
|
|
1054
|
+
|
|
1055
|
+
const prompt = createJsonCorrectionPrompt(longResponse, schema, error, 0);
|
|
1056
|
+
|
|
1057
|
+
expect(prompt).toContain('...');
|
|
1058
|
+
expect(prompt.length).toBeLessThan(longResponse.length + 500);
|
|
1059
|
+
});
|
|
1060
|
+
|
|
1061
|
+
test('should handle default retryCount parameter', () => {
|
|
1062
|
+
const invalidResponse = '{"test": value}';
|
|
1063
|
+
const schema = '{"test": "string"}';
|
|
1064
|
+
const error = 'Unexpected token v in JSON';
|
|
1065
|
+
|
|
1066
|
+
const prompt = createJsonCorrectionPrompt(invalidResponse, schema, error);
|
|
1067
|
+
|
|
1068
|
+
expect(prompt).toContain('CRITICAL JSON ERROR:');
|
|
1069
|
+
});
|
|
1070
|
+
|
|
1071
|
+
test('should handle multiline responses with truncation', () => {
|
|
1072
|
+
const invalidResponse = '{\n "test": value\n}';
|
|
1073
|
+
const schema = '{"test": "string"}';
|
|
1074
|
+
const error = 'Unexpected token v';
|
|
1075
|
+
|
|
1076
|
+
const prompt = createJsonCorrectionPrompt(invalidResponse, schema, error, 1);
|
|
1077
|
+
|
|
1078
|
+
expect(prompt).toContain('URGENT');
|
|
1079
|
+
expect(prompt.split('\n').length).toBeGreaterThan(5);
|
|
1080
|
+
});
|
|
1081
|
+
});
|
|
1082
|
+
|
|
1083
|
+
describe('processSchemaResponse', () => {
|
|
1084
|
+
test('should process and clean response', () => {
|
|
1085
|
+
const input = '```json\n{"test": "value"}\n```';
|
|
1086
|
+
const result = processSchemaResponse(input, '{}');
|
|
1087
|
+
|
|
1088
|
+
expect(result.cleaned).toBe('{"test": "value"}');
|
|
1089
|
+
});
|
|
1090
|
+
|
|
1091
|
+
test('should include debug information when requested', () => {
|
|
1092
|
+
const input = '```json\n{"test": "value"}\n```';
|
|
1093
|
+
const result = processSchemaResponse(input, '{}', { debug: true });
|
|
1094
|
+
|
|
1095
|
+
expect(result.debug).toBeDefined();
|
|
1096
|
+
expect(result.debug.wasModified).toBe(true);
|
|
1097
|
+
expect(result.debug.originalLength).toBeGreaterThan(result.debug.cleanedLength);
|
|
1098
|
+
});
|
|
1099
|
+
|
|
1100
|
+
test('should validate JSON when requested', () => {
|
|
1101
|
+
const input = '{"test": "value"}';
|
|
1102
|
+
const result = processSchemaResponse(input, '{}', { validateJson: true });
|
|
1103
|
+
|
|
1104
|
+
expect(result.jsonValidation).toBeDefined();
|
|
1105
|
+
expect(result.jsonValidation.isValid).toBe(true);
|
|
1106
|
+
});
|
|
1107
|
+
|
|
1108
|
+
test('should validate XML when requested', () => {
|
|
1109
|
+
const input = '<root>test</root>';
|
|
1110
|
+
const result = processSchemaResponse(input, '<schema/>', { validateXml: true });
|
|
1111
|
+
|
|
1112
|
+
expect(result.xmlValidation).toBeDefined();
|
|
1113
|
+
expect(result.xmlValidation.isValid).toBe(true);
|
|
1114
|
+
});
|
|
1115
|
+
});
|
|
1116
|
+
|
|
1117
|
+
describe('generateExampleFromSchema', () => {
|
|
1118
|
+
test('should generate example from object schema with basic types', () => {
|
|
1119
|
+
const schema = {
|
|
1120
|
+
type: 'object',
|
|
1121
|
+
properties: {
|
|
1122
|
+
name: { type: 'string', description: 'User name' },
|
|
1123
|
+
age: { type: 'number' },
|
|
1124
|
+
active: { type: 'boolean' },
|
|
1125
|
+
tags: { type: 'array' }
|
|
1126
|
+
}
|
|
1127
|
+
};
|
|
1128
|
+
|
|
1129
|
+
const result = generateExampleFromSchema(schema);
|
|
1130
|
+
|
|
1131
|
+
expect(result).toEqual({
|
|
1132
|
+
name: 'User name',
|
|
1133
|
+
age: 0,
|
|
1134
|
+
active: false,
|
|
1135
|
+
tags: []
|
|
1136
|
+
});
|
|
1137
|
+
});
|
|
1138
|
+
|
|
1139
|
+
test('should use description for string fields', () => {
|
|
1140
|
+
const schema = {
|
|
1141
|
+
type: 'object',
|
|
1142
|
+
properties: {
|
|
1143
|
+
message: { type: 'string', description: 'Hello world' },
|
|
1144
|
+
noDescription: { type: 'string' }
|
|
1145
|
+
}
|
|
1146
|
+
};
|
|
1147
|
+
|
|
1148
|
+
const result = generateExampleFromSchema(schema);
|
|
1149
|
+
|
|
1150
|
+
expect(result.message).toBe('Hello world');
|
|
1151
|
+
expect(result.noDescription).toBe('your answer here');
|
|
1152
|
+
});
|
|
1153
|
+
|
|
1154
|
+
test('should handle nested objects', () => {
|
|
1155
|
+
const schema = {
|
|
1156
|
+
type: 'object',
|
|
1157
|
+
properties: {
|
|
1158
|
+
user: { type: 'object' },
|
|
1159
|
+
settings: { type: 'object' }
|
|
1160
|
+
}
|
|
1161
|
+
};
|
|
1162
|
+
|
|
1163
|
+
const result = generateExampleFromSchema(schema);
|
|
1164
|
+
|
|
1165
|
+
expect(result).toEqual({
|
|
1166
|
+
user: {},
|
|
1167
|
+
settings: {}
|
|
1168
|
+
});
|
|
1169
|
+
});
|
|
1170
|
+
|
|
1171
|
+
test('should parse schema from JSON string', () => {
|
|
1172
|
+
const schema = JSON.stringify({
|
|
1173
|
+
type: 'object',
|
|
1174
|
+
properties: {
|
|
1175
|
+
refined: { type: 'boolean' },
|
|
1176
|
+
text: { type: 'string' }
|
|
1177
|
+
}
|
|
1178
|
+
});
|
|
1179
|
+
|
|
1180
|
+
const result = generateExampleFromSchema(schema);
|
|
1181
|
+
|
|
1182
|
+
expect(result).toEqual({
|
|
1183
|
+
refined: false,
|
|
1184
|
+
text: 'your answer here'
|
|
1185
|
+
});
|
|
1186
|
+
});
|
|
1187
|
+
|
|
1188
|
+
test('should return null for non-object schema', () => {
|
|
1189
|
+
const schema = {
|
|
1190
|
+
type: 'string'
|
|
1191
|
+
};
|
|
1192
|
+
|
|
1193
|
+
const result = generateExampleFromSchema(schema);
|
|
1194
|
+
|
|
1195
|
+
expect(result).toBeNull();
|
|
1196
|
+
});
|
|
1197
|
+
|
|
1198
|
+
test('should return null for schema without properties', () => {
|
|
1199
|
+
const schema = {
|
|
1200
|
+
type: 'object'
|
|
1201
|
+
};
|
|
1202
|
+
|
|
1203
|
+
const result = generateExampleFromSchema(schema);
|
|
1204
|
+
|
|
1205
|
+
expect(result).toBeNull();
|
|
1206
|
+
});
|
|
1207
|
+
|
|
1208
|
+
test('should return null for invalid schema', () => {
|
|
1209
|
+
const schema = 'not valid json {';
|
|
1210
|
+
|
|
1211
|
+
const result = generateExampleFromSchema(schema);
|
|
1212
|
+
|
|
1213
|
+
expect(result).toBeNull();
|
|
1214
|
+
});
|
|
1215
|
+
|
|
1216
|
+
test('should handle schema with multiple field types', () => {
|
|
1217
|
+
const schema = {
|
|
1218
|
+
type: 'object',
|
|
1219
|
+
properties: {
|
|
1220
|
+
id: { type: 'number' },
|
|
1221
|
+
name: { type: 'string', description: 'Product name' },
|
|
1222
|
+
inStock: { type: 'boolean' },
|
|
1223
|
+
categories: { type: 'array' },
|
|
1224
|
+
metadata: { type: 'object' }
|
|
1225
|
+
}
|
|
1226
|
+
};
|
|
1227
|
+
|
|
1228
|
+
const result = generateExampleFromSchema(schema);
|
|
1229
|
+
|
|
1230
|
+
expect(result).toEqual({
|
|
1231
|
+
id: 0,
|
|
1232
|
+
name: 'Product name',
|
|
1233
|
+
inStock: false,
|
|
1234
|
+
categories: [],
|
|
1235
|
+
metadata: {}
|
|
1236
|
+
});
|
|
1237
|
+
});
|
|
1238
|
+
|
|
1239
|
+
test('should match the exact schema used in Visor refine check', () => {
|
|
1240
|
+
// This is the actual schema from the bug report
|
|
1241
|
+
const schema = {
|
|
1242
|
+
type: 'object',
|
|
1243
|
+
additionalProperties: false,
|
|
1244
|
+
properties: {
|
|
1245
|
+
refined: {
|
|
1246
|
+
type: 'boolean',
|
|
1247
|
+
description: 'true if the task description is clear and actionable, false if clarification is needed'
|
|
1248
|
+
},
|
|
1249
|
+
text: {
|
|
1250
|
+
type: 'string',
|
|
1251
|
+
description: 'If refined=true, confirmation message. If refined=false, specific questions to ask.'
|
|
1252
|
+
}
|
|
1253
|
+
},
|
|
1254
|
+
required: ['refined', 'text']
|
|
1255
|
+
};
|
|
1256
|
+
|
|
1257
|
+
const result = generateExampleFromSchema(schema);
|
|
1258
|
+
|
|
1259
|
+
expect(result).toEqual({
|
|
1260
|
+
refined: false,
|
|
1261
|
+
text: 'If refined=true, confirmation message. If refined=false, specific questions to ask.'
|
|
1262
|
+
});
|
|
1263
|
+
|
|
1264
|
+
// Verify it's valid JSON when stringified
|
|
1265
|
+
expect(() => JSON.parse(JSON.stringify(result))).not.toThrow();
|
|
1266
|
+
});
|
|
1267
|
+
});
|
|
1268
|
+
});
|