Skip to content
17 changes: 11 additions & 6 deletions amplify-migration-apps/_test-common/signup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,18 +32,20 @@ function getErrorMessage(error: unknown): string {
}

function resolveSigninIdentifier(usernameAttributes: string[]): SigninIdentifier {
if (usernameAttributes.includes('PHONE_NUMBER')) return 'phone';
if (usernameAttributes.includes('EMAIL')) return 'email';
const normalized = usernameAttributes.map((a) => a.toUpperCase());
if (normalized.includes('PHONE_NUMBER') || normalized.includes('PHONE')) return 'phone';
if (normalized.includes('EMAIL')) return 'email';
return 'username';
}

function resolveSignupAttributes(signupAttributes: string[]): SignupAttribute[] {
const mapping: Record<string, SignupAttribute> = {
EMAIL: 'email',
PHONE_NUMBER: 'phone',
PHONE: 'phone',
USERNAME: 'username',
};
const mapped = signupAttributes.map((attr) => mapping[attr]).filter((a): a is SignupAttribute => a !== undefined);
const mapped = signupAttributes.map((attr) => mapping[attr.toUpperCase()]).filter((a): a is SignupAttribute => a !== undefined);
return mapped.length > 0 ? mapped : ['email'];
}

Expand Down Expand Up @@ -154,11 +156,14 @@ function generateTestPassword(): string {
* Returns the username to use for signIn.
*/
export async function provisionTestUser(config: AmplifyConfig): Promise<{ signinValue: string; testUser: TestUser }> {
const { aws_user_pools_id: userPoolId, aws_cognito_region: region } = config;
// Support both Gen1 (aws_user_pools_id) and Gen2 (auth.user_pool_id) config formats
const gen2Auth = (config as any)?.auth;
const userPoolId = config.aws_user_pools_id ?? gen2Auth?.user_pool_id;
const region = config.aws_cognito_region ?? gen2Auth?.aws_region;

const resolved: ResolvedAuthConfig = {
signinIdentifier: resolveSigninIdentifier(config.aws_cognito_username_attributes ?? []),
signupAttributes: resolveSignupAttributes(config.aws_cognito_signup_attributes ?? []),
signinIdentifier: resolveSigninIdentifier(config.aws_cognito_username_attributes ?? gen2Auth?.username_attributes ?? []),
signupAttributes: resolveSignupAttributes(config.aws_cognito_signup_attributes ?? gen2Auth?.standard_required_attributes ?? []),
};

const credentials = generateCredentials(resolved);
Expand Down
156 changes: 104 additions & 52 deletions amplify-migration-apps/discussions/gen2-test-script.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,17 @@
/**
* Gen2 Test Script for Discussions App
*
* This script tests all functionality for Gen2 Amplify configuration.
* IMPORTANT: Update TEST_USER credentials before running authenticated tests.
* This script tests all functionality for Amplify Gen2:
* 1. GraphQL Queries (Topics, Posts, Comments)
* 2. Topic CRUD Operations
* 3. Post CRUD Operations
* 4. Comment CRUD Operations
* 5. User Activity Tracking
* 6. S3 Storage (Avatars)
* 7. Bookmarks DDB
* 8. Cleanup (Delete Test Data)
*
* Credentials are provisioned automatically via Cognito AdminCreateUser.
*/

// Polyfill crypto for Node.js environment (required for Amplify Auth)
Expand All @@ -12,59 +21,102 @@ if (typeof globalThis.crypto === 'undefined') {
}

import { Amplify } from 'aws-amplify';
import { signIn, signOut, getCurrentUser } from 'aws-amplify/auth';
import amplifyconfig from './src/amplify_outputs.json';
import { createTestRunner, runAllTests } from './test-utils';
import { getTopic, listTopics, getPost, listPosts, getComment, listComments, fetchUserActivity } from './src/graphql/queries';
import {
createTopic,
updateTopic,
deleteTopic,
createPost,
updatePost,
deletePost,
createComment,
updateComment,
deleteComment,
} from './src/graphql/mutations';

// Configure Amplify with Gen2 configuration
import { TestRunner } from '../_test-common/test-apps-test-utils';
import { provisionTestUser } from '../_test-common/signup';
import { createTestFunctions, createTestOrchestrator } from './test-utils';

// Configure Amplify with Gen2 outputs
Amplify.configure(amplifyconfig);

// ============================================================
// CONFIGURATION - Update with your test user credentials
// Main Test Execution
// ============================================================
const TEST_USER = {
username: 'YOUR_USERNAME_HERE', // Phone number format
password: 'YOUR_PASSWORD_HERE',
};

// ============================================================
// Main Entry Point
// ============================================================
const { runTest, printSummary } = createTestRunner();

void runAllTests({
queries: {
getTopic,
listTopics,
getPost,
listPosts,
getComment,
listComments,
fetchUserActivity,
},
mutations: {
createTopic,
updateTopic,
deleteTopic,
createPost,
updatePost,
deletePost,
createComment,
updateComment,
deleteComment,
},
testUser: TEST_USER,
runTest,
printSummary,
});
async function runAllTests(): Promise<void> {
console.log('🚀 Starting Discussions App Gen2 Test Script\n');
console.log('This script tests:');
console.log(' 1. GraphQL Queries (Topics, Posts, Comments)');
console.log(' 2. Topic CRUD Operations');
console.log(' 3. Post CRUD Operations');
console.log(' 4. Comment CRUD Operations');
console.log(' 5. User Activity Tracking');
console.log(' 6. S3 Storage (Avatars)');
console.log(' 7. Bookmarks DDB');
console.log(' 8. Cleanup (Delete Test Data)');

// Provision user via admin APIs, then sign in here so tokens stay in this module's Amplify scope
const { signinValue, testUser } = await provisionTestUser(amplifyconfig);

try {
await signIn({ username: signinValue, password: testUser.password });
const currentUser = await getCurrentUser();
console.log(`✅ Signed in as: ${currentUser.username}`);
} catch (error: any) {
console.error('❌ SignIn failed:', error.message || error);
process.exit(1);
}

const runner = new TestRunner();
const testFunctions = createTestFunctions();
const {
runQueryTests,
runTopicMutationTests,
runPostMutationTests,
runCommentMutationTests,
runActivityTests,
runStorageTests,
runBookmarksTests,
runCleanupTests,
} = createTestOrchestrator(testFunctions, runner);

// Get current user ID for activity tests
const currentUser = await getCurrentUser();

// Part 1: Query tests
await runQueryTests();

// Part 2: Topic mutations
const topicId = await runTopicMutationTests();

// Part 3: Post mutations (requires topic)
let postId: string | null = null;
if (topicId) {
postId = await runPostMutationTests(topicId);
}

// Part 4: Comment mutations (requires post)
let commentId: string | null = null;
if (postId) {
commentId = await runCommentMutationTests(postId);
}

// Part 5: Activity tests
await runActivityTests(currentUser.userId);

// Part 6: S3 Storage (Avatars)
await runStorageTests();

// Part 7: Bookmarks DDB (requires a post to bookmark)
if (postId) {
await runBookmarksTests(currentUser.userId, postId);
}

// Part 8: Cleanup
await runCleanupTests(topicId, postId, commentId);

// Sign out
try {
await signOut();
console.log('✅ Signed out successfully');
} catch (error: any) {
console.error('❌ Sign out error:', error.message || error);
}

// Print summary and exit with appropriate code
runner.printSummary();
}

// Run all tests
void runAllTests();
184 changes: 184 additions & 0 deletions amplify-migration-apps/fitness-tracker/gen2-test-script.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,184 @@
/**
* Gen2 Test Script for Fitness Tracker App
*
* This script tests all functionality for Amplify Gen2:
* 1. Authenticated GraphQL Queries (requires auth)
* 2. Authenticated GraphQL Mutations (requires auth)
* 3. REST API Operations (nutrition logging)
*
* Credentials are provisioned automatically via Cognito AdminCreateUser.
*/

// Polyfill crypto for Node.js environment (required for Amplify Auth)
import { webcrypto } from 'crypto';
if (typeof globalThis.crypto === 'undefined') {
(globalThis as any).crypto = webcrypto;
}

import { Amplify } from 'aws-amplify';
import { signIn, signOut, getCurrentUser, fetchAuthSession } from 'aws-amplify/auth';
import { parseAmplifyConfig } from 'aws-amplify/utils';
import amplifyconfig from './src/amplify_outputs.json';
import { SignatureV4 } from '@aws-sdk/signature-v4';
import { HttpRequest } from '@aws-sdk/protocol-http';
import { Sha256 } from '@aws-crypto/sha256-js';
import { TestRunner } from '../_test-common/test-apps-test-utils';
import { provisionTestUser } from '../_test-common/signup';
import { createTestFunctions, createTestOrchestrator } from './test-utils';

// Configure Amplify with Gen2 outputs, merging REST API config
const parsedConfig = parseAmplifyConfig(amplifyconfig);
Amplify.configure({
...parsedConfig,
API: {
...parsedConfig.API,
REST: {
...(amplifyconfig as any).custom?.API,
},
},
});

// ============================================================
// REST API Test Functions (Gen2-specific, uses SigV4 signing)
// ============================================================

/**
* Make a signed REST API request using AWS SigV4.
* Gen2 REST APIs require manual signing since Amplify's post() helper
* has signing issues in Node.js environments.
*/
async function makeSignedRequest(
method: 'GET' | 'POST' | 'PUT' | 'DELETE',
path: string,
body?: any,
): Promise<any> {
const session = await fetchAuthSession();
const credentials = session.credentials;

if (!credentials) {
throw new Error('No credentials available');
}

const apiConfigs = (amplifyconfig as any).custom.API;
const apiName = Object.keys(apiConfigs)[0];
const apiConfig = apiConfigs[apiName];
let endpoint = apiConfig.endpoint;
const region = apiConfig.region;

if (endpoint.endsWith('/')) {
endpoint = endpoint.slice(0, -1);
}

const normalizedPath = path.startsWith('/') ? path : '/' + path;
const url = new URL(endpoint + normalizedPath);

console.log(' 🔗 Request URL:', url.toString());

const request = new HttpRequest({
method,
protocol: url.protocol,
hostname: url.hostname,
path: url.pathname + url.search,
headers: {
'Content-Type': 'application/json',
host: url.hostname,
},
body: body ? JSON.stringify(body) : undefined,
});

const signer = new SignatureV4({
credentials,
region,
service: 'execute-api',
sha256: Sha256,
});

const signedRequest = await signer.sign(request);

const response = await fetch(url.toString(), {
method: signedRequest.method,
headers: signedRequest.headers,
body: signedRequest.body,
});

const responseText = await response.text();
console.log(' 📥 Response status:', response.status);

if (!response.ok) {
throw new Error(`HTTP ${response.status}: ${responseText}`);
}

return JSON.parse(responseText);
}

async function testNutritionLogAPI(): Promise<void> {
console.log('\n🍔 Testing Gen2 REST API - POST /nutrition/log...');
const user = await getCurrentUser();

const requestBody = {
userName: user.username,
content: `Test nutrition log via Gen2 REST API - Pizza and salad - ${Date.now()}`,
};

const response = await makeSignedRequest('POST', 'nutrition/log', requestBody);
console.log('✅ Gen2 REST API Response:', response);
console.log(' Message:', response.message);
}

// ============================================================
// Main Test Execution
// ============================================================

async function runAllTests(): Promise<void> {
console.log('🚀 Starting Gen2 Test Script for Fitness Tracker\n');
console.log('This script tests:');
console.log(' 1. Authenticated GraphQL Queries');
console.log(' 2. Authenticated GraphQL Mutations');
console.log(' 3. REST API Operations (Nutrition Logging)');

// Provision user via admin APIs, then sign in here so tokens stay in this module's Amplify scope
const { signinValue, testUser } = await provisionTestUser(amplifyconfig);

try {
await signIn({ username: signinValue, password: testUser.password });
const currentUser = await getCurrentUser();
console.log(`✅ Signed in as: ${currentUser.username}`);
} catch (error: any) {
console.error('❌ SignIn failed:', error.message || error);
process.exit(1);
}

const runner = new TestRunner();
const testFunctions = createTestFunctions();
const { runQueryTests, runMutationTests } = createTestOrchestrator(testFunctions, runner);

// Part 1: Queries
await runQueryTests();

// Part 2: Mutations
await runMutationTests();

// Part 3: REST API (gen2-specific, uses SigV4 signing)
console.log('\n' + '='.repeat(50));
console.log('🌐 PART 3: REST API Operations');
console.log('='.repeat(50));

await runner.runTest('nutritionLogAPI', testNutritionLogAPI);

console.log('\n💡 Note: The REST API creates meals directly in DynamoDB.');
console.log(' Check your app to see the logged nutrition data!');

// Sign out
try {
await signOut();
console.log('✅ Signed out successfully');
} catch (error: any) {
console.error('❌ Sign out error:', error.message || error);
}

// Print summary and exit with appropriate code
runner.printSummary();
}

// Run all tests
void runAllTests();
Loading
Loading