From 3ce72d79be158713c4dfadbca78c758550789723 Mon Sep 17 00:00:00 2001 From: Brendan Chen Date: Wed, 12 Feb 2025 19:30:11 -0800 Subject: [PATCH 01/10] add NotificationService method and test case stub --- src/services/NotificationService.ts | 8 ++++++++ test/services/NotificationServiceTests.test.ts | 10 ++++++++++ 2 files changed, 18 insertions(+) diff --git a/src/services/NotificationService.ts b/src/services/NotificationService.ts index c562986..a1eb118 100644 --- a/src/services/NotificationService.ts +++ b/src/services/NotificationService.ts @@ -250,4 +250,12 @@ export class NotificationService { } return this.deviceIdsToDeliverTo[tuple.toString()].has(deviceId); } + + /** + * Return all scheduled notification for the given device ID. + * @param deviceId + */ + public async getAllScheduledNotificationsForDevice(deviceId: string): Promise { + return []; + } } diff --git a/test/services/NotificationServiceTests.test.ts b/test/services/NotificationServiceTests.test.ts index 7f3cfcf..9d28b22 100644 --- a/test/services/NotificationServiceTests.test.ts +++ b/test/services/NotificationServiceTests.test.ts @@ -230,4 +230,14 @@ describe("NotificationService", () => { expect(http2.connect as jest.Mock).toHaveBeenCalledTimes(0); }); }); + + describe("getAllScheduledNotificationsForDevice", () => { + it("returns scheduled notifications for the device ID", async () => { + + }); + + it("returns an empty array if there are no notifications", async () => { + + }); + }); }); From 420682dbb852a612fd5fcb8d0aea55e88782a0c8 Mon Sep 17 00:00:00 2001 From: Brendan Chen Date: Wed, 12 Feb 2025 19:32:46 -0800 Subject: [PATCH 02/10] add arrange portion of first test --- .../services/NotificationServiceTests.test.ts | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/test/services/NotificationServiceTests.test.ts b/test/services/NotificationServiceTests.test.ts index 9d28b22..85744b2 100644 --- a/test/services/NotificationServiceTests.test.ts +++ b/test/services/NotificationServiceTests.test.ts @@ -233,6 +233,29 @@ describe("NotificationService", () => { describe("getAllScheduledNotificationsForDevice", () => { it("returns scheduled notifications for the device ID", async () => { + // Arrange + const shuttle1 = await addMockShuttleToRepository(repository, "1"); + const stop = await addMockStopToRepository(repository, "1"); + const { eta, notificationData1 } = generateNotificationDataAndEta(shuttle1, stop); + await notificationService.scheduleNotification(notificationData1); + + const shuttle2 = { + ...shuttle1, + id: "2", + } + await repository.addOrUpdateShuttle(shuttle2); + + const notificationData2 = { + ...notificationData1, + shuttleId: shuttle2.id, + } + await notificationService.scheduleNotification(notificationData2); + + // Act + + // Assert + + }); From b4f1f1b87d9a8eed810e09206c8e5ad1986d1204 Mon Sep 17 00:00:00 2001 From: Brendan Chen Date: Wed, 12 Feb 2025 19:33:55 -0800 Subject: [PATCH 03/10] add rest of first test --- test/services/NotificationServiceTests.test.ts | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/test/services/NotificationServiceTests.test.ts b/test/services/NotificationServiceTests.test.ts index 85744b2..d7ecefa 100644 --- a/test/services/NotificationServiceTests.test.ts +++ b/test/services/NotificationServiceTests.test.ts @@ -252,11 +252,10 @@ describe("NotificationService", () => { await notificationService.scheduleNotification(notificationData2); // Act + const notifications = await notificationService.getAllScheduledNotificationsForDevice(notificationData1.deviceId); // Assert - - - + expect(notifications.length).toBe(2); }); it("returns an empty array if there are no notifications", async () => { From bb77aca4ed23e2a1ae6cfd8732f524edb7eaccc6 Mon Sep 17 00:00:00 2001 From: Brendan Chen Date: Wed, 12 Feb 2025 19:34:19 -0800 Subject: [PATCH 04/10] add second test (base case) --- test/services/NotificationServiceTests.test.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/test/services/NotificationServiceTests.test.ts b/test/services/NotificationServiceTests.test.ts index d7ecefa..8517727 100644 --- a/test/services/NotificationServiceTests.test.ts +++ b/test/services/NotificationServiceTests.test.ts @@ -259,7 +259,9 @@ describe("NotificationService", () => { }); it("returns an empty array if there are no notifications", async () => { - + // Act + const notifications = await notificationService.getAllScheduledNotificationsForDevice("1"); + expect(notifications.length).toBe(0); }); }); }); From c1ec50db7b29f4c8dbe91176195df1fadf43aea8 Mon Sep 17 00:00:00 2001 From: Brendan Chen Date: Wed, 12 Feb 2025 19:39:37 -0800 Subject: [PATCH 05/10] add TupleKey method to convert string key back to tuple --- src/types/TupleKey.ts | 5 +++++ test/types/TupleKeyTests.test.ts | 16 +++++++++++++++- 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/src/types/TupleKey.ts b/src/types/TupleKey.ts index 08dcfb4..f95f4f0 100644 --- a/src/types/TupleKey.ts +++ b/src/types/TupleKey.ts @@ -16,4 +16,9 @@ export class TupleKey { valueOf(): string { return this.strKey; } + + static fromExistingStringKey(strKey: string) { + const tuple = strKey.split(separator); + return new TupleKey(...tuple); + } } diff --git a/test/types/TupleKeyTests.test.ts b/test/types/TupleKeyTests.test.ts index ac260dd..b04c3ec 100644 --- a/test/types/TupleKeyTests.test.ts +++ b/test/types/TupleKeyTests.test.ts @@ -28,4 +28,18 @@ describe("TupleKey", () => { expect(sampleObject[tupleKey1.toString()]).toEqual("value1"); expect(sampleObject[(new TupleKey("1", "2")).toString()]).toEqual("value1"); }); -}); \ No newline at end of file + + describe("fromExistingStringKey", () => { + it("creates a new TupleKey from an existing string key", () => { + const strKey = "hello|there"; + const tupleKey = TupleKey.fromExistingStringKey(strKey); + expect(tupleKey.toString()).toEqual(strKey); + }); + + it("creates an empty tuple if there is no string", () => { + const strKey = ""; + const tupleKey = TupleKey.fromExistingStringKey(strKey); + expect(tupleKey.toString()).toEqual(strKey); + }) + }) +}); From c09d35b65c8810f3fe943a170b46f87d10eaa803 Mon Sep 17 00:00:00 2001 From: Brendan Chen Date: Wed, 12 Feb 2025 19:43:05 -0800 Subject: [PATCH 06/10] implement method in NotificationService --- src/services/NotificationService.ts | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/src/services/NotificationService.ts b/src/services/NotificationService.ts index a1eb118..573e2fb 100644 --- a/src/services/NotificationService.ts +++ b/src/services/NotificationService.ts @@ -256,6 +256,22 @@ export class NotificationService { * @param deviceId */ public async getAllScheduledNotificationsForDevice(deviceId: string): Promise { - return []; + const scheduledNotifications: ScheduledNotificationData[] = []; + + for (const key of Object.keys(this.deviceIdsToDeliverTo)) { + if (this.deviceIdsToDeliverTo[key].has(deviceId)) { + const tupleKey = TupleKey.fromExistingStringKey(key); + const shuttleId = tupleKey.tuple[0] + const stopId = tupleKey.tuple[1]; + + scheduledNotifications.push({ + shuttleId, + stopId, + deviceId, + }); + } + } + + return scheduledNotifications; } } From e697fd89b8961f5f7a42fb125329f2e08a62ba5b Mon Sep 17 00:00:00 2001 From: Brendan Chen Date: Wed, 12 Feb 2025 19:45:16 -0800 Subject: [PATCH 07/10] add isNotificationScheduled resolver for Query type --- schema.graphqls | 2 ++ 1 file changed, 2 insertions(+) diff --git a/schema.graphqls b/schema.graphqls index 6296660..c5a6726 100644 --- a/schema.graphqls +++ b/schema.graphqls @@ -63,6 +63,8 @@ type Shuttle { type Query { systems: [System!]! system(id: ID): System + + isNotificationScheduled(input: NotificationInput!): Boolean } # Mutations From 8971e3514d62146b1aa27454f2af1ac8d768d957 Mon Sep 17 00:00:00 2001 From: Brendan Chen Date: Wed, 12 Feb 2025 19:51:42 -0800 Subject: [PATCH 08/10] add isNotificationScheduled resolver and test cases --- src/resolvers/QueryResolvers.ts | 5 ++- test/resolvers/QueryResolverTests.test.ts | 44 +++++++++++++++++++++++ 2 files changed, 48 insertions(+), 1 deletion(-) diff --git a/src/resolvers/QueryResolvers.ts b/src/resolvers/QueryResolvers.ts index 8bb439d..e676218 100644 --- a/src/resolvers/QueryResolvers.ts +++ b/src/resolvers/QueryResolvers.ts @@ -15,6 +15,9 @@ export const QueryResolvers: Resolvers = { name: system.name, id: system.id, }; + }, + isNotificationScheduled: async (_parent, args, contextValue, _info) => { + return false; } }, -} \ No newline at end of file +} diff --git a/test/resolvers/QueryResolverTests.test.ts b/test/resolvers/QueryResolverTests.test.ts index af73aca..f75d436 100644 --- a/test/resolvers/QueryResolverTests.test.ts +++ b/test/resolvers/QueryResolverTests.test.ts @@ -2,6 +2,8 @@ import { describe, expect, it } from "@jest/globals"; import { generateMockSystems } from "../testHelpers/mockDataGenerators"; import { setupTestServerContext, setupTestServerHolder } from "../testHelpers/apolloTestServerHelpers"; import assert = require("node:assert"); +import { ScheduledNotificationData } from "../../src/services/NotificationService"; +import { addMockShuttleToRepository, addMockStopToRepository } from "../testHelpers/repositorySetupHelpers"; // See Apollo documentation for integration test guide // https://www.apollographql.com/docs/apollo-server/testing/testing @@ -93,4 +95,46 @@ describe("QueryResolvers", () => { expect(response.body.singleResult.data?.system).toBeNull(); }); }); + + describe("isNotificationScheduled", () => { + const query = ` + query IsNotificationScheduled($input: NotificationInput!) { + isNotificationScheduled(input: $input) + } + ` + + it("returns true if the notification is scheduled", async () => { + // Arrange + const shuttle = await addMockShuttleToRepository(context.repository, "1"); + const stop = await addMockStopToRepository(context.repository, "1") + + const notification: ScheduledNotificationData = { + shuttleId: shuttle.id, + stopId: stop.id, + deviceId: "1", + }; + await context.notificationService.scheduleNotification(notification); + + // Act + const response = await holder.testServer.executeOperation({ + query, + variables: { + input: { + ...notification, + }, + } + }, { + contextValue: context, + }); + + // Assert + assert(response.body.kind === "single"); + expect(response.body.singleResult.errors).toBeUndefined(); + expect(response.body.singleResult.data?.isNotificationScheduled).toBe(true); + }); + + it("returns false if the notification isn't scheduled", async () => { + + }); + }); }); From e22537d93de57656201b3e66118449f8ca368a2f Mon Sep 17 00:00:00 2001 From: Brendan Chen Date: Wed, 12 Feb 2025 19:52:20 -0800 Subject: [PATCH 09/10] add second test for false condition --- test/resolvers/QueryResolverTests.test.ts | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/test/resolvers/QueryResolverTests.test.ts b/test/resolvers/QueryResolverTests.test.ts index f75d436..bb53d5b 100644 --- a/test/resolvers/QueryResolverTests.test.ts +++ b/test/resolvers/QueryResolverTests.test.ts @@ -134,7 +134,24 @@ describe("QueryResolvers", () => { }); it("returns false if the notification isn't scheduled", async () => { + // Act + const response = await holder.testServer.executeOperation({ + query, + variables: { + input: { + shuttleId: "1", + stopId: "1", + deviceId: "1", + }, + } + }, { + contextValue: context, + }); + // Assert + assert(response.body.kind === "single"); + expect(response.body.singleResult.errors).toBeUndefined(); + expect(response.body.singleResult.data?.isNotificationScheduled).toBe(false); }); }); }); From f5d40ebd7ac4236e36e378d53e269e042e6dc5fa Mon Sep 17 00:00:00 2001 From: Brendan Chen Date: Wed, 12 Feb 2025 19:53:26 -0800 Subject: [PATCH 10/10] add implementation for isNotificationScheduled --- src/resolvers/QueryResolvers.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/resolvers/QueryResolvers.ts b/src/resolvers/QueryResolvers.ts index e676218..98c8965 100644 --- a/src/resolvers/QueryResolvers.ts +++ b/src/resolvers/QueryResolvers.ts @@ -17,7 +17,8 @@ export const QueryResolvers: Resolvers = { }; }, isNotificationScheduled: async (_parent, args, contextValue, _info) => { - return false; + const notificationData = args.input; + return contextValue.notificationService.isNotificationScheduled(notificationData); } }, }