@dizzlkheinz/ynab-mcpb 0.12.1
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/.chunkhound.json +11 -0
- package/.code/agents/0427d95e-edca-431f-a214-5e53264e29c4/error.txt +8 -0
- package/.code/agents/0d675174-d1e1-41c3-9975-4c2e275819a9/error.txt +3 -0
- package/.code/agents/0d8c5afd-4787-422b-abf8-2e5943fc7e67/error.txt +3 -0
- package/.code/agents/0ec34a70-ed5d-4b9e-bee4-bb0e4cccbc4b/error.txt +1 -0
- package/.code/agents/0ef51a21-1ab1-49d7-9561-0eaa43875ebc/error.txt +12 -0
- package/.code/agents/15db95d7-abad-4b4d-9c3b-8446089cb61d/error.txt +1 -0
- package/.code/agents/19ab9acb-f675-4ff0-902a-09a5476f8149/error.txt +1 -0
- package/.code/agents/1ef7e12d-f6ff-4897-8a9b-152d523d898e/error.txt +5 -0
- package/.code/agents/2465/exec-call_lroN9KKzJVWC7t5423DK1nT9.txt +1453 -0
- package/.code/agents/28edb6fe-95a9-41a0-ae69-aa0100d26c0c/error.txt +8 -0
- package/.code/agents/2ae40cf5-b4bf-42e2-92bf-7ea350a7755e/error.txt +9 -0
- package/.code/agents/2bfc4e1f-ac4b-45a5-b6df-bf89d4dbb54c/error.txt +1 -0
- package/.code/agents/2e2e1134-eff0-49be-ba25-8e2c3468a564/error.txt +5 -0
- package/.code/agents/3/exec-call_203OC4TNVkLxW7z2HCVEQ1cM.txt +81 -0
- package/.code/agents/3/exec-call_SS5T0XSiXB4LSNzUKTl75wkh.txt +610 -0
- package/.code/agents/3322c003-ce5e-48e3-a342-f5049c5bf9a2/error.txt +1 -0
- package/.code/agents/391e9b08-1ebc-468c-9bcd-6d0cc3193b37/error.txt +1 -0
- package/.code/agents/3ab0aa84-b7bb-4054-afa3-40b8fd7d3be0/error.txt +1 -0
- package/.code/agents/3bed368d-50fe-477e-aee3-a6707eaa1ab9/error.txt +3 -0
- package/.code/agents/3e40b925-db12-442f-8d7a-a25fc69a6672/error.txt +8 -0
- package/.code/agents/414d5776-cf58-41f3-9328-a6daed503a50/error.txt +5 -0
- package/.code/agents/42687751-4565-4610-b240-67835b17d861/error.txt +1 -0
- package/.code/agents/46b98876-1a39-43c9-9e2f-507ca6d47335/error.txt +9 -0
- package/.code/agents/4a7d9491-b26f-43dd-850d-2ecdc49b5d1b/error.txt +1 -0
- package/.code/agents/4e60f00a-1b3e-447f-87f3-7faf9deddec3/error.txt +13 -0
- package/.code/agents/5138fc1c-4d49-4b74-a7da-ccdb3a8e44e7/error.txt +14 -0
- package/.code/agents/521cff39-a7a3-42e5-a557-134f0f7daaa0/error.txt +5 -0
- package/.code/agents/53302dc5-3857-4413-9a47-9e0f64a51dc4/error.txt +5 -0
- package/.code/agents/567c7c2e-6a6f-4761-a08d-d36deeb2e0ac/error.txt +5 -0
- package/.code/agents/57b00845-80dc-47c9-953c-3028d16275d6/error.txt +3 -0
- package/.code/agents/593d9005-c2a5-48fd-8813-ece0d3f2de96/error.txt +1 -0
- package/.code/agents/5a112e66-0e1a-42f9-877c-53af56ea3551/error.txt +1 -0
- package/.code/agents/5b05e8ed-7788-4738-b7ee-9faa8180f992/error.txt +5 -0
- package/.code/agents/5f888d6f-d7ca-4ac8-be23-9ea1bf753951/error.txt +5 -0
- package/.code/agents/607db3ab-e4b0-435b-b497-93e9aa525549/error.txt +8 -0
- package/.code/agents/67dcb2a2-900f-4c78-b3fc-80b5213e0ddf/error.txt +8 -0
- package/.code/agents/69ad848c-4e98-49b3-b16c-0094ac2d1759/error.txt +5 -0
- package/.code/agents/6c9cfc5f-0d0b-445c-b121-9f60082c4f70/error.txt +1 -0
- package/.code/agents/6f6f8f77-4ab0-4f6e-9f30-40e8be0bd8f5/error.txt +1 -0
- package/.code/agents/72a7cde4-fa8a-4024-9038-27faa550539b/error.txt +1 -0
- package/.code/agents/7b48335c-8247-43aa-9949-5f820ba8e199/error.txt +1 -0
- package/.code/agents/80944249-bea9-4ac5-87de-a666c4df306e/error.txt +1 -0
- package/.code/agents/826099df-1b66-4186-a915-7eb59f9db19d/error.txt +5 -0
- package/.code/agents/8291d158-18a8-4a92-b799-4e9a4d9cce88/error.txt +1 -0
- package/.code/agents/82fb71a3-20fb-4341-804a-a2fc900f95bc/error.txt +1 -0
- package/.code/agents/855790ea-54ee-43e4-8209-a66994e37590/error.txt +1 -0
- package/.code/agents/88ce3a2e-04f2-42be-9062-bf97aa798da0/error.txt +3 -0
- package/.code/agents/9a17e398-b6ed-4218-bb55-bc64a8d38ce8/error.txt +8 -0
- package/.code/agents/9a4f4bfc-a2a6-4f40-a896-9335b41a7ed1/error.txt +1 -0
- package/.code/agents/9b633e55-ef84-47d6-94bb-fd3dd172ad97/error.txt +1 -0
- package/.code/agents/9b81f3ab-c72b-4a81-9a8f-28a49ddba84a/error.txt +8 -0
- package/.code/agents/a35daf29-b2d1-4aef-9b42-dad63a76bd47/error.txt +3 -0
- package/.code/agents/a81990cc-69ee-44d2-b907-17403c9bc5d7/error.txt +5 -0
- package/.code/agents/ab56260a-4a83-4ad4-9410-f88a23d6520a/error.txt +1 -0
- package/.code/agents/ad722c31-2d1d-45f7-bae2-3f02ca455b60/error.txt +1 -0
- package/.code/agents/b62e8690-3324-4b97-9309-731bee79416b/error.txt +5 -0
- package/.code/agents/baf60a3a-752b-4ad8-99d6-df32423ed2eb/error.txt +1 -0
- package/.code/agents/be049042-7dcb-4ac8-9beb-c8f1aea67742/error.txt +14 -0
- package/.code/agents/bed1dcb4-bfce-4a9f-8594-0f994962aafd/error.txt +1 -0
- package/.code/agents/c324a6cf-e935-4ede-9529-b3ebc18e8d6b/error.txt +5 -0
- package/.code/agents/c37c06ff-dfe3-43f2-9bbc-3ec73ec8f41d/error.txt +5 -0
- package/.code/agents/c8cd6671-433a-456b-9f88-e51cb2df6bfc/error.txt +11 -0
- package/.code/agents/ca2ccb67-2f24-428e-b27d-9365beadd140/error.txt +1 -0
- package/.code/agents/cf08c0c8-e7f0-423e-93ba-547e8e818340/error.txt +8 -0
- package/.code/agents/d579c74f-874b-40a4-9d56-ced1eb6a701d/error.txt +1 -0
- package/.code/agents/df412c98-7378-4deb-8e1e-76c416931181/error.txt +3 -0
- package/.code/agents/e5134eb3-2af4-45b0-8998-051cb4afdb45/error.txt +3 -0
- package/.code/agents/e6308471-aa45-4e9e-9496-2e9404164d97/error.txt +8 -0
- package/.code/agents/e7bd8bc7-23fb-4f46-98dc-b0dcf11b75a1/error.txt +1 -0
- package/.code/agents/e92bec35-378d-4fe1-8ac0-6e1bb3c86911/error.txt +5 -0
- package/.code/agents/ed918fbf-2dc4-4aa2-bfc5-04b65d9471ea/error.txt +1 -0
- package/.code/agents/ef1d756f-b272-48fc-8729-f05c494674f7/error.txt +1 -0
- package/.code/agents/ef359853-0249-4e41-a804-c0fc459fe456/error.txt +1 -0
- package/.code/agents/effc7b4a-4b90-40a0-8c86-a7a99d2d5fd2/error.txt +1 -0
- package/.code/agents/fa15f8d5-8359-4a8b-83a3-2f2056b3ff40/error.txt +3 -0
- package/.code/agents/fbef4193-eadf-4c8a-83ff-4878a6310f25/error.txt +8 -0
- package/.code/agents/fd0a4b4a-fda4-4964-a6d6-2b8a2da387c6/error.txt +1 -0
- package/.dxtignore +57 -0
- package/.env.example +44 -0
- package/.gemini/settings.json +8 -0
- package/.github/ISSUE_TEMPLATE/bug_report.md +41 -0
- package/.github/ISSUE_TEMPLATE/config.yml +5 -0
- package/.github/ISSUE_TEMPLATE/feature_request.md +24 -0
- package/.github/ISSUE_TEMPLATE/release_checklist.md +31 -0
- package/.github/pull_request_template.md +41 -0
- package/.github/workflows/ci-tests.yml +41 -0
- package/.github/workflows/claude-code-review.yml +57 -0
- package/.github/workflows/claude.yml +50 -0
- package/.github/workflows/full-integration.yml +22 -0
- package/.github/workflows/pr-description-check.yml +88 -0
- package/.github/workflows/publish.yml +33 -0
- package/.github/workflows/release.yml +89 -0
- package/.mcpbignore +58 -0
- package/.prettierignore +10 -0
- package/.prettierrc.json +10 -0
- package/ADOS-2-Module-1-Complete-Manual.md +757 -0
- package/AGENTS.md +36 -0
- package/CHANGELOG.md +187 -0
- package/CLAUDE.md +414 -0
- package/CODEREVIEW_RESPONSE.md +128 -0
- package/LICENSE +17 -0
- package/NUL +1 -0
- package/README.md +222 -0
- package/SCHEMA_IMPROVEMENT_SUMMARY.md +120 -0
- package/TESTING_NOTES.md +217 -0
- package/WARP.md +245 -0
- package/accountactivity-merged.csv +149 -0
- package/bin/ynab-mcp-server.cjs +4 -0
- package/bin/ynab-mcp-server.js +8 -0
- package/bundle-analysis.html +13110 -0
- package/dist/bundle/index.cjs +124 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +85 -0
- package/dist/server/YNABMCPServer.d.ts +264 -0
- package/dist/server/YNABMCPServer.js +845 -0
- package/dist/server/budgetResolver.d.ts +15 -0
- package/dist/server/budgetResolver.js +99 -0
- package/dist/server/cacheManager.d.ts +74 -0
- package/dist/server/cacheManager.js +306 -0
- package/dist/server/config.d.ts +3 -0
- package/dist/server/config.js +19 -0
- package/dist/server/deltaCache.d.ts +61 -0
- package/dist/server/deltaCache.js +206 -0
- package/dist/server/deltaCache.merge.d.ts +9 -0
- package/dist/server/deltaCache.merge.js +111 -0
- package/dist/server/diagnostics.d.ts +90 -0
- package/dist/server/diagnostics.js +163 -0
- package/dist/server/errorHandler.d.ts +69 -0
- package/dist/server/errorHandler.js +524 -0
- package/dist/server/prompts.d.ts +31 -0
- package/dist/server/prompts.js +205 -0
- package/dist/server/rateLimiter.d.ts +27 -0
- package/dist/server/rateLimiter.js +82 -0
- package/dist/server/requestLogger.d.ts +62 -0
- package/dist/server/requestLogger.js +190 -0
- package/dist/server/resources.d.ts +39 -0
- package/dist/server/resources.js +85 -0
- package/dist/server/responseFormatter.d.ts +14 -0
- package/dist/server/responseFormatter.js +42 -0
- package/dist/server/securityMiddleware.d.ts +87 -0
- package/dist/server/securityMiddleware.js +117 -0
- package/dist/server/serverKnowledgeStore.d.ts +11 -0
- package/dist/server/serverKnowledgeStore.js +42 -0
- package/dist/server/toolRegistry.d.ts +85 -0
- package/dist/server/toolRegistry.js +272 -0
- package/dist/tools/__tests__/deltaTestUtils.d.ts +18 -0
- package/dist/tools/__tests__/deltaTestUtils.js +26 -0
- package/dist/tools/accountTools.d.ts +37 -0
- package/dist/tools/accountTools.js +175 -0
- package/dist/tools/budgetTools.d.ts +10 -0
- package/dist/tools/budgetTools.js +68 -0
- package/dist/tools/categoryTools.d.ts +27 -0
- package/dist/tools/categoryTools.js +232 -0
- package/dist/tools/compareTransactions/formatter.d.ts +71 -0
- package/dist/tools/compareTransactions/formatter.js +97 -0
- package/dist/tools/compareTransactions/index.d.ts +30 -0
- package/dist/tools/compareTransactions/index.js +160 -0
- package/dist/tools/compareTransactions/matcher.d.ts +12 -0
- package/dist/tools/compareTransactions/matcher.js +140 -0
- package/dist/tools/compareTransactions/parser.d.ts +14 -0
- package/dist/tools/compareTransactions/parser.js +430 -0
- package/dist/tools/compareTransactions/types.d.ts +27 -0
- package/dist/tools/compareTransactions/types.js +1 -0
- package/dist/tools/compareTransactions.d.ts +1 -0
- package/dist/tools/compareTransactions.js +1 -0
- package/dist/tools/deltaFetcher.d.ts +22 -0
- package/dist/tools/deltaFetcher.js +137 -0
- package/dist/tools/deltaSupport.d.ts +20 -0
- package/dist/tools/deltaSupport.js +176 -0
- package/dist/tools/exportTransactions.d.ts +17 -0
- package/dist/tools/exportTransactions.js +191 -0
- package/dist/tools/monthTools.d.ts +16 -0
- package/dist/tools/monthTools.js +107 -0
- package/dist/tools/payeeTools.d.ts +17 -0
- package/dist/tools/payeeTools.js +82 -0
- package/dist/tools/reconcileAdapter.d.ts +25 -0
- package/dist/tools/reconcileAdapter.js +167 -0
- package/dist/tools/reconciliation/analyzer.d.ts +3 -0
- package/dist/tools/reconciliation/analyzer.js +567 -0
- package/dist/tools/reconciliation/executor.d.ts +94 -0
- package/dist/tools/reconciliation/executor.js +611 -0
- package/dist/tools/reconciliation/index.d.ts +54 -0
- package/dist/tools/reconciliation/index.js +249 -0
- package/dist/tools/reconciliation/matcher.d.ts +3 -0
- package/dist/tools/reconciliation/matcher.js +160 -0
- package/dist/tools/reconciliation/payeeNormalizer.d.ts +6 -0
- package/dist/tools/reconciliation/payeeNormalizer.js +77 -0
- package/dist/tools/reconciliation/recommendationEngine.d.ts +2 -0
- package/dist/tools/reconciliation/recommendationEngine.js +273 -0
- package/dist/tools/reconciliation/reportFormatter.d.ts +13 -0
- package/dist/tools/reconciliation/reportFormatter.js +214 -0
- package/dist/tools/reconciliation/types.d.ts +172 -0
- package/dist/tools/reconciliation/types.js +7 -0
- package/dist/tools/schemas/outputs/accountOutputs.d.ts +58 -0
- package/dist/tools/schemas/outputs/accountOutputs.js +24 -0
- package/dist/tools/schemas/outputs/budgetOutputs.d.ts +48 -0
- package/dist/tools/schemas/outputs/budgetOutputs.js +15 -0
- package/dist/tools/schemas/outputs/categoryOutputs.d.ts +93 -0
- package/dist/tools/schemas/outputs/categoryOutputs.js +37 -0
- package/dist/tools/schemas/outputs/comparisonOutputs.d.ts +269 -0
- package/dist/tools/schemas/outputs/comparisonOutputs.js +181 -0
- package/dist/tools/schemas/outputs/index.d.ts +14 -0
- package/dist/tools/schemas/outputs/index.js +14 -0
- package/dist/tools/schemas/outputs/monthOutputs.d.ts +122 -0
- package/dist/tools/schemas/outputs/monthOutputs.js +51 -0
- package/dist/tools/schemas/outputs/payeeOutputs.d.ts +34 -0
- package/dist/tools/schemas/outputs/payeeOutputs.js +16 -0
- package/dist/tools/schemas/outputs/reconciliationOutputs.d.ts +1275 -0
- package/dist/tools/schemas/outputs/reconciliationOutputs.js +377 -0
- package/dist/tools/schemas/outputs/transactionMutationOutputs.d.ts +717 -0
- package/dist/tools/schemas/outputs/transactionMutationOutputs.js +260 -0
- package/dist/tools/schemas/outputs/transactionOutputs.d.ts +98 -0
- package/dist/tools/schemas/outputs/transactionOutputs.js +49 -0
- package/dist/tools/schemas/outputs/utilityOutputs.d.ts +219 -0
- package/dist/tools/schemas/outputs/utilityOutputs.js +120 -0
- package/dist/tools/schemas/shared/commonOutputs.d.ts +24 -0
- package/dist/tools/schemas/shared/commonOutputs.js +27 -0
- package/dist/tools/toolCategories.d.ts +32 -0
- package/dist/tools/toolCategories.js +32 -0
- package/dist/tools/transactionTools.d.ts +315 -0
- package/dist/tools/transactionTools.js +1722 -0
- package/dist/tools/utilityTools.d.ts +10 -0
- package/dist/tools/utilityTools.js +56 -0
- package/dist/types/index.d.ts +20 -0
- package/dist/types/index.js +16 -0
- package/dist/types/toolAnnotations.d.ts +7 -0
- package/dist/types/toolAnnotations.js +1 -0
- package/dist/utils/amountUtils.d.ts +3 -0
- package/dist/utils/amountUtils.js +10 -0
- package/dist/utils/dateUtils.d.ts +9 -0
- package/dist/utils/dateUtils.js +43 -0
- package/dist/utils/money.d.ts +21 -0
- package/dist/utils/money.js +51 -0
- package/docs/README.md +72 -0
- package/docs/assets/examples/reconciliation-with-recommendations.json +68 -0
- package/docs/assets/schemas/reconciliation-v2.json +338 -0
- package/docs/getting-started/CONFIGURATION.md +175 -0
- package/docs/getting-started/INSTALLATION.md +333 -0
- package/docs/getting-started/QUICKSTART.md +282 -0
- package/docs/guides/ARCHITECTURE.md +650 -0
- package/docs/guides/DEPLOYMENT.md +189 -0
- package/docs/guides/INTEGRATION_TESTING.md +730 -0
- package/docs/guides/TESTING.md +591 -0
- package/docs/reconciliation-flow.md +83 -0
- package/docs/reference/API.md +1450 -0
- package/docs/reference/EXAMPLES.md +946 -0
- package/docs/reference/TOOLS.md +348 -0
- package/docs/reference/TROUBLESHOOTING.md +481 -0
- package/esbuild.config.mjs +68 -0
- package/eslint.config.js +49 -0
- package/fix-types.sh +17 -0
- package/meta.json +12550 -0
- package/package.json +105 -0
- package/package.json.tmp +105 -0
- package/scripts/analyze-bundle.mjs +41 -0
- package/scripts/create-pr-description.js +203 -0
- package/scripts/generate-mcpb.ps1 +96 -0
- package/scripts/run-domain-integration-tests.js +33 -0
- package/scripts/run-generate-mcpb.js +29 -0
- package/scripts/run-throttled-integration-tests.js +116 -0
- package/scripts/test-delta-params.mjs +140 -0
- package/scripts/test-recommendations.ts +53 -0
- package/scripts/tmpTransaction.ts +48 -0
- package/scripts/validate-env.js +122 -0
- package/scripts/verify-build.js +105 -0
- package/scripts/watch-and-restart.ps1 +50 -0
- package/src/__tests__/comprehensive.integration.test.ts +1196 -0
- package/src/__tests__/delta.performance.test.ts +80 -0
- package/src/__tests__/performance.test.ts +725 -0
- package/src/__tests__/setup.ts +449 -0
- package/src/__tests__/testRunner.ts +444 -0
- package/src/__tests__/testUtils.ts +563 -0
- package/src/__tests__/workflows.e2e.test.ts +1675 -0
- package/src/index.ts +124 -0
- package/src/server/.gitkeep +1 -0
- package/src/server/YNABMCPServer.ts +1188 -0
- package/src/server/__tests__/YNABMCPServer.integration.test.ts +903 -0
- package/src/server/__tests__/YNABMCPServer.test.ts +894 -0
- package/src/server/__tests__/budgetResolver.test.ts +425 -0
- package/src/server/__tests__/cacheManager.test.ts +880 -0
- package/src/server/__tests__/config.test.ts +166 -0
- package/src/server/__tests__/deltaCache.merge.test.ts +724 -0
- package/src/server/__tests__/deltaCache.swr.test.ts +168 -0
- package/src/server/__tests__/deltaCache.test.ts +774 -0
- package/src/server/__tests__/diagnostics.test.ts +823 -0
- package/src/server/__tests__/errorHandler.integration.test.ts +466 -0
- package/src/server/__tests__/errorHandler.test.ts +416 -0
- package/src/server/__tests__/prompts.test.ts +354 -0
- package/src/server/__tests__/rateLimiter.test.ts +314 -0
- package/src/server/__tests__/requestLogger.test.ts +408 -0
- package/src/server/__tests__/resources.test.ts +299 -0
- package/src/server/__tests__/security.integration.test.ts +426 -0
- package/src/server/__tests__/securityMiddleware.test.ts +449 -0
- package/src/server/__tests__/server-startup.integration.test.ts +477 -0
- package/src/server/__tests__/serverKnowledgeStore.test.ts +174 -0
- package/src/server/__tests__/toolRegistry.test.ts +855 -0
- package/src/server/budgetResolver.ts +235 -0
- package/src/server/cacheManager.ts +503 -0
- package/src/server/config.ts +41 -0
- package/src/server/deltaCache.merge.ts +149 -0
- package/src/server/deltaCache.ts +341 -0
- package/src/server/diagnostics.ts +338 -0
- package/src/server/errorHandler.ts +756 -0
- package/src/server/prompts.ts +291 -0
- package/src/server/rateLimiter.ts +156 -0
- package/src/server/requestLogger.ts +344 -0
- package/src/server/resources.ts +168 -0
- package/src/server/responseFormatter.ts +51 -0
- package/src/server/securityMiddleware.ts +236 -0
- package/src/server/serverKnowledgeStore.ts +91 -0
- package/src/server/toolRegistry.ts +489 -0
- package/src/tools/.gitkeep +1 -0
- package/src/tools/__tests__/accountTools.delta.integration.test.ts +128 -0
- package/src/tools/__tests__/accountTools.integration.test.ts +117 -0
- package/src/tools/__tests__/accountTools.test.ts +653 -0
- package/src/tools/__tests__/budgetTools.delta.integration.test.ts +90 -0
- package/src/tools/__tests__/budgetTools.integration.test.ts +134 -0
- package/src/tools/__tests__/budgetTools.test.ts +423 -0
- package/src/tools/__tests__/categoryTools.delta.integration.test.ts +80 -0
- package/src/tools/__tests__/categoryTools.integration.test.ts +295 -0
- package/src/tools/__tests__/categoryTools.test.ts +622 -0
- package/src/tools/__tests__/compareTransactions/formatter.test.ts +486 -0
- package/src/tools/__tests__/compareTransactions/index.test.ts +383 -0
- package/src/tools/__tests__/compareTransactions/matcher.test.ts +410 -0
- package/src/tools/__tests__/compareTransactions/parser.test.ts +764 -0
- package/src/tools/__tests__/compareTransactions.test.ts +342 -0
- package/src/tools/__tests__/compareTransactions.window.test.ts +147 -0
- package/src/tools/__tests__/deltaFetcher.scheduled.integration.test.ts +76 -0
- package/src/tools/__tests__/deltaFetcher.test.ts +270 -0
- package/src/tools/__tests__/deltaSupport.test.ts +188 -0
- package/src/tools/__tests__/deltaTestUtils.ts +46 -0
- package/src/tools/__tests__/exportTransactions.test.ts +213 -0
- package/src/tools/__tests__/monthTools.delta.integration.test.ts +80 -0
- package/src/tools/__tests__/monthTools.integration.test.ts +174 -0
- package/src/tools/__tests__/monthTools.test.ts +523 -0
- package/src/tools/__tests__/payeeTools.delta.integration.test.ts +80 -0
- package/src/tools/__tests__/payeeTools.integration.test.ts +150 -0
- package/src/tools/__tests__/payeeTools.test.ts +445 -0
- package/src/tools/__tests__/transactionTools.integration.test.ts +762 -0
- package/src/tools/__tests__/transactionTools.test.ts +3521 -0
- package/src/tools/__tests__/utilityTools.integration.test.ts +128 -0
- package/src/tools/__tests__/utilityTools.test.ts +205 -0
- package/src/tools/accountTools.ts +283 -0
- package/src/tools/budgetTools.ts +112 -0
- package/src/tools/categoryTools.ts +366 -0
- package/src/tools/compareTransactions/formatter.ts +163 -0
- package/src/tools/compareTransactions/index.ts +228 -0
- package/src/tools/compareTransactions/matcher.ts +240 -0
- package/src/tools/compareTransactions/parser.ts +557 -0
- package/src/tools/compareTransactions/types.ts +60 -0
- package/src/tools/compareTransactions.ts +3 -0
- package/src/tools/deltaFetcher.ts +278 -0
- package/src/tools/deltaSupport.ts +293 -0
- package/src/tools/exportTransactions.ts +273 -0
- package/src/tools/monthTools.ts +164 -0
- package/src/tools/payeeTools.ts +140 -0
- package/src/tools/reconcileAdapter.ts +312 -0
- package/src/tools/reconciliation/__tests__/adapter.causes.test.ts +122 -0
- package/src/tools/reconciliation/__tests__/adapter.test.ts +234 -0
- package/src/tools/reconciliation/__tests__/analyzer.test.ts +406 -0
- package/src/tools/reconciliation/__tests__/executor.integration.test.ts +366 -0
- package/src/tools/reconciliation/__tests__/executor.test.ts +779 -0
- package/src/tools/reconciliation/__tests__/matcher.test.ts +650 -0
- package/src/tools/reconciliation/__tests__/payeeNormalizer.test.ts +278 -0
- package/src/tools/reconciliation/__tests__/recommendationEngine.integration.test.ts +658 -0
- package/src/tools/reconciliation/__tests__/recommendationEngine.test.ts +1000 -0
- package/src/tools/reconciliation/__tests__/reconciliation.delta.integration.test.ts +151 -0
- package/src/tools/reconciliation/__tests__/reportFormatter.test.ts +573 -0
- package/src/tools/reconciliation/__tests__/scenarios/adapterCurrency.scenario.test.ts +78 -0
- package/src/tools/reconciliation/__tests__/scenarios/extremes.scenario.test.ts +47 -0
- package/src/tools/reconciliation/__tests__/scenarios/repeatAmount.scenario.test.ts +61 -0
- package/src/tools/reconciliation/__tests__/schemaUrl.test.ts +49 -0
- package/src/tools/reconciliation/analyzer.ts +824 -0
- package/src/tools/reconciliation/executor.ts +880 -0
- package/src/tools/reconciliation/index.ts +400 -0
- package/src/tools/reconciliation/matcher.ts +269 -0
- package/src/tools/reconciliation/payeeNormalizer.ts +167 -0
- package/src/tools/reconciliation/recommendationEngine.ts +506 -0
- package/src/tools/reconciliation/reportFormatter.ts +363 -0
- package/src/tools/reconciliation/types.ts +314 -0
- package/src/tools/schemas/outputs/__tests__/accountOutputs.test.ts +424 -0
- package/src/tools/schemas/outputs/__tests__/budgetOutputs.test.ts +310 -0
- package/src/tools/schemas/outputs/__tests__/categoryOutputs.test.ts +448 -0
- package/src/tools/schemas/outputs/__tests__/comparisonOutputs.test.ts +519 -0
- package/src/tools/schemas/outputs/__tests__/dateValidation.test.ts +155 -0
- package/src/tools/schemas/outputs/__tests__/discrepancyDirection.test.ts +288 -0
- package/src/tools/schemas/outputs/__tests__/monthOutputs.test.ts +478 -0
- package/src/tools/schemas/outputs/__tests__/payeeOutputs.test.ts +370 -0
- package/src/tools/schemas/outputs/__tests__/reconciliationOutputs.test.ts +401 -0
- package/src/tools/schemas/outputs/__tests__/transactionMutationSchemas.test.ts +213 -0
- package/src/tools/schemas/outputs/__tests__/transactionOutputs.test.ts +474 -0
- package/src/tools/schemas/outputs/__tests__/utilityOutputs.test.ts +333 -0
- package/src/tools/schemas/outputs/accountOutputs.ts +137 -0
- package/src/tools/schemas/outputs/budgetOutputs.ts +86 -0
- package/src/tools/schemas/outputs/categoryOutputs.ts +194 -0
- package/src/tools/schemas/outputs/comparisonOutputs.ts +600 -0
- package/src/tools/schemas/outputs/index.ts +270 -0
- package/src/tools/schemas/outputs/monthOutputs.ts +243 -0
- package/src/tools/schemas/outputs/payeeOutputs.ts +105 -0
- package/src/tools/schemas/outputs/reconciliationOutputs.ts +796 -0
- package/src/tools/schemas/outputs/transactionMutationOutputs.ts +758 -0
- package/src/tools/schemas/outputs/transactionOutputs.ts +243 -0
- package/src/tools/schemas/outputs/utilityOutputs.ts +411 -0
- package/src/tools/schemas/shared/commonOutputs.ts +140 -0
- package/src/tools/toolCategories.ts +140 -0
- package/src/tools/transactionTools.ts +2509 -0
- package/src/tools/utilityTools.ts +90 -0
- package/src/types/.gitkeep +1 -0
- package/src/types/__tests__/index.test.ts +52 -0
- package/src/types/index.ts +67 -0
- package/src/types/integration-tests.d.ts +35 -0
- package/src/types/toolAnnotations.ts +44 -0
- package/src/utils/__tests__/dateUtils.test.ts +170 -0
- package/src/utils/__tests__/money.test.ts +189 -0
- package/src/utils/amountUtils.ts +32 -0
- package/src/utils/dateUtils.ts +108 -0
- package/src/utils/money.ts +123 -0
- package/test-csv-sample.csv +28 -0
- package/test-exports/sample_bank_statement.csv +7 -0
- package/test-exports/ynab_account_e9ddc2a6_minimal_1items_2025-11-19_09-04-53.json +23 -0
- package/test-exports/ynab_account_e9ddc2a6_minimal_1items_2025-11-19_10-37-42.json +23 -0
- package/test-exports/ynab_account_e9ddc2a6_minimal_4items_2025-11-19_09-02-09.json +44 -0
- package/test-exports/ynab_account_e9ddc2a6_minimal_6items_2025-11-19_10-37-52.json +58 -0
- package/test-exports/ynab_since_2025-11-01_account_4c18e9f0_minimal_14items_2025-11-16_10-07-10.json +115 -0
- package/test-reconcile-autodetect.js +40 -0
- package/test-reconcile-tool.js +152 -0
- package/test-reconcile-with-csv.cjs +89 -0
- package/test-statement.csv +8 -0
- package/test_debug.js +47 -0
- package/test_simple.mjs +16 -0
- package/tsconfig.json +31 -0
- package/tsconfig.prod.json +18 -0
- package/vitest-reporters/split-json-reporter.ts +211 -0
- package/vitest.config.ts +96 -0
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
import { describe, it, expect, beforeAll } from 'vitest';
|
|
2
|
+
import * as ynab from 'ynab';
|
|
3
|
+
import { handleListPayees, handleGetPayee } from '../payeeTools.js';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Integration tests for payee tools using real YNAB API
|
|
7
|
+
* Skips if YNAB_ACCESS_TOKEN is not set or if SKIP_E2E_TESTS is true
|
|
8
|
+
*/
|
|
9
|
+
const hasToken = !!process.env['YNAB_ACCESS_TOKEN'];
|
|
10
|
+
const shouldSkip = process.env['SKIP_E2E_TESTS'] === 'true' || !hasToken;
|
|
11
|
+
const describeIntegration = shouldSkip ? describe.skip : describe;
|
|
12
|
+
|
|
13
|
+
describeIntegration('Payee Tools Integration', () => {
|
|
14
|
+
let ynabAPI: ynab.API;
|
|
15
|
+
let testBudgetId: string;
|
|
16
|
+
let testPayeeId: string;
|
|
17
|
+
|
|
18
|
+
beforeAll(async () => {
|
|
19
|
+
const accessToken = process.env['YNAB_ACCESS_TOKEN']!;
|
|
20
|
+
ynabAPI = new ynab.API(accessToken);
|
|
21
|
+
const budgetsResponse = await ynabAPI.budgets.getBudgets();
|
|
22
|
+
testBudgetId = budgetsResponse.data.budgets[0].id;
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
describe('handleListPayees', () => {
|
|
26
|
+
it(
|
|
27
|
+
'should successfully list payees from real API',
|
|
28
|
+
{ meta: { tier: 'domain', domain: 'payees' } },
|
|
29
|
+
async () => {
|
|
30
|
+
const result = await handleListPayees(ynabAPI, { budget_id: testBudgetId });
|
|
31
|
+
|
|
32
|
+
expect(result.content).toHaveLength(1);
|
|
33
|
+
expect(result.content[0].type).toBe('text');
|
|
34
|
+
|
|
35
|
+
const parsedContent = JSON.parse(result.content[0].text);
|
|
36
|
+
expect(parsedContent.payees).toBeDefined();
|
|
37
|
+
expect(Array.isArray(parsedContent.payees)).toBe(true);
|
|
38
|
+
expect(parsedContent.payees.length).toBeGreaterThan(0);
|
|
39
|
+
|
|
40
|
+
// Store first payee ID for next test
|
|
41
|
+
testPayeeId = parsedContent.payees[0].id;
|
|
42
|
+
|
|
43
|
+
// Verify payee structure
|
|
44
|
+
const firstPayee = parsedContent.payees[0];
|
|
45
|
+
expect(firstPayee.id).toBeDefined();
|
|
46
|
+
expect(firstPayee.name).toBeDefined();
|
|
47
|
+
expect(firstPayee.deleted).toBeDefined();
|
|
48
|
+
expect(typeof firstPayee.deleted).toBe('boolean');
|
|
49
|
+
|
|
50
|
+
// Check for transfer payees
|
|
51
|
+
const transferPayees = parsedContent.payees.filter(
|
|
52
|
+
(p: any) => p.transfer_account_id !== null,
|
|
53
|
+
);
|
|
54
|
+
console.warn(`✅ Successfully listed ${parsedContent.payees.length} payees`);
|
|
55
|
+
console.warn(` - ${transferPayees.length} transfer payees`);
|
|
56
|
+
console.warn(` - ${parsedContent.payees.length - transferPayees.length} regular payees`);
|
|
57
|
+
},
|
|
58
|
+
);
|
|
59
|
+
|
|
60
|
+
it(
|
|
61
|
+
'should handle invalid budget ID gracefully',
|
|
62
|
+
{ meta: { tier: 'domain', domain: 'payees' } },
|
|
63
|
+
async () => {
|
|
64
|
+
const result = await handleListPayees(ynabAPI, { budget_id: 'invalid-budget-id' });
|
|
65
|
+
|
|
66
|
+
expect(result.content).toHaveLength(1);
|
|
67
|
+
expect(result.content[0].type).toBe('text');
|
|
68
|
+
|
|
69
|
+
const parsedContent = JSON.parse(result.content[0].text);
|
|
70
|
+
expect(parsedContent.error).toBeDefined();
|
|
71
|
+
expect(parsedContent.error.message).toBeDefined();
|
|
72
|
+
|
|
73
|
+
console.warn(`✅ Correctly handled invalid budget ID: ${parsedContent.error.message}`);
|
|
74
|
+
},
|
|
75
|
+
);
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
describe('handleGetPayee', () => {
|
|
79
|
+
it(
|
|
80
|
+
'should successfully get payee details from real API',
|
|
81
|
+
{ meta: { tier: 'domain', domain: 'payees' } },
|
|
82
|
+
async () => {
|
|
83
|
+
// Use the payee ID from the previous test
|
|
84
|
+
const result = await handleGetPayee(ynabAPI, {
|
|
85
|
+
budget_id: testBudgetId,
|
|
86
|
+
payee_id: testPayeeId,
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
expect(result.content).toHaveLength(1);
|
|
90
|
+
expect(result.content[0].type).toBe('text');
|
|
91
|
+
|
|
92
|
+
const parsedContent = JSON.parse(result.content[0].text);
|
|
93
|
+
expect(parsedContent.payee).toBeDefined();
|
|
94
|
+
|
|
95
|
+
const payee = parsedContent.payee;
|
|
96
|
+
expect(payee.id).toBe(testPayeeId);
|
|
97
|
+
expect(payee.name).toBeDefined();
|
|
98
|
+
expect(payee.deleted).toBeDefined();
|
|
99
|
+
expect(typeof payee.deleted).toBe('boolean');
|
|
100
|
+
|
|
101
|
+
console.warn(`✅ Successfully retrieved payee: ${payee.name}`);
|
|
102
|
+
if (payee.transfer_account_id) {
|
|
103
|
+
console.warn(` - Transfer payee for account: ${payee.transfer_account_id}`);
|
|
104
|
+
} else {
|
|
105
|
+
console.warn(` - Regular payee`);
|
|
106
|
+
}
|
|
107
|
+
},
|
|
108
|
+
);
|
|
109
|
+
|
|
110
|
+
it(
|
|
111
|
+
'should handle invalid payee ID gracefully',
|
|
112
|
+
{ meta: { tier: 'domain', domain: 'payees' } },
|
|
113
|
+
async () => {
|
|
114
|
+
const result = await handleGetPayee(ynabAPI, {
|
|
115
|
+
budget_id: testBudgetId,
|
|
116
|
+
payee_id: 'invalid-payee-id',
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
expect(result.content).toHaveLength(1);
|
|
120
|
+
expect(result.content[0].type).toBe('text');
|
|
121
|
+
|
|
122
|
+
const parsedContent = JSON.parse(result.content[0].text);
|
|
123
|
+
expect(parsedContent.error).toBeDefined();
|
|
124
|
+
expect(parsedContent.error.message).toBeDefined();
|
|
125
|
+
|
|
126
|
+
console.warn(`✅ Correctly handled invalid payee ID: ${parsedContent.error.message}`);
|
|
127
|
+
},
|
|
128
|
+
);
|
|
129
|
+
|
|
130
|
+
it(
|
|
131
|
+
'should handle invalid budget ID gracefully',
|
|
132
|
+
{ meta: { tier: 'domain', domain: 'payees' } },
|
|
133
|
+
async () => {
|
|
134
|
+
const result = await handleGetPayee(ynabAPI, {
|
|
135
|
+
budget_id: 'invalid-budget-id',
|
|
136
|
+
payee_id: testPayeeId,
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
expect(result.content).toHaveLength(1);
|
|
140
|
+
expect(result.content[0].type).toBe('text');
|
|
141
|
+
|
|
142
|
+
const parsedContent = JSON.parse(result.content[0].text);
|
|
143
|
+
expect(parsedContent.error).toBeDefined();
|
|
144
|
+
expect(parsedContent.error.message).toBeDefined();
|
|
145
|
+
|
|
146
|
+
console.warn(`✅ Correctly handled invalid budget ID: ${parsedContent.error.message}`);
|
|
147
|
+
},
|
|
148
|
+
);
|
|
149
|
+
});
|
|
150
|
+
});
|
|
@@ -0,0 +1,445 @@
|
|
|
1
|
+
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
|
2
|
+
import * as ynab from 'ynab';
|
|
3
|
+
import {
|
|
4
|
+
handleListPayees,
|
|
5
|
+
handleGetPayee,
|
|
6
|
+
ListPayeesSchema,
|
|
7
|
+
GetPayeeSchema,
|
|
8
|
+
} from '../payeeTools.js';
|
|
9
|
+
import { createDeltaFetcherMock, createRejectingDeltaFetcherMock } from './deltaTestUtils.js';
|
|
10
|
+
|
|
11
|
+
// Mock the cache manager
|
|
12
|
+
vi.mock('../../server/cacheManager.js', () => ({
|
|
13
|
+
cacheManager: {
|
|
14
|
+
wrap: vi.fn(),
|
|
15
|
+
has: vi.fn(),
|
|
16
|
+
delete: vi.fn(),
|
|
17
|
+
deleteMany: vi.fn(),
|
|
18
|
+
deleteByPrefix: vi.fn(),
|
|
19
|
+
deleteByBudgetId: vi.fn(),
|
|
20
|
+
clear: vi.fn(),
|
|
21
|
+
},
|
|
22
|
+
CacheManager: {
|
|
23
|
+
generateKey: vi.fn(),
|
|
24
|
+
},
|
|
25
|
+
CACHE_TTLS: {
|
|
26
|
+
PAYEES: 300000,
|
|
27
|
+
},
|
|
28
|
+
}));
|
|
29
|
+
|
|
30
|
+
// Mock the YNAB API
|
|
31
|
+
const mockYnabAPI = {
|
|
32
|
+
payees: {
|
|
33
|
+
getPayees: vi.fn(),
|
|
34
|
+
getPayeeById: vi.fn(),
|
|
35
|
+
},
|
|
36
|
+
} as unknown as ynab.API;
|
|
37
|
+
|
|
38
|
+
// Import mocked cache manager
|
|
39
|
+
const { cacheManager, CacheManager, CACHE_TTLS } = await import('../../server/cacheManager.js');
|
|
40
|
+
|
|
41
|
+
describe('Payee Tools', () => {
|
|
42
|
+
beforeEach(() => {
|
|
43
|
+
vi.resetAllMocks();
|
|
44
|
+
// Reset NODE_ENV to test to ensure cache bypassing in tests
|
|
45
|
+
process.env['NODE_ENV'] = 'test';
|
|
46
|
+
// Mock cache.wrap to call the loader directly (bypass cache)
|
|
47
|
+
(cacheManager.wrap as ReturnType<typeof vi.fn>).mockImplementation(
|
|
48
|
+
async (
|
|
49
|
+
_key: string,
|
|
50
|
+
options: {
|
|
51
|
+
loader: () => Promise<unknown>;
|
|
52
|
+
},
|
|
53
|
+
) => {
|
|
54
|
+
return await options.loader();
|
|
55
|
+
},
|
|
56
|
+
);
|
|
57
|
+
(cacheManager.has as ReturnType<typeof vi.fn>).mockReturnValue(false);
|
|
58
|
+
(CacheManager.generateKey as ReturnType<typeof vi.fn>).mockImplementation(
|
|
59
|
+
(prefix: string, ...parts: (string | number | boolean | undefined)[]) =>
|
|
60
|
+
[prefix, ...parts.filter((part) => part !== undefined)].join(':'),
|
|
61
|
+
);
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
describe('handleListPayees', () => {
|
|
65
|
+
it('should include cache metadata from delta fetcher results', async () => {
|
|
66
|
+
const mockPayees = [
|
|
67
|
+
{
|
|
68
|
+
id: 'payee-1',
|
|
69
|
+
name: 'Grocery Store',
|
|
70
|
+
transfer_account_id: null,
|
|
71
|
+
deleted: false,
|
|
72
|
+
},
|
|
73
|
+
{
|
|
74
|
+
id: 'payee-2',
|
|
75
|
+
name: 'Gas Station',
|
|
76
|
+
transfer_account_id: null,
|
|
77
|
+
deleted: false,
|
|
78
|
+
},
|
|
79
|
+
];
|
|
80
|
+
const { fetcher, resolved } = createDeltaFetcherMock('fetchPayees', {
|
|
81
|
+
data: mockPayees,
|
|
82
|
+
wasCached: true,
|
|
83
|
+
usedDelta: true,
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
const result = await handleListPayees(mockYnabAPI, fetcher, { budget_id: 'budget-1' });
|
|
87
|
+
|
|
88
|
+
const parsedContent = JSON.parse(result.content[0].text);
|
|
89
|
+
expect(parsedContent.cached).toBe(resolved.wasCached);
|
|
90
|
+
expect(parsedContent.cache_info).toContain('delta merge applied');
|
|
91
|
+
expect(parsedContent.payees).toHaveLength(2);
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
it.skip('should use cache when NODE_ENV is not test - obsolete test, caching now handled by DeltaFetcher', async () => {
|
|
95
|
+
// Temporarily set NODE_ENV to non-test
|
|
96
|
+
process.env['NODE_ENV'] = 'development';
|
|
97
|
+
|
|
98
|
+
const mockPayees = [
|
|
99
|
+
{
|
|
100
|
+
id: 'payee-1',
|
|
101
|
+
name: 'Grocery Store',
|
|
102
|
+
transfer_account_id: null,
|
|
103
|
+
deleted: false,
|
|
104
|
+
},
|
|
105
|
+
];
|
|
106
|
+
|
|
107
|
+
const mockCacheKey = 'payees:list:budget-1:generated-key';
|
|
108
|
+
(CacheManager.generateKey as any).mockReturnValue(mockCacheKey);
|
|
109
|
+
(cacheManager.wrap as any).mockResolvedValue(mockPayees);
|
|
110
|
+
(cacheManager.has as any).mockReturnValue(true);
|
|
111
|
+
|
|
112
|
+
const result = await handleListPayees(mockYnabAPI, { budget_id: 'budget-1' });
|
|
113
|
+
|
|
114
|
+
// Verify cache was used
|
|
115
|
+
expect(CacheManager.generateKey).toHaveBeenCalledWith('payees', 'list', 'budget-1');
|
|
116
|
+
expect(cacheManager.wrap).toHaveBeenCalledWith(mockCacheKey, {
|
|
117
|
+
ttl: CACHE_TTLS.PAYEES,
|
|
118
|
+
loader: expect.any(Function),
|
|
119
|
+
});
|
|
120
|
+
expect(cacheManager.has).toHaveBeenCalledWith(mockCacheKey);
|
|
121
|
+
|
|
122
|
+
const parsedContent = JSON.parse(result.content[0].text);
|
|
123
|
+
expect(parsedContent.cached).toBe(true);
|
|
124
|
+
expect(parsedContent.cache_info).toBe('Data retrieved from cache for improved performance');
|
|
125
|
+
|
|
126
|
+
// Reset NODE_ENV
|
|
127
|
+
process.env['NODE_ENV'] = 'test';
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
it('should return formatted payee list on success', async () => {
|
|
131
|
+
const mockPayees = [
|
|
132
|
+
{
|
|
133
|
+
id: 'payee-1',
|
|
134
|
+
name: 'Grocery Store',
|
|
135
|
+
transfer_account_id: null,
|
|
136
|
+
deleted: false,
|
|
137
|
+
},
|
|
138
|
+
{
|
|
139
|
+
id: 'payee-2',
|
|
140
|
+
name: 'Gas Station',
|
|
141
|
+
transfer_account_id: null,
|
|
142
|
+
deleted: false,
|
|
143
|
+
},
|
|
144
|
+
{
|
|
145
|
+
id: 'payee-3',
|
|
146
|
+
name: 'Transfer : Savings',
|
|
147
|
+
transfer_account_id: 'account-2',
|
|
148
|
+
deleted: false,
|
|
149
|
+
},
|
|
150
|
+
];
|
|
151
|
+
const { fetcher, resolved } = createDeltaFetcherMock('fetchPayees', {
|
|
152
|
+
data: mockPayees,
|
|
153
|
+
wasCached: false,
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
const result = await handleListPayees(mockYnabAPI, fetcher, { budget_id: 'budget-1' });
|
|
157
|
+
|
|
158
|
+
expect(result.content).toHaveLength(1);
|
|
159
|
+
expect(result.content[0].type).toBe('text');
|
|
160
|
+
|
|
161
|
+
const parsedContent = JSON.parse(result.content[0].text);
|
|
162
|
+
expect(parsedContent.payees).toHaveLength(3);
|
|
163
|
+
expect(parsedContent.cached).toBe(resolved.wasCached);
|
|
164
|
+
expect(parsedContent.cache_info).toBe('Fresh data retrieved from YNAB API');
|
|
165
|
+
expect(parsedContent.payees[0]).toEqual({
|
|
166
|
+
id: 'payee-1',
|
|
167
|
+
name: 'Grocery Store',
|
|
168
|
+
transfer_account_id: null,
|
|
169
|
+
deleted: false,
|
|
170
|
+
});
|
|
171
|
+
expect(parsedContent.payees[2]).toEqual({
|
|
172
|
+
id: 'payee-3',
|
|
173
|
+
name: 'Transfer : Savings',
|
|
174
|
+
transfer_account_id: 'account-2',
|
|
175
|
+
deleted: false,
|
|
176
|
+
});
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
it('should handle authentication errors', async () => {
|
|
180
|
+
const { fetcher } = createRejectingDeltaFetcherMock(
|
|
181
|
+
'fetchPayees',
|
|
182
|
+
new Error('401 Unauthorized'),
|
|
183
|
+
);
|
|
184
|
+
|
|
185
|
+
const result = await handleListPayees(mockYnabAPI, fetcher, { budget_id: 'budget-1' });
|
|
186
|
+
|
|
187
|
+
expect(result.content).toHaveLength(1);
|
|
188
|
+
const parsedContent = JSON.parse(result.content[0].text);
|
|
189
|
+
expect(parsedContent.error.message).toBe('Invalid or expired YNAB access token');
|
|
190
|
+
});
|
|
191
|
+
|
|
192
|
+
it('should handle forbidden errors', async () => {
|
|
193
|
+
const { fetcher } = createRejectingDeltaFetcherMock(
|
|
194
|
+
'fetchPayees',
|
|
195
|
+
new Error('403 Forbidden'),
|
|
196
|
+
);
|
|
197
|
+
|
|
198
|
+
const result = await handleListPayees(mockYnabAPI, fetcher, { budget_id: 'budget-1' });
|
|
199
|
+
|
|
200
|
+
expect(result.content).toHaveLength(1);
|
|
201
|
+
const parsedContent = JSON.parse(result.content[0].text);
|
|
202
|
+
expect(parsedContent.error.message).toBe('Insufficient permissions to access YNAB data');
|
|
203
|
+
});
|
|
204
|
+
|
|
205
|
+
it('should handle not found errors', async () => {
|
|
206
|
+
const { fetcher } = createRejectingDeltaFetcherMock(
|
|
207
|
+
'fetchPayees',
|
|
208
|
+
new Error('404 Not Found'),
|
|
209
|
+
);
|
|
210
|
+
|
|
211
|
+
const result = await handleListPayees(mockYnabAPI, fetcher, { budget_id: 'invalid-budget' });
|
|
212
|
+
|
|
213
|
+
expect(result.content).toHaveLength(1);
|
|
214
|
+
const parsedContent = JSON.parse(result.content[0].text);
|
|
215
|
+
expect(parsedContent.error.message).toBe('Budget or payee not found');
|
|
216
|
+
});
|
|
217
|
+
|
|
218
|
+
it('should handle rate limit errors', async () => {
|
|
219
|
+
const { fetcher } = createRejectingDeltaFetcherMock(
|
|
220
|
+
'fetchPayees',
|
|
221
|
+
new Error('429 Too Many Requests'),
|
|
222
|
+
);
|
|
223
|
+
|
|
224
|
+
const result = await handleListPayees(mockYnabAPI, fetcher, { budget_id: 'budget-1' });
|
|
225
|
+
|
|
226
|
+
expect(result.content).toHaveLength(1);
|
|
227
|
+
const parsedContent = JSON.parse(result.content[0].text);
|
|
228
|
+
expect(parsedContent.error.message).toBe('Rate limit exceeded. Please try again later');
|
|
229
|
+
});
|
|
230
|
+
|
|
231
|
+
it('should handle server errors', async () => {
|
|
232
|
+
const { fetcher } = createRejectingDeltaFetcherMock(
|
|
233
|
+
'fetchPayees',
|
|
234
|
+
new Error('500 Internal Server Error'),
|
|
235
|
+
);
|
|
236
|
+
|
|
237
|
+
const result = await handleListPayees(mockYnabAPI, fetcher, { budget_id: 'budget-1' });
|
|
238
|
+
|
|
239
|
+
expect(result.content).toHaveLength(1);
|
|
240
|
+
const parsedContent = JSON.parse(result.content[0].text);
|
|
241
|
+
expect(parsedContent.error.message).toBe('YNAB service is currently unavailable');
|
|
242
|
+
});
|
|
243
|
+
|
|
244
|
+
it('should handle generic errors', async () => {
|
|
245
|
+
const { fetcher } = createRejectingDeltaFetcherMock(
|
|
246
|
+
'fetchPayees',
|
|
247
|
+
new Error('Network error'),
|
|
248
|
+
);
|
|
249
|
+
|
|
250
|
+
const result = await handleListPayees(mockYnabAPI, fetcher, { budget_id: 'budget-1' });
|
|
251
|
+
|
|
252
|
+
expect(result.content).toHaveLength(1);
|
|
253
|
+
const parsedContent = JSON.parse(result.content[0].text);
|
|
254
|
+
expect(parsedContent.error.message).toBe('Failed to list payees');
|
|
255
|
+
});
|
|
256
|
+
});
|
|
257
|
+
|
|
258
|
+
describe('handleGetPayee', () => {
|
|
259
|
+
it.skip('should use cache when NODE_ENV is not test - obsolete test, caching now handled by DeltaFetcher', async () => {
|
|
260
|
+
// Temporarily set NODE_ENV to non-test
|
|
261
|
+
process.env['NODE_ENV'] = 'development';
|
|
262
|
+
|
|
263
|
+
const mockPayee = {
|
|
264
|
+
id: 'payee-1',
|
|
265
|
+
name: 'Grocery Store',
|
|
266
|
+
transfer_account_id: null,
|
|
267
|
+
deleted: false,
|
|
268
|
+
};
|
|
269
|
+
|
|
270
|
+
const mockCacheKey = 'payee:get:budget-1:payee-1:generated-key';
|
|
271
|
+
(CacheManager.generateKey as any).mockReturnValue(mockCacheKey);
|
|
272
|
+
(cacheManager.wrap as any).mockResolvedValue(mockPayee);
|
|
273
|
+
(cacheManager.has as any).mockReturnValue(true);
|
|
274
|
+
|
|
275
|
+
const result = await handleGetPayee(mockYnabAPI, {
|
|
276
|
+
budget_id: 'budget-1',
|
|
277
|
+
payee_id: 'payee-1',
|
|
278
|
+
});
|
|
279
|
+
|
|
280
|
+
// Verify cache was used
|
|
281
|
+
expect(CacheManager.generateKey).toHaveBeenCalledWith('payee', 'get', 'budget-1', 'payee-1');
|
|
282
|
+
expect(cacheManager.wrap).toHaveBeenCalledWith(mockCacheKey, {
|
|
283
|
+
ttl: CACHE_TTLS.PAYEES,
|
|
284
|
+
loader: expect.any(Function),
|
|
285
|
+
});
|
|
286
|
+
|
|
287
|
+
const parsedContent = JSON.parse(result.content[0].text);
|
|
288
|
+
expect(parsedContent.cached).toBe(true);
|
|
289
|
+
expect(parsedContent.cache_info).toBe('Data retrieved from cache for improved performance');
|
|
290
|
+
|
|
291
|
+
// Reset NODE_ENV
|
|
292
|
+
process.env['NODE_ENV'] = 'test';
|
|
293
|
+
});
|
|
294
|
+
|
|
295
|
+
it('should return detailed payee information on success', async () => {
|
|
296
|
+
const mockPayee = {
|
|
297
|
+
id: 'payee-1',
|
|
298
|
+
name: 'Grocery Store',
|
|
299
|
+
transfer_account_id: null,
|
|
300
|
+
deleted: false,
|
|
301
|
+
};
|
|
302
|
+
|
|
303
|
+
(mockYnabAPI.payees.getPayeeById as any).mockResolvedValue({
|
|
304
|
+
data: { payee: mockPayee },
|
|
305
|
+
});
|
|
306
|
+
|
|
307
|
+
const result = await handleGetPayee(mockYnabAPI, {
|
|
308
|
+
budget_id: 'budget-1',
|
|
309
|
+
payee_id: 'payee-1',
|
|
310
|
+
});
|
|
311
|
+
|
|
312
|
+
expect(result.content).toHaveLength(1);
|
|
313
|
+
expect(result.content[0].type).toBe('text');
|
|
314
|
+
|
|
315
|
+
const parsedContent = JSON.parse(result.content[0].text);
|
|
316
|
+
expect(parsedContent.payee).toEqual({
|
|
317
|
+
id: 'payee-1',
|
|
318
|
+
name: 'Grocery Store',
|
|
319
|
+
transfer_account_id: null,
|
|
320
|
+
deleted: false,
|
|
321
|
+
});
|
|
322
|
+
});
|
|
323
|
+
|
|
324
|
+
it('should return transfer payee information on success', async () => {
|
|
325
|
+
const mockPayee = {
|
|
326
|
+
id: 'payee-2',
|
|
327
|
+
name: 'Transfer : Savings',
|
|
328
|
+
transfer_account_id: 'account-2',
|
|
329
|
+
deleted: false,
|
|
330
|
+
};
|
|
331
|
+
|
|
332
|
+
(mockYnabAPI.payees.getPayeeById as any).mockResolvedValue({
|
|
333
|
+
data: { payee: mockPayee },
|
|
334
|
+
});
|
|
335
|
+
|
|
336
|
+
const result = await handleGetPayee(mockYnabAPI, {
|
|
337
|
+
budget_id: 'budget-1',
|
|
338
|
+
payee_id: 'payee-2',
|
|
339
|
+
});
|
|
340
|
+
|
|
341
|
+
expect(result.content).toHaveLength(1);
|
|
342
|
+
const parsedContent = JSON.parse(result.content[0].text);
|
|
343
|
+
expect(parsedContent.payee.transfer_account_id).toBe('account-2');
|
|
344
|
+
});
|
|
345
|
+
|
|
346
|
+
it('should handle 404 not found errors', async () => {
|
|
347
|
+
(mockYnabAPI.payees.getPayeeById as any).mockRejectedValue(new Error('404 Not Found'));
|
|
348
|
+
|
|
349
|
+
const result = await handleGetPayee(mockYnabAPI, {
|
|
350
|
+
budget_id: 'budget-1',
|
|
351
|
+
payee_id: 'invalid-payee',
|
|
352
|
+
});
|
|
353
|
+
|
|
354
|
+
expect(result.content).toHaveLength(1);
|
|
355
|
+
const parsedContent = JSON.parse(result.content[0].text);
|
|
356
|
+
expect(parsedContent.error.message).toBe('Budget or payee not found');
|
|
357
|
+
});
|
|
358
|
+
|
|
359
|
+
it('should handle authentication errors', async () => {
|
|
360
|
+
(mockYnabAPI.payees.getPayeeById as any).mockRejectedValue(new Error('401 Unauthorized'));
|
|
361
|
+
|
|
362
|
+
const result = await handleGetPayee(mockYnabAPI, {
|
|
363
|
+
budget_id: 'budget-1',
|
|
364
|
+
payee_id: 'payee-1',
|
|
365
|
+
});
|
|
366
|
+
|
|
367
|
+
expect(result.content).toHaveLength(1);
|
|
368
|
+
const parsedContent = JSON.parse(result.content[0].text);
|
|
369
|
+
expect(parsedContent.error.message).toBe('Invalid or expired YNAB access token');
|
|
370
|
+
});
|
|
371
|
+
});
|
|
372
|
+
|
|
373
|
+
describe('ListPayeesSchema', () => {
|
|
374
|
+
it('should validate valid budget_id', () => {
|
|
375
|
+
const result = ListPayeesSchema.parse({ budget_id: 'valid-budget-id' });
|
|
376
|
+
expect(result.budget_id).toBe('valid-budget-id');
|
|
377
|
+
});
|
|
378
|
+
|
|
379
|
+
it('should reject empty budget_id', () => {
|
|
380
|
+
expect(() => ListPayeesSchema.parse({ budget_id: '' })).toThrow();
|
|
381
|
+
});
|
|
382
|
+
|
|
383
|
+
it('should reject missing budget_id', () => {
|
|
384
|
+
expect(() => ListPayeesSchema.parse({})).toThrow();
|
|
385
|
+
});
|
|
386
|
+
|
|
387
|
+
it('should reject non-string budget_id', () => {
|
|
388
|
+
expect(() => ListPayeesSchema.parse({ budget_id: 123 })).toThrow();
|
|
389
|
+
});
|
|
390
|
+
});
|
|
391
|
+
|
|
392
|
+
describe('GetPayeeSchema', () => {
|
|
393
|
+
it('should validate valid parameters', () => {
|
|
394
|
+
const result = GetPayeeSchema.parse({
|
|
395
|
+
budget_id: 'valid-budget-id',
|
|
396
|
+
payee_id: 'valid-payee-id',
|
|
397
|
+
});
|
|
398
|
+
expect(result.budget_id).toBe('valid-budget-id');
|
|
399
|
+
expect(result.payee_id).toBe('valid-payee-id');
|
|
400
|
+
});
|
|
401
|
+
|
|
402
|
+
it('should reject empty budget_id', () => {
|
|
403
|
+
expect(() =>
|
|
404
|
+
GetPayeeSchema.parse({
|
|
405
|
+
budget_id: '',
|
|
406
|
+
payee_id: 'valid-payee-id',
|
|
407
|
+
}),
|
|
408
|
+
).toThrow();
|
|
409
|
+
});
|
|
410
|
+
|
|
411
|
+
it('should reject empty payee_id', () => {
|
|
412
|
+
expect(() =>
|
|
413
|
+
GetPayeeSchema.parse({
|
|
414
|
+
budget_id: 'valid-budget-id',
|
|
415
|
+
payee_id: '',
|
|
416
|
+
}),
|
|
417
|
+
).toThrow();
|
|
418
|
+
});
|
|
419
|
+
|
|
420
|
+
it('should reject missing budget_id', () => {
|
|
421
|
+
expect(() =>
|
|
422
|
+
GetPayeeSchema.parse({
|
|
423
|
+
payee_id: 'valid-payee-id',
|
|
424
|
+
}),
|
|
425
|
+
).toThrow();
|
|
426
|
+
});
|
|
427
|
+
|
|
428
|
+
it('should reject missing payee_id', () => {
|
|
429
|
+
expect(() =>
|
|
430
|
+
GetPayeeSchema.parse({
|
|
431
|
+
budget_id: 'valid-budget-id',
|
|
432
|
+
}),
|
|
433
|
+
).toThrow();
|
|
434
|
+
});
|
|
435
|
+
|
|
436
|
+
it('should reject non-string parameters', () => {
|
|
437
|
+
expect(() =>
|
|
438
|
+
GetPayeeSchema.parse({
|
|
439
|
+
budget_id: 123,
|
|
440
|
+
payee_id: 456,
|
|
441
|
+
}),
|
|
442
|
+
).toThrow();
|
|
443
|
+
});
|
|
444
|
+
});
|
|
445
|
+
});
|