@apify/actors-mcp-server 0.14.4-beta.0 → 0.14.4-beta.2

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 (67) hide show
  1. package/dist/apify_client.d.ts.map +1 -1
  2. package/dist/apify_client.js +3 -2
  3. package/dist/apify_client.js.map +1 -1
  4. package/dist/index_internals_test_kit.d.ts +11 -0
  5. package/dist/index_internals_test_kit.d.ts.map +1 -0
  6. package/dist/index_internals_test_kit.js +15 -0
  7. package/dist/index_internals_test_kit.js.map +1 -0
  8. package/dist/mcp/legacy_server.d.ts.map +1 -1
  9. package/dist/mcp/legacy_server.js +1 -3
  10. package/dist/mcp/legacy_server.js.map +1 -1
  11. package/dist/mcp/stateless_server.js +2 -2
  12. package/dist/mcp/stateless_server.js.map +1 -1
  13. package/dist/server_card.js +1 -1
  14. package/dist/server_card.js.map +1 -1
  15. package/dist/test_kit/cases/actors.cases.d.ts +4 -0
  16. package/dist/test_kit/cases/actors.cases.d.ts.map +1 -0
  17. package/dist/test_kit/cases/actors.cases.js +911 -0
  18. package/dist/test_kit/cases/actors.cases.js.map +1 -0
  19. package/dist/test_kit/cases/apps.cases.d.ts +4 -0
  20. package/dist/test_kit/cases/apps.cases.d.ts.map +1 -0
  21. package/dist/test_kit/cases/apps.cases.js +111 -0
  22. package/dist/test_kit/cases/apps.cases.js.map +1 -0
  23. package/dist/test_kit/cases/payments.cases.d.ts +4 -0
  24. package/dist/test_kit/cases/payments.cases.d.ts.map +1 -0
  25. package/dist/test_kit/cases/payments.cases.js +207 -0
  26. package/dist/test_kit/cases/payments.cases.js.map +1 -0
  27. package/dist/test_kit/cases/registration.cases.d.ts +4 -0
  28. package/dist/test_kit/cases/registration.cases.d.ts.map +1 -0
  29. package/dist/test_kit/cases/registration.cases.js +431 -0
  30. package/dist/test_kit/cases/registration.cases.js.map +1 -0
  31. package/dist/test_kit/cases/storage.cases.d.ts +4 -0
  32. package/dist/test_kit/cases/storage.cases.d.ts.map +1 -0
  33. package/dist/test_kit/cases/storage.cases.js +378 -0
  34. package/dist/test_kit/cases/storage.cases.js.map +1 -0
  35. package/dist/test_kit/cases/tasks.cases.d.ts +4 -0
  36. package/dist/test_kit/cases/tasks.cases.d.ts.map +1 -0
  37. package/dist/test_kit/cases/tasks.cases.js +320 -0
  38. package/dist/test_kit/cases/tasks.cases.js.map +1 -0
  39. package/dist/test_kit/cases/tools.cases.d.ts +4 -0
  40. package/dist/test_kit/cases/tools.cases.d.ts.map +1 -0
  41. package/dist/test_kit/cases/tools.cases.js +204 -0
  42. package/dist/test_kit/cases/tools.cases.js.map +1 -0
  43. package/dist/test_kit/helpers.d.ts +69 -0
  44. package/dist/test_kit/helpers.d.ts.map +1 -0
  45. package/dist/test_kit/helpers.js +223 -0
  46. package/dist/test_kit/helpers.js.map +1 -0
  47. package/dist/test_kit/index.d.ts +20 -0
  48. package/dist/test_kit/index.d.ts.map +1 -0
  49. package/dist/test_kit/index.js +32 -0
  50. package/dist/test_kit/index.js.map +1 -0
  51. package/dist/test_kit/mcp_client.d.ts +31 -0
  52. package/dist/test_kit/mcp_client.d.ts.map +1 -0
  53. package/dist/test_kit/mcp_client.js +73 -0
  54. package/dist/test_kit/mcp_client.js.map +1 -0
  55. package/dist/test_kit/register.d.ts +9 -0
  56. package/dist/test_kit/register.d.ts.map +1 -0
  57. package/dist/test_kit/register.js +39 -0
  58. package/dist/test_kit/register.js.map +1 -0
  59. package/dist/test_kit/tsconfig.tsbuildinfo +1 -0
  60. package/dist/test_kit/types.d.ts +45 -0
  61. package/dist/test_kit/types.d.ts.map +1 -0
  62. package/dist/test_kit/types.js +2 -0
  63. package/dist/test_kit/types.js.map +1 -0
  64. package/dist/tsconfig.tsbuildinfo +1 -1
  65. package/dist/types.d.ts +1 -3
  66. package/dist/types.d.ts.map +1 -1
  67. package/package.json +18 -3
@@ -0,0 +1,911 @@
1
+ import { expect } from 'vitest';
2
+ import { actorNameToToolName, getCategoryTools } from '@apify/actors-mcp-server/internals.js';
3
+ import { HELPER_TOOLS, MAX_LIMIT_WITH_INPUT_SCHEMA } from '@apify/actors-mcp-server/internals/test-kit.js';
4
+ import { ACTOR_EXAMPLE_MCP_SERVER, ACTOR_NORMAL_MODE, AUTO_INJECTED_TOOL_NAMES, expectNormalModeTestStructuredContent, expectReadmeInStructuredContent, expectToolNamesToContain, expectUsageCostMeta, findToolByName, getToolNames, validateStructuredOutput, validateStructuredOutputForTool, withClient, } from '../helpers.js';
5
+ // call-actor calls Actors by name — this tool name must never appear in the tool list.
6
+ const NORMAL_MODE_TOOL_NAME = actorNameToToolName(ACTOR_NORMAL_MODE);
7
+ /** Actor tools: search, details, call-actor, get-actor-run. */
8
+ export const actorsCases = [
9
+ {
10
+ name: 'calls an Actor directly via call-actor without a separate add step',
11
+ isDeploymentTest: false,
12
+ run: withClient({ tools: ['call-actor'] }, async (client) => {
13
+ const names = getToolNames(await client.listTools());
14
+ expect(names).toHaveLength(1 + AUTO_INJECTED_TOOL_NAMES.length);
15
+ expect(names).toContain(HELPER_TOOLS.ACTOR_CALL);
16
+ expectToolNamesToContain(names, AUTO_INJECTED_TOOL_NAMES);
17
+ expect(names).not.toContain(NORMAL_MODE_TOOL_NAME);
18
+ // No dynamic "add" step exists — call-actor calls any Actor by name directly.
19
+ const result = await client.callTool({
20
+ name: HELPER_TOOLS.ACTOR_CALL,
21
+ arguments: { actor: ACTOR_NORMAL_MODE, input: { firstNumber: 1, secondNumber: 2 } },
22
+ });
23
+ expectNormalModeTestStructuredContent(result);
24
+ }),
25
+ },
26
+ {
27
+ name: 'should call Actor dynamically via generic call-actor tool without need to add it first',
28
+ isDeploymentTest: false,
29
+ run: withClient({ tools: ['actors'] }, async (client) => {
30
+ var _a;
31
+ const names = getToolNames(await client.listTools());
32
+ // actors category (already has call-actor) + auto-injected helpers.
33
+ const numberOfTools = getCategoryTools('default').actors.length + AUTO_INJECTED_TOOL_NAMES.length;
34
+ expect(names).toHaveLength(numberOfTools);
35
+ // get-actor-run should be automatically included when call-actor is present
36
+ expect(names).toContain(HELPER_TOOLS.ACTOR_RUNS_GET);
37
+ expectToolNamesToContain(names, AUTO_INJECTED_TOOL_NAMES);
38
+ // Check that the Actor is not in the tools list
39
+ expect(names).not.toContain(NORMAL_MODE_TOOL_NAME);
40
+ const result = await client.callTool({
41
+ name: HELPER_TOOLS.ACTOR_CALL,
42
+ arguments: { actor: ACTOR_NORMAL_MODE, input: { firstNumber: 1, secondNumber: 2 } },
43
+ });
44
+ const content = result.content;
45
+ // content[0] mirrors structuredContent as JSON; content[1] is "${summary}\n${nextStep}".
46
+ expect((_a = content[0]) === null || _a === void 0 ? void 0 : _a.type).toBe('text');
47
+ const mirrored = JSON.parse(content[0].text);
48
+ expect(mirrored.runId).toBeDefined();
49
+ expect(mirrored.status).toBe('SUCCEEDED');
50
+ // Validate structured output has run-response metadata for the normal-mode-test-actor.
51
+ expectNormalModeTestStructuredContent(result);
52
+ }),
53
+ },
54
+ {
55
+ // isDeploymentTest: full-pipeline smoke (auth + real Actor run).
56
+ name: 'should call Actor directly with required input',
57
+ isDeploymentTest: true,
58
+ run: withClient({ tools: ['actors'] }, async (client) => {
59
+ // Should fail without input (AJV validation error)
60
+ await expect(client.callTool({ name: HELPER_TOOLS.ACTOR_CALL, arguments: { actor: ACTOR_NORMAL_MODE } })).rejects.toThrow(/must have required property 'input'/);
61
+ // Should succeed with input
62
+ const callResult = await client.callTool({
63
+ name: HELPER_TOOLS.ACTOR_CALL,
64
+ arguments: { actor: ACTOR_NORMAL_MODE, input: { firstNumber: 1, secondNumber: 2 } },
65
+ });
66
+ expect(callResult.content).toBeDefined();
67
+ }),
68
+ },
69
+ {
70
+ name: 'returns terminal RunResponse with usage cost meta when the run completes within waitSecs',
71
+ isDeploymentTest: false,
72
+ run: withClient({ tools: ['actors'] }, async (client) => {
73
+ const callResult = await client.callTool({
74
+ name: HELPER_TOOLS.ACTOR_CALL,
75
+ // Max wait (45s) so the test does not flake on a slow run.
76
+ arguments: { actor: ACTOR_NORMAL_MODE, input: { firstNumber: 1, secondNumber: 2 }, waitSecs: 45 },
77
+ });
78
+ validateStructuredOutputForTool(callResult, HELPER_TOOLS.ACTOR_CALL, 'default');
79
+ expectNormalModeTestStructuredContent(callResult);
80
+ const sc = callResult.structuredContent;
81
+ expect(sc === null || sc === void 0 ? void 0 : sc.status).toBe('SUCCEEDED');
82
+ expect(sc === null || sc === void 0 ? void 0 : sc.summary).toMatch(/SUCCEEDED/);
83
+ expectUsageCostMeta(callResult);
84
+ }),
85
+ },
86
+ {
87
+ name: 'returns immediately with a non-terminal RunResponse when waitSecs=0',
88
+ isDeploymentTest: false,
89
+ run: withClient({ tools: ['actors'] }, async (client) => {
90
+ const callResult = await client.callTool({
91
+ name: HELPER_TOOLS.ACTOR_CALL,
92
+ arguments: { actor: ACTOR_NORMAL_MODE, input: { firstNumber: 1, secondNumber: 2 }, waitSecs: 0 },
93
+ });
94
+ validateStructuredOutputForTool(callResult, HELPER_TOOLS.ACTOR_CALL, 'default');
95
+ const sc = callResult.structuredContent;
96
+ expect(sc === null || sc === void 0 ? void 0 : sc.runId).toBeDefined();
97
+ // Non-blocking: status is typically READY or RUNNING at this point (terminal also tolerated for very fast actors).
98
+ expect(['READY', 'RUNNING', 'SUCCEEDED']).toContain(sc === null || sc === void 0 ? void 0 : sc.status);
99
+ }),
100
+ },
101
+ {
102
+ name: 'accepts but ignores the deprecated previewOutput field',
103
+ isDeploymentTest: false,
104
+ run: withClient({ tools: ['actors'] }, async (client) => {
105
+ const callResult = await client.callTool({
106
+ name: HELPER_TOOLS.ACTOR_CALL,
107
+ arguments: {
108
+ actor: ACTOR_NORMAL_MODE,
109
+ input: { firstNumber: 1, secondNumber: 2 },
110
+ previewOutput: false,
111
+ waitSecs: 45,
112
+ },
113
+ });
114
+ // previewOutput is ignored; still canonical RunResponse.
115
+ validateStructuredOutputForTool(callResult, HELPER_TOOLS.ACTOR_CALL, 'default');
116
+ expectNormalModeTestStructuredContent(callResult);
117
+ }),
118
+ },
119
+ {
120
+ name: 'accepts callOptions.maxItems on call-actor and runs successfully',
121
+ isDeploymentTest: false,
122
+ run: withClient({ tools: ['actors'] }, async (client) => {
123
+ var _a, _b, _c;
124
+ const callResult = await client.callTool({
125
+ name: HELPER_TOOLS.ACTOR_CALL,
126
+ arguments: {
127
+ actor: ACTOR_NORMAL_MODE,
128
+ input: { firstNumber: 1, secondNumber: 2 },
129
+ callOptions: { maxItems: 1 },
130
+ waitSecs: 45,
131
+ },
132
+ });
133
+ expect(callResult.isError).not.toBe(true);
134
+ const sc = callResult.structuredContent;
135
+ expect(sc === null || sc === void 0 ? void 0 : sc.status).toBe('SUCCEEDED');
136
+ expect((_c = (_b = (_a = sc === null || sc === void 0 ? void 0 : sc.storages) === null || _a === void 0 ? void 0 : _a.datasets) === null || _b === void 0 ? void 0 : _b.default) === null || _c === void 0 ? void 0 : _c.id).toBeDefined();
137
+ }),
138
+ },
139
+ {
140
+ name: 'surfaces dataset fields in the canonical response (no inline preview)',
141
+ isDeploymentTest: false,
142
+ run: withClient({ tools: ['actors'] }, async (client) => {
143
+ var _a, _b, _c, _d;
144
+ const callResult = await client.callTool({
145
+ name: HELPER_TOOLS.ACTOR_CALL,
146
+ arguments: { actor: ACTOR_NORMAL_MODE, input: { firstNumber: 1, secondNumber: 2 }, waitSecs: 45 },
147
+ });
148
+ // No inlined items — agents use dataset id + fields via get-dataset-items.
149
+ validateStructuredOutputForTool(callResult, HELPER_TOOLS.ACTOR_CALL, 'default');
150
+ expectNormalModeTestStructuredContent(callResult);
151
+ const sc = callResult.structuredContent;
152
+ // nextStep should interpolate the datasetId so a text-only client can act without parsing storages.
153
+ expect(sc === null || sc === void 0 ? void 0 : sc.nextStep).toContain((_d = (_c = (_b = (_a = sc === null || sc === void 0 ? void 0 : sc.storages) === null || _a === void 0 ? void 0 : _a.datasets) === null || _b === void 0 ? void 0 : _b.default) === null || _c === void 0 ? void 0 : _c.id) !== null && _d !== void 0 ? _d : '__unset__');
154
+ }),
155
+ },
156
+ {
157
+ name: 'surfaces aliased storages from run.storageIds in the canonical response',
158
+ isDeploymentTest: false,
159
+ run: withClient({ tools: ['actors'] }, async (client) => {
160
+ var _a, _b, _c, _d, _e, _f;
161
+ const callResult = await client.callTool({
162
+ name: HELPER_TOOLS.ACTOR_CALL,
163
+ arguments: { actor: ACTOR_NORMAL_MODE, input: { firstNumber: 1, secondNumber: 2 }, waitSecs: 45 },
164
+ });
165
+ expect(callResult.isError).not.toBe(true);
166
+ // Schema validation must accept the alias entries (additionalProperties: full dataset shape).
167
+ validateStructuredOutputForTool(callResult, HELPER_TOOLS.ACTOR_CALL, 'default');
168
+ const sc = callResult.structuredContent;
169
+ // Aliased `books` dataset must appear beside default.
170
+ expect((_c = (_b = (_a = sc === null || sc === void 0 ? void 0 : sc.storages) === null || _a === void 0 ? void 0 : _a.datasets) === null || _b === void 0 ? void 0 : _b.default) === null || _c === void 0 ? void 0 : _c.id).toBeDefined();
171
+ expect((_f = (_e = (_d = sc === null || sc === void 0 ? void 0 : sc.storages) === null || _d === void 0 ? void 0 : _d.datasets) === null || _e === void 0 ? void 0 : _e.books) === null || _f === void 0 ? void 0 : _f.id).toEqual(expect.any(String));
172
+ }),
173
+ },
174
+ {
175
+ // isDeploymentTest: store-search discovery path.
176
+ name: 'should find Actors in store search',
177
+ isDeploymentTest: true,
178
+ run: withClient(undefined, async (client) => {
179
+ const result = await client.callTool({
180
+ name: HELPER_TOOLS.STORE_SEARCH,
181
+ arguments: { keywords: 'normal-mode-test-actor', limit: 5 },
182
+ });
183
+ const content = result.content;
184
+ expect(content.some((item) => item.text.includes(ACTOR_NORMAL_MODE))).toBe(true);
185
+ }),
186
+ },
187
+ {
188
+ name: 'should not return rental Actors from store search',
189
+ isDeploymentTest: false,
190
+ run: withClient(undefined, async (client) => {
191
+ const result = await client.callTool({
192
+ name: HELPER_TOOLS.STORE_SEARCH,
193
+ arguments: { keywords: 'rental', limit: MAX_LIMIT_WITH_INPUT_SCHEMA },
194
+ });
195
+ const content = result.content;
196
+ expect(content.length).toBe(1);
197
+ const outputText = content[0].text;
198
+ expect(outputText).toContain('This Actor');
199
+ expect(outputText).not.toContain('This Actor is rental');
200
+ }),
201
+ },
202
+ {
203
+ name: 'should return an Actor-not-found error when calling a non-existent actor via call-actor',
204
+ isDeploymentTest: false,
205
+ run: withClient({ tools: ['actors'] }, async (client) => {
206
+ const nonExistentActor = 'apify/this-actor-does-not-exist';
207
+ const result = await client.callTool({
208
+ name: HELPER_TOOLS.ACTOR_CALL,
209
+ arguments: { actor: nonExistentActor, input: {} },
210
+ });
211
+ expect(result).toBeDefined();
212
+ expect(result.isError).toBe(true);
213
+ const content = result.content;
214
+ expect(content.length).toBeGreaterThan(0);
215
+ expect(content[0].text).toContain(nonExistentActor);
216
+ expect(content[0].text).toContain('was not found');
217
+ }),
218
+ },
219
+ {
220
+ name: 'should return structured output for fetch-actor-details matching outputSchema',
221
+ isDeploymentTest: false,
222
+ run: withClient({ tools: ['actors'] }, async (client) => {
223
+ const toolName = HELPER_TOOLS.ACTOR_GET_DETAILS;
224
+ const result = await client.callTool({ name: toolName, arguments: { actor: ACTOR_NORMAL_MODE } });
225
+ const content = result.content;
226
+ expect(content.length).toBeGreaterThan(0);
227
+ validateStructuredOutputForTool(result, HELPER_TOOLS.ACTOR_GET_DETAILS, 'default');
228
+ }),
229
+ },
230
+ {
231
+ name: 'should return only input schema when output={ inputSchema: true }',
232
+ isDeploymentTest: false,
233
+ run: withClient({ tools: ['actors'] }, async (client) => {
234
+ const result = await client.callTool({
235
+ name: HELPER_TOOLS.ACTOR_GET_DETAILS,
236
+ arguments: {
237
+ actor: ACTOR_NORMAL_MODE,
238
+ output: {
239
+ description: false,
240
+ stats: false,
241
+ pricing: false,
242
+ rating: false,
243
+ metadata: false,
244
+ inputSchema: true,
245
+ readme: false,
246
+ mcpTools: false,
247
+ },
248
+ },
249
+ });
250
+ const content = result.content;
251
+ // Should contain schema but NOT readme or actor card
252
+ expect(content.some((item) => item.text.includes('Input schema'))).toBe(true);
253
+ expect(content.some((item) => item.text.includes('README'))).toBe(false);
254
+ }),
255
+ },
256
+ {
257
+ name: 'should return only description and stats when specified',
258
+ isDeploymentTest: false,
259
+ run: withClient({ tools: ['actors'] }, async (client) => {
260
+ const result = await client.callTool({
261
+ name: HELPER_TOOLS.ACTOR_GET_DETAILS,
262
+ arguments: {
263
+ actor: ACTOR_NORMAL_MODE,
264
+ output: {
265
+ description: true,
266
+ stats: true,
267
+ pricing: false,
268
+ rating: false,
269
+ metadata: false,
270
+ inputSchema: false,
271
+ readme: false,
272
+ mcpTools: false,
273
+ },
274
+ },
275
+ });
276
+ const content = result.content;
277
+ // Should contain actor info but NOT readme or schema
278
+ expect(content.some((item) => item.text.includes('Actor information'))).toBe(true);
279
+ expect(content.some((item) => item.text.includes('Input schema'))).toBe(false);
280
+ }),
281
+ },
282
+ {
283
+ name: 'should list MCP tools when output={ mcpTools: true } for MCP server Actor',
284
+ isDeploymentTest: false,
285
+ run: withClient({ tools: ['actors'] }, async (client) => {
286
+ const result = await client.callTool({
287
+ name: HELPER_TOOLS.ACTOR_GET_DETAILS,
288
+ arguments: {
289
+ actor: ACTOR_EXAMPLE_MCP_SERVER,
290
+ output: {
291
+ description: false,
292
+ stats: false,
293
+ pricing: false,
294
+ rating: false,
295
+ metadata: false,
296
+ inputSchema: false,
297
+ readme: false,
298
+ mcpTools: true,
299
+ },
300
+ },
301
+ });
302
+ const content = result.content;
303
+ expect(content.some((item) => item.text.includes('Available MCP Tools'))).toBe(true);
304
+ expect(content.some((item) => item.text.includes('add'))).toBe(true);
305
+ }),
306
+ },
307
+ {
308
+ name: 'should return graceful note when output={ mcpTools: true } for regular Actor',
309
+ isDeploymentTest: false,
310
+ run: withClient({ tools: ['actors'] }, async (client) => {
311
+ const result = await client.callTool({
312
+ name: HELPER_TOOLS.ACTOR_GET_DETAILS,
313
+ arguments: {
314
+ actor: ACTOR_NORMAL_MODE,
315
+ output: {
316
+ description: false,
317
+ stats: false,
318
+ pricing: false,
319
+ rating: false,
320
+ metadata: false,
321
+ inputSchema: false,
322
+ readme: false,
323
+ mcpTools: true,
324
+ },
325
+ },
326
+ });
327
+ const content = result.content;
328
+ expect(content.some((item) => item.text.includes('This Actor is not an MCP server'))).toBe(true);
329
+ }),
330
+ },
331
+ {
332
+ name: 'should return structured output for fetch-actor-details with selective output matching outputSchema',
333
+ isDeploymentTest: false,
334
+ run: withClient({ tools: ['actors'] }, async (client) => {
335
+ const toolName = HELPER_TOOLS.ACTOR_GET_DETAILS;
336
+ // Test with output={ mcpTools: true } - should validate against schema even with selective fields
337
+ const result = await client.callTool({
338
+ name: toolName,
339
+ arguments: {
340
+ actor: ACTOR_EXAMPLE_MCP_SERVER,
341
+ output: {
342
+ description: false,
343
+ stats: false,
344
+ pricing: false,
345
+ rating: false,
346
+ metadata: false,
347
+ inputSchema: false,
348
+ readme: false,
349
+ mcpTools: true,
350
+ },
351
+ },
352
+ });
353
+ const content = result.content;
354
+ expect(content.length).toBeGreaterThan(0);
355
+ // This should validate successfully - structured output must match schema
356
+ validateStructuredOutputForTool(result, HELPER_TOOLS.ACTOR_GET_DETAILS, 'default');
357
+ }),
358
+ },
359
+ {
360
+ name: 'should return structured output for fetch-actor-details with output={ description: true, readme: true } matching outputSchema',
361
+ isDeploymentTest: false,
362
+ run: withClient({ tools: ['actors'] }, async (client) => {
363
+ const toolName = HELPER_TOOLS.ACTOR_GET_DETAILS;
364
+ // Test with output={ description: true, readme: true } - inputSchema should be undefined
365
+ const result = await client.callTool({
366
+ name: toolName,
367
+ arguments: {
368
+ actor: ACTOR_NORMAL_MODE,
369
+ output: {
370
+ description: true,
371
+ stats: false,
372
+ pricing: false,
373
+ rating: false,
374
+ metadata: false,
375
+ inputSchema: false,
376
+ readme: true,
377
+ mcpTools: false,
378
+ },
379
+ },
380
+ });
381
+ const content = result.content;
382
+ expect(content.length).toBeGreaterThan(0);
383
+ // This should validate successfully - structured output must match schema
384
+ validateStructuredOutputForTool(result, HELPER_TOOLS.ACTOR_GET_DETAILS, 'default');
385
+ }),
386
+ },
387
+ {
388
+ name: 'should return only pricing when output={ pricing: true }',
389
+ isDeploymentTest: false,
390
+ run: withClient({ tools: ['actors'] }, async (client) => {
391
+ const result = await client.callTool({
392
+ name: HELPER_TOOLS.ACTOR_GET_DETAILS,
393
+ arguments: {
394
+ actor: ACTOR_NORMAL_MODE,
395
+ output: {
396
+ description: false,
397
+ stats: false,
398
+ pricing: true,
399
+ rating: false,
400
+ metadata: false,
401
+ inputSchema: false,
402
+ readme: false,
403
+ mcpTools: false,
404
+ },
405
+ },
406
+ });
407
+ const content = result.content;
408
+ // Should contain actor info (pricing is part of actor card) but NOT readme or schema
409
+ expect(content.some((item) => item.text.includes('Actor information'))).toBe(true);
410
+ expect(content.some((item) => item.text.includes('README'))).toBe(false);
411
+ expect(content.some((item) => item.text.includes('Input schema'))).toBe(false);
412
+ // Validate structured output
413
+ validateStructuredOutputForTool(result, HELPER_TOOLS.ACTOR_GET_DETAILS, 'default');
414
+ }),
415
+ },
416
+ {
417
+ name: 'should return only readme when output={ readme: true }',
418
+ isDeploymentTest: false,
419
+ run: withClient({ tools: ['actors'] }, async (client) => {
420
+ const result = await client.callTool({
421
+ name: HELPER_TOOLS.ACTOR_GET_DETAILS,
422
+ arguments: {
423
+ actor: ACTOR_NORMAL_MODE,
424
+ output: {
425
+ description: false,
426
+ stats: false,
427
+ pricing: false,
428
+ rating: false,
429
+ metadata: false,
430
+ inputSchema: false,
431
+ readme: true,
432
+ mcpTools: false,
433
+ },
434
+ },
435
+ });
436
+ const content = result.content;
437
+ // Should contain readme text but NOT actor info card or input schema
438
+ expect(content.length).toBeGreaterThan(0);
439
+ expect(content.some((item) => item.text.includes('Actor information'))).toBe(false);
440
+ expect(content.some((item) => item.text.includes('Input schema'))).toBe(false);
441
+ // Validate structured output
442
+ validateStructuredOutputForTool(result, HELPER_TOOLS.ACTOR_GET_DETAILS, 'default');
443
+ }),
444
+ },
445
+ {
446
+ name: 'should return README content (summary or full) in text and structured response for fetch-actor-details',
447
+ isDeploymentTest: false,
448
+ run: withClient({ tools: ['actors'] }, async (client) => {
449
+ var _a;
450
+ const result = await client.callTool({
451
+ name: 'fetch-actor-details',
452
+ arguments: { actor: ACTOR_NORMAL_MODE, output: { description: true, readme: true, inputSchema: true } },
453
+ });
454
+ expect(result.content).toBeDefined();
455
+ const content = result.content;
456
+ const allText = content.map((item) => item.text).join('\n');
457
+ // Text should contain actor card, README section (summary or full fallback), and input schema
458
+ expect(allText).toContain('Actor information');
459
+ expect(allText).toMatch(/# README summary|# README/);
460
+ expect(allText).toContain('Input schema');
461
+ expectReadmeInStructuredContent(result, ACTOR_NORMAL_MODE);
462
+ validateStructuredOutput(result, (_a = findToolByName(HELPER_TOOLS.ACTOR_GET_DETAILS, 'default')) === null || _a === void 0 ? void 0 : _a.outputSchema, 'fetch-actor-details');
463
+ }),
464
+ },
465
+ {
466
+ name: 'should use default values when output object is not provided',
467
+ isDeploymentTest: false,
468
+ run: withClient({ tools: ['actors'] }, async (client) => {
469
+ // When output is not provided, all fields should default to their default values
470
+ const result = await client.callTool({
471
+ name: HELPER_TOOLS.ACTOR_GET_DETAILS,
472
+ arguments: { actor: ACTOR_NORMAL_MODE },
473
+ });
474
+ const content = result.content;
475
+ // Should contain all default sections (description, stats, pricing, rating, metadata, readme, inputSchema)
476
+ // but NOT mcpTools (which defaults to false)
477
+ expect(content.some((item) => item.text.includes('Actor information'))).toBe(true);
478
+ expect(content.some((item) => item.text.includes('Input schema'))).toBe(true);
479
+ expect(content.some((item) => item.text.includes('Available MCP Tools'))).toBe(false);
480
+ }),
481
+ },
482
+ {
483
+ name: 'should return all fields when output includes all standard options',
484
+ isDeploymentTest: false,
485
+ run: withClient({ tools: ['actors'] }, async (client) => {
486
+ var _a, _b;
487
+ const result = await client.callTool({
488
+ name: HELPER_TOOLS.ACTOR_GET_DETAILS,
489
+ arguments: {
490
+ actor: ACTOR_NORMAL_MODE,
491
+ output: {
492
+ description: true,
493
+ stats: true,
494
+ pricing: true,
495
+ rating: false,
496
+ metadata: false,
497
+ inputSchema: true,
498
+ readme: true,
499
+ mcpTools: false,
500
+ },
501
+ },
502
+ });
503
+ const content = result.content;
504
+ // Should contain all sections in text
505
+ expect(content.some((item) => item.text.includes('Actor information'))).toBe(true);
506
+ expect(content.some((item) => item.text.includes('Input schema'))).toBe(true);
507
+ // Validate structured output exists and has all fields
508
+ const resultWithStructured = result;
509
+ expect(resultWithStructured.structuredContent).toBeDefined();
510
+ expect((_a = resultWithStructured.structuredContent) === null || _a === void 0 ? void 0 : _a.actorInfo).toBeDefined();
511
+ expect((_b = resultWithStructured.structuredContent) === null || _b === void 0 ? void 0 : _b.inputSchema).toBeDefined();
512
+ // Validate against schema
513
+ validateStructuredOutputForTool(result, HELPER_TOOLS.ACTOR_GET_DETAILS, 'default');
514
+ }),
515
+ },
516
+ {
517
+ name: 'should support granular output controls for rating and metadata',
518
+ isDeploymentTest: false,
519
+ run: withClient({ tools: ['actors'] }, async (client) => {
520
+ // Test 1: Only pricing (should include pricing, NOT other sections)
521
+ const pricingOnlyResult = await client.callTool({
522
+ name: HELPER_TOOLS.ACTOR_GET_DETAILS,
523
+ arguments: {
524
+ actor: ACTOR_NORMAL_MODE,
525
+ output: {
526
+ description: false,
527
+ stats: false,
528
+ pricing: true,
529
+ rating: false,
530
+ metadata: false,
531
+ inputSchema: false,
532
+ readme: false,
533
+ mcpTools: false,
534
+ },
535
+ },
536
+ });
537
+ const pricingContent = pricingOnlyResult.content;
538
+ const pricingText = pricingContent.map((item) => item.text).join('\n');
539
+ // Should include actor card header and pricing
540
+ expect(pricingText).toContain('Actor information');
541
+ expect(pricingText).toContain('Pricing');
542
+ // Should NOT include other sections
543
+ expect(pricingText).not.toContain('Description:');
544
+ expect(pricingText).not.toContain('Stats:');
545
+ expect(pricingText).not.toContain('Rating:');
546
+ expect(pricingText).not.toContain('Developed by:');
547
+ expect(pricingText).not.toContain('Categories:');
548
+ expect(pricingText).not.toContain('Last modified:');
549
+ expect(pricingText).not.toContain('README');
550
+ // Test 2: Only rating
551
+ const ratingOnlyResult = await client.callTool({
552
+ name: HELPER_TOOLS.ACTOR_GET_DETAILS,
553
+ arguments: {
554
+ actor: ACTOR_NORMAL_MODE,
555
+ output: {
556
+ description: false,
557
+ stats: false,
558
+ pricing: false,
559
+ rating: true,
560
+ metadata: false,
561
+ inputSchema: false,
562
+ readme: false,
563
+ mcpTools: false,
564
+ },
565
+ },
566
+ });
567
+ const ratingContent = ratingOnlyResult.content;
568
+ const ratingText = ratingContent.map((item) => item.text).join('\n');
569
+ // Should include actor card header and rating
570
+ expect(ratingText).toContain('Actor information');
571
+ // TODO: re-enable once apify/normal-mode-test-actor has reviews; Rating: is omitted when review count is 0
572
+ // expect(ratingText).toContain('Rating:');
573
+ // Should NOT include other sections
574
+ expect(ratingText).not.toContain('Description:');
575
+ expect(ratingText).not.toContain('Stats:');
576
+ expect(ratingText).not.toContain('Pricing');
577
+ expect(ratingText).not.toContain('Developed by:');
578
+ expect(ratingText).not.toContain('Categories:');
579
+ expect(ratingText).not.toContain('Last modified:');
580
+ expect(ratingText).not.toContain('README');
581
+ // Test 3: Only metadata (should include developer, categories, last modified, deprecation status)
582
+ const metadataOnlyResult = await client.callTool({
583
+ name: HELPER_TOOLS.ACTOR_GET_DETAILS,
584
+ arguments: {
585
+ actor: ACTOR_NORMAL_MODE,
586
+ output: {
587
+ description: false,
588
+ stats: false,
589
+ pricing: false,
590
+ rating: false,
591
+ metadata: true,
592
+ inputSchema: false,
593
+ readme: false,
594
+ mcpTools: false,
595
+ },
596
+ },
597
+ });
598
+ const metadataContent = metadataOnlyResult.content;
599
+ const metadataText = metadataContent.map((item) => item.text).join('\n');
600
+ // Should include developer, categories, and last modified date
601
+ expect(metadataText).toContain('Developed by:');
602
+ expect(metadataText).toContain('Categories:');
603
+ expect(metadataText).toContain('Last modified:');
604
+ // Should NOT include other sections
605
+ expect(metadataText).not.toContain('Description:');
606
+ expect(metadataText).not.toContain('Stats:');
607
+ expect(metadataText).not.toContain('Pricing');
608
+ expect(metadataText).not.toContain('Rating:');
609
+ expect(metadataText).not.toContain('README');
610
+ // Test 4: Combination - pricing + rating + metadata (should exclude description, stats, readme, input-schema)
611
+ const combinationResult = await client.callTool({
612
+ name: HELPER_TOOLS.ACTOR_GET_DETAILS,
613
+ arguments: {
614
+ actor: ACTOR_NORMAL_MODE,
615
+ output: {
616
+ description: false,
617
+ stats: false,
618
+ pricing: true,
619
+ rating: true,
620
+ metadata: true,
621
+ inputSchema: false,
622
+ readme: false,
623
+ mcpTools: false,
624
+ },
625
+ },
626
+ });
627
+ const combinationContent = combinationResult.content;
628
+ const combinationText = combinationContent.map((item) => item.text).join('\n');
629
+ // Should include: pricing, rating, metadata (developer, categories, last modified)
630
+ expect(combinationText).toContain('Pricing');
631
+ // TODO: re-enable once apify/normal-mode-test-actor has reviews; Rating: is omitted when review count is 0
632
+ // expect(combinationText).toContain('Rating:');
633
+ expect(combinationText).toContain('Developed by:');
634
+ expect(combinationText).toContain('Categories:');
635
+ expect(combinationText).toContain('Last modified:');
636
+ // Should NOT include: description, stats, readme, input-schema
637
+ expect(combinationText).not.toContain('Description:');
638
+ expect(combinationText).not.toContain('Stats:');
639
+ expect(combinationText).not.toContain('README');
640
+ expect(combinationText).not.toContain('Input schema');
641
+ // Validate structured output for all test cases
642
+ validateStructuredOutputForTool(pricingOnlyResult, HELPER_TOOLS.ACTOR_GET_DETAILS, 'default');
643
+ validateStructuredOutputForTool(ratingOnlyResult, HELPER_TOOLS.ACTOR_GET_DETAILS, 'default');
644
+ validateStructuredOutputForTool(metadataOnlyResult, HELPER_TOOLS.ACTOR_GET_DETAILS, 'default');
645
+ validateStructuredOutputForTool(combinationResult, HELPER_TOOLS.ACTOR_GET_DETAILS, 'default');
646
+ }),
647
+ },
648
+ {
649
+ name: 'should dynamically test all output options and verify section presence/absence',
650
+ isDeploymentTest: false,
651
+ run: withClient({ tools: ['actors'] }, async (client) => {
652
+ // Define all output options with their expected markers in text
653
+ const outputOptions = [
654
+ {
655
+ name: 'description',
656
+ field: 'description',
657
+ markers: ['Description:'],
658
+ notMarkers: [
659
+ 'Developed by:',
660
+ 'Categories:',
661
+ 'Stats:',
662
+ 'Pricing',
663
+ 'Rating:',
664
+ 'Last modified:',
665
+ 'README',
666
+ 'Input schema',
667
+ ],
668
+ },
669
+ {
670
+ name: 'stats',
671
+ field: 'stats',
672
+ markers: ['Stats:', 'total users', 'monthly users'],
673
+ notMarkers: [
674
+ 'Developed by:',
675
+ 'Categories:',
676
+ 'Description:',
677
+ 'Pricing',
678
+ 'Rating:',
679
+ 'Last modified:',
680
+ 'README',
681
+ 'Input schema',
682
+ ],
683
+ },
684
+ {
685
+ name: 'pricing',
686
+ field: 'pricing',
687
+ markers: ['Pricing'],
688
+ notMarkers: [
689
+ 'Developed by:',
690
+ 'Categories:',
691
+ 'Description:',
692
+ 'Stats:',
693
+ 'Rating:',
694
+ 'Last modified:',
695
+ 'README',
696
+ 'Input schema',
697
+ ],
698
+ },
699
+ {
700
+ name: 'rating',
701
+ field: 'rating',
702
+ // TODO: restore markers to ['Rating:', 'out of 5'] once apify/normal-mode-test-actor has reviews;
703
+ // Rating: is omitted when review count is 0
704
+ markers: [],
705
+ notMarkers: [
706
+ 'Developed by:',
707
+ 'Categories:',
708
+ 'Description:',
709
+ 'Stats:',
710
+ 'Pricing',
711
+ 'Last modified:',
712
+ 'README',
713
+ 'Input schema',
714
+ ],
715
+ },
716
+ {
717
+ name: 'metadata',
718
+ field: 'metadata',
719
+ markers: ['Developed by:', 'Categories:', 'Last modified:'],
720
+ notMarkers: ['Description:', 'Stats:', 'Pricing', 'Rating:', 'README', 'Input schema'],
721
+ },
722
+ {
723
+ name: 'input-schema',
724
+ field: 'inputSchema',
725
+ markers: ['Input schema', '```json'],
726
+ notMarkers: [
727
+ 'Developed by:',
728
+ 'Description:',
729
+ 'Stats:',
730
+ 'Pricing',
731
+ 'Rating:',
732
+ 'Last modified:',
733
+ 'README',
734
+ ],
735
+ },
736
+ {
737
+ name: 'readme',
738
+ field: 'readme',
739
+ markers: [],
740
+ notMarkers: ['Input schema'],
741
+ },
742
+ ];
743
+ // Test each output option individually
744
+ for (const option of outputOptions) {
745
+ const result = await client.callTool({
746
+ name: HELPER_TOOLS.ACTOR_GET_DETAILS,
747
+ arguments: {
748
+ actor: ACTOR_NORMAL_MODE,
749
+ output: {
750
+ description: option.field === 'description',
751
+ stats: option.field === 'stats',
752
+ pricing: option.field === 'pricing',
753
+ rating: option.field === 'rating',
754
+ metadata: option.field === 'metadata',
755
+ inputSchema: option.field === 'inputSchema',
756
+ readme: option.field === 'readme',
757
+ mcpTools: false,
758
+ },
759
+ },
760
+ });
761
+ const content = result.content;
762
+ const text = content.map((item) => item.text).join('\n');
763
+ // Verify expected markers are present
764
+ for (const marker of option.markers) {
765
+ expect(text, `output=${option.name} should contain "${marker}"`).toContain(marker);
766
+ }
767
+ // Verify unwanted markers are absent
768
+ for (const notMarker of option.notMarkers) {
769
+ expect(text, `output=${option.name} should NOT contain "${notMarker}"`).not.toContain(notMarker);
770
+ }
771
+ // Validate structured output
772
+ validateStructuredOutputForTool(result, HELPER_TOOLS.ACTOR_GET_DETAILS, 'default');
773
+ }
774
+ // Test a combination: all actor card sections (description, stats, pricing, rating, metadata)
775
+ const allCardSectionsResult = await client.callTool({
776
+ name: HELPER_TOOLS.ACTOR_GET_DETAILS,
777
+ arguments: {
778
+ actor: ACTOR_NORMAL_MODE,
779
+ output: {
780
+ description: true,
781
+ stats: true,
782
+ pricing: true,
783
+ rating: true,
784
+ metadata: true,
785
+ inputSchema: false,
786
+ readme: false,
787
+ mcpTools: false,
788
+ },
789
+ },
790
+ });
791
+ const allCardContent = allCardSectionsResult.content;
792
+ const allCardText = allCardContent.map((item) => item.text).join('\n');
793
+ // Should include all actor card sections
794
+ expect(allCardText).toContain('Description:');
795
+ expect(allCardText).toContain('Stats:');
796
+ expect(allCardText).toContain('Pricing');
797
+ // TODO: re-enable once apify/normal-mode-test-actor has reviews; Rating: is omitted when review count is 0
798
+ // expect(allCardText).toContain('Rating:');
799
+ expect(allCardText).toContain('Developed by:');
800
+ expect(allCardText).toContain('Categories:');
801
+ expect(allCardText).toContain('Last modified:');
802
+ // Should NOT include readme or input-schema
803
+ expect(allCardText).not.toContain('README');
804
+ expect(allCardText).not.toContain('Input schema');
805
+ validateStructuredOutputForTool(allCardSectionsResult, HELPER_TOOLS.ACTOR_GET_DETAILS, 'default');
806
+ }),
807
+ },
808
+ {
809
+ name: 'should return structured output for search-actors matching outputSchema',
810
+ isDeploymentTest: false,
811
+ run: withClient({ tools: ['actors'] }, async (client) => {
812
+ const toolName = HELPER_TOOLS.STORE_SEARCH;
813
+ const result = await client.callTool({
814
+ name: toolName,
815
+ arguments: { keywords: 'rag web browser', limit: 5, offset: 0 },
816
+ });
817
+ const content = result.content;
818
+ expect(content.length).toBeGreaterThan(0);
819
+ validateStructuredOutputForTool(result, HELPER_TOOLS.STORE_SEARCH, 'default');
820
+ }),
821
+ },
822
+ {
823
+ name: 'should return structured output for get-actor-run matching outputSchema',
824
+ isDeploymentTest: false,
825
+ run: withClient({ tools: ['actors', 'runs'] }, async (client) => {
826
+ var _a;
827
+ // First, start an async actor run to get a runId
828
+ const callResult = await client.callTool({
829
+ name: HELPER_TOOLS.ACTOR_CALL,
830
+ arguments: { actor: ACTOR_NORMAL_MODE, input: { firstNumber: 1, secondNumber: 2 }, waitSecs: 0 },
831
+ });
832
+ const resultWithStructured = callResult;
833
+ expect((_a = resultWithStructured.structuredContent) === null || _a === void 0 ? void 0 : _a.runId).toBeDefined();
834
+ const runId = resultWithStructured.structuredContent.runId;
835
+ // Now test get-actor-run
836
+ const runResult = await client.callTool({ name: HELPER_TOOLS.ACTOR_RUNS_GET, arguments: { runId } });
837
+ expect(runResult.content).toBeDefined();
838
+ // Validate structured output for get-actor-run
839
+ validateStructuredOutputForTool(runResult, HELPER_TOOLS.ACTOR_RUNS_GET, 'default');
840
+ }),
841
+ },
842
+ {
843
+ name: 'should return Actor details both for full Actor name and ID',
844
+ isDeploymentTest: false,
845
+ run: withClient(undefined, async (client, ctx) => {
846
+ const apifyClient = ctx.createApifyClient();
847
+ const actor = await apifyClient.actor(ACTOR_NORMAL_MODE).get();
848
+ expect(actor).toBeDefined();
849
+ const actorId = actor.id;
850
+ // Fetch by full Actor name
851
+ const resultByName = await client.callTool({
852
+ name: 'fetch-actor-details',
853
+ arguments: { actor: ACTOR_NORMAL_MODE },
854
+ });
855
+ const contentByName = resultByName.content;
856
+ expect(contentByName[0].text).toContain(ACTOR_NORMAL_MODE);
857
+ // Fetch by Actor ID only
858
+ const resultById = await client.callTool({
859
+ name: 'fetch-actor-details',
860
+ arguments: { actor: actorId },
861
+ });
862
+ const contentById = resultById.content;
863
+ expect(contentById[0].text).toContain(ACTOR_NORMAL_MODE);
864
+ }),
865
+ },
866
+ {
867
+ name: 'returns structuredContent for get-actor-run',
868
+ isDeploymentTest: false,
869
+ run: withClient({ tools: ['actors', 'runs'] }, async (client) => {
870
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j;
871
+ // First, start an async actor run to get a runId
872
+ const callResult = await client.callTool({
873
+ name: HELPER_TOOLS.ACTOR_CALL,
874
+ arguments: { actor: ACTOR_NORMAL_MODE, input: { firstNumber: 1, secondNumber: 2 }, waitSecs: 0 },
875
+ });
876
+ const resultWithStructured = callResult;
877
+ const runId = resultWithStructured.structuredContent.runId;
878
+ // Now test get-actor-run with waitSecs to drive it to terminal state.
879
+ const runResult = await client.callTool({
880
+ name: HELPER_TOOLS.ACTOR_RUNS_GET,
881
+ arguments: { runId, waitSecs: 30 },
882
+ });
883
+ const runContent = runResult;
884
+ expect(runContent.structuredContent).toBeDefined();
885
+ expect((_a = runContent.structuredContent) === null || _a === void 0 ? void 0 : _a.runId).toBe(runId);
886
+ expect((_b = runContent.structuredContent) === null || _b === void 0 ? void 0 : _b.actorId).toBeDefined();
887
+ expect((_c = runContent.structuredContent) === null || _c === void 0 ? void 0 : _c.status).toBeDefined();
888
+ expect((_d = runContent.structuredContent) === null || _d === void 0 ? void 0 : _d.summary).toBeDefined();
889
+ expect((_e = runContent.structuredContent) === null || _e === void 0 ? void 0 : _e.nextStep).toBeDefined();
890
+ expect((_f = runContent.structuredContent) === null || _f === void 0 ? void 0 : _f.storages).toBeDefined();
891
+ // No inlined dataset items or KV record bodies anywhere on the response.
892
+ const dump = JSON.stringify(runContent.structuredContent);
893
+ expect(dump).not.toContain('previewItems');
894
+ if (((_g = runContent.structuredContent) === null || _g === void 0 ? void 0 : _g.status) === 'SUCCEEDED') {
895
+ expect((_j = (_h = runContent.structuredContent) === null || _h === void 0 ? void 0 : _h.storages.datasets) === null || _j === void 0 ? void 0 : _j.default.id).toBeDefined();
896
+ }
897
+ }),
898
+ },
899
+ {
900
+ name: 'rejects get-actor-run waitSecs above 45',
901
+ isDeploymentTest: false,
902
+ run: withClient({ tools: ['actors', 'runs'] }, async (client) => {
903
+ // Fake-but-plausible runId so failure is waitSecs validation, not missing run.
904
+ await expect(client.callTool({
905
+ name: HELPER_TOOLS.ACTOR_RUNS_GET,
906
+ arguments: { runId: 'aaaaaaaaaaaaaaaaa', waitSecs: 46 },
907
+ })).rejects.toThrow(/waitSecs|less than or equal to 45|<= 45/i);
908
+ }),
909
+ },
910
+ ];
911
+ //# sourceMappingURL=actors.cases.js.map