Skip to content

Commit

Permalink
fix(integ-tests): cannot make two or more identical assertions (#27380)
Browse files Browse the repository at this point in the history
This PR resolves errors in the integration test library when users run identical assertions multiple times.

This change ensures that each use of `awsApiCall`, `httpApiCall`, and `invokeFunction` has a unique identifier, even when multiple identical assertions are used in the same context. We introduced a `uniqueAssertionId` function in the `DeployAssert` construct that maintains backward compatibility with the old id rendering scheme, but prevents the use of conflicting construct ids by tracking the usage of ids and differentiating them where there's a conflict.

Closes #22043, #23049

----

*By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license*
  • Loading branch information
misterjoshua authored Oct 10, 2023
1 parent 5893b3f commit ea06f7d
Show file tree
Hide file tree
Showing 10 changed files with 221 additions and 22 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ export class DeployAssert extends Construct implements IDeployAssert {
}

public scope: Stack;
private assertionIdCounts = new Map<string, number>();

constructor(scope: Construct, props?: DeployAssertProps) {
super(scope, 'Default');
Expand All @@ -65,7 +66,7 @@ export class DeployAssert extends Construct implements IDeployAssert {
hash = md5hash(this.scope.resolve(parameters));
} catch {}

return new AwsApiCall(this.scope, `AwsApiCall${service}${api}${hash}`, {
return new AwsApiCall(this.scope, this.uniqueAssertionId(`AwsApiCall${service}${api}${hash}`), {
api,
service,
parameters,
Expand All @@ -87,15 +88,15 @@ export class DeployAssert extends Construct implements IDeployAssert {
const parsedUrl = new URL(url);
append = `${parsedUrl.hostname}${parsedUrl.pathname}`;
}
return new HttpApiCall(this.scope, `HttpApiCall${append}${hash}`, {
return new HttpApiCall(this.scope, this.uniqueAssertionId(`HttpApiCall${append}${hash}`), {
url,
fetchOptions: options,
});
}

public invokeFunction(props: LambdaInvokeFunctionProps): IApiCall {
const hash = md5hash(this.scope.resolve(props));
return new LambdaInvokeFunction(this.scope, `LambdaInvoke${hash}`, props);
return new LambdaInvokeFunction(this.scope, this.uniqueAssertionId(`LambdaInvoke${hash}`), props);
}

public expect(id: string, expected: ExpectedResult, actual: ActualResult): void {
Expand All @@ -104,4 +105,22 @@ export class DeployAssert extends Construct implements IDeployAssert {
actual,
});
}

/**
* Gets a unique logical id based on a proposed assertion id.
*/
private uniqueAssertionId(id: string): string {
const count = this.assertionIdCounts.get(id);

if (count === undefined) {
// If we've never seen this id before, we'll return the id unchanged
// to maintain backward compatibility.
this.assertionIdCounts.set(id, 1);
return id;
}

// Otherwise, we'll increment the counter and return a unique id.
this.assertionIdCounts.set(id, count + 1);
return `${id}${count}`;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,21 @@ describe('DeployAssert', () => {
},
});
});

test('multiple identical calls can be configured', () => {
// GIVEN
const app = new App();

// WHEN
const deployAssert = new DeployAssert(app);
deployAssert.invokeFunction({ functionName: 'my-func' });
deployAssert.invokeFunction({ functionName: 'my-func' });

// THEN
const template = Template.fromStack(deployAssert.scope);
template.resourceCountIs('AWS::Lambda::Function', 1);
template.resourceCountIs('Custom::DeployAssert@SdkCallLambdainvoke', 2);
});
});

describe('assertions', () => {
Expand Down Expand Up @@ -145,6 +160,21 @@ describe('DeployAssert', () => {
template.resourceCountIs('Custom::DeployAssert@SdkCallMyServiceMyApi2', 1);
});

test('multiple identical calls can be configured', () => {
// GIVEN
const app = new App();

// WHEN
const deployAssert = new DeployAssert(app);
deployAssert.awsApiCall('MyService', 'MyApi');
deployAssert.awsApiCall('MyService', 'MyApi');

// THEN
const template = Template.fromStack(deployAssert.scope);
template.resourceCountIs('AWS::Lambda::Function', 1);
template.resourceCountIs('Custom::DeployAssert@SdkCallMyServiceMyApi', 2);
});

test('custom resource type length is truncated when greater than 60 characters', () => {
// GIVEN
const app = new App();
Expand Down Expand Up @@ -203,6 +233,21 @@ describe('DeployAssert', () => {
template.resourceCountIs('Custom::DeployAssert@HttpCallexamplecomtest789', 1);
});

test('multiple identical calls can be configured', () => {
// GIVEN
const app = new App();

// WHEN
const deployAssert = new DeployAssert(app);
deployAssert.httpApiCall('https://example.com/test');
deployAssert.httpApiCall('https://example.com/test');

// THEN
const template = Template.fromStack(deployAssert.scope);
template.resourceCountIs('AWS::Lambda::Function', 1);
template.resourceCountIs('Custom::DeployAssert@HttpCallexamplecomtest', 2);
});

test('call with fetch options', () => {
// GIVEN
const app = new App();
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading

0 comments on commit ea06f7d

Please sign in to comment.