Skip to content

Commit

Permalink
Add descriptions for primary and integration test workshop steps
Browse files Browse the repository at this point in the history
  • Loading branch information
BenBowers committed Mar 14, 2024
1 parent 4222605 commit 756adb8
Show file tree
Hide file tree
Showing 3 changed files with 168 additions and 150 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

[Hexyback Workshop Documentation](https://benbowers.github.io/hexyback-workshop/)

## About ℹ️
## About

This educational journey is designed for enthusiastic learners like you, aiming to demystify the complexities of cloud technology and application architecture.

Expand Down
193 changes: 107 additions & 86 deletions docs/docs/loan-application/workshop-activities/integration.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -6,43 +6,66 @@ tags:

# Integration

## Scenario 1
Alright all the coding is complete! Now it is time to test our API from the front door.
To achieve this we are going to use a http client calling our actual API gateway deployed in the cloud.

Unskip the next test case. Remove the `.todo`: `it.todo(...)` --> `it(...)`:
Make sure you have all the code changes you made deployed:

```bash
pnpm run deploy
```

And start up the integration tests:

```bash
pnpm run test:integration
```

Just like when we were testing the secondary adaptors, we want to pull in some parameters from our AWS deployment to interact with them.
The test:integration script will bind the `Confg` parameters from our stack to our test harnesses through the magic of AWS SSM parameter store.

## Invalid Payload (400)

When the user provides an invalid body to the api we expect it to return a 400 Bad Request.
This behaviour is implemented in the configuration for the API Gateway making it a prime candidate for integration testing, as we cannot validate this behaviour running the code locally.

Unskip the next test case. Remove the `.todo`: `it.todo(...)` --> `it(...)`:

```typescript title="backend/tests/integration/api-gw-apply-for-loan.spec.ts"
// ...
describe.concurrent('api-aw-apply-for-loan', () => {
const apiClient = openApiFetch<paths>({
baseUrl: baseUrl,
});
// highlight-next
it.todo('responds with a 400 Bad Request given the user does not provide the required request body', async ({
expect,
}) => {
await expect(
apiClient.POST('/loan', {
body: {} as unknown as ApplyForLoanRequestBody,
})
).resolves.toEqual({
error: {
message: 'Invalid request body',
},
response: expect.objectContaining({
status: 400,
statusText: 'Bad Request',
}),
});
});
// highlight-next-line
it.todo(
'responds with a 400 Bad Request given the user does not provide the required request body',
async ({ expect }) => {
await expect(
apiClient.POST('/loan', {
body: {} as unknown as ApplyForLoanRequestBody,
})
).resolves.toEqual({
error: {
message: 'Invalid request body',
},
response: expect.objectContaining({
status: 400,
statusText: 'Bad Request',
}),
});
}
);
// ...
});
```

## Scenario 2
## Borrower Does Not Exist (400)

Unskip the next test case. Remove the `.todo`: `it.todo(...)` --> `it(...)`:
Similar to our previous API endpoints we want to return a 400 with the message of "borrower with the provided email does not exist".
This behaviour was implemented by catching the exception in the primary adaptor.

Unskip the next test case. Remove the `.todo`: `it.todo(...)` --> `it(...)`:

```typescript title="backend/tests/integration/api-gw-apply-for-loan.spec.ts"
// ...
Expand All @@ -51,43 +74,40 @@ describe.concurrent('api-aw-apply-for-loan', () => {
baseUrl: baseUrl,
});
// ...
// highlight-next
it('response with a 400 Bad Request given the user provides the required request body but the borrower profile does not exist', async ({
expect,
}) => {
const borrowerEmail = `loan-application+${randomUUID()}@example.com`;
await expect(
apiClient.POST('/loan', {
body: {
borrowerEmail: borrowerEmail,
grossAnnualIncome: 100_000,
employmentStatus: 'FULL_TIME',
monthlyExpenses: 1000,
// highlight-next-line
it.todo(
'response with a 400 Bad Request given the user provides the required request body but the borrower profile does not exist',
async ({ expect }) => {
const borrowerEmail = `loan-application+${randomUUID()}@example.com`;
await expect(
apiClient.POST('/loan', {
body: {
borrowerEmail: borrowerEmail,
grossAnnualIncome: 100_000,
employmentStatus: 'FULL_TIME',
monthlyExpenses: 1000,
},
})
).resolves.toEqual({
error: {
message: 'Borrower with the provided email does not exist',
},
})
).resolves.toEqual({
error: {
message: 'Borrower with the provided email does not exist',
},
response: expect.objectContaining({
status: 400,
statusText: 'Bad Request',
}),
});

await Promise.all([
deleteBorrowerProfile(borrowerEmail),
deleteLoanApplications(borrowerEmail),
]);
});
response: expect.objectContaining({
status: 400,
statusText: 'Bad Request',
}),
});
}
);
// ...
});
```

## Scenario 3

Unskip the next test case. Remove the `.todo`: `it.todo(...)` --> `it(...)`:
Finally we want to test the successful case. To do this we create a borrower using another api and then POST the loan api with a borrower with the same email address

Unskip the next test case. Remove the `.todo`: `it.todo(...)` --> `it(...)`:

```typescript title="backend/tests/integration/api-gw-apply-for-loan.spec.ts"
// ...
Expand All @@ -96,43 +116,44 @@ describe.concurrent('api-aw-apply-for-loan', () => {
baseUrl: baseUrl,
});
// ...
// highlight-next
it('responds with a 201 Submitted with the loan application status given the user provides the required request body when the borrower profile exists', async ({
expect,
}) => {
const borrowerEmail = `loan-application+${randomUUID()}@example.com`;
await apiClient.POST('/borrower', {
body: {
email: borrowerEmail,
name: 'John Doe',
creditScore: 800,
dob: '1999-01-01',
},
});
await expect(
apiClient.POST('/loan', {
// highlight-next-line
it.todo(
'responds with a 201 Submitted with the loan application status given the user provides the required request body when the borrower profile exists',
async ({ expect }) => {
const borrowerEmail = `loan-application+${randomUUID()}@example.com`;
await apiClient.POST('/borrower', {
body: {
borrowerEmail: borrowerEmail,
grossAnnualIncome: 100_000,
employmentStatus: 'FULL_TIME',
monthlyExpenses: 1000,
email: borrowerEmail,
name: 'John Doe',
creditScore: 800,
dob: '1999-01-01',
},
})
).resolves.toEqual({
data: {
loanApplicationStatus: 'APPROVED',
},
response: expect.objectContaining({
status: 201,
statusText: 'Created',
}),
});

await Promise.all([
deleteBorrowerProfile(borrowerEmail),
deleteLoanApplications(borrowerEmail),
]);
});
});
await expect(
apiClient.POST('/loan', {
body: {
borrowerEmail: borrowerEmail,
grossAnnualIncome: 100_000,
employmentStatus: 'FULL_TIME',
monthlyExpenses: 1000,
},
})
).resolves.toEqual({
data: {
loanApplicationStatus: 'APPROVED',
},
response: expect.objectContaining({
status: 201,
statusText: 'Created',
}),
});
}
);
// ...
});
```

## Smoke Test

Now we have finished all our automated integration tests have a look in X-Ray and in the Dynamo table to confirm the application is working.
This is similar to when we were exploring the `/borrower` & `/borrowingCapcity` endpoints.
Loading

0 comments on commit 756adb8

Please sign in to comment.