Skip to content

Commit

Permalink
add code to record and play back tests from real data
Browse files Browse the repository at this point in the history
  • Loading branch information
johnduffell committed Jun 5, 2024
1 parent 45f7154 commit 2bc50ab
Show file tree
Hide file tree
Showing 15 changed files with 5,179 additions and 5,307 deletions.
3 changes: 2 additions & 1 deletion handlers/discount-api/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@
},
"dependencies": {
"dayjs": "^1.11.11",
"zod": "^3.22.4"
"zod": "^3.22.4",
"@aws-sdk/client-s3": "3.590.0"
},
"devDependencies": {
"@types/aws-lambda": "^8.10.129"
Expand Down
4 changes: 3 additions & 1 deletion handlers/discount-api/src/discountEndpoint.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { addDiscount, previewDiscount } from '@modules/zuora/addDiscount';
import { getBillingPreview } from '@modules/zuora/billingPreview';
import { getAccount } from '@modules/zuora/getAccount';
import { getSubscription } from '@modules/zuora/getSubscription';
import type { FetchInterface } from '@modules/zuora/requestLogger';
import { ZuoraClient } from '@modules/zuora/zuoraClient';
import type { ZuoraSubscription } from '@modules/zuora/zuoraSchemas';
import { getZuoraCatalog } from '@modules/zuora-catalog/S3';
Expand All @@ -21,8 +22,9 @@ export const discountEndpoint = async (
preview: boolean,
headers: APIGatewayProxyEventHeaders,
body: string | null,
fetchInterface: FetchInterface,
) => {
const zuoraClient = await ZuoraClient.create(stage);
const zuoraClient = await ZuoraClient.create(stage, fetchInterface);
const catalog = await getZuoraCatalog(stage);
const eligibilityChecker = new EligibilityChecker(catalog);

Expand Down
40 changes: 29 additions & 11 deletions handlers/discount-api/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,32 +1,50 @@
import { ValidationError } from '@modules/errors';
import type { Stage } from '@modules/stage';
import type {
APIGatewayProxyEvent,
APIGatewayProxyResult,
Handler,
} from 'aws-lambda';
import type { FetchInterface } from '@modules/zuora/requestLogger';
import { RequestLogger } from '@modules/zuora/requestLogger';
import type { APIGatewayProxyEvent, APIGatewayProxyResult } from 'aws-lambda';
import { discountEndpoint } from './discountEndpoint';

const stage = process.env.STAGE as Stage;
export const handler: Handler = async (
const stage = process.env.STAGE as Stage | undefined;
export const handler: (
event: APIGatewayProxyEvent,
) => Promise<APIGatewayProxyResult> = async (
event: APIGatewayProxyEvent,
): Promise<APIGatewayProxyResult> => {
console.log(`Input is ${JSON.stringify(event)}`);
const response = await routeRequest(event);
const requestLogger = new RequestLogger(stage ?? 'DEV');
requestLogger.setRequest(JSON.stringify(event));
const response = await routeRequest(event, requestLogger);
console.log(`Response is ${JSON.stringify(response)}`);
await requestLogger.setResponse(response);
return response;
};

const routeRequest = async (event: APIGatewayProxyEvent) => {
export const routeRequest = async (
event: APIGatewayProxyEvent,
fetchInterface: FetchInterface,
): Promise<APIGatewayProxyResult> => {
try {
switch (true) {
case event.path === '/apply-discount' && event.httpMethod === 'POST': {
console.log('Applying a discount');
return await discountEndpoint(stage, false, event.headers, event.body);
return await discountEndpoint(
stage ?? 'CODE',
false,
event.headers,
event.body,
fetchInterface,
);
}
case event.path === '/preview-discount' && event.httpMethod === 'POST': {
console.log('Previewing discount');
return await discountEndpoint(stage, true, event.headers, event.body);
return await discountEndpoint(
stage ?? 'CODE',
true,
event.headers,
event.body,
fetchInterface,
);
}
default:
return {
Expand Down
5 changes: 5 additions & 0 deletions handlers/discount-api/test/previewDiscountIntegration.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { discountEndpoint } from '../src/discountEndpoint';
import { previewDiscountSchema } from '../src/responseSchema';
import { createDigitalSubscription, createSubscription } from './helpers';
import { supporterPlusSubscribeBody } from './fixtures/request-bodies/supporterplus-subscribe-body-tier2';
import { PassThrough } from '@modules/zuora/requestLogger';

const stage: Stage = 'CODE';
const validIdentityId = '200175946';
Expand Down Expand Up @@ -37,6 +38,7 @@ test("Subscriptions which don't belong to the provided identity Id are not eligi
true,
{ 'x-identity-id': invalidIdentityId },
JSON.stringify(requestBody),
new PassThrough(),
);
}).rejects.toThrow('does not belong to identity ID');

Expand Down Expand Up @@ -71,6 +73,7 @@ test('Subscriptions on the old price are not eligible', async () => {
true,
{ 'x-identity-id': validIdentityId },
JSON.stringify(requestBody),
new PassThrough(),
);
}).rejects.toThrow('it is not eligible for a discount');

Expand Down Expand Up @@ -105,6 +108,7 @@ test('Subscriptions on the new price are eligible', async () => {
true,
{ 'x-identity-id': validIdentityId },
JSON.stringify(requestBody),
new PassThrough(),
);
const eligibilityCheckResult = previewDiscountSchema.parse(
JSON.parse(result.body),
Expand Down Expand Up @@ -146,6 +150,7 @@ test('Supporter Plus subscriptions are eligible', async () => {
true,
{ 'x-identity-id': validIdentityId },
JSON.stringify(requestBody),
new PassThrough(),
);
const eligibilityCheckResult = previewDiscountSchema.parse(
JSON.parse(result.body),
Expand Down
68 changes: 68 additions & 0 deletions handlers/discount-api/test/recordedE2e.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
/**
* @group integration
*/
import { handler, routeRequest } from '../src';
import { getKeys, RequestPlayback } from '@modules/zuora/requestLogger';

async function testSingle(requestPlayback: RequestPlayback) {
const response = await routeRequest(
JSON.parse(requestPlayback.request),
requestPlayback,
);
console.log(`Response is ${response.statusCode}\n${response.body}`);
expect(response.statusCode).toEqual(requestPlayback.response.statusCode);
expect(response.body).toEqual(requestPlayback.response.body);
}
test('call the lambda with all recorded data', async () => {
const keys = await getKeys('DEV');
for (const key of keys) {
const requestPlayback = await RequestPlayback.loadKey(key);
await testSingle(requestPlayback);
}
});

test('call the lambda with recorded data', async () => {
// load data from s3
const requestPlayback = await RequestPlayback.load('DEV', '1717622177150');
await testSingle(requestPlayback);
});

test('call the lambda to record data - preview', async () => {
const testDataObject = JSON.parse(previewTestData);
// const expected = '';
const response = await handler(testDataObject);
expect(response.statusCode).toEqual(200);
expect(response.body).toEqual(expectedBody);
}, 30000);

test('call the lambda to record data - apply', async () => {
const testDataObject = JSON.parse(applyTestData);
// const expected = '';
const response = await handler(testDataObject);
expect(response.statusCode).toEqual(200);
expect(response.body).toEqual('Success');
}, 30000);

const subscriptionNumber = 'A-S00894688';
const identityId = '200259370';
const previewTestData = `{
"path": "/preview-discount",
"httpMethod": "POST",
"headers": {
"Content-Type": "application/json",
"x-identity-id": "${identityId}"
},
"body": "{\\n \\"subscriptionNumber\\": \\"${subscriptionNumber}\\"\\n}"
}`;

const applyTestData = `{
"path": "/apply-discount",
"httpMethod": "POST",
"headers": {
"Content-Type": "application/json",
"x-identity-id": "${identityId}"
},
"body": "{\\n \\"subscriptionNumber\\": \\"${subscriptionNumber}\\"\\n}"
}`;

const expectedBody = `{"discountedPrice":0,"upToPeriods":2,"upToPeriodsType":"Months"}`;
2 changes: 1 addition & 1 deletion handlers/generate-product-catalog/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,6 @@
},
"devDependencies": {
"@types/aws-lambda": "^8.10.129",
"@aws-sdk/client-s3": "3.451.0"
"@aws-sdk/client-s3": "3.590.0"
}
}
2 changes: 1 addition & 1 deletion handlers/product-switch-api/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
"dependencies": {
"dayjs": "^1.11.11",
"zod": "^3.22.4",
"@aws-sdk/client-sqs": "3.451.0"
"@aws-sdk/client-sqs": "3.590.0"
},
"devDependencies": {
"@types/aws-lambda": "^8.10.129"
Expand Down
2 changes: 1 addition & 1 deletion modules/aws/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,6 @@
"name": "aws",
"version": "1.0.0",
"dependencies": {
"@aws-sdk/credential-provider-node": "3.451.0"
"@aws-sdk/credential-provider-node": "3.590.0"
}
}
2 changes: 1 addition & 1 deletion modules/email/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,6 @@
"it-test": "jest --group=integration"
},
"dependencies": {
"@aws-sdk/client-sqs": "3.451.0"
"@aws-sdk/client-sqs": "3.590.0"
}
}
2 changes: 1 addition & 1 deletion modules/zuora-catalog/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
"it-test": "jest --group=integration"
},
"dependencies": {
"@aws-sdk/client-s3": "3.451.0",
"@aws-sdk/client-s3": "3.590.0",
"zod": "^3.22.4"
}
}
4 changes: 2 additions & 2 deletions modules/zuora/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@
"it-test": "jest --group=integration"
},
"dependencies": {
"@aws-sdk/client-s3": "3.451.0",
"@aws-sdk/client-secrets-manager": "3.451.0",
"@aws-sdk/client-s3": "3.590.0",
"@aws-sdk/client-secrets-manager": "3.590.0",
"dayjs": "^1.11.11",
"zod": "^3.22.4"
}
Expand Down
11 changes: 8 additions & 3 deletions modules/zuora/src/bearerTokenProvider.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { zuoraServerUrl } from './common';
import type { OAuthClientCredentials, ZuoraBearerToken } from './zuoraSchemas';
import { zuoraBearerTokenSchema } from './zuoraSchemas';
import { FetchInterface } from '@modules/zuora/requestLogger';

export class BearerTokenProvider {
private bearerToken: ZuoraBearerToken | null = null;
Expand All @@ -9,6 +10,7 @@ export class BearerTokenProvider {
constructor(
private stage: string,
private credentials: OAuthClientCredentials,
private fetchInterface: FetchInterface,
) {}

private tokenIsExpired = () => {
Expand Down Expand Up @@ -40,14 +42,17 @@ export class BearerTokenProvider {
['client_id', this.credentials.clientId],
['client_secret', this.credentials.clientSecret],
['grant_type', 'client_credentials'],
]);
]).toString();

const response = await fetch(url, {
const response = await this.fetchInterface.execute(url, {
method: 'POST',
headers: {
'Content-type': 'application/x-www-form-urlencoded',
},
body: formData,
});

const json = await response.json();
const json = JSON.parse(response.text);
console.log('Response from Zuora was: ', json);

return zuoraBearerTokenSchema.parse(json);
Expand Down
Loading

0 comments on commit 2bc50ab

Please sign in to comment.