@elevasis/core 0.7.1 → 0.8.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/package.json +1 -1
- package/src/README.md +41 -41
- package/src/__tests__/publish.test.ts +18 -18
- package/src/__tests__/{template-foundations-compatibility.test.ts → template-core-compatibility.test.ts} +99 -99
- package/src/_gen/__tests__/__snapshots__/contracts.md.snap +1135 -1131
- package/src/_gen/__tests__/scaffold-contracts.test.ts +53 -53
- package/src/_gen/scaffold-contracts.ts +45 -45
- package/src/auth/multi-tenancy/credentials/README.md +38 -38
- package/src/auth/multi-tenancy/credentials/index.ts +6 -6
- package/src/auth/multi-tenancy/credentials/server/encryption.ts +39 -39
- package/src/auth/multi-tenancy/credentials/server/service.ts +60 -60
- package/src/auth/multi-tenancy/index.ts +17 -17
- package/src/auth/multi-tenancy/invitations/api-schemas.ts +107 -107
- package/src/auth/multi-tenancy/invitations/index.ts +37 -37
- package/src/auth/multi-tenancy/invitations/invitation.ts +86 -86
- package/src/auth/multi-tenancy/invitations/server/index.ts +25 -25
- package/src/auth/multi-tenancy/invitations/server/transforms.ts +24 -24
- package/src/auth/multi-tenancy/invitations/server/workos.ts +24 -24
- package/src/auth/multi-tenancy/invitations/supabase.ts +50 -50
- package/src/auth/multi-tenancy/memberships/api-schemas.ts +126 -126
- package/src/auth/multi-tenancy/memberships/index.ts +21 -21
- package/src/auth/multi-tenancy/memberships/membership.ts +138 -138
- package/src/auth/multi-tenancy/memberships/server/index.ts +15 -15
- package/src/auth/multi-tenancy/memberships/server/transforms.ts +32 -32
- package/src/auth/multi-tenancy/memberships/server/workos.ts +21 -21
- package/src/auth/multi-tenancy/memberships/supabase.ts +46 -46
- package/src/auth/multi-tenancy/organizations/api-schemas.ts +128 -128
- package/src/auth/multi-tenancy/organizations/index.ts +23 -23
- package/src/auth/multi-tenancy/organizations/organization.ts +24 -24
- package/src/auth/multi-tenancy/organizations/server/index.ts +10 -10
- package/src/auth/multi-tenancy/organizations/server/transforms.ts +35 -35
- package/src/auth/multi-tenancy/organizations/server/workos.ts +20 -20
- package/src/auth/multi-tenancy/types.ts +83 -83
- package/src/auth/multi-tenancy/users/api-schemas.ts +194 -194
- package/src/auth/multi-tenancy/users/index.ts +27 -27
- package/src/auth/multi-tenancy/users/server/index.ts +19 -19
- package/src/auth/multi-tenancy/users/server/transforms.ts +21 -21
- package/src/auth/multi-tenancy/users/server/workos.ts +16 -16
- package/src/auth/multi-tenancy/users/user.ts +65 -65
- package/src/business/README.md +52 -52
- package/src/business/__tests__/entities-published.test.ts +33 -33
- package/src/business/acquisition/api-schemas.ts +759 -759
- package/src/business/acquisition/index.ts +109 -109
- package/src/business/acquisition/types.ts +402 -402
- package/src/business/base-entities.test.ts +481 -481
- package/src/business/base-entities.ts +241 -241
- package/src/business/entities-published.ts +24 -24
- package/src/business/index.ts +15 -15
- package/src/business/pdf/browser/pdfmake-browser.ts +229 -229
- package/src/business/pdf/index.ts +10 -10
- package/src/business/pdf/server/index.ts +21 -21
- package/src/business/pdf/server/themes/default.ts +8 -8
- package/src/business/pdf/server/themes/index.ts +9 -9
- package/src/business/pdf/server/themes/types.ts +8 -8
- package/src/business/pdf/types.ts +272 -272
- package/src/business/projects/index.ts +2 -2
- package/src/business/projects/sse-events.ts +21 -21
- package/src/business/projects/types.ts +89 -89
- package/src/business/sales/api-schemas.ts +75 -75
- package/src/business/seo/__tests__/linking.test.ts +549 -549
- package/src/business/seo/__tests__/types.test.ts +404 -404
- package/src/business/seo/index.ts +2 -2
- package/src/business/seo/linking.ts +281 -281
- package/src/business/seo/types.ts +199 -199
- package/src/commands/queue/index.ts +3 -3
- package/src/commands/queue/schemas.test.ts +593 -593
- package/src/commands/queue/schemas.ts +125 -125
- package/src/commands/queue/sse-events.ts +61 -61
- package/src/commands/queue/types/action.ts +52 -52
- package/src/commands/queue/types/checkpoint.ts +44 -44
- package/src/commands/queue/types/index.ts +7 -7
- package/src/commands/queue/types/task.ts +116 -116
- package/src/commands/queue/types.ts +14 -14
- package/src/content/distribution-metadata.ts +61 -61
- package/src/content/index.ts +10 -10
- package/src/deployments/index.ts +22 -22
- package/src/execution/core/__tests__/archived-logs.test.ts +72 -72
- package/src/execution/core/index.ts +11 -11
- package/src/execution/core/runner-types.ts +80 -80
- package/src/execution/core/server/environment.ts +31 -31
- package/src/execution/core/sse-executions.ts +119 -119
- package/src/execution/core/types.ts +29 -29
- package/src/execution/engine/__tests__/fixtures/test-agents.ts +4 -4
- package/src/execution/engine/__tests__/timeout.test.ts +565 -565
- package/src/execution/engine/agent/__tests__/errors.test.ts +508 -508
- package/src/execution/engine/agent/actions/__tests__/processor.test.ts +531 -531
- package/src/execution/engine/agent/actions/executor.ts +205 -205
- package/src/execution/engine/agent/actions/navigate-knowledge-executor.ts +230 -230
- package/src/execution/engine/agent/actions/processor.ts +116 -116
- package/src/execution/engine/agent/actions/types.ts +70 -70
- package/src/execution/engine/agent/core/agent.ts +810 -810
- package/src/execution/engine/agent/core/types.ts +155 -155
- package/src/execution/engine/agent/errors.ts +251 -251
- package/src/execution/engine/agent/index.ts +78 -78
- package/src/execution/engine/agent/knowledge-map/types.ts +106 -106
- package/src/execution/engine/agent/knowledge-map/utils.ts +101 -101
- package/src/execution/engine/agent/memory/__tests__/manager.test.ts +754 -754
- package/src/execution/engine/agent/memory/domains.ts +99 -99
- package/src/execution/engine/agent/memory/manager.ts +365 -365
- package/src/execution/engine/agent/memory/processor.ts +66 -66
- package/src/execution/engine/agent/memory/types.ts +90 -90
- package/src/execution/engine/agent/memory/utils.ts +134 -134
- package/src/execution/engine/agent/observability/logging.ts +467 -467
- package/src/execution/engine/agent/observability/types.ts +64 -64
- package/src/execution/engine/agent/reasoning/adapters/agent-adapter-helpers.ts +349 -349
- package/src/execution/engine/agent/reasoning/processor.ts +92 -92
- package/src/execution/engine/agent/reasoning/prompt-sections/base-actions.ts +134 -134
- package/src/execution/engine/agent/reasoning/prompt-sections/completion.ts +49 -49
- package/src/execution/engine/agent/reasoning/prompt-sections/knowledge-map.ts +93 -93
- package/src/execution/engine/agent/reasoning/prompt-sections/memory.ts +65 -65
- package/src/execution/engine/agent/reasoning/prompt-sections/tools.ts +44 -44
- package/src/execution/engine/agent/reasoning/request-builder.ts +169 -169
- package/src/execution/engine/agent/reasoning/types.ts +18 -18
- package/src/execution/engine/base/errors.ts +118 -118
- package/src/execution/engine/base/index.ts +2 -2
- package/src/execution/engine/base/logging.ts +31 -31
- package/src/execution/engine/base/serialization.ts +324 -324
- package/src/execution/engine/base/types.ts +126 -126
- package/src/execution/engine/base/utils.ts +41 -41
- package/src/execution/engine/index.ts +434 -434
- package/src/execution/engine/interface/index.ts +1 -1
- package/src/execution/engine/interface/types.ts +62 -62
- package/src/execution/engine/llm/__tests__/model-info.test.ts +50 -50
- package/src/execution/engine/llm/__tests__/model-validation.test.ts +321 -321
- package/src/execution/engine/llm/__tests__/response-schema-validator.test.ts +115 -115
- package/src/execution/engine/llm/adapters/__tests__/adapter-factory.test.ts +375 -375
- package/src/execution/engine/llm/adapters/__tests__/anthropic-adapter.test.ts +463 -463
- package/src/execution/engine/llm/adapters/__tests__/anthropic.integration.test.ts +177 -177
- package/src/execution/engine/llm/adapters/__tests__/google-adapter.test.ts +722 -722
- package/src/execution/engine/llm/adapters/__tests__/google.integration.test.ts +376 -376
- package/src/execution/engine/llm/adapters/__tests__/openai-adapter.test.ts +551 -551
- package/src/execution/engine/llm/adapters/__tests__/openrouter-adapter.test.ts +563 -563
- package/src/execution/engine/llm/adapters/__tests__/openrouter.integration.test.ts +105 -105
- package/src/execution/engine/llm/adapters/__tests__/universal-adapter.test.ts +537 -537
- package/src/execution/engine/llm/adapters/circuit-breaker.ts +147 -147
- package/src/execution/engine/llm/adapters/index.ts +17 -17
- package/src/execution/engine/llm/adapters/mock-adapter.ts +116 -116
- package/src/execution/engine/llm/adapters/server/adapter-factory.ts +130 -130
- package/src/execution/engine/llm/adapters/server/anthropic.ts +137 -137
- package/src/execution/engine/llm/adapters/server/google.ts +283 -283
- package/src/execution/engine/llm/adapters/server/index.ts +12 -12
- package/src/execution/engine/llm/adapters/server/openai.ts +206 -206
- package/src/execution/engine/llm/adapters/server/openrouter.ts +235 -235
- package/src/execution/engine/llm/adapters/universal-adapter.ts +230 -230
- package/src/execution/engine/llm/errors.ts +186 -186
- package/src/execution/engine/llm/model-info.ts +332 -332
- package/src/execution/engine/llm/response-schema-validator.ts +113 -113
- package/src/execution/engine/llm/types.ts +86 -86
- package/src/execution/engine/test-utils/index.ts +6 -6
- package/src/execution/engine/test-utils/mocks.ts +56 -56
- package/src/execution/engine/tools/integration/base-integration-adapter.ts +50 -50
- package/src/execution/engine/tools/integration/index.ts +53 -53
- package/src/execution/engine/tools/integration/server/adapters/anymailfinder/anymailfinder-adapter.ts +73 -73
- package/src/execution/engine/tools/integration/server/adapters/anymailfinder/anymailfinder-tools.ts +209 -209
- package/src/execution/engine/tools/integration/server/adapters/anymailfinder/fetch/find-company-email/index.ts +82 -82
- package/src/execution/engine/tools/integration/server/adapters/anymailfinder/fetch/find-decision-maker-email/index.ts +122 -122
- package/src/execution/engine/tools/integration/server/adapters/anymailfinder/fetch/find-person-email/index.ts +89 -89
- package/src/execution/engine/tools/integration/server/adapters/anymailfinder/fetch/verify-email/index.ts +84 -84
- package/src/execution/engine/tools/integration/server/adapters/anymailfinder/index.ts +16 -16
- package/src/execution/engine/tools/integration/server/adapters/apify/__tests__/apify-run-actor.integration.test.ts +293 -293
- package/src/execution/engine/tools/integration/server/adapters/apify/apify-adapter.ts +100 -100
- package/src/execution/engine/tools/integration/server/adapters/apify/apify-tools.ts +217 -217
- package/src/execution/engine/tools/integration/server/adapters/apify/fetch/get-dataset-items/index.ts +92 -92
- package/src/execution/engine/tools/integration/server/adapters/apify/fetch/run-actor/index.ts +218 -218
- package/src/execution/engine/tools/integration/server/adapters/apify/fetch/start-actor/index.ts +87 -87
- package/src/execution/engine/tools/integration/server/adapters/apify/index.ts +11 -11
- package/src/execution/engine/tools/integration/server/adapters/attio/__tests__/attio-crud.integration.test.ts +361 -361
- package/src/execution/engine/tools/integration/server/adapters/attio/attio-adapter.ts +162 -162
- package/src/execution/engine/tools/integration/server/adapters/attio/attio-tools.ts +594 -594
- package/src/execution/engine/tools/integration/server/adapters/attio/fetch/create-attribute/index.ts +214 -214
- package/src/execution/engine/tools/integration/server/adapters/attio/fetch/create-note/index.ts +152 -152
- package/src/execution/engine/tools/integration/server/adapters/attio/fetch/create-record/index.ts +141 -141
- package/src/execution/engine/tools/integration/server/adapters/attio/fetch/delete-note/index.ts +86 -86
- package/src/execution/engine/tools/integration/server/adapters/attio/fetch/delete-record/index.ts +105 -105
- package/src/execution/engine/tools/integration/server/adapters/attio/fetch/get-record/index.ts +118 -118
- package/src/execution/engine/tools/integration/server/adapters/attio/fetch/list-attributes/index.ts +165 -165
- package/src/execution/engine/tools/integration/server/adapters/attio/fetch/list-notes/index.ts +96 -96
- package/src/execution/engine/tools/integration/server/adapters/attio/fetch/list-objects/index.ts +104 -104
- package/src/execution/engine/tools/integration/server/adapters/attio/fetch/list-records/index.ts +156 -156
- package/src/execution/engine/tools/integration/server/adapters/attio/fetch/update-attribute/index.ts +220 -220
- package/src/execution/engine/tools/integration/server/adapters/attio/fetch/update-record/index.ts +140 -140
- package/src/execution/engine/tools/integration/server/adapters/attio/fetch/utils/types.ts +146 -146
- package/src/execution/engine/tools/integration/server/adapters/attio/index.ts +31 -31
- package/src/execution/engine/tools/integration/server/adapters/gmail/gmail-adapter.ts +210 -210
- package/src/execution/engine/tools/integration/server/adapters/gmail/gmail-tools.ts +104 -104
- package/src/execution/engine/tools/integration/server/adapters/google-sheets/__tests__/google-sheets.integration.test.ts +261 -261
- package/src/execution/engine/tools/integration/server/adapters/google-sheets/google-sheets-adapter.ts +1189 -1189
- package/src/execution/engine/tools/integration/server/adapters/google-sheets/google-sheets-tools.ts +641 -641
- package/src/execution/engine/tools/integration/server/adapters/google-sheets/index.ts +18 -18
- package/src/execution/engine/tools/integration/server/adapters/instantly/fetch/activate-campaign/index.ts +86 -86
- package/src/execution/engine/tools/integration/server/adapters/instantly/fetch/add-to-campaign/__tests__/index.test.ts +289 -289
- package/src/execution/engine/tools/integration/server/adapters/instantly/fetch/add-to-campaign/index.ts +154 -154
- package/src/execution/engine/tools/integration/server/adapters/instantly/fetch/bulk-add-leads/__tests__/index.test.ts +325 -325
- package/src/execution/engine/tools/integration/server/adapters/instantly/fetch/bulk-add-leads/index.ts +153 -153
- package/src/execution/engine/tools/integration/server/adapters/instantly/fetch/bulk-delete-leads/index.ts +84 -84
- package/src/execution/engine/tools/integration/server/adapters/instantly/fetch/create-campaign/index.ts +125 -125
- package/src/execution/engine/tools/integration/server/adapters/instantly/fetch/create-inbox-test/index.ts +107 -107
- package/src/execution/engine/tools/integration/server/adapters/instantly/fetch/delete-campaign/index.ts +85 -85
- package/src/execution/engine/tools/integration/server/adapters/instantly/fetch/get-account-health/index.ts +91 -91
- package/src/execution/engine/tools/integration/server/adapters/instantly/fetch/get-campaign/index.ts +92 -92
- package/src/execution/engine/tools/integration/server/adapters/instantly/fetch/get-campaign-analytics/__tests__/index.test.ts +195 -195
- package/src/execution/engine/tools/integration/server/adapters/instantly/fetch/get-campaign-analytics/index.ts +113 -113
- package/src/execution/engine/tools/integration/server/adapters/instantly/fetch/get-daily-campaign-analytics/index.ts +104 -104
- package/src/execution/engine/tools/integration/server/adapters/instantly/fetch/get-emails/index.ts +155 -155
- package/src/execution/engine/tools/integration/server/adapters/instantly/fetch/get-step-analytics/__tests__/index.test.ts +196 -196
- package/src/execution/engine/tools/integration/server/adapters/instantly/fetch/get-step-analytics/index.ts +102 -102
- package/src/execution/engine/tools/integration/server/adapters/instantly/fetch/list-campaigns/__tests__/index.test.ts +189 -189
- package/src/execution/engine/tools/integration/server/adapters/instantly/fetch/list-campaigns/index.ts +87 -87
- package/src/execution/engine/tools/integration/server/adapters/instantly/fetch/list-leads/index.ts +112 -112
- package/src/execution/engine/tools/integration/server/adapters/instantly/fetch/patch-lead/index.ts +76 -76
- package/src/execution/engine/tools/integration/server/adapters/instantly/fetch/pause-campaign/index.ts +86 -86
- package/src/execution/engine/tools/integration/server/adapters/instantly/fetch/remove-from-subsequence/index.ts +98 -98
- package/src/execution/engine/tools/integration/server/adapters/instantly/fetch/send-reply/index.ts +126 -126
- package/src/execution/engine/tools/integration/server/adapters/instantly/fetch/update-campaign/__tests__/index.test.ts +193 -193
- package/src/execution/engine/tools/integration/server/adapters/instantly/fetch/update-campaign/index.ts +99 -99
- package/src/execution/engine/tools/integration/server/adapters/instantly/fetch/update-interest-status/__tests__/index.test.ts +621 -621
- package/src/execution/engine/tools/integration/server/adapters/instantly/fetch/update-interest-status/index.ts +125 -125
- package/src/execution/engine/tools/integration/server/adapters/instantly/index.ts +29 -29
- package/src/execution/engine/tools/integration/server/adapters/instantly/instantly-adapter.ts +178 -178
- package/src/execution/engine/tools/integration/server/adapters/instantly/instantly-tools.ts +1473 -1473
- package/src/execution/engine/tools/integration/server/adapters/millionverifier/fetch/check-credits/index.ts +59 -59
- package/src/execution/engine/tools/integration/server/adapters/millionverifier/fetch/verify-email/index.ts +102 -102
- package/src/execution/engine/tools/integration/server/adapters/millionverifier/index.ts +17 -17
- package/src/execution/engine/tools/integration/server/adapters/millionverifier/millionverifier-adapter.ts +80 -80
- package/src/execution/engine/tools/integration/server/adapters/millionverifier/millionverifier-tools.ts +102 -102
- package/src/execution/engine/tools/integration/server/adapters/resend/fetch/get-email/index.ts +102 -102
- package/src/execution/engine/tools/integration/server/adapters/resend/fetch/send-email/index.ts +134 -134
- package/src/execution/engine/tools/integration/server/adapters/resend/fetch/utils/types.ts +75 -75
- package/src/execution/engine/tools/integration/server/adapters/resend/index.ts +27 -27
- package/src/execution/engine/tools/integration/server/adapters/resend/resend-adapter.ts +108 -108
- package/src/execution/engine/tools/integration/server/adapters/resend/resend-tools.ts +132 -132
- package/src/execution/engine/tools/integration/server/adapters/signature-api/fetch/create-envelope/index.ts +274 -274
- package/src/execution/engine/tools/integration/server/adapters/signature-api/fetch/download-document/index.ts +230 -230
- package/src/execution/engine/tools/integration/server/adapters/signature-api/fetch/get-envelope/index.ts +133 -133
- package/src/execution/engine/tools/integration/server/adapters/signature-api/fetch/void-envelope/index.ts +90 -90
- package/src/execution/engine/tools/integration/server/adapters/stripe/fetch/utils/types.ts +210 -210
- package/src/execution/engine/tools/integration/server/adapters/stripe/stripe-adapter.ts +517 -517
- package/src/execution/engine/tools/integration/server/adapters/stripe/stripe-tools.ts +309 -309
- package/src/execution/engine/tools/integration/server/adapters/tomba/fetch/domain-search/index.ts +133 -133
- package/src/execution/engine/tools/integration/server/adapters/tomba/fetch/email-finder/index.ts +122 -122
- package/src/execution/engine/tools/integration/server/adapters/tomba/fetch/email-verifier/index.ts +111 -111
- package/src/execution/engine/tools/integration/server/adapters/tomba/index.ts +11 -11
- package/src/execution/engine/tools/integration/server/adapters/tomba/tomba-adapter.ts +78 -78
- package/src/execution/engine/tools/integration/server/adapters/tomba/tomba-tools.ts +222 -222
- package/src/execution/engine/tools/integration/server/index.ts +61 -61
- package/src/execution/engine/tools/integration/service.ts +161 -161
- package/src/execution/engine/tools/integration/tool.ts +253 -253
- package/src/execution/engine/tools/integration/types/anymailfinder.ts +74 -74
- package/src/execution/engine/tools/integration/types/apify.ts +92 -92
- package/src/execution/engine/tools/integration/types/index.ts +19 -19
- package/src/execution/engine/tools/integration/types/instantly.ts +557 -557
- package/src/execution/engine/tools/integration/types/millionverifier.ts +56 -56
- package/src/execution/engine/tools/integration/types/stripe.ts +162 -162
- package/src/execution/engine/tools/integration/types/tomba.ts +94 -94
- package/src/execution/engine/tools/lead-service-types.ts +884 -884
- package/src/execution/engine/tools/llm/index.ts +11 -11
- package/src/execution/engine/tools/llm/server/index.ts +8 -8
- package/src/execution/engine/tools/llm/server/llm-call-tool.ts +118 -118
- package/src/execution/engine/tools/platform/__tests__/pdf.test.ts +441 -441
- package/src/execution/engine/tools/platform/acquisition/company-tools.ts +248 -248
- package/src/execution/engine/tools/platform/acquisition/contact-tools.ts +319 -319
- package/src/execution/engine/tools/platform/acquisition/index.ts +43 -43
- package/src/execution/engine/tools/platform/acquisition/list-tools.ts +148 -148
- package/src/execution/engine/tools/platform/acquisition/types.ts +260 -260
- package/src/execution/engine/tools/platform/email/index.ts +122 -122
- package/src/execution/engine/tools/platform/email/types.ts +96 -96
- package/src/execution/engine/tools/platform/index.ts +157 -157
- package/src/execution/engine/tools/platform/notification.ts +81 -81
- package/src/execution/engine/tools/platform/pdf/index.ts +110 -110
- package/src/execution/engine/tools/platform/pdf/types.ts +77 -77
- package/src/execution/engine/tools/platform/scheduler.ts +87 -87
- package/src/execution/engine/tools/platform/storage/index.ts +370 -370
- package/src/execution/engine/tools/platform/types.ts +148 -148
- package/src/execution/engine/tools/registry.ts +700 -700
- package/src/execution/engine/tools/tool-maps.ts +786 -786
- package/src/execution/engine/tools/types.ts +233 -233
- package/src/execution/engine/workflow/__tests__/errors.test.ts +139 -139
- package/src/execution/engine/workflow/errors.ts +63 -63
- package/src/execution/engine/workflow/helpers/index.ts +11 -11
- package/src/execution/engine/workflow/helpers/server/index.ts +8 -8
- package/src/execution/engine/workflow/helpers/server/llm-call.ts +93 -93
- package/src/execution/engine/workflow/index.ts +19 -19
- package/src/execution/engine/workflow/log-truncate.ts +26 -26
- package/src/execution/engine/workflow/logging.ts +191 -191
- package/src/execution/engine/workflow/types.ts +182 -182
- package/src/execution/engine/workflow/utils.ts +280 -280
- package/src/execution/engine/workflow/workflow.ts +168 -168
- package/src/execution/index.ts +3 -3
- package/src/execution/scheduler/__tests__/api-schemas.test.ts +733 -733
- package/src/execution/scheduler/__tests__/utils.test.ts +1009 -1009
- package/src/execution/scheduler/api-schemas.ts +296 -296
- package/src/execution/scheduler/index.ts +50 -50
- package/src/execution/scheduler/schemas.ts +264 -264
- package/src/execution/scheduler/types.ts +111 -111
- package/src/execution/scheduler/utils.ts +364 -364
- package/src/forms/index.ts +7 -7
- package/src/forms/schemas.ts +69 -69
- package/src/forms/types.ts +70 -70
- package/src/index.ts +71 -60
- package/src/integrations/credentials/__tests__/schemas.test.ts +82 -82
- package/src/integrations/credentials/__tests__/utils.test.ts +144 -144
- package/src/integrations/credentials/api-schemas.ts +143 -143
- package/src/integrations/credentials/index.ts +32 -32
- package/src/integrations/credentials/schemas.ts +164 -164
- package/src/integrations/credentials/utils.ts +59 -59
- package/src/integrations/oauth/__tests__/provider-registry.test.ts +59 -59
- package/src/integrations/oauth/api-schemas.ts +92 -92
- package/src/integrations/oauth/index.ts +19 -19
- package/src/integrations/oauth/provider-registry.ts +61 -61
- package/src/integrations/oauth/server/__tests__/refresh-concurrent.test.ts +183 -183
- package/src/integrations/oauth/server/__tests__/refresh.test.ts +577 -577
- package/src/integrations/oauth/server/credentials.ts +39 -39
- package/src/integrations/oauth/server/refresh.ts +214 -214
- package/src/integrations/oauth/types.ts +34 -34
- package/src/integrations/webhook-endpoints/__tests__/api-schemas.test.ts +318 -318
- package/src/integrations/webhook-endpoints/api-schemas.ts +102 -102
- package/src/integrations/webhook-endpoints/index.ts +28 -28
- package/src/integrations/webhook-endpoints/types.ts +51 -51
- package/src/operations/activities/api-schemas.ts +79 -79
- package/src/operations/activities/index.ts +9 -9
- package/src/operations/activities/sse-events.ts +30 -30
- package/src/operations/activities/types.ts +63 -63
- package/src/operations/debug-logs/client.ts +60 -60
- package/src/operations/debug-logs/debug-logger.ts +83 -83
- package/src/operations/debug-logs/index.ts +8 -8
- package/src/operations/debug-logs/server.ts +19 -19
- package/src/operations/debug-logs/types.ts +33 -33
- package/src/operations/index.ts +50 -50
- package/src/operations/notifications/api-schemas.ts +91 -91
- package/src/operations/notifications/index.ts +3 -3
- package/src/operations/notifications/sse-events.ts +21 -21
- package/src/operations/notifications/types.ts +47 -47
- package/src/operations/observability/__tests__/openrouter-cost-flow.test.ts +297 -297
- package/src/operations/observability/__tests__/utils.test.ts +54 -54
- package/src/operations/observability/ai-usage-collector.ts +64 -64
- package/src/operations/observability/index.ts +13 -13
- package/src/operations/observability/metrics-collector.ts +49 -49
- package/src/operations/observability/schemas.ts +39 -39
- package/src/operations/observability/types.ts +463 -463
- package/src/operations/observability/utils.ts +77 -77
- package/src/operations/sessions/__tests__/manager.test.ts +821 -821
- package/src/operations/sessions/index.ts +26 -26
- package/src/operations/sessions/server/manager.ts +90 -90
- package/src/operations/sessions/server/session.ts +180 -180
- package/src/operations/sessions/types.ts +98 -98
- package/src/operations/triggers/index.ts +12 -12
- package/src/operations/triggers/webhook/definitions/instantly-account-error.ts +44 -44
- package/src/operations/triggers/webhook/definitions/instantly-auto-reply-received.ts +51 -51
- package/src/operations/triggers/webhook/definitions/instantly-campaign-completed.ts +45 -45
- package/src/operations/triggers/webhook/definitions/instantly-email-bounced.ts +49 -49
- package/src/operations/triggers/webhook/definitions/instantly-lead-unsubscribed.ts +45 -45
- package/src/operations/triggers/webhook/definitions/instantly-reply-received.ts +54 -54
- package/src/operations/triggers/webhook/index.ts +35 -35
- package/src/operations/triggers/webhook/types.ts +74 -74
- package/src/organization-model/README.md +97 -97
- package/src/organization-model/__tests__/defaults.test.ts +175 -175
- package/src/organization-model/__tests__/domains/customers.test.ts +295 -295
- package/src/organization-model/__tests__/domains/goals.test.ts +479 -479
- package/src/organization-model/__tests__/domains/identity.test.ts +279 -279
- package/src/organization-model/__tests__/domains/navigation.test.ts +212 -212
- package/src/organization-model/__tests__/domains/offerings.test.ts +419 -419
- package/src/organization-model/__tests__/domains/operations.test.ts +203 -203
- package/src/organization-model/__tests__/domains/resource-mappings.test.ts +362 -362
- package/src/organization-model/__tests__/domains/roles.test.ts +347 -347
- package/src/organization-model/__tests__/domains/statuses.test.ts +243 -243
- package/src/organization-model/__tests__/foundation.test.ts +105 -105
- package/src/organization-model/__tests__/graph.test.ts +894 -894
- package/src/organization-model/__tests__/resolve.test.ts +690 -690
- package/src/organization-model/__tests__/schema.test.ts +407 -407
- package/src/organization-model/contracts.ts +14 -14
- package/src/organization-model/defaults.ts +148 -148
- package/src/organization-model/domains/branding.ts +22 -22
- package/src/organization-model/domains/customers.ts +75 -75
- package/src/organization-model/domains/features.ts +22 -22
- package/src/organization-model/domains/goals.ts +80 -80
- package/src/organization-model/domains/identity.ts +94 -94
- package/src/organization-model/domains/navigation.ts +391 -391
- package/src/organization-model/domains/offerings.ts +66 -66
- package/src/organization-model/domains/operations.ts +85 -85
- package/src/organization-model/domains/projects.ts +48 -48
- package/src/organization-model/domains/prospecting.ts +33 -33
- package/src/organization-model/domains/roles.ts +55 -55
- package/src/organization-model/domains/sales.ts +94 -94
- package/src/organization-model/domains/shared.ts +62 -62
- package/src/organization-model/domains/statuses.ts +130 -130
- package/src/organization-model/foundation.ts +97 -97
- package/src/organization-model/graph/build.ts +399 -399
- package/src/organization-model/graph/index.ts +4 -4
- package/src/organization-model/graph/schema.ts +48 -48
- package/src/organization-model/graph/types.ts +40 -40
- package/src/organization-model/index.ts +13 -13
- package/src/organization-model/organization-graph.mdx +272 -272
- package/src/organization-model/organization-model.mdx +320 -320
- package/src/organization-model/published.ts +85 -85
- package/src/organization-model/resolve.ts +66 -66
- package/src/organization-model/schema.ts +287 -287
- package/src/organization-model/types.ts +46 -46
- package/src/platform/api/index.ts +1 -1
- package/src/platform/api/types.ts +35 -35
- package/src/platform/constants/http.ts +37 -37
- package/src/platform/constants/index.ts +5 -5
- package/src/platform/constants/limits.ts +32 -32
- package/src/platform/constants/resilience.ts +51 -51
- package/src/platform/constants/timeouts.ts +20 -20
- package/src/platform/constants/versions.ts +3 -3
- package/src/platform/registry/__tests__/resource-registry-static.test.ts +347 -347
- package/src/platform/registry/__tests__/resource-registry.integration.test.ts +1028 -1028
- package/src/platform/registry/__tests__/resource-registry.list-executable.test.ts +393 -393
- package/src/platform/registry/__tests__/resource-registry.test.ts +2005 -2005
- package/src/platform/registry/__tests__/serialization.test.ts +1127 -1127
- package/src/platform/registry/command-view.ts +180 -180
- package/src/platform/registry/domains.ts +165 -165
- package/src/platform/registry/index.ts +93 -93
- package/src/platform/registry/reserved.ts +24 -24
- package/src/platform/registry/resource-metadata.ts +59 -59
- package/src/platform/registry/resource-registry.command-queue-groups.test.ts +129 -129
- package/src/platform/registry/resource-registry.ts +876 -876
- package/src/platform/registry/serialization.ts +273 -273
- package/src/platform/registry/serialized-types.ts +231 -231
- package/src/platform/registry/stats-types.ts +66 -66
- package/src/platform/registry/types.ts +404 -404
- package/src/platform/registry/validation.ts +513 -513
- package/src/platform/resilience/__tests__/rate-limiter.test.ts +471 -471
- package/src/platform/resilience/circuit-breaker.ts +164 -164
- package/src/platform/resilience/errors.ts +68 -68
- package/src/platform/resilience/http-error-mapper.ts +129 -129
- package/src/platform/resilience/index.ts +93 -93
- package/src/platform/resilience/rate-limiter-types.ts +46 -46
- package/src/platform/resilience/rate-limiter.ts +140 -140
- package/src/platform/resilience/retry.ts +89 -89
- package/src/platform/resilience/timeout.ts +63 -63
- package/src/platform/sse/events.ts +37 -37
- package/src/platform/sse/index.ts +7 -7
- package/src/platform/utils/__tests__/validation.test.ts +1083 -1083
- package/src/platform/utils/currency.ts +96 -96
- package/src/platform/utils/debounce.ts +52 -52
- package/src/platform/utils/error.ts +41 -41
- package/src/platform/utils/hmac.test.ts +97 -97
- package/src/platform/utils/index.ts +32 -32
- package/src/platform/utils/server/betterstack-logger.ts +210 -210
- package/src/platform/utils/server/hmac.ts +44 -44
- package/src/platform/utils/server/unsubscribe.ts +111 -111
- package/src/platform/utils/token-counter.ts +96 -96
- package/src/platform/utils/validation.ts +425 -425
- package/src/projects/api-schemas.ts +268 -268
- package/src/published.ts +1 -1
- package/src/reference/_generated/contracts.md +607 -607
- package/src/reference/glossary.md +105 -105
- package/src/requests/__tests__/api-schemas.test.ts +277 -277
- package/src/requests/api-schemas.ts +83 -83
- package/src/requests/index.ts +1 -1
- package/src/scaffold-registry/__tests__/index.test.ts +17 -0
- package/src/scaffold-registry/__tests__/schema.test.ts +329 -230
- package/src/scaffold-registry/index.ts +205 -189
- package/src/scaffold-registry/schema.ts +196 -128
- package/src/server.ts +272 -272
- package/src/supabase/database.types.ts +2719 -2719
- package/src/supabase/helpers.ts +20 -20
- package/src/supabase/index.ts +52 -52
- package/src/supabase/server/client.ts +58 -58
- package/src/test-utils/README.md +38 -38
- package/src/test-utils/browser-mocks.ts +54 -54
- package/src/test-utils/fixtures/api-keys.ts +52 -52
- package/src/test-utils/fixtures/index.ts +4 -4
- package/src/test-utils/fixtures/memberships.ts +80 -80
- package/src/test-utils/fixtures/organizations.ts +69 -69
- package/src/test-utils/fixtures/users.ts +79 -79
- package/src/test-utils/index.ts +11 -11
- package/src/test-utils/mocks/index.ts +2 -2
- package/src/test-utils/mocks/supabase.ts +142 -142
- package/src/test-utils/mocks/workos.ts +108 -108
- package/src/test-utils/rls/RLSTestContext.ts +556 -556
- package/src/test-utils/rls/index.ts +1 -1
|
@@ -1,821 +1,821 @@
|
|
|
1
|
-
import { describe, it, expect, beforeEach, vi } from 'vitest'
|
|
2
|
-
import { SessionManager } from '../server/manager'
|
|
3
|
-
import { Session } from '../server/session'
|
|
4
|
-
import type { SupabaseClient } from '../../../supabase'
|
|
5
|
-
|
|
6
|
-
describe('SessionManager', () => {
|
|
7
|
-
let mockDb: SupabaseClient
|
|
8
|
-
let sessionManager: SessionManager
|
|
9
|
-
|
|
10
|
-
beforeEach(() => {
|
|
11
|
-
// Create mock Supabase client
|
|
12
|
-
mockDb = {
|
|
13
|
-
from: vi.fn()
|
|
14
|
-
} as unknown as SupabaseClient
|
|
15
|
-
|
|
16
|
-
sessionManager = new SessionManager(mockDb)
|
|
17
|
-
})
|
|
18
|
-
|
|
19
|
-
describe('create', () => {
|
|
20
|
-
it('creates a new session with all parameters', async () => {
|
|
21
|
-
const mockSessionData = {
|
|
22
|
-
session_id: 'session-123',
|
|
23
|
-
resource_id: 'agent-1',
|
|
24
|
-
organization_id: 'org-123',
|
|
25
|
-
user_id: 'user-123',
|
|
26
|
-
memory_snapshot: { sessionMemory: {}, history: [] },
|
|
27
|
-
metadata: { foo: 'bar' },
|
|
28
|
-
session_total_turns: 0,
|
|
29
|
-
created_at: '2025-01-01T00:00:00Z',
|
|
30
|
-
updated_at: '2025-01-01T00:00:00Z'
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
const mockInsert = vi.fn().mockReturnValue({
|
|
34
|
-
select: vi.fn().mockReturnValue({
|
|
35
|
-
single: vi.fn().mockResolvedValue({ data: mockSessionData, error: null })
|
|
36
|
-
})
|
|
37
|
-
})
|
|
38
|
-
|
|
39
|
-
;(mockDb.from as ReturnType<typeof vi.fn>).mockReturnValue({
|
|
40
|
-
insert: mockInsert
|
|
41
|
-
})
|
|
42
|
-
|
|
43
|
-
const session = await sessionManager.create({
|
|
44
|
-
resourceId: 'agent-1',
|
|
45
|
-
organizationId: 'org-123',
|
|
46
|
-
userId: 'user-123',
|
|
47
|
-
metadata: { foo: 'bar' }
|
|
48
|
-
})
|
|
49
|
-
|
|
50
|
-
expect(mockDb.from).toHaveBeenCalledWith('sessions')
|
|
51
|
-
expect(mockInsert).toHaveBeenCalledWith({
|
|
52
|
-
resource_id: 'agent-1',
|
|
53
|
-
organization_id: 'org-123',
|
|
54
|
-
user_id: 'user-123',
|
|
55
|
-
memory_snapshot: { sessionMemory: {}, history: [] },
|
|
56
|
-
metadata: { foo: 'bar' },
|
|
57
|
-
session_total_turns: 0
|
|
58
|
-
})
|
|
59
|
-
expect(session).toBeInstanceOf(Session)
|
|
60
|
-
expect(session.sessionId).toBe('session-123')
|
|
61
|
-
expect(session.organizationId).toBe('org-123')
|
|
62
|
-
})
|
|
63
|
-
|
|
64
|
-
it('creates session without userId (optional)', async () => {
|
|
65
|
-
const mockSessionData = {
|
|
66
|
-
session_id: 'session-123',
|
|
67
|
-
resource_id: 'agent-1',
|
|
68
|
-
organization_id: 'org-123',
|
|
69
|
-
user_id: null,
|
|
70
|
-
memory_snapshot: { sessionMemory: {}, history: [] },
|
|
71
|
-
metadata: null,
|
|
72
|
-
session_total_turns: 0,
|
|
73
|
-
created_at: '2025-01-01T00:00:00Z',
|
|
74
|
-
updated_at: '2025-01-01T00:00:00Z'
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
const mockInsert = vi.fn().mockReturnValue({
|
|
78
|
-
select: vi.fn().mockReturnValue({
|
|
79
|
-
single: vi.fn().mockResolvedValue({ data: mockSessionData, error: null })
|
|
80
|
-
})
|
|
81
|
-
})
|
|
82
|
-
|
|
83
|
-
;(mockDb.from as ReturnType<typeof vi.fn>).mockReturnValue({
|
|
84
|
-
insert: mockInsert
|
|
85
|
-
})
|
|
86
|
-
|
|
87
|
-
const session = await sessionManager.create({
|
|
88
|
-
resourceId: 'agent-1',
|
|
89
|
-
organizationId: 'org-123'
|
|
90
|
-
})
|
|
91
|
-
|
|
92
|
-
expect(mockInsert).toHaveBeenCalledWith({
|
|
93
|
-
resource_id: 'agent-1',
|
|
94
|
-
organization_id: 'org-123',
|
|
95
|
-
user_id: null,
|
|
96
|
-
memory_snapshot: { sessionMemory: {}, history: [] },
|
|
97
|
-
metadata: null,
|
|
98
|
-
session_total_turns: 0
|
|
99
|
-
})
|
|
100
|
-
expect(session.userId).toBeNull()
|
|
101
|
-
})
|
|
102
|
-
|
|
103
|
-
it('throws error when database insert fails', async () => {
|
|
104
|
-
const mockInsert = vi.fn().mockReturnValue({
|
|
105
|
-
select: vi.fn().mockReturnValue({
|
|
106
|
-
single: vi.fn().mockResolvedValue({
|
|
107
|
-
data: null,
|
|
108
|
-
error: { message: 'Database connection failed' }
|
|
109
|
-
})
|
|
110
|
-
})
|
|
111
|
-
})
|
|
112
|
-
|
|
113
|
-
;(mockDb.from as ReturnType<typeof vi.fn>).mockReturnValue({
|
|
114
|
-
insert: mockInsert
|
|
115
|
-
})
|
|
116
|
-
|
|
117
|
-
await expect(
|
|
118
|
-
sessionManager.create({
|
|
119
|
-
resourceId: 'agent-1',
|
|
120
|
-
organizationId: 'org-123'
|
|
121
|
-
})
|
|
122
|
-
).rejects.toThrow('Failed to create session: Database connection failed')
|
|
123
|
-
})
|
|
124
|
-
})
|
|
125
|
-
|
|
126
|
-
describe('get', () => {
|
|
127
|
-
it('retrieves session by ID and organization', async () => {
|
|
128
|
-
const mockSessionData = {
|
|
129
|
-
session_id: 'session-123',
|
|
130
|
-
resource_id: 'agent-1',
|
|
131
|
-
organization_id: 'org-123',
|
|
132
|
-
user_id: 'user-123',
|
|
133
|
-
memory_snapshot: { sessionMemory: {}, history: [] },
|
|
134
|
-
metadata: null,
|
|
135
|
-
session_total_turns: 0,
|
|
136
|
-
created_at: '2025-01-01T00:00:00Z',
|
|
137
|
-
updated_at: '2025-01-01T00:00:00Z'
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
const mockIs = vi.fn().mockReturnValue({
|
|
141
|
-
single: vi.fn().mockResolvedValue({ data: mockSessionData, error: null })
|
|
142
|
-
})
|
|
143
|
-
|
|
144
|
-
const mockSelect = vi.fn().mockReturnValue({
|
|
145
|
-
eq: vi.fn().mockReturnValue({
|
|
146
|
-
eq: vi.fn().mockReturnValue({
|
|
147
|
-
is: mockIs
|
|
148
|
-
})
|
|
149
|
-
})
|
|
150
|
-
})
|
|
151
|
-
|
|
152
|
-
;(mockDb.from as ReturnType<typeof vi.fn>).mockReturnValue({
|
|
153
|
-
select: mockSelect
|
|
154
|
-
})
|
|
155
|
-
|
|
156
|
-
const session = await sessionManager.get('session-123', 'org-123')
|
|
157
|
-
|
|
158
|
-
expect(mockDb.from).toHaveBeenCalledWith('sessions')
|
|
159
|
-
expect(session).toBeInstanceOf(Session)
|
|
160
|
-
expect(session!.sessionId).toBe('session-123')
|
|
161
|
-
})
|
|
162
|
-
|
|
163
|
-
it('returns null when session not found', async () => {
|
|
164
|
-
const mockIs = vi.fn().mockReturnValue({
|
|
165
|
-
single: vi.fn().mockResolvedValue({
|
|
166
|
-
data: null,
|
|
167
|
-
error: { code: 'PGRST116', message: 'Not found' }
|
|
168
|
-
})
|
|
169
|
-
})
|
|
170
|
-
|
|
171
|
-
const mockSelect = vi.fn().mockReturnValue({
|
|
172
|
-
eq: vi.fn().mockReturnValue({
|
|
173
|
-
eq: vi.fn().mockReturnValue({
|
|
174
|
-
is: mockIs
|
|
175
|
-
})
|
|
176
|
-
})
|
|
177
|
-
})
|
|
178
|
-
|
|
179
|
-
;(mockDb.from as ReturnType<typeof vi.fn>).mockReturnValue({
|
|
180
|
-
select: mockSelect
|
|
181
|
-
})
|
|
182
|
-
|
|
183
|
-
const session = await sessionManager.get('nonexistent', 'org-123')
|
|
184
|
-
|
|
185
|
-
expect(session).toBeNull()
|
|
186
|
-
})
|
|
187
|
-
|
|
188
|
-
it('throws error for database errors (not 404)', async () => {
|
|
189
|
-
const mockIs = vi.fn().mockReturnValue({
|
|
190
|
-
single: vi.fn().mockResolvedValue({
|
|
191
|
-
data: null,
|
|
192
|
-
error: { code: 'DB_ERROR', message: 'Connection timeout' }
|
|
193
|
-
})
|
|
194
|
-
})
|
|
195
|
-
|
|
196
|
-
const mockSelect = vi.fn().mockReturnValue({
|
|
197
|
-
eq: vi.fn().mockReturnValue({
|
|
198
|
-
eq: vi.fn().mockReturnValue({
|
|
199
|
-
is: mockIs
|
|
200
|
-
})
|
|
201
|
-
})
|
|
202
|
-
})
|
|
203
|
-
|
|
204
|
-
;(mockDb.from as ReturnType<typeof vi.fn>).mockReturnValue({
|
|
205
|
-
select: mockSelect
|
|
206
|
-
})
|
|
207
|
-
|
|
208
|
-
await expect(sessionManager.get('session-123', 'org-123')).rejects.toThrow(
|
|
209
|
-
'Failed to get session: Connection timeout'
|
|
210
|
-
)
|
|
211
|
-
})
|
|
212
|
-
})
|
|
213
|
-
|
|
214
|
-
describe('list', () => {
|
|
215
|
-
it('lists sessions for organization', async () => {
|
|
216
|
-
const mockSessionsData = [
|
|
217
|
-
{
|
|
218
|
-
session_id: 'session-1',
|
|
219
|
-
resource_id: 'agent-1',
|
|
220
|
-
organization_id: 'org-123',
|
|
221
|
-
user_id: 'user-1',
|
|
222
|
-
memory_snapshot: { sessionMemory: {}, history: [] },
|
|
223
|
-
metadata: null,
|
|
224
|
-
session_total_turns: 0,
|
|
225
|
-
created_at: '2025-01-01T00:00:00Z',
|
|
226
|
-
updated_at: '2025-01-01T00:00:00Z'
|
|
227
|
-
},
|
|
228
|
-
{
|
|
229
|
-
session_id: 'session-2',
|
|
230
|
-
resource_id: 'agent-1',
|
|
231
|
-
organization_id: 'org-123',
|
|
232
|
-
user_id: 'user-2',
|
|
233
|
-
memory_snapshot: { sessionMemory: {}, history: [] },
|
|
234
|
-
metadata: null,
|
|
235
|
-
session_total_turns: 0,
|
|
236
|
-
created_at: '2025-01-01T00:00:00Z',
|
|
237
|
-
updated_at: '2025-01-01T00:00:00Z'
|
|
238
|
-
}
|
|
239
|
-
]
|
|
240
|
-
|
|
241
|
-
const mockLimit = vi.fn().mockResolvedValue({ data: mockSessionsData, error: null })
|
|
242
|
-
const mockOrder = vi.fn().mockReturnValue({ limit: mockLimit })
|
|
243
|
-
const mockIs = vi.fn().mockReturnValue({ order: mockOrder })
|
|
244
|
-
const mockEq = vi.fn().mockReturnValue({ is: mockIs })
|
|
245
|
-
const mockSelect = vi.fn().mockReturnValue({ eq: mockEq })
|
|
246
|
-
|
|
247
|
-
;(mockDb.from as ReturnType<typeof vi.fn>).mockReturnValue({
|
|
248
|
-
select: mockSelect
|
|
249
|
-
})
|
|
250
|
-
|
|
251
|
-
const sessions = await sessionManager.list({
|
|
252
|
-
organizationId: 'org-123'
|
|
253
|
-
})
|
|
254
|
-
|
|
255
|
-
expect(mockDb.from).toHaveBeenCalledWith('sessions')
|
|
256
|
-
expect(mockEq).toHaveBeenCalledWith('organization_id', 'org-123')
|
|
257
|
-
expect(mockIs).toHaveBeenCalledWith('deleted_at', null)
|
|
258
|
-
expect(mockOrder).toHaveBeenCalledWith('created_at', { ascending: false })
|
|
259
|
-
expect(mockLimit).toHaveBeenCalledWith(50) // Default limit
|
|
260
|
-
expect(sessions).toHaveLength(2)
|
|
261
|
-
expect(sessions[0]).toBeInstanceOf(Session)
|
|
262
|
-
})
|
|
263
|
-
|
|
264
|
-
it('filters sessions by userId', async () => {
|
|
265
|
-
const mockSessionsData = [
|
|
266
|
-
{
|
|
267
|
-
session_id: 'session-1',
|
|
268
|
-
resource_id: 'agent-1',
|
|
269
|
-
organization_id: 'org-123',
|
|
270
|
-
user_id: 'user-123',
|
|
271
|
-
memory_snapshot: { sessionMemory: {}, history: [] },
|
|
272
|
-
metadata: null,
|
|
273
|
-
session_total_turns: 0,
|
|
274
|
-
created_at: '2025-01-01T00:00:00Z',
|
|
275
|
-
updated_at: '2025-01-01T00:00:00Z'
|
|
276
|
-
}
|
|
277
|
-
]
|
|
278
|
-
|
|
279
|
-
// Create a mock query builder that chains properly and is awaitable
|
|
280
|
-
const mockQueryBuilder: Record<string, unknown> = {}
|
|
281
|
-
;(mockQueryBuilder as Record<string, unknown>).eq = vi.fn().mockReturnValue(mockQueryBuilder)
|
|
282
|
-
;(mockQueryBuilder as Record<string, unknown>).then = vi.fn((resolve: (data: unknown) => void) => {
|
|
283
|
-
resolve({ data: mockSessionsData, error: null })
|
|
284
|
-
return Promise.resolve({ data: mockSessionsData, error: null })
|
|
285
|
-
})
|
|
286
|
-
|
|
287
|
-
const mockLimit = vi.fn().mockReturnValue(mockQueryBuilder)
|
|
288
|
-
const mockOrder = vi.fn().mockReturnValue({ limit: mockLimit })
|
|
289
|
-
const mockIs = vi.fn().mockReturnValue({ order: mockOrder })
|
|
290
|
-
const mockOrgEq = vi.fn().mockReturnValue({ is: mockIs })
|
|
291
|
-
const mockSelect = vi.fn().mockReturnValue({ eq: mockOrgEq })
|
|
292
|
-
|
|
293
|
-
;(mockDb.from as ReturnType<typeof vi.fn>).mockReturnValue({
|
|
294
|
-
select: mockSelect
|
|
295
|
-
})
|
|
296
|
-
|
|
297
|
-
const sessions = await sessionManager.list({
|
|
298
|
-
organizationId: 'org-123',
|
|
299
|
-
userId: 'user-123'
|
|
300
|
-
})
|
|
301
|
-
|
|
302
|
-
expect(mockQueryBuilder.eq).toHaveBeenCalledWith('user_id', 'user-123')
|
|
303
|
-
expect(sessions).toHaveLength(1)
|
|
304
|
-
})
|
|
305
|
-
|
|
306
|
-
it('filters sessions by resourceId', async () => {
|
|
307
|
-
const mockSessionsData = [
|
|
308
|
-
{
|
|
309
|
-
session_id: 'session-1',
|
|
310
|
-
resource_id: 'agent-1',
|
|
311
|
-
organization_id: 'org-123',
|
|
312
|
-
user_id: 'user-1',
|
|
313
|
-
memory_snapshot: { sessionMemory: {}, history: [] },
|
|
314
|
-
metadata: null,
|
|
315
|
-
session_total_turns: 0,
|
|
316
|
-
created_at: '2025-01-01T00:00:00Z',
|
|
317
|
-
updated_at: '2025-01-01T00:00:00Z'
|
|
318
|
-
}
|
|
319
|
-
]
|
|
320
|
-
|
|
321
|
-
// Create a mock query builder that chains properly and is awaitable
|
|
322
|
-
const mockQueryBuilder: Record<string, unknown> = {}
|
|
323
|
-
;(mockQueryBuilder as Record<string, unknown>).eq = vi.fn().mockReturnValue(mockQueryBuilder)
|
|
324
|
-
;(mockQueryBuilder as Record<string, unknown>).then = vi.fn((resolve: (data: unknown) => void) => {
|
|
325
|
-
resolve({ data: mockSessionsData, error: null })
|
|
326
|
-
return Promise.resolve({ data: mockSessionsData, error: null })
|
|
327
|
-
})
|
|
328
|
-
|
|
329
|
-
const mockLimit = vi.fn().mockReturnValue(mockQueryBuilder)
|
|
330
|
-
const mockOrder = vi.fn().mockReturnValue({ limit: mockLimit })
|
|
331
|
-
const mockIs = vi.fn().mockReturnValue({ order: mockOrder })
|
|
332
|
-
const mockOrgEq = vi.fn().mockReturnValue({ is: mockIs })
|
|
333
|
-
const mockSelect = vi.fn().mockReturnValue({ eq: mockOrgEq })
|
|
334
|
-
|
|
335
|
-
;(mockDb.from as ReturnType<typeof vi.fn>).mockReturnValue({
|
|
336
|
-
select: mockSelect
|
|
337
|
-
})
|
|
338
|
-
|
|
339
|
-
const sessions = await sessionManager.list({
|
|
340
|
-
organizationId: 'org-123',
|
|
341
|
-
resourceId: 'agent-1'
|
|
342
|
-
})
|
|
343
|
-
|
|
344
|
-
expect(mockQueryBuilder.eq).toHaveBeenCalledWith('resource_id', 'agent-1')
|
|
345
|
-
expect(sessions).toHaveLength(1)
|
|
346
|
-
})
|
|
347
|
-
|
|
348
|
-
it('respects custom limit parameter', async () => {
|
|
349
|
-
const mockLimit = vi.fn().mockResolvedValue({ data: [], error: null })
|
|
350
|
-
const mockOrder = vi.fn().mockReturnValue({ limit: mockLimit })
|
|
351
|
-
const mockIs = vi.fn().mockReturnValue({ order: mockOrder })
|
|
352
|
-
const mockEq = vi.fn().mockReturnValue({ is: mockIs })
|
|
353
|
-
const mockSelect = vi.fn().mockReturnValue({ eq: mockEq })
|
|
354
|
-
|
|
355
|
-
;(mockDb.from as ReturnType<typeof vi.fn>).mockReturnValue({
|
|
356
|
-
select: mockSelect
|
|
357
|
-
})
|
|
358
|
-
|
|
359
|
-
await sessionManager.list({
|
|
360
|
-
organizationId: 'org-123',
|
|
361
|
-
limit: 100
|
|
362
|
-
})
|
|
363
|
-
|
|
364
|
-
expect(mockLimit).toHaveBeenCalledWith(100)
|
|
365
|
-
})
|
|
366
|
-
|
|
367
|
-
it('throws error when list query fails', async () => {
|
|
368
|
-
const mockLimit = vi.fn().mockResolvedValue({
|
|
369
|
-
data: null,
|
|
370
|
-
error: { message: 'Query timeout' }
|
|
371
|
-
})
|
|
372
|
-
const mockOrder = vi.fn().mockReturnValue({ limit: mockLimit })
|
|
373
|
-
const mockIs = vi.fn().mockReturnValue({ order: mockOrder })
|
|
374
|
-
const mockEq = vi.fn().mockReturnValue({ is: mockIs })
|
|
375
|
-
const mockSelect = vi.fn().mockReturnValue({ eq: mockEq })
|
|
376
|
-
|
|
377
|
-
;(mockDb.from as ReturnType<typeof vi.fn>).mockReturnValue({
|
|
378
|
-
select: mockSelect
|
|
379
|
-
})
|
|
380
|
-
|
|
381
|
-
await expect(sessionManager.list({ organizationId: 'org-123' })).rejects.toThrow(
|
|
382
|
-
'Failed to list sessions: Query timeout'
|
|
383
|
-
)
|
|
384
|
-
})
|
|
385
|
-
})
|
|
386
|
-
|
|
387
|
-
describe('delete', () => {
|
|
388
|
-
it('deletes session by ID and organization (soft delete)', async () => {
|
|
389
|
-
const mockEq = vi.fn().mockResolvedValue({ error: null })
|
|
390
|
-
const mockUpdate = vi.fn().mockReturnValue({
|
|
391
|
-
eq: vi.fn().mockReturnValue({
|
|
392
|
-
eq: mockEq
|
|
393
|
-
})
|
|
394
|
-
})
|
|
395
|
-
|
|
396
|
-
;(mockDb.from as ReturnType<typeof vi.fn>).mockReturnValue({
|
|
397
|
-
update: mockUpdate
|
|
398
|
-
})
|
|
399
|
-
|
|
400
|
-
await sessionManager.delete('session-123', 'org-123')
|
|
401
|
-
|
|
402
|
-
expect(mockDb.from).toHaveBeenCalledWith('sessions')
|
|
403
|
-
expect(mockUpdate).toHaveBeenCalledWith(expect.objectContaining({ deleted_at: expect.any(String) }))
|
|
404
|
-
})
|
|
405
|
-
|
|
406
|
-
it('throws error when delete fails', async () => {
|
|
407
|
-
const mockEq = vi.fn().mockResolvedValue({
|
|
408
|
-
error: { message: 'Foreign key constraint violation' }
|
|
409
|
-
})
|
|
410
|
-
const mockUpdate = vi.fn().mockReturnValue({
|
|
411
|
-
eq: vi.fn().mockReturnValue({
|
|
412
|
-
eq: mockEq
|
|
413
|
-
})
|
|
414
|
-
})
|
|
415
|
-
|
|
416
|
-
;(mockDb.from as ReturnType<typeof vi.fn>).mockReturnValue({
|
|
417
|
-
update: mockUpdate
|
|
418
|
-
})
|
|
419
|
-
|
|
420
|
-
await expect(sessionManager.delete('session-123', 'org-123')).rejects.toThrow(
|
|
421
|
-
'Failed to delete session: Foreign key constraint violation'
|
|
422
|
-
)
|
|
423
|
-
})
|
|
424
|
-
|
|
425
|
-
it('enforces organization isolation on delete', async () => {
|
|
426
|
-
const mockSecondEq = vi.fn().mockResolvedValue({ error: null })
|
|
427
|
-
const mockFirstEq = vi.fn().mockReturnValue({
|
|
428
|
-
eq: mockSecondEq
|
|
429
|
-
})
|
|
430
|
-
const mockUpdate = vi.fn().mockReturnValue({
|
|
431
|
-
eq: mockFirstEq
|
|
432
|
-
})
|
|
433
|
-
|
|
434
|
-
;(mockDb.from as ReturnType<typeof vi.fn>).mockReturnValue({
|
|
435
|
-
update: mockUpdate
|
|
436
|
-
})
|
|
437
|
-
|
|
438
|
-
await sessionManager.delete('session-123', 'org-456')
|
|
439
|
-
|
|
440
|
-
expect(mockFirstEq).toHaveBeenCalledWith('session_id', 'session-123')
|
|
441
|
-
expect(mockSecondEq).toHaveBeenCalledWith('organization_id', 'org-456')
|
|
442
|
-
})
|
|
443
|
-
})
|
|
444
|
-
|
|
445
|
-
describe('Concurrency & Race Conditions', () => {
|
|
446
|
-
it('handles multiple simultaneous create operations', async () => {
|
|
447
|
-
let callCount = 0
|
|
448
|
-
|
|
449
|
-
// Create a new mock for each call to avoid closure issues
|
|
450
|
-
;(mockDb.from as ReturnType<typeof vi.fn>).mockImplementation(() => ({
|
|
451
|
-
insert: vi.fn().mockReturnValue({
|
|
452
|
-
select: vi.fn().mockReturnValue({
|
|
453
|
-
single: vi.fn().mockImplementation(async () => {
|
|
454
|
-
const currentId = ++callCount
|
|
455
|
-
// Simulate async delay
|
|
456
|
-
await new Promise((resolve) => setTimeout(resolve, 10))
|
|
457
|
-
return {
|
|
458
|
-
data: {
|
|
459
|
-
session_id: `session-${currentId}`,
|
|
460
|
-
resource_id: 'agent-1',
|
|
461
|
-
organization_id: 'org-123',
|
|
462
|
-
user_id: 'user-123',
|
|
463
|
-
memory_snapshot: { sessionMemory: {}, history: [] },
|
|
464
|
-
metadata: null,
|
|
465
|
-
session_total_turns: 0,
|
|
466
|
-
created_at: '2025-01-01T00:00:00Z',
|
|
467
|
-
updated_at: '2025-01-01T00:00:00Z'
|
|
468
|
-
},
|
|
469
|
-
error: null
|
|
470
|
-
}
|
|
471
|
-
})
|
|
472
|
-
})
|
|
473
|
-
})
|
|
474
|
-
}))
|
|
475
|
-
|
|
476
|
-
// Create 5 sessions concurrently
|
|
477
|
-
const promises = Array.from({ length: 5 }, () =>
|
|
478
|
-
sessionManager.create({
|
|
479
|
-
resourceId: 'agent-1',
|
|
480
|
-
organizationId: 'org-123',
|
|
481
|
-
userId: 'user-123'
|
|
482
|
-
})
|
|
483
|
-
)
|
|
484
|
-
|
|
485
|
-
const sessions = await Promise.all(promises)
|
|
486
|
-
|
|
487
|
-
expect(sessions).toHaveLength(5)
|
|
488
|
-
// Each session should have unique ID
|
|
489
|
-
const ids = sessions.map((s) => s.sessionId)
|
|
490
|
-
expect(new Set(ids).size).toBe(5)
|
|
491
|
-
})
|
|
492
|
-
|
|
493
|
-
it('handles concurrent get operations for same session', async () => {
|
|
494
|
-
const mockSessionData = {
|
|
495
|
-
session_id: 'session-123',
|
|
496
|
-
resource_id: 'agent-1',
|
|
497
|
-
organization_id: 'org-123',
|
|
498
|
-
user_id: 'user-123',
|
|
499
|
-
memory_snapshot: { sessionMemory: {}, history: [] },
|
|
500
|
-
metadata: null,
|
|
501
|
-
session_total_turns: 0,
|
|
502
|
-
created_at: '2025-01-01T00:00:00Z',
|
|
503
|
-
updated_at: '2025-01-01T00:00:00Z'
|
|
504
|
-
}
|
|
505
|
-
|
|
506
|
-
const mockIs = vi.fn().mockReturnValue({
|
|
507
|
-
single: vi.fn().mockResolvedValue({ data: mockSessionData, error: null })
|
|
508
|
-
})
|
|
509
|
-
|
|
510
|
-
const mockSelect = vi.fn().mockReturnValue({
|
|
511
|
-
eq: vi.fn().mockReturnValue({
|
|
512
|
-
eq: vi.fn().mockReturnValue({
|
|
513
|
-
is: mockIs
|
|
514
|
-
})
|
|
515
|
-
})
|
|
516
|
-
})
|
|
517
|
-
|
|
518
|
-
;(mockDb.from as ReturnType<typeof vi.fn>).mockReturnValue({
|
|
519
|
-
select: mockSelect
|
|
520
|
-
})
|
|
521
|
-
|
|
522
|
-
// Read same session concurrently
|
|
523
|
-
const promises = Array.from({ length: 10 }, () => sessionManager.get('session-123', 'org-123'))
|
|
524
|
-
|
|
525
|
-
const sessions = await Promise.all(promises)
|
|
526
|
-
|
|
527
|
-
expect(sessions).toHaveLength(10)
|
|
528
|
-
expect(mockSelect).toHaveBeenCalledTimes(10)
|
|
529
|
-
// All should return session instances
|
|
530
|
-
sessions.forEach((s) => expect(s).toBeInstanceOf(Session))
|
|
531
|
-
})
|
|
532
|
-
|
|
533
|
-
it('handles concurrent list operations', async () => {
|
|
534
|
-
const mockSessionsData = [
|
|
535
|
-
{
|
|
536
|
-
session_id: 'session-1',
|
|
537
|
-
resource_id: 'agent-1',
|
|
538
|
-
organization_id: 'org-123',
|
|
539
|
-
user_id: 'user-1',
|
|
540
|
-
memory_snapshot: { sessionMemory: {}, history: [] },
|
|
541
|
-
metadata: null,
|
|
542
|
-
session_total_turns: 0,
|
|
543
|
-
created_at: '2025-01-01T00:00:00Z',
|
|
544
|
-
updated_at: '2025-01-01T00:00:00Z'
|
|
545
|
-
}
|
|
546
|
-
]
|
|
547
|
-
|
|
548
|
-
const mockLimit = vi.fn().mockResolvedValue({ data: mockSessionsData, error: null })
|
|
549
|
-
const mockOrder = vi.fn().mockReturnValue({ limit: mockLimit })
|
|
550
|
-
const mockIs = vi.fn().mockReturnValue({ order: mockOrder })
|
|
551
|
-
const mockEq = vi.fn().mockReturnValue({ is: mockIs })
|
|
552
|
-
const mockSelect = vi.fn().mockReturnValue({ eq: mockEq })
|
|
553
|
-
|
|
554
|
-
;(mockDb.from as ReturnType<typeof vi.fn>).mockReturnValue({
|
|
555
|
-
select: mockSelect
|
|
556
|
-
})
|
|
557
|
-
|
|
558
|
-
// Multiple concurrent list requests
|
|
559
|
-
const promises = Array.from({ length: 3 }, () => sessionManager.list({ organizationId: 'org-123' }))
|
|
560
|
-
|
|
561
|
-
const results = await Promise.all(promises)
|
|
562
|
-
|
|
563
|
-
expect(results).toHaveLength(3)
|
|
564
|
-
expect(mockSelect).toHaveBeenCalledTimes(3)
|
|
565
|
-
})
|
|
566
|
-
|
|
567
|
-
it('handles rapid create-then-get sequence', async () => {
|
|
568
|
-
const mockSessionData = {
|
|
569
|
-
session_id: 'session-123',
|
|
570
|
-
resource_id: 'agent-1',
|
|
571
|
-
organization_id: 'org-123',
|
|
572
|
-
user_id: 'user-123',
|
|
573
|
-
memory_snapshot: { sessionMemory: {}, history: [] },
|
|
574
|
-
metadata: null,
|
|
575
|
-
session_total_turns: 0,
|
|
576
|
-
created_at: '2025-01-01T00:00:00Z',
|
|
577
|
-
updated_at: '2025-01-01T00:00:00Z'
|
|
578
|
-
}
|
|
579
|
-
|
|
580
|
-
// Mock create
|
|
581
|
-
const mockInsert = vi.fn().mockReturnValue({
|
|
582
|
-
select: vi.fn().mockReturnValue({
|
|
583
|
-
single: vi.fn().mockResolvedValue({ data: mockSessionData, error: null })
|
|
584
|
-
})
|
|
585
|
-
})
|
|
586
|
-
|
|
587
|
-
// Mock get
|
|
588
|
-
const mockIs = vi.fn().mockReturnValue({
|
|
589
|
-
single: vi.fn().mockResolvedValue({ data: mockSessionData, error: null })
|
|
590
|
-
})
|
|
591
|
-
|
|
592
|
-
const mockSelect = vi.fn().mockReturnValue({
|
|
593
|
-
eq: vi.fn().mockReturnValue({
|
|
594
|
-
eq: vi.fn().mockReturnValue({
|
|
595
|
-
is: mockIs
|
|
596
|
-
})
|
|
597
|
-
})
|
|
598
|
-
})
|
|
599
|
-
|
|
600
|
-
;(mockDb.from as ReturnType<typeof vi.fn>).mockImplementation((table: string) => {
|
|
601
|
-
if (table === 'sessions') {
|
|
602
|
-
return {
|
|
603
|
-
insert: mockInsert,
|
|
604
|
-
select: mockSelect
|
|
605
|
-
}
|
|
606
|
-
}
|
|
607
|
-
return {}
|
|
608
|
-
})
|
|
609
|
-
|
|
610
|
-
// Create and immediately get
|
|
611
|
-
const session = await sessionManager.create({
|
|
612
|
-
resourceId: 'agent-1',
|
|
613
|
-
organizationId: 'org-123',
|
|
614
|
-
userId: 'user-123'
|
|
615
|
-
})
|
|
616
|
-
|
|
617
|
-
const retrieved = await sessionManager.get(session.sessionId, 'org-123')
|
|
618
|
-
|
|
619
|
-
expect(retrieved).not.toBeNull()
|
|
620
|
-
expect(retrieved!.sessionId).toBe(session.sessionId)
|
|
621
|
-
})
|
|
622
|
-
|
|
623
|
-
it('handles rapid create-then-delete sequence', async () => {
|
|
624
|
-
const mockSessionData = {
|
|
625
|
-
session_id: 'session-123',
|
|
626
|
-
resource_id: 'agent-1',
|
|
627
|
-
organization_id: 'org-123',
|
|
628
|
-
user_id: 'user-123',
|
|
629
|
-
memory_snapshot: { sessionMemory: {}, history: [] },
|
|
630
|
-
metadata: null,
|
|
631
|
-
session_total_turns: 0,
|
|
632
|
-
created_at: '2025-01-01T00:00:00Z',
|
|
633
|
-
updated_at: '2025-01-01T00:00:00Z'
|
|
634
|
-
}
|
|
635
|
-
|
|
636
|
-
const mockInsert = vi.fn().mockReturnValue({
|
|
637
|
-
select: vi.fn().mockReturnValue({
|
|
638
|
-
single: vi.fn().mockResolvedValue({ data: mockSessionData, error: null })
|
|
639
|
-
})
|
|
640
|
-
})
|
|
641
|
-
|
|
642
|
-
const mockUpdateEq = vi.fn().mockResolvedValue({ error: null })
|
|
643
|
-
const mockUpdate = vi.fn().mockReturnValue({
|
|
644
|
-
eq: vi.fn().mockReturnValue({
|
|
645
|
-
eq: mockUpdateEq
|
|
646
|
-
})
|
|
647
|
-
})
|
|
648
|
-
|
|
649
|
-
;(mockDb.from as ReturnType<typeof vi.fn>).mockImplementation((table: string) => {
|
|
650
|
-
if (table === 'sessions') {
|
|
651
|
-
return {
|
|
652
|
-
insert: mockInsert,
|
|
653
|
-
update: mockUpdate
|
|
654
|
-
}
|
|
655
|
-
}
|
|
656
|
-
return {}
|
|
657
|
-
})
|
|
658
|
-
|
|
659
|
-
const session = await sessionManager.create({
|
|
660
|
-
resourceId: 'agent-1',
|
|
661
|
-
organizationId: 'org-123',
|
|
662
|
-
userId: 'user-123'
|
|
663
|
-
})
|
|
664
|
-
|
|
665
|
-
await sessionManager.delete(session.sessionId, 'org-123')
|
|
666
|
-
|
|
667
|
-
expect(mockUpdate).toHaveBeenCalledWith(expect.objectContaining({ deleted_at: expect.any(String) }))
|
|
668
|
-
})
|
|
669
|
-
})
|
|
670
|
-
|
|
671
|
-
describe('Error Resilience', () => {
|
|
672
|
-
it('handles database connection errors on create', async () => {
|
|
673
|
-
const mockInsert = vi.fn().mockReturnValue({
|
|
674
|
-
select: vi.fn().mockReturnValue({
|
|
675
|
-
single: vi.fn().mockRejectedValue(new Error('Network error'))
|
|
676
|
-
})
|
|
677
|
-
})
|
|
678
|
-
|
|
679
|
-
;(mockDb.from as ReturnType<typeof vi.fn>).mockReturnValue({
|
|
680
|
-
insert: mockInsert
|
|
681
|
-
})
|
|
682
|
-
|
|
683
|
-
await expect(
|
|
684
|
-
sessionManager.create({
|
|
685
|
-
resourceId: 'agent-1',
|
|
686
|
-
organizationId: 'org-123'
|
|
687
|
-
})
|
|
688
|
-
).rejects.toThrow('Network error')
|
|
689
|
-
})
|
|
690
|
-
|
|
691
|
-
it('handles database timeout on get', async () => {
|
|
692
|
-
const mockIs = vi.fn().mockReturnValue({
|
|
693
|
-
single: vi.fn().mockRejectedValue(new Error('Query timeout'))
|
|
694
|
-
})
|
|
695
|
-
|
|
696
|
-
const mockSelect = vi.fn().mockReturnValue({
|
|
697
|
-
eq: vi.fn().mockReturnValue({
|
|
698
|
-
eq: vi.fn().mockReturnValue({
|
|
699
|
-
is: mockIs
|
|
700
|
-
})
|
|
701
|
-
})
|
|
702
|
-
})
|
|
703
|
-
|
|
704
|
-
;(mockDb.from as ReturnType<typeof vi.fn>).mockReturnValue({
|
|
705
|
-
select: mockSelect
|
|
706
|
-
})
|
|
707
|
-
|
|
708
|
-
await expect(sessionManager.get('session-123', 'org-123')).rejects.toThrow('Query timeout')
|
|
709
|
-
})
|
|
710
|
-
|
|
711
|
-
it('handles malformed database responses on create', async () => {
|
|
712
|
-
const mockInsert = vi.fn().mockReturnValue({
|
|
713
|
-
select: vi.fn().mockReturnValue({
|
|
714
|
-
single: vi.fn().mockResolvedValue({
|
|
715
|
-
data: null, // Missing data
|
|
716
|
-
error: null // But no error either (malformed response)
|
|
717
|
-
})
|
|
718
|
-
})
|
|
719
|
-
})
|
|
720
|
-
|
|
721
|
-
;(mockDb.from as ReturnType<typeof vi.fn>).mockReturnValue({
|
|
722
|
-
insert: mockInsert
|
|
723
|
-
})
|
|
724
|
-
|
|
725
|
-
// If implementation doesn't validate null data, this documents the behavior
|
|
726
|
-
// Either throws TypeError or successfully creates Session with null
|
|
727
|
-
try {
|
|
728
|
-
const result = await sessionManager.create({
|
|
729
|
-
resourceId: 'agent-1',
|
|
730
|
-
organizationId: 'org-123'
|
|
731
|
-
})
|
|
732
|
-
// If it succeeds, Session constructor handles null gracefully
|
|
733
|
-
expect(result).toBeDefined()
|
|
734
|
-
} catch (error) {
|
|
735
|
-
// Or it throws - both behaviors are acceptable for malformed responses
|
|
736
|
-
expect(error).toBeDefined()
|
|
737
|
-
}
|
|
738
|
-
})
|
|
739
|
-
|
|
740
|
-
it('handles empty organization ID gracefully', async () => {
|
|
741
|
-
const mockInsert = vi.fn().mockReturnValue({
|
|
742
|
-
select: vi.fn().mockReturnValue({
|
|
743
|
-
single: vi.fn().mockResolvedValue({
|
|
744
|
-
data: null,
|
|
745
|
-
error: { message: 'organization_id cannot be empty' }
|
|
746
|
-
})
|
|
747
|
-
})
|
|
748
|
-
})
|
|
749
|
-
|
|
750
|
-
;(mockDb.from as ReturnType<typeof vi.fn>).mockReturnValue({
|
|
751
|
-
insert: mockInsert
|
|
752
|
-
})
|
|
753
|
-
|
|
754
|
-
await expect(
|
|
755
|
-
sessionManager.create({
|
|
756
|
-
resourceId: 'agent-1',
|
|
757
|
-
organizationId: ''
|
|
758
|
-
})
|
|
759
|
-
).rejects.toThrow('organization_id cannot be empty')
|
|
760
|
-
})
|
|
761
|
-
|
|
762
|
-
it('handles null values in required fields', async () => {
|
|
763
|
-
const mockInsert = vi.fn().mockReturnValue({
|
|
764
|
-
select: vi.fn().mockReturnValue({
|
|
765
|
-
single: vi.fn().mockResolvedValue({
|
|
766
|
-
data: null,
|
|
767
|
-
error: { message: 'null value in column "resource_id" violates not-null constraint' }
|
|
768
|
-
})
|
|
769
|
-
})
|
|
770
|
-
})
|
|
771
|
-
|
|
772
|
-
;(mockDb.from as ReturnType<typeof vi.fn>).mockReturnValue({
|
|
773
|
-
insert: mockInsert
|
|
774
|
-
})
|
|
775
|
-
|
|
776
|
-
await expect(
|
|
777
|
-
sessionManager.create({
|
|
778
|
-
resourceId: null as unknown,
|
|
779
|
-
organizationId: 'org-123'
|
|
780
|
-
})
|
|
781
|
-
).rejects.toThrow('null value in column "resource_id"')
|
|
782
|
-
})
|
|
783
|
-
|
|
784
|
-
it('handles list with no results gracefully', async () => {
|
|
785
|
-
const mockLimit = vi.fn().mockResolvedValue({ data: [], error: null })
|
|
786
|
-
const mockOrder = vi.fn().mockReturnValue({ limit: mockLimit })
|
|
787
|
-
const mockIs = vi.fn().mockReturnValue({ order: mockOrder })
|
|
788
|
-
const mockEq = vi.fn().mockReturnValue({ is: mockIs })
|
|
789
|
-
const mockSelect = vi.fn().mockReturnValue({ eq: mockEq })
|
|
790
|
-
|
|
791
|
-
;(mockDb.from as ReturnType<typeof vi.fn>).mockReturnValue({
|
|
792
|
-
select: mockSelect
|
|
793
|
-
})
|
|
794
|
-
|
|
795
|
-
const sessions = await sessionManager.list({ organizationId: 'org-123' })
|
|
796
|
-
|
|
797
|
-
expect(sessions).toEqual([])
|
|
798
|
-
})
|
|
799
|
-
|
|
800
|
-
it('handles database returning null data for list', async () => {
|
|
801
|
-
const mockLimit = vi.fn().mockResolvedValue({ data: null, error: null })
|
|
802
|
-
const mockOrder = vi.fn().mockReturnValue({ limit: mockLimit })
|
|
803
|
-
const mockIs = vi.fn().mockReturnValue({ order: mockOrder })
|
|
804
|
-
const mockEq = vi.fn().mockReturnValue({ is: mockIs })
|
|
805
|
-
const mockSelect = vi.fn().mockReturnValue({ eq: mockEq })
|
|
806
|
-
|
|
807
|
-
;(mockDb.from as ReturnType<typeof vi.fn>).mockReturnValue({
|
|
808
|
-
select: mockSelect
|
|
809
|
-
})
|
|
810
|
-
|
|
811
|
-
// May throw TypeError when trying to map over null
|
|
812
|
-
try {
|
|
813
|
-
await sessionManager.list({ organizationId: 'org-123' })
|
|
814
|
-
// If it doesn't throw, fail the test
|
|
815
|
-
expect.fail('Expected an error to be thrown')
|
|
816
|
-
} catch (error) {
|
|
817
|
-
expect(error).toBeDefined()
|
|
818
|
-
}
|
|
819
|
-
})
|
|
820
|
-
})
|
|
821
|
-
})
|
|
1
|
+
import { describe, it, expect, beforeEach, vi } from 'vitest'
|
|
2
|
+
import { SessionManager } from '../server/manager'
|
|
3
|
+
import { Session } from '../server/session'
|
|
4
|
+
import type { SupabaseClient } from '../../../supabase'
|
|
5
|
+
|
|
6
|
+
describe('SessionManager', () => {
|
|
7
|
+
let mockDb: SupabaseClient
|
|
8
|
+
let sessionManager: SessionManager
|
|
9
|
+
|
|
10
|
+
beforeEach(() => {
|
|
11
|
+
// Create mock Supabase client
|
|
12
|
+
mockDb = {
|
|
13
|
+
from: vi.fn()
|
|
14
|
+
} as unknown as SupabaseClient
|
|
15
|
+
|
|
16
|
+
sessionManager = new SessionManager(mockDb)
|
|
17
|
+
})
|
|
18
|
+
|
|
19
|
+
describe('create', () => {
|
|
20
|
+
it('creates a new session with all parameters', async () => {
|
|
21
|
+
const mockSessionData = {
|
|
22
|
+
session_id: 'session-123',
|
|
23
|
+
resource_id: 'agent-1',
|
|
24
|
+
organization_id: 'org-123',
|
|
25
|
+
user_id: 'user-123',
|
|
26
|
+
memory_snapshot: { sessionMemory: {}, history: [] },
|
|
27
|
+
metadata: { foo: 'bar' },
|
|
28
|
+
session_total_turns: 0,
|
|
29
|
+
created_at: '2025-01-01T00:00:00Z',
|
|
30
|
+
updated_at: '2025-01-01T00:00:00Z'
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const mockInsert = vi.fn().mockReturnValue({
|
|
34
|
+
select: vi.fn().mockReturnValue({
|
|
35
|
+
single: vi.fn().mockResolvedValue({ data: mockSessionData, error: null })
|
|
36
|
+
})
|
|
37
|
+
})
|
|
38
|
+
|
|
39
|
+
;(mockDb.from as ReturnType<typeof vi.fn>).mockReturnValue({
|
|
40
|
+
insert: mockInsert
|
|
41
|
+
})
|
|
42
|
+
|
|
43
|
+
const session = await sessionManager.create({
|
|
44
|
+
resourceId: 'agent-1',
|
|
45
|
+
organizationId: 'org-123',
|
|
46
|
+
userId: 'user-123',
|
|
47
|
+
metadata: { foo: 'bar' }
|
|
48
|
+
})
|
|
49
|
+
|
|
50
|
+
expect(mockDb.from).toHaveBeenCalledWith('sessions')
|
|
51
|
+
expect(mockInsert).toHaveBeenCalledWith({
|
|
52
|
+
resource_id: 'agent-1',
|
|
53
|
+
organization_id: 'org-123',
|
|
54
|
+
user_id: 'user-123',
|
|
55
|
+
memory_snapshot: { sessionMemory: {}, history: [] },
|
|
56
|
+
metadata: { foo: 'bar' },
|
|
57
|
+
session_total_turns: 0
|
|
58
|
+
})
|
|
59
|
+
expect(session).toBeInstanceOf(Session)
|
|
60
|
+
expect(session.sessionId).toBe('session-123')
|
|
61
|
+
expect(session.organizationId).toBe('org-123')
|
|
62
|
+
})
|
|
63
|
+
|
|
64
|
+
it('creates session without userId (optional)', async () => {
|
|
65
|
+
const mockSessionData = {
|
|
66
|
+
session_id: 'session-123',
|
|
67
|
+
resource_id: 'agent-1',
|
|
68
|
+
organization_id: 'org-123',
|
|
69
|
+
user_id: null,
|
|
70
|
+
memory_snapshot: { sessionMemory: {}, history: [] },
|
|
71
|
+
metadata: null,
|
|
72
|
+
session_total_turns: 0,
|
|
73
|
+
created_at: '2025-01-01T00:00:00Z',
|
|
74
|
+
updated_at: '2025-01-01T00:00:00Z'
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
const mockInsert = vi.fn().mockReturnValue({
|
|
78
|
+
select: vi.fn().mockReturnValue({
|
|
79
|
+
single: vi.fn().mockResolvedValue({ data: mockSessionData, error: null })
|
|
80
|
+
})
|
|
81
|
+
})
|
|
82
|
+
|
|
83
|
+
;(mockDb.from as ReturnType<typeof vi.fn>).mockReturnValue({
|
|
84
|
+
insert: mockInsert
|
|
85
|
+
})
|
|
86
|
+
|
|
87
|
+
const session = await sessionManager.create({
|
|
88
|
+
resourceId: 'agent-1',
|
|
89
|
+
organizationId: 'org-123'
|
|
90
|
+
})
|
|
91
|
+
|
|
92
|
+
expect(mockInsert).toHaveBeenCalledWith({
|
|
93
|
+
resource_id: 'agent-1',
|
|
94
|
+
organization_id: 'org-123',
|
|
95
|
+
user_id: null,
|
|
96
|
+
memory_snapshot: { sessionMemory: {}, history: [] },
|
|
97
|
+
metadata: null,
|
|
98
|
+
session_total_turns: 0
|
|
99
|
+
})
|
|
100
|
+
expect(session.userId).toBeNull()
|
|
101
|
+
})
|
|
102
|
+
|
|
103
|
+
it('throws error when database insert fails', async () => {
|
|
104
|
+
const mockInsert = vi.fn().mockReturnValue({
|
|
105
|
+
select: vi.fn().mockReturnValue({
|
|
106
|
+
single: vi.fn().mockResolvedValue({
|
|
107
|
+
data: null,
|
|
108
|
+
error: { message: 'Database connection failed' }
|
|
109
|
+
})
|
|
110
|
+
})
|
|
111
|
+
})
|
|
112
|
+
|
|
113
|
+
;(mockDb.from as ReturnType<typeof vi.fn>).mockReturnValue({
|
|
114
|
+
insert: mockInsert
|
|
115
|
+
})
|
|
116
|
+
|
|
117
|
+
await expect(
|
|
118
|
+
sessionManager.create({
|
|
119
|
+
resourceId: 'agent-1',
|
|
120
|
+
organizationId: 'org-123'
|
|
121
|
+
})
|
|
122
|
+
).rejects.toThrow('Failed to create session: Database connection failed')
|
|
123
|
+
})
|
|
124
|
+
})
|
|
125
|
+
|
|
126
|
+
describe('get', () => {
|
|
127
|
+
it('retrieves session by ID and organization', async () => {
|
|
128
|
+
const mockSessionData = {
|
|
129
|
+
session_id: 'session-123',
|
|
130
|
+
resource_id: 'agent-1',
|
|
131
|
+
organization_id: 'org-123',
|
|
132
|
+
user_id: 'user-123',
|
|
133
|
+
memory_snapshot: { sessionMemory: {}, history: [] },
|
|
134
|
+
metadata: null,
|
|
135
|
+
session_total_turns: 0,
|
|
136
|
+
created_at: '2025-01-01T00:00:00Z',
|
|
137
|
+
updated_at: '2025-01-01T00:00:00Z'
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
const mockIs = vi.fn().mockReturnValue({
|
|
141
|
+
single: vi.fn().mockResolvedValue({ data: mockSessionData, error: null })
|
|
142
|
+
})
|
|
143
|
+
|
|
144
|
+
const mockSelect = vi.fn().mockReturnValue({
|
|
145
|
+
eq: vi.fn().mockReturnValue({
|
|
146
|
+
eq: vi.fn().mockReturnValue({
|
|
147
|
+
is: mockIs
|
|
148
|
+
})
|
|
149
|
+
})
|
|
150
|
+
})
|
|
151
|
+
|
|
152
|
+
;(mockDb.from as ReturnType<typeof vi.fn>).mockReturnValue({
|
|
153
|
+
select: mockSelect
|
|
154
|
+
})
|
|
155
|
+
|
|
156
|
+
const session = await sessionManager.get('session-123', 'org-123')
|
|
157
|
+
|
|
158
|
+
expect(mockDb.from).toHaveBeenCalledWith('sessions')
|
|
159
|
+
expect(session).toBeInstanceOf(Session)
|
|
160
|
+
expect(session!.sessionId).toBe('session-123')
|
|
161
|
+
})
|
|
162
|
+
|
|
163
|
+
it('returns null when session not found', async () => {
|
|
164
|
+
const mockIs = vi.fn().mockReturnValue({
|
|
165
|
+
single: vi.fn().mockResolvedValue({
|
|
166
|
+
data: null,
|
|
167
|
+
error: { code: 'PGRST116', message: 'Not found' }
|
|
168
|
+
})
|
|
169
|
+
})
|
|
170
|
+
|
|
171
|
+
const mockSelect = vi.fn().mockReturnValue({
|
|
172
|
+
eq: vi.fn().mockReturnValue({
|
|
173
|
+
eq: vi.fn().mockReturnValue({
|
|
174
|
+
is: mockIs
|
|
175
|
+
})
|
|
176
|
+
})
|
|
177
|
+
})
|
|
178
|
+
|
|
179
|
+
;(mockDb.from as ReturnType<typeof vi.fn>).mockReturnValue({
|
|
180
|
+
select: mockSelect
|
|
181
|
+
})
|
|
182
|
+
|
|
183
|
+
const session = await sessionManager.get('nonexistent', 'org-123')
|
|
184
|
+
|
|
185
|
+
expect(session).toBeNull()
|
|
186
|
+
})
|
|
187
|
+
|
|
188
|
+
it('throws error for database errors (not 404)', async () => {
|
|
189
|
+
const mockIs = vi.fn().mockReturnValue({
|
|
190
|
+
single: vi.fn().mockResolvedValue({
|
|
191
|
+
data: null,
|
|
192
|
+
error: { code: 'DB_ERROR', message: 'Connection timeout' }
|
|
193
|
+
})
|
|
194
|
+
})
|
|
195
|
+
|
|
196
|
+
const mockSelect = vi.fn().mockReturnValue({
|
|
197
|
+
eq: vi.fn().mockReturnValue({
|
|
198
|
+
eq: vi.fn().mockReturnValue({
|
|
199
|
+
is: mockIs
|
|
200
|
+
})
|
|
201
|
+
})
|
|
202
|
+
})
|
|
203
|
+
|
|
204
|
+
;(mockDb.from as ReturnType<typeof vi.fn>).mockReturnValue({
|
|
205
|
+
select: mockSelect
|
|
206
|
+
})
|
|
207
|
+
|
|
208
|
+
await expect(sessionManager.get('session-123', 'org-123')).rejects.toThrow(
|
|
209
|
+
'Failed to get session: Connection timeout'
|
|
210
|
+
)
|
|
211
|
+
})
|
|
212
|
+
})
|
|
213
|
+
|
|
214
|
+
describe('list', () => {
|
|
215
|
+
it('lists sessions for organization', async () => {
|
|
216
|
+
const mockSessionsData = [
|
|
217
|
+
{
|
|
218
|
+
session_id: 'session-1',
|
|
219
|
+
resource_id: 'agent-1',
|
|
220
|
+
organization_id: 'org-123',
|
|
221
|
+
user_id: 'user-1',
|
|
222
|
+
memory_snapshot: { sessionMemory: {}, history: [] },
|
|
223
|
+
metadata: null,
|
|
224
|
+
session_total_turns: 0,
|
|
225
|
+
created_at: '2025-01-01T00:00:00Z',
|
|
226
|
+
updated_at: '2025-01-01T00:00:00Z'
|
|
227
|
+
},
|
|
228
|
+
{
|
|
229
|
+
session_id: 'session-2',
|
|
230
|
+
resource_id: 'agent-1',
|
|
231
|
+
organization_id: 'org-123',
|
|
232
|
+
user_id: 'user-2',
|
|
233
|
+
memory_snapshot: { sessionMemory: {}, history: [] },
|
|
234
|
+
metadata: null,
|
|
235
|
+
session_total_turns: 0,
|
|
236
|
+
created_at: '2025-01-01T00:00:00Z',
|
|
237
|
+
updated_at: '2025-01-01T00:00:00Z'
|
|
238
|
+
}
|
|
239
|
+
]
|
|
240
|
+
|
|
241
|
+
const mockLimit = vi.fn().mockResolvedValue({ data: mockSessionsData, error: null })
|
|
242
|
+
const mockOrder = vi.fn().mockReturnValue({ limit: mockLimit })
|
|
243
|
+
const mockIs = vi.fn().mockReturnValue({ order: mockOrder })
|
|
244
|
+
const mockEq = vi.fn().mockReturnValue({ is: mockIs })
|
|
245
|
+
const mockSelect = vi.fn().mockReturnValue({ eq: mockEq })
|
|
246
|
+
|
|
247
|
+
;(mockDb.from as ReturnType<typeof vi.fn>).mockReturnValue({
|
|
248
|
+
select: mockSelect
|
|
249
|
+
})
|
|
250
|
+
|
|
251
|
+
const sessions = await sessionManager.list({
|
|
252
|
+
organizationId: 'org-123'
|
|
253
|
+
})
|
|
254
|
+
|
|
255
|
+
expect(mockDb.from).toHaveBeenCalledWith('sessions')
|
|
256
|
+
expect(mockEq).toHaveBeenCalledWith('organization_id', 'org-123')
|
|
257
|
+
expect(mockIs).toHaveBeenCalledWith('deleted_at', null)
|
|
258
|
+
expect(mockOrder).toHaveBeenCalledWith('created_at', { ascending: false })
|
|
259
|
+
expect(mockLimit).toHaveBeenCalledWith(50) // Default limit
|
|
260
|
+
expect(sessions).toHaveLength(2)
|
|
261
|
+
expect(sessions[0]).toBeInstanceOf(Session)
|
|
262
|
+
})
|
|
263
|
+
|
|
264
|
+
it('filters sessions by userId', async () => {
|
|
265
|
+
const mockSessionsData = [
|
|
266
|
+
{
|
|
267
|
+
session_id: 'session-1',
|
|
268
|
+
resource_id: 'agent-1',
|
|
269
|
+
organization_id: 'org-123',
|
|
270
|
+
user_id: 'user-123',
|
|
271
|
+
memory_snapshot: { sessionMemory: {}, history: [] },
|
|
272
|
+
metadata: null,
|
|
273
|
+
session_total_turns: 0,
|
|
274
|
+
created_at: '2025-01-01T00:00:00Z',
|
|
275
|
+
updated_at: '2025-01-01T00:00:00Z'
|
|
276
|
+
}
|
|
277
|
+
]
|
|
278
|
+
|
|
279
|
+
// Create a mock query builder that chains properly and is awaitable
|
|
280
|
+
const mockQueryBuilder: Record<string, unknown> = {}
|
|
281
|
+
;(mockQueryBuilder as Record<string, unknown>).eq = vi.fn().mockReturnValue(mockQueryBuilder)
|
|
282
|
+
;(mockQueryBuilder as Record<string, unknown>).then = vi.fn((resolve: (data: unknown) => void) => {
|
|
283
|
+
resolve({ data: mockSessionsData, error: null })
|
|
284
|
+
return Promise.resolve({ data: mockSessionsData, error: null })
|
|
285
|
+
})
|
|
286
|
+
|
|
287
|
+
const mockLimit = vi.fn().mockReturnValue(mockQueryBuilder)
|
|
288
|
+
const mockOrder = vi.fn().mockReturnValue({ limit: mockLimit })
|
|
289
|
+
const mockIs = vi.fn().mockReturnValue({ order: mockOrder })
|
|
290
|
+
const mockOrgEq = vi.fn().mockReturnValue({ is: mockIs })
|
|
291
|
+
const mockSelect = vi.fn().mockReturnValue({ eq: mockOrgEq })
|
|
292
|
+
|
|
293
|
+
;(mockDb.from as ReturnType<typeof vi.fn>).mockReturnValue({
|
|
294
|
+
select: mockSelect
|
|
295
|
+
})
|
|
296
|
+
|
|
297
|
+
const sessions = await sessionManager.list({
|
|
298
|
+
organizationId: 'org-123',
|
|
299
|
+
userId: 'user-123'
|
|
300
|
+
})
|
|
301
|
+
|
|
302
|
+
expect(mockQueryBuilder.eq).toHaveBeenCalledWith('user_id', 'user-123')
|
|
303
|
+
expect(sessions).toHaveLength(1)
|
|
304
|
+
})
|
|
305
|
+
|
|
306
|
+
it('filters sessions by resourceId', async () => {
|
|
307
|
+
const mockSessionsData = [
|
|
308
|
+
{
|
|
309
|
+
session_id: 'session-1',
|
|
310
|
+
resource_id: 'agent-1',
|
|
311
|
+
organization_id: 'org-123',
|
|
312
|
+
user_id: 'user-1',
|
|
313
|
+
memory_snapshot: { sessionMemory: {}, history: [] },
|
|
314
|
+
metadata: null,
|
|
315
|
+
session_total_turns: 0,
|
|
316
|
+
created_at: '2025-01-01T00:00:00Z',
|
|
317
|
+
updated_at: '2025-01-01T00:00:00Z'
|
|
318
|
+
}
|
|
319
|
+
]
|
|
320
|
+
|
|
321
|
+
// Create a mock query builder that chains properly and is awaitable
|
|
322
|
+
const mockQueryBuilder: Record<string, unknown> = {}
|
|
323
|
+
;(mockQueryBuilder as Record<string, unknown>).eq = vi.fn().mockReturnValue(mockQueryBuilder)
|
|
324
|
+
;(mockQueryBuilder as Record<string, unknown>).then = vi.fn((resolve: (data: unknown) => void) => {
|
|
325
|
+
resolve({ data: mockSessionsData, error: null })
|
|
326
|
+
return Promise.resolve({ data: mockSessionsData, error: null })
|
|
327
|
+
})
|
|
328
|
+
|
|
329
|
+
const mockLimit = vi.fn().mockReturnValue(mockQueryBuilder)
|
|
330
|
+
const mockOrder = vi.fn().mockReturnValue({ limit: mockLimit })
|
|
331
|
+
const mockIs = vi.fn().mockReturnValue({ order: mockOrder })
|
|
332
|
+
const mockOrgEq = vi.fn().mockReturnValue({ is: mockIs })
|
|
333
|
+
const mockSelect = vi.fn().mockReturnValue({ eq: mockOrgEq })
|
|
334
|
+
|
|
335
|
+
;(mockDb.from as ReturnType<typeof vi.fn>).mockReturnValue({
|
|
336
|
+
select: mockSelect
|
|
337
|
+
})
|
|
338
|
+
|
|
339
|
+
const sessions = await sessionManager.list({
|
|
340
|
+
organizationId: 'org-123',
|
|
341
|
+
resourceId: 'agent-1'
|
|
342
|
+
})
|
|
343
|
+
|
|
344
|
+
expect(mockQueryBuilder.eq).toHaveBeenCalledWith('resource_id', 'agent-1')
|
|
345
|
+
expect(sessions).toHaveLength(1)
|
|
346
|
+
})
|
|
347
|
+
|
|
348
|
+
it('respects custom limit parameter', async () => {
|
|
349
|
+
const mockLimit = vi.fn().mockResolvedValue({ data: [], error: null })
|
|
350
|
+
const mockOrder = vi.fn().mockReturnValue({ limit: mockLimit })
|
|
351
|
+
const mockIs = vi.fn().mockReturnValue({ order: mockOrder })
|
|
352
|
+
const mockEq = vi.fn().mockReturnValue({ is: mockIs })
|
|
353
|
+
const mockSelect = vi.fn().mockReturnValue({ eq: mockEq })
|
|
354
|
+
|
|
355
|
+
;(mockDb.from as ReturnType<typeof vi.fn>).mockReturnValue({
|
|
356
|
+
select: mockSelect
|
|
357
|
+
})
|
|
358
|
+
|
|
359
|
+
await sessionManager.list({
|
|
360
|
+
organizationId: 'org-123',
|
|
361
|
+
limit: 100
|
|
362
|
+
})
|
|
363
|
+
|
|
364
|
+
expect(mockLimit).toHaveBeenCalledWith(100)
|
|
365
|
+
})
|
|
366
|
+
|
|
367
|
+
it('throws error when list query fails', async () => {
|
|
368
|
+
const mockLimit = vi.fn().mockResolvedValue({
|
|
369
|
+
data: null,
|
|
370
|
+
error: { message: 'Query timeout' }
|
|
371
|
+
})
|
|
372
|
+
const mockOrder = vi.fn().mockReturnValue({ limit: mockLimit })
|
|
373
|
+
const mockIs = vi.fn().mockReturnValue({ order: mockOrder })
|
|
374
|
+
const mockEq = vi.fn().mockReturnValue({ is: mockIs })
|
|
375
|
+
const mockSelect = vi.fn().mockReturnValue({ eq: mockEq })
|
|
376
|
+
|
|
377
|
+
;(mockDb.from as ReturnType<typeof vi.fn>).mockReturnValue({
|
|
378
|
+
select: mockSelect
|
|
379
|
+
})
|
|
380
|
+
|
|
381
|
+
await expect(sessionManager.list({ organizationId: 'org-123' })).rejects.toThrow(
|
|
382
|
+
'Failed to list sessions: Query timeout'
|
|
383
|
+
)
|
|
384
|
+
})
|
|
385
|
+
})
|
|
386
|
+
|
|
387
|
+
describe('delete', () => {
|
|
388
|
+
it('deletes session by ID and organization (soft delete)', async () => {
|
|
389
|
+
const mockEq = vi.fn().mockResolvedValue({ error: null })
|
|
390
|
+
const mockUpdate = vi.fn().mockReturnValue({
|
|
391
|
+
eq: vi.fn().mockReturnValue({
|
|
392
|
+
eq: mockEq
|
|
393
|
+
})
|
|
394
|
+
})
|
|
395
|
+
|
|
396
|
+
;(mockDb.from as ReturnType<typeof vi.fn>).mockReturnValue({
|
|
397
|
+
update: mockUpdate
|
|
398
|
+
})
|
|
399
|
+
|
|
400
|
+
await sessionManager.delete('session-123', 'org-123')
|
|
401
|
+
|
|
402
|
+
expect(mockDb.from).toHaveBeenCalledWith('sessions')
|
|
403
|
+
expect(mockUpdate).toHaveBeenCalledWith(expect.objectContaining({ deleted_at: expect.any(String) }))
|
|
404
|
+
})
|
|
405
|
+
|
|
406
|
+
it('throws error when delete fails', async () => {
|
|
407
|
+
const mockEq = vi.fn().mockResolvedValue({
|
|
408
|
+
error: { message: 'Foreign key constraint violation' }
|
|
409
|
+
})
|
|
410
|
+
const mockUpdate = vi.fn().mockReturnValue({
|
|
411
|
+
eq: vi.fn().mockReturnValue({
|
|
412
|
+
eq: mockEq
|
|
413
|
+
})
|
|
414
|
+
})
|
|
415
|
+
|
|
416
|
+
;(mockDb.from as ReturnType<typeof vi.fn>).mockReturnValue({
|
|
417
|
+
update: mockUpdate
|
|
418
|
+
})
|
|
419
|
+
|
|
420
|
+
await expect(sessionManager.delete('session-123', 'org-123')).rejects.toThrow(
|
|
421
|
+
'Failed to delete session: Foreign key constraint violation'
|
|
422
|
+
)
|
|
423
|
+
})
|
|
424
|
+
|
|
425
|
+
it('enforces organization isolation on delete', async () => {
|
|
426
|
+
const mockSecondEq = vi.fn().mockResolvedValue({ error: null })
|
|
427
|
+
const mockFirstEq = vi.fn().mockReturnValue({
|
|
428
|
+
eq: mockSecondEq
|
|
429
|
+
})
|
|
430
|
+
const mockUpdate = vi.fn().mockReturnValue({
|
|
431
|
+
eq: mockFirstEq
|
|
432
|
+
})
|
|
433
|
+
|
|
434
|
+
;(mockDb.from as ReturnType<typeof vi.fn>).mockReturnValue({
|
|
435
|
+
update: mockUpdate
|
|
436
|
+
})
|
|
437
|
+
|
|
438
|
+
await sessionManager.delete('session-123', 'org-456')
|
|
439
|
+
|
|
440
|
+
expect(mockFirstEq).toHaveBeenCalledWith('session_id', 'session-123')
|
|
441
|
+
expect(mockSecondEq).toHaveBeenCalledWith('organization_id', 'org-456')
|
|
442
|
+
})
|
|
443
|
+
})
|
|
444
|
+
|
|
445
|
+
describe('Concurrency & Race Conditions', () => {
|
|
446
|
+
it('handles multiple simultaneous create operations', async () => {
|
|
447
|
+
let callCount = 0
|
|
448
|
+
|
|
449
|
+
// Create a new mock for each call to avoid closure issues
|
|
450
|
+
;(mockDb.from as ReturnType<typeof vi.fn>).mockImplementation(() => ({
|
|
451
|
+
insert: vi.fn().mockReturnValue({
|
|
452
|
+
select: vi.fn().mockReturnValue({
|
|
453
|
+
single: vi.fn().mockImplementation(async () => {
|
|
454
|
+
const currentId = ++callCount
|
|
455
|
+
// Simulate async delay
|
|
456
|
+
await new Promise((resolve) => setTimeout(resolve, 10))
|
|
457
|
+
return {
|
|
458
|
+
data: {
|
|
459
|
+
session_id: `session-${currentId}`,
|
|
460
|
+
resource_id: 'agent-1',
|
|
461
|
+
organization_id: 'org-123',
|
|
462
|
+
user_id: 'user-123',
|
|
463
|
+
memory_snapshot: { sessionMemory: {}, history: [] },
|
|
464
|
+
metadata: null,
|
|
465
|
+
session_total_turns: 0,
|
|
466
|
+
created_at: '2025-01-01T00:00:00Z',
|
|
467
|
+
updated_at: '2025-01-01T00:00:00Z'
|
|
468
|
+
},
|
|
469
|
+
error: null
|
|
470
|
+
}
|
|
471
|
+
})
|
|
472
|
+
})
|
|
473
|
+
})
|
|
474
|
+
}))
|
|
475
|
+
|
|
476
|
+
// Create 5 sessions concurrently
|
|
477
|
+
const promises = Array.from({ length: 5 }, () =>
|
|
478
|
+
sessionManager.create({
|
|
479
|
+
resourceId: 'agent-1',
|
|
480
|
+
organizationId: 'org-123',
|
|
481
|
+
userId: 'user-123'
|
|
482
|
+
})
|
|
483
|
+
)
|
|
484
|
+
|
|
485
|
+
const sessions = await Promise.all(promises)
|
|
486
|
+
|
|
487
|
+
expect(sessions).toHaveLength(5)
|
|
488
|
+
// Each session should have unique ID
|
|
489
|
+
const ids = sessions.map((s) => s.sessionId)
|
|
490
|
+
expect(new Set(ids).size).toBe(5)
|
|
491
|
+
})
|
|
492
|
+
|
|
493
|
+
it('handles concurrent get operations for same session', async () => {
|
|
494
|
+
const mockSessionData = {
|
|
495
|
+
session_id: 'session-123',
|
|
496
|
+
resource_id: 'agent-1',
|
|
497
|
+
organization_id: 'org-123',
|
|
498
|
+
user_id: 'user-123',
|
|
499
|
+
memory_snapshot: { sessionMemory: {}, history: [] },
|
|
500
|
+
metadata: null,
|
|
501
|
+
session_total_turns: 0,
|
|
502
|
+
created_at: '2025-01-01T00:00:00Z',
|
|
503
|
+
updated_at: '2025-01-01T00:00:00Z'
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
const mockIs = vi.fn().mockReturnValue({
|
|
507
|
+
single: vi.fn().mockResolvedValue({ data: mockSessionData, error: null })
|
|
508
|
+
})
|
|
509
|
+
|
|
510
|
+
const mockSelect = vi.fn().mockReturnValue({
|
|
511
|
+
eq: vi.fn().mockReturnValue({
|
|
512
|
+
eq: vi.fn().mockReturnValue({
|
|
513
|
+
is: mockIs
|
|
514
|
+
})
|
|
515
|
+
})
|
|
516
|
+
})
|
|
517
|
+
|
|
518
|
+
;(mockDb.from as ReturnType<typeof vi.fn>).mockReturnValue({
|
|
519
|
+
select: mockSelect
|
|
520
|
+
})
|
|
521
|
+
|
|
522
|
+
// Read same session concurrently
|
|
523
|
+
const promises = Array.from({ length: 10 }, () => sessionManager.get('session-123', 'org-123'))
|
|
524
|
+
|
|
525
|
+
const sessions = await Promise.all(promises)
|
|
526
|
+
|
|
527
|
+
expect(sessions).toHaveLength(10)
|
|
528
|
+
expect(mockSelect).toHaveBeenCalledTimes(10)
|
|
529
|
+
// All should return session instances
|
|
530
|
+
sessions.forEach((s) => expect(s).toBeInstanceOf(Session))
|
|
531
|
+
})
|
|
532
|
+
|
|
533
|
+
it('handles concurrent list operations', async () => {
|
|
534
|
+
const mockSessionsData = [
|
|
535
|
+
{
|
|
536
|
+
session_id: 'session-1',
|
|
537
|
+
resource_id: 'agent-1',
|
|
538
|
+
organization_id: 'org-123',
|
|
539
|
+
user_id: 'user-1',
|
|
540
|
+
memory_snapshot: { sessionMemory: {}, history: [] },
|
|
541
|
+
metadata: null,
|
|
542
|
+
session_total_turns: 0,
|
|
543
|
+
created_at: '2025-01-01T00:00:00Z',
|
|
544
|
+
updated_at: '2025-01-01T00:00:00Z'
|
|
545
|
+
}
|
|
546
|
+
]
|
|
547
|
+
|
|
548
|
+
const mockLimit = vi.fn().mockResolvedValue({ data: mockSessionsData, error: null })
|
|
549
|
+
const mockOrder = vi.fn().mockReturnValue({ limit: mockLimit })
|
|
550
|
+
const mockIs = vi.fn().mockReturnValue({ order: mockOrder })
|
|
551
|
+
const mockEq = vi.fn().mockReturnValue({ is: mockIs })
|
|
552
|
+
const mockSelect = vi.fn().mockReturnValue({ eq: mockEq })
|
|
553
|
+
|
|
554
|
+
;(mockDb.from as ReturnType<typeof vi.fn>).mockReturnValue({
|
|
555
|
+
select: mockSelect
|
|
556
|
+
})
|
|
557
|
+
|
|
558
|
+
// Multiple concurrent list requests
|
|
559
|
+
const promises = Array.from({ length: 3 }, () => sessionManager.list({ organizationId: 'org-123' }))
|
|
560
|
+
|
|
561
|
+
const results = await Promise.all(promises)
|
|
562
|
+
|
|
563
|
+
expect(results).toHaveLength(3)
|
|
564
|
+
expect(mockSelect).toHaveBeenCalledTimes(3)
|
|
565
|
+
})
|
|
566
|
+
|
|
567
|
+
it('handles rapid create-then-get sequence', async () => {
|
|
568
|
+
const mockSessionData = {
|
|
569
|
+
session_id: 'session-123',
|
|
570
|
+
resource_id: 'agent-1',
|
|
571
|
+
organization_id: 'org-123',
|
|
572
|
+
user_id: 'user-123',
|
|
573
|
+
memory_snapshot: { sessionMemory: {}, history: [] },
|
|
574
|
+
metadata: null,
|
|
575
|
+
session_total_turns: 0,
|
|
576
|
+
created_at: '2025-01-01T00:00:00Z',
|
|
577
|
+
updated_at: '2025-01-01T00:00:00Z'
|
|
578
|
+
}
|
|
579
|
+
|
|
580
|
+
// Mock create
|
|
581
|
+
const mockInsert = vi.fn().mockReturnValue({
|
|
582
|
+
select: vi.fn().mockReturnValue({
|
|
583
|
+
single: vi.fn().mockResolvedValue({ data: mockSessionData, error: null })
|
|
584
|
+
})
|
|
585
|
+
})
|
|
586
|
+
|
|
587
|
+
// Mock get
|
|
588
|
+
const mockIs = vi.fn().mockReturnValue({
|
|
589
|
+
single: vi.fn().mockResolvedValue({ data: mockSessionData, error: null })
|
|
590
|
+
})
|
|
591
|
+
|
|
592
|
+
const mockSelect = vi.fn().mockReturnValue({
|
|
593
|
+
eq: vi.fn().mockReturnValue({
|
|
594
|
+
eq: vi.fn().mockReturnValue({
|
|
595
|
+
is: mockIs
|
|
596
|
+
})
|
|
597
|
+
})
|
|
598
|
+
})
|
|
599
|
+
|
|
600
|
+
;(mockDb.from as ReturnType<typeof vi.fn>).mockImplementation((table: string) => {
|
|
601
|
+
if (table === 'sessions') {
|
|
602
|
+
return {
|
|
603
|
+
insert: mockInsert,
|
|
604
|
+
select: mockSelect
|
|
605
|
+
}
|
|
606
|
+
}
|
|
607
|
+
return {}
|
|
608
|
+
})
|
|
609
|
+
|
|
610
|
+
// Create and immediately get
|
|
611
|
+
const session = await sessionManager.create({
|
|
612
|
+
resourceId: 'agent-1',
|
|
613
|
+
organizationId: 'org-123',
|
|
614
|
+
userId: 'user-123'
|
|
615
|
+
})
|
|
616
|
+
|
|
617
|
+
const retrieved = await sessionManager.get(session.sessionId, 'org-123')
|
|
618
|
+
|
|
619
|
+
expect(retrieved).not.toBeNull()
|
|
620
|
+
expect(retrieved!.sessionId).toBe(session.sessionId)
|
|
621
|
+
})
|
|
622
|
+
|
|
623
|
+
it('handles rapid create-then-delete sequence', async () => {
|
|
624
|
+
const mockSessionData = {
|
|
625
|
+
session_id: 'session-123',
|
|
626
|
+
resource_id: 'agent-1',
|
|
627
|
+
organization_id: 'org-123',
|
|
628
|
+
user_id: 'user-123',
|
|
629
|
+
memory_snapshot: { sessionMemory: {}, history: [] },
|
|
630
|
+
metadata: null,
|
|
631
|
+
session_total_turns: 0,
|
|
632
|
+
created_at: '2025-01-01T00:00:00Z',
|
|
633
|
+
updated_at: '2025-01-01T00:00:00Z'
|
|
634
|
+
}
|
|
635
|
+
|
|
636
|
+
const mockInsert = vi.fn().mockReturnValue({
|
|
637
|
+
select: vi.fn().mockReturnValue({
|
|
638
|
+
single: vi.fn().mockResolvedValue({ data: mockSessionData, error: null })
|
|
639
|
+
})
|
|
640
|
+
})
|
|
641
|
+
|
|
642
|
+
const mockUpdateEq = vi.fn().mockResolvedValue({ error: null })
|
|
643
|
+
const mockUpdate = vi.fn().mockReturnValue({
|
|
644
|
+
eq: vi.fn().mockReturnValue({
|
|
645
|
+
eq: mockUpdateEq
|
|
646
|
+
})
|
|
647
|
+
})
|
|
648
|
+
|
|
649
|
+
;(mockDb.from as ReturnType<typeof vi.fn>).mockImplementation((table: string) => {
|
|
650
|
+
if (table === 'sessions') {
|
|
651
|
+
return {
|
|
652
|
+
insert: mockInsert,
|
|
653
|
+
update: mockUpdate
|
|
654
|
+
}
|
|
655
|
+
}
|
|
656
|
+
return {}
|
|
657
|
+
})
|
|
658
|
+
|
|
659
|
+
const session = await sessionManager.create({
|
|
660
|
+
resourceId: 'agent-1',
|
|
661
|
+
organizationId: 'org-123',
|
|
662
|
+
userId: 'user-123'
|
|
663
|
+
})
|
|
664
|
+
|
|
665
|
+
await sessionManager.delete(session.sessionId, 'org-123')
|
|
666
|
+
|
|
667
|
+
expect(mockUpdate).toHaveBeenCalledWith(expect.objectContaining({ deleted_at: expect.any(String) }))
|
|
668
|
+
})
|
|
669
|
+
})
|
|
670
|
+
|
|
671
|
+
describe('Error Resilience', () => {
|
|
672
|
+
it('handles database connection errors on create', async () => {
|
|
673
|
+
const mockInsert = vi.fn().mockReturnValue({
|
|
674
|
+
select: vi.fn().mockReturnValue({
|
|
675
|
+
single: vi.fn().mockRejectedValue(new Error('Network error'))
|
|
676
|
+
})
|
|
677
|
+
})
|
|
678
|
+
|
|
679
|
+
;(mockDb.from as ReturnType<typeof vi.fn>).mockReturnValue({
|
|
680
|
+
insert: mockInsert
|
|
681
|
+
})
|
|
682
|
+
|
|
683
|
+
await expect(
|
|
684
|
+
sessionManager.create({
|
|
685
|
+
resourceId: 'agent-1',
|
|
686
|
+
organizationId: 'org-123'
|
|
687
|
+
})
|
|
688
|
+
).rejects.toThrow('Network error')
|
|
689
|
+
})
|
|
690
|
+
|
|
691
|
+
it('handles database timeout on get', async () => {
|
|
692
|
+
const mockIs = vi.fn().mockReturnValue({
|
|
693
|
+
single: vi.fn().mockRejectedValue(new Error('Query timeout'))
|
|
694
|
+
})
|
|
695
|
+
|
|
696
|
+
const mockSelect = vi.fn().mockReturnValue({
|
|
697
|
+
eq: vi.fn().mockReturnValue({
|
|
698
|
+
eq: vi.fn().mockReturnValue({
|
|
699
|
+
is: mockIs
|
|
700
|
+
})
|
|
701
|
+
})
|
|
702
|
+
})
|
|
703
|
+
|
|
704
|
+
;(mockDb.from as ReturnType<typeof vi.fn>).mockReturnValue({
|
|
705
|
+
select: mockSelect
|
|
706
|
+
})
|
|
707
|
+
|
|
708
|
+
await expect(sessionManager.get('session-123', 'org-123')).rejects.toThrow('Query timeout')
|
|
709
|
+
})
|
|
710
|
+
|
|
711
|
+
it('handles malformed database responses on create', async () => {
|
|
712
|
+
const mockInsert = vi.fn().mockReturnValue({
|
|
713
|
+
select: vi.fn().mockReturnValue({
|
|
714
|
+
single: vi.fn().mockResolvedValue({
|
|
715
|
+
data: null, // Missing data
|
|
716
|
+
error: null // But no error either (malformed response)
|
|
717
|
+
})
|
|
718
|
+
})
|
|
719
|
+
})
|
|
720
|
+
|
|
721
|
+
;(mockDb.from as ReturnType<typeof vi.fn>).mockReturnValue({
|
|
722
|
+
insert: mockInsert
|
|
723
|
+
})
|
|
724
|
+
|
|
725
|
+
// If implementation doesn't validate null data, this documents the behavior
|
|
726
|
+
// Either throws TypeError or successfully creates Session with null
|
|
727
|
+
try {
|
|
728
|
+
const result = await sessionManager.create({
|
|
729
|
+
resourceId: 'agent-1',
|
|
730
|
+
organizationId: 'org-123'
|
|
731
|
+
})
|
|
732
|
+
// If it succeeds, Session constructor handles null gracefully
|
|
733
|
+
expect(result).toBeDefined()
|
|
734
|
+
} catch (error) {
|
|
735
|
+
// Or it throws - both behaviors are acceptable for malformed responses
|
|
736
|
+
expect(error).toBeDefined()
|
|
737
|
+
}
|
|
738
|
+
})
|
|
739
|
+
|
|
740
|
+
it('handles empty organization ID gracefully', async () => {
|
|
741
|
+
const mockInsert = vi.fn().mockReturnValue({
|
|
742
|
+
select: vi.fn().mockReturnValue({
|
|
743
|
+
single: vi.fn().mockResolvedValue({
|
|
744
|
+
data: null,
|
|
745
|
+
error: { message: 'organization_id cannot be empty' }
|
|
746
|
+
})
|
|
747
|
+
})
|
|
748
|
+
})
|
|
749
|
+
|
|
750
|
+
;(mockDb.from as ReturnType<typeof vi.fn>).mockReturnValue({
|
|
751
|
+
insert: mockInsert
|
|
752
|
+
})
|
|
753
|
+
|
|
754
|
+
await expect(
|
|
755
|
+
sessionManager.create({
|
|
756
|
+
resourceId: 'agent-1',
|
|
757
|
+
organizationId: ''
|
|
758
|
+
})
|
|
759
|
+
).rejects.toThrow('organization_id cannot be empty')
|
|
760
|
+
})
|
|
761
|
+
|
|
762
|
+
it('handles null values in required fields', async () => {
|
|
763
|
+
const mockInsert = vi.fn().mockReturnValue({
|
|
764
|
+
select: vi.fn().mockReturnValue({
|
|
765
|
+
single: vi.fn().mockResolvedValue({
|
|
766
|
+
data: null,
|
|
767
|
+
error: { message: 'null value in column "resource_id" violates not-null constraint' }
|
|
768
|
+
})
|
|
769
|
+
})
|
|
770
|
+
})
|
|
771
|
+
|
|
772
|
+
;(mockDb.from as ReturnType<typeof vi.fn>).mockReturnValue({
|
|
773
|
+
insert: mockInsert
|
|
774
|
+
})
|
|
775
|
+
|
|
776
|
+
await expect(
|
|
777
|
+
sessionManager.create({
|
|
778
|
+
resourceId: null as unknown,
|
|
779
|
+
organizationId: 'org-123'
|
|
780
|
+
})
|
|
781
|
+
).rejects.toThrow('null value in column "resource_id"')
|
|
782
|
+
})
|
|
783
|
+
|
|
784
|
+
it('handles list with no results gracefully', async () => {
|
|
785
|
+
const mockLimit = vi.fn().mockResolvedValue({ data: [], error: null })
|
|
786
|
+
const mockOrder = vi.fn().mockReturnValue({ limit: mockLimit })
|
|
787
|
+
const mockIs = vi.fn().mockReturnValue({ order: mockOrder })
|
|
788
|
+
const mockEq = vi.fn().mockReturnValue({ is: mockIs })
|
|
789
|
+
const mockSelect = vi.fn().mockReturnValue({ eq: mockEq })
|
|
790
|
+
|
|
791
|
+
;(mockDb.from as ReturnType<typeof vi.fn>).mockReturnValue({
|
|
792
|
+
select: mockSelect
|
|
793
|
+
})
|
|
794
|
+
|
|
795
|
+
const sessions = await sessionManager.list({ organizationId: 'org-123' })
|
|
796
|
+
|
|
797
|
+
expect(sessions).toEqual([])
|
|
798
|
+
})
|
|
799
|
+
|
|
800
|
+
it('handles database returning null data for list', async () => {
|
|
801
|
+
const mockLimit = vi.fn().mockResolvedValue({ data: null, error: null })
|
|
802
|
+
const mockOrder = vi.fn().mockReturnValue({ limit: mockLimit })
|
|
803
|
+
const mockIs = vi.fn().mockReturnValue({ order: mockOrder })
|
|
804
|
+
const mockEq = vi.fn().mockReturnValue({ is: mockIs })
|
|
805
|
+
const mockSelect = vi.fn().mockReturnValue({ eq: mockEq })
|
|
806
|
+
|
|
807
|
+
;(mockDb.from as ReturnType<typeof vi.fn>).mockReturnValue({
|
|
808
|
+
select: mockSelect
|
|
809
|
+
})
|
|
810
|
+
|
|
811
|
+
// May throw TypeError when trying to map over null
|
|
812
|
+
try {
|
|
813
|
+
await sessionManager.list({ organizationId: 'org-123' })
|
|
814
|
+
// If it doesn't throw, fail the test
|
|
815
|
+
expect.fail('Expected an error to be thrown')
|
|
816
|
+
} catch (error) {
|
|
817
|
+
expect(error).toBeDefined()
|
|
818
|
+
}
|
|
819
|
+
})
|
|
820
|
+
})
|
|
821
|
+
})
|