@elevasis/core 0.23.0 → 0.24.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/dist/index.d.ts +1326 -552
- package/dist/index.js +869 -154
- package/dist/knowledge/index.d.ts +487 -209
- package/dist/knowledge/index.js +104 -1
- package/dist/organization-model/index.d.ts +1326 -552
- package/dist/organization-model/index.js +869 -154
- package/dist/test-utils/index.d.ts +357 -72
- package/dist/test-utils/index.js +795 -142
- package/package.json +5 -5
- package/src/README.md +14 -14
- package/src/__tests__/publish.test.ts +24 -24
- package/src/__tests__/template-core-compatibility.test.ts +9 -12
- package/src/_gen/__tests__/__snapshots__/contracts.md.snap +2102 -2096
- package/src/_gen/__tests__/scaffold-contracts.test.ts +30 -30
- package/src/auth/multi-tenancy/credentials/__tests__/encryption.test.ts +217 -217
- package/src/auth/multi-tenancy/credentials/server/encryption.ts +69 -69
- package/src/auth/multi-tenancy/credentials/server/kek-loader.ts +37 -37
- package/src/auth/multi-tenancy/index.ts +26 -26
- package/src/auth/multi-tenancy/invitations/api-schemas.ts +104 -104
- package/src/auth/multi-tenancy/memberships/api-schemas.ts +143 -143
- package/src/auth/multi-tenancy/memberships/index.ts +26 -26
- package/src/auth/multi-tenancy/memberships/membership.ts +130 -130
- package/src/auth/multi-tenancy/organizations/__tests__/api-schemas.test.ts +194 -194
- package/src/auth/multi-tenancy/organizations/api-schemas.ts +136 -136
- package/src/auth/multi-tenancy/permissions.test.ts +42 -42
- package/src/auth/multi-tenancy/permissions.ts +123 -123
- package/src/auth/multi-tenancy/role-management/api-schemas.ts +78 -78
- package/src/auth/multi-tenancy/role-management/index.ts +16 -16
- package/src/auth/multi-tenancy/theme-presets.ts +45 -45
- package/src/auth/multi-tenancy/types.ts +57 -57
- package/src/auth/multi-tenancy/users/api-schemas.ts +165 -165
- package/src/business/README.md +2 -2
- package/src/business/acquisition/activity-events.test.ts +250 -250
- package/src/business/acquisition/activity-events.ts +93 -93
- package/src/business/acquisition/api-schemas.test.ts +1883 -1843
- package/src/business/acquisition/api-schemas.ts +1492 -1497
- package/src/business/acquisition/build-templates.test.ts +240 -240
- package/src/business/acquisition/build-templates.ts +98 -98
- package/src/business/acquisition/crm-next-action.test.ts +262 -262
- package/src/business/acquisition/crm-next-action.ts +220 -220
- package/src/business/acquisition/crm-priority.test.ts +216 -216
- package/src/business/acquisition/crm-priority.ts +349 -349
- package/src/business/acquisition/crm-state-actions.test.ts +153 -153
- package/src/business/acquisition/deal-ownership.test.ts +351 -351
- package/src/business/acquisition/deal-ownership.ts +120 -120
- package/src/business/acquisition/derive-actions.test.ts +129 -104
- package/src/business/acquisition/derive-actions.ts +74 -84
- package/src/business/acquisition/index.ts +171 -170
- package/src/business/acquisition/ontology-validation.ts +309 -0
- package/src/business/acquisition/stateful.ts +30 -30
- package/src/business/acquisition/types.ts +396 -396
- package/src/business/clients/api-schemas.test.ts +115 -115
- package/src/business/clients/api-schemas.ts +158 -158
- package/src/business/clients/index.ts +1 -1
- package/src/business/crm/api-schemas.ts +40 -40
- package/src/business/crm/index.ts +1 -1
- package/src/business/deals/api-schemas.ts +87 -87
- package/src/business/deals/index.ts +1 -1
- package/src/business/index.ts +5 -5
- package/src/business/projects/types.ts +144 -144
- package/src/commands/queue/types/task.ts +15 -15
- package/src/execution/core/runner-types.ts +61 -61
- package/src/execution/core/sse-executions.ts +7 -7
- package/src/execution/engine/__tests__/fixtures/test-agents.ts +10 -10
- package/src/execution/engine/agent/core/__tests__/agent.test.ts +16 -16
- package/src/execution/engine/agent/core/__tests__/error-passthrough.test.ts +4 -4
- package/src/execution/engine/agent/core/types.ts +25 -25
- package/src/execution/engine/agent/index.ts +6 -6
- package/src/execution/engine/agent/reasoning/__tests__/request-builder.test.ts +24 -24
- package/src/execution/engine/index.ts +443 -443
- package/src/execution/engine/tools/integration/server/adapters/apify/__tests__/apify-run-actor.integration.test.ts +298 -298
- package/src/execution/engine/tools/integration/server/adapters/apify/apify-adapter.test.ts +55 -55
- package/src/execution/engine/tools/integration/server/adapters/apify/apify-adapter.ts +107 -107
- package/src/execution/engine/tools/integration/server/adapters/apollo/apollo-adapter.test.ts +48 -48
- package/src/execution/engine/tools/integration/server/adapters/apollo/apollo-adapter.ts +99 -99
- package/src/execution/engine/tools/integration/server/adapters/apollo/index.ts +1 -1
- package/src/execution/engine/tools/integration/server/adapters/attio/__tests__/attio-crud.integration.test.ts +363 -363
- package/src/execution/engine/tools/integration/server/adapters/attio/fetch/get-record/index.test.ts +162 -162
- package/src/execution/engine/tools/integration/server/adapters/attio/fetch/list-records/index.test.ts +316 -316
- package/src/execution/engine/tools/integration/server/adapters/clickup/clickup-adapter.test.ts +18 -18
- package/src/execution/engine/tools/integration/server/adapters/clickup/clickup-adapter.ts +194 -194
- package/src/execution/engine/tools/integration/server/adapters/clickup/index.ts +7 -7
- package/src/execution/engine/tools/integration/server/adapters/gmail/gmail-adapter.ts +204 -204
- package/src/execution/engine/tools/integration/server/adapters/gmail/gmail-tools.ts +105 -105
- package/src/execution/engine/tools/integration/server/adapters/google-calendar/google-calendar-adapter.ts +428 -428
- package/src/execution/engine/tools/integration/server/adapters/google-calendar/index.ts +2 -2
- 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/instantly/instantly-tools.ts +1474 -1474
- package/src/execution/engine/tools/integration/server/adapters/millionverifier/millionverifier-tools.ts +103 -103
- package/src/execution/engine/tools/integration/server/adapters/resend/fetch/send-email/index.test.ts +88 -88
- package/src/execution/engine/tools/integration/server/adapters/resend/fetch/send-email/index.ts +141 -141
- package/src/execution/engine/tools/integration/server/adapters/resend/fetch/utils/types.ts +76 -76
- package/src/execution/engine/tools/integration/server/adapters/signature-api/signature-api-tools.ts +182 -182
- package/src/execution/engine/tools/integration/server/adapters/stripe/stripe-tools.ts +310 -310
- package/src/execution/engine/tools/integration/service.test.ts +239 -239
- package/src/execution/engine/tools/integration/service.ts +172 -172
- package/src/execution/engine/tools/integration/tool.ts +255 -255
- package/src/execution/engine/tools/lead-service-types.ts +1005 -1005
- package/src/execution/engine/tools/messages.ts +43 -43
- package/src/execution/engine/tools/platform/acquisition/company-tools.ts +7 -7
- package/src/execution/engine/tools/platform/acquisition/contact-tools.ts +6 -6
- package/src/execution/engine/tools/platform/acquisition/list-tools.ts +6 -6
- package/src/execution/engine/tools/platform/acquisition/types.ts +280 -280
- package/src/execution/engine/tools/platform/email/types.ts +97 -97
- package/src/execution/engine/tools/registry.ts +704 -704
- package/src/execution/engine/tools/tool-maps.ts +831 -831
- package/src/execution/engine/tools/types.ts +234 -234
- package/src/execution/engine/workflow/types.ts +202 -202
- package/src/execution/external/__tests__/api-schemas.test.ts +127 -127
- package/src/execution/external/api-schemas.ts +40 -40
- package/src/execution/external/index.ts +1 -1
- package/src/index.ts +18 -18
- package/src/integrations/credentials/__tests__/api-schemas.test.ts +420 -420
- package/src/integrations/credentials/api-schemas.ts +146 -146
- package/src/integrations/credentials/schemas.ts +200 -200
- package/src/integrations/oauth/__tests__/provider-registry.test.ts +7 -7
- package/src/integrations/oauth/provider-registry.ts +74 -74
- package/src/integrations/oauth/server/credentials.ts +43 -43
- package/src/integrations/webhook-endpoints/__tests__/api-schemas.test.ts +327 -327
- package/src/integrations/webhook-endpoints/api-schemas.ts +103 -103
- package/src/integrations/webhook-endpoints/types.ts +58 -58
- package/src/knowledge/README.md +32 -32
- package/src/knowledge/__tests__/queries.test.ts +626 -535
- package/src/knowledge/format.ts +99 -99
- package/src/knowledge/index.ts +5 -5
- package/src/knowledge/published.ts +5 -5
- package/src/knowledge/queries.ts +269 -218
- package/src/operations/activities/api-schemas.ts +80 -80
- package/src/operations/activities/types.ts +64 -64
- package/src/organization-model/README.md +149 -149
- package/src/organization-model/__tests__/content-kinds-registry.test.ts +210 -210
- package/src/organization-model/__tests__/defaults.test.ts +168 -168
- package/src/organization-model/__tests__/domains/actions.test.ts +78 -56
- package/src/organization-model/__tests__/domains/customers.test.ts +299 -299
- package/src/organization-model/__tests__/domains/entities.test.ts +56 -56
- package/src/organization-model/__tests__/domains/goals.test.ts +493 -493
- package/src/organization-model/__tests__/domains/identity.test.ts +280 -280
- package/src/organization-model/__tests__/domains/navigation.test.ts +268 -268
- package/src/organization-model/__tests__/domains/offerings.test.ts +414 -414
- package/src/organization-model/__tests__/domains/policies.test.ts +323 -323
- package/src/organization-model/__tests__/domains/resource-mappings.test.ts +293 -293
- package/src/organization-model/__tests__/domains/resources.test.ts +382 -283
- package/src/organization-model/__tests__/domains/roles.test.ts +463 -463
- package/src/organization-model/__tests__/domains/statuses.test.ts +246 -246
- package/src/organization-model/__tests__/domains/systems.test.ts +209 -209
- package/src/organization-model/__tests__/flatten-additive-merge.test.ts +362 -361
- package/src/organization-model/__tests__/foundation.test.ts +77 -77
- package/src/organization-model/__tests__/get-resources-for-system.test.ts +144 -144
- package/src/organization-model/__tests__/graph.test.ts +1246 -887
- package/src/organization-model/__tests__/icons.test.ts +10 -1
- package/src/organization-model/__tests__/knowledge.test.ts +251 -15
- package/src/organization-model/__tests__/lookup-helpers.test.ts +438 -438
- package/src/organization-model/__tests__/migration-helpers.test.ts +591 -591
- package/src/organization-model/__tests__/prospecting-ssot.test.ts +103 -103
- package/src/organization-model/__tests__/recursive-system-schema.test.ts +535 -506
- package/src/organization-model/__tests__/resolve.test.ts +274 -164
- package/src/organization-model/__tests__/schema.test.ts +834 -301
- package/src/organization-model/__tests__/surface-projection.test.ts +284 -284
- package/src/organization-model/catalogs/lead-gen.ts +144 -144
- package/src/organization-model/content-kinds/config.ts +36 -36
- package/src/organization-model/content-kinds/index.ts +76 -72
- package/src/organization-model/content-kinds/pipeline.ts +68 -68
- package/src/organization-model/content-kinds/registry.ts +44 -44
- package/src/organization-model/content-kinds/status.ts +71 -71
- package/src/organization-model/content-kinds/template.ts +83 -83
- package/src/organization-model/content-kinds/types.ts +117 -117
- package/src/organization-model/contracts.ts +27 -27
- package/src/organization-model/defaults.ts +40 -50
- package/src/organization-model/domains/actions.ts +333 -239
- package/src/organization-model/domains/customers.ts +78 -78
- package/src/organization-model/domains/entities.ts +144 -144
- package/src/organization-model/domains/goals.ts +83 -83
- package/src/organization-model/domains/knowledge.ts +117 -101
- package/src/organization-model/domains/navigation.ts +139 -139
- package/src/organization-model/domains/offerings.ts +71 -71
- package/src/organization-model/domains/policies.ts +102 -102
- package/src/organization-model/domains/projects.ts +14 -14
- package/src/organization-model/domains/prospecting.ts +395 -395
- package/src/organization-model/domains/resources.ts +167 -132
- package/src/organization-model/domains/roles.ts +96 -96
- package/src/organization-model/domains/sales.test.ts +218 -218
- package/src/organization-model/domains/sales.ts +380 -380
- package/src/organization-model/domains/shared.ts +63 -63
- package/src/organization-model/domains/statuses.ts +339 -339
- package/src/organization-model/domains/systems.ts +217 -172
- package/src/organization-model/foundation.ts +75 -75
- package/src/organization-model/graph/build.ts +1016 -888
- package/src/organization-model/graph/index.ts +4 -4
- package/src/organization-model/graph/link.ts +10 -10
- package/src/organization-model/graph/schema.ts +76 -70
- package/src/organization-model/graph/types.ts +73 -67
- package/src/organization-model/helpers.ts +289 -241
- package/src/organization-model/icons.ts +78 -66
- package/src/organization-model/index.ts +130 -128
- package/src/organization-model/migration-helpers.ts +247 -244
- package/src/organization-model/ontology.ts +661 -0
- package/src/organization-model/organization-graph.mdx +110 -90
- package/src/organization-model/organization-model.mdx +226 -219
- package/src/organization-model/published.ts +289 -235
- package/src/organization-model/resolve.ts +146 -91
- package/src/organization-model/schema.ts +790 -671
- package/src/organization-model/surface-projection.ts +212 -212
- package/src/organization-model/types.ts +177 -167
- package/src/platform/api/types.ts +38 -38
- package/src/platform/constants/versions.ts +3 -3
- package/src/platform/index.ts +23 -23
- package/src/platform/registry/__tests__/command-view.test.ts +10 -10
- package/src/platform/registry/__tests__/resource-link.test.ts +35 -35
- package/src/platform/registry/__tests__/resource-registry.integration.test.ts +20 -20
- package/src/platform/registry/__tests__/resource-registry.nested-systems.test.ts +245 -245
- package/src/platform/registry/__tests__/resource-registry.test.ts +2053 -2053
- package/src/platform/registry/__tests__/validation.test.ts +1347 -1347
- package/src/platform/registry/command-view.ts +10 -10
- package/src/platform/registry/index.ts +103 -103
- package/src/platform/registry/resource-link.ts +32 -32
- package/src/platform/registry/resource-registry.ts +890 -890
- package/src/platform/registry/serialization.ts +295 -295
- package/src/platform/registry/serialized-types.ts +166 -166
- package/src/platform/registry/stats-types.ts +68 -68
- package/src/platform/registry/types.ts +425 -425
- package/src/platform/registry/validation.ts +745 -745
- package/src/platform/utils/__tests__/validation.test.ts +1084 -1084
- package/src/platform/utils/validation.ts +425 -425
- package/src/projects/api-schemas.test.ts +39 -39
- package/src/projects/api-schemas.ts +291 -291
- package/src/reference/_generated/contracts.md +2101 -2096
- package/src/reference/glossary.md +76 -76
- package/src/scaffold-registry/__tests__/index.test.ts +206 -206
- package/src/scaffold-registry/__tests__/schema.test.ts +166 -166
- package/src/scaffold-registry/index.ts +392 -392
- package/src/scaffold-registry/schema.ts +243 -243
- package/src/server.ts +289 -289
- package/src/supabase/database.types.ts +3153 -3153
- package/src/test-utils/README.md +37 -37
- package/src/test-utils/entities.ts +108 -108
- package/src/test-utils/fixtures/memberships.ts +82 -82
- package/src/test-utils/index.ts +12 -12
- package/src/test-utils/organization-model.ts +65 -65
- package/src/test-utils/published.ts +6 -6
- package/src/test-utils/rls/RLSTestContext.ts +588 -588
- package/src/test-utils/test-utils.test.ts +44 -44
|
@@ -1,239 +1,239 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* IntegrationService — credential cache regression tests
|
|
3
|
-
*
|
|
4
|
-
* Regression guard for the context.store plumbing bug fixed in commit 75205cd8e:
|
|
5
|
-
* IntegrationService.call() reads context.store.get(cacheKey) for per-execution
|
|
6
|
-
* credential caching. Before the fix, dispatchIntegrationTool never threaded `store`
|
|
7
|
-
* into the context, causing a V8 TypeError on every integration tool call.
|
|
8
|
-
*
|
|
9
|
-
* These tests exercise the real IntegrationService.call() code path — not a mock —
|
|
10
|
-
* so any future regression that removes context.store from the dispatch path will
|
|
11
|
-
* cause a real crash here, not a silent type error.
|
|
12
|
-
*/
|
|
13
|
-
|
|
14
|
-
import { describe, it, expect, vi, beforeEach } from 'vitest'
|
|
15
|
-
import { IntegrationService } from './service'
|
|
16
|
-
import type { BaseIntegrationAdapter } from './base-integration-adapter'
|
|
17
|
-
import type { ExecutionContext } from '../../base/types'
|
|
18
|
-
|
|
19
|
-
// ---------------------------------------------------------------------------
|
|
20
|
-
// Mock the registry that IntegrationService.call() uses internally
|
|
21
|
-
// ---------------------------------------------------------------------------
|
|
22
|
-
|
|
23
|
-
const mockGetCredential = vi.fn()
|
|
24
|
-
|
|
25
|
-
vi.mock('../registry', () => ({
|
|
26
|
-
getToolServices: () => ({
|
|
27
|
-
credentialsService: {
|
|
28
|
-
getCredential: mockGetCredential
|
|
29
|
-
}
|
|
30
|
-
})
|
|
31
|
-
}))
|
|
32
|
-
|
|
33
|
-
// ---------------------------------------------------------------------------
|
|
34
|
-
// Helpers
|
|
35
|
-
// ---------------------------------------------------------------------------
|
|
36
|
-
|
|
37
|
-
/** Minimal stub adapter — always validates, always succeeds */
|
|
38
|
-
function makeStubAdapter(name: string): BaseIntegrationAdapter {
|
|
39
|
-
return {
|
|
40
|
-
name,
|
|
41
|
-
validateCredentials: () => true,
|
|
42
|
-
call: vi.fn().mockResolvedValue({ stubResult: true })
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
/** Minimal ExecutionContext with a real Map for store */
|
|
47
|
-
function makeContext(store: Map<string, unknown> = new Map()): ExecutionContext {
|
|
48
|
-
return {
|
|
49
|
-
executionId: 'exec-test-001',
|
|
50
|
-
organizationId: 'org-test-001',
|
|
51
|
-
organizationName: 'Test Org',
|
|
52
|
-
resourceId: 'resource-test-001',
|
|
53
|
-
executionDepth: 0,
|
|
54
|
-
logger: {
|
|
55
|
-
debug: () => {},
|
|
56
|
-
info: () => {},
|
|
57
|
-
warn: () => {},
|
|
58
|
-
error: () => {}
|
|
59
|
-
},
|
|
60
|
-
store
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
// ---------------------------------------------------------------------------
|
|
65
|
-
// Tests
|
|
66
|
-
// ---------------------------------------------------------------------------
|
|
67
|
-
|
|
68
|
-
describe('IntegrationService — credential cache (context.store)', () => {
|
|
69
|
-
beforeEach(() => {
|
|
70
|
-
vi.clearAllMocks()
|
|
71
|
-
})
|
|
72
|
-
|
|
73
|
-
describe('call() with a valid store Map', () => {
|
|
74
|
-
it('does not throw when context.store is a real Map (regression: was crashing with undefined.get)', async () => {
|
|
75
|
-
const adapter = makeStubAdapter('stripe')
|
|
76
|
-
const service = new IntegrationService(new Map([['stripe', adapter]]))
|
|
77
|
-
const credentials = { apiKey: 'sk_test_123' }
|
|
78
|
-
|
|
79
|
-
mockGetCredential.mockResolvedValue(credentials)
|
|
80
|
-
|
|
81
|
-
const context = makeContext()
|
|
82
|
-
|
|
83
|
-
// This call must NOT throw "Cannot read properties of undefined (reading 'get')"
|
|
84
|
-
const result = await service.call('stripe', 'listPaymentLinks', {}, 'stripe-cred', context)
|
|
85
|
-
|
|
86
|
-
expect(result).toEqual({ stubResult: true })
|
|
87
|
-
})
|
|
88
|
-
|
|
89
|
-
it('fetches credentials from credentialsService on first call', async () => {
|
|
90
|
-
const adapter = makeStubAdapter('stripe')
|
|
91
|
-
const service = new IntegrationService(new Map([['stripe', adapter]]))
|
|
92
|
-
const credentials = { apiKey: 'sk_test_123' }
|
|
93
|
-
|
|
94
|
-
mockGetCredential.mockResolvedValue(credentials)
|
|
95
|
-
|
|
96
|
-
await service.call('stripe', 'listPaymentLinks', {}, 'stripe-cred', makeContext())
|
|
97
|
-
|
|
98
|
-
expect(mockGetCredential).toHaveBeenCalledOnce()
|
|
99
|
-
expect(mockGetCredential).toHaveBeenCalledWith('org-test-001', 'stripe-cred')
|
|
100
|
-
})
|
|
101
|
-
|
|
102
|
-
it('hits the cache and skips credentialsService on second call with the same store', async () => {
|
|
103
|
-
// This is the core regression: the second call should reuse the cached credential
|
|
104
|
-
// (store.get returns the value set by the first call) and NOT call credentialsService again.
|
|
105
|
-
const adapter = makeStubAdapter('stripe')
|
|
106
|
-
const service = new IntegrationService(new Map([['stripe', adapter]]))
|
|
107
|
-
const credentials = { apiKey: 'sk_test_123' }
|
|
108
|
-
|
|
109
|
-
mockGetCredential.mockResolvedValue(credentials)
|
|
110
|
-
|
|
111
|
-
// Use a SHARED store Map across both calls — as worker-executor does
|
|
112
|
-
const sharedStore = new Map<string, unknown>()
|
|
113
|
-
const ctx = makeContext(sharedStore)
|
|
114
|
-
|
|
115
|
-
await service.call('stripe', 'listPaymentLinks', {}, 'stripe-cred', ctx)
|
|
116
|
-
await service.call('stripe', 'listPaymentLinks', {}, 'stripe-cred', ctx)
|
|
117
|
-
|
|
118
|
-
// credentialsService should only have been called once — second call hit the cache
|
|
119
|
-
expect(mockGetCredential).toHaveBeenCalledOnce()
|
|
120
|
-
})
|
|
121
|
-
|
|
122
|
-
it('fetches credentials again when a fresh store is used (different execution)', async () => {
|
|
123
|
-
// Two distinct executions each own their own store Map.
|
|
124
|
-
// The credential cache must NOT leak across executions.
|
|
125
|
-
const adapter = makeStubAdapter('stripe')
|
|
126
|
-
const service = new IntegrationService(new Map([['stripe', adapter]]))
|
|
127
|
-
const credentials = { apiKey: 'sk_test_123' }
|
|
128
|
-
|
|
129
|
-
mockGetCredential.mockResolvedValue(credentials)
|
|
130
|
-
|
|
131
|
-
// Execution A — fresh store
|
|
132
|
-
await service.call('stripe', 'listPaymentLinks', {}, 'stripe-cred', makeContext(new Map()))
|
|
133
|
-
// Execution B — different fresh store
|
|
134
|
-
await service.call('stripe', 'listPaymentLinks', {}, 'stripe-cred', makeContext(new Map()))
|
|
135
|
-
|
|
136
|
-
// Each execution must independently fetch the credential
|
|
137
|
-
expect(mockGetCredential).toHaveBeenCalledTimes(2)
|
|
138
|
-
})
|
|
139
|
-
|
|
140
|
-
it('writes the credential to store.set after fetching so subsequent calls can retrieve it', async () => {
|
|
141
|
-
const adapter = makeStubAdapter('stripe')
|
|
142
|
-
const service = new IntegrationService(new Map([['stripe', adapter]]))
|
|
143
|
-
const credentials = { apiKey: 'sk_test_456' }
|
|
144
|
-
|
|
145
|
-
mockGetCredential.mockResolvedValue(credentials)
|
|
146
|
-
|
|
147
|
-
const store = new Map<string, unknown>()
|
|
148
|
-
await service.call('stripe', 'listPaymentLinks', {}, 'stripe-cred', makeContext(store))
|
|
149
|
-
|
|
150
|
-
// The credential must have been written into the store under the expected cache key
|
|
151
|
-
const cacheKey = '__cred_cache__:stripe-cred'
|
|
152
|
-
expect(store.has(cacheKey)).toBe(true)
|
|
153
|
-
expect(store.get(cacheKey)).toEqual(credentials)
|
|
154
|
-
})
|
|
155
|
-
})
|
|
156
|
-
|
|
157
|
-
describe('call() error conditions', () => {
|
|
158
|
-
it('throws with credentials_missing when credentialsService returns null', async () => {
|
|
159
|
-
const adapter = makeStubAdapter('stripe')
|
|
160
|
-
const service = new IntegrationService(new Map([['stripe', adapter]]))
|
|
161
|
-
|
|
162
|
-
mockGetCredential.mockResolvedValue(null)
|
|
163
|
-
|
|
164
|
-
await expect(service.call('stripe', 'listPaymentLinks', {}, 'missing-cred', makeContext())).rejects.toMatchObject(
|
|
165
|
-
{
|
|
166
|
-
errorType: 'credentials_missing'
|
|
167
|
-
}
|
|
168
|
-
)
|
|
169
|
-
})
|
|
170
|
-
|
|
171
|
-
it('throws with adapter_not_found when integration is not registered', async () => {
|
|
172
|
-
const service = new IntegrationService(new Map())
|
|
173
|
-
|
|
174
|
-
await expect(service.call('nonexistent', 'someMethod', {}, 'some-cred', makeContext())).rejects.toMatchObject({
|
|
175
|
-
errorType: 'adapter_not_found'
|
|
176
|
-
})
|
|
177
|
-
})
|
|
178
|
-
|
|
179
|
-
it('throws validation_error when context is undefined', async () => {
|
|
180
|
-
const adapter = makeStubAdapter('stripe')
|
|
181
|
-
const service = new IntegrationService(new Map([['stripe', adapter]]))
|
|
182
|
-
|
|
183
|
-
await expect(service.call('stripe', 'listPaymentLinks', {}, 'stripe-cred', undefined)).rejects.toMatchObject({
|
|
184
|
-
errorType: 'validation_error'
|
|
185
|
-
})
|
|
186
|
-
})
|
|
187
|
-
|
|
188
|
-
it('treats context.store as opportunistic — missing store falls through to credential fetch instead of crashing', async () => {
|
|
189
|
-
// 2026-04-25 regression guard: the original bug crashed with
|
|
190
|
-
// "Cannot read properties of undefined (reading 'get')"
|
|
191
|
-
// because context.store.get(cacheKey) had no optional chaining. After the
|
|
192
|
-
// cache-as-contract-leak fix, the cache is treated as a perf optimization,
|
|
193
|
-
// not a load-bearing contract. A missing store must therefore fall through
|
|
194
|
-
// to the credentialsService fetch path and succeed.
|
|
195
|
-
const adapter = makeStubAdapter('stripe')
|
|
196
|
-
const service = new IntegrationService(new Map([['stripe', adapter]]))
|
|
197
|
-
const credentials = { apiKey: 'sk_test_123' }
|
|
198
|
-
|
|
199
|
-
mockGetCredential.mockResolvedValue(credentials)
|
|
200
|
-
|
|
201
|
-
const contextWithoutStore = {
|
|
202
|
-
executionId: 'exec-broken',
|
|
203
|
-
organizationId: 'org-test-001',
|
|
204
|
-
organizationName: 'Test Org',
|
|
205
|
-
resourceId: 'resource-broken',
|
|
206
|
-
executionDepth: 0,
|
|
207
|
-
logger: { debug: () => {}, info: () => {}, warn: () => {}, error: () => {} }
|
|
208
|
-
// store is intentionally absent
|
|
209
|
-
} as unknown as ExecutionContext
|
|
210
|
-
|
|
211
|
-
const result = await service.call('stripe', 'listPaymentLinks', {}, 'stripe-cred', contextWithoutStore)
|
|
212
|
-
|
|
213
|
-
expect(result).toEqual({ stubResult: true })
|
|
214
|
-
// Credentials must have been fetched once (no cache hit, no cache write).
|
|
215
|
-
expect(mockGetCredential).toHaveBeenCalledOnce()
|
|
216
|
-
expect(mockGetCredential).toHaveBeenCalledWith('org-test-001', 'stripe-cred')
|
|
217
|
-
})
|
|
218
|
-
|
|
219
|
-
it('handles a null context.store the same way as undefined (opportunistic fallthrough)', async () => {
|
|
220
|
-
const adapter = makeStubAdapter('stripe')
|
|
221
|
-
const service = new IntegrationService(new Map([['stripe', adapter]]))
|
|
222
|
-
mockGetCredential.mockResolvedValue({ apiKey: 'sk_test_123' })
|
|
223
|
-
|
|
224
|
-
const contextNullStore = {
|
|
225
|
-
executionId: 'exec-null',
|
|
226
|
-
organizationId: 'org-test-001',
|
|
227
|
-
organizationName: 'Test Org',
|
|
228
|
-
resourceId: 'resource-null',
|
|
229
|
-
executionDepth: 0,
|
|
230
|
-
logger: { debug: () => {}, info: () => {}, warn: () => {}, error: () => {} },
|
|
231
|
-
store: null
|
|
232
|
-
} as unknown as ExecutionContext
|
|
233
|
-
|
|
234
|
-
await expect(service.call('stripe', 'listPaymentLinks', {}, 'stripe-cred', contextNullStore)).resolves.toEqual({
|
|
235
|
-
stubResult: true
|
|
236
|
-
})
|
|
237
|
-
})
|
|
238
|
-
})
|
|
239
|
-
})
|
|
1
|
+
/**
|
|
2
|
+
* IntegrationService — credential cache regression tests
|
|
3
|
+
*
|
|
4
|
+
* Regression guard for the context.store plumbing bug fixed in commit 75205cd8e:
|
|
5
|
+
* IntegrationService.call() reads context.store.get(cacheKey) for per-execution
|
|
6
|
+
* credential caching. Before the fix, dispatchIntegrationTool never threaded `store`
|
|
7
|
+
* into the context, causing a V8 TypeError on every integration tool call.
|
|
8
|
+
*
|
|
9
|
+
* These tests exercise the real IntegrationService.call() code path — not a mock —
|
|
10
|
+
* so any future regression that removes context.store from the dispatch path will
|
|
11
|
+
* cause a real crash here, not a silent type error.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import { describe, it, expect, vi, beforeEach } from 'vitest'
|
|
15
|
+
import { IntegrationService } from './service'
|
|
16
|
+
import type { BaseIntegrationAdapter } from './base-integration-adapter'
|
|
17
|
+
import type { ExecutionContext } from '../../base/types'
|
|
18
|
+
|
|
19
|
+
// ---------------------------------------------------------------------------
|
|
20
|
+
// Mock the registry that IntegrationService.call() uses internally
|
|
21
|
+
// ---------------------------------------------------------------------------
|
|
22
|
+
|
|
23
|
+
const mockGetCredential = vi.fn()
|
|
24
|
+
|
|
25
|
+
vi.mock('../registry', () => ({
|
|
26
|
+
getToolServices: () => ({
|
|
27
|
+
credentialsService: {
|
|
28
|
+
getCredential: mockGetCredential
|
|
29
|
+
}
|
|
30
|
+
})
|
|
31
|
+
}))
|
|
32
|
+
|
|
33
|
+
// ---------------------------------------------------------------------------
|
|
34
|
+
// Helpers
|
|
35
|
+
// ---------------------------------------------------------------------------
|
|
36
|
+
|
|
37
|
+
/** Minimal stub adapter — always validates, always succeeds */
|
|
38
|
+
function makeStubAdapter(name: string): BaseIntegrationAdapter {
|
|
39
|
+
return {
|
|
40
|
+
name,
|
|
41
|
+
validateCredentials: () => true,
|
|
42
|
+
call: vi.fn().mockResolvedValue({ stubResult: true })
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/** Minimal ExecutionContext with a real Map for store */
|
|
47
|
+
function makeContext(store: Map<string, unknown> = new Map()): ExecutionContext {
|
|
48
|
+
return {
|
|
49
|
+
executionId: 'exec-test-001',
|
|
50
|
+
organizationId: 'org-test-001',
|
|
51
|
+
organizationName: 'Test Org',
|
|
52
|
+
resourceId: 'resource-test-001',
|
|
53
|
+
executionDepth: 0,
|
|
54
|
+
logger: {
|
|
55
|
+
debug: () => {},
|
|
56
|
+
info: () => {},
|
|
57
|
+
warn: () => {},
|
|
58
|
+
error: () => {}
|
|
59
|
+
},
|
|
60
|
+
store
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// ---------------------------------------------------------------------------
|
|
65
|
+
// Tests
|
|
66
|
+
// ---------------------------------------------------------------------------
|
|
67
|
+
|
|
68
|
+
describe('IntegrationService — credential cache (context.store)', () => {
|
|
69
|
+
beforeEach(() => {
|
|
70
|
+
vi.clearAllMocks()
|
|
71
|
+
})
|
|
72
|
+
|
|
73
|
+
describe('call() with a valid store Map', () => {
|
|
74
|
+
it('does not throw when context.store is a real Map (regression: was crashing with undefined.get)', async () => {
|
|
75
|
+
const adapter = makeStubAdapter('stripe')
|
|
76
|
+
const service = new IntegrationService(new Map([['stripe', adapter]]))
|
|
77
|
+
const credentials = { apiKey: 'sk_test_123' }
|
|
78
|
+
|
|
79
|
+
mockGetCredential.mockResolvedValue(credentials)
|
|
80
|
+
|
|
81
|
+
const context = makeContext()
|
|
82
|
+
|
|
83
|
+
// This call must NOT throw "Cannot read properties of undefined (reading 'get')"
|
|
84
|
+
const result = await service.call('stripe', 'listPaymentLinks', {}, 'stripe-cred', context)
|
|
85
|
+
|
|
86
|
+
expect(result).toEqual({ stubResult: true })
|
|
87
|
+
})
|
|
88
|
+
|
|
89
|
+
it('fetches credentials from credentialsService on first call', async () => {
|
|
90
|
+
const adapter = makeStubAdapter('stripe')
|
|
91
|
+
const service = new IntegrationService(new Map([['stripe', adapter]]))
|
|
92
|
+
const credentials = { apiKey: 'sk_test_123' }
|
|
93
|
+
|
|
94
|
+
mockGetCredential.mockResolvedValue(credentials)
|
|
95
|
+
|
|
96
|
+
await service.call('stripe', 'listPaymentLinks', {}, 'stripe-cred', makeContext())
|
|
97
|
+
|
|
98
|
+
expect(mockGetCredential).toHaveBeenCalledOnce()
|
|
99
|
+
expect(mockGetCredential).toHaveBeenCalledWith('org-test-001', 'stripe-cred')
|
|
100
|
+
})
|
|
101
|
+
|
|
102
|
+
it('hits the cache and skips credentialsService on second call with the same store', async () => {
|
|
103
|
+
// This is the core regression: the second call should reuse the cached credential
|
|
104
|
+
// (store.get returns the value set by the first call) and NOT call credentialsService again.
|
|
105
|
+
const adapter = makeStubAdapter('stripe')
|
|
106
|
+
const service = new IntegrationService(new Map([['stripe', adapter]]))
|
|
107
|
+
const credentials = { apiKey: 'sk_test_123' }
|
|
108
|
+
|
|
109
|
+
mockGetCredential.mockResolvedValue(credentials)
|
|
110
|
+
|
|
111
|
+
// Use a SHARED store Map across both calls — as worker-executor does
|
|
112
|
+
const sharedStore = new Map<string, unknown>()
|
|
113
|
+
const ctx = makeContext(sharedStore)
|
|
114
|
+
|
|
115
|
+
await service.call('stripe', 'listPaymentLinks', {}, 'stripe-cred', ctx)
|
|
116
|
+
await service.call('stripe', 'listPaymentLinks', {}, 'stripe-cred', ctx)
|
|
117
|
+
|
|
118
|
+
// credentialsService should only have been called once — second call hit the cache
|
|
119
|
+
expect(mockGetCredential).toHaveBeenCalledOnce()
|
|
120
|
+
})
|
|
121
|
+
|
|
122
|
+
it('fetches credentials again when a fresh store is used (different execution)', async () => {
|
|
123
|
+
// Two distinct executions each own their own store Map.
|
|
124
|
+
// The credential cache must NOT leak across executions.
|
|
125
|
+
const adapter = makeStubAdapter('stripe')
|
|
126
|
+
const service = new IntegrationService(new Map([['stripe', adapter]]))
|
|
127
|
+
const credentials = { apiKey: 'sk_test_123' }
|
|
128
|
+
|
|
129
|
+
mockGetCredential.mockResolvedValue(credentials)
|
|
130
|
+
|
|
131
|
+
// Execution A — fresh store
|
|
132
|
+
await service.call('stripe', 'listPaymentLinks', {}, 'stripe-cred', makeContext(new Map()))
|
|
133
|
+
// Execution B — different fresh store
|
|
134
|
+
await service.call('stripe', 'listPaymentLinks', {}, 'stripe-cred', makeContext(new Map()))
|
|
135
|
+
|
|
136
|
+
// Each execution must independently fetch the credential
|
|
137
|
+
expect(mockGetCredential).toHaveBeenCalledTimes(2)
|
|
138
|
+
})
|
|
139
|
+
|
|
140
|
+
it('writes the credential to store.set after fetching so subsequent calls can retrieve it', async () => {
|
|
141
|
+
const adapter = makeStubAdapter('stripe')
|
|
142
|
+
const service = new IntegrationService(new Map([['stripe', adapter]]))
|
|
143
|
+
const credentials = { apiKey: 'sk_test_456' }
|
|
144
|
+
|
|
145
|
+
mockGetCredential.mockResolvedValue(credentials)
|
|
146
|
+
|
|
147
|
+
const store = new Map<string, unknown>()
|
|
148
|
+
await service.call('stripe', 'listPaymentLinks', {}, 'stripe-cred', makeContext(store))
|
|
149
|
+
|
|
150
|
+
// The credential must have been written into the store under the expected cache key
|
|
151
|
+
const cacheKey = '__cred_cache__:stripe-cred'
|
|
152
|
+
expect(store.has(cacheKey)).toBe(true)
|
|
153
|
+
expect(store.get(cacheKey)).toEqual(credentials)
|
|
154
|
+
})
|
|
155
|
+
})
|
|
156
|
+
|
|
157
|
+
describe('call() error conditions', () => {
|
|
158
|
+
it('throws with credentials_missing when credentialsService returns null', async () => {
|
|
159
|
+
const adapter = makeStubAdapter('stripe')
|
|
160
|
+
const service = new IntegrationService(new Map([['stripe', adapter]]))
|
|
161
|
+
|
|
162
|
+
mockGetCredential.mockResolvedValue(null)
|
|
163
|
+
|
|
164
|
+
await expect(service.call('stripe', 'listPaymentLinks', {}, 'missing-cred', makeContext())).rejects.toMatchObject(
|
|
165
|
+
{
|
|
166
|
+
errorType: 'credentials_missing'
|
|
167
|
+
}
|
|
168
|
+
)
|
|
169
|
+
})
|
|
170
|
+
|
|
171
|
+
it('throws with adapter_not_found when integration is not registered', async () => {
|
|
172
|
+
const service = new IntegrationService(new Map())
|
|
173
|
+
|
|
174
|
+
await expect(service.call('nonexistent', 'someMethod', {}, 'some-cred', makeContext())).rejects.toMatchObject({
|
|
175
|
+
errorType: 'adapter_not_found'
|
|
176
|
+
})
|
|
177
|
+
})
|
|
178
|
+
|
|
179
|
+
it('throws validation_error when context is undefined', async () => {
|
|
180
|
+
const adapter = makeStubAdapter('stripe')
|
|
181
|
+
const service = new IntegrationService(new Map([['stripe', adapter]]))
|
|
182
|
+
|
|
183
|
+
await expect(service.call('stripe', 'listPaymentLinks', {}, 'stripe-cred', undefined)).rejects.toMatchObject({
|
|
184
|
+
errorType: 'validation_error'
|
|
185
|
+
})
|
|
186
|
+
})
|
|
187
|
+
|
|
188
|
+
it('treats context.store as opportunistic — missing store falls through to credential fetch instead of crashing', async () => {
|
|
189
|
+
// 2026-04-25 regression guard: the original bug crashed with
|
|
190
|
+
// "Cannot read properties of undefined (reading 'get')"
|
|
191
|
+
// because context.store.get(cacheKey) had no optional chaining. After the
|
|
192
|
+
// cache-as-contract-leak fix, the cache is treated as a perf optimization,
|
|
193
|
+
// not a load-bearing contract. A missing store must therefore fall through
|
|
194
|
+
// to the credentialsService fetch path and succeed.
|
|
195
|
+
const adapter = makeStubAdapter('stripe')
|
|
196
|
+
const service = new IntegrationService(new Map([['stripe', adapter]]))
|
|
197
|
+
const credentials = { apiKey: 'sk_test_123' }
|
|
198
|
+
|
|
199
|
+
mockGetCredential.mockResolvedValue(credentials)
|
|
200
|
+
|
|
201
|
+
const contextWithoutStore = {
|
|
202
|
+
executionId: 'exec-broken',
|
|
203
|
+
organizationId: 'org-test-001',
|
|
204
|
+
organizationName: 'Test Org',
|
|
205
|
+
resourceId: 'resource-broken',
|
|
206
|
+
executionDepth: 0,
|
|
207
|
+
logger: { debug: () => {}, info: () => {}, warn: () => {}, error: () => {} }
|
|
208
|
+
// store is intentionally absent
|
|
209
|
+
} as unknown as ExecutionContext
|
|
210
|
+
|
|
211
|
+
const result = await service.call('stripe', 'listPaymentLinks', {}, 'stripe-cred', contextWithoutStore)
|
|
212
|
+
|
|
213
|
+
expect(result).toEqual({ stubResult: true })
|
|
214
|
+
// Credentials must have been fetched once (no cache hit, no cache write).
|
|
215
|
+
expect(mockGetCredential).toHaveBeenCalledOnce()
|
|
216
|
+
expect(mockGetCredential).toHaveBeenCalledWith('org-test-001', 'stripe-cred')
|
|
217
|
+
})
|
|
218
|
+
|
|
219
|
+
it('handles a null context.store the same way as undefined (opportunistic fallthrough)', async () => {
|
|
220
|
+
const adapter = makeStubAdapter('stripe')
|
|
221
|
+
const service = new IntegrationService(new Map([['stripe', adapter]]))
|
|
222
|
+
mockGetCredential.mockResolvedValue({ apiKey: 'sk_test_123' })
|
|
223
|
+
|
|
224
|
+
const contextNullStore = {
|
|
225
|
+
executionId: 'exec-null',
|
|
226
|
+
organizationId: 'org-test-001',
|
|
227
|
+
organizationName: 'Test Org',
|
|
228
|
+
resourceId: 'resource-null',
|
|
229
|
+
executionDepth: 0,
|
|
230
|
+
logger: { debug: () => {}, info: () => {}, warn: () => {}, error: () => {} },
|
|
231
|
+
store: null
|
|
232
|
+
} as unknown as ExecutionContext
|
|
233
|
+
|
|
234
|
+
await expect(service.call('stripe', 'listPaymentLinks', {}, 'stripe-cred', contextNullStore)).resolves.toEqual({
|
|
235
|
+
stubResult: true
|
|
236
|
+
})
|
|
237
|
+
})
|
|
238
|
+
})
|
|
239
|
+
})
|