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
26 changes: 13 additions & 13 deletions integ-tests/add-remove-config-bundle.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ describe('integration: add and remove config-bundle', () => {
expect(json.bundleName).toBe('InlineBundle');

const config = await readProjectConfig(project.projectPath);
const bundle = config.configBundles.find(b => b.name === 'InlineBundle');
const bundle = config.configBundles!.find(b => b.name === 'InlineBundle');
expect(bundle).toBeDefined();
expect(bundle!.type).toBe('ConfigurationBundle');
expect(bundle!.branchName).toBe('mainline');
Expand Down Expand Up @@ -68,7 +68,7 @@ describe('integration: add and remove config-bundle', () => {
expect(json.bundleName).toBe('FileBundle');

const config = await readProjectConfig(project.projectPath);
const bundle = config.configBundles.find(b => b.name === 'FileBundle');
const bundle = config.configBundles!.find(b => b.name === 'FileBundle');
expect(bundle).toBeDefined();
expect(Object.keys(bundle!.components)).toHaveLength(2);
});
Expand Down Expand Up @@ -102,7 +102,7 @@ describe('integration: add and remove config-bundle', () => {
expect(json.bundleName).toBe('FullOptsBundle');

const config = await readProjectConfig(project.projectPath);
const bundle = config.configBundles.find(b => b.name === 'FullOptsBundle');
const bundle = config.configBundles!.find(b => b.name === 'FullOptsBundle');
expect(bundle).toBeDefined();
expect(bundle!.description).toBe('A bundle with all optional fields');
expect(bundle!.branchName).toBe('feature-branch');
Expand All @@ -127,7 +127,7 @@ describe('integration: add and remove config-bundle', () => {
expect(json.bundleName).toBe('PlaceholderBundle');

const config = await readProjectConfig(project.projectPath);
const bundle = config.configBundles.find(b => b.name === 'PlaceholderBundle');
const bundle = config.configBundles!.find(b => b.name === 'PlaceholderBundle');
expect(bundle).toBeDefined();
const keys = Object.keys(bundle!.components);
expect(keys).toContain('{{runtime:AgentA}}');
Expand Down Expand Up @@ -236,7 +236,7 @@ describe('integration: add and remove config-bundle', () => {
expect(json.success).toBe(true);

const config = await readProjectConfig(project.projectPath);
const bundle = config.configBundles.find(b => b.name === 'InlineBundle');
const bundle = config.configBundles!.find(b => b.name === 'InlineBundle');
expect(bundle).toBeUndefined();
});

Expand All @@ -251,14 +251,14 @@ describe('integration: add and remove config-bundle', () => {

it('removes all remaining config bundles one by one', async () => {
const configBefore = await readProjectConfig(project.projectPath);
const remaining = configBefore.configBundles.map(b => b.name);
const remaining = configBefore.configBundles!.map(b => b.name);

for (const name of remaining) {
await runSuccess(['remove', 'config-bundle', '--name', name, '--json'], project.projectPath);
}

const configAfter = await readProjectConfig(project.projectPath);
expect(configAfter.configBundles).toHaveLength(0);
expect(configAfter.configBundles!).toHaveLength(0);
});
});

Expand All @@ -282,21 +282,21 @@ describe('integration: add and remove config-bundle', () => {
}

const config = await readProjectConfig(project.projectPath);
expect(config.configBundles).toHaveLength(bundleNames.length);
expect(config.configBundles!).toHaveLength(bundleNames.length);

for (const name of bundleNames) {
expect(config.configBundles.find(b => b.name === name)).toBeDefined();
expect(config.configBundles!.find(b => b.name === name)).toBeDefined();
}
});

it('removing one bundle does not affect others', async () => {
await runSuccess(['remove', 'config-bundle', '--name', 'BundleBeta', '--json'], project.projectPath);

const config = await readProjectConfig(project.projectPath);
expect(config.configBundles).toHaveLength(2);
expect(config.configBundles.find(b => b.name === 'BundleAlpha')).toBeDefined();
expect(config.configBundles.find(b => b.name === 'BundleGamma')).toBeDefined();
expect(config.configBundles.find(b => b.name === 'BundleBeta')).toBeUndefined();
expect(config.configBundles!).toHaveLength(2);
expect(config.configBundles!.find(b => b.name === 'BundleAlpha')).toBeDefined();
expect(config.configBundles!.find(b => b.name === 'BundleGamma')).toBeDefined();
expect(config.configBundles!.find(b => b.name === 'BundleBeta')).toBeUndefined();
});

afterAll(async () => {
Expand Down
6 changes: 3 additions & 3 deletions src/cli/operations/ab-test/promote.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,13 +55,13 @@ export async function promoteABTestConfig(abTestId: string, testNameFallback?: s
`[promote] Could not resolve AB test ID "${abTestId}" from deployed state; falling back to name "${testNameFallback}".`
);
const lowerName = testNameFallback.toLowerCase();
const match = project.abTests.find(
const match = (project.abTests ?? []).find(
t => t.name.toLowerCase() === lowerName || `${project.name}_${t.name}`.toLowerCase() === lowerName
);
specName = match?.name;
}

const abTest = specName ? project.abTests.find(t => t.name === specName) : undefined;
const abTest = specName ? (project.abTests ?? []).find(t => t.name === specName) : undefined;

if (!abTest) {
return { promoted: false, promotionDetail: `AB test with ID "${abTestId}" not found in project config.` };
Expand All @@ -78,7 +78,7 @@ export async function promoteABTestConfig(abTestId: string, testNameFallback?: s
const gwMatch = /^\{\{gateway:(.+)\}\}$/.exec(abTest.gatewayRef);
const gwName = gwMatch?.[1];
if (gwName) {
const gw = project.httpGateways.find(g => g.name === gwName);
const gw = (project.httpGateways ?? []).find(g => g.name === gwName);
if (gw?.targets) {
const controlTarget = gw.targets.find(t => t.name === controlTargetName);
const treatmentTarget = gw.targets.find(t => t.name === treatmentTargetName);
Expand Down
3 changes: 2 additions & 1 deletion src/cli/operations/agent/config-bundle-defaults.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,9 @@ export async function createConfigBundleForAgent(agentName: string, configBaseDi
const project = await configIO.readProjectSpec();

const bundleName = `${agentName}Config`;
if (project.configBundles.some(b => b.name === bundleName)) return;
if ((project.configBundles ?? []).some(b => b.name === bundleName)) return;

project.configBundles ??= [];
project.configBundles.push({
type: 'ConfigurationBundle',
name: bundleName,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -537,7 +537,7 @@ describe('resolveConfigBundleComponentKeys', () => {
});

const result = resolveConfigBundleComponentKeys(spec, deployedState, 'target1');
const keys = Object.keys(result.configBundles[0]!.components);
const keys = Object.keys(result.configBundles![0]!.components);
expect(keys).toEqual(['arn:aws:bedrock-agentcore:us-east-1:123:runtime/rt-1']);
});

Expand All @@ -550,7 +550,7 @@ describe('resolveConfigBundleComponentKeys', () => {
});

const result = resolveConfigBundleComponentKeys(spec, deployedState, 'target1');
const keys = Object.keys(result.configBundles[0]!.components);
const keys = Object.keys(result.configBundles![0]!.components);
expect(keys).toEqual(['arn:aws:bedrock-agentcore:us-east-1:123:gateway/gw-1']);
});

Expand All @@ -563,7 +563,7 @@ describe('resolveConfigBundleComponentKeys', () => {
});

const result = resolveConfigBundleComponentKeys(spec, deployedState, 'target1');
const keys = Object.keys(result.configBundles[0]!.components);
const keys = Object.keys(result.configBundles![0]!.components);
expect(keys).toEqual(['arn:mcp:gw:resolved']);
});

Expand All @@ -574,7 +574,7 @@ describe('resolveConfigBundleComponentKeys', () => {
const deployedState = makeDeployedState('target1', { runtimes: {} });

const result = resolveConfigBundleComponentKeys(spec, deployedState, 'target1');
const keys = Object.keys(result.configBundles[0]!.components);
const keys = Object.keys(result.configBundles![0]!.components);
expect(keys).toEqual(['arn:existing:key']);
});

Expand All @@ -585,7 +585,7 @@ describe('resolveConfigBundleComponentKeys', () => {
const deployedState = makeDeployedState('target1', { runtimes: {} });

const result = resolveConfigBundleComponentKeys(spec, deployedState, 'target1');
const keys = Object.keys(result.configBundles[0]!.components);
const keys = Object.keys(result.configBundles![0]!.components);
expect(keys).toEqual(['some-plain-key']);
});

Expand Down Expand Up @@ -629,9 +629,9 @@ describe('resolveConfigBundleComponentKeys', () => {

const result = resolveConfigBundleComponentKeys(spec, deployedState, 'target1');
// Original should still have the placeholder
expect(Object.keys(spec.configBundles[0]!.components)).toEqual(['{{runtime:my-rt}}']);
expect(Object.keys(spec.configBundles![0]!.components)).toEqual(['{{runtime:my-rt}}']);
// Result should have the resolved key
expect(Object.keys(result.configBundles[0]!.components)).toEqual(['arn:resolved']);
expect(Object.keys(result.configBundles![0]!.components)).toEqual(['arn:resolved']);
});

it('prefers HTTP gateway over MCP gateway when both exist with same name', () => {
Expand All @@ -644,7 +644,7 @@ describe('resolveConfigBundleComponentKeys', () => {
});

const result = resolveConfigBundleComponentKeys(spec, deployedState, 'target1');
const keys = Object.keys(result.configBundles[0]!.components);
const keys = Object.keys(result.configBundles![0]!.components);
// HTTP gateway should take precedence (checked first in code)
expect(keys).toEqual(['arn:http:gw']);
});
Expand Down
4 changes: 2 additions & 2 deletions src/cli/operations/deploy/post-deploy-ab-tests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,7 @@ export async function setupABTests(options: SetupABTestsOptions): Promise<SetupA
const abTests: Record<string, ABTestDeployedState> = {};

// Create or skip tests from the spec
for (const testSpec of projectSpec.abTests) {
for (const testSpec of projectSpec.abTests ?? []) {
let resolvedRoleArn: string | undefined;
let roleCreatedByCli = false;
try {
Expand Down Expand Up @@ -321,7 +321,7 @@ export async function deleteOrphanedABTests(options: {
const { region, projectSpec, existingABTests } = options;
if (!existingABTests) return { results: [], hasErrors: false };

const specTestNames = new Set(projectSpec.abTests.map(t => t.name));
const specTestNames = new Set((projectSpec.abTests ?? []).map(t => t.name));
const results: ABTestSetupResult[] = [];

for (const [testName, testState] of Object.entries(existingABTests)) {
Expand Down
4 changes: 2 additions & 2 deletions src/cli/operations/deploy/post-deploy-config-bundles.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,11 +53,11 @@ export async function setupConfigBundles(options: SetupConfigBundlesOptions): Pr
const results: ConfigBundleSetupResult[] = [];
const configBundles: Record<string, ConfigBundleDeployedState> = {};

const specBundleNames = new Set(projectSpec.configBundles.map(b => b.name));
const specBundleNames = new Set((projectSpec.configBundles ?? []).map(b => b.name));
const projectName = projectSpec.name;

// Create or update bundles from the spec
for (const bundleSpec of projectSpec.configBundles) {
for (const bundleSpec of projectSpec.configBundles ?? []) {
// Prepend project name to the API-side bundle name (no separator for config bundles)
const apiBundleName = `${projectName}${bundleSpec.name}`;

Expand Down
2 changes: 1 addition & 1 deletion src/cli/operations/deploy/post-deploy-http-gateways.ts
Original file line number Diff line number Diff line change
Expand Up @@ -420,7 +420,7 @@ export async function deleteOrphanedHttpGateways(options: {
const { region, projectSpec, existingHttpGateways } = options;
if (!existingHttpGateways) return { results: [], hasErrors: false };

const specGatewayNames = new Set(projectSpec.httpGateways.map(g => g.name));
const specGatewayNames = new Set((projectSpec.httpGateways ?? []).map(g => g.name));
const results: HttpGatewaySetupResult[] = [];

for (const [gwName, gwState] of Object.entries(existingHttpGateways)) {
Expand Down
Loading
Loading