Skip to content

Commit

Permalink
Apply workshop dry run feedback
Browse files Browse the repository at this point in the history
  • Loading branch information
ramiruhayel committed Mar 13, 2024
1 parent 1ea3386 commit ff79786
Show file tree
Hide file tree
Showing 24 changed files with 223 additions and 77 deletions.
16 changes: 16 additions & 0 deletions backend/infra/api-definition.ts
Original file line number Diff line number Diff line change
Expand Up @@ -303,6 +303,22 @@ export const generateApiSpec = ({
},
},
},
'400': {
description: 'Bad Request',
content: {
'application/json': {
schema: {
type: 'object',
required: ['message'],
properties: {
message: {
type: 'string',
},
},
},
},
},
},
},
'x-amazon-apigateway-integration': {
uri: `arn:\${AWS::Partition}:apigateway:\${AWS::Region}:lambda:path/2015-03-31/functions/${applyForLoanHandlerArn}/invocations`,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ describe('api-gw-create-borrower-profile', () => {
const borrowerProfile: BorrowerProfile = {
creditScore: 500,
dob: '1981-11-02',
email: 'john.doe@example',
email: 'john.doe@example.com',
name: 'John Doe',
};
const context = {} as Context;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ describe('ddb-put-borrowing-capacity-calculation', () => {
const borrowingCapacityCalculation: PutBorrowingCapacityCalculationInput = {
estimatedBorrowingCapacity: 300_000,
timestamp: '2024-03-15T00:00:00.000Z',
borrowerEmail: 'john.doe@example',
borrowerEmail: 'john.doe@example.com',
borrowingCapacityCalculationId: 'd5fe6ff5-20b3-4581-b38f-9b034c20d783',
employmentStatus: 'FULL_TIME',
grossAnnualIncome: 60_000,
Expand Down Expand Up @@ -41,7 +41,7 @@ describe('ddb-put-borrowing-capacity-calculation', () => {
expect(dynamoDbMock).toHaveReceivedCommandWith(PutItemCommand, {
TableName: 'financial-data-table',
Item: {
pk: { S: 'john.doe@example' },
pk: { S: 'john.doe@example.com' },
sk: {
S:
`BORROWING_CAPACITY_CALCULATION#${borrowingCapacityCalculation.borrowingCapacityCalculationId}` +
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ describe('ddb-put-loan-application', () => {
});

const loanApplication: PutLoanApplicationInput = {
borrowerEmail: 'john.doe@example',
borrowerEmail: 'john.doe@example.com',
creditScore: 700,
employmentStatus: 'FULL_TIME',
grossAnnualIncome: 100000,
Expand Down Expand Up @@ -55,7 +55,7 @@ describe('ddb-put-loan-application', () => {
expect(dynamoDbMock).toHaveReceivedCommandWith(PutItemCommand, {
TableName: 'financial-data-table',
Item: {
pk: { S: 'john.doe@example' },
pk: { S: 'john.doe@example.com' },
sk: {
S:
`LOAN_APPLICATION#${loanApplication.loanApplicationId}` +
Expand Down
7 changes: 4 additions & 3 deletions backend/src/entities/DetailedBorrowerProfile.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { BorrowingCapacityCalculationInput } from './BorrowingCapacityCalculationInput';

export type DetailedBorrowerProfile = BorrowingCapacityCalculationInput & {
export type DetailedBorrowerProfile = {
age: number;
employmentStatus: 'FULL_TIME' | 'PART_TIME' | 'CASUAL' | 'SELF_EMPLOYED';
grossAnnualIncome: number;
monthlyExpenses: number;
creditScore: number;
};
Original file line number Diff line number Diff line change
Expand Up @@ -29,13 +29,13 @@ describe('borrowing-capacity', () => {
putBorrowingCapacityCalculation
);
const calculateBorrowingCapacityInput: CalculateBorrowingCapacityInput = {
borrowerEmail: 'john.doe@example',
borrowerEmail: 'john.doe@example.com',
grossAnnualIncome: 60_000,
employmentStatus: 'FULL_TIME',
};
const borrowerProfile: BorrowerProfile = {
dob: '1981-11-02',
email: 'john.doe@example',
email: 'john.doe@example.com',
name: 'John Doe',
creditScore: 500,
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ describe('create-borrower-profile', () => {
const borrowerProfile: BorrowerProfile = {
creditScore: 500,
dob: '1981-11-02',
email: 'john.doe@example',
email: 'john.doe@example.com',
name: 'John Doe',
};
describe('given a borrower profile', () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ describe('process loan-application', () => {

const borrowerProfile: BorrowerProfile = {
dob: '1981-11-02',
email: 'john.doe@example',
email: 'john.doe@example.com',
name: 'John Doe',
creditScore: 500,
};
Expand All @@ -39,7 +39,7 @@ describe('process loan-application', () => {
const assessLoanApplicationSpy = vi.mocked(assessLoanApplication);

const processLoanApplicationInput: ProcessLoanApplicationInput = {
borrowerEmail: 'john.doe@example',
borrowerEmail: 'john.doe@example.com',
employmentStatus: 'FULL_TIME',
grossAnnualIncome: 100000,
monthlyExpenses: 5000,
Expand Down
2 changes: 1 addition & 1 deletion docs/docs/borrower-profile/table-design.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ tags:

| Name | Type | Example | Description |
|-------------|--------|----------------------|--------------------------------------------------------------|
| pk | string | [email protected] | Primary Key: The borrower's email address |
| pk | string | [email protected] | Partition Key: The borrower's email address |
| sk | string | BORROWER | Sort Key: The entity type (BORROWER) |
| creditScore | number | 500 | Borrower's credit score. This is a number between 0 and 1000 |
| dob | string | '1985-09-01' | Borrower's date of birth as an ISO8061 date string |
Expand Down
4 changes: 2 additions & 2 deletions docs/docs/borrowing-capacity/overview.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,7 @@ sequenceDiagram
User->>+PrimaryAdaptor: Request Borrowing Power
PrimaryAdaptor->>+CalculateBorrowingCapacityUseCase: Validate & Forward Request
CalculateBorrowingCapacityUseCase->>+GetBorrowerProfileSecondaryAdapter: Get Borrower Profile
GetBorrowerProfileSecondaryAdapter-->>-CalculateBorrowingCapacityUseCase: throws BorrowerProfileDoesNotExistError
CalculateBorrowingCapacityUseCase-->>-PrimaryAdaptor: throws InternalServerError
GetBorrowerProfileSecondaryAdapter-->>-CalculateBorrowingCapacityUseCase: undefined
CalculateBorrowingCapacityUseCase-->>-PrimaryAdaptor: throws BorrowerDoesNotExistError
PrimaryAdaptor-->>-User: {statusCode: 400, message: 'Borrower with the provided email does not exist'}
```
2 changes: 1 addition & 1 deletion docs/docs/borrowing-capacity/table-design.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ tags:

| Name | Type | Example | Description |
|--------------------------------|--------|--------------------------------------------------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------|
| pk | string | [email protected] | Primary Key: The borrower's email address |
| pk | string | [email protected] | Partition Key: The borrower's email address |
| sk | string | BORROWING_CAPACITY_CALCULATION#2dd0ce10-4d28-4275-af31-b2e5edc7430a#TIMESTAMP#2024-03-15T00:00:00.000Z | Sort Key: The entity type ('BORROWING_CAPACITY_CALCULATION'), borrowingCapacityCalculationId and timestamp separated by '#' |
| estimatedBorrowingCapacity | number | 100000 | The borrower's borrowing capacity. This is a number >= 0 |
| grossAnnualIncome | number | 40000 | The borrower's gross annual income. This is a number >= 0 |
Expand Down
15 changes: 15 additions & 0 deletions docs/docs/clean-up.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
---
sidebar_position: 10
tags:
- clean-up
---


# Clean Up


Run the following command in your terminal:

```shell
pnpm run remove
```
4 changes: 3 additions & 1 deletion docs/docs/deployment.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,9 @@ tags:
You'll be prompted to a stage name. Enter your first name and hit `Enter ⏎`:

```shell
Please
> sst deploy

Please enter a name you'd like to use for your personal stage. Or hit enter to use node: joe
```
4. Run the unit tests:
Expand Down
2 changes: 1 addition & 1 deletion docs/docs/loan-application/overview.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ flowchart TD

## Scenarios

### Loan Application Completed (200)
### Loan Application Completed (201)

```mermaid
sequenceDiagram
Expand Down
2 changes: 1 addition & 1 deletion docs/docs/loan-application/table-design.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ tags:

| Name | Type | Example | Description |
|-----------------------|--------|------------------------------------------------------------------------------------------|----------------------------------------------------------------------------------------------------|
| pk | string | [email protected] | Primary Key: The borrower's email address |
| pk | string | [email protected] | Partition Key: The borrower's email address |
| sk | string | LOAN_APPLICATION#2dd0ce10-4d28-4275-af31-b2e5edc7430a#TIMESTAMP#2024-03-15T00:00:00.000Z | Sort Key: The entity type ('LOAN_APPLICATION'), loanApplicationId and timestamp separated by '#' |
| loanApplicationStatus | string | 'APPROVED' | The borrower's employment status. One of 'APPROVED', 'REJECTED', 'REVIEW' |
| creditScore | number | 700 | Borrower's credit score. This is a number between 0 and 1000 |
Expand Down
138 changes: 138 additions & 0 deletions docs/docs/loan-application/workshop-activities/integration.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
---
sidebar_position: 5
tags:
- integration
---

# Integration

## Scenario 1

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',
}),
});
});
// ...
});
```

## Scenario 2

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('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',
},
response: expect.objectContaining({
status: 400,
statusText: 'Bad Request',
}),
});

await Promise.all([
deleteBorrowerProfile(borrowerEmail),
deleteLoanApplications(borrowerEmail),
]);
});
// ...
});
```

## Scenario 3

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('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', {
body: {
borrowerEmail: borrowerEmail,
grossAnnualIncome: 100_000,
employmentStatus: 'FULL_TIME',
monthlyExpenses: 1000,
},
})
).resolves.toEqual({
data: {
loanApplicationStatus: 'APPROVED',
},
response: expect.objectContaining({
status: 201,
statusText: 'Created',
}),
});

await Promise.all([
deleteBorrowerProfile(borrowerEmail),
deleteLoanApplications(borrowerEmail),
]);
});
// ...
});
```
Original file line number Diff line number Diff line change
Expand Up @@ -187,7 +187,7 @@ describe('api gw apply for loan', () => {
const monthlyExpenses = 1500;
describe('given an api gateway proxy event', () => {
// ...
describe('given the use case rejects with borrower not found', () => {
describe('given the use case rejects with borrower profile does not exist', () => {
// highlight-next-line
it.todo('resolves with a status code 400 and a message containing borrower with the provided email does not exist', async () => {
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,7 @@ describe('ddb-put-loan-application', () => {
grossAnnualIncome: loanApplication.grossAnnualIncome,
monthlyExpenses: loanApplication.monthlyExpenses,
loanApplicationStatus: loanApplication.loanApplicationStatus,
employmentStatus: loanApplication.employmentStatus,
});
}
);
Expand All @@ -186,7 +187,7 @@ describe('ddb-put-loan-application', () => {
Run the following command:

```shell
pnpm run test
pnpm run test:integration
```

You should see a failing test case, similar to the following:
Expand Down Expand Up @@ -281,7 +282,7 @@ describe('ddb-put-loan-application', () => {
});

const loanApplication: PutLoanApplicationInput = {
borrowerEmail: 'john.doe@example',
borrowerEmail: 'john.doe@example.com',
creditScore: 700,
employmentStatus: 'FULL_TIME',
grossAnnualIncome: 100000,
Expand Down
11 changes: 0 additions & 11 deletions docs/docs/loan-application/workshop-activities/use-case.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -486,17 +486,6 @@ describe('process loan-application', () => {
describe('given the borrower profile is successfully retrieved', () => {
// highlight-next-line
it('writes the loan application to the database', async () => {
await processLoanApplication(processLoanApplicationInput);
expect(putLoanApplicationSpy).toHaveBeenCalledWith({
loanApplicationId: expect.any(String),
borrowerEmail: processLoanApplicationInput.borrowerEmail,
creditScore: borrowerProfile.creditScore,
employmentStatus: processLoanApplicationInput.employmentStatus,
grossAnnualIncome: processLoanApplicationInput.grossAnnualIncome,
loanApplicationStatus: 'APPROVED',
monthlyExpenses: processLoanApplicationInput.monthlyExpenses,
timestamp: mockedTimestamp,
});
});
// ...
});
Expand Down
2 changes: 1 addition & 1 deletion docs/docs/stories.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -16,5 +16,5 @@ timeline
Get Borrower Profile : Look up Borrower Profile w/ Email Address
Collect Additional Information: Employment Status, Gross Annual Income
Calculate Borrower's Age: Use D.O.B. from Borrower's profile.
Calculate Borrowing Capacity: Uses: Age,Employment Status, Gross Annual Income
Calculate Borrowing Capacity: Uses - Age,Employment Status, Gross Annual Income
```
Loading

0 comments on commit ff79786

Please sign in to comment.