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
4 changes: 2 additions & 2 deletions src/cli/commands/pause/command.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ function registerOnlineEvalSubcommand(parent: Command, action: 'pause' | 'resume
process.exit(1);
}

if (!cliOptions.arn) {
if (!cliOptions.arn || name) {
requireProject();
}

Expand Down Expand Up @@ -126,7 +126,7 @@ function registerOnlineInsightsSubcommand(parent: Command, action: 'pause' | 're
process.exit(1);
}

if (!cliOptions.arn) {
if (!cliOptions.arn || name) {
requireProject();
}

Expand Down
50 changes: 50 additions & 0 deletions src/cli/operations/eval/__tests__/pause-resume.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -176,4 +176,54 @@ describe('handlePauseResume', () => {
expect(result.error.message).toContain('Could not extract config ID');
});
});

describe('name + ARN cross-validation', () => {
it('accepts matching name and ARN', async () => {
mockLoadDeployedProjectConfig.mockResolvedValue(makeContext('my-config', 'cfg-123'));
mockUpdateOnlineEvalExecutionStatus.mockResolvedValue({
configId: 'cfg-123',
executionStatus: 'DISABLED',
status: 'ACTIVE',
});

const arn = 'arn:aws:bedrock-agentcore:us-east-1:123456789012:online-evaluation-config/cfg-123';
const result = await handlePauseResume({ name: 'my-config', arn }, 'pause');

assert(result.success);
expect(result.executionStatus).toBe('DISABLED');
});

it('rejects mismatched name and ARN', async () => {
mockLoadDeployedProjectConfig.mockResolvedValue(makeContext('my-config', 'cfg-123'));

const arn = 'arn:aws:bedrock-agentcore:us-east-1:123456789012:online-evaluation-config/different-cfg';
const result = await handlePauseResume({ name: 'my-config', arn }, 'pause');

assert(!result.success);
expect(result.error.message).toContain('refer to different configs');
expect(mockUpdateOnlineEvalExecutionStatus).not.toHaveBeenCalled();
});

it('rejects mismatched regions between name and ARN', async () => {
mockLoadDeployedProjectConfig.mockResolvedValue(makeContext('my-config', 'cfg-123', 'dev', 'us-west-2'));

const arn = 'arn:aws:bedrock-agentcore:us-east-1:123456789012:online-evaluation-config/cfg-123';
const result = await handlePauseResume({ name: 'my-config', arn }, 'pause');

assert(!result.success);
expect(result.error.message).toContain('different regions');
expect(mockUpdateOnlineEvalExecutionStatus).not.toHaveBeenCalled();
});

it('returns name-lookup error when both are passed but name is unknown', async () => {
mockLoadDeployedProjectConfig.mockResolvedValue(makeContext('other-config', 'cfg-999'));

const arn = 'arn:aws:bedrock-agentcore:us-east-1:123456789012:online-evaluation-config/cfg-999';
const result = await handlePauseResume({ name: 'missing-config', arn }, 'pause');

assert(!result.success);
expect(result.error.message).toContain('missing-config');
expect(result.error.message).toContain('not found');
});
});
});
36 changes: 35 additions & 1 deletion src/cli/operations/eval/pause-resume.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,11 +68,45 @@ function parseOnlineEvalConfigArn(
}

/**
* Resolve config ID and region from either a project config name or an ARN.
* Resolve config ID and region from a project config name, an ARN, or both.
*
* When both are provided, the named config is looked up and its configId is
* cross-checked against the ARN. A mismatch means the user passed a name and
* ARN that point to different configs — we reject rather than silently
* preferring one (the ARN previously won, so the name was a no-op).
*/
async function resolveConfig(
options: OnlineEvalActionOptions
): Promise<{ success: true; configId: string; region: string } | { success: false; error: string }> {
if (options.arn && options.name) {
const arnResolution = parseOnlineEvalConfigArn(options.arn, options.region);
if (!arnResolution.success) return arnResolution;

const nameResolution = await resolveOnlineEvalConfig(options.name);
Comment thread
notgitika marked this conversation as resolved.
if (!nameResolution.success) return nameResolution;

if (nameResolution.configId !== arnResolution.configId) {
return {
success: false,
error:
`--arn and config name "${options.name}" refer to different configs ` +
`(name resolves to "${nameResolution.configId}", ARN resolves to "${arnResolution.configId}"). ` +
`Pass only one, or pass matching values.`,
};
}

if (nameResolution.region !== arnResolution.region) {
return {
success: false,
error:
`--arn and config name "${options.name}" resolve to different regions ` +
`(name resolves to "${nameResolution.region}", ARN resolves to "${arnResolution.region}"). ` +
`Pass only one, or use --region to override.`,
};
}

return arnResolution;
Comment thread
notgitika marked this conversation as resolved.
}
if (options.arn) {
return parseOnlineEvalConfigArn(options.arn, options.region);
}
Expand Down
Loading