Skip to content

Commit 54ba53d

Browse files
Initial idempotency test
1 parent 500e7af commit 54ba53d

3 files changed

Lines changed: 168 additions & 0 deletions

File tree

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
import { expect, test } from "@playwright/test";
2+
import {
3+
getTotalAllocationForVolumeGroup,
4+
getTotalDailyAllocation,
5+
} from "tests/helpers/supplier-quotas-helper";
6+
import { supplierIdFromSupplierAllocatorLog } from "tests/helpers/aws-cloudwatch-helper";
7+
import { logger } from "tests/helpers/pino-logger";
8+
import { format } from "date-fns";
9+
import { toZonedTime } from "date-fns-tz";
10+
import { randomUUID } from "node:crypto";
11+
import { createPreparedV1Event } from "tests/helpers/event-fixtures";
12+
import { sendSnsBatchEvent } from "tests/helpers/send-sns-event";
13+
14+
test.describe("Supplier Allocation Tests", () => {
15+
test("Verify that supplier allocations are correctly updated for a volume group", async () => {
16+
test.setTimeout(180_000);
17+
const volumeGroupId = "volumeGroup-test3";
18+
const originalTotalAllocation =
19+
await getTotalAllocationForVolumeGroup(volumeGroupId);
20+
logger.info(
21+
`Total allocation for volume group ${volumeGroupId}: ${originalTotalAllocation}`,
22+
);
23+
24+
const allocationDate = format(
25+
toZonedTime(new Date(), "Europe/London"),
26+
"yyyy-MM-dd",
27+
);
28+
const originalTotalDailyAllocation =
29+
await getTotalDailyAllocation(allocationDate);
30+
logger.info(
31+
`Total daily allocation for date ${allocationDate}: ${originalTotalDailyAllocation}`,
32+
);
33+
34+
// Create 2 messages with same domain id
35+
const domainId = randomUUID();
36+
37+
const message1 = createPreparedV1Event({ domainId });
38+
const message2 = createPreparedV1Event({ domainId });
39+
40+
const eventBatch = [message1, message2];
41+
const response = await sendSnsBatchEvent(
42+
eventBatch.map((event) => ({ id: event.id, message: event })),
43+
);
44+
expect(response.Successful).toHaveLength(eventBatch.length);
45+
46+
await supplierIdFromSupplierAllocatorLog(domainId);
47+
48+
const newTotalAllocation =
49+
await getTotalAllocationForVolumeGroup(volumeGroupId);
50+
logger.info(
51+
`New total allocation for volume group ${volumeGroupId}: ${newTotalAllocation}`,
52+
);
53+
expect(newTotalAllocation).toBe(originalTotalAllocation + 1);
54+
55+
const newTotalDailyAllocation =
56+
await getTotalDailyAllocation(allocationDate);
57+
logger.info(
58+
`New total daily allocation for date ${allocationDate}: ${newTotalDailyAllocation}`,
59+
);
60+
expect(newTotalDailyAllocation).toBe(originalTotalDailyAllocation + 1);
61+
});
62+
});

tests/constants/api-constants.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,3 +16,4 @@ export const EVENT_SUBSCRIPTION_TOPIC_ARN =
1616
process.env.EVENT_SUBSCRIPTION_TOPIC_ARN ??
1717
`arn:aws:sns:${AWS_REGION}:${AWS_ACCOUNT_ID}:${EVENT_SUBSCRIPTION_TOPIC_NAME}`;
1818
export const LETTERQUEUE_TABLENAME = `nhs-${envName}-supapi-letter-queue`;
19+
export const SUPPLIER_QUOTAS_TABLENAME = `nhs-${envName}-supapi-supplier-quotas`;
Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
import { DynamoDBClient } from "@aws-sdk/client-dynamodb";
2+
import { DynamoDBDocumentClient, GetCommand } from "@aws-sdk/lib-dynamodb";
3+
import z from "zod";
4+
import { SUPPLIER_QUOTAS_TABLENAME } from "../constants/api-constants";
5+
import { logger } from "./pino-logger";
6+
7+
const ddb = new DynamoDBClient({});
8+
const docClient = DynamoDBDocumentClient.from(ddb);
9+
10+
export const overallAllocationSchema = z.object({
11+
id: z.string(),
12+
volumeGroup: z.string(),
13+
allocations: z.record(z.string(), z.number()),
14+
});
15+
16+
export const dailyAllocationSchema = z.object({
17+
id: z.string(),
18+
date: z.string(),
19+
allocations: z.record(z.string(), z.number()),
20+
});
21+
22+
export async function getTotalAllocationForVolumeGroup(
23+
volumeGroupId: string,
24+
): Promise<number> {
25+
try {
26+
const result = await docClient.send(
27+
new GetCommand({
28+
TableName: SUPPLIER_QUOTAS_TABLENAME,
29+
Key: { pk: "ENTITY#overall-allocation", sk: `ID#${volumeGroupId}` },
30+
}),
31+
);
32+
logger.info(`Selecting from table name: ${SUPPLIER_QUOTAS_TABLENAME}`);
33+
34+
if (!result.Item) {
35+
logger.warn(
36+
`No overall allocation found for volume group ${volumeGroupId}`,
37+
);
38+
return 0; // Default to 0 if no allocation record exists
39+
}
40+
// Strip DynamoDB keys before parsing
41+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
42+
const { pk, sk, ...item } = result.Item;
43+
const overallAllocation = overallAllocationSchema.parse(item);
44+
45+
logger.info(
46+
`Fetched overall allocation for volume group ${volumeGroupId}: ${JSON.stringify(overallAllocation)}`,
47+
);
48+
const { allocations } = overallAllocation;
49+
50+
const totalAllocation = Object.values(allocations).reduce(
51+
(sum, allocation) => sum + allocation,
52+
0,
53+
);
54+
logger.info(
55+
`Fetched overall allocation for volume group ${volumeGroupId}: ${totalAllocation}`,
56+
);
57+
return totalAllocation;
58+
} catch (error) {
59+
logger.error(
60+
`Error fetching overall allocation for volume group ${volumeGroupId}: ${error}`,
61+
);
62+
throw error;
63+
}
64+
}
65+
export async function getTotalDailyAllocation(
66+
allocationDate: string,
67+
): Promise<number> {
68+
try {
69+
const result = await docClient.send(
70+
new GetCommand({
71+
TableName: SUPPLIER_QUOTAS_TABLENAME,
72+
Key: { pk: "ENTITY#daily-allocation", sk: `ID#${allocationDate}` },
73+
}),
74+
);
75+
logger.info(`Selecting from table name: ${SUPPLIER_QUOTAS_TABLENAME}`);
76+
77+
if (!result.Item) {
78+
logger.warn(`No daily allocation found for date ${allocationDate}`);
79+
return 0; // Default to 0 if no allocation record exists
80+
}
81+
// Strip DynamoDB keys before parsing
82+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
83+
const { pk, sk, ...item } = result.Item;
84+
const dailyAllocation = dailyAllocationSchema.parse(item);
85+
86+
logger.info(
87+
`Fetched daily allocation for date ${allocationDate}: ${JSON.stringify(dailyAllocation)}`,
88+
);
89+
const { allocations } = dailyAllocation;
90+
91+
const totalAllocation = Object.values(allocations).reduce(
92+
(sum, allocation) => sum + allocation,
93+
0,
94+
);
95+
logger.info(
96+
`Fetched daily allocation for date ${allocationDate}: ${totalAllocation}`,
97+
);
98+
return totalAllocation;
99+
} catch (error) {
100+
logger.error(
101+
`Error fetching daily allocation for date ${allocationDate}: ${error}`,
102+
);
103+
throw error;
104+
}
105+
}

0 commit comments

Comments
 (0)