apify-test-tools 0.6.4-beta.0 → 0.6.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.
- package/CHANGELOG.md +20 -0
- package/bin/diff-changes.ts +10 -9
- package/bin/git.ts +5 -45
- package/bin/main.ts +22 -46
- package/dist/bin/diff-changes.d.ts.map +1 -1
- package/dist/bin/diff-changes.js +10 -8
- package/dist/bin/diff-changes.js.map +1 -1
- package/dist/bin/git.d.ts +0 -11
- package/dist/bin/git.d.ts.map +1 -1
- package/dist/bin/git.js +3 -37
- package/dist/bin/git.js.map +1 -1
- package/dist/bin/main.js +22 -33
- package/dist/bin/main.js.map +1 -1
- package/dist/lib/lib.d.ts +5 -2
- package/dist/lib/lib.d.ts.map +1 -1
- package/dist/lib/lib.js +38 -28
- package/dist/lib/lib.js.map +1 -1
- package/dist/test/unit/bin/git.test.js +1 -68
- package/dist/test/unit/bin/git.test.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/lib/lib.ts +46 -32
- package/package.json +1 -1
- package/test/unit/bin/git.test.ts +1 -87
package/lib/lib.ts
CHANGED
|
@@ -92,29 +92,24 @@ export const testStandbyActor = <I = any, O = any>(
|
|
|
92
92
|
|
|
93
93
|
vitestTest.runIf(shouldRun)(name, options, async <T extends TestContext>(context: T) => {
|
|
94
94
|
const standbyTask = await createStandbyTask(actorName, config.get(actorName)?.buildNumber);
|
|
95
|
-
const { annotate } = context;
|
|
96
95
|
const { expect, ...rest } = context;
|
|
97
96
|
|
|
98
|
-
// NOTE: we
|
|
97
|
+
// NOTE: we wrap `fn` in try/finally so cleanup (deleting the task) always runs afterwards
|
|
99
98
|
try {
|
|
100
99
|
await fn({
|
|
101
100
|
expect: extendExpect(expect),
|
|
102
|
-
callStandby: createStartStandbyFn(standbyTask),
|
|
101
|
+
callStandby: createStartStandbyFn(standbyTask, context, name),
|
|
103
102
|
...rest,
|
|
104
103
|
});
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
if (taskId) {
|
|
117
|
-
await apifyClient.task(taskId).delete();
|
|
104
|
+
} finally {
|
|
105
|
+
const { taskId } = standbyTask;
|
|
106
|
+
// we want to delete the task at the end of the test
|
|
107
|
+
await apifyClient
|
|
108
|
+
.task(taskId)
|
|
109
|
+
.delete()
|
|
110
|
+
.catch((error) => {
|
|
111
|
+
console.error(`Failed to delete standby task "${taskId}": ${error}`);
|
|
112
|
+
});
|
|
118
113
|
}
|
|
119
114
|
});
|
|
120
115
|
};
|
|
@@ -129,7 +124,7 @@ export const testTestActor = <T>(
|
|
|
129
124
|
expect: extendExpect(expect),
|
|
130
125
|
// @ts-expect-error: this just to test custom matchers
|
|
131
126
|
// eslint-disable-next-line @typescript-eslint/no-empty-function
|
|
132
|
-
run: () => {},
|
|
127
|
+
run: () => { },
|
|
133
128
|
...rest,
|
|
134
129
|
});
|
|
135
130
|
});
|
|
@@ -138,20 +133,44 @@ export const testTestActor = <T>(
|
|
|
138
133
|
export const it = testActor;
|
|
139
134
|
|
|
140
135
|
/**
|
|
141
|
-
* Creates a function
|
|
136
|
+
* Creates a function that accepts input for a standby actor and sends a request containing the input
|
|
142
137
|
* to the task's standby url.
|
|
143
138
|
*/
|
|
144
|
-
const createStartStandbyFn = <I, O>(standbyTask: StandbyTask) => {
|
|
145
|
-
const { standbyUrl } = standbyTask;
|
|
146
|
-
|
|
147
|
-
|
|
139
|
+
const createStartStandbyFn = <I, O>(standbyTask: StandbyTask, { annotate }: TestContext, testName: string) => {
|
|
140
|
+
const { standbyUrl, taskId } = standbyTask;
|
|
141
|
+
const annotatedRuns = new Set<string>();
|
|
142
|
+
|
|
143
|
+
// We annotate all the runs of the task, though it will be only one run
|
|
144
|
+
// for most of the tests. To avoid annotating the same run multiple times
|
|
145
|
+
// (in case the test calls the standby more than once), we use `annotatedRuns`
|
|
146
|
+
// set to keep track of which runs have already been annotated.
|
|
147
|
+
const annotateStandbyRuns = async () => {
|
|
148
|
+
const runs = (await apifyClient.task(taskId).runs().list()).items;
|
|
149
|
+
for (const run of runs) {
|
|
150
|
+
if (!annotatedRuns.has(run.id)) {
|
|
151
|
+
const runLink = generateRunLink(run);
|
|
152
|
+
await annotate(`${testName} - ${runLink}`, 'run_link');
|
|
153
|
+
}
|
|
154
|
+
annotatedRuns.add(run.id);
|
|
155
|
+
}
|
|
156
|
+
};
|
|
157
|
+
|
|
158
|
+
return async ({
|
|
159
|
+
input,
|
|
160
|
+
path = '',
|
|
161
|
+
headers = {},
|
|
162
|
+
}: Pick<RunOptions<I>, 'input'> & { path?: string; headers?: Record<string, string> }) => {
|
|
163
|
+
const response = await fetch(standbyUrl + path, {
|
|
148
164
|
headers: {
|
|
165
|
+
...headers,
|
|
149
166
|
Authorization: `Bearer ${apifyClient.token}`,
|
|
150
167
|
},
|
|
151
168
|
method: 'POST',
|
|
152
169
|
body: JSON.stringify(input),
|
|
153
170
|
});
|
|
154
171
|
|
|
172
|
+
await annotateStandbyRuns();
|
|
173
|
+
|
|
155
174
|
const data = (await response.json()) as O;
|
|
156
175
|
return {
|
|
157
176
|
data,
|
|
@@ -166,10 +185,6 @@ interface StandbyTask {
|
|
|
166
185
|
taskId: string;
|
|
167
186
|
}
|
|
168
187
|
|
|
169
|
-
const randomInt = (min: number, max: number) => {
|
|
170
|
-
return Math.floor(Math.random() * (max - min + 1)) + min;
|
|
171
|
-
};
|
|
172
|
-
|
|
173
188
|
/**
|
|
174
189
|
* Creates a task with specific `build` - either `buildNumber` or default.
|
|
175
190
|
*
|
|
@@ -201,16 +216,15 @@ const createStandbyTask = async (actorNameOrId: string, buildNumber?: string): P
|
|
|
201
216
|
};
|
|
202
217
|
|
|
203
218
|
try {
|
|
204
|
-
const title = `Test task - ${build}
|
|
219
|
+
const title = `Test task - ${build}`.slice(0, 62);
|
|
220
|
+
const randomValueLength = 15;
|
|
205
221
|
// we try to create unique task name containing only `a-z0-9-` characters and at most 63 characters long
|
|
206
|
-
const
|
|
207
|
-
|
|
208
|
-
.replaceAll(/\s+/g, '')
|
|
209
|
-
.replaceAll(/[^a-z0-9-]+/g, '-')}`.slice(0, 62);
|
|
222
|
+
const randomValue = Math.random().toString(10).slice(2).padEnd(randomValueLength, '0');
|
|
223
|
+
const name = `test-${randomValue.slice(0, randomValueLength)}`;
|
|
210
224
|
const newTask = (await apifyClient.tasks().create({
|
|
211
225
|
actId: actorNameOrId,
|
|
212
226
|
actorStandby: actorStandbyOptions,
|
|
213
|
-
description: `Task for testing standby version ${build}`,
|
|
227
|
+
description: `Task for testing standby version ${build} of actor "${actorNameOrId}"`,
|
|
214
228
|
title,
|
|
215
229
|
name,
|
|
216
230
|
})) as Task & { standbyUrl?: string };
|
package/package.json
CHANGED
|
@@ -1,13 +1,7 @@
|
|
|
1
1
|
import type { MockInstance } from 'vitest';
|
|
2
2
|
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
|
3
3
|
|
|
4
|
-
import {
|
|
5
|
-
getBranchOnlyChangedFiles,
|
|
6
|
-
getChangedFiles,
|
|
7
|
-
getCommits,
|
|
8
|
-
hasMergeFromTarget,
|
|
9
|
-
parseBaseCommit,
|
|
10
|
-
} from '../../../bin/git.js';
|
|
4
|
+
import { getChangedFiles, getCommits, parseBaseCommit } from '../../../bin/git.js';
|
|
11
5
|
import * as Utils from '../../../bin/utils.js';
|
|
12
6
|
|
|
13
7
|
describe('getCommits', () => {
|
|
@@ -123,86 +117,6 @@ describe('getChangedFiles', () => {
|
|
|
123
117
|
});
|
|
124
118
|
});
|
|
125
119
|
|
|
126
|
-
describe('hasMergeFromTarget', () => {
|
|
127
|
-
const sourceBranch = 'feature-branch';
|
|
128
|
-
const targetBranch = 'main';
|
|
129
|
-
const mergeSha = 'f'.repeat(40);
|
|
130
|
-
const branchParentSha = 'b'.repeat(40);
|
|
131
|
-
const targetParentSha = 't'.repeat(40);
|
|
132
|
-
|
|
133
|
-
let gitCommandSpy: MockInstance;
|
|
134
|
-
|
|
135
|
-
beforeEach(() => {
|
|
136
|
-
gitCommandSpy = vi.spyOn(Utils, 'spawnCommandInGhWorkspace');
|
|
137
|
-
});
|
|
138
|
-
|
|
139
|
-
it('should return false when there are no merge commits on the branch', () => {
|
|
140
|
-
gitCommandSpy.mockImplementation((cmd: string) => {
|
|
141
|
-
if (cmd.includes('--merges')) return '';
|
|
142
|
-
return '';
|
|
143
|
-
});
|
|
144
|
-
|
|
145
|
-
expect(hasMergeFromTarget(sourceBranch, targetBranch)).toBe(false);
|
|
146
|
-
expect(gitCommandSpy).toHaveBeenCalledWith(
|
|
147
|
-
`git log --merges --pretty=format:%H ${targetBranch}..${sourceBranch}`,
|
|
148
|
-
);
|
|
149
|
-
});
|
|
150
|
-
|
|
151
|
-
it('should return true when a merge commit has a parent reachable from targetBranch', () => {
|
|
152
|
-
gitCommandSpy.mockImplementation((cmd: string) => {
|
|
153
|
-
if (cmd.includes('--merges')) return mergeSha;
|
|
154
|
-
if (cmd.includes('--pretty=format:%P')) return `${branchParentSha} ${targetParentSha}`;
|
|
155
|
-
if (cmd.startsWith(`git merge-base ${branchParentSha}`)) return branchParentSha; // not ancestor
|
|
156
|
-
if (cmd.startsWith(`git merge-base ${targetParentSha}`)) return targetParentSha; // is ancestor
|
|
157
|
-
return '';
|
|
158
|
-
});
|
|
159
|
-
|
|
160
|
-
expect(hasMergeFromTarget(sourceBranch, targetBranch)).toBe(true);
|
|
161
|
-
});
|
|
162
|
-
|
|
163
|
-
it('should return false when the merge commit parent is not reachable from targetBranch (unrelated branch merge)', () => {
|
|
164
|
-
const unrelatedSha = 'e'.repeat(40);
|
|
165
|
-
const differentMergeBase = '0'.repeat(40);
|
|
166
|
-
gitCommandSpy.mockImplementation((cmd: string) => {
|
|
167
|
-
if (cmd.includes('--merges')) return mergeSha;
|
|
168
|
-
if (cmd.includes('--pretty=format:%P')) return `${branchParentSha} ${unrelatedSha}`;
|
|
169
|
-
// merge-base returns something other than the parent — not an ancestor
|
|
170
|
-
if (cmd.startsWith('git merge-base')) return differentMergeBase;
|
|
171
|
-
return '';
|
|
172
|
-
});
|
|
173
|
-
|
|
174
|
-
expect(hasMergeFromTarget(sourceBranch, targetBranch)).toBe(false);
|
|
175
|
-
});
|
|
176
|
-
});
|
|
177
|
-
|
|
178
|
-
describe('getBranchOnlyChangedFiles', () => {
|
|
179
|
-
const sourceBranch = 'feature-branch';
|
|
180
|
-
const targetBranch = 'main';
|
|
181
|
-
|
|
182
|
-
let gitCommandSpy: MockInstance;
|
|
183
|
-
|
|
184
|
-
beforeEach(() => {
|
|
185
|
-
gitCommandSpy = vi.spyOn(Utils, 'spawnCommandInGhWorkspace');
|
|
186
|
-
});
|
|
187
|
-
|
|
188
|
-
it('should return files touched by non-merge commits', () => {
|
|
189
|
-
gitCommandSpy.mockReturnValue('README.md\n\nactors/foo_bar/src/main.ts\n');
|
|
190
|
-
|
|
191
|
-
const result = getBranchOnlyChangedFiles(sourceBranch, targetBranch);
|
|
192
|
-
|
|
193
|
-
expect(result).toStrictEqual(['README.md', 'actors/foo_bar/src/main.ts']);
|
|
194
|
-
expect(gitCommandSpy).toHaveBeenCalledWith(
|
|
195
|
-
`git log --no-merges --name-only --pretty=format: ${targetBranch}..${sourceBranch}`,
|
|
196
|
-
);
|
|
197
|
-
});
|
|
198
|
-
|
|
199
|
-
it('should return empty array when there are no non-merge commits', () => {
|
|
200
|
-
gitCommandSpy.mockReturnValue('');
|
|
201
|
-
|
|
202
|
-
expect(getBranchOnlyChangedFiles(sourceBranch, targetBranch)).toStrictEqual([]);
|
|
203
|
-
});
|
|
204
|
-
});
|
|
205
|
-
|
|
206
120
|
const VALID_SHA = 'a'.repeat(40);
|
|
207
121
|
const VALID_JSON = JSON.stringify({ sha: VALID_SHA, author: 'test', date: 'now', message: 'msg' });
|
|
208
122
|
|