Skip to content

Commit 8c5fbd6

Browse files
committed
Add unit tests for the database client
1 parent e3dcf14 commit 8c5fbd6

4 files changed

Lines changed: 289 additions & 1 deletion

File tree

packages/postDatedLambda/src/databaseClient.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ const client = new DynamoDBClient()
1313
const tableName = process.env.TABLE_NAME ?? "PrescriptionStatusUpdates"
1414
const pharmacyPrescriptionIndexName = "PharmacyODSCodePrescriptionIDIndex"
1515

16-
function createPrescriptionLookupKey(prescriptionID: string, pharmacyODSCode: string): string {
16+
export function createPrescriptionLookupKey(prescriptionID: string, pharmacyODSCode: string): string {
1717
return `${prescriptionID.toUpperCase()}#${pharmacyODSCode.toUpperCase()}`
1818
}
1919

Lines changed: 255 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,255 @@
1+
import {
2+
expect,
3+
describe,
4+
it,
5+
jest
6+
} from "@jest/globals"
7+
8+
import * as dynamo from "@aws-sdk/client-dynamodb"
9+
10+
import {Logger} from "@aws-lambda-powertools/logger"
11+
12+
import {createMockPostModifiedDataItem} from "./testUtils"
13+
14+
// Uses unstable jest method to enable mocking while using ESM. To be replaced in future.
15+
export function mockDynamoDBClient() {
16+
const mockSend = jest.fn()
17+
jest.unstable_mockModule("@aws-sdk/client-dynamodb", () => {
18+
return {
19+
...dynamo,
20+
DynamoDBClient: jest.fn().mockImplementation(() => ({
21+
send: mockSend
22+
}))
23+
}
24+
})
25+
return {mockSend}
26+
}
27+
28+
const {mockSend} = mockDynamoDBClient()
29+
const {
30+
createPrescriptionLookupKey,
31+
getExistingRecordsByPrescriptionID,
32+
fetchExistingRecordsForPrescriptions,
33+
enrichMessagesWithExistingRecords
34+
} = await import("../src/databaseClient")
35+
36+
const logger = new Logger({serviceName: "postDatedLambdaTEST"})
37+
38+
describe("databaseClient", () => {
39+
describe("createPrescriptionLookupKey", () => {
40+
it("should create the correct lookup key", () => {
41+
const prescriptionID = "abc123"
42+
const pharmacyODSCode = "pharm001"
43+
const expectedKey = "ABC123#PHARM001"
44+
45+
const result = createPrescriptionLookupKey(prescriptionID, pharmacyODSCode)
46+
expect(result).toBe(expectedKey)
47+
})
48+
})
49+
50+
describe("getExistingRecordsByPrescriptionID", () => {
51+
it("should return existing records from DynamoDB", async () => {
52+
const prescriptionID = "testPrescID"
53+
const pharmacyODSCode = "testPharmODS"
54+
55+
// Mock DynamoDB response
56+
const mockItems = [
57+
{
58+
PrescriptionID: {S: prescriptionID},
59+
PharmacyODSCode: {S: pharmacyODSCode},
60+
Status: {S: "Dispensed"},
61+
LastModified: {S: "2024-01-01T12:00:00Z"}
62+
},
63+
{
64+
PrescriptionID: {S: prescriptionID},
65+
PharmacyODSCode: {S: pharmacyODSCode},
66+
Status: {S: "ReadyForCollection"},
67+
LastModified: {S: "2023-12-31T12:00:00Z"}
68+
}
69+
]
70+
71+
mockSend.mockReturnValueOnce({
72+
Items: mockItems,
73+
LastEvaluatedKey: undefined
74+
})
75+
76+
const records = await getExistingRecordsByPrescriptionID(
77+
prescriptionID,
78+
pharmacyODSCode,
79+
logger
80+
)
81+
82+
expect(records).toHaveLength(2)
83+
expect(records[0].Status).toBe("Dispensed")
84+
expect(records[1].Status).toBe("ReadyForCollection")
85+
})
86+
87+
it("Should log and throw an error if the DynamoDB query fails", async () => {
88+
const prescriptionID = "errorPrescID"
89+
const pharmacyODSCode = "errorPharmODS"
90+
91+
// Mock DynamoDB to throw an error
92+
const mockError = new Error("DynamoDB query failed")
93+
mockSend.mockImplementationOnce(() => {
94+
throw mockError
95+
})
96+
97+
await expect(
98+
getExistingRecordsByPrescriptionID(
99+
prescriptionID,
100+
pharmacyODSCode,
101+
logger
102+
)
103+
).rejects.toThrow("DynamoDB query failed")
104+
})
105+
})
106+
107+
describe("fetchExistingRecordsForPrescriptions", () => {
108+
it("should fetch existing records for multiple prescriptions", async () => {
109+
const prescriptions = [
110+
createMockPostModifiedDataItem({PrescriptionID: "presc1", PharmacyODSCode: "pharmA"}),
111+
createMockPostModifiedDataItem({PrescriptionID: "presc2", PharmacyODSCode: "pharmB"})
112+
]
113+
114+
// Mock DynamoDB responses
115+
const mockItemsPresc1 = [
116+
{
117+
PrescriptionID: {S: "presc1"},
118+
PharmacyODSCode: {S: "pharmA"},
119+
Status: {S: "Dispensed"},
120+
LastModified: {S: "2024-01-01T12:00:00Z"}
121+
}
122+
]
123+
124+
const mockItemsPresc2 = [
125+
{
126+
PrescriptionID: {S: "presc2"},
127+
PharmacyODSCode: {S: "pharmB"},
128+
Status: {S: "ReadyForCollection"},
129+
LastModified: {S: "2024-01-02T12:00:00Z"}
130+
}
131+
]
132+
133+
mockSend
134+
.mockReturnValueOnce({
135+
Items: mockItemsPresc1,
136+
LastEvaluatedKey: undefined
137+
})
138+
.mockReturnValueOnce({
139+
Items: mockItemsPresc2,
140+
LastEvaluatedKey: undefined
141+
})
142+
143+
const result = await fetchExistingRecordsForPrescriptions(
144+
prescriptions,
145+
logger
146+
)
147+
148+
expect(result.length).toBe(2)
149+
expect(result[0].existingRecords.length).toBe(1)
150+
expect(result[0].existingRecords[0].Status).toBe("Dispensed")
151+
expect(result[1].existingRecords.length).toBe(1)
152+
expect(result[1].existingRecords[0].Status).toBe("ReadyForCollection")
153+
})
154+
155+
it(
156+
"Should log an error if the fetch fails for one prescription, and set the existingRecords to empty array",
157+
async () => {
158+
const prescriptions = [
159+
createMockPostModifiedDataItem({PrescriptionID: "presc1", PharmacyODSCode: "pharmA"}),
160+
createMockPostModifiedDataItem({PrescriptionID: "errorPresc", PharmacyODSCode: "errorPharm"})
161+
]
162+
163+
// Mock DynamoDB responses
164+
const mockItemsPresc1 = [
165+
{
166+
PrescriptionID: {S: "presc1"},
167+
PharmacyODSCode: {S: "pharmA"},
168+
Status: {S: "Dispensed"},
169+
LastModified: {S: "2024-01-01T12:00:00Z"}
170+
}
171+
]
172+
173+
mockSend
174+
.mockReturnValueOnce({
175+
Items: mockItemsPresc1,
176+
LastEvaluatedKey: undefined
177+
})
178+
.mockImplementationOnce(() => {
179+
throw new Error("DynamoDB query failed")
180+
})
181+
182+
const result = await fetchExistingRecordsForPrescriptions(
183+
prescriptions,
184+
logger
185+
)
186+
187+
expect(result.length).toBe(2)
188+
expect(result[0].existingRecords.length).toBe(1)
189+
expect(result[0].existingRecords[0].Status).toBe("Dispensed")
190+
expect(result[1].existingRecords.length).toBe(0)
191+
})
192+
})
193+
194+
describe("enrichMessagesWithExistingRecords", () => {
195+
it("should enrich messages with existing records", async () => {
196+
const prescriptions = [
197+
createMockPostModifiedDataItem({PrescriptionID: "presc1", PharmacyODSCode: "pharmA"}),
198+
createMockPostModifiedDataItem({PrescriptionID: "presc2", PharmacyODSCode: "pharmB"})
199+
]
200+
201+
// Mock DynamoDB responses
202+
const mockItemsPresc1 = [
203+
{
204+
PrescriptionID: {S: "presc1"},
205+
PharmacyODSCode: {S: "pharmA"},
206+
Status: {S: "Dispensed"},
207+
LastModified: {S: "2024-01-01T12:00:00Z"}
208+
}
209+
]
210+
211+
const mockItemsPresc2 = [
212+
{
213+
PrescriptionID: {S: "presc2"},
214+
PharmacyODSCode: {S: "pharmB"},
215+
Status: {S: "ReadyForCollection"},
216+
LastModified: {S: "2024-01-02T12:00:00Z"}
217+
}
218+
]
219+
220+
mockSend
221+
.mockReturnValueOnce({
222+
Items: mockItemsPresc1,
223+
LastEvaluatedKey: undefined
224+
})
225+
.mockReturnValueOnce({
226+
Items: mockItemsPresc2,
227+
LastEvaluatedKey: undefined
228+
})
229+
230+
const messages = prescriptions.map((presc) => ({
231+
prescriptionData: presc
232+
}))
233+
234+
const enrichedMessages = await enrichMessagesWithExistingRecords(
235+
messages,
236+
logger
237+
)
238+
239+
expect(enrichedMessages.length).toBe(2)
240+
expect(enrichedMessages[0].existingRecords.length).toBe(1)
241+
expect(enrichedMessages[0].existingRecords[0].Status).toBe("Dispensed")
242+
expect(enrichedMessages[1].existingRecords.length).toBe(1)
243+
expect(enrichedMessages[1].existingRecords[0].Status).toBe("ReadyForCollection")
244+
})
245+
})
246+
247+
it("Should return empty array when no messages are provided", async () => {
248+
const enrichedMessages = await enrichMessagesWithExistingRecords(
249+
[],
250+
logger
251+
)
252+
253+
expect(enrichedMessages).toEqual([])
254+
})
255+
})
File renamed without changes.
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
import {PSUDataItem, PostDatedNotifyDataItem} from "packages/common/commonTypes/lib/src"
2+
3+
export function createMockDataItem(overrides: Partial<PSUDataItem>): PSUDataItem {
4+
return {
5+
LastModified: "2023-01-02T00:00:00Z",
6+
LineItemID: "spamandeggs",
7+
PatientNHSNumber: "0123456789",
8+
PharmacyODSCode: "ABC123",
9+
PrescriptionID: "abcdef-ghijkl-mnopqr",
10+
RequestID: "x-request-id",
11+
Status: "ready to collect",
12+
TaskID: "mnopqr-ghijkl-abcdef",
13+
TerminalStatus: "ready to collect",
14+
ApplicationName: "Internal Test System",
15+
ExpiryTime: 123,
16+
...overrides
17+
}
18+
}
19+
20+
export function createMockPostModifiedDataItem(overrides: Partial<PostDatedNotifyDataItem>): PostDatedNotifyDataItem {
21+
return {
22+
LastModified: "2023-01-02T00:00:00Z",
23+
LineItemID: "spamandeggs",
24+
PatientNHSNumber: "0123456789",
25+
PharmacyODSCode: "ABC123",
26+
PrescriptionID: "abcdef-ghijkl-mnopqr",
27+
RequestID: "x-request-id",
28+
Status: "ready to collect",
29+
TaskID: "mnopqr-ghijkl-abcdef",
30+
PostDatedLastModifiedSetAt: "Changed dosage instructions",
31+
...overrides
32+
}
33+
}

0 commit comments

Comments
 (0)