apify-test-tools 0.10.2-beta.1 → 0.10.2-beta.3

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 (99) hide show
  1. package/.gitattributes +2 -0
  2. package/.github/actions/checkout-restore-dependencies/action.yaml +13 -29
  3. package/.github/cli/apify-test-tools.mjs +361 -0
  4. package/.github/scripts/run-with-apify-tokens.mjs +2 -2
  5. package/.github/workflows/_check_code.yaml +25 -0
  6. package/.github/workflows/_move_major_tag.yaml +3 -81
  7. package/.github/workflows/_tests.yaml +34 -0
  8. package/.github/workflows/_update_release_metadata.yaml +0 -12
  9. package/.github/workflows/manual_move_major_tag.yaml +3 -3
  10. package/.github/workflows/manual_release_stable.yaml +3 -13
  11. package/.github/workflows/on_master.yaml +9 -5
  12. package/.github/workflows/on_pull_request.yaml +3 -48
  13. package/.github/workflows/public_platform-tests.yaml +1 -1
  14. package/.github/workflows/public_pr-build-test.yaml +2 -2
  15. package/.github/workflows/public_push-build-latest.yaml +2 -2
  16. package/.husky/pre-commit +5 -0
  17. package/.prettierignore +3 -0
  18. package/CHANGELOG.md +5 -0
  19. package/CONTRIBUTING.md +8 -18
  20. package/README.md +94 -44
  21. package/bin/affected-actors.ts +166 -0
  22. package/bin/build-from-local.ts +47 -13
  23. package/bin/build.ts +35 -19
  24. package/bin/diff-changes.ts +25 -17
  25. package/bin/git.ts +52 -41
  26. package/bin/main.ts +63 -78
  27. package/bin/slack.ts +28 -6
  28. package/bin/test-report.ts +6 -9
  29. package/bin/types.ts +9 -0
  30. package/dist/bin/affected-actors.d.ts +50 -0
  31. package/dist/bin/affected-actors.d.ts.map +1 -0
  32. package/dist/bin/affected-actors.js +89 -0
  33. package/dist/bin/affected-actors.js.map +1 -0
  34. package/dist/bin/build-from-local.d.ts +10 -2
  35. package/dist/bin/build-from-local.d.ts.map +1 -1
  36. package/dist/bin/build-from-local.js +29 -11
  37. package/dist/bin/build-from-local.js.map +1 -1
  38. package/dist/bin/build.d.ts +15 -5
  39. package/dist/bin/build.d.ts.map +1 -1
  40. package/dist/bin/build.js +24 -15
  41. package/dist/bin/build.js.map +1 -1
  42. package/dist/bin/diff-changes.d.ts +8 -2
  43. package/dist/bin/diff-changes.d.ts.map +1 -1
  44. package/dist/bin/diff-changes.js +16 -16
  45. package/dist/bin/diff-changes.js.map +1 -1
  46. package/dist/bin/git.d.ts +23 -13
  47. package/dist/bin/git.d.ts.map +1 -1
  48. package/dist/bin/git.js +44 -32
  49. package/dist/bin/git.js.map +1 -1
  50. package/dist/bin/main.js +57 -57
  51. package/dist/bin/main.js.map +1 -1
  52. package/dist/bin/slack.d.ts +8 -1
  53. package/dist/bin/slack.d.ts.map +1 -1
  54. package/dist/bin/slack.js +26 -5
  55. package/dist/bin/slack.js.map +1 -1
  56. package/dist/bin/test-report.d.ts.map +1 -1
  57. package/dist/bin/test-report.js +6 -8
  58. package/dist/bin/test-report.js.map +1 -1
  59. package/dist/bin/types.d.ts +8 -0
  60. package/dist/bin/types.d.ts.map +1 -1
  61. package/dist/test/bundle/cli-bundle.test.d.ts +2 -0
  62. package/dist/test/bundle/cli-bundle.test.d.ts.map +1 -0
  63. package/dist/test/bundle/cli-bundle.test.js +169 -0
  64. package/dist/test/bundle/cli-bundle.test.js.map +1 -0
  65. package/dist/test/e2e/cli-bundle.e2e.test.d.ts +2 -0
  66. package/dist/test/e2e/cli-bundle.e2e.test.d.ts.map +1 -0
  67. package/dist/test/e2e/cli-bundle.e2e.test.js +172 -0
  68. package/dist/test/e2e/cli-bundle.e2e.test.js.map +1 -0
  69. package/dist/test/unit/bin/affected-actors.test.d.ts +2 -0
  70. package/dist/test/unit/bin/affected-actors.test.d.ts.map +1 -0
  71. package/dist/test/unit/bin/affected-actors.test.js +160 -0
  72. package/dist/test/unit/bin/affected-actors.test.js.map +1 -0
  73. package/dist/test/unit/bin/build-from-local.test.js +53 -1
  74. package/dist/test/unit/bin/build-from-local.test.js.map +1 -1
  75. package/dist/test/unit/bin/build.test.js +113 -3
  76. package/dist/test/unit/bin/build.test.js.map +1 -1
  77. package/dist/test/unit/bin/diff-changes.test.js +78 -49
  78. package/dist/test/unit/bin/diff-changes.test.js.map +1 -1
  79. package/dist/test/unit/bin/git.test.js +50 -53
  80. package/dist/test/unit/bin/git.test.js.map +1 -1
  81. package/dist/test/unit/bin/slack.test.d.ts +2 -0
  82. package/dist/test/unit/bin/slack.test.d.ts.map +1 -0
  83. package/dist/test/unit/bin/slack.test.js +54 -0
  84. package/dist/test/unit/bin/slack.test.js.map +1 -0
  85. package/dist/test/unit/should-built-and-test.test.js +23 -17
  86. package/dist/test/unit/should-built-and-test.test.js.map +1 -1
  87. package/dist/tsconfig.tsbuildinfo +1 -1
  88. package/package.json +13 -3
  89. package/test/bundle/cli-bundle.test.ts +199 -0
  90. package/test/e2e/cli-bundle.e2e.test.ts +237 -0
  91. package/test/unit/bin/affected-actors.test.ts +190 -0
  92. package/test/unit/bin/build-from-local.test.ts +55 -0
  93. package/test/unit/bin/build.test.ts +135 -3
  94. package/test/unit/bin/diff-changes.test.ts +87 -49
  95. package/test/unit/bin/git.test.ts +56 -53
  96. package/test/unit/bin/slack.test.ts +65 -0
  97. package/test/unit/should-built-and-test.test.ts +27 -17
  98. package/.github/scripts/check-package-version-bump.mjs +0 -74
  99. package/.github/workflows-package-version +0 -1
@@ -0,0 +1,169 @@
1
+ import { execFileSync, spawnSync } from 'node:child_process';
2
+ import { mkdir, mkdtemp, rm, writeFile } from 'node:fs/promises';
3
+ import os from 'node:os';
4
+ import path from 'node:path';
5
+ import process from 'node:process';
6
+ import { fileURLToPath } from 'node:url';
7
+ import { afterAll, beforeAll, describe, expect, it } from 'vitest';
8
+ // Runs the committed bundle the composite action ships, not the sources, so these catch anything
9
+ // that only breaks once bundled (a dependency loading code at runtime, a missing shim). Everything
10
+ // here works without tokens or network: read-only commands, dry runs that end up with nothing to
11
+ // build, and the missing-token error. The `CLI bundle` check keeps the bundle in sync with bin/.
12
+ const bundlePath = fileURLToPath(new URL('../../.github/cli/apify-test-tools.mjs', import.meta.url));
13
+ // Only what node and git need, so no Apify or Slack token from the developer's shell leaks in.
14
+ const cliEnv = { PATH: process.env.PATH ?? '', HOME: os.tmpdir() };
15
+ const gitEnv = {
16
+ ...cliEnv,
17
+ GIT_AUTHOR_NAME: 'Tester',
18
+ GIT_AUTHOR_EMAIL: 'tester@example.com',
19
+ GIT_COMMITTER_NAME: 'Tester',
20
+ GIT_COMMITTER_EMAIL: 'tester@example.com',
21
+ };
22
+ let repoDir;
23
+ let baseSha;
24
+ let headSha;
25
+ const git = (...args) => execFileSync('git', args, { cwd: repoDir, env: gitEnv, encoding: 'utf8' }).trim();
26
+ const writeRepoFile = async (file, content) => {
27
+ await mkdir(path.dirname(path.join(repoDir, file)), { recursive: true });
28
+ await writeFile(path.join(repoDir, file), content);
29
+ };
30
+ const runCli = (...args) => {
31
+ const result = spawnSync(process.execPath, [bundlePath, ...args], { cwd: repoDir, env: cliEnv, encoding: 'utf8' });
32
+ return { status: result.status, stdout: result.stdout, stderr: result.stderr };
33
+ };
34
+ const gitRange = ['--source-branch', 'feature', '--target-branch', 'main'];
35
+ describe('CLI bundle', () => {
36
+ // Two Actors on `main`; `feature` changes only tester/foo and the root README.
37
+ beforeAll(async () => {
38
+ repoDir = await mkdtemp(path.join(os.tmpdir(), 'apify-cli-bundle-'));
39
+ git('init', '-q', '-b', 'main');
40
+ git('remote', 'add', 'origin', 'https://github.com/apify/fixture-repo.git');
41
+ for (const name of ['foo', 'bar']) {
42
+ await writeRepoFile(`actors/${name}/.actor/actor.json`, JSON.stringify({ actorSpecification: 1, name, version: '0.1' }));
43
+ }
44
+ await writeRepoFile('apify-test-tools.config.json', JSON.stringify({
45
+ actors: [
46
+ { folder: 'actors/foo', actorFullName: 'tester/foo', tokenEnvVar: 'FOO_TOKEN' },
47
+ { folder: 'actors/bar', actorFullName: 'tester/bar', tokenEnvVar: 'BAR_TOKEN' },
48
+ ],
49
+ }));
50
+ git('add', '-A');
51
+ git('commit', '-q', '-m', 'chore: init');
52
+ baseSha = git('rev-parse', 'HEAD');
53
+ git('checkout', '-q', '-b', 'feature');
54
+ await writeRepoFile('actors/foo/main.js', 'console.log("changed");\n');
55
+ git('add', '-A');
56
+ git('commit', '-q', '-m', 'fix(foo): change foo');
57
+ await writeRepoFile('README.md', 'readme\n');
58
+ git('add', '-A');
59
+ git('commit', '-q', '-m', 'docs: readme');
60
+ headSha = git('rev-parse', 'HEAD');
61
+ });
62
+ afterAll(async () => {
63
+ if (repoDir)
64
+ await rm(repoDir, { recursive: true, force: true });
65
+ });
66
+ it('lists every command in --help', () => {
67
+ const { status, stdout } = runCli('--help');
68
+ expect(status).toBe(0);
69
+ for (const command of [
70
+ 'get-commits',
71
+ 'get-latest-commit',
72
+ 'get-changed-files',
73
+ 'get-actor-configs',
74
+ 'get-affected-actors',
75
+ 'report-tests',
76
+ 'build',
77
+ 'release',
78
+ 'build-from-local',
79
+ 'delete-old-builds',
80
+ ]) {
81
+ expect(stdout).toContain(command);
82
+ }
83
+ });
84
+ it('get-commits returns the commits on the source branch', () => {
85
+ const { status, stdout, stderr } = runCli('get-commits', ...gitRange);
86
+ expect(status, stderr).toBe(0);
87
+ expect(JSON.parse(stdout).map((commit) => commit.message)).toEqual([
88
+ 'fix(foo): change foo',
89
+ 'docs: readme',
90
+ ]);
91
+ });
92
+ it('get-latest-commit returns the head of the source branch', () => {
93
+ const { status, stdout, stderr } = runCli('get-latest-commit', ...gitRange);
94
+ expect(status, stderr).toBe(0);
95
+ expect(JSON.parse(stdout)).toMatchObject({ sha: headSha, message: 'docs: readme' });
96
+ });
97
+ it('get-changed-files returns the files changed on the source branch', () => {
98
+ const { status, stdout, stderr } = runCli('get-changed-files', ...gitRange);
99
+ expect(status, stderr).toBe(0);
100
+ expect(JSON.parse(stdout)).toEqual(['README.md', 'actors/foo/main.js']);
101
+ });
102
+ it('get-actor-configs reads both Actors from the config file', () => {
103
+ const { status, stdout, stderr } = runCli('get-actor-configs');
104
+ expect(status, stderr).toBe(0);
105
+ expect(JSON.parse(stdout).map((actor) => actor.actorFullName)).toEqual([
106
+ 'tester/foo',
107
+ 'tester/bar',
108
+ ]);
109
+ });
110
+ it('get-affected-actors returns only the Actor whose files changed', () => {
111
+ // `feature` is checked out, and the range always ends at HEAD
112
+ const { status, stdout, stderr } = runCli('get-affected-actors', '--base', 'main');
113
+ expect(status, stderr).toBe(0);
114
+ expect(JSON.parse(stdout)).toMatchObject({
115
+ version: 1,
116
+ head: headSha,
117
+ functionallyChangedActors: [{ actorFullName: 'tester/foo' }],
118
+ // The root README isn't in tester/bar's folder, so it doesn't count for it
119
+ cosmeticallyChangedActors: [],
120
+ });
121
+ });
122
+ it('build --dry-run with nothing to build needs no token', () => {
123
+ const { status, stdout, stderr } = runCli('build', ...gitRange, '--actors', 'tester/bar', '--dry-run');
124
+ expect(status, stderr).toBe(0);
125
+ expect(JSON.parse(stdout)).toEqual({ dryRun: true, builds: [] });
126
+ });
127
+ it('build --dry-run fails cleanly when the Actor token is missing', () => {
128
+ const { status, stderr } = runCli('build', ...gitRange, '--dry-run');
129
+ expect(status).toBe(1);
130
+ expect(stderr).toContain('Env var FOO_TOKEN is not set (needed for actor "tester/foo")');
131
+ });
132
+ it('release --dry-run stops when HEAD is the base commit', () => {
133
+ const { status, stderr } = runCli('release', '--base-commit', headSha, '--dry-run');
134
+ expect(status, stderr).toBe(0);
135
+ expect(stderr).toContain(`HEAD is the base commit ${headSha}, there is nothing new to release`);
136
+ });
137
+ it('release --dry-run with nothing to build skips Slack without a token', () => {
138
+ const { status, stderr } = runCli('release', '--base-commit', baseSha, '--actors', 'tester/bar', '--dry-run');
139
+ expect(status, stderr).toBe(0);
140
+ expect(stderr).toContain('{"dryRun":true,"builds":[]}');
141
+ expect(stderr).toContain('Skipping slack notification');
142
+ });
143
+ it('report-tests summarizes a vitest JSON report', async () => {
144
+ const assertion = (title, status, runId) => ({
145
+ ancestorTitles: [],
146
+ fullName: `foo ${title}`,
147
+ title,
148
+ status,
149
+ failureMessages: status === 'failed' ? ['AssertionError: expected 1 to be 2\n at foo.test.ts:1:1'] : [],
150
+ meta: { runId, runLink: `https://console.apify.com/view/runs/${runId}`, actorId: 'tester/foo' },
151
+ });
152
+ await writeRepoFile('test-output.json', JSON.stringify({
153
+ success: false,
154
+ testResults: [
155
+ {
156
+ name: 'test/platform/foo.test.ts',
157
+ status: 'failed',
158
+ assertionResults: [assertion('works', 'passed', 'run1'), assertion('fails', 'failed', 'run2')],
159
+ },
160
+ ],
161
+ }));
162
+ const { status, stderr } = runCli('report-tests', '--report-file', 'test-output.json', '--dry-run');
163
+ expect(status, stderr).toBe(0);
164
+ expect(stderr).toContain('PASSED: 1, FAILED: 1');
165
+ expect(stderr).toContain('1) foo fails ... https://console.apify.com/view/runs/run2');
166
+ expect(stderr).toContain('Skipping slack notification');
167
+ });
168
+ });
169
+ //# sourceMappingURL=cli-bundle.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli-bundle.test.js","sourceRoot":"","sources":["../../../test/bundle/cli-bundle.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=cli-bundle.e2e.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli-bundle.e2e.test.d.ts","sourceRoot":"","sources":["../../../test/e2e/cli-bundle.e2e.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,172 @@
1
+ import { execFileSync, spawnSync } from 'node:child_process';
2
+ import { mkdir, mkdtemp, readFile, rm, symlink, writeFile } from 'node:fs/promises';
3
+ import os from 'node:os';
4
+ import path from 'node:path';
5
+ import process from 'node:process';
6
+ import { fileURLToPath } from 'node:url';
7
+ import { ApifyClient } from 'apify-client';
8
+ import { afterAll, beforeAll, describe, expect, it } from 'vitest';
9
+ // End-to-end run of the shipped CLI bundle against two dummy Actors in an isolated sandbox account:
10
+ // dry-run plans read the real Actors, `build-from-local` really builds them, the lib runs them
11
+ // through `testActor` on exactly those builds (the ACTOR_BUILDS handoff), `report-tests` reads the
12
+ // result, and `delete-old-builds` cleans up. Git builds (`build`/`release` without --dry-run) are
13
+ // not covered: the Apify builder would have to clone the fixture repo, which only exists locally.
14
+ //
15
+ // Runs with `npm run test:e2e`, kept out of `npm test`. Skipped without the token, e.g. on fork PRs.
16
+ const TOKEN_ENV_VAR = 'E2E_TEST_APIFY_TOKEN_DBYPODOIHUZABNQTR';
17
+ const token = process.env[TOKEN_ENV_VAR];
18
+ const ACTORS = ['test-tools-testing-sandbox/dummy-for-testing-1', 'test-tools-testing-sandbox/dummy-for-testing-2'];
19
+ // Only used as the source URL in dry-run plans; nothing is cloned from it.
20
+ const REPO_URL = 'https://github.com/apify/apify-test-tools-e2e-fixture';
21
+ const projectRoot = fileURLToPath(new URL('../../', import.meta.url));
22
+ const bundlePath = path.join(projectRoot, '.github/cli/apify-test-tools.mjs');
23
+ const vitestPath = path.join(projectRoot, 'node_modules/vitest/vitest.mjs');
24
+ // Proxy and CA settings pass through so the API is reachable behind a proxy; no other variable does.
25
+ const networkEnv = Object.fromEntries(['HTTPS_PROXY', 'https_proxy', 'HTTP_PROXY', 'http_proxy', 'NO_PROXY', 'no_proxy', 'NODE_EXTRA_CA_CERTS']
26
+ .filter((name) => process.env[name] !== undefined)
27
+ .map((name) => [name, process.env[name]]));
28
+ const baseEnv = { PATH: process.env.PATH ?? '', HOME: os.tmpdir() };
29
+ // The CLI gets only the sandbox token, under the name the fixture config points at.
30
+ const cliEnv = { ...baseEnv, ...networkEnv, [TOKEN_ENV_VAR]: token ?? '' };
31
+ const gitEnv = {
32
+ ...baseEnv,
33
+ GIT_AUTHOR_NAME: 'Tester',
34
+ GIT_AUTHOR_EMAIL: 'tester@example.com',
35
+ GIT_COMMITTER_NAME: 'Tester',
36
+ GIT_COMMITTER_EMAIL: 'tester@example.com',
37
+ };
38
+ let repoDir;
39
+ let baseSha;
40
+ const git = (...args) => execFileSync('git', args, { cwd: repoDir, env: gitEnv, encoding: 'utf8' }).trim();
41
+ const writeRepoFile = async (file, content) => {
42
+ await mkdir(path.dirname(path.join(repoDir, file)), { recursive: true });
43
+ await writeFile(path.join(repoDir, file), content);
44
+ };
45
+ // Spawned processes are blocking, so vitest's own timeouts can't stop one that hangs. apify-client
46
+ // retries an unreachable API for minutes, so a hang is killed here and fails with a clear error.
47
+ const PROCESS_TIMEOUT_MS = 8 * 60_000;
48
+ const runCli = (...args) => {
49
+ const result = spawnSync(process.execPath, [bundlePath, ...args], {
50
+ cwd: repoDir,
51
+ env: cliEnv,
52
+ encoding: 'utf8',
53
+ timeout: PROCESS_TIMEOUT_MS,
54
+ });
55
+ const error = result.error ? `\n${result.error.message}` : '';
56
+ return { status: result.status, stdout: result.stdout, stderr: result.stderr + error };
57
+ };
58
+ describe.runIf(token)('CLI bundle against real Actors', { timeout: 2 * 60_000 }, () => {
59
+ let builds;
60
+ // Both Actors on `main`; `feature` changes only dummy-for-testing-1.
61
+ beforeAll(async () => {
62
+ repoDir = await mkdtemp(path.join(os.tmpdir(), 'apify-cli-e2e-'));
63
+ git('init', '-q', '-b', 'main');
64
+ git('remote', 'add', 'origin', `${REPO_URL}.git`);
65
+ const actors = [];
66
+ for (const [i, actorFullName] of ACTORS.entries()) {
67
+ const folder = `actors/dummy-${i + 1}`;
68
+ await writeRepoFile(`${folder}/.actor/actor.json`, JSON.stringify({
69
+ actorSpecification: 1,
70
+ name: actorFullName.split('/')[1],
71
+ version: '0.0',
72
+ dockerfile: '../Dockerfile',
73
+ }));
74
+ await writeRepoFile(`${folder}/Dockerfile`, 'FROM apify/actor-node:22\nCOPY . ./\nCMD ["node", "main.js"]\n');
75
+ await writeRepoFile(`${folder}/main.js`, `console.log('dummy ${i + 1}');\n`);
76
+ actors.push({ folder, actorFullName, tokenEnvVar: TOKEN_ENV_VAR });
77
+ }
78
+ await writeRepoFile('apify-test-tools.config.json', JSON.stringify({ actors }));
79
+ git('add', '-A');
80
+ git('commit', '-q', '-m', 'chore: init');
81
+ baseSha = git('rev-parse', 'HEAD');
82
+ git('checkout', '-q', '-b', 'feature');
83
+ await writeRepoFile('actors/dummy-1/main.js', "console.log('dummy 1 changed');\n");
84
+ git('add', '-A');
85
+ git('commit', '-q', '-m', 'fix(dummy-1): change');
86
+ });
87
+ afterAll(async () => {
88
+ if (repoDir)
89
+ await rm(repoDir, { recursive: true, force: true });
90
+ });
91
+ it('build --dry-run plans a test build of the changed Actor only', () => {
92
+ const { status, stdout, stderr } = runCli('build', '--source-branch', 'feature', '--target-branch', 'main', '--repo-url', REPO_URL, '--dry-run');
93
+ expect(status, stderr).toBe(0);
94
+ expect(JSON.parse(stdout)).toEqual({
95
+ dryRun: true,
96
+ builds: [
97
+ {
98
+ actorFullName: ACTORS[0],
99
+ versionNumber: '0.99',
100
+ gitRepoUrl: `${REPO_URL}#feature:actors/dummy-1`,
101
+ useDockerCache: false,
102
+ },
103
+ ],
104
+ });
105
+ });
106
+ it('release --dry-run plans a build of the default version and tag', () => {
107
+ const { status, stderr } = runCli('release', '--base-commit', baseSha, '--repo-url', REPO_URL, '--dry-run');
108
+ expect(status, stderr).toBe(0);
109
+ expect(stderr).toContain(`[DRY RUN] Would build:\n ${ACTORS[0]} (0.0) from ${REPO_URL}#feature:actors/dummy-1`);
110
+ expect(stderr).toContain('Skipping slack notification');
111
+ });
112
+ it('build-from-local --dry-run plans both Actors from the working tree', () => {
113
+ const { status, stdout, stderr } = runCli('build-from-local', '--dry-run');
114
+ expect(status, stderr).toBe(0);
115
+ expect(JSON.parse(stdout)).toMatchObject({
116
+ dryRun: true,
117
+ builds: ACTORS.map((actorFullName) => ({ actorFullName, versionNumber: '0.98', fileCount: 3 })),
118
+ });
119
+ });
120
+ it('build-from-local builds both Actors', { timeout: 10 * 60_000 }, () => {
121
+ const { status, stdout, stderr } = runCli('build-from-local');
122
+ expect(status, stderr).toBe(0);
123
+ builds = JSON.parse(stdout);
124
+ expect(builds.map((build) => build.actorFullName)).toEqual(ACTORS);
125
+ for (const build of builds)
126
+ expect(build.buildNumber).toMatch(/^0\.98\.\d+$/);
127
+ });
128
+ it('runs the builds through testActor and reports the results', { timeout: 10 * 60_000 }, async () => {
129
+ expect(builds, 'needs the builds from the previous test').toBeDefined();
130
+ // A consumer's platform test, importing the lib from this checkout. The symlink lets it
131
+ // resolve vitest and the lib's dependencies the way an installed package would.
132
+ await symlink(path.join(projectRoot, 'node_modules'), path.join(repoDir, 'node_modules'), 'dir');
133
+ const libPath = path.join(projectRoot, 'index.ts');
134
+ const actorTests = ACTORS.map((actorId) => `
135
+ testActor(${JSON.stringify(actorId)}, 'finishes', async ({ expect, run }) => {
136
+ const result = await run({ input: {} });
137
+ expect(result.status).toBe('SUCCEEDED');
138
+ });`).join('\n');
139
+ await writeRepoFile('test/platform/dummies.test.ts', `import { describe, testActor } from ${JSON.stringify(libPath)};
140
+
141
+ describe('sandbox dummies', () => {${actorTests}
142
+ });
143
+ `);
144
+ const vitest = spawnSync(process.execPath, [vitestPath, 'run', 'test/platform', '--reporter=json', '--outputFile=./test-output.json'], {
145
+ cwd: repoDir,
146
+ encoding: 'utf8',
147
+ timeout: PROCESS_TIMEOUT_MS,
148
+ env: { ...cliEnv, TESTER_APIFY_TOKEN: token, ACTOR_BUILDS: JSON.stringify(builds) },
149
+ });
150
+ expect(vitest.status, `${vitest.stdout}\n${vitest.stderr}${vitest.error ? `\n${vitest.error.message}` : ''}`).toBe(0);
151
+ // Each run used the build from ACTOR_BUILDS, not the Actor's default build.
152
+ const report = JSON.parse(await readFile(path.join(repoDir, 'test-output.json'), 'utf8'));
153
+ const assertions = report.testResults.flatMap((file) => file.assertionResults);
154
+ expect(assertions).toHaveLength(ACTORS.length);
155
+ const client = new ApifyClient({ token });
156
+ const runBuildIds = await Promise.all(assertions.map(async ({ meta }) => (await client.run(meta.runId).get())?.buildId));
157
+ expect(runBuildIds.sort()).toEqual(builds.map((build) => build.buildId).sort());
158
+ const { status, stderr } = runCli('report-tests', '--report-file', 'test-output.json', '--dry-run');
159
+ expect(status, stderr).toBe(0);
160
+ expect(stderr).toContain(`PASSED: ${ACTORS.length}, FAILED: 0`);
161
+ expect(stderr).toContain('https://console.apify.com/view/runs/');
162
+ });
163
+ // Last, so builds from earlier runs of this suite don't pile up in the sandbox.
164
+ it('delete-old-builds keeps the default builds', () => {
165
+ const { status, stderr } = runCli('delete-old-builds');
166
+ expect(status, stderr).toBe(0);
167
+ for (const actorFullName of ACTORS) {
168
+ expect(stderr).toContain(`[DELETE OLD BUILDS][${actorFullName}]: Skipping default build`);
169
+ }
170
+ });
171
+ });
172
+ //# sourceMappingURL=cli-bundle.e2e.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli-bundle.e2e.test.js","sourceRoot":"","sources":["../../../test/e2e/cli-bundle.e2e.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=affected-actors.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"affected-actors.test.d.ts","sourceRoot":"","sources":["../../../../test/unit/bin/affected-actors.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,160 @@
1
+ import { beforeEach, describe, expect, it, vi } from 'vitest';
2
+ import { getAffectedActors, toAffectedActorsDocument } from '../../../bin/affected-actors.js';
3
+ import * as DiffChanges from '../../../bin/diff-changes.js';
4
+ import * as Git from '../../../bin/git.js';
5
+ import * as LoadConfig from '../../../bin/utils/config/load-config.js';
6
+ // Everything that reads the configs is mocked, so only the identity and name of an Actor matter here
7
+ const actorA = { actorFullName: 'owner/a' };
8
+ const actorB = { actorFullName: 'owner/b' };
9
+ const headSha = 'f'.repeat(40);
10
+ const sinceSha = '5'.repeat(40);
11
+ const commit = (sha) => ({ sha, author: 'Dev<dev@example.com>', date: 'Date', message: 'msg' });
12
+ const commits = [commit('1'.repeat(40)), commit('2'.repeat(40))];
13
+ const changedFiles = ['src/main.ts', 'CHANGELOG.md'];
14
+ const selection = { actors: [], ignore: [] };
15
+ describe('getAffectedActors', () => {
16
+ let changedActorsSpy;
17
+ beforeEach(() => {
18
+ vi.spyOn(LoadConfig, 'readConfigFile').mockResolvedValue([actorA, actorB]);
19
+ changedActorsSpy = vi
20
+ .spyOn(DiffChanges, 'getChangedActors')
21
+ .mockResolvedValue({ functional: [actorA], cosmetic: [actorB] });
22
+ vi.spyOn(Git, 'resolveCommitSha').mockResolvedValue(headSha);
23
+ vi.spyOn(Git, 'resolveBranchName').mockResolvedValue('feature');
24
+ vi.spyOn(Git, 'getChangedFilesBetween').mockResolvedValue(changedFiles);
25
+ vi.spyOn(Git, 'getChangelogAdditions').mockResolvedValue('- Added foo');
26
+ });
27
+ it('requires --base or --since', async () => {
28
+ const options = { head: 'HEAD', ...selection };
29
+ await expect(getAffectedActors(options)).rejects.toThrow('Pass --base, --since, or both');
30
+ // An empty --since (e.g. from an empty cache file) counts as not passed
31
+ await expect(getAffectedActors({ ...options, since: '' })).rejects.toThrow('Pass --base, --since, or both');
32
+ });
33
+ describe('with --base', () => {
34
+ let getCommitsSpy;
35
+ beforeEach(() => {
36
+ vi.spyOn(Git, 'hasMergeFromTarget').mockResolvedValue(false);
37
+ getCommitsSpy = vi.spyOn(Git, 'getCommits').mockResolvedValue(commits);
38
+ });
39
+ it('inspects the branch commits after --since', async () => {
40
+ const result = await getAffectedActors({
41
+ head: 'origin/feature',
42
+ base: 'origin/master',
43
+ since: sinceSha,
44
+ ...selection,
45
+ });
46
+ expect(result).toStrictEqual({
47
+ headSha,
48
+ ref: 'feature',
49
+ commits,
50
+ changedFiles,
51
+ changelog: '- Added foo',
52
+ functionallyChangedActors: [actorA],
53
+ cosmeticallyChangedActors: [actorB],
54
+ });
55
+ expect(getCommitsSpy).toHaveBeenCalledWith({
56
+ sourceBranch: 'origin/feature',
57
+ targetBranch: 'origin/master',
58
+ baseCommit: sinceSha,
59
+ });
60
+ // From the parent of the oldest commit to the newest one, for the files and the changelog alike
61
+ expect(Git.getChangedFilesBetween).toHaveBeenCalledWith(`${commits[0].sha}~`, commits[1].sha);
62
+ expect(Git.getChangelogAdditions).toHaveBeenCalledWith(`${commits[0].sha}~`, commits[1].sha, changedFiles);
63
+ expect(changedActorsSpy).toHaveBeenCalledWith({
64
+ filepathsChanged: changedFiles,
65
+ actorConfigs: [actorA, actorB],
66
+ commits,
67
+ });
68
+ });
69
+ describe('when the branch merged --base in', () => {
70
+ beforeEach(() => {
71
+ vi.spyOn(Git, 'hasMergeFromTarget').mockResolvedValue(true);
72
+ vi.spyOn(Git, 'getBranchOnlyChangedFiles').mockResolvedValue(['own-file']);
73
+ });
74
+ const getWithOwnChanges = async (own) => {
75
+ changedActorsSpy.mockImplementation(async ({ filepathsChanged }) => filepathsChanged[0] === 'own-file' ? own : { functional: [actorA], cosmetic: [actorB] });
76
+ return getAffectedActors({ head: 'feature', base: 'master', since: sinceSha, ...selection });
77
+ };
78
+ it("classifies the branch's own files against the whole branch, ignoring --since", async () => {
79
+ await getWithOwnChanges({ functional: [actorA], cosmetic: [] });
80
+ expect(changedActorsSpy).toHaveBeenCalledWith({
81
+ filepathsChanged: ['own-file'],
82
+ actorConfigs: [actorA, actorB],
83
+ commits,
84
+ });
85
+ expect(getCommitsSpy).toHaveBeenCalledWith({ sourceBranch: 'feature', targetBranch: 'master' });
86
+ });
87
+ it('reports everything when the branch has functional changes of its own', async () => {
88
+ const result = await getWithOwnChanges({ functional: [actorA], cosmetic: [] });
89
+ expect(result.functionallyChangedActors).toStrictEqual([actorA]);
90
+ expect(result.cosmeticallyChangedActors).toStrictEqual([actorB]);
91
+ });
92
+ it('reports only cosmetic Actors when its own changes are only cosmetic', async () => {
93
+ const result = await getWithOwnChanges({ functional: [], cosmetic: [actorB] });
94
+ expect(result.functionallyChangedActors).toStrictEqual([]);
95
+ expect(result.cosmeticallyChangedActors).toStrictEqual([actorB]);
96
+ });
97
+ it('reports nothing when it has no changes of its own', async () => {
98
+ const result = await getWithOwnChanges({ functional: [], cosmetic: [] });
99
+ expect(result.functionallyChangedActors).toStrictEqual([]);
100
+ expect(result.cosmeticallyChangedActors).toStrictEqual([]);
101
+ // The range is still reported
102
+ expect(result.commits).toStrictEqual(commits);
103
+ });
104
+ });
105
+ });
106
+ describe('with --since only', () => {
107
+ let resolveBaseSpy;
108
+ let commitsBetweenSpy;
109
+ beforeEach(() => {
110
+ resolveBaseSpy = vi.spyOn(Git, 'resolveReleaseBaseCommit').mockResolvedValue(sinceSha);
111
+ commitsBetweenSpy = vi.spyOn(Git, 'getCommitsBetween').mockResolvedValue(commits);
112
+ });
113
+ it('inspects everything after --since up to --head', async () => {
114
+ const result = await getAffectedActors({ head: 'release', since: 'HEAD~2', ...selection });
115
+ expect(result).toStrictEqual({
116
+ headSha,
117
+ ref: 'feature',
118
+ commits,
119
+ changedFiles,
120
+ changelog: '- Added foo',
121
+ functionallyChangedActors: [actorA],
122
+ cosmeticallyChangedActors: [actorB],
123
+ });
124
+ expect(resolveBaseSpy).toHaveBeenCalledWith('HEAD~2', 'release');
125
+ expect(commitsBetweenSpy).toHaveBeenCalledWith(sinceSha, headSha);
126
+ expect(Git.getChangedFilesBetween).toHaveBeenCalledWith(sinceSha, headSha);
127
+ expect(Git.getChangelogAdditions).toHaveBeenCalledWith(sinceSha, headSha, changedFiles);
128
+ });
129
+ it('has no commits when head is the --since commit', async () => {
130
+ resolveBaseSpy.mockResolvedValue(headSha);
131
+ const result = await getAffectedActors({ head: 'HEAD', since: headSha, ...selection });
132
+ expect(result.commits).toStrictEqual([]);
133
+ // Empty `git log x..x` output doesn't parse as a commit, so it's never run
134
+ expect(commitsBetweenSpy).not.toHaveBeenCalled();
135
+ });
136
+ });
137
+ });
138
+ describe('toAffectedActorsDocument', () => {
139
+ it('keeps only the Actor names, the config file stays the source of truth for the rest', () => {
140
+ expect(toAffectedActorsDocument({
141
+ headSha,
142
+ ref: null,
143
+ commits,
144
+ changedFiles: ['src/main.ts'],
145
+ changelog: null,
146
+ functionallyChangedActors: [actorA],
147
+ cosmeticallyChangedActors: [actorB],
148
+ })).toStrictEqual({
149
+ version: 1,
150
+ ref: null,
151
+ head: headSha,
152
+ commits,
153
+ changedFiles: ['src/main.ts'],
154
+ changelog: null,
155
+ functionallyChangedActors: [{ actorFullName: 'owner/a' }],
156
+ cosmeticallyChangedActors: [{ actorFullName: 'owner/b' }],
157
+ });
158
+ });
159
+ });
160
+ //# sourceMappingURL=affected-actors.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"affected-actors.test.js","sourceRoot":"","sources":["../../../../test/unit/bin/affected-actors.test.ts"],"names":[],"mappings":"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"}
@@ -2,7 +2,8 @@ import fs from 'node:fs/promises';
2
2
  import os from 'node:os';
3
3
  import path from 'node:path';
4
4
  import { afterEach, describe, expect, it, vi } from 'vitest';
5
- import { collectNonIgnoredFiles, collectSourceFiles, flattenMonorepoContext, rewriteActorJsonPaths,
5
+ import { ApifyBuilder } from '../../../bin/build.js';
6
+ import { collectNonIgnoredFiles, collectSourceFiles, flattenMonorepoContext, rewriteActorJsonPaths, runBuildsFromLocal,
6
7
  // eslint-disable-next-line @typescript-eslint/ban-ts-comment
7
8
  // @ts-ignore: editor-only TS6059 — test/tsconfig.json's rootDir doesn't span bin/, but the root
8
9
  // tsconfig (used for the real build and for eslint's type-aware linting) has no such restriction.
@@ -179,5 +180,56 @@ describe('build-from-local helpers', () => {
179
180
  expect(rewritten.changelog).toBe('./CHANGELOG.md');
180
181
  });
181
182
  });
183
+ describe('runBuildsFromLocal dry run', () => {
184
+ it('looks the Actor up and zips its sources, but builds nothing', async () => {
185
+ const repoRoot = await mkTempDir('apify-test-tools-dry-run-');
186
+ tempDirs.push(repoRoot);
187
+ const actorDir = path.join(repoRoot, 'actors', 'owner_actor');
188
+ await fs.mkdir(path.join(actorDir, '.actor'), { recursive: true });
189
+ await fs.writeFile(path.join(actorDir, '.actor', 'actor.json'), JSON.stringify({ name: 'actor' }));
190
+ await fs.writeFile(path.join(actorDir, 'main.js'), '');
191
+ vi.spyOn(Utils, 'listRepoFilePaths').mockResolvedValue([
192
+ 'actors/owner_actor/.actor/actor.json',
193
+ 'actors/owner_actor/main.js',
194
+ ]);
195
+ vi.spyOn(Utils, 'getGitignoredPaths').mockResolvedValue(new Set());
196
+ const getActorInfo = vi.fn(async () => ({}));
197
+ const fromActorConfig = vi
198
+ .spyOn(ApifyBuilder, 'fromActorConfig')
199
+ .mockReturnValue({ getActorInfo });
200
+ const originalCwd = process.cwd();
201
+ process.chdir(repoRoot);
202
+ try {
203
+ const plan = await runBuildsFromLocal({
204
+ actorConfigs: [
205
+ {
206
+ actorFullName: 'owner/actor',
207
+ folder: new ExistingDir('actors/owner_actor'),
208
+ tokenEnvVar: 'APIFY_TOKEN_OWNER',
209
+ actorJson: {
210
+ file: new ExistingFile('actors/owner_actor/.actor/actor.json'),
211
+ dockerContextDir: new RelativeDir('actors/owner_actor'),
212
+ },
213
+ dockerContextDir: new RelativeDir('actors/owner_actor'),
214
+ contextPaths: [new RelativeDir('actors/owner_actor')],
215
+ },
216
+ ],
217
+ dryRun: true,
218
+ });
219
+ expect(plan).toEqual({
220
+ dryRun: true,
221
+ builds: [
222
+ expect.objectContaining({ actorFullName: 'owner/actor', versionNumber: '0.98', fileCount: 2 }),
223
+ ],
224
+ });
225
+ }
226
+ finally {
227
+ process.chdir(originalCwd);
228
+ }
229
+ expect(getActorInfo).toHaveBeenCalledTimes(1);
230
+ // Only the read-only lookup ran: builders for uploads and builds are created after the dry-run cutoff
231
+ expect(fromActorConfig).toHaveBeenCalledTimes(1);
232
+ });
233
+ });
182
234
  });
183
235
  //# sourceMappingURL=build-from-local.test.js.map