@dizzlkheinz/ynab-mcpb 0.18.3 → 0.19.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/CHANGELOG.md +17 -0
- package/CLAUDE.md +87 -8
- package/bin/ynab-mcp-server.cjs +2 -2
- package/bin/ynab-mcp-server.js +3 -3
- package/biome.json +39 -0
- package/dist/bundle/index.cjs +67 -67
- package/dist/index.d.ts +1 -1
- package/dist/index.js +27 -27
- package/dist/server/YNABMCPServer.d.ts +3 -4
- package/dist/server/YNABMCPServer.js +111 -116
- package/dist/server/budgetResolver.d.ts +6 -5
- package/dist/server/budgetResolver.js +46 -36
- package/dist/server/cacheKeys.js +6 -6
- package/dist/server/cacheManager.js +14 -11
- package/dist/server/completions.d.ts +2 -2
- package/dist/server/completions.js +20 -15
- package/dist/server/config.d.ts +10 -5
- package/dist/server/config.js +24 -7
- package/dist/server/deltaCache.d.ts +2 -2
- package/dist/server/deltaCache.js +22 -16
- package/dist/server/deltaCache.merge.d.ts +2 -2
- package/dist/server/diagnostics.d.ts +4 -4
- package/dist/server/diagnostics.js +38 -32
- package/dist/server/errorHandler.d.ts +5 -12
- package/dist/server/errorHandler.js +219 -217
- package/dist/server/prompts.d.ts +2 -2
- package/dist/server/prompts.js +45 -45
- package/dist/server/rateLimiter.js +4 -4
- package/dist/server/requestLogger.d.ts +1 -1
- package/dist/server/requestLogger.js +40 -35
- package/dist/server/resources.d.ts +3 -3
- package/dist/server/resources.js +55 -52
- package/dist/server/responseFormatter.js +6 -6
- package/dist/server/securityMiddleware.d.ts +2 -2
- package/dist/server/securityMiddleware.js +22 -20
- package/dist/server/serverKnowledgeStore.js +1 -1
- package/dist/server/toolRegistry.d.ts +3 -3
- package/dist/server/toolRegistry.js +47 -40
- package/dist/tools/__tests__/deltaTestUtils.d.ts +3 -3
- package/dist/tools/__tests__/deltaTestUtils.js +2 -2
- package/dist/tools/accountTools.d.ts +9 -8
- package/dist/tools/accountTools.js +47 -47
- package/dist/tools/adapters.d.ts +13 -8
- package/dist/tools/adapters.js +21 -11
- package/dist/tools/budgetTools.d.ts +8 -7
- package/dist/tools/budgetTools.js +22 -22
- package/dist/tools/categoryTools.d.ts +9 -8
- package/dist/tools/categoryTools.js +68 -59
- package/dist/tools/compareTransactions/formatter.d.ts +3 -3
- package/dist/tools/compareTransactions/formatter.js +9 -9
- package/dist/tools/compareTransactions/index.d.ts +6 -6
- package/dist/tools/compareTransactions/index.js +58 -43
- package/dist/tools/compareTransactions/matcher.d.ts +1 -1
- package/dist/tools/compareTransactions/matcher.js +28 -15
- package/dist/tools/compareTransactions/parser.d.ts +2 -2
- package/dist/tools/compareTransactions/parser.js +144 -138
- package/dist/tools/compareTransactions/types.d.ts +4 -4
- package/dist/tools/compareTransactions.d.ts +1 -1
- package/dist/tools/compareTransactions.js +1 -1
- package/dist/tools/deltaFetcher.d.ts +2 -2
- package/dist/tools/deltaFetcher.js +16 -15
- package/dist/tools/deltaSupport.d.ts +4 -4
- package/dist/tools/deltaSupport.js +35 -41
- package/dist/tools/exportTransactions.d.ts +5 -4
- package/dist/tools/exportTransactions.js +61 -59
- package/dist/tools/monthTools.d.ts +7 -6
- package/dist/tools/monthTools.js +31 -29
- package/dist/tools/payeeTools.d.ts +7 -6
- package/dist/tools/payeeTools.js +28 -28
- package/dist/tools/reconcileAdapter.d.ts +2 -2
- package/dist/tools/reconcileAdapter.js +21 -11
- package/dist/tools/reconciliation/analyzer.d.ts +4 -4
- package/dist/tools/reconciliation/analyzer.js +136 -57
- package/dist/tools/reconciliation/csvParser.d.ts +3 -3
- package/dist/tools/reconciliation/csvParser.js +128 -104
- package/dist/tools/reconciliation/executor.d.ts +4 -4
- package/dist/tools/reconciliation/executor.js +148 -109
- package/dist/tools/reconciliation/index.d.ts +10 -10
- package/dist/tools/reconciliation/index.js +96 -83
- package/dist/tools/reconciliation/matcher.d.ts +3 -3
- package/dist/tools/reconciliation/matcher.js +17 -16
- package/dist/tools/reconciliation/payeeNormalizer.js +19 -8
- package/dist/tools/reconciliation/recommendationEngine.d.ts +1 -1
- package/dist/tools/reconciliation/recommendationEngine.js +40 -40
- package/dist/tools/reconciliation/reportFormatter.d.ts +2 -2
- package/dist/tools/reconciliation/reportFormatter.js +79 -54
- package/dist/tools/reconciliation/signDetector.d.ts +1 -1
- package/dist/tools/reconciliation/types.d.ts +19 -16
- package/dist/tools/reconciliation/ynabAdapter.d.ts +2 -2
- package/dist/tools/schemas/common.d.ts +1 -1
- package/dist/tools/schemas/common.js +1 -1
- package/dist/tools/schemas/outputs/accountOutputs.d.ts +1 -1
- package/dist/tools/schemas/outputs/accountOutputs.js +24 -18
- package/dist/tools/schemas/outputs/budgetOutputs.d.ts +1 -1
- package/dist/tools/schemas/outputs/budgetOutputs.js +14 -11
- package/dist/tools/schemas/outputs/categoryOutputs.d.ts +1 -1
- package/dist/tools/schemas/outputs/categoryOutputs.js +49 -29
- package/dist/tools/schemas/outputs/comparisonOutputs.d.ts +1 -1
- package/dist/tools/schemas/outputs/comparisonOutputs.js +12 -12
- package/dist/tools/schemas/outputs/index.d.ts +14 -14
- package/dist/tools/schemas/outputs/index.js +14 -14
- package/dist/tools/schemas/outputs/monthOutputs.d.ts +1 -1
- package/dist/tools/schemas/outputs/monthOutputs.js +56 -41
- package/dist/tools/schemas/outputs/payeeOutputs.d.ts +1 -1
- package/dist/tools/schemas/outputs/payeeOutputs.js +10 -10
- package/dist/tools/schemas/outputs/reconciliationOutputs.d.ts +2 -2
- package/dist/tools/schemas/outputs/reconciliationOutputs.js +45 -45
- package/dist/tools/schemas/outputs/transactionMutationOutputs.d.ts +1 -1
- package/dist/tools/schemas/outputs/transactionMutationOutputs.js +28 -22
- package/dist/tools/schemas/outputs/transactionOutputs.d.ts +1 -1
- package/dist/tools/schemas/outputs/transactionOutputs.js +43 -35
- package/dist/tools/schemas/outputs/utilityOutputs.d.ts +1 -1
- package/dist/tools/schemas/outputs/utilityOutputs.js +5 -3
- package/dist/tools/schemas/shared/commonOutputs.d.ts +1 -1
- package/dist/tools/schemas/shared/commonOutputs.js +15 -9
- package/dist/tools/transactionReadTools.d.ts +11 -0
- package/dist/tools/transactionReadTools.js +202 -0
- package/dist/tools/transactionSchemas.d.ts +309 -0
- package/dist/tools/transactionSchemas.js +235 -0
- package/dist/tools/transactionTools.d.ts +6 -302
- package/dist/tools/transactionTools.js +7 -2054
- package/dist/tools/transactionUtils.d.ts +31 -0
- package/dist/tools/transactionUtils.js +364 -0
- package/dist/tools/transactionWriteTools.d.ts +20 -0
- package/dist/tools/transactionWriteTools.js +1342 -0
- package/dist/tools/utilityTools.d.ts +5 -4
- package/dist/tools/utilityTools.js +11 -11
- package/dist/types/index.d.ts +7 -7
- package/dist/types/index.js +6 -6
- package/dist/types/reconciliation.d.ts +1 -1
- package/dist/types/toolRegistration.d.ts +14 -12
- package/dist/utils/amountUtils.js +1 -1
- package/dist/utils/dateUtils.js +4 -4
- package/dist/utils/errors.d.ts +3 -3
- package/dist/utils/errors.js +4 -4
- package/dist/utils/money.d.ts +2 -2
- package/dist/utils/money.js +8 -8
- package/dist/utils/validationError.d.ts +1 -1
- package/dist/utils/validationError.js +1 -1
- package/docs/assets/examples/reconciliation-with-recommendations.json +66 -66
- package/docs/assets/schemas/reconciliation-v2.json +360 -336
- package/docs/plans/2025-12-25-transaction-tools-refactor-design.md +211 -0
- package/docs/plans/2025-12-25-transaction-tools-refactor.md +905 -0
- package/esbuild.config.mjs +53 -50
- package/meta.json +12548 -12548
- package/package.json +98 -109
- package/scripts/analyze-bundle.mjs +33 -30
- package/scripts/create-pr-description.js +169 -120
- package/scripts/run-all-tests.js +205 -0
- package/scripts/run-domain-integration-tests.js +28 -18
- package/scripts/run-generate-mcpb.js +19 -17
- package/scripts/run-throttled-integration-tests.js +92 -83
- package/scripts/test-delta-params.mjs +149 -120
- package/scripts/test-recommendations.ts +36 -32
- package/scripts/tmpTransaction.ts +80 -43
- package/scripts/validate-env.js +98 -91
- package/scripts/verify-build.js +78 -76
- package/src/__tests__/comprehensive.integration.test.ts +1281 -1154
- package/src/__tests__/performance.test.ts +723 -671
- package/src/__tests__/setup.ts +442 -395
- package/src/__tests__/smoke.e2e.test.ts +41 -39
- package/src/__tests__/testRunner.ts +314 -295
- package/src/__tests__/testUtils.ts +456 -364
- package/src/__tests__/tools/reconciliation/csvParser.integration.test.ts +109 -107
- package/src/__tests__/tools/reconciliation/real-world.integration.test.ts +41 -41
- package/src/index.ts +68 -59
- package/src/server/CLAUDE.md +480 -0
- package/src/server/YNABMCPServer.ts +821 -794
- package/src/server/__tests__/YNABMCPServer.integration.test.ts +929 -893
- package/src/server/__tests__/YNABMCPServer.test.ts +903 -899
- package/src/server/__tests__/budgetResolver.test.ts +466 -423
- package/src/server/__tests__/cacheManager.test.ts +891 -874
- package/src/server/__tests__/completions.integration.test.ts +115 -106
- package/src/server/__tests__/completions.test.ts +334 -313
- package/src/server/__tests__/config.test.ts +98 -86
- package/src/server/__tests__/deltaCache.merge.test.ts +774 -703
- package/src/server/__tests__/deltaCache.swr.test.ts +198 -153
- package/src/server/__tests__/deltaCache.test.ts +946 -759
- package/src/server/__tests__/diagnostics.test.ts +825 -792
- package/src/server/__tests__/errorHandler.integration.test.ts +512 -462
- package/src/server/__tests__/errorHandler.test.ts +402 -397
- package/src/server/__tests__/prompts.test.ts +424 -347
- package/src/server/__tests__/rateLimiter.test.ts +313 -309
- package/src/server/__tests__/requestLogger.test.ts +443 -403
- package/src/server/__tests__/resources.template.test.ts +196 -185
- package/src/server/__tests__/resources.test.ts +294 -288
- package/src/server/__tests__/security.integration.test.ts +487 -421
- package/src/server/__tests__/securityMiddleware.test.ts +519 -444
- package/src/server/__tests__/server-startup.integration.test.ts +509 -490
- package/src/server/__tests__/serverKnowledgeStore.test.ts +174 -173
- package/src/server/__tests__/toolRegistration.test.ts +239 -210
- package/src/server/__tests__/toolRegistry.test.ts +907 -845
- package/src/server/budgetResolver.ts +221 -181
- package/src/server/cacheKeys.ts +6 -6
- package/src/server/cacheManager.ts +498 -484
- package/src/server/completions.ts +267 -243
- package/src/server/config.ts +35 -14
- package/src/server/deltaCache.merge.ts +146 -128
- package/src/server/deltaCache.ts +352 -309
- package/src/server/diagnostics.ts +257 -242
- package/src/server/errorHandler.ts +747 -744
- package/src/server/prompts.ts +181 -176
- package/src/server/rateLimiter.ts +131 -129
- package/src/server/requestLogger.ts +350 -322
- package/src/server/resources.ts +442 -374
- package/src/server/responseFormatter.ts +41 -37
- package/src/server/securityMiddleware.ts +223 -205
- package/src/server/serverKnowledgeStore.ts +67 -67
- package/src/server/toolRegistry.ts +508 -474
- package/src/tools/CLAUDE.md +604 -0
- package/src/tools/__tests__/accountTools.delta.integration.test.ts +128 -111
- package/src/tools/__tests__/accountTools.integration.test.ts +129 -111
- package/src/tools/__tests__/accountTools.test.ts +685 -638
- package/src/tools/__tests__/adapters.test.ts +142 -108
- package/src/tools/__tests__/budgetTools.delta.integration.test.ts +73 -73
- package/src/tools/__tests__/budgetTools.integration.test.ts +132 -124
- package/src/tools/__tests__/budgetTools.test.ts +442 -413
- package/src/tools/__tests__/categoryTools.delta.integration.test.ts +76 -68
- package/src/tools/__tests__/categoryTools.integration.test.ts +314 -288
- package/src/tools/__tests__/categoryTools.test.ts +656 -625
- package/src/tools/__tests__/compareTransactions/formatter.test.ts +535 -462
- package/src/tools/__tests__/compareTransactions/index.test.ts +378 -358
- package/src/tools/__tests__/compareTransactions/matcher.test.ts +497 -398
- package/src/tools/__tests__/compareTransactions/parser.test.ts +765 -747
- package/src/tools/__tests__/compareTransactions.test.ts +352 -332
- package/src/tools/__tests__/compareTransactions.window.test.ts +150 -146
- package/src/tools/__tests__/deltaFetcher.scheduled.integration.test.ts +69 -65
- package/src/tools/__tests__/deltaFetcher.test.ts +325 -265
- package/src/tools/__tests__/deltaSupport.test.ts +211 -184
- package/src/tools/__tests__/deltaTestUtils.ts +37 -33
- package/src/tools/__tests__/exportTransactions.test.ts +205 -200
- package/src/tools/__tests__/monthTools.delta.integration.test.ts +68 -68
- package/src/tools/__tests__/monthTools.integration.test.ts +178 -166
- package/src/tools/__tests__/monthTools.test.ts +561 -512
- package/src/tools/__tests__/payeeTools.delta.integration.test.ts +68 -68
- package/src/tools/__tests__/payeeTools.integration.test.ts +158 -142
- package/src/tools/__tests__/payeeTools.test.ts +486 -434
- package/src/tools/__tests__/transactionSchemas.test.ts +1204 -0
- package/src/tools/__tests__/transactionTools.integration.test.ts +875 -825
- package/src/tools/__tests__/transactionTools.test.ts +4923 -4366
- package/src/tools/__tests__/transactionUtils.test.ts +1016 -0
- package/src/tools/__tests__/utilityTools.integration.test.ts +32 -32
- package/src/tools/__tests__/utilityTools.test.ts +68 -58
- package/src/tools/accountTools.ts +293 -271
- package/src/tools/adapters.ts +120 -63
- package/src/tools/budgetTools.ts +121 -116
- package/src/tools/categoryTools.ts +379 -339
- package/src/tools/compareTransactions/formatter.ts +131 -119
- package/src/tools/compareTransactions/index.ts +249 -214
- package/src/tools/compareTransactions/matcher.ts +259 -209
- package/src/tools/compareTransactions/parser.ts +517 -487
- package/src/tools/compareTransactions/types.ts +38 -38
- package/src/tools/compareTransactions.ts +1 -1
- package/src/tools/deltaFetcher.ts +281 -260
- package/src/tools/deltaSupport.ts +264 -259
- package/src/tools/exportTransactions.ts +230 -218
- package/src/tools/monthTools.ts +180 -165
- package/src/tools/payeeTools.ts +152 -140
- package/src/tools/reconcileAdapter.ts +297 -246
- package/src/tools/reconciliation/CLAUDE.md +506 -0
- package/src/tools/reconciliation/__tests__/adapter.causes.test.ts +135 -112
- package/src/tools/reconciliation/__tests__/adapter.test.ts +249 -227
- package/src/tools/reconciliation/__tests__/analyzer.test.ts +408 -335
- package/src/tools/reconciliation/__tests__/csvParser.test.ts +71 -69
- package/src/tools/reconciliation/__tests__/executor.integration.test.ts +348 -323
- package/src/tools/reconciliation/__tests__/executor.progress.test.ts +503 -457
- package/src/tools/reconciliation/__tests__/executor.test.ts +898 -831
- package/src/tools/reconciliation/__tests__/matcher.test.ts +667 -663
- package/src/tools/reconciliation/__tests__/payeeNormalizer.test.ts +296 -276
- package/src/tools/reconciliation/__tests__/recommendationEngine.integration.test.ts +692 -624
- package/src/tools/reconciliation/__tests__/recommendationEngine.test.ts +1008 -986
- package/src/tools/reconciliation/__tests__/reconciliation.delta.integration.test.ts +187 -146
- package/src/tools/reconciliation/__tests__/reportFormatter.test.ts +583 -530
- package/src/tools/reconciliation/__tests__/scenarios/adapterCurrency.scenario.test.ts +75 -71
- package/src/tools/reconciliation/__tests__/scenarios/extremes.scenario.test.ts +70 -58
- package/src/tools/reconciliation/__tests__/scenarios/repeatAmount.scenario.test.ts +102 -88
- package/src/tools/reconciliation/__tests__/schemaUrl.test.ts +58 -43
- package/src/tools/reconciliation/__tests__/signDetector.test.ts +209 -206
- package/src/tools/reconciliation/__tests__/ynabAdapter.test.ts +66 -60
- package/src/tools/reconciliation/analyzer.ts +582 -406
- package/src/tools/reconciliation/csvParser.ts +656 -609
- package/src/tools/reconciliation/executor.ts +1290 -1128
- package/src/tools/reconciliation/index.ts +580 -528
- package/src/tools/reconciliation/matcher.ts +256 -240
- package/src/tools/reconciliation/payeeNormalizer.ts +92 -78
- package/src/tools/reconciliation/recommendationEngine.ts +357 -345
- package/src/tools/reconciliation/reportFormatter.ts +349 -276
- package/src/tools/reconciliation/signDetector.ts +89 -83
- package/src/tools/reconciliation/types.ts +164 -153
- package/src/tools/reconciliation/ynabAdapter.ts +17 -15
- package/src/tools/schemas/CLAUDE.md +546 -0
- package/src/tools/schemas/common.ts +1 -1
- package/src/tools/schemas/outputs/__tests__/accountOutputs.test.ts +410 -409
- package/src/tools/schemas/outputs/__tests__/budgetOutputs.test.ts +305 -299
- package/src/tools/schemas/outputs/__tests__/categoryOutputs.test.ts +431 -430
- package/src/tools/schemas/outputs/__tests__/comparisonOutputs.test.ts +510 -495
- package/src/tools/schemas/outputs/__tests__/dateValidation.test.ts +179 -153
- package/src/tools/schemas/outputs/__tests__/discrepancyDirection.test.ts +293 -254
- package/src/tools/schemas/outputs/__tests__/monthOutputs.test.ts +457 -457
- package/src/tools/schemas/outputs/__tests__/payeeOutputs.test.ts +362 -356
- package/src/tools/schemas/outputs/__tests__/reconciliationOutputs.test.ts +402 -399
- package/src/tools/schemas/outputs/__tests__/transactionMutationSchemas.test.ts +225 -211
- package/src/tools/schemas/outputs/__tests__/transactionOutputs.test.ts +457 -454
- package/src/tools/schemas/outputs/__tests__/utilityOutputs.test.ts +316 -315
- package/src/tools/schemas/outputs/accountOutputs.ts +40 -34
- package/src/tools/schemas/outputs/budgetOutputs.ts +24 -19
- package/src/tools/schemas/outputs/categoryOutputs.ts +76 -56
- package/src/tools/schemas/outputs/comparisonOutputs.ts +192 -169
- package/src/tools/schemas/outputs/index.ts +163 -163
- package/src/tools/schemas/outputs/monthOutputs.ts +95 -80
- package/src/tools/schemas/outputs/payeeOutputs.ts +18 -18
- package/src/tools/schemas/outputs/reconciliationOutputs.ts +386 -373
- package/src/tools/schemas/outputs/transactionMutationOutputs.ts +259 -231
- package/src/tools/schemas/outputs/transactionOutputs.ts +81 -71
- package/src/tools/schemas/outputs/utilityOutputs.ts +90 -84
- package/src/tools/schemas/shared/commonOutputs.ts +27 -19
- package/src/tools/toolCategories.ts +114 -114
- package/src/tools/transactionReadTools.ts +327 -0
- package/src/tools/transactionSchemas.ts +484 -0
- package/src/tools/transactionTools.ts +107 -2990
- package/src/tools/transactionUtils.ts +621 -0
- package/src/tools/transactionWriteTools.ts +2110 -0
- package/src/tools/utilityTools.ts +46 -41
- package/src/types/CLAUDE.md +477 -0
- package/src/types/__tests__/index.test.ts +51 -51
- package/src/types/index.ts +43 -39
- package/src/types/integration-tests.d.ts +26 -26
- package/src/types/reconciliation.ts +29 -29
- package/src/types/toolAnnotations.ts +30 -30
- package/src/types/toolRegistration.ts +43 -32
- package/src/utils/CLAUDE.md +508 -0
- package/src/utils/__tests__/dateUtils.test.ts +174 -168
- package/src/utils/__tests__/money.test.ts +193 -187
- package/src/utils/amountUtils.ts +5 -5
- package/src/utils/baseError.ts +5 -5
- package/src/utils/dateUtils.ts +29 -26
- package/src/utils/errors.ts +14 -14
- package/src/utils/money.ts +66 -52
- package/src/utils/validationError.ts +1 -1
- package/tsconfig.json +29 -29
- package/tsconfig.prod.json +16 -16
- package/vitest-reporters/split-json-reporter.ts +247 -204
- package/vitest.config.ts +99 -95
- package/.prettierignore +0 -10
- package/.prettierrc.json +0 -10
- package/eslint.config.js +0 -49
package/src/__tests__/setup.ts
CHANGED
|
@@ -3,475 +3,522 @@
|
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
5
|
// Load environment variables from .env for integration tests
|
|
6
|
-
import
|
|
7
|
-
import {
|
|
8
|
-
import { cacheManager } from
|
|
6
|
+
import "dotenv/config";
|
|
7
|
+
import { afterAll, afterEach, beforeAll, beforeEach } from "vitest";
|
|
8
|
+
import { cacheManager } from "../server/cacheManager.js";
|
|
9
|
+
|
|
10
|
+
const normalizeAccessToken = (
|
|
11
|
+
token: string | undefined,
|
|
12
|
+
): string | undefined => {
|
|
13
|
+
if (typeof token !== "string") {
|
|
14
|
+
return undefined;
|
|
15
|
+
}
|
|
16
|
+
const trimmed = token.trim();
|
|
17
|
+
if (!trimmed) {
|
|
18
|
+
return undefined;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const lowered = trimmed.toLowerCase();
|
|
22
|
+
if (lowered === "undefined" || lowered === "null") {
|
|
23
|
+
return undefined;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
if (lowered === "your_ynab_personal_access_token_here") {
|
|
27
|
+
return undefined;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
return trimmed;
|
|
31
|
+
};
|
|
9
32
|
|
|
10
33
|
// Skip E2E tests by default unless explicitly enabled
|
|
11
|
-
const
|
|
12
|
-
|
|
13
|
-
|
|
34
|
+
const normalizedToken = normalizeAccessToken(process.env["YNAB_ACCESS_TOKEN"]);
|
|
35
|
+
const hasAccessToken = !!normalizedToken;
|
|
36
|
+
if (!process.env["SKIP_E2E_TESTS"]) {
|
|
37
|
+
process.env["SKIP_E2E_TESTS"] = hasAccessToken ? "false" : "true";
|
|
14
38
|
}
|
|
15
|
-
if (
|
|
16
|
-
|
|
39
|
+
if (normalizedToken) {
|
|
40
|
+
process.env["YNAB_ACCESS_TOKEN"] = normalizedToken;
|
|
41
|
+
} else {
|
|
42
|
+
process.env["YNAB_ACCESS_TOKEN"] = "test-token-for-mocked-tests";
|
|
17
43
|
}
|
|
18
44
|
|
|
19
45
|
// Set test environment variables immediately
|
|
20
|
-
process.env[
|
|
21
|
-
if (!process.env[
|
|
22
|
-
|
|
46
|
+
process.env["NODE_ENV"] = "test";
|
|
47
|
+
if (!process.env["LOG_LEVEL"]) {
|
|
48
|
+
process.env["LOG_LEVEL"] = "error";
|
|
23
49
|
}
|
|
24
50
|
|
|
25
51
|
// Disable console output for cleaner test output unless VERBOSE_TESTS is set
|
|
26
|
-
if (!process.env[
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
52
|
+
if (!process.env["VERBOSE_TESTS"]) {
|
|
53
|
+
const originalConsoleError = console.error;
|
|
54
|
+
|
|
55
|
+
console.error = (...args: any[]) => {
|
|
56
|
+
const firstArg = args[0];
|
|
57
|
+
const isString = typeof firstArg === "string";
|
|
58
|
+
// Only show errors that are part of test assertions, actual errors, or explicitly marked [ERROR]
|
|
59
|
+
if (
|
|
60
|
+
(isString &&
|
|
61
|
+
(firstArg.includes("❌") ||
|
|
62
|
+
firstArg.includes("Test") ||
|
|
63
|
+
firstArg.includes("[ERROR]"))) ||
|
|
64
|
+
firstArg instanceof Error
|
|
65
|
+
) {
|
|
66
|
+
originalConsoleError(...args);
|
|
67
|
+
}
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
console.warn = () => {
|
|
71
|
+
// Suppress warnings by default
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
console.log = () => {
|
|
75
|
+
// Suppress logs by default
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
console.info = () => {
|
|
79
|
+
// Suppress info logs by default
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
console.debug = () => {
|
|
83
|
+
// Suppress debug logs by default
|
|
84
|
+
};
|
|
57
85
|
}
|
|
58
86
|
|
|
59
|
-
type TierFilter =
|
|
87
|
+
type TierFilter = "core" | "domain" | "full";
|
|
60
88
|
interface TestMeta {
|
|
61
|
-
|
|
62
|
-
|
|
89
|
+
tier?: TierFilter;
|
|
90
|
+
domain?: string;
|
|
63
91
|
}
|
|
64
92
|
|
|
65
93
|
const parseFilterList = (value: string | undefined) =>
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
94
|
+
value
|
|
95
|
+
?.split(",")
|
|
96
|
+
.map((entry) => entry.trim().toLowerCase())
|
|
97
|
+
.filter(Boolean) ?? [];
|
|
70
98
|
|
|
71
|
-
const tierFilters = parseFilterList(
|
|
72
|
-
|
|
99
|
+
const tierFilters = parseFilterList(
|
|
100
|
+
process.env["INTEGRATION_TEST_TIER"] ?? "full",
|
|
101
|
+
) as TierFilter[];
|
|
102
|
+
const domainFilters = parseFilterList(process.env["INTEGRATION_TEST_DOMAINS"]);
|
|
73
103
|
|
|
74
104
|
const shouldRunTier = (tier?: string): boolean => {
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
105
|
+
if (!tier) return true;
|
|
106
|
+
if (tierFilters.length === 0 || tierFilters.includes("full")) {
|
|
107
|
+
return true;
|
|
108
|
+
}
|
|
109
|
+
return tierFilters.includes(tier.toLowerCase() as TierFilter);
|
|
80
110
|
};
|
|
81
111
|
|
|
82
112
|
const shouldRunDomain = (domain?: string): boolean => {
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
113
|
+
if (!domain || domainFilters.length === 0) {
|
|
114
|
+
return true;
|
|
115
|
+
}
|
|
116
|
+
return domainFilters.includes(domain.toLowerCase());
|
|
87
117
|
};
|
|
88
118
|
|
|
89
119
|
/**
|
|
90
120
|
* Global test setup
|
|
91
121
|
*/
|
|
92
122
|
beforeAll(async () => {
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
123
|
+
// Set default test token if not provided
|
|
124
|
+
if (!process.env["YNAB_ACCESS_TOKEN"]) {
|
|
125
|
+
process.env["YNAB_ACCESS_TOKEN"] = "test-token-for-mocked-tests";
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
if (process.env["VERBOSE_TESTS"]) {
|
|
129
|
+
console.warn("🧪 Test environment initialized");
|
|
130
|
+
}
|
|
101
131
|
});
|
|
102
132
|
|
|
103
133
|
/**
|
|
104
134
|
* Global test cleanup
|
|
105
135
|
*/
|
|
106
136
|
afterAll(async () => {
|
|
107
|
-
|
|
108
|
-
|
|
137
|
+
// Clean up any global resources
|
|
138
|
+
console.warn("🧹 Test environment cleaned up");
|
|
109
139
|
});
|
|
110
140
|
|
|
111
141
|
/**
|
|
112
142
|
* Per-test setup
|
|
113
143
|
*/
|
|
114
144
|
beforeEach(async () => {
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
145
|
+
// Reset environment for each test
|
|
146
|
+
process.env["NODE_ENV"] = "test";
|
|
147
|
+
|
|
148
|
+
// Clear cache state between tests to prevent interference
|
|
149
|
+
cacheManager.clear();
|
|
150
|
+
|
|
151
|
+
// Clear any cached modules that might interfere (only if they exist)
|
|
152
|
+
try {
|
|
153
|
+
const modulePath = require.resolve("../server/YNABMCPServer.js");
|
|
154
|
+
if (require.cache[modulePath]) {
|
|
155
|
+
require.cache[modulePath] = undefined;
|
|
156
|
+
}
|
|
157
|
+
} catch {
|
|
158
|
+
// Module doesn't exist yet, which is fine
|
|
159
|
+
}
|
|
130
160
|
});
|
|
131
161
|
|
|
132
162
|
beforeEach((ctx) => {
|
|
133
|
-
|
|
163
|
+
const meta = ctx.task.meta as TestMeta;
|
|
134
164
|
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
165
|
+
if (!shouldRunTier(meta?.tier)) {
|
|
166
|
+
ctx.skip();
|
|
167
|
+
return;
|
|
168
|
+
}
|
|
139
169
|
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
170
|
+
if (!shouldRunDomain(meta?.domain)) {
|
|
171
|
+
ctx.skip();
|
|
172
|
+
}
|
|
143
173
|
});
|
|
144
174
|
|
|
145
175
|
/**
|
|
146
176
|
* Per-test cleanup
|
|
147
177
|
*/
|
|
148
178
|
afterEach(async () => {
|
|
149
|
-
|
|
150
|
-
|
|
179
|
+
// Clean up any test-specific resources
|
|
180
|
+
// This is handled by individual test files
|
|
151
181
|
});
|
|
152
182
|
|
|
153
183
|
/**
|
|
154
184
|
* Test utilities for environment management
|
|
155
185
|
*/
|
|
156
186
|
export class TestEnvironment {
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
187
|
+
private originalEnv: Record<string, string | undefined> = {};
|
|
188
|
+
private originalNodeEnv: string | undefined = undefined;
|
|
189
|
+
|
|
190
|
+
/**
|
|
191
|
+
* Set environment variables for a test
|
|
192
|
+
*/
|
|
193
|
+
setEnv(vars: Record<string, string>): void {
|
|
194
|
+
for (const [key, value] of Object.entries(vars)) {
|
|
195
|
+
this.originalEnv[key] = process.env[key];
|
|
196
|
+
process.env[key] = value;
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
/**
|
|
201
|
+
* Restore original environment variables
|
|
202
|
+
*/
|
|
203
|
+
restoreEnv(): void {
|
|
204
|
+
for (const [key, value] of Object.entries(this.originalEnv)) {
|
|
205
|
+
if (value === undefined) {
|
|
206
|
+
process.env[key] = undefined;
|
|
207
|
+
} else {
|
|
208
|
+
process.env[key] = value;
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
this.originalEnv = {};
|
|
212
|
+
|
|
213
|
+
// Restore original NODE_ENV if it was modified by cache methods
|
|
214
|
+
if (this.originalNodeEnv !== undefined) {
|
|
215
|
+
process.env["NODE_ENV"] = this.originalNodeEnv;
|
|
216
|
+
this.originalNodeEnv = undefined;
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
/**
|
|
221
|
+
* Check if running in CI environment
|
|
222
|
+
*/
|
|
223
|
+
isCI(): boolean {
|
|
224
|
+
return !!(
|
|
225
|
+
process.env["CI"] ||
|
|
226
|
+
process.env["GITHUB_ACTIONS"] ||
|
|
227
|
+
process.env["TRAVIS"]
|
|
228
|
+
);
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
/**
|
|
232
|
+
* Check if E2E tests should be skipped
|
|
233
|
+
*/
|
|
234
|
+
shouldSkipE2E(): boolean {
|
|
235
|
+
return (
|
|
236
|
+
process.env["SKIP_E2E_TESTS"] === "true" ||
|
|
237
|
+
!process.env["YNAB_ACCESS_TOKEN"] ||
|
|
238
|
+
this.isCI()
|
|
239
|
+
);
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
/**
|
|
243
|
+
* Get test timeout based on environment
|
|
244
|
+
*/
|
|
245
|
+
getTestTimeout(): number {
|
|
246
|
+
if (this.isCI()) {
|
|
247
|
+
return 60000; // 60 seconds in CI
|
|
248
|
+
}
|
|
249
|
+
return 30000; // 30 seconds locally
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
/**
|
|
253
|
+
* Disable cache for testing by setting maxEntries to 0
|
|
254
|
+
*/
|
|
255
|
+
disableCache(): void {
|
|
256
|
+
// Store original NODE_ENV value if not already stored
|
|
257
|
+
if (this.originalNodeEnv === undefined) {
|
|
258
|
+
this.originalNodeEnv = process.env["NODE_ENV"];
|
|
259
|
+
}
|
|
260
|
+
// This would require access to CacheManager internals
|
|
261
|
+
// For now, we rely on NODE_ENV=test to disable caching
|
|
262
|
+
process.env["NODE_ENV"] = "test";
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
/**
|
|
266
|
+
* Enable cache for testing specific cache behavior
|
|
267
|
+
*/
|
|
268
|
+
enableCache(): void {
|
|
269
|
+
// Store original NODE_ENV value if not already stored
|
|
270
|
+
if (this.originalNodeEnv === undefined) {
|
|
271
|
+
this.originalNodeEnv = process.env["NODE_ENV"];
|
|
272
|
+
}
|
|
273
|
+
// Temporarily enable cache for specific tests
|
|
274
|
+
process.env["NODE_ENV"] = "development";
|
|
275
|
+
}
|
|
240
276
|
}
|
|
241
277
|
|
|
242
278
|
/**
|
|
243
279
|
* Mock console methods for testing
|
|
244
280
|
*/
|
|
245
281
|
export class MockConsole {
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
282
|
+
private originalMethods: Record<string, (...args: any[]) => void> = {};
|
|
283
|
+
private logs: { method: string; args: any[] }[] = [];
|
|
284
|
+
|
|
285
|
+
/**
|
|
286
|
+
* Start mocking console methods
|
|
287
|
+
*/
|
|
288
|
+
mock(methods: string[] = ["log", "error", "warn", "info"]): void {
|
|
289
|
+
for (const method of methods) {
|
|
290
|
+
this.originalMethods[method] = (console as any)[method];
|
|
291
|
+
(console as any)[method] = (...args: any[]) => {
|
|
292
|
+
this.logs.push({ method, args });
|
|
293
|
+
};
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
/**
|
|
298
|
+
* Restore original console methods
|
|
299
|
+
*/
|
|
300
|
+
restore(): void {
|
|
301
|
+
for (const [method, originalFn] of Object.entries(this.originalMethods)) {
|
|
302
|
+
(console as any)[method] = originalFn;
|
|
303
|
+
}
|
|
304
|
+
this.originalMethods = {};
|
|
305
|
+
this.logs = [];
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
/**
|
|
309
|
+
* Get captured logs
|
|
310
|
+
*/
|
|
311
|
+
getLogs(): { method: string; args: any[] }[] {
|
|
312
|
+
return [...this.logs];
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
/**
|
|
316
|
+
* Get logs for a specific method
|
|
317
|
+
*/
|
|
318
|
+
getLogsFor(method: string): any[][] {
|
|
319
|
+
return this.logs
|
|
320
|
+
.filter((log) => log.method === method)
|
|
321
|
+
.map((log) => log.args);
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
/**
|
|
325
|
+
* Check if a specific message was logged
|
|
326
|
+
*/
|
|
327
|
+
hasLog(method: string, message: string): boolean {
|
|
328
|
+
return this.logs.some(
|
|
329
|
+
(log) =>
|
|
330
|
+
log.method === method &&
|
|
331
|
+
log.args.some(
|
|
332
|
+
(arg) => typeof arg === "string" && arg.includes(message),
|
|
333
|
+
),
|
|
334
|
+
);
|
|
335
|
+
}
|
|
296
336
|
}
|
|
297
337
|
|
|
298
338
|
/**
|
|
299
339
|
* Test data factory
|
|
300
340
|
*/
|
|
301
341
|
export class TestDataFactory {
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
342
|
+
/**
|
|
343
|
+
* Create mock budget data
|
|
344
|
+
*/
|
|
345
|
+
static createMockBudget(overrides: Partial<any> = {}): any {
|
|
346
|
+
return {
|
|
347
|
+
id: "test-budget-id",
|
|
348
|
+
name: "Test Budget",
|
|
349
|
+
last_modified_on: "2024-01-01T00:00:00Z",
|
|
350
|
+
first_month: "2024-01-01",
|
|
351
|
+
last_month: "2024-12-01",
|
|
352
|
+
date_format: { format: "MM/DD/YYYY" },
|
|
353
|
+
currency_format: { iso_code: "USD", example_format: "$123.45" },
|
|
354
|
+
...overrides,
|
|
355
|
+
};
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
/**
|
|
359
|
+
* Create mock account data
|
|
360
|
+
*/
|
|
361
|
+
static createMockAccount(overrides: Partial<any> = {}): any {
|
|
362
|
+
return {
|
|
363
|
+
id: "test-account-id",
|
|
364
|
+
name: "Test Account",
|
|
365
|
+
type: "checking",
|
|
366
|
+
on_budget: true,
|
|
367
|
+
closed: false,
|
|
368
|
+
note: null,
|
|
369
|
+
balance: 100000, // $100.00
|
|
370
|
+
cleared_balance: 95000,
|
|
371
|
+
uncleared_balance: 5000,
|
|
372
|
+
...overrides,
|
|
373
|
+
};
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
/**
|
|
377
|
+
* Create mock transaction data
|
|
378
|
+
*/
|
|
379
|
+
static createMockTransaction(overrides: Partial<any> = {}): any {
|
|
380
|
+
return {
|
|
381
|
+
id: "test-transaction-id",
|
|
382
|
+
date: "2024-01-15",
|
|
383
|
+
amount: -5000, // $5.00 outflow
|
|
384
|
+
memo: "Test transaction",
|
|
385
|
+
cleared: "cleared",
|
|
386
|
+
approved: true,
|
|
387
|
+
flag_color: null,
|
|
388
|
+
account_id: "test-account-id",
|
|
389
|
+
payee_id: "test-payee-id",
|
|
390
|
+
category_id: "test-category-id",
|
|
391
|
+
transfer_account_id: null,
|
|
392
|
+
...overrides,
|
|
393
|
+
};
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
/**
|
|
397
|
+
* Create mock category data
|
|
398
|
+
*/
|
|
399
|
+
static createMockCategory(overrides: Partial<any> = {}): any {
|
|
400
|
+
return {
|
|
401
|
+
id: "test-category-id",
|
|
402
|
+
category_group_id: "test-group-id",
|
|
403
|
+
name: "Test Category",
|
|
404
|
+
hidden: false,
|
|
405
|
+
note: null,
|
|
406
|
+
budgeted: 10000, // $10.00
|
|
407
|
+
activity: -5000,
|
|
408
|
+
balance: 5000,
|
|
409
|
+
goal_type: null,
|
|
410
|
+
...overrides,
|
|
411
|
+
};
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
/**
|
|
415
|
+
* Create mock payee data
|
|
416
|
+
*/
|
|
417
|
+
static createMockPayee(overrides: Partial<any> = {}): any {
|
|
418
|
+
return {
|
|
419
|
+
id: "test-payee-id",
|
|
420
|
+
name: "Test Payee",
|
|
421
|
+
transfer_account_id: null,
|
|
422
|
+
...overrides,
|
|
423
|
+
};
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
/**
|
|
427
|
+
* Create mock user data
|
|
428
|
+
*/
|
|
429
|
+
static createMockUser(overrides: Partial<any> = {}): any {
|
|
430
|
+
return {
|
|
431
|
+
id: "test-user-id",
|
|
432
|
+
email: "test@example.com",
|
|
433
|
+
...overrides,
|
|
434
|
+
};
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
/**
|
|
438
|
+
* Create mock cache statistics
|
|
439
|
+
*/
|
|
440
|
+
static createMockCacheStats(overrides: Partial<any> = {}): any {
|
|
441
|
+
return {
|
|
442
|
+
hits: 10,
|
|
443
|
+
misses: 5,
|
|
444
|
+
hitRate: 0.67,
|
|
445
|
+
entryCount: 15,
|
|
446
|
+
maxEntries: 100,
|
|
447
|
+
ttl: 300000,
|
|
448
|
+
...overrides,
|
|
449
|
+
};
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
/**
|
|
453
|
+
* Create mock cache entry structure
|
|
454
|
+
*/
|
|
455
|
+
static createMockCacheEntry(
|
|
456
|
+
key: string,
|
|
457
|
+
value: any,
|
|
458
|
+
overrides: Partial<any> = {},
|
|
459
|
+
): any {
|
|
460
|
+
return {
|
|
461
|
+
key,
|
|
462
|
+
value,
|
|
463
|
+
createdAt: Date.now(),
|
|
464
|
+
expiresAt: Date.now() + 300000,
|
|
465
|
+
ttl: 300000,
|
|
466
|
+
...overrides,
|
|
467
|
+
};
|
|
468
|
+
}
|
|
425
469
|
}
|
|
426
470
|
|
|
427
471
|
/**
|
|
428
472
|
* Performance measurement utilities
|
|
429
473
|
*/
|
|
430
474
|
export class PerformanceTracker {
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
+
private measurements: Map<string, number> = new Map();
|
|
476
|
+
|
|
477
|
+
/**
|
|
478
|
+
* Start measuring performance
|
|
479
|
+
*/
|
|
480
|
+
start(label: string): void {
|
|
481
|
+
this.measurements.set(label, Date.now());
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
/**
|
|
485
|
+
* End measurement and return duration
|
|
486
|
+
*/
|
|
487
|
+
end(label: string): number {
|
|
488
|
+
const startTime = this.measurements.get(label);
|
|
489
|
+
if (!startTime) {
|
|
490
|
+
throw new Error(`No measurement started for label: ${label}`);
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
const duration = Date.now() - startTime;
|
|
494
|
+
this.measurements.delete(label);
|
|
495
|
+
return duration;
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
/**
|
|
499
|
+
* Measure a function execution
|
|
500
|
+
*/
|
|
501
|
+
async measure<T>(
|
|
502
|
+
label: string,
|
|
503
|
+
fn: () => Promise<T>,
|
|
504
|
+
): Promise<{ result: T; duration: number }> {
|
|
505
|
+
this.start(label);
|
|
506
|
+
const result = await fn();
|
|
507
|
+
const duration = this.end(label);
|
|
508
|
+
return { result, duration };
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
/**
|
|
512
|
+
* Assert performance threshold
|
|
513
|
+
*/
|
|
514
|
+
assertDuration(duration: number, maxDuration: number, label?: string): void {
|
|
515
|
+
if (duration > maxDuration) {
|
|
516
|
+
throw new Error(
|
|
517
|
+
`Performance assertion failed${label ? ` for ${label}` : ""}: ` +
|
|
518
|
+
`${duration}ms > ${maxDuration}ms`,
|
|
519
|
+
);
|
|
520
|
+
}
|
|
521
|
+
}
|
|
475
522
|
}
|
|
476
523
|
|
|
477
524
|
// Export singleton instances for convenience
|