@elevasis/core 0.11.2 → 0.13.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.
Files changed (50) hide show
  1. package/dist/index.d.ts +2 -1
  2. package/dist/index.js +8 -1
  3. package/dist/organization-model/index.d.ts +2 -1
  4. package/dist/organization-model/index.js +8 -1
  5. package/dist/test-utils/index.d.ts +27 -15
  6. package/dist/test-utils/index.js +25 -0
  7. package/package.json +1 -1
  8. package/src/_gen/__tests__/__snapshots__/contracts.md.snap +27 -270
  9. package/src/auth/multi-tenancy/credentials/__tests__/encryption.test.ts +217 -216
  10. package/src/auth/multi-tenancy/credentials/server/encryption.ts +69 -39
  11. package/src/auth/multi-tenancy/credentials/server/kek-loader.ts +37 -0
  12. package/src/auth/multi-tenancy/index.ts +3 -0
  13. package/src/auth/multi-tenancy/invitations/api-schemas.ts +104 -107
  14. package/src/auth/multi-tenancy/memberships/api-schemas.ts +6 -5
  15. package/src/auth/multi-tenancy/memberships/membership.ts +130 -138
  16. package/src/auth/multi-tenancy/permissions.ts +12 -5
  17. package/src/auth/multi-tenancy/role-management/api-schemas.ts +78 -0
  18. package/src/auth/multi-tenancy/role-management/index.ts +16 -0
  19. package/src/business/acquisition/activity-events.ts +142 -0
  20. package/src/business/acquisition/api-schemas.ts +694 -689
  21. package/src/business/acquisition/derive-actions.ts +90 -0
  22. package/src/business/acquisition/index.ts +111 -109
  23. package/src/execution/engine/index.ts +434 -434
  24. package/src/execution/engine/tools/integration/server/adapters/apify/__tests__/apify-run-actor.integration.test.ts +298 -293
  25. package/src/execution/engine/tools/integration/server/adapters/attio/__tests__/attio-crud.integration.test.ts +0 -1
  26. package/src/execution/engine/tools/integration/service.test.ts +214 -0
  27. package/src/execution/engine/tools/integration/service.ts +169 -161
  28. package/src/execution/engine/tools/lead-service-types.ts +882 -879
  29. package/src/execution/engine/tools/registry.ts +699 -700
  30. package/src/execution/engine/tools/tool-maps.ts +777 -780
  31. package/src/integrations/credentials/__tests__/api-schemas.test.ts +420 -496
  32. package/src/integrations/credentials/api-schemas.ts +127 -143
  33. package/src/integrations/webhook-endpoints/__tests__/api-schemas.test.ts +327 -318
  34. package/src/integrations/webhook-endpoints/api-schemas.ts +103 -102
  35. package/src/integrations/webhook-endpoints/types.ts +58 -51
  36. package/src/operations/activities/api-schemas.ts +80 -79
  37. package/src/operations/activities/types.ts +64 -63
  38. package/src/organization-model/contracts.ts +1 -0
  39. package/src/organization-model/defaults.ts +6 -0
  40. package/src/organization-model/domains/navigation.ts +37 -23
  41. package/src/organization-model/organization-graph.mdx +2 -2
  42. package/src/organization-model/published.ts +2 -1
  43. package/src/platform/constants/versions.ts +1 -1
  44. package/src/reference/_generated/contracts.md +27 -270
  45. package/src/scaffold-registry/__tests__/index.test.ts +72 -7
  46. package/src/scaffold-registry/index.ts +163 -29
  47. package/src/scaffold-registry/schema.ts +68 -62
  48. package/src/server.ts +281 -272
  49. package/src/supabase/database.types.ts +16 -10
  50. package/src/test-utils/rls/RLSTestContext.ts +585 -553
@@ -1,293 +1,298 @@
1
- import { describe, it, expect, beforeAll, vi } from 'vitest'
2
- import { ApifyAdapter } from '../apify-adapter'
3
- import type { ExecutionContext } from '../../../../../../base/types'
4
- import { createClient } from '@supabase/supabase-js'
5
- import type { Database } from '../../../../../../../../supabase/database.types'
6
- import { decryptCredentialValue } from '../../../../../../../../auth/multi-tenancy/credentials/server/service'
7
- import type { RunActorResult } from '../fetch/run-actor'
8
-
9
- /**
10
- * Apify Actor Integration Test Suite
11
- *
12
- * Tests the complete runActor flow:
13
- * 1. Start actor with input
14
- * 2. Poll for completion
15
- * 3. Fetch dataset results
16
- * 4. Handle various actor statuses
17
- *
18
- * Prerequisites:
19
- * - Supabase database with credentials table
20
- * - Credential 'elevasis-apify' with valid Apify API token
21
- * - Organization ID: f9aa5a56-8c13-4cd1-9161-8827ae7b452b
22
- * - SUPABASE_URL and SUPABASE_SERVICE_KEY env vars set
23
- * - SECRETS_ENCRYPTION_KEY env var set
24
- * - Apify account accessible with provided credentials
25
- *
26
- * Run: pnpm test apify-run-actor.integration.test.ts
27
- */
28
-
29
- const SKIP_TESTS = !process.env.SUPABASE_URL || !process.env.SUPABASE_SERVICE_KEY || !process.env.SECRETS_ENCRYPTION_KEY
30
-
31
- describe.skipIf(SKIP_TESTS)('Apify Run Actor Integration Tests', () => {
32
- const adapter = new ApifyAdapter()
33
- const organizationId = 'f9aa5a56-8c13-4cd1-9161-8827ae7b452b'
34
- const credentialName = 'elevasis-apify'
35
-
36
- const context: ExecutionContext = {
37
- organizationId,
38
- executionId: 'apify-integration-test',
39
- resourceId: 'apify-test-agent',
40
- resourceType: 'agent',
41
- logger: { info: vi.fn(), warn: vi.fn(), error: vi.fn(), debug: vi.fn(), child: vi.fn().mockReturnThis() }
42
- }
43
-
44
- let supabase: ReturnType<typeof createClient<Database>>
45
- let credentials: Record<string, unknown>
46
-
47
- beforeAll(async () => {
48
- console.log('\n=== Apify Actor Integration Test Suite ===')
49
- console.log(`Organization: ${organizationId}`)
50
- console.log(`Credential: ${credentialName}\n`)
51
-
52
- // Initialize Supabase
53
- supabase = createClient<Database>(process.env.SUPABASE_URL!, process.env.SUPABASE_SERVICE_KEY!)
54
-
55
- // Fetch credential from database
56
- const { data: credRow, error } = await supabase
57
- .from('credentials')
58
- .select('encrypted_value')
59
- .eq('organization_id', organizationId)
60
- .eq('name', credentialName)
61
- .single()
62
-
63
- if (error || !credRow) {
64
- throw new Error(`Credential '${credentialName}' not found for org ${organizationId}. Error: ${error?.message}`)
65
- }
66
-
67
- // Decrypt credentials
68
- credentials = decryptCredentialValue(credRow.encrypted_value)
69
-
70
- console.log('✓ Credentials loaded and decrypted')
71
- console.log('Credential fields:', Object.keys(credentials))
72
- console.log(
73
- 'Credential values (masked):',
74
- Object.keys(credentials).reduce(
75
- (acc, key) => {
76
- acc[key] = typeof credentials[key] === 'string' ? credentials[key].substring(0, 10) + '...' : credentials[key]
77
- return acc
78
- },
79
- {} as Record<string, unknown>
80
- )
81
- )
82
- const tokenPresent = !!(credentials.apiToken || credentials.apiKey || credentials.api_token || credentials.token)
83
- console.log(`✓ API Token present: ${tokenPresent}`)
84
-
85
- // Validate credentials
86
- const isValid = adapter.validateCredentials(credentials)
87
- if (!isValid) {
88
- throw new Error('Credential validation failed')
89
- }
90
- console.log('✓ Credentials validated')
91
- console.log('\n✓ Ready to run tests')
92
- })
93
-
94
- it('should run a simple actor and return results', { timeout: 60000 }, async () => {
95
- console.log('\n[Test] Run simple hello-world actor')
96
-
97
- // Using apify/hello-world - a simple actor that runs quickly
98
- const result = (await adapter.call(
99
- 'runActor',
100
- {
101
- actorId: 'apify/hello-world',
102
- input: {
103
- message: 'Integration test from Elevasis'
104
- },
105
- timeoutSecs: 60,
106
- pollIntervalSecs: 5
107
- },
108
- credentials,
109
- context
110
- )) as RunActorResult
111
-
112
- console.log(`✓ Actor completed with status: ${result.status}`)
113
- console.log(`✓ Run ID: ${result.runId}`)
114
- console.log(`✓ Dataset ID: ${result.datasetId}`)
115
- console.log(`✓ Items returned: ${result.totalCount}`)
116
- console.log(`✓ Execution time: ${result.executionTimeMs}ms`)
117
-
118
- expect(result).toBeDefined()
119
- expect(result.status).toBe('SUCCEEDED')
120
- expect(result.runId).toBeDefined()
121
- expect(result.datasetId).toBeDefined()
122
- expect(result.items).toBeInstanceOf(Array)
123
- expect(result.totalCount).toBeGreaterThanOrEqual(0)
124
- expect(result.executionTimeMs).toBeGreaterThan(0)
125
- })
126
-
127
- it('should handle actor with custom input', { timeout: 60000 }, async () => {
128
- console.log('\n[Test] Run actor with custom input')
129
-
130
- const result = (await adapter.call(
131
- 'runActor',
132
- {
133
- actorId: 'apify/hello-world',
134
- input: {
135
- message: 'Custom test message',
136
- outputDatasetItems: 5
137
- },
138
- timeoutSecs: 60,
139
- pollIntervalSecs: 5
140
- },
141
- credentials,
142
- context
143
- )) as RunActorResult
144
-
145
- console.log(`✓ Actor completed: ${result.status}`)
146
- console.log(`✓ Items in dataset: ${result.totalCount}`)
147
-
148
- expect(result).toBeDefined()
149
- expect(result.status).toBe('SUCCEEDED')
150
- expect(result.items).toBeInstanceOf(Array)
151
- })
152
-
153
- it('should handle maxItems parameter', { timeout: 60000 }, async () => {
154
- console.log('\n[Test] Run actor with maxItems limit')
155
-
156
- const maxItems = 3
157
-
158
- const result = (await adapter.call(
159
- 'runActor',
160
- {
161
- actorId: 'apify/hello-world',
162
- input: {
163
- outputDatasetItems: 10
164
- },
165
- maxItems: maxItems,
166
- timeoutSecs: 60,
167
- pollIntervalSecs: 5
168
- },
169
- credentials,
170
- context
171
- )) as RunActorResult
172
-
173
- console.log(`✓ Actor completed: ${result.status}`)
174
- console.log(`✓ Items returned (limited): ${result.totalCount}`)
175
-
176
- expect(result).toBeDefined()
177
- expect(result.status).toBe('SUCCEEDED')
178
- expect(result.items).toBeInstanceOf(Array)
179
- expect(result.items.length).toBeLessThanOrEqual(maxItems)
180
- })
181
-
182
- it('should handle timeout gracefully', { timeout: 15000 }, async () => {
183
- console.log('\n[Test] Handle actor timeout')
184
-
185
- // Use very short timeout to trigger TIMED_OUT status
186
- const result = (await adapter.call(
187
- 'runActor',
188
- {
189
- actorId: 'apify/hello-world',
190
- input: {},
191
- timeoutSecs: 1, // Very short timeout
192
- pollIntervalSecs: 1
193
- },
194
- credentials,
195
- context
196
- )) as RunActorResult
197
-
198
- console.log(`✓ Actor status: ${result.status}`)
199
- console.log(`✓ Timeout handled gracefully`)
200
-
201
- expect(result).toBeDefined()
202
- // Actor might succeed, timeout, or fail depending on external state
203
- expect(['SUCCEEDED', 'TIMED_OUT', 'FAILED']).toContain(result.status)
204
-
205
- if (result.status === 'TIMED_OUT') {
206
- expect(result.items).toEqual([])
207
- expect(result.totalCount).toBe(0)
208
- console.log(' TIMED_OUT status returns empty items as expected')
209
- }
210
- })
211
-
212
- it('should handle invalid actor ID gracefully', async () => {
213
- console.log('\n[Test] Handle invalid actor ID')
214
-
215
- await expect(
216
- adapter.call(
217
- 'runActor',
218
- {
219
- actorId: 'invalid/nonexistent-actor-xyz-123',
220
- input: {},
221
- timeoutSecs: 30,
222
- pollIntervalSecs: 5
223
- },
224
- credentials,
225
- context
226
- )
227
- ).rejects.toThrow()
228
-
229
- console.log('✓ Invalid actor ID rejected as expected')
230
- })
231
-
232
- it('should handle missing actorId parameter', async () => {
233
- console.log('\n[Test] Handle missing actorId')
234
-
235
- await expect(
236
- adapter.call(
237
- 'runActor',
238
- {
239
- // Missing actorId
240
- input: {},
241
- timeoutSecs: 30
242
- },
243
- credentials,
244
- context
245
- )
246
- ).rejects.toThrow('Missing required parameter: actorId')
247
-
248
- console.log('✓ Missing actorId parameter rejected as expected')
249
- })
250
-
251
- it('should handle invalid credentials gracefully', async () => {
252
- console.log('\n[Test] Handle invalid credentials')
253
-
254
- const invalidCreds = { apiToken: 'invalid-token-12345' }
255
-
256
- await expect(
257
- adapter.call(
258
- 'runActor',
259
- {
260
- actorId: 'apify/hello-world',
261
- input: {},
262
- timeoutSecs: 30
263
- },
264
- invalidCreds,
265
- context
266
- )
267
- ).rejects.toThrow()
268
-
269
- console.log('✓ Invalid credentials rejected as expected')
270
- })
271
-
272
- it('should validate credentials correctly', () => {
273
- console.log('\n[Test] Validate credentials')
274
-
275
- // Valid credentials
276
- const validCreds = { apiToken: 'test-token' }
277
- expect(adapter.validateCredentials(validCreds)).toBe(true)
278
-
279
- // Invalid credentials (missing apiToken)
280
- const invalidCreds1 = {}
281
- expect(adapter.validateCredentials(invalidCreds1)).toBe(false)
282
-
283
- // Invalid credentials (empty apiToken)
284
- const invalidCreds2 = { apiToken: '' }
285
- expect(adapter.validateCredentials(invalidCreds2)).toBe(false)
286
-
287
- // Invalid credentials (wrong type)
288
- const invalidCreds3 = { apiToken: 123 }
289
- expect(adapter.validateCredentials(invalidCreds3)).toBe(false)
290
-
291
- console.log('✓ Credential validation working correctly')
292
- })
293
- })
1
+ import { describe, it, expect, beforeAll, vi } from 'vitest'
2
+ import { ApifyAdapter } from '../apify-adapter'
3
+ import type { ExecutionContext } from '../../../../../../base/types'
4
+ import { createClient } from '@supabase/supabase-js'
5
+ import type { Database } from '../../../../../../../../supabase/database.types'
6
+ import { decryptCredentialValue } from '../../../../../../../../auth/multi-tenancy/credentials/server/service'
7
+ import { loadCredentialKEKs } from '../../../../../../../../auth/multi-tenancy/credentials/server/kek-loader'
8
+ import type { RunActorResult } from '../fetch/run-actor'
9
+
10
+ /**
11
+ * Apify Actor Integration Test Suite
12
+ *
13
+ * Tests the complete runActor flow:
14
+ * 1. Start actor with input
15
+ * 2. Poll for completion
16
+ * 3. Fetch dataset results
17
+ * 4. Handle various actor statuses
18
+ *
19
+ * Prerequisites:
20
+ * - Supabase database with credentials table
21
+ * - Credential 'elevasis-apify' with valid Apify API token
22
+ * - Organization ID: f9aa5a56-8c13-4cd1-9161-8827ae7b452b
23
+ * - SUPABASE_URL and SUPABASE_SERVICE_KEY env vars set
24
+ * - Apify account accessible with provided credentials
25
+ *
26
+ * Run: pnpm test apify-run-actor.integration.test.ts
27
+ */
28
+
29
+ const SKIP_TESTS = !process.env.SUPABASE_URL || !process.env.SUPABASE_SERVICE_KEY
30
+
31
+ describe.skipIf(SKIP_TESTS)('Apify Run Actor Integration Tests', () => {
32
+ const adapter = new ApifyAdapter()
33
+ const organizationId = 'f9aa5a56-8c13-4cd1-9161-8827ae7b452b'
34
+ const credentialName = 'elevasis-apify'
35
+
36
+ const context: ExecutionContext = {
37
+ organizationId,
38
+ executionId: 'apify-integration-test',
39
+ resourceId: 'apify-test-agent',
40
+ resourceType: 'agent',
41
+ logger: { info: vi.fn(), warn: vi.fn(), error: vi.fn(), debug: vi.fn(), child: vi.fn().mockReturnThis() }
42
+ }
43
+
44
+ let supabase: ReturnType<typeof createClient<Database>>
45
+ let credentials: Record<string, unknown>
46
+
47
+ beforeAll(async () => {
48
+ console.log('\n=== Apify Actor Integration Test Suite ===')
49
+ console.log(`Organization: ${organizationId}`)
50
+ console.log(`Credential: ${credentialName}\n`)
51
+
52
+ // Initialize Supabase
53
+ supabase = createClient<Database>(process.env.SUPABASE_URL!, process.env.SUPABASE_SERVICE_KEY!)
54
+
55
+ // Load Vault KEK so platform-v1 ciphertext (post-Wave-B4 re-encrypted rows)
56
+ // decrypts. Without this, the env-fallback would register the legacy key
57
+ // under platform-v1 and AES-GCM auth-tag verification would fail.
58
+ await loadCredentialKEKs()
59
+
60
+ // Fetch credential from database
61
+ const { data: credRow, error } = await supabase
62
+ .from('credentials')
63
+ .select('encrypted_value')
64
+ .eq('organization_id', organizationId)
65
+ .eq('name', credentialName)
66
+ .single()
67
+
68
+ if (error || !credRow) {
69
+ throw new Error(`Credential '${credentialName}' not found for org ${organizationId}. Error: ${error?.message}`)
70
+ }
71
+
72
+ // Decrypt credentials
73
+ credentials = decryptCredentialValue(credRow.encrypted_value)
74
+
75
+ console.log('✓ Credentials loaded and decrypted')
76
+ console.log('Credential fields:', Object.keys(credentials))
77
+ console.log(
78
+ 'Credential values (masked):',
79
+ Object.keys(credentials).reduce(
80
+ (acc, key) => {
81
+ acc[key] = typeof credentials[key] === 'string' ? credentials[key].substring(0, 10) + '...' : credentials[key]
82
+ return acc
83
+ },
84
+ {} as Record<string, unknown>
85
+ )
86
+ )
87
+ const tokenPresent = !!(credentials.apiToken || credentials.apiKey || credentials.api_token || credentials.token)
88
+ console.log(`✓ API Token present: ${tokenPresent}`)
89
+
90
+ // Validate credentials
91
+ const isValid = adapter.validateCredentials(credentials)
92
+ if (!isValid) {
93
+ throw new Error('Credential validation failed')
94
+ }
95
+ console.log(' Credentials validated')
96
+ console.log('\n✓ Ready to run tests')
97
+ })
98
+
99
+ it('should run a simple actor and return results', { timeout: 60000 }, async () => {
100
+ console.log('\n[Test] Run simple hello-world actor')
101
+
102
+ // Using apify/hello-world - a simple actor that runs quickly
103
+ const result = (await adapter.call(
104
+ 'runActor',
105
+ {
106
+ actorId: 'apify/hello-world',
107
+ input: {
108
+ message: 'Integration test from Elevasis'
109
+ },
110
+ timeoutSecs: 60,
111
+ pollIntervalSecs: 5
112
+ },
113
+ credentials,
114
+ context
115
+ )) as RunActorResult
116
+
117
+ console.log(`✓ Actor completed with status: ${result.status}`)
118
+ console.log(`✓ Run ID: ${result.runId}`)
119
+ console.log(`✓ Dataset ID: ${result.datasetId}`)
120
+ console.log(`✓ Items returned: ${result.totalCount}`)
121
+ console.log(`✓ Execution time: ${result.executionTimeMs}ms`)
122
+
123
+ expect(result).toBeDefined()
124
+ expect(result.status).toBe('SUCCEEDED')
125
+ expect(result.runId).toBeDefined()
126
+ expect(result.datasetId).toBeDefined()
127
+ expect(result.items).toBeInstanceOf(Array)
128
+ expect(result.totalCount).toBeGreaterThanOrEqual(0)
129
+ expect(result.executionTimeMs).toBeGreaterThan(0)
130
+ })
131
+
132
+ it('should handle actor with custom input', { timeout: 60000 }, async () => {
133
+ console.log('\n[Test] Run actor with custom input')
134
+
135
+ const result = (await adapter.call(
136
+ 'runActor',
137
+ {
138
+ actorId: 'apify/hello-world',
139
+ input: {
140
+ message: 'Custom test message',
141
+ outputDatasetItems: 5
142
+ },
143
+ timeoutSecs: 60,
144
+ pollIntervalSecs: 5
145
+ },
146
+ credentials,
147
+ context
148
+ )) as RunActorResult
149
+
150
+ console.log(`✓ Actor completed: ${result.status}`)
151
+ console.log(`✓ Items in dataset: ${result.totalCount}`)
152
+
153
+ expect(result).toBeDefined()
154
+ expect(result.status).toBe('SUCCEEDED')
155
+ expect(result.items).toBeInstanceOf(Array)
156
+ })
157
+
158
+ it('should handle maxItems parameter', { timeout: 60000 }, async () => {
159
+ console.log('\n[Test] Run actor with maxItems limit')
160
+
161
+ const maxItems = 3
162
+
163
+ const result = (await adapter.call(
164
+ 'runActor',
165
+ {
166
+ actorId: 'apify/hello-world',
167
+ input: {
168
+ outputDatasetItems: 10
169
+ },
170
+ maxItems: maxItems,
171
+ timeoutSecs: 60,
172
+ pollIntervalSecs: 5
173
+ },
174
+ credentials,
175
+ context
176
+ )) as RunActorResult
177
+
178
+ console.log(`✓ Actor completed: ${result.status}`)
179
+ console.log(`✓ Items returned (limited): ${result.totalCount}`)
180
+
181
+ expect(result).toBeDefined()
182
+ expect(result.status).toBe('SUCCEEDED')
183
+ expect(result.items).toBeInstanceOf(Array)
184
+ expect(result.items.length).toBeLessThanOrEqual(maxItems)
185
+ })
186
+
187
+ it('should handle timeout gracefully', { timeout: 15000 }, async () => {
188
+ console.log('\n[Test] Handle actor timeout')
189
+
190
+ // Use very short timeout to trigger TIMED_OUT status
191
+ const result = (await adapter.call(
192
+ 'runActor',
193
+ {
194
+ actorId: 'apify/hello-world',
195
+ input: {},
196
+ timeoutSecs: 1, // Very short timeout
197
+ pollIntervalSecs: 1
198
+ },
199
+ credentials,
200
+ context
201
+ )) as RunActorResult
202
+
203
+ console.log(`✓ Actor status: ${result.status}`)
204
+ console.log(`✓ Timeout handled gracefully`)
205
+
206
+ expect(result).toBeDefined()
207
+ // Actor might succeed, timeout, or fail depending on external state
208
+ expect(['SUCCEEDED', 'TIMED_OUT', 'FAILED']).toContain(result.status)
209
+
210
+ if (result.status === 'TIMED_OUT') {
211
+ expect(result.items).toEqual([])
212
+ expect(result.totalCount).toBe(0)
213
+ console.log(' TIMED_OUT status returns empty items as expected')
214
+ }
215
+ })
216
+
217
+ it('should handle invalid actor ID gracefully', async () => {
218
+ console.log('\n[Test] Handle invalid actor ID')
219
+
220
+ await expect(
221
+ adapter.call(
222
+ 'runActor',
223
+ {
224
+ actorId: 'invalid/nonexistent-actor-xyz-123',
225
+ input: {},
226
+ timeoutSecs: 30,
227
+ pollIntervalSecs: 5
228
+ },
229
+ credentials,
230
+ context
231
+ )
232
+ ).rejects.toThrow()
233
+
234
+ console.log('✓ Invalid actor ID rejected as expected')
235
+ })
236
+
237
+ it('should handle missing actorId parameter', async () => {
238
+ console.log('\n[Test] Handle missing actorId')
239
+
240
+ await expect(
241
+ adapter.call(
242
+ 'runActor',
243
+ {
244
+ // Missing actorId
245
+ input: {},
246
+ timeoutSecs: 30
247
+ },
248
+ credentials,
249
+ context
250
+ )
251
+ ).rejects.toThrow('Missing required parameter: actorId')
252
+
253
+ console.log('✓ Missing actorId parameter rejected as expected')
254
+ })
255
+
256
+ it('should handle invalid credentials gracefully', async () => {
257
+ console.log('\n[Test] Handle invalid credentials')
258
+
259
+ const invalidCreds = { apiToken: 'invalid-token-12345' }
260
+
261
+ await expect(
262
+ adapter.call(
263
+ 'runActor',
264
+ {
265
+ actorId: 'apify/hello-world',
266
+ input: {},
267
+ timeoutSecs: 30
268
+ },
269
+ invalidCreds,
270
+ context
271
+ )
272
+ ).rejects.toThrow()
273
+
274
+ console.log('✓ Invalid credentials rejected as expected')
275
+ })
276
+
277
+ it('should validate credentials correctly', () => {
278
+ console.log('\n[Test] Validate credentials')
279
+
280
+ // Valid credentials
281
+ const validCreds = { apiToken: 'test-token' }
282
+ expect(adapter.validateCredentials(validCreds)).toBe(true)
283
+
284
+ // Invalid credentials (missing apiToken)
285
+ const invalidCreds1 = {}
286
+ expect(adapter.validateCredentials(invalidCreds1)).toBe(false)
287
+
288
+ // Invalid credentials (empty apiToken)
289
+ const invalidCreds2 = { apiToken: '' }
290
+ expect(adapter.validateCredentials(invalidCreds2)).toBe(false)
291
+
292
+ // Invalid credentials (wrong type)
293
+ const invalidCreds3 = { apiToken: 123 }
294
+ expect(adapter.validateCredentials(invalidCreds3)).toBe(false)
295
+
296
+ console.log('✓ Credential validation working correctly')
297
+ })
298
+ })
@@ -28,7 +28,6 @@ import type {
28
28
  * - Credential 'elevasis-attio' with valid Attio API Key
29
29
  * - Organization ID: f9aa5a56-8c13-4cd1-9161-8827ae7b452b
30
30
  * - SUPABASE_URL and SUPABASE_SERVICE_KEY env vars set
31
- * - SECRETS_ENCRYPTION_KEY env var set
32
31
  * - Attio workspace accessible with provided credentials
33
32
  *
34
33
  * Run: pnpm test attio-crud.integration.test.ts