Skip to content

Commit 469b9f2

Browse files
author
Kamil Sobol
authored
Split some of auth and functions e2e tests (#2211)
1 parent 06bf37e commit 469b9f2

File tree

21 files changed

+463
-351
lines changed

21 files changed

+463
-351
lines changed

.changeset/orange-days-look.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
---
2+
---

packages/integration-tests/src/test-e2e/backend_output.test.ts

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@ import { S3Client } from '@aws-sdk/client-s3';
2121
import { IAMClient } from '@aws-sdk/client-iam';
2222
import { DeployedResourcesFinder } from '../find_deployed_resource.js';
2323
import { DataStorageAuthWithTriggerTestProjectCreator } from '../test-project-setup/data_storage_auth_with_triggers.js';
24-
import { SQSClient } from '@aws-sdk/client-sqs';
2524
import { setupDeployedBackendClient } from '../test-project-setup/setup_deployed_backend_client.js';
2625
import { CloudTrailClient } from '@aws-sdk/client-cloudtrail';
2726

@@ -46,7 +45,6 @@ void describe(
4645
const lambdaClient = new LambdaClient(e2eToolingClientConfig);
4746
const s3Client = new S3Client(e2eToolingClientConfig);
4847
const iamClient = new IAMClient(e2eToolingClientConfig);
49-
const sqsClient = new SQSClient(e2eToolingClientConfig);
5048
const resourceFinder = new DeployedResourcesFinder(cfnClient);
5149
const dataStorageAuthWithTriggerTestProjectCreator =
5250
new DataStorageAuthWithTriggerTestProjectCreator(
@@ -56,7 +54,6 @@ void describe(
5654
lambdaClient,
5755
s3Client,
5856
iamClient,
59-
sqsClient,
6057
cloudTrailClient,
6158
resourceFinder
6259
);
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
import { defineDeploymentTest } from './deployment.test.template.js';
2+
import { AdvancedAuthAndFunctionsTestProjectCreator } from '../../test-project-setup/advanced_auth_and_functions.js';
3+
4+
defineDeploymentTest(new AdvancedAuthAndFunctionsTestProjectCreator());
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
import { defineSandboxTest } from './sandbox.test.template.js';
2+
import { AdvancedAuthAndFunctionsTestProjectCreator } from '../../test-project-setup/advanced_auth_and_functions.js';
3+
4+
defineSandboxTest(new AdvancedAuthAndFunctionsTestProjectCreator());

packages/integration-tests/src/test-in-memory/data_storage_auth_with_triggers.test.ts

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -52,11 +52,6 @@ void it('data storage auth with triggers', () => {
5252
assertExpectedLogicalIds(templates.defaultNodeFunc, 'AWS::Lambda::Function', [
5353
'defaultNodeFunctionlambda5C194062',
5454
'echoFunclambdaE17DCA46',
55-
'funcCustomEmailSenderlambda3CCBA9A6',
56-
'funcNoMinifylambda91CDF3E0',
57-
'funcWithAwsSdklambda5F770AD7',
58-
'funcWithSchedulelambda0B6E4271',
59-
'funcWithSsmlambda6A8824A1',
6055
'handler2lambda1B9C7EFF',
6156
'node16Functionlambda97ECC775',
6257
'onUploadlambdaA252C959',
Lines changed: 335 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,335 @@
1+
import fs from 'fs/promises';
2+
import { BackendIdentifier } from '@aws-amplify/plugin-types';
3+
import { createEmptyAmplifyProject } from './create_empty_amplify_project.js';
4+
import { CloudFormationClient } from '@aws-sdk/client-cloudformation';
5+
import { TestProjectBase } from './test_project_base.js';
6+
import { TestProjectCreator } from './test_project_creator.js';
7+
import { DeployedResourcesFinder } from '../find_deployed_resource.js';
8+
import assert from 'node:assert';
9+
import {
10+
GetFunctionCommand,
11+
InvokeCommand,
12+
LambdaClient,
13+
} from '@aws-sdk/client-lambda';
14+
import { AmplifyClient } from '@aws-sdk/client-amplify';
15+
import {
16+
DeleteMessageCommand,
17+
ReceiveMessageCommand,
18+
SQSClient,
19+
} from '@aws-sdk/client-sqs';
20+
import { e2eToolingClientConfig } from '../e2e_tooling_client_config.js';
21+
import { TextWriter, ZipReader } from '@zip.js/zip.js';
22+
import {
23+
AdminCreateUserCommand,
24+
CognitoIdentityProviderClient,
25+
} from '@aws-sdk/client-cognito-identity-provider';
26+
27+
/**
28+
* Creates test projects with advanced use cases of auth and functions categories.
29+
*/
30+
export class AdvancedAuthAndFunctionsTestProjectCreator
31+
implements TestProjectCreator
32+
{
33+
readonly name = 'advanced-auth-and-functions';
34+
35+
/**
36+
* Creates project creator.
37+
*/
38+
constructor(
39+
private readonly cfnClient: CloudFormationClient = new CloudFormationClient(
40+
e2eToolingClientConfig
41+
),
42+
private readonly amplifyClient: AmplifyClient = new AmplifyClient(
43+
e2eToolingClientConfig
44+
),
45+
private readonly lambdaClient: LambdaClient = new LambdaClient(
46+
e2eToolingClientConfig
47+
),
48+
private readonly sqsClient: SQSClient = new SQSClient(
49+
e2eToolingClientConfig
50+
),
51+
private readonly resourceFinder: DeployedResourcesFinder = new DeployedResourcesFinder(),
52+
private readonly cognitoClient: CognitoIdentityProviderClient = new CognitoIdentityProviderClient(
53+
e2eToolingClientConfig
54+
)
55+
) {}
56+
57+
createProject = async (e2eProjectDir: string): Promise<TestProjectBase> => {
58+
const { projectName, projectRoot, projectAmplifyDir } =
59+
await createEmptyAmplifyProject(this.name, e2eProjectDir);
60+
61+
const project = new AdvancedAuthAndFunctionsTestProject(
62+
projectName,
63+
projectRoot,
64+
projectAmplifyDir,
65+
this.cfnClient,
66+
this.amplifyClient,
67+
this.lambdaClient,
68+
this.sqsClient,
69+
this.resourceFinder,
70+
this.cognitoClient
71+
);
72+
await fs.cp(
73+
project.sourceProjectAmplifyDirURL,
74+
project.projectAmplifyDirPath,
75+
{
76+
recursive: true,
77+
}
78+
);
79+
80+
return project;
81+
};
82+
}
83+
84+
/**
85+
* Test project with advanced use cases of auth and functions categories.
86+
*/
87+
class AdvancedAuthAndFunctionsTestProject extends TestProjectBase {
88+
readonly sourceProjectRootPath =
89+
'../../src/test-projects/advanced-auth-and-functions';
90+
91+
readonly sourceProjectAmplifyDirURL: URL = new URL(
92+
`${this.sourceProjectRootPath}/amplify`,
93+
import.meta.url
94+
);
95+
96+
/**
97+
* Create a test project instance.
98+
*/
99+
constructor(
100+
name: string,
101+
projectDirPath: string,
102+
projectAmplifyDirPath: string,
103+
cfnClient: CloudFormationClient,
104+
amplifyClient: AmplifyClient,
105+
private readonly lambdaClient: LambdaClient,
106+
private readonly sqsClient: SQSClient,
107+
private readonly resourceFinder: DeployedResourcesFinder,
108+
private readonly cognitoClient: CognitoIdentityProviderClient
109+
) {
110+
super(
111+
name,
112+
projectDirPath,
113+
projectAmplifyDirPath,
114+
cfnClient,
115+
amplifyClient
116+
);
117+
}
118+
119+
override async assertPostDeployment(
120+
backendId: BackendIdentifier
121+
): Promise<void> {
122+
await super.assertPostDeployment(backendId);
123+
124+
// Check that deployed lambdas are working correctly
125+
126+
// find lambda functions
127+
const funcWithSsm = await this.resourceFinder.findByBackendIdentifier(
128+
backendId,
129+
'AWS::Lambda::Function',
130+
(name) => name.includes('funcWithSsm')
131+
);
132+
133+
const funcWithAwsSdk = await this.resourceFinder.findByBackendIdentifier(
134+
backendId,
135+
'AWS::Lambda::Function',
136+
(name) => name.includes('funcWithAwsSdk')
137+
);
138+
139+
const funcWithSchedule = await this.resourceFinder.findByBackendIdentifier(
140+
backendId,
141+
'AWS::Lambda::Function',
142+
(name) => name.includes('funcWithSchedule')
143+
);
144+
145+
const funcNoMinify = await this.resourceFinder.findByBackendIdentifier(
146+
backendId,
147+
'AWS::Lambda::Function',
148+
(name) => name.includes('funcNoMinify')
149+
);
150+
const funcCustomEmailSender =
151+
await this.resourceFinder.findByBackendIdentifier(
152+
backendId,
153+
'AWS::Lambda::Function',
154+
(name) => name.includes('funcCustomEmailSender')
155+
);
156+
157+
assert.equal(funcWithSsm.length, 1);
158+
assert.equal(funcWithAwsSdk.length, 1);
159+
assert.equal(funcWithSchedule.length, 1);
160+
assert.equal(funcCustomEmailSender.length, 1);
161+
162+
await this.checkLambdaResponse(funcWithSsm[0], 'It is working');
163+
164+
// Custom email sender assertion
165+
await this.assertCustomEmailSenderWorks(backendId);
166+
167+
await this.assertScheduleInvokesFunction(backendId);
168+
169+
const expectedNoMinifyChunk = [
170+
'var handler = async () => {',
171+
' return "No minify";',
172+
'};',
173+
].join('\n');
174+
await this.checkLambdaCode(funcNoMinify[0], expectedNoMinifyChunk);
175+
}
176+
177+
private checkLambdaResponse = async (
178+
lambdaName: string,
179+
expectedResponse: unknown
180+
) => {
181+
// invoke the lambda
182+
const response = await this.lambdaClient.send(
183+
new InvokeCommand({ FunctionName: lambdaName })
184+
);
185+
const responsePayload = JSON.parse(
186+
response.Payload?.transformToString() || ''
187+
);
188+
189+
// check expected response
190+
assert.deepStrictEqual(responsePayload, expectedResponse);
191+
};
192+
193+
private checkLambdaCode = async (
194+
lambdaName: string,
195+
expectedCode: string
196+
) => {
197+
// get the lambda code
198+
const response = await this.lambdaClient.send(
199+
new GetFunctionCommand({ FunctionName: lambdaName })
200+
);
201+
const codeUrl = response.Code?.Location;
202+
assert(codeUrl !== undefined);
203+
const fetchResponse = await fetch(codeUrl);
204+
const zipReader = new ZipReader(fetchResponse.body!);
205+
const entries = await zipReader.getEntries();
206+
const entry = entries.find((entry) => entry.filename.endsWith('index.mjs'));
207+
assert(entry !== undefined);
208+
const sourceCode = await entry.getData!(new TextWriter());
209+
assert(sourceCode.includes(expectedCode));
210+
};
211+
212+
private assertScheduleInvokesFunction = async (
213+
backendId: BackendIdentifier
214+
) => {
215+
const TIMEOUT_MS = 1000 * 60 * 2; // 2 minutes
216+
const startTime = Date.now();
217+
let receivedMessageCount = 0;
218+
219+
const queue = await this.resourceFinder.findByBackendIdentifier(
220+
backendId,
221+
'AWS::SQS::Queue',
222+
(name) => name.includes('testFuncQueue')
223+
);
224+
225+
// wait for schedule to invoke the function one time for it to send a message
226+
while (Date.now() - startTime < TIMEOUT_MS) {
227+
const response = await this.sqsClient.send(
228+
new ReceiveMessageCommand({
229+
QueueUrl: queue[0],
230+
WaitTimeSeconds: 20,
231+
MaxNumberOfMessages: 10,
232+
})
233+
);
234+
235+
if (response.Messages) {
236+
receivedMessageCount += response.Messages.length;
237+
238+
// delete messages afterwards
239+
for (const message of response.Messages) {
240+
await this.sqsClient.send(
241+
new DeleteMessageCommand({
242+
QueueUrl: queue[0],
243+
ReceiptHandle: message.ReceiptHandle,
244+
})
245+
);
246+
}
247+
}
248+
}
249+
250+
if (receivedMessageCount === 0) {
251+
assert.fail(
252+
`The scheduled function failed to invoke and send a message to the queue.`
253+
);
254+
}
255+
};
256+
257+
private assertCustomEmailSenderWorks = async (
258+
backendId: BackendIdentifier
259+
) => {
260+
const TIMEOUT_MS = 1000 * 60 * 2; // 2 minutes
261+
const startTime = Date.now();
262+
const queue = await this.resourceFinder.findByBackendIdentifier(
263+
backendId,
264+
'AWS::SQS::Queue',
265+
(name) => name.includes('customEmailSenderQueue')
266+
);
267+
268+
assert.strictEqual(queue.length, 1, 'Custom email sender queue not found');
269+
270+
// Trigger an email sending operation
271+
await this.triggerEmailSending(backendId);
272+
273+
// Wait for the SQS message
274+
let messageReceived = false;
275+
while (Date.now() - startTime < TIMEOUT_MS && !messageReceived) {
276+
const response = await this.sqsClient.send(
277+
new ReceiveMessageCommand({
278+
QueueUrl: queue[0],
279+
WaitTimeSeconds: 20,
280+
})
281+
);
282+
283+
if (response.Messages && response.Messages.length > 0) {
284+
messageReceived = true;
285+
// Verify the message content
286+
const messageBody = JSON.parse(response.Messages[0].Body || '{}');
287+
assert.strictEqual(
288+
messageBody.message,
289+
'Custom Email Sender is working',
290+
'Unexpected message content'
291+
);
292+
293+
// Delete the message
294+
await this.sqsClient.send(
295+
new DeleteMessageCommand({
296+
QueueUrl: queue[0],
297+
ReceiptHandle: response.Messages[0].ReceiptHandle!,
298+
})
299+
);
300+
}
301+
}
302+
303+
assert.strictEqual(
304+
messageReceived,
305+
true,
306+
'Custom email sender was not triggered within the timeout period'
307+
);
308+
};
309+
310+
private triggerEmailSending = async (backendId: BackendIdentifier) => {
311+
const userPoolId = await this.resourceFinder.findByBackendIdentifier(
312+
backendId,
313+
'AWS::Cognito::UserPool',
314+
() => true
315+
);
316+
317+
assert.strictEqual(userPoolId.length, 1, 'User pool not found');
318+
319+
const username = `testuser_${Date.now()}@example.com`;
320+
const password = 'TestPassword123!';
321+
322+
await this.cognitoClient.send(
323+
new AdminCreateUserCommand({
324+
UserPoolId: userPoolId[0],
325+
Username: username,
326+
TemporaryPassword: password,
327+
UserAttributes: [
328+
{ Name: 'email', Value: username },
329+
{ Name: 'email_verified', Value: 'true' },
330+
],
331+
})
332+
);
333+
// The creation of a new user should trigger the custom email sender
334+
};
335+
}

0 commit comments

Comments
 (0)