@elevasis/core 0.13.0 → 0.14.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 +1 -1
- package/dist/index.js +9 -2
- package/dist/organization-model/index.d.ts +1 -1
- package/dist/organization-model/index.js +9 -2
- package/dist/test-utils/index.d.ts +463 -377
- package/dist/test-utils/index.js +9 -2
- package/package.json +1 -1
- package/src/_gen/__tests__/__snapshots__/contracts.md.snap +2324 -0
- package/src/business/acquisition/activity-events.test.ts +250 -0
- package/src/business/acquisition/activity-events.ts +7 -65
- package/src/business/acquisition/api-schemas.test.ts +1180 -0
- package/src/business/acquisition/api-schemas.ts +1075 -859
- package/src/business/acquisition/crm-state-actions.test.ts +160 -0
- package/src/business/acquisition/derive-actions.test.ts +518 -0
- package/src/business/acquisition/derive-actions.ts +103 -90
- package/src/business/acquisition/index.ts +149 -111
- package/src/business/acquisition/stateful.ts +30 -0
- package/src/business/acquisition/types.ts +44 -77
- package/src/execution/engine/index.ts +437 -434
- package/src/execution/engine/tools/integration/server/adapters/attio/__tests__/attio-crud.integration.test.ts +363 -360
- package/src/execution/engine/tools/integration/server/adapters/attio/fetch/get-record/index.test.ts +162 -186
- package/src/execution/engine/tools/integration/server/adapters/attio/fetch/list-records/index.test.ts +316 -338
- package/src/execution/engine/tools/integration/server/adapters/gmail/gmail-adapter.ts +204 -210
- package/src/execution/engine/tools/integration/server/adapters/resend/fetch/send-email/index.test.ts +88 -0
- package/src/execution/engine/tools/integration/server/adapters/resend/fetch/send-email/index.ts +141 -134
- package/src/execution/engine/tools/integration/server/adapters/resend/fetch/utils/types.ts +76 -75
- package/src/execution/engine/tools/integration/service.test.ts +34 -9
- package/src/execution/engine/tools/integration/service.ts +6 -3
- package/src/execution/engine/tools/lead-service-types.ts +945 -888
- package/src/execution/engine/tools/platform/acquisition/types.ts +266 -260
- package/src/execution/engine/tools/registry.ts +701 -699
- package/src/execution/engine/tools/tool-maps.ts +816 -791
- package/src/execution/engine/workflow/types.ts +11 -0
- package/src/organization-model/contracts.ts +4 -4
- package/src/organization-model/domains/navigation.ts +62 -62
- package/src/organization-model/domains/sales.ts +272 -0
- package/src/organization-model/published.ts +21 -21
- package/src/organization-model/resolve.ts +21 -8
- package/src/platform/constants/versions.ts +1 -1
- package/src/reference/_generated/contracts.md +2324 -0
- package/src/supabase/database.types.ts +2958 -2886
|
@@ -1,338 +1,316 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Tests for Attio listRecords fetch implementation
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
import { describe, it, expect, vi, beforeEach } from 'vitest'
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
)
|
|
57
|
-
|
|
58
|
-
expect(result.records).toHaveLength(2)
|
|
59
|
-
expect(result.records[0].id).toBe('record-1')
|
|
60
|
-
expect(result.records[1].id).toBe('record-2')
|
|
61
|
-
expect(result.hasMore).toBe(false) // Only 2 records, default limit is 500
|
|
62
|
-
|
|
63
|
-
expect(mockFetch).toHaveBeenCalledWith(
|
|
64
|
-
'https://api.attio.com/v2/objects/people/records/query',
|
|
65
|
-
expect.objectContaining({
|
|
66
|
-
method: 'POST',
|
|
67
|
-
headers: {
|
|
68
|
-
|
|
69
|
-
'Content-Type': 'application/json',
|
|
70
|
-
|
|
71
|
-
},
|
|
72
|
-
body: JSON.stringify({
|
|
73
|
-
limit: 500,
|
|
74
|
-
offset: 0
|
|
75
|
-
})
|
|
76
|
-
})
|
|
77
|
-
)
|
|
78
|
-
})
|
|
79
|
-
|
|
80
|
-
it('should support custom limit and offset', async () => {
|
|
81
|
-
const mockResponse = {
|
|
82
|
-
data: Array.from({ length: 50 }, (_, i) => ({
|
|
83
|
-
id: {
|
|
84
|
-
workspace_id: 'workspace-123',
|
|
85
|
-
object_id: 'object-456',
|
|
86
|
-
record_id: `record-${i + 50}`
|
|
87
|
-
},
|
|
88
|
-
created_at: '2024-01-01T12:00:00Z',
|
|
89
|
-
web_url: `https://app.attio.com/workspace/record-${i + 50}`,
|
|
90
|
-
values: {}
|
|
91
|
-
}))
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
mockFetch.mockResolvedValue({
|
|
95
|
-
ok: true,
|
|
96
|
-
json: async () => mockResponse
|
|
97
|
-
})
|
|
98
|
-
|
|
99
|
-
const result = await listRecords(
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
)
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
)
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
)
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
)
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
{
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
const
|
|
280
|
-
{
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
)
|
|
313
|
-
|
|
314
|
-
expect(
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
const mockResponse2 = {
|
|
318
|
-
data: Array.from({ length: 50 }, (_, i) => ({
|
|
319
|
-
id: { workspace_id: 'w', object_id: 'o', record_id: `r-${i}` },
|
|
320
|
-
created_at: '2024-01-01T12:00:00Z',
|
|
321
|
-
web_url: `https://app.attio.com/w/r-${i}`,
|
|
322
|
-
values: {}
|
|
323
|
-
}))
|
|
324
|
-
}
|
|
325
|
-
|
|
326
|
-
mockFetch.mockResolvedValueOnce({
|
|
327
|
-
ok: true,
|
|
328
|
-
json: async () => mockResponse2
|
|
329
|
-
})
|
|
330
|
-
|
|
331
|
-
const result2 = await listRecords(
|
|
332
|
-
{ apiKey: 'test-key' },
|
|
333
|
-
{ object: 'people', limit: 50 }
|
|
334
|
-
)
|
|
335
|
-
|
|
336
|
-
expect(result2.hasMore).toBe(true)
|
|
337
|
-
})
|
|
338
|
-
})
|
|
1
|
+
/**
|
|
2
|
+
* Tests for Attio listRecords fetch implementation
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { describe as _describe, it, expect, vi, beforeEach } from 'vitest'
|
|
6
|
+
|
|
7
|
+
// Attio integration not in use; suite skipped per project decision (2026-04-29).
|
|
8
|
+
const describe = _describe.skip
|
|
9
|
+
import { listRecords } from './index.js'
|
|
10
|
+
import { ToolingError } from '../../../../../../types.js'
|
|
11
|
+
|
|
12
|
+
// Mock fetch globally
|
|
13
|
+
const mockFetch = vi.fn()
|
|
14
|
+
global.fetch = mockFetch as typeof fetch
|
|
15
|
+
|
|
16
|
+
describe('listRecords', () => {
|
|
17
|
+
beforeEach(() => {
|
|
18
|
+
vi.clearAllMocks()
|
|
19
|
+
})
|
|
20
|
+
|
|
21
|
+
it('should list records successfully with default pagination', async () => {
|
|
22
|
+
const mockResponse = {
|
|
23
|
+
data: [
|
|
24
|
+
{
|
|
25
|
+
id: {
|
|
26
|
+
workspace_id: 'workspace-123',
|
|
27
|
+
object_id: 'object-456',
|
|
28
|
+
record_id: 'record-1'
|
|
29
|
+
},
|
|
30
|
+
created_at: '2024-01-01T12:00:00Z',
|
|
31
|
+
web_url: 'https://app.attio.com/workspace/record-1',
|
|
32
|
+
values: {
|
|
33
|
+
name: [{ first_name: 'John', last_name: 'Doe' }]
|
|
34
|
+
}
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
id: {
|
|
38
|
+
workspace_id: 'workspace-123',
|
|
39
|
+
object_id: 'object-456',
|
|
40
|
+
record_id: 'record-2'
|
|
41
|
+
},
|
|
42
|
+
created_at: '2024-01-02T12:00:00Z',
|
|
43
|
+
web_url: 'https://app.attio.com/workspace/record-2',
|
|
44
|
+
values: {
|
|
45
|
+
name: [{ first_name: 'Jane', last_name: 'Smith' }]
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
]
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
mockFetch.mockResolvedValue({
|
|
52
|
+
ok: true,
|
|
53
|
+
json: async () => mockResponse
|
|
54
|
+
})
|
|
55
|
+
|
|
56
|
+
const result = await listRecords({ apiKey: 'test-key' }, { object: 'people' })
|
|
57
|
+
|
|
58
|
+
expect(result.records).toHaveLength(2)
|
|
59
|
+
expect(result.records[0].id).toBe('record-1')
|
|
60
|
+
expect(result.records[1].id).toBe('record-2')
|
|
61
|
+
expect(result.hasMore).toBe(false) // Only 2 records, default limit is 500
|
|
62
|
+
|
|
63
|
+
expect(mockFetch).toHaveBeenCalledWith(
|
|
64
|
+
'https://api.attio.com/v2/objects/people/records/query',
|
|
65
|
+
expect.objectContaining({
|
|
66
|
+
method: 'POST',
|
|
67
|
+
headers: {
|
|
68
|
+
Authorization: 'Bearer test-key',
|
|
69
|
+
'Content-Type': 'application/json',
|
|
70
|
+
Accept: 'application/json'
|
|
71
|
+
},
|
|
72
|
+
body: JSON.stringify({
|
|
73
|
+
limit: 500,
|
|
74
|
+
offset: 0
|
|
75
|
+
})
|
|
76
|
+
})
|
|
77
|
+
)
|
|
78
|
+
})
|
|
79
|
+
|
|
80
|
+
it('should support custom limit and offset', async () => {
|
|
81
|
+
const mockResponse = {
|
|
82
|
+
data: Array.from({ length: 50 }, (_, i) => ({
|
|
83
|
+
id: {
|
|
84
|
+
workspace_id: 'workspace-123',
|
|
85
|
+
object_id: 'object-456',
|
|
86
|
+
record_id: `record-${i + 50}`
|
|
87
|
+
},
|
|
88
|
+
created_at: '2024-01-01T12:00:00Z',
|
|
89
|
+
web_url: `https://app.attio.com/workspace/record-${i + 50}`,
|
|
90
|
+
values: {}
|
|
91
|
+
}))
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
mockFetch.mockResolvedValue({
|
|
95
|
+
ok: true,
|
|
96
|
+
json: async () => mockResponse
|
|
97
|
+
})
|
|
98
|
+
|
|
99
|
+
const result = await listRecords({ apiKey: 'test-key' }, { object: 'people', limit: 50, offset: 50 })
|
|
100
|
+
|
|
101
|
+
expect(result.records).toHaveLength(50)
|
|
102
|
+
expect(result.hasMore).toBe(true) // Returned count equals limit
|
|
103
|
+
|
|
104
|
+
const fetchCall = mockFetch.mock.calls[0][1] as { body: string }
|
|
105
|
+
const calledBody = JSON.parse(fetchCall.body)
|
|
106
|
+
expect(calledBody.limit).toBe(50)
|
|
107
|
+
expect(calledBody.offset).toBe(50)
|
|
108
|
+
})
|
|
109
|
+
|
|
110
|
+
it('should support filter parameters', async () => {
|
|
111
|
+
const mockResponse = { data: [] }
|
|
112
|
+
|
|
113
|
+
mockFetch.mockResolvedValue({
|
|
114
|
+
ok: true,
|
|
115
|
+
json: async () => mockResponse
|
|
116
|
+
})
|
|
117
|
+
|
|
118
|
+
await listRecords(
|
|
119
|
+
{ apiKey: 'test-key' },
|
|
120
|
+
{
|
|
121
|
+
object: 'people',
|
|
122
|
+
filter: {
|
|
123
|
+
name: {
|
|
124
|
+
first_name: {
|
|
125
|
+
$eq: 'John'
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
)
|
|
131
|
+
|
|
132
|
+
const fetchCall = mockFetch.mock.calls[0][1] as { body: string }
|
|
133
|
+
const calledBody = JSON.parse(fetchCall.body)
|
|
134
|
+
expect(calledBody.filter).toEqual({
|
|
135
|
+
name: {
|
|
136
|
+
first_name: {
|
|
137
|
+
$eq: 'John'
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
})
|
|
141
|
+
})
|
|
142
|
+
|
|
143
|
+
it('should support sorts parameters', async () => {
|
|
144
|
+
const mockResponse = { data: [] }
|
|
145
|
+
|
|
146
|
+
mockFetch.mockResolvedValue({
|
|
147
|
+
ok: true,
|
|
148
|
+
json: async () => mockResponse
|
|
149
|
+
})
|
|
150
|
+
|
|
151
|
+
await listRecords(
|
|
152
|
+
{ apiKey: 'test-key' },
|
|
153
|
+
{
|
|
154
|
+
object: 'people',
|
|
155
|
+
sorts: [
|
|
156
|
+
{
|
|
157
|
+
attribute: 'name',
|
|
158
|
+
field: 'last_name',
|
|
159
|
+
direction: 'asc'
|
|
160
|
+
}
|
|
161
|
+
]
|
|
162
|
+
}
|
|
163
|
+
)
|
|
164
|
+
|
|
165
|
+
const fetchCall = mockFetch.mock.calls[0][1] as { body: string }
|
|
166
|
+
const calledBody = JSON.parse(fetchCall.body)
|
|
167
|
+
expect(calledBody.sorts).toEqual([
|
|
168
|
+
{
|
|
169
|
+
attribute: 'name',
|
|
170
|
+
field: 'last_name',
|
|
171
|
+
direction: 'asc'
|
|
172
|
+
}
|
|
173
|
+
])
|
|
174
|
+
})
|
|
175
|
+
|
|
176
|
+
it('should support complex filters with logical operators', async () => {
|
|
177
|
+
const mockResponse = { data: [] }
|
|
178
|
+
|
|
179
|
+
mockFetch.mockResolvedValue({
|
|
180
|
+
ok: true,
|
|
181
|
+
json: async () => mockResponse
|
|
182
|
+
})
|
|
183
|
+
|
|
184
|
+
await listRecords(
|
|
185
|
+
{ apiKey: 'test-key' },
|
|
186
|
+
{
|
|
187
|
+
object: 'deals',
|
|
188
|
+
filter: {
|
|
189
|
+
$and: [
|
|
190
|
+
{
|
|
191
|
+
value: {
|
|
192
|
+
value: {
|
|
193
|
+
$gte: 10000
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
},
|
|
197
|
+
{
|
|
198
|
+
status: {
|
|
199
|
+
$eq: 'open'
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
]
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
)
|
|
206
|
+
|
|
207
|
+
const fetchCall = mockFetch.mock.calls[0][1] as { body: string }
|
|
208
|
+
const calledBody = JSON.parse(fetchCall.body)
|
|
209
|
+
expect(calledBody.filter.$and).toHaveLength(2)
|
|
210
|
+
})
|
|
211
|
+
|
|
212
|
+
it('should throw credentials_invalid when API key is missing', async () => {
|
|
213
|
+
await expect(listRecords({ apiKey: '' }, { object: 'people' })).rejects.toThrow('Missing Attio API key')
|
|
214
|
+
})
|
|
215
|
+
|
|
216
|
+
it('should throw validation_error when object is missing', async () => {
|
|
217
|
+
await expect(listRecords({ apiKey: 'test-key' }, { object: '' })).rejects.toThrow(
|
|
218
|
+
'Missing required parameter: object'
|
|
219
|
+
)
|
|
220
|
+
})
|
|
221
|
+
|
|
222
|
+
it('should handle 401 authentication errors', async () => {
|
|
223
|
+
mockFetch.mockResolvedValue({
|
|
224
|
+
ok: false,
|
|
225
|
+
status: 401,
|
|
226
|
+
statusText: 'Unauthorized',
|
|
227
|
+
text: async () =>
|
|
228
|
+
JSON.stringify({
|
|
229
|
+
status_code: 401,
|
|
230
|
+
type: 'authentication_error',
|
|
231
|
+
code: 'unauthorized',
|
|
232
|
+
message: 'Invalid API key'
|
|
233
|
+
})
|
|
234
|
+
})
|
|
235
|
+
|
|
236
|
+
try {
|
|
237
|
+
await listRecords({ apiKey: 'invalid-key' }, { object: 'people' })
|
|
238
|
+
} catch (error) {
|
|
239
|
+
expect((error as ToolingError).errorType).toBe('credentials_invalid')
|
|
240
|
+
}
|
|
241
|
+
})
|
|
242
|
+
|
|
243
|
+
it('should work with different object types', async () => {
|
|
244
|
+
const mockResponse = {
|
|
245
|
+
data: [
|
|
246
|
+
{
|
|
247
|
+
id: {
|
|
248
|
+
workspace_id: 'workspace-123',
|
|
249
|
+
object_id: 'object-456',
|
|
250
|
+
record_id: 'company-1'
|
|
251
|
+
},
|
|
252
|
+
created_at: '2024-01-01T12:00:00Z',
|
|
253
|
+
web_url: 'https://app.attio.com/workspace/company-1',
|
|
254
|
+
values: {
|
|
255
|
+
name: 'Acme Corp'
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
]
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
mockFetch.mockResolvedValue({
|
|
262
|
+
ok: true,
|
|
263
|
+
json: async () => mockResponse
|
|
264
|
+
})
|
|
265
|
+
|
|
266
|
+
const result = await listRecords({ apiKey: 'test-key' }, { object: 'companies' })
|
|
267
|
+
|
|
268
|
+
expect(result.records).toHaveLength(1)
|
|
269
|
+
expect(result.records[0].values.name).toBe('Acme Corp')
|
|
270
|
+
|
|
271
|
+
expect(mockFetch).toHaveBeenCalledWith(
|
|
272
|
+
'https://api.attio.com/v2/objects/companies/records/query',
|
|
273
|
+
expect.anything()
|
|
274
|
+
)
|
|
275
|
+
})
|
|
276
|
+
|
|
277
|
+
it('should determine hasMore correctly based on limit', async () => {
|
|
278
|
+
// Case 1: Returned count < limit -> hasMore = false
|
|
279
|
+
const mockResponse1 = {
|
|
280
|
+
data: Array.from({ length: 25 }, (_, i) => ({
|
|
281
|
+
id: { workspace_id: 'w', object_id: 'o', record_id: `r-${i}` },
|
|
282
|
+
created_at: '2024-01-01T12:00:00Z',
|
|
283
|
+
web_url: `https://app.attio.com/w/r-${i}`,
|
|
284
|
+
values: {}
|
|
285
|
+
}))
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
mockFetch.mockResolvedValueOnce({
|
|
289
|
+
ok: true,
|
|
290
|
+
json: async () => mockResponse1
|
|
291
|
+
})
|
|
292
|
+
|
|
293
|
+
const result1 = await listRecords({ apiKey: 'test-key' }, { object: 'people', limit: 50 })
|
|
294
|
+
|
|
295
|
+
expect(result1.hasMore).toBe(false)
|
|
296
|
+
|
|
297
|
+
// Case 2: Returned count = limit -> hasMore = true
|
|
298
|
+
const mockResponse2 = {
|
|
299
|
+
data: Array.from({ length: 50 }, (_, i) => ({
|
|
300
|
+
id: { workspace_id: 'w', object_id: 'o', record_id: `r-${i}` },
|
|
301
|
+
created_at: '2024-01-01T12:00:00Z',
|
|
302
|
+
web_url: `https://app.attio.com/w/r-${i}`,
|
|
303
|
+
values: {}
|
|
304
|
+
}))
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
mockFetch.mockResolvedValueOnce({
|
|
308
|
+
ok: true,
|
|
309
|
+
json: async () => mockResponse2
|
|
310
|
+
})
|
|
311
|
+
|
|
312
|
+
const result2 = await listRecords({ apiKey: 'test-key' }, { object: 'people', limit: 50 })
|
|
313
|
+
|
|
314
|
+
expect(result2.hasMore).toBe(true)
|
|
315
|
+
})
|
|
316
|
+
})
|