Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions packages/@aws-cdk-testing/cli-integ/lib/cli/run-suite.ts
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,11 @@ async function main() {
type: 'string',
requiresArg: true,
})
.options('seed', {
describe: 'Set the seed value to replicate a test run',
type: 'string',
requiresArg: true,
})
.options('verbose', {
alias: 'v',
describe: 'Run in verbose mode',
Expand Down Expand Up @@ -225,6 +230,7 @@ async function main() {

await jest.run([
'--randomize',
...args.seed ? [`--seed=${args.seed}`] : [],
...args.runInBand ? ['-i'] : [],
...args.test ? ['-t', args.test] : [],
...args.verbose ? ['--verbose'] : [],
Expand Down
36 changes: 34 additions & 2 deletions packages/@aws-cdk-testing/cli-integ/lib/with-aws.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import type { Constraint } from '@cdklabs/cdk-atmosphere-client';
import { AtmosphereClient } from '@cdklabs/cdk-atmosphere-client';
import { AwsClients } from './aws';
import type { TestContext } from './integ-test';
Expand Down Expand Up @@ -27,23 +28,54 @@ export function atmospherePool() {

export type AwsContext = { readonly aws: AwsClients };

export interface AwsContextOptions {
/**
* Request the test environment to be in one of these regions
*
* @default - all regions are possible
*/
readonly regions?: string[];

/**
* Do not bootstrap the env
*
* @default false
*/
readonly disableBootstrap?: boolean;
}

/**
* Higher order function to execute a block with an AWS client setup
*
* Allocate the next region from the REGION pool and dispose it afterwards.
*/
export function withAws<A extends TestContext>(
block: (context: A & AwsContext & DisableBootstrapContext) => Promise<void>,
disableBootstrap: boolean = false,
options: AwsContextOptions = {},
): (context: A) => Promise<void> {
return async (context: A) => {
const disableBootstrap = options.disableBootstrap ?? false;

if (atmosphereEnabled()) {
const atmosphere = new AtmosphereClient(atmosphereEndpoint(), {
logStream: context.output,
});

const constraints: Constraint[] = [];
if (options.regions) {
constraints.push({
type: 'region',
value: [...options.regions],
});
}

const start = Date.now();
const allocation = await atmosphere.acquire({ pool: atmospherePool(), requester: context.name, timeoutSeconds: 60 * 30 });
const allocation = await atmosphere.acquire({
pool: atmospherePool(),
requester: context.name,
timeoutSeconds: 60 * 30,
constraints,
});
let outcome = 'success';
context.reportWaitTime(Date.now() - start);

Expand Down
36 changes: 25 additions & 11 deletions packages/@aws-cdk-testing/cli-integ/lib/with-cdk-app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,21 @@ import { testSource } from './package-sources/subprocess';
import { RESOURCES_DIR } from './resources';
import type { ShellOptions } from './shell';
import { shell, ShellHelper, rimraf } from './shell';
import type { AwsContext } from './with-aws';
import type { AwsContext, AwsContextOptions } from './with-aws';
import { atmosphereEnabled, withAws } from './with-aws';
import { withTimeout } from './with-timeout';
import { findYarnPackages } from './yarn';

export const DEFAULT_TEST_TIMEOUT_S = 20 * 60;
export const EXTENDED_TEST_TIMEOUT_S = 30 * 60;

export interface CdkAppContextOptions {
/**
* Configure the AWS context of the app.
*/
readonly aws?: AwsContextOptions;
}

/**
* Higher order function to execute a block with a CDK app fixture
*
Expand Down Expand Up @@ -147,20 +154,20 @@ export function withCdkMigrateApp(
* We could have put `withAws(withCdkApp(fixture => { /... actual test here.../ }))` in every
* test declaration but centralizing it is going to make it convenient to modify in the future.
*/
export function withDefaultFixture(block: (context: TestFixture) => Promise<void>) {
return withAws(withTimeout(DEFAULT_TEST_TIMEOUT_S, withCdkApp(block)));
export function withDefaultFixture(block: (context: TestFixture) => Promise<void>, options: CdkAppContextOptions = {}) {
return withAws(withTimeout(DEFAULT_TEST_TIMEOUT_S, withCdkApp(block)), options.aws);
}

export function withSpecificFixture(appName: string, block: (context: TestFixture) => Promise<void>) {
return withAws(withTimeout(DEFAULT_TEST_TIMEOUT_S, withSpecificCdkApp(appName, block)));
export function withSpecificFixture(appName: string, block: (context: TestFixture) => Promise<void>, options: CdkAppContextOptions = {}) {
return withAws(withTimeout(DEFAULT_TEST_TIMEOUT_S, withSpecificCdkApp(appName, block)), options.aws);
}

export function withExtendedTimeoutFixture(block: (context: TestFixture) => Promise<void>) {
return withAws(withTimeout(EXTENDED_TEST_TIMEOUT_S, withCdkApp(block)));
export function withExtendedTimeoutFixture(block: (context: TestFixture) => Promise<void>, options: CdkAppContextOptions = {}) {
return withAws(withTimeout(EXTENDED_TEST_TIMEOUT_S, withCdkApp(block)), options.aws);
}

export function withCDKMigrateFixture(language: string, block: (content: TestFixture) => Promise<void>) {
return withAws(withTimeout(DEFAULT_TEST_TIMEOUT_S, withCdkMigrateApp(language, block)));
export function withCDKMigrateFixture(language: string, block: (content: TestFixture) => Promise<void>, options: CdkAppContextOptions = {}) {
return withAws(withTimeout(DEFAULT_TEST_TIMEOUT_S, withCdkMigrateApp(language, block)), options.aws);
}

/**
Expand Down Expand Up @@ -210,8 +217,11 @@ export interface DisableBootstrapContext {
* To be used in place of `withDefaultFixture` when the test
* should not create the default bootstrap stack
*/
export function withoutBootstrap(block: (context: TestFixture) => Promise<void>) {
return withAws(withCdkApp(block), true);
export function withoutBootstrap(block: (context: TestFixture) => Promise<void>, options: CdkAppContextOptions = {}) {
return withAws(withCdkApp(block), {
...options.aws,
disableBootstrap: true,
});
}

export interface CdkCliOptions extends ShellOptions {
Expand Down Expand Up @@ -405,6 +415,10 @@ export class TestFixture extends ShellHelper {
});
}

/**
* @returns the captured output of the deploy command.
* !!! DO NOT assume this is the stack's ARN. It will contain other output. !!!
*/
public async cdkDeploy(stackNames: string | string[], options: CdkCliOptions = {}, skipStackRename?: boolean) {
return this.cdk(this.cdkDeployCommandLine(stackNames, options, skipStackRename), options);
}
Expand Down
2 changes: 1 addition & 1 deletion packages/@aws-cdk-testing/cli-integ/package.json

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

Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,6 @@ integTest(

expect(output).toContain('Flag changes:');
}),
true,
{ disableBootstrap: true },
),
);
Original file line number Diff line number Diff line change
Expand Up @@ -3,31 +3,12 @@ import { integTest, withDefaultFixture } from '../../../lib';

jest.setTimeout(2 * 60 * 60_000); // Includes the time to acquire locks, worst-case single-threaded runtime

// Bedrock AgentCore Runtime is only available in specific regions
// Source: https://docs.aws.amazon.com/bedrock-agentcore/latest/devguide/agentcore-regions.html
const SUPPORTED_REGIONS = [
'ap-south-1',
'ap-southeast-1',
'ap-southeast-2',
'ap-northeast-1',
'eu-west-1',
'eu-central-1',
'us-east-1',
'us-east-2',
'us-west-2',
];

integTest(
'hotswap deployment supports Bedrock AgentCore Runtime',
withDefaultFixture(async (fixture) => {
const currentRegion = fixture.aws.region;
if (!SUPPORTED_REGIONS.includes(currentRegion)) {
fixture.log(`Skipping test: Bedrock AgentCore Runtime is not supported in region ${currentRegion}`);
return;
}

// GIVEN
const stackArn = await fixture.cdkDeploy('agentcore-hotswap', {
const stackName = 'agentcore-hotswap';
await fixture.cdkDeploy(stackName, {
captureStderr: false,
modEnv: {
DYNAMIC_BEDROCK_RUNTIME_DESCRIPTION: 'original description',
Expand All @@ -36,7 +17,7 @@ integTest(
});

// WHEN
const deployOutput = await fixture.cdkDeploy('agentcore-hotswap', {
const deployOutput = await fixture.cdkDeploy(stackName, {
options: ['--hotswap'],
captureStderr: true,
onlyStderr: true,
Expand All @@ -46,13 +27,9 @@ integTest(
},
});

// Extract stack name from ARN (format: arn:aws:cloudformation:region:account:stack/name/id)
// Using the full ARN can cause "Stack name cannot exceed 128 characters" error in some cases
const stackName = stackArn.split('/')[1];

const response = await fixture.aws.cloudFormation.send(
new DescribeStacksCommand({
StackName: stackName,
StackName: fixture.fullStackName(stackName),
}),
);
const runtimeId = response.Stacks?.[0].Outputs?.find((output) => output.OutputKey === 'RuntimeId')?.OutputValue;
Expand All @@ -65,5 +42,21 @@ integTest(
// The entire string fails locally due to formatting. Making this test less specific
expect(deployOutput).toMatch(/hotswapped!/);
expect(deployOutput).toContain(runtimeId);
}, {
aws: {
// Bedrock AgentCore Runtime is only available in specific regions
// Source: https://docs.aws.amazon.com/bedrock-agentcore/latest/devguide/agentcore-regions.html
regions: [
'ap-south-1',
'ap-southeast-1',
'ap-southeast-2',
'ap-northeast-1',
'eu-west-1',
'eu-central-1',
'us-east-1',
'us-east-2',
'us-west-2',
],
},
}),
);
Loading
Loading