From 64da83f0c11e5b0d12162890405b063b2d2527aa Mon Sep 17 00:00:00 2001 From: agentcore-bot Date: Thu, 7 May 2026 19:29:05 +0000 Subject: [PATCH 1/3] fix: change DEFAULT_PYTHON_VERSION to PYTHON_3_13 (#907) --- src/schema/__tests__/constants.test.ts | 203 ++----------------------- src/schema/constants.ts | 2 +- 2 files changed, 13 insertions(+), 192 deletions(-) diff --git a/src/schema/__tests__/constants.test.ts b/src/schema/__tests__/constants.test.ts index 2d8bd3bc9..f02473341 100644 --- a/src/schema/__tests__/constants.test.ts +++ b/src/schema/__tests__/constants.test.ts @@ -1,195 +1,16 @@ -import { - ModelProviderSchema, - NetworkModeSchema, - NodeRuntimeSchema, - PROTOCOL_FRAMEWORK_MATRIX, - PythonRuntimeSchema, - RESERVED_PROJECT_NAMES, - RuntimeVersionSchema, - SDKFrameworkSchema, - TargetLanguageSchema, - getSupportedFrameworksForProtocol, - getSupportedModelProviders, - isFrameworkSupportedForProtocol, - isModelProviderSupported, - isReservedProjectName, - matchEnumValue, -} from '../constants.js'; +import { DEFAULT_PYTHON_VERSION, PythonRuntimeSchema } from '../constants.js'; import { describe, expect, it } from 'vitest'; -describe('matchEnumValue', () => { - it('returns canonical value for case-insensitive match', () => { - expect(matchEnumValue(SDKFrameworkSchema, 'strands')).toBe('Strands'); - expect(matchEnumValue(SDKFrameworkSchema, 'STRANDS')).toBe('Strands'); - expect(matchEnumValue(SDKFrameworkSchema, 'Strands')).toBe('Strands'); - expect(matchEnumValue(ModelProviderSchema, 'bedrock')).toBe('Bedrock'); - expect(matchEnumValue(TargetLanguageSchema, 'python')).toBe('Python'); - }); - - it('returns undefined for non-matching input', () => { - expect(matchEnumValue(SDKFrameworkSchema, 'nonexistent')).toBeUndefined(); - expect(matchEnumValue(ModelProviderSchema, 'azure')).toBeUndefined(); - }); - - it('handles multi-word enum values', () => { - expect(matchEnumValue(SDKFrameworkSchema, 'langchain_langgraph')).toBe('LangChain_LangGraph'); - expect(matchEnumValue(SDKFrameworkSchema, 'openaiagents')).toBe('OpenAIAgents'); - expect(matchEnumValue(SDKFrameworkSchema, 'googleadk')).toBe('GoogleADK'); - }); -}); - -describe('SDKFrameworkSchema', () => { - it('accepts valid frameworks and rejects invalid', () => { - expect(SDKFrameworkSchema.safeParse('Strands').success).toBe(true); - expect(SDKFrameworkSchema.safeParse('OpenAIAgents').success).toBe(true); - expect(SDKFrameworkSchema.safeParse('AutoGen').success).toBe(false); - expect(SDKFrameworkSchema.safeParse('strands').success).toBe(false); // case-sensitive - }); -}); - -describe('ModelProviderSchema', () => { - it('accepts valid providers and rejects invalid', () => { - expect(ModelProviderSchema.safeParse('Bedrock').success).toBe(true); - expect(ModelProviderSchema.safeParse('Anthropic').success).toBe(true); - expect(ModelProviderSchema.safeParse('Azure').success).toBe(false); - }); -}); - -describe('RuntimeVersionSchemas', () => { - it('accepts valid Python and Node versions', () => { - expect(PythonRuntimeSchema.safeParse('PYTHON_3_10').success).toBe(true); - expect(PythonRuntimeSchema.safeParse('PYTHON_3_14').success).toBe(true); - expect(NodeRuntimeSchema.safeParse('NODE_18').success).toBe(true); - expect(NodeRuntimeSchema.safeParse('NODE_22').success).toBe(true); - expect(RuntimeVersionSchema.safeParse('PYTHON_3_12').success).toBe(true); - expect(RuntimeVersionSchema.safeParse('NODE_20').success).toBe(true); - }); - - it('rejects invalid versions', () => { - expect(PythonRuntimeSchema.safeParse('PYTHON_3_9').success).toBe(false); - expect(PythonRuntimeSchema.safeParse('PYTHON_3_15').success).toBe(false); - expect(NodeRuntimeSchema.safeParse('NODE_16').success).toBe(false); - expect(NodeRuntimeSchema.safeParse('NODE_24').success).toBe(false); - expect(RuntimeVersionSchema.safeParse('RUBY_3_0').success).toBe(false); - }); -}); - -describe('NetworkModeSchema', () => { - it('accepts valid modes and rejects invalid', () => { - expect(NetworkModeSchema.safeParse('PUBLIC').success).toBe(true); - expect(NetworkModeSchema.safeParse('VPC').success).toBe(true); - expect(NetworkModeSchema.safeParse('PRIVATE').success).toBe(false); - }); -}); - -describe('getSupportedModelProviders', () => { - it('returns all 4 providers for Strands', () => { - expect(getSupportedModelProviders('Strands')).toEqual(['Bedrock', 'Anthropic', 'OpenAI', 'Gemini']); - }); - - it('returns only Gemini for GoogleADK', () => { - expect(getSupportedModelProviders('GoogleADK')).toEqual(['Gemini']); - }); - - it('returns only OpenAI for OpenAIAgents', () => { - expect(getSupportedModelProviders('OpenAIAgents')).toEqual(['OpenAI']); - }); -}); - -describe('isModelProviderSupported', () => { - it('returns true for supported combinations', () => { - expect(isModelProviderSupported('Strands', 'Bedrock')).toBe(true); - expect(isModelProviderSupported('GoogleADK', 'Gemini')).toBe(true); - expect(isModelProviderSupported('OpenAIAgents', 'OpenAI')).toBe(true); - }); - - it('returns false for unsupported combinations', () => { - expect(isModelProviderSupported('GoogleADK', 'Bedrock')).toBe(false); - expect(isModelProviderSupported('OpenAIAgents', 'Anthropic')).toBe(false); - }); -}); - -describe('isReservedProjectName', () => { - it('detects reserved names case-insensitively', () => { - expect(isReservedProjectName('anthropic')).toBe(true); - expect(isReservedProjectName('Anthropic')).toBe(true); - expect(isReservedProjectName('ANTHROPIC')).toBe(true); - }); - - it('detects common reserved names', () => { - expect(isReservedProjectName('boto3')).toBe(true); - expect(isReservedProjectName('openai')).toBe(true); - expect(isReservedProjectName('test')).toBe(true); - expect(isReservedProjectName('pip')).toBe(true); - expect(isReservedProjectName('build')).toBe(true); - }); - - it('returns false for non-reserved names', () => { - expect(isReservedProjectName('MyProject')).toBe(false); - expect(isReservedProjectName('AgentOne')).toBe(false); - }); - - it('RESERVED_PROJECT_NAMES is not empty', () => { - expect(RESERVED_PROJECT_NAMES.length).toBeGreaterThan(0); - }); -}); - -describe('PROTOCOL_FRAMEWORK_MATRIX', () => { - it('defines all protocol modes', () => { - expect(Object.keys(PROTOCOL_FRAMEWORK_MATRIX)).toEqual(expect.arrayContaining(['HTTP', 'MCP', 'A2A', 'AGUI'])); - expect(Object.keys(PROTOCOL_FRAMEWORK_MATRIX)).toHaveLength(4); - }); - - it('HTTP supports all visible frameworks', () => { - expect(PROTOCOL_FRAMEWORK_MATRIX.HTTP).toEqual( - expect.arrayContaining(['Strands', 'LangChain_LangGraph', 'GoogleADK', 'OpenAIAgents']) - ); - }); - - it('MCP returns empty frameworks array', () => { - expect(PROTOCOL_FRAMEWORK_MATRIX.MCP).toEqual([]); - }); - - it('A2A includes Strands and GoogleADK but not OpenAIAgents', () => { - expect(PROTOCOL_FRAMEWORK_MATRIX.A2A).toContain('Strands'); - expect(PROTOCOL_FRAMEWORK_MATRIX.A2A).toContain('GoogleADK'); - expect(PROTOCOL_FRAMEWORK_MATRIX.A2A).not.toContain('OpenAIAgents'); - }); -}); - -describe('getSupportedFrameworksForProtocol', () => { - it('returns all frameworks for HTTP', () => { - const frameworks = getSupportedFrameworksForProtocol('HTTP'); - expect(frameworks).toContain('Strands'); - expect(frameworks.length).toBeGreaterThan(0); - }); - - it('returns empty array for MCP', () => { - expect(getSupportedFrameworksForProtocol('MCP')).toEqual([]); - }); - - it('returns frameworks for A2A', () => { - const frameworks = getSupportedFrameworksForProtocol('A2A'); - expect(frameworks).toContain('Strands'); - expect(frameworks.length).toBeGreaterThan(0); - }); -}); - -describe('isFrameworkSupportedForProtocol', () => { - it('returns true for Strands + HTTP', () => { - expect(isFrameworkSupportedForProtocol('HTTP', 'Strands')).toBe(true); - }); - - it('returns true for Strands + A2A', () => { - expect(isFrameworkSupportedForProtocol('A2A', 'Strands')).toBe(true); - }); - - it('returns false for OpenAIAgents + A2A', () => { - expect(isFrameworkSupportedForProtocol('A2A', 'OpenAIAgents')).toBe(false); - }); - - it('returns false for any framework + MCP', () => { - expect(isFrameworkSupportedForProtocol('MCP', 'Strands')).toBe(false); - expect(isFrameworkSupportedForProtocol('MCP', 'OpenAIAgents')).toBe(false); +describe('schema/constants', () => { + describe('DEFAULT_PYTHON_VERSION', () => { + // Issue #907: PYTHON_3_14 is rejected by CloudFormation in many regions, + // so the default must be a server-side-supported version. + it('defaults to PYTHON_3_13', () => { + expect(DEFAULT_PYTHON_VERSION).toBe('PYTHON_3_13'); + }); + + it('is a valid member of PythonRuntimeSchema', () => { + expect(PythonRuntimeSchema.safeParse(DEFAULT_PYTHON_VERSION).success).toBe(true); + }); }); }); diff --git a/src/schema/constants.ts b/src/schema/constants.ts index d235a0df1..22d9b2545 100644 --- a/src/schema/constants.ts +++ b/src/schema/constants.ts @@ -147,7 +147,7 @@ export const PythonRuntimeSchema = z.enum(['PYTHON_3_10', 'PYTHON_3_11', 'PYTHON export type PythonRuntime = z.infer; /** Default Python runtime version for new agents and MCP tools */ -export const DEFAULT_PYTHON_VERSION: PythonRuntime = 'PYTHON_3_14'; +export const DEFAULT_PYTHON_VERSION: PythonRuntime = 'PYTHON_3_13'; export const NodeRuntimeSchema = z.enum(['NODE_18', 'NODE_20', 'NODE_22']); export type NodeRuntime = z.infer; From 24202d434cf5f3a0f1435d7295560b08ff41471d Mon Sep 17 00:00:00 2001 From: agentcore-bot Date: Thu, 7 May 2026 19:35:43 +0000 Subject: [PATCH 2/3] fix: address review findings round 1 - Restore deleted unrelated tests in src/schema/__tests__/constants.test.ts (matchEnumValue, SDKFrameworkSchema, ModelProviderSchema, etc.) that were accidentally overwritten when the new DEFAULT_PYTHON_VERSION test was introduced. New tests are now appended, not replacing. - Add comments to PythonRuntimeSchema and DEFAULT_PYTHON_VERSION explaining why PYTHON_3_14 is kept in the enum (supported in us-west-2/us-east-1) but not the default (issue #907 / CFN rejection in other regions). - Note: secretlint version in package.json was not modified by this PR (^12.2.0 is already on origin/main); no change needed. --- src/schema/__tests__/constants.test.ts | 218 +++++++++++++++++++++++-- src/schema/constants.ts | 22 ++- 2 files changed, 227 insertions(+), 13 deletions(-) diff --git a/src/schema/__tests__/constants.test.ts b/src/schema/__tests__/constants.test.ts index f02473341..584309fc0 100644 --- a/src/schema/__tests__/constants.test.ts +++ b/src/schema/__tests__/constants.test.ts @@ -1,16 +1,210 @@ -import { DEFAULT_PYTHON_VERSION, PythonRuntimeSchema } from '../constants.js'; +import { + DEFAULT_PYTHON_VERSION, + ModelProviderSchema, + NetworkModeSchema, + NodeRuntimeSchema, + PROTOCOL_FRAMEWORK_MATRIX, + PythonRuntimeSchema, + RESERVED_PROJECT_NAMES, + RuntimeVersionSchema, + SDKFrameworkSchema, + TargetLanguageSchema, + getSupportedFrameworksForProtocol, + getSupportedModelProviders, + isFrameworkSupportedForProtocol, + isModelProviderSupported, + isReservedProjectName, + matchEnumValue, +} from '../constants.js'; import { describe, expect, it } from 'vitest'; -describe('schema/constants', () => { - describe('DEFAULT_PYTHON_VERSION', () => { - // Issue #907: PYTHON_3_14 is rejected by CloudFormation in many regions, - // so the default must be a server-side-supported version. - it('defaults to PYTHON_3_13', () => { - expect(DEFAULT_PYTHON_VERSION).toBe('PYTHON_3_13'); - }); - - it('is a valid member of PythonRuntimeSchema', () => { - expect(PythonRuntimeSchema.safeParse(DEFAULT_PYTHON_VERSION).success).toBe(true); - }); +describe('matchEnumValue', () => { + it('returns canonical value for case-insensitive match', () => { + expect(matchEnumValue(SDKFrameworkSchema, 'strands')).toBe('Strands'); + expect(matchEnumValue(SDKFrameworkSchema, 'STRANDS')).toBe('Strands'); + expect(matchEnumValue(SDKFrameworkSchema, 'Strands')).toBe('Strands'); + expect(matchEnumValue(ModelProviderSchema, 'bedrock')).toBe('Bedrock'); + expect(matchEnumValue(TargetLanguageSchema, 'python')).toBe('Python'); + }); + + it('returns undefined for non-matching input', () => { + expect(matchEnumValue(SDKFrameworkSchema, 'nonexistent')).toBeUndefined(); + expect(matchEnumValue(ModelProviderSchema, 'azure')).toBeUndefined(); + }); + + it('handles multi-word enum values', () => { + expect(matchEnumValue(SDKFrameworkSchema, 'langchain_langgraph')).toBe('LangChain_LangGraph'); + expect(matchEnumValue(SDKFrameworkSchema, 'openaiagents')).toBe('OpenAIAgents'); + expect(matchEnumValue(SDKFrameworkSchema, 'googleadk')).toBe('GoogleADK'); + }); +}); + +describe('SDKFrameworkSchema', () => { + it('accepts valid frameworks and rejects invalid', () => { + expect(SDKFrameworkSchema.safeParse('Strands').success).toBe(true); + expect(SDKFrameworkSchema.safeParse('OpenAIAgents').success).toBe(true); + expect(SDKFrameworkSchema.safeParse('AutoGen').success).toBe(false); + expect(SDKFrameworkSchema.safeParse('strands').success).toBe(false); // case-sensitive + }); +}); + +describe('ModelProviderSchema', () => { + it('accepts valid providers and rejects invalid', () => { + expect(ModelProviderSchema.safeParse('Bedrock').success).toBe(true); + expect(ModelProviderSchema.safeParse('Anthropic').success).toBe(true); + expect(ModelProviderSchema.safeParse('Azure').success).toBe(false); + }); +}); + +describe('RuntimeVersionSchemas', () => { + it('accepts valid Python and Node versions', () => { + expect(PythonRuntimeSchema.safeParse('PYTHON_3_10').success).toBe(true); + expect(PythonRuntimeSchema.safeParse('PYTHON_3_14').success).toBe(true); + expect(NodeRuntimeSchema.safeParse('NODE_18').success).toBe(true); + expect(NodeRuntimeSchema.safeParse('NODE_22').success).toBe(true); + expect(RuntimeVersionSchema.safeParse('PYTHON_3_12').success).toBe(true); + expect(RuntimeVersionSchema.safeParse('NODE_20').success).toBe(true); + }); + + it('rejects invalid versions', () => { + expect(PythonRuntimeSchema.safeParse('PYTHON_3_9').success).toBe(false); + expect(PythonRuntimeSchema.safeParse('PYTHON_3_15').success).toBe(false); + expect(NodeRuntimeSchema.safeParse('NODE_16').success).toBe(false); + expect(NodeRuntimeSchema.safeParse('NODE_24').success).toBe(false); + expect(RuntimeVersionSchema.safeParse('RUBY_3_0').success).toBe(false); + }); +}); + +describe('NetworkModeSchema', () => { + it('accepts valid modes and rejects invalid', () => { + expect(NetworkModeSchema.safeParse('PUBLIC').success).toBe(true); + expect(NetworkModeSchema.safeParse('VPC').success).toBe(true); + expect(NetworkModeSchema.safeParse('PRIVATE').success).toBe(false); + }); +}); + +describe('getSupportedModelProviders', () => { + it('returns all 4 providers for Strands', () => { + expect(getSupportedModelProviders('Strands')).toEqual(['Bedrock', 'Anthropic', 'OpenAI', 'Gemini']); + }); + + it('returns only Gemini for GoogleADK', () => { + expect(getSupportedModelProviders('GoogleADK')).toEqual(['Gemini']); + }); + + it('returns only OpenAI for OpenAIAgents', () => { + expect(getSupportedModelProviders('OpenAIAgents')).toEqual(['OpenAI']); + }); +}); + +describe('isModelProviderSupported', () => { + it('returns true for supported combinations', () => { + expect(isModelProviderSupported('Strands', 'Bedrock')).toBe(true); + expect(isModelProviderSupported('GoogleADK', 'Gemini')).toBe(true); + expect(isModelProviderSupported('OpenAIAgents', 'OpenAI')).toBe(true); + }); + + it('returns false for unsupported combinations', () => { + expect(isModelProviderSupported('GoogleADK', 'Bedrock')).toBe(false); + expect(isModelProviderSupported('OpenAIAgents', 'Anthropic')).toBe(false); + }); +}); + +describe('isReservedProjectName', () => { + it('detects reserved names case-insensitively', () => { + expect(isReservedProjectName('anthropic')).toBe(true); + expect(isReservedProjectName('Anthropic')).toBe(true); + expect(isReservedProjectName('ANTHROPIC')).toBe(true); + }); + + it('detects common reserved names', () => { + expect(isReservedProjectName('boto3')).toBe(true); + expect(isReservedProjectName('openai')).toBe(true); + expect(isReservedProjectName('test')).toBe(true); + expect(isReservedProjectName('pip')).toBe(true); + expect(isReservedProjectName('build')).toBe(true); + }); + + it('returns false for non-reserved names', () => { + expect(isReservedProjectName('MyProject')).toBe(false); + expect(isReservedProjectName('AgentOne')).toBe(false); + }); + + it('RESERVED_PROJECT_NAMES is not empty', () => { + expect(RESERVED_PROJECT_NAMES.length).toBeGreaterThan(0); + }); +}); + +describe('PROTOCOL_FRAMEWORK_MATRIX', () => { + it('defines all protocol modes', () => { + expect(Object.keys(PROTOCOL_FRAMEWORK_MATRIX)).toEqual(expect.arrayContaining(['HTTP', 'MCP', 'A2A', 'AGUI'])); + expect(Object.keys(PROTOCOL_FRAMEWORK_MATRIX)).toHaveLength(4); + }); + + it('HTTP supports all visible frameworks', () => { + expect(PROTOCOL_FRAMEWORK_MATRIX.HTTP).toEqual( + expect.arrayContaining(['Strands', 'LangChain_LangGraph', 'GoogleADK', 'OpenAIAgents']) + ); + }); + + it('MCP returns empty frameworks array', () => { + expect(PROTOCOL_FRAMEWORK_MATRIX.MCP).toEqual([]); + }); + + it('A2A includes Strands and GoogleADK but not OpenAIAgents', () => { + expect(PROTOCOL_FRAMEWORK_MATRIX.A2A).toContain('Strands'); + expect(PROTOCOL_FRAMEWORK_MATRIX.A2A).toContain('GoogleADK'); + expect(PROTOCOL_FRAMEWORK_MATRIX.A2A).not.toContain('OpenAIAgents'); + }); +}); + +describe('getSupportedFrameworksForProtocol', () => { + it('returns all frameworks for HTTP', () => { + const frameworks = getSupportedFrameworksForProtocol('HTTP'); + expect(frameworks).toContain('Strands'); + expect(frameworks.length).toBeGreaterThan(0); + }); + + it('returns empty array for MCP', () => { + expect(getSupportedFrameworksForProtocol('MCP')).toEqual([]); + }); + + it('returns frameworks for A2A', () => { + const frameworks = getSupportedFrameworksForProtocol('A2A'); + expect(frameworks).toContain('Strands'); + expect(frameworks.length).toBeGreaterThan(0); + }); +}); + +describe('isFrameworkSupportedForProtocol', () => { + it('returns true for Strands + HTTP', () => { + expect(isFrameworkSupportedForProtocol('HTTP', 'Strands')).toBe(true); + }); + + it('returns true for Strands + A2A', () => { + expect(isFrameworkSupportedForProtocol('A2A', 'Strands')).toBe(true); + }); + + it('returns false for OpenAIAgents + A2A', () => { + expect(isFrameworkSupportedForProtocol('A2A', 'OpenAIAgents')).toBe(false); + }); + + it('returns false for any framework + MCP', () => { + expect(isFrameworkSupportedForProtocol('MCP', 'Strands')).toBe(false); + expect(isFrameworkSupportedForProtocol('MCP', 'OpenAIAgents')).toBe(false); + }); +}); + +describe('DEFAULT_PYTHON_VERSION (issue #907)', () => { + // Issue #907: PYTHON_3_14 was the default but is rejected by CloudFormation + // in many regions (only us-west-2 / us-east-1 are tested per maintainers). + // The default must therefore be a server-side-supported version. + it('defaults to PYTHON_3_13 (not PYTHON_3_14)', () => { + expect(DEFAULT_PYTHON_VERSION).toBe('PYTHON_3_13'); + expect(DEFAULT_PYTHON_VERSION).not.toBe('PYTHON_3_14'); + }); + + it('is a valid member of PythonRuntimeSchema', () => { + expect(PythonRuntimeSchema.safeParse(DEFAULT_PYTHON_VERSION).success).toBe(true); }); }); diff --git a/src/schema/constants.ts b/src/schema/constants.ts index 22d9b2545..6cf2d9f64 100644 --- a/src/schema/constants.ts +++ b/src/schema/constants.ts @@ -143,10 +143,30 @@ export function isReservedProjectName(name: string): boolean { // Infrastructure Constants (shared between agent-env and mcp schemas) // ============================================================================ +/** + * Supported Python runtime versions. + * + * NOTE on PYTHON_3_14 (issue #907): PYTHON_3_14 is kept in this enum because + * CloudFormation accepts it in `us-west-2` and `us-east-1`. In other regions, + * `AWS::EarlyValidation::PropertyValidation` rejects it. To avoid breaking + * users in supported regions while protecting users in unsupported regions, + * PYTHON_3_14 remains opt-in and is not the default — see DEFAULT_PYTHON_VERSION + * below. + */ export const PythonRuntimeSchema = z.enum(['PYTHON_3_10', 'PYTHON_3_11', 'PYTHON_3_12', 'PYTHON_3_13', 'PYTHON_3_14']); export type PythonRuntime = z.infer; -/** Default Python runtime version for new agents and MCP tools */ +/** + * Default Python runtime version for new agents and MCP tools. + * + * NOTE: Keep this below the newest enum value until CloudFormation supports + * it in all regions. PYTHON_3_14 is intentionally NOT the default — see + * issue #907 (https://github.com/aws/agentcore-cli/issues/907): + * CloudFormation's `AWS::EarlyValidation::PropertyValidation` rejects + * PYTHON_3_14 outside of us-west-2 / us-east-1, leaving stacks stuck in + * REVIEW_IN_PROGRESS. PYTHON_3_14 remains a valid enum member for users + * deploying in supported regions who explicitly opt in. + */ export const DEFAULT_PYTHON_VERSION: PythonRuntime = 'PYTHON_3_13'; export const NodeRuntimeSchema = z.enum(['NODE_18', 'NODE_20', 'NODE_22']); From 7d2c58499c8b1558abca7f74272482f94fc559f6 Mon Sep 17 00:00:00 2001 From: agentcore-bot Date: Thu, 7 May 2026 19:41:49 +0000 Subject: [PATCH 3/3] fix: address review findings round 2 - Update schema-mapper.test.ts to assert against DEFAULT_PYTHON_VERSION (resolves stale 'PYTHON_3_14' assertion that would have broken CI). - Add regression invariants to constants.test.ts: * DEFAULT_PYTHON_VERSION must not equal the newest entry in PythonRuntimeSchema (prevents future lockstep bumps from re-opening #907). * PYTHON_3_14 still accepted as explicit opt-in (documents intent). --- .../generate/__tests__/schema-mapper.test.ts | 4 +++- src/schema/__tests__/constants.test.ts | 15 +++++++++++++++ 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/src/cli/operations/agent/generate/__tests__/schema-mapper.test.ts b/src/cli/operations/agent/generate/__tests__/schema-mapper.test.ts index d30ae23fc..87a77d57b 100644 --- a/src/cli/operations/agent/generate/__tests__/schema-mapper.test.ts +++ b/src/cli/operations/agent/generate/__tests__/schema-mapper.test.ts @@ -1,5 +1,6 @@ import { computeManagedOAuthCredentialName } from '../../../../primitives/credential-utils.js'; import { mapByoConfigToAgent } from '../../../../tui/screens/agent/useAddAgent.js'; +import { DEFAULT_PYTHON_VERSION } from '../../../../tui/screens/generate/defaults.js'; import type { GenerateConfig } from '../../../../tui/screens/generate/types.js'; import { mapGenerateConfigToAgent, @@ -87,7 +88,8 @@ describe('mapGenerateConfigToAgent', () => { expect(result.name).toBe('TestProject'); expect(result.build).toBe('CodeZip'); expect(result.entrypoint).toBe('main.py'); - expect(result.runtimeVersion).toBe('PYTHON_3_14'); + expect(result.runtimeVersion).toBe(DEFAULT_PYTHON_VERSION); + expect(result.runtimeVersion).toBe('PYTHON_3_13'); expect(result.networkMode).toBe('PUBLIC'); expect(result.protocol).toBe('HTTP'); }); diff --git a/src/schema/__tests__/constants.test.ts b/src/schema/__tests__/constants.test.ts index 584309fc0..b855061bc 100644 --- a/src/schema/__tests__/constants.test.ts +++ b/src/schema/__tests__/constants.test.ts @@ -207,4 +207,19 @@ describe('DEFAULT_PYTHON_VERSION (issue #907)', () => { it('is a valid member of PythonRuntimeSchema', () => { expect(PythonRuntimeSchema.safeParse(DEFAULT_PYTHON_VERSION).success).toBe(true); }); + + // Regression invariant: the default must lag the newest enum entry until + // CloudFormation catches up. If a future PR bumps both the newest version + // and the default in lockstep, this test will fail and force a maintainer + // to confirm CFN support. + it('does not equal the newest entry in PythonRuntimeSchema', () => { + const versions = PythonRuntimeSchema.options; + expect(DEFAULT_PYTHON_VERSION).not.toBe(versions[versions.length - 1]); + }); + + // PYTHON_3_14 is intentionally retained as a valid opt-in for users in + // CloudFormation-supported regions (us-west-2, us-east-1). + it('still accepts PYTHON_3_14 as an explicit opt-in via PythonRuntimeSchema', () => { + expect(PythonRuntimeSchema.safeParse('PYTHON_3_14').success).toBe(true); + }); });