Skip to content

Commit c0d5863

Browse files
feat(integ-runner): add --strict flag to throw error on missing tests (#929)
## Description of the change This PR adds a new flag `--strict` to integ-runner. When enabled, this flag enforces that all specified tests are found and can be run. With `strict` = `true`, integ-runner will throw an error when any specified tests are not found. ## Reason for this change For the [Integration Test Workflow](https://github.com/aws/aws-cdk/blob/main/.github/workflows/integration-test-deployment.yml), we want to throw an error if some specified tests are not found. This ensures that all tests that have been changed in a PR are actually run in the workflow. Currently, integ-runner does not throw an error when tests are not found. We can use the `--strict` flag in the workflow for our use case. ## Test Added unit tests to verify the functionality of the new flag. --- By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license.
1 parent c79e35e commit c0d5863

File tree

5 files changed

+40
-2
lines changed

5 files changed

+40
-2
lines changed

packages/@aws-cdk/integ-runner/README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,8 @@ If not, changes cannot be compared across systems and the [update workflow](#upd
7373
List of AWS Profiles to use when running tests in parallel
7474
- `--exclude` (default=`false`)
7575
If this is set to `true` then the list of tests provided will be excluded
76+
- `--strict` (default=`false`)
77+
Fail if any specified tests are not found. Cannot be used with `--exclude`
7678
- `--from-file`
7779
Read the list of tests from this file
7880
- `--disable-update-workflow` (default=`false`)

packages/@aws-cdk/integ-runner/lib/cli.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ export function parseCliArgs(args: string[] = []) {
3737
.options('profiles', { type: 'array', desc: 'list of AWS profiles to use. Tests will be run in parallel across each profile+regions', default: [] })
3838
.options('max-workers', { type: 'number', desc: 'The max number of workerpool workers to use when running integration tests in parallel', default: 16 })
3939
.options('exclude', { type: 'boolean', desc: 'Run all tests in the directory, except the specified TESTs', default: false })
40+
.option('strict', { type: 'boolean', default: false, desc: 'Fail if any specified tests are not found' })
4041
.options('from-file', { type: 'string', desc: 'Read TEST names from a file (one TEST per line)' })
4142
.option('inspect-failures', { type: 'boolean', desc: 'Keep the integ test cloud assembly if a failure occurs for inspection', default: false })
4243
.option('disable-update-workflow', { type: 'boolean', default: false, desc: 'If this is "true" then the stack update workflow will be disabled' })
@@ -71,6 +72,11 @@ export function parseCliArgs(args: string[] = []) {
7172
if (tests.length > 0 && fromFile) {
7273
throw new Error('A list of tests cannot be provided if "--from-file" is provided');
7374
}
75+
76+
if (argv.strict && argv.exclude) {
77+
throw new Error('Cannot use --strict with --exclude');
78+
}
79+
7480
const requestedTests = fromFile
7581
? (fs.readFileSync(fromFile, { encoding: 'utf8' })).split('\n').filter(x => x)
7682
: (tests.length > 0 ? tests : undefined); // 'undefined' means no request
@@ -97,6 +103,7 @@ export function parseCliArgs(args: string[] = []) {
97103
disableUpdateWorkflow: argv['disable-update-workflow'] as boolean,
98104
language: arrayFromYargs(argv.language),
99105
watch: argv.watch as boolean,
106+
strict: argv.strict as boolean,
100107
unstable: arrayFromYargs(argv.unstable) ?? [],
101108
};
102109
}

packages/@aws-cdk/integ-runner/lib/runner/integration-tests.ts

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,13 @@ export interface IntegrationTestsDiscoveryOptions {
159159
*/
160160
readonly exclude?: boolean;
161161

162+
/**
163+
* If this is set to true, throw an error if any specified tests are not found
164+
*
165+
* @default false
166+
*/
167+
readonly strict?: boolean;
168+
162169
/**
163170
* List of tests to include (or exclude if `exclude=true`)
164171
*
@@ -204,10 +211,12 @@ export class IntegrationTests {
204211
language?: string[];
205212
testRegex?: string[];
206213
tests?: string[];
214+
strict?: boolean;
207215
}): Promise<IntegTest[]> {
208216
const baseOptions = {
209217
tests: options.tests,
210218
exclude: options.exclude,
219+
strict: options.strict,
211220
};
212221

213222
// Explicitly set both, app and test-regex
@@ -283,7 +292,7 @@ export class IntegrationTests {
283292
* If they have provided a test name that we don't find, then we write out that error message.
284293
* - If it is a list of tests to exclude, then we discover all available tests and filter out the tests that were provided by the user.
285294
*/
286-
private filterTests(discoveredTests: IntegTest[], requestedTests?: string[], exclude?: boolean): IntegTest[] {
295+
private filterTests(discoveredTests: IntegTest[], requestedTests?: string[], exclude?: boolean, strict?: boolean): IntegTest[] {
287296
if (!requestedTests) {
288297
return discoveredTests;
289298
}
@@ -301,6 +310,9 @@ export class IntegrationTests {
301310
}
302311
if (unmatchedPatterns.length > 0) {
303312
process.stderr.write(`Available tests: ${discoveredTests.map(t => t.discoveryRelativeFileName).join(' ')}\n`);
313+
if (strict) {
314+
throw new Error(`Strict mode: ${unmatchedPatterns.length} test(s) not found: ${unmatchedPatterns.join(', ')}`);
315+
}
304316
return [];
305317
}
306318
}
@@ -333,7 +345,7 @@ export class IntegrationTests {
333345

334346
const discoveredTests = ignoreUncompiledTypeScript ? this.filterUncompiledTypeScript(testCases) : testCases;
335347

336-
return this.filterTests(discoveredTests, options.tests, options.exclude);
348+
return this.filterTests(discoveredTests, options.tests, options.exclude, options.strict);
337349
}
338350

339351
private filterUncompiledTypeScript(testCases: IntegTest[]): IntegTest[] {

packages/@aws-cdk/integ-runner/test/cli.test.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,15 @@ describe.each([
125125
'--app="node --prof {filePath}"',
126126
])).rejects.toThrow('Only a single "--language" can be used with "--app". Alternatively provide both "--app" and "--test-regex" to fully customize the configuration.');
127127
});
128+
129+
test('cannot use --strict with --exclude', async () => {
130+
await expect(() => cli([
131+
'xxxxx.integ-test1.js',
132+
'--language=javascript',
133+
'--strict',
134+
'--exclude',
135+
])).rejects.toThrow('Cannot use --strict with --exclude');
136+
});
128137
});
129138

130139
describe('CLI config file', () => {

packages/@aws-cdk/integ-runner/test/runner/integration-tests.test.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,14 @@ describe('IntegrationTests Discovery', () => {
8282
);
8383
});
8484

85+
test('test not found throws error only in Strict mode', async () => {
86+
const testNames = [`test-data/${namedTest}`, `test-data/${namedTest.replace('test1', 'nonexistent')}`];
87+
88+
await expect(() => tests.fromCliOptions({ ...cliOptions, tests: testNames })).not.toThrow();
89+
await expect(() => tests.fromCliOptions({ ...cliOptions, tests: testNames, strict: true }))
90+
.rejects.toThrow(`Strict mode: 1 test(s) not found: test-data/${namedTest.replace('test1', 'nonexistent')}`);
91+
});
92+
8593
test('exclude tests', async () => {
8694
const integTests = await tests.fromCliOptions({ ...cliOptions, tests: [`test-data/${namedTest}`], exclude: true });
8795

0 commit comments

Comments
 (0)