apify-test-tools 0.9.1-beta.3 → 0.9.1-beta.4

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/lib/consts.ts CHANGED
@@ -17,6 +17,10 @@ export const TO_FINISH_WITH_OPTIONS: ToFinishWithOptionsWithDefaults = {
17
17
  */
18
18
  export const DEFAULT_TEST_RUN_DURATION_MS = 60 * 60 * 1000; // 1 hour
19
19
 
20
+ // Prevent orphaned runs - timeout the Actor 1 minute before the test does so everything is logged correctly.
21
+ // - Otherwise the run link URL can get lost.
22
+ export const DEFAULT_TEST_ACTOR_TIMEOUT_SECS = DEFAULT_TEST_RUN_DURATION_MS / 1000 - 60;
23
+
20
24
  /**
21
25
  * Delay before checking the dataset and statistics after a run finishes to resolve eventual consistency.
22
26
  * - This value should ensure that the dataset and statistics are fully updated before any assertions are made.
package/lib/lib.ts CHANGED
@@ -3,7 +3,7 @@ import { ApifyClient } from 'apify-client';
3
3
  import type { SuiteFactory, TestContext, TestFunction } from 'vitest';
4
4
  import { describe as vitestDescribe, ExpectStatic, test as vitestTest } from 'vitest';
5
5
 
6
- import { DATASET_SYNC_DELAY_MS, DEFAULT_TEST_RUN_DURATION_MS } from './consts.js';
6
+ import { DATASET_SYNC_DELAY_MS, DEFAULT_TEST_ACTOR_TIMEOUT_SECS, DEFAULT_TEST_RUN_DURATION_MS } from './consts.js';
7
7
  import { extendExpect } from './extend-expect.js';
8
8
  import { RunTestResult } from './run-test-result.js';
9
9
  import type { ActorBuild, ActorTestOptions, RunOptions } from './types.js';
@@ -39,6 +39,7 @@ const DEFAULT_TEST_OPTIONS: ActorTestOptions = {
39
39
  concurrent: true,
40
40
  // test should finish within 1 hour
41
41
  timeout: DEFAULT_TEST_RUN_DURATION_MS,
42
+ retry: 1,
42
43
  };
43
44
 
44
45
  /**
@@ -51,12 +52,6 @@ export const describe = (name: string, fn?: SuiteFactory<object>, options: Actor
51
52
  vitestDescribe.runIf(!!TESTER_APIFY_TOKEN || !!RUN_ALL_PLATFORM_TESTS)(name, options, fn);
52
53
  };
53
54
 
54
- const DEFAULT_TEST_ACTOR_OPTIONS: ActorTestOptions = {
55
- retry: 1,
56
- // prevent orphaned runs
57
- timeout: DEFAULT_TEST_RUN_DURATION_MS,
58
- };
59
-
60
55
  /**
61
56
  * @param actorId - The actor's raw platform ID or its full name (`owner/name`, e.g. `"apify/web-scraper"`).
62
57
  */
@@ -66,10 +61,8 @@ export const testActor = <T>(
66
61
  fn: TestFunction<{ run: ReturnType<typeof createStartRunFn<T>> }>,
67
62
  testOptions?: ActorTestOptions,
68
63
  ) => {
69
- const options = {
70
- ...DEFAULT_TEST_ACTOR_OPTIONS,
71
- ...testOptions,
72
- };
64
+ const options = { ...DEFAULT_TEST_OPTIONS, ...testOptions };
65
+
73
66
  const name = `${actorId}: ${testName}`;
74
67
  // `RUN_ALL_PLATFORM_TESTS` is needed for the scheduled tests, which have no `ACTOR_BUILDS` to match the
75
68
  // tests against - without it, every test would be filtered out as an actor we didn't build.
@@ -100,10 +93,8 @@ export const testStandbyActor = <I = any, O = any>(
100
93
  fn: TestFunction<{ callStandby: ReturnType<typeof createStartStandbyFn<I, O>> }>,
101
94
  testOptions?: ActorTestOptions,
102
95
  ) => {
103
- const options = {
104
- ...DEFAULT_TEST_ACTOR_OPTIONS,
105
- ...testOptions,
106
- };
96
+ const options = { ...DEFAULT_TEST_OPTIONS, ...testOptions };
97
+
107
98
  const name = `${actorId}: ${testName}`;
108
99
  // `RUN_ALL_PLATFORM_TESTS` is needed for the scheduled tests, which have no `ACTOR_BUILDS` to match the
109
100
  // tests against - without it, every test would be filtered out as an actor we didn't build.
@@ -279,13 +270,20 @@ const createStartRunFn = <T>(actorId: string, testContext: TestContext) => {
279
270
  return new RunTestResult(apifyClient, run);
280
271
  }
281
272
 
282
- const actor = apifyClient.actor(actorId);
283
-
284
273
  const actorInput = {
285
274
  ...(prefilledInput && (await getActorPrefilledInput(apifyClient, actorId, buildId))),
286
275
  ...input,
287
276
  };
288
- const run = await actor.call(actorInput, { build, log: null, ...options });
277
+
278
+ const actor = apifyClient.actor(actorId);
279
+ const actorInfo = await actor.get();
280
+ const timeout = Math.min(
281
+ actorInfo?.defaultRunOptions?.timeoutSecs ?? DEFAULT_TEST_ACTOR_TIMEOUT_SECS,
282
+ DEFAULT_TEST_ACTOR_TIMEOUT_SECS,
283
+ );
284
+
285
+ const actorOptions = { timeout, build, log: null, ...options };
286
+ const run = await actor.call(actorInput, actorOptions);
289
287
 
290
288
  const runLink = generateRunLink(run);
291
289
  await annotate(`${task.name} - ${runLink}`, 'run_link');
@@ -306,3 +304,7 @@ const createStartRunFn = <T>(actorId: string, testContext: TestContext) => {
306
304
  const generateRunLink = (run: ActorRun | ActorRunListItem): string => {
307
305
  return `https://console.apify.com/view/runs/${run.id}`;
308
306
  };
307
+
308
+ /** Used for unit testing */
309
+ // eslint-disable-next-line no-underscore-dangle
310
+ export const _private = { createStartRunFn } as const;
package/lib/types.ts CHANGED
@@ -10,7 +10,15 @@ export type ActorBuild = {
10
10
 
11
11
  export type RunOptions<T> = {
12
12
  input: Omit<T, 'actorName'>;
13
- options?: ActorCallOptions;
13
+ options?: Omit<ActorCallOptions, 'timeout'> & {
14
+ /**
15
+ * Timeout for the actor run in seconds. Zero value means there is no timeout.
16
+ * - If `undefined`, the run uses timeout of the default Actor run configuration.
17
+ *
18
+ * @default 3540 // 59 minutes (finish the run before the test)
19
+ */
20
+ timeout?: number;
21
+ };
14
22
  prefilledInput?: boolean;
15
23
  /**
16
24
  * If you specify `runId`, all the other options will be ignored and this run's data will
@@ -138,8 +146,8 @@ export type ActorTestOptions = Omit<TestOptions, 'retry' | 'timeout'> & {
138
146
  // we are just extending the docs here to replace the default value, otherwise it's the exact same
139
147
  retry?: TestOptions['retry'];
140
148
  /**
141
- * Timeout for the actor run in milliseconds. Zero value means there is no timeout.
142
- * - If `undefined`, the run uses timeout of the default Actor run configuration.
149
+ * Timeout for the test in milliseconds. Zero value means there is no timeout.
150
+ * - If `undefined`, the run uses timeout of the default test configuration.
143
151
  *
144
152
  * @default 60 * 60 * 1000 // 1 hour
145
153
  */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "apify-test-tools",
3
- "version": "0.9.1-beta.3",
3
+ "version": "0.9.1-beta.4",
4
4
  "type": "module",
5
5
  "description": "TBD",
6
6
  "repository": {
@@ -0,0 +1,53 @@
1
+ import type { Actor, ActorClient, ActorRun } from 'apify-client';
2
+ import { ApifyClient } from 'apify-client';
3
+ import type { TestContext } from 'vitest';
4
+ import { beforeEach, describe, expect, it, vi } from 'vitest';
5
+
6
+ import { DEFAULT_TEST_ACTOR_TIMEOUT_SECS } from '../../lib/consts.js';
7
+ import { _private } from '../../lib/lib.js';
8
+ import * as UtilsModule from '../../lib/utils.js';
9
+
10
+ const { createStartRunFn } = _private;
11
+
12
+ describe('createStartRunFn()', () => {
13
+ const actorCallMock = vi.fn(async () => Promise.resolve({ id: 'fake-run-id' } as ActorRun));
14
+ const testContext = { task: {}, annotate: vi.fn() as TestContext['annotate'] } as TestContext;
15
+
16
+ vi.spyOn(UtilsModule, 'sleep').mockResolvedValue(undefined);
17
+
18
+ beforeEach(vi.clearAllMocks);
19
+
20
+ it('should apply default timeout to actor runs', async () => {
21
+ // Arrange
22
+
23
+ vi.spyOn(ApifyClient.prototype, 'actor').mockReturnValue({
24
+ call: actorCallMock as ActorClient['call'],
25
+ get: vi.fn(async () => ({ defaultRunOptions: {} }) as Actor) as ActorClient['get'],
26
+ } as ActorClient);
27
+
28
+ // Act
29
+ await createStartRunFn('123', testContext)({ input: {} });
30
+
31
+ // Assert
32
+ expect(actorCallMock).toHaveBeenCalledTimes(1);
33
+ expect(actorCallMock).toHaveBeenCalledWith(
34
+ {},
35
+ { build: undefined, log: null, timeout: DEFAULT_TEST_ACTOR_TIMEOUT_SECS },
36
+ );
37
+ });
38
+
39
+ it('should respect the default actor run timeout if less', async () => {
40
+ // Arrange
41
+ vi.spyOn(ApifyClient.prototype, 'actor').mockReturnValue({
42
+ call: actorCallMock as ActorClient['call'],
43
+ get: vi.fn(async () => ({ defaultRunOptions: { timeoutSecs: 60 } }) as Actor) as ActorClient['get'],
44
+ } as ActorClient);
45
+
46
+ // Act
47
+ await createStartRunFn('123', testContext)({ input: {} });
48
+
49
+ // Assert
50
+ expect(actorCallMock).toHaveBeenCalledTimes(1);
51
+ expect(actorCallMock).toHaveBeenCalledWith({}, { build: undefined, log: null, timeout: 60 });
52
+ });
53
+ });