Skip to content

Commit 166221e

Browse files
all framework and models (#347)
* all framework and models * fix tui test fails by waiting escape key to work
1 parent 43f5b27 commit 166221e

18 files changed

+292
-158
lines changed

.github/workflows/e2e-tests.yml

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,10 +38,19 @@ jobs:
3838
- name: Get AWS Account ID
3939
id: aws
4040
run: echo "account_id=$(aws sts get-caller-identity --query Account --output text)" >> "$GITHUB_OUTPUT"
41+
- name: Get API keys from Secrets Manager
42+
uses: aws-actions/aws-secretsmanager-get-secrets@v2
43+
with:
44+
secret-ids: |
45+
E2E,${{ secrets.E2E_SECRET_ARN }}
46+
parse-json-secrets: true
4147
- run: npm ci
4248
- run: npm run build
4349
- name: Run E2E tests
4450
env:
4551
AWS_ACCOUNT_ID: ${{ steps.aws.outputs.account_id }}
4652
AWS_REGION: ${{ inputs.aws_region || 'us-east-1' }}
53+
ANTHROPIC_API_KEY: ${{ env.E2E_ANTHROPIC_API_KEY }}
54+
OPENAI_API_KEY: ${{ env.E2E_OPENAI_API_KEY }}
55+
GEMINI_API_KEY: ${{ env.E2E_GEMINI_API_KEY }}
4756
run: npm run test:e2e

e2e-tests/create-deploy-invoke.test.ts

Lines changed: 0 additions & 112 deletions
This file was deleted.

e2e-tests/e2e-helper.ts

Lines changed: 152 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,152 @@
1+
import { hasAwsCredentials, parseJsonOutput, prereqs, runCLI } from '../src/test-utils/index.js';
2+
import { execSync } from 'node:child_process';
3+
import { randomUUID } from 'node:crypto';
4+
import { mkdir, rm, writeFile } from 'node:fs/promises';
5+
import { tmpdir } from 'node:os';
6+
import { join } from 'node:path';
7+
import { afterAll, beforeAll, describe, expect, it } from 'vitest';
8+
9+
const hasAws = hasAwsCredentials();
10+
const baseCanRun = prereqs.npm && prereqs.git && prereqs.uv && hasAws;
11+
12+
interface E2EConfig {
13+
framework: string;
14+
modelProvider: string;
15+
requiredEnvVar?: string;
16+
}
17+
18+
/**
19+
* Retry an async function up to `times` attempts with a delay between retries.
20+
*/
21+
async function retry<T>(fn: () => Promise<T>, times: number, delayMs: number): Promise<T> {
22+
let lastError: unknown;
23+
for (let i = 0; i < times; i++) {
24+
try {
25+
return await fn();
26+
} catch (err) {
27+
lastError = err;
28+
if (i < times - 1) {
29+
await new Promise(resolve => setTimeout(resolve, delayMs));
30+
}
31+
}
32+
}
33+
throw lastError;
34+
}
35+
36+
export function createE2ESuite(cfg: E2EConfig) {
37+
const hasApiKey = !cfg.requiredEnvVar || !!process.env[cfg.requiredEnvVar];
38+
const canRun = baseCanRun && hasApiKey;
39+
40+
describe.sequential(`e2e: ${cfg.framework}/${cfg.modelProvider} — create → deploy → invoke`, () => {
41+
let testDir: string;
42+
let projectPath: string;
43+
let agentName: string;
44+
45+
beforeAll(async () => {
46+
if (!canRun) return;
47+
48+
testDir = join(tmpdir(), `agentcore-e2e-${randomUUID()}`);
49+
await mkdir(testDir, { recursive: true });
50+
51+
agentName = `E2e${cfg.framework.slice(0, 4)}${cfg.modelProvider.slice(0, 4)}${String(Date.now()).slice(-8)}`;
52+
const createArgs = [
53+
'create',
54+
'--name',
55+
agentName,
56+
'--language',
57+
'Python',
58+
'--framework',
59+
cfg.framework,
60+
'--model-provider',
61+
cfg.modelProvider,
62+
'--memory',
63+
'none',
64+
'--json',
65+
];
66+
67+
// Pass API key so the credential is registered in the project and .env.local
68+
const apiKey = cfg.requiredEnvVar ? process.env[cfg.requiredEnvVar] : undefined;
69+
if (apiKey) {
70+
createArgs.push('--api-key', apiKey);
71+
}
72+
73+
const result = await runCLI(createArgs, testDir, false);
74+
75+
expect(result.exitCode, `Create failed: ${result.stderr}`).toBe(0);
76+
const json = parseJsonOutput(result.stdout) as { projectPath: string };
77+
projectPath = json.projectPath;
78+
79+
// TODO: Replace with `agentcore add target` once the CLI command is re-introduced
80+
const account =
81+
process.env.AWS_ACCOUNT_ID ??
82+
execSync('aws sts get-caller-identity --query Account --output text').toString().trim();
83+
const region = process.env.AWS_REGION ?? 'us-east-1';
84+
const awsTargetsPath = join(projectPath, 'agentcore', 'aws-targets.json');
85+
await writeFile(awsTargetsPath, JSON.stringify([{ name: 'default', account, region }]));
86+
}, 300000);
87+
88+
afterAll(async () => {
89+
if (projectPath && hasAws) {
90+
await runCLI(['remove', 'all', '--json'], projectPath, false);
91+
const result = await runCLI(['deploy', '--yes', '--json'], projectPath, false);
92+
93+
if (result.exitCode !== 0) {
94+
console.log('Teardown stdout:', result.stdout);
95+
console.log('Teardown stderr:', result.stderr);
96+
}
97+
}
98+
if (testDir) await rm(testDir, { recursive: true, force: true, maxRetries: 3, retryDelay: 1000 });
99+
}, 600000);
100+
101+
it.skipIf(!canRun)(
102+
'deploys to AWS successfully',
103+
async () => {
104+
expect(projectPath, 'Project should have been created').toBeTruthy();
105+
106+
const result = await runCLI(['deploy', '--yes', '--json'], projectPath, false);
107+
108+
if (result.exitCode !== 0) {
109+
console.log('Deploy stdout:', result.stdout);
110+
console.log('Deploy stderr:', result.stderr);
111+
}
112+
113+
expect(result.exitCode, `Deploy failed: ${result.stderr}`).toBe(0);
114+
115+
const json = parseJsonOutput(result.stdout) as { success: boolean };
116+
expect(json.success, 'Deploy should report success').toBe(true);
117+
},
118+
600000
119+
);
120+
121+
it.skipIf(!canRun)(
122+
'invokes the deployed agent',
123+
async () => {
124+
expect(projectPath, 'Project should have been created').toBeTruthy();
125+
126+
// Retry invoke to handle cold-start / runtime initialization delays
127+
await retry(
128+
async () => {
129+
const result = await runCLI(
130+
['invoke', '--prompt', 'Say hello', '--agent', agentName, '--json'],
131+
projectPath,
132+
false
133+
);
134+
135+
if (result.exitCode !== 0) {
136+
console.log('Invoke stdout:', result.stdout);
137+
console.log('Invoke stderr:', result.stderr);
138+
}
139+
140+
expect(result.exitCode, `Invoke failed: ${result.stderr}`).toBe(0);
141+
142+
const json = parseJsonOutput(result.stdout) as { success: boolean };
143+
expect(json.success, 'Invoke should report success').toBe(true);
144+
},
145+
3,
146+
15000
147+
);
148+
},
149+
180000
150+
);
151+
});
152+
}

e2e-tests/googleadk-gemini.test.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
import { createE2ESuite } from './e2e-helper.js';
2+
3+
createE2ESuite({ framework: 'GoogleADK', modelProvider: 'Gemini', requiredEnvVar: 'GEMINI_API_KEY' });
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
import { createE2ESuite } from './e2e-helper.js';
2+
3+
createE2ESuite({ framework: 'LangChain_LangGraph', modelProvider: 'Anthropic', requiredEnvVar: 'ANTHROPIC_API_KEY' });
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
import { createE2ESuite } from './e2e-helper.js';
2+
3+
createE2ESuite({ framework: 'LangChain_LangGraph', modelProvider: 'Bedrock' });

e2e-tests/langgraph-gemini.test.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
import { createE2ESuite } from './e2e-helper.js';
2+
3+
createE2ESuite({ framework: 'LangChain_LangGraph', modelProvider: 'Gemini', requiredEnvVar: 'GEMINI_API_KEY' });

e2e-tests/langgraph-openai.test.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
import { createE2ESuite } from './e2e-helper.js';
2+
3+
createE2ESuite({ framework: 'LangChain_LangGraph', modelProvider: 'OpenAI', requiredEnvVar: 'OPENAI_API_KEY' });
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
import { createE2ESuite } from './e2e-helper.js';
2+
3+
createE2ESuite({ framework: 'OpenAIAgents', modelProvider: 'OpenAI', requiredEnvVar: 'OPENAI_API_KEY' });
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
import { createE2ESuite } from './e2e-helper.js';
2+
3+
createE2ESuite({ framework: 'Strands', modelProvider: 'Anthropic', requiredEnvVar: 'ANTHROPIC_API_KEY' });

0 commit comments

Comments
 (0)