Skip to content

Commit fdddaf0

Browse files
committed
revamp the tests with integ-runner and integ-tests-alpha
1 parent 9917de0 commit fdddaf0

17 files changed

+1623
-2961
lines changed

.github/workflows/main.yml

Lines changed: 2 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -61,28 +61,9 @@ jobs:
6161
awslocal sqs list-queues
6262
awslocal dynamodb list-tables
6363
64-
- name: Send Messages
64+
- name: Run the tests
6565
run: |
66-
front_queue_url=$(awslocal sqs list-queues | jq -r .QueueUrls[0])
67-
awslocal sqs send-message-batch --queue-url $front_queue_url --entries file://test/testMessagesFirst.json
68-
awslocal sqs send-message-batch --queue-url $front_queue_url --entries file://test/testMessagesSecond.json
69-
awslocal sqs send-message-batch --queue-url $front_queue_url --entries file://test/testMessagesThird.json
70-
71-
awslocal sqs send-message-batch --queue-url $front_queue_url --entries file://test/cornerCase1.json
72-
awslocal sqs send-message-batch --queue-url $front_queue_url --entries file://test/cornerCase2.json
73-
74-
75-
- name: Run tests
76-
run: |
77-
yarn test
78-
endpoint_url=$(cat cdk_outputs.json | jq -r .FriendMicroservicesStack[])
79-
result_player_1=$(curl -X GET "$endpoint_url"friends/player1 | jq -r .[0].friend_id)
80-
result_player_2=$(curl -X GET "$endpoint_url"friends/player2 | jq -r .[0].friend_id)
81-
if [[ $result_player_1 != "player2" ]] || [[ $result_player_2 != "player1" ]]; then
82-
echo "unexpected response"
83-
echo $result_player_1 $result_player_2
84-
exit 1
85-
fi
66+
yarn integ-test
8667
8768
- name: Send a Slack notification
8869
if: failure() || github.event_name != 'pull_request'

.gitignore

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,18 @@ jspm_packages/
4444
# TypeScript cache
4545
*.tsbuildinfo
4646

47+
# Sources
48+
lambda/**/*.js
49+
lambda/**/*.d.ts
50+
integ-tests/**/*.js
51+
integ-tests/**/*.d.ts
52+
bin/**/*.js
53+
bin/**/*.d.ts
54+
lib/**/*.js
55+
lib/**/*.d.ts
56+
models/**/*.js
57+
models/**/*.d.ts
58+
4759
# Optional npm cache directory
4860
.npm
4961

@@ -108,3 +120,4 @@ cdk.context.json
108120
.cdk.staging/
109121
cdk.out/
110122
*.tabl.json
123+
*.snapshot
Lines changed: 165 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,165 @@
1+
/// !cdk-integ FriendMicroservicesIntegStack
2+
import "source-map-support/register";
3+
import { IntegTest, ExpectedResult } from "@aws-cdk/integ-tests-alpha";
4+
import { App, Duration } from "aws-cdk-lib";
5+
import { FriendMicroservicesStack } from "../lib/friend-microservices-stack";
6+
7+
const app = new App();
8+
9+
// Stack under test
10+
// NOTE: Do NOT set explicit `env` - integ-runner provides CDK_DEFAULT_ACCOUNT
11+
// and CDK_DEFAULT_REGION, and the assertion stack must share the same environment.
12+
const stackUnderTest = new FriendMicroservicesStack(
13+
app,
14+
"FriendMicroservicesIntegStack"
15+
);
16+
17+
// Create integration test
18+
const integ = new IntegTest(app, "FriendMicroservicesIntegTest", {
19+
testCases: [stackUnderTest],
20+
cdkCommandOptions: {
21+
destroy: {
22+
args: {
23+
force: true,
24+
},
25+
},
26+
},
27+
regions: ["us-east-1"],
28+
});
29+
30+
// ============================================================================
31+
// Test 1: Send a friend request via SQS and verify the DynamoDB record
32+
// ============================================================================
33+
34+
// Send a "Request" action to the SQS queue
35+
const sendFriendRequest = integ.assertions.awsApiCall("SQS", "sendMessage", {
36+
QueueUrl: stackUnderTest.queueUrl,
37+
MessageBody: JSON.stringify({
38+
player_id: "player-integ-1",
39+
friend_id: "player-integ-2",
40+
friend_action: "Request",
41+
}),
42+
});
43+
44+
// Wait for the Lambda to process and write to DynamoDB
45+
// The frontHandler reads from SQS and writes a "Requested" record
46+
const verifyDynamoDbRecord = integ.assertions
47+
.awsApiCall("DynamoDB", "getItem", {
48+
TableName: stackUnderTest.tableName,
49+
Key: {
50+
player_id: { S: "player-integ-1" },
51+
friend_id: { S: "player-integ-2" },
52+
},
53+
})
54+
.expect(
55+
ExpectedResult.objectLike({
56+
Item: {
57+
player_id: { S: "player-integ-1" },
58+
friend_id: { S: "player-integ-2" },
59+
state: { S: "Requested" },
60+
},
61+
})
62+
)
63+
.waitForAssertions({
64+
totalTimeout: Duration.minutes(2),
65+
interval: Duration.seconds(10),
66+
});
67+
68+
// Chain: send request → verify DynamoDB
69+
sendFriendRequest.next(verifyDynamoDbRecord);
70+
71+
// ============================================================================
72+
// Test 2: Verify the requestStateHandler created the reverse "Pending" record
73+
// (DynamoDB Stream → requestStateHandler creates player-integ-2 → player-integ-1 as Pending)
74+
// ============================================================================
75+
76+
const verifyPendingRecord = integ.assertions
77+
.awsApiCall("DynamoDB", "getItem", {
78+
TableName: stackUnderTest.tableName,
79+
Key: {
80+
player_id: { S: "player-integ-2" },
81+
friend_id: { S: "player-integ-1" },
82+
},
83+
})
84+
.expect(
85+
ExpectedResult.objectLike({
86+
Item: {
87+
player_id: { S: "player-integ-2" },
88+
friend_id: { S: "player-integ-1" },
89+
state: { S: "Pending" },
90+
},
91+
})
92+
)
93+
.waitForAssertions({
94+
totalTimeout: Duration.minutes(2),
95+
interval: Duration.seconds(10),
96+
});
97+
98+
// Chain: verify DynamoDB requested record → verify pending record
99+
verifyDynamoDbRecord.next(verifyPendingRecord);
100+
101+
// ============================================================================
102+
// Test 3: Accept the friend request and verify both records become "Friends"
103+
// ============================================================================
104+
105+
// Send an "Accept" action for player-integ-2 accepting player-integ-1
106+
const sendAccept = integ.assertions.awsApiCall("SQS", "sendMessage", {
107+
QueueUrl: stackUnderTest.queueUrl,
108+
MessageBody: JSON.stringify({
109+
player_id: "player-integ-2",
110+
friend_id: "player-integ-1",
111+
friend_action: "Accept",
112+
}),
113+
});
114+
115+
// Verify player-integ-2's record is now "Friends"
116+
const verifyAcceptedRecord = integ.assertions
117+
.awsApiCall("DynamoDB", "getItem", {
118+
TableName: stackUnderTest.tableName,
119+
Key: {
120+
player_id: { S: "player-integ-2" },
121+
friend_id: { S: "player-integ-1" },
122+
},
123+
})
124+
.expect(
125+
ExpectedResult.objectLike({
126+
Item: {
127+
player_id: { S: "player-integ-2" },
128+
friend_id: { S: "player-integ-1" },
129+
state: { S: "Friends" },
130+
},
131+
})
132+
)
133+
.waitForAssertions({
134+
totalTimeout: Duration.minutes(2),
135+
interval: Duration.seconds(10),
136+
});
137+
138+
// Verify the acceptStateHandler also updated player-integ-1's record to "Friends"
139+
const verifyReverseAccepted = integ.assertions
140+
.awsApiCall("DynamoDB", "getItem", {
141+
TableName: stackUnderTest.tableName,
142+
Key: {
143+
player_id: { S: "player-integ-1" },
144+
friend_id: { S: "player-integ-2" },
145+
},
146+
})
147+
.expect(
148+
ExpectedResult.objectLike({
149+
Item: {
150+
player_id: { S: "player-integ-1" },
151+
friend_id: { S: "player-integ-2" },
152+
state: { S: "Friends" },
153+
},
154+
})
155+
)
156+
.waitForAssertions({
157+
totalTimeout: Duration.minutes(2),
158+
interval: Duration.seconds(10),
159+
});
160+
161+
// Chain: verify pending → send accept → verify accepted → verify reverse accepted
162+
verifyPendingRecord
163+
.next(sendAccept)
164+
.next(verifyAcceptedRecord)
165+
.next(verifyReverseAccepted);

integ.config.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
"parallelRegions": ["us-east-1"]
3+
}

jest.config.js

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

lib/friend-microservices-stack.ts

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,13 +33,21 @@ const friendPk = keyMap.get(Friend)!.get(Keys.PK)!;
3333
const friendSk = keyMap.get(Friend)!.get(Keys.SK)!;
3434

3535
export class FriendMicroservicesStack extends Stack {
36+
public readonly queueUrl: string;
37+
public readonly tableName: string;
38+
public readonly apiUrl: string;
39+
3640
constructor(scope: Construct, id: string, props?: StackProps) {
3741
super(scope, id, props);
3842

3943
const functionProp: NodejsFunctionProps = {
40-
runtime: Runtime.NODEJS_18_X,
44+
runtime: Runtime.NODEJS_22_X,
4145
timeout: cdk.Duration.seconds(10000),
4246
memorySize: 1024,
47+
bundling: {
48+
// aws-sdk v2 is NOT in the Node 22 Lambda runtime, must be bundled
49+
externalModules: [],
50+
},
4351
};
4452

4553
const frontHandler = new NodejsFunction(this, "frontHandler", {
@@ -102,7 +110,9 @@ export class FriendMicroservicesStack extends Stack {
102110
friendTable.grantWriteData(unfriendStateHandler);
103111
friendTable.grantReadData(readHandler);
104112

105-
const frontQueue = new Queue(this, "frontQueue");
113+
const frontQueue = new Queue(this, "frontQueue", {
114+
queueName: cdk.PhysicalName.GENERATE_IF_NEEDED,
115+
});
106116
frontHandler.addEventSource(
107117
new SqsEventSource(frontQueue, {
108118
reportBatchItemFailures: true,
@@ -199,5 +209,14 @@ export class FriendMicroservicesStack extends Stack {
199209
.addResource("{playerId}")
200210
.addResource("{friendId}")
201211
.addMethod("GET");
212+
213+
// Expose resource identifiers for integration tests
214+
this.queueUrl = frontQueue.queueUrl;
215+
this.tableName = friendTable.tableName;
216+
this.apiUrl = readAPI.url;
217+
218+
new cdk.CfnOutput(this, "QueueUrl", { value: frontQueue.queueUrl });
219+
new cdk.CfnOutput(this, "TableName", { value: friendTable.tableName });
220+
new cdk.CfnOutput(this, "ApiUrl", { value: readAPI.url });
202221
}
203222
}

0 commit comments

Comments
 (0)