Merge pull request #18 from brendan-ch/feat/notification-service-integration

feat/notification-service-integration
This commit is contained in:
2025-02-04 11:45:17 -08:00
committed by GitHub
16 changed files with 321 additions and 58 deletions

6
package-lock.json generated
View File

@@ -9,7 +9,6 @@
"version": "1.0.0", "version": "1.0.0",
"dependencies": { "dependencies": {
"@apollo/server": "^4.11.2", "@apollo/server": "^4.11.2",
"@types/jsonwebtoken": "^9.0.8",
"graphql": "^16.10.0", "graphql": "^16.10.0",
"jsonwebtoken": "^9.0.2" "jsonwebtoken": "^9.0.2"
}, },
@@ -18,6 +17,7 @@
"@graphql-codegen/typescript": "4.1.2", "@graphql-codegen/typescript": "4.1.2",
"@graphql-codegen/typescript-resolvers": "4.4.1", "@graphql-codegen/typescript-resolvers": "4.4.1",
"@jest/globals": "^29.7.0", "@jest/globals": "^29.7.0",
"@types/jsonwebtoken": "^9.0.8",
"@types/node": "^22.10.2", "@types/node": "^22.10.2",
"jest": "^29.7.0", "jest": "^29.7.0",
"ts-jest": "^29.2.5", "ts-jest": "^29.2.5",
@@ -3571,6 +3571,7 @@
"version": "9.0.8", "version": "9.0.8",
"resolved": "https://registry.npmjs.org/@types/jsonwebtoken/-/jsonwebtoken-9.0.8.tgz", "resolved": "https://registry.npmjs.org/@types/jsonwebtoken/-/jsonwebtoken-9.0.8.tgz",
"integrity": "sha512-7fx54m60nLFUVYlxAB1xpe9CBWX2vSrk50Y6ogRJ1v5xxtba7qXTg5BgYDN5dq+yuQQ9HaVlHJyAAt1/mxryFg==", "integrity": "sha512-7fx54m60nLFUVYlxAB1xpe9CBWX2vSrk50Y6ogRJ1v5xxtba7qXTg5BgYDN5dq+yuQQ9HaVlHJyAAt1/mxryFg==",
"dev": true,
"dependencies": { "dependencies": {
"@types/ms": "*", "@types/ms": "*",
"@types/node": "*" "@types/node": "*"
@@ -3589,7 +3590,8 @@
"node_modules/@types/ms": { "node_modules/@types/ms": {
"version": "2.1.0", "version": "2.1.0",
"resolved": "https://registry.npmjs.org/@types/ms/-/ms-2.1.0.tgz", "resolved": "https://registry.npmjs.org/@types/ms/-/ms-2.1.0.tgz",
"integrity": "sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==" "integrity": "sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==",
"dev": true
}, },
"node_modules/@types/node": { "node_modules/@types/node": {
"version": "22.10.2", "version": "22.10.2",

View File

@@ -1,8 +1,4 @@
type Query { # Base types
systems: [System!]!
system(id: ID): System
}
type System { type System {
id: ID! id: ID!
name: String! name: String!
@@ -62,3 +58,33 @@ type Shuttle {
etas: [ETA!] etas: [ETA!]
eta(forStopId: ID): ETA eta(forStopId: ID): ETA
} }
# Queries
type Query {
systems: [System!]!
system(id: ID): System
}
# Mutations
type Mutation {
scheduleNotification(input: NotificationInput!): NotificationResponse!
cancelNotification(input: NotificationInput!): NotificationResponse!
}
input NotificationInput {
deviceId: ID!
shuttleId: ID!
stopId: ID!
}
type NotificationResponse {
success: Boolean!
message: String!
data: Notification
}
type Notification {
deviceId: ID!
shuttleId: ID!
stopId: ID!
}

View File

@@ -7,6 +7,7 @@ import { OrderedStopResolvers } from "./resolvers/OrderedStopResolvers";
import { StopResolvers } from "./resolvers/StopResolvers"; import { StopResolvers } from "./resolvers/StopResolvers";
import { ShuttleResolvers } from "./resolvers/ShuttleResolvers"; import { ShuttleResolvers } from "./resolvers/ShuttleResolvers";
import { RouteResolvers } from "./resolvers/RouteResolvers"; import { RouteResolvers } from "./resolvers/RouteResolvers";
import { MutationResolvers } from "./resolvers/MutationResolvers";
export const MergedResolvers: Resolvers<ServerContext> = { export const MergedResolvers: Resolvers<ServerContext> = {
...QueryResolvers, ...QueryResolvers,
@@ -16,4 +17,5 @@ export const MergedResolvers: Resolvers<ServerContext> = {
...StopResolvers, ...StopResolvers,
...OrderedStopResolvers, ...OrderedStopResolvers,
...EtaResolvers, ...EtaResolvers,
...MutationResolvers,
}; };

View File

@@ -1,5 +1,7 @@
import { GetterRepository } from "./repositories/GetterRepository"; import { NotificationService } from "./services/NotificationService";
import { GetterSetterRepository } from "./repositories/GetterSetterRepository";
export interface ServerContext { export interface ServerContext {
repository: GetterRepository; repository: GetterSetterRepository;
notificationService: NotificationService;
} }

View File

@@ -5,6 +5,7 @@ import { MergedResolvers } from "./MergedResolvers";
import { ServerContext } from "./ServerContext"; import { ServerContext } from "./ServerContext";
import { UnoptimizedInMemoryRepository } from "./repositories/UnoptimizedInMemoryRepository"; import { UnoptimizedInMemoryRepository } from "./repositories/UnoptimizedInMemoryRepository";
import { TimedApiBasedRepositoryLoader } from "./loaders/TimedApiBasedRepositoryLoader"; import { TimedApiBasedRepositoryLoader } from "./loaders/TimedApiBasedRepositoryLoader";
import { NotificationService } from "./services/NotificationService";
const typeDefs = readFileSync("./schema.graphqls", "utf8"); const typeDefs = readFileSync("./schema.graphqls", "utf8");
@@ -16,13 +17,13 @@ async function main() {
}); });
const repository = new UnoptimizedInMemoryRepository(); const repository = new UnoptimizedInMemoryRepository();
// await loadTestData(repository);
const repositoryDataUpdater = new TimedApiBasedRepositoryLoader( const repositoryDataUpdater = new TimedApiBasedRepositoryLoader(
repository repository
); );
await repositoryDataUpdater.start(); await repositoryDataUpdater.start();
const notificationService = new NotificationService(repository);
const { url } = await startStandaloneServer(server, { const { url } = await startStandaloneServer(server, {
listen: { listen: {
port: process.env.PORT ? parseInt(process.env.PORT) : 4000, port: process.env.PORT ? parseInt(process.env.PORT) : 4000,
@@ -30,6 +31,7 @@ async function main() {
context: async ({ req, res }) => { context: async ({ req, res }) => {
return { return {
repository, repository,
notificationService,
} }
}, },
}); });

View File

@@ -0,0 +1,47 @@
import { NotificationInput, NotificationResponse, Resolvers } from "../generated/graphql";
import { ServerContext } from "../ServerContext";
export const MutationResolvers: Resolvers<ServerContext> = {
Mutation: {
scheduleNotification: async (_parent, args, context, _info) => {
const shuttle = await context.repository.getShuttleById(args.input.shuttleId);
if (!shuttle) {
return {
message: "Shuttle ID doesn't exist",
success: false,
}
}
const stop = await context.repository.getStopById(args.input.stopId);
if (!stop) {
return {
message: "Stop ID doesn't exist",
success: false,
}
}
await context.notificationService.scheduleNotification(args.input);
const response: NotificationResponse = {
message: "Notification scheduled",
success: true,
data: args.input,
}
return response;
},
cancelNotification: async (_parent, args, context, _info) => {
if (context.notificationService.isNotificationScheduled(args.input)) {
await context.notificationService.cancelNotificationIfExists(args.input);
return {
success: true,
message: "Notification cancelled",
data: args.input,
}
}
return {
success: false,
message: "Notification doesn't exist"
}
},
},
}

View File

@@ -1,5 +1,5 @@
import { beforeEach, describe, expect, it } from "@jest/globals"; import { beforeEach, describe, expect, it } from "@jest/globals";
import { setupTestServerContext } from "../testHelpers/apolloTestServerHelpers"; import { setupTestServerContext, setupTestServerHolder } from "../testHelpers/apolloTestServerHelpers";
import { IEta, IShuttle, IStop, ISystem } from "../../src/entities/entities"; import { IEta, IShuttle, IStop, ISystem } from "../../src/entities/entities";
import { import {
addMockEtaToRepository, addMockShuttleToRepository, addMockEtaToRepository, addMockShuttleToRepository,
@@ -9,6 +9,7 @@ import {
import assert = require("node:assert"); import assert = require("node:assert");
describe("EtaResolvers", () => { describe("EtaResolvers", () => {
const holder = setupTestServerHolder();
const context = setupTestServerContext(); const context = setupTestServerContext();
let mockSystem: ISystem; let mockSystem: ISystem;
@@ -24,7 +25,7 @@ describe("EtaResolvers", () => {
}); });
async function getResponseForEtaQuery(query: string) { async function getResponseForEtaQuery(query: string) {
const response = await context.testServer.executeOperation({ const response = await holder.testServer.executeOperation({
query, query,
variables: { variables: {
systemId: mockSystem.id, systemId: mockSystem.id,

View File

@@ -0,0 +1,156 @@
import { describe, expect, it } from "@jest/globals";
import { setupTestServerContext, setupTestServerHolder } from "../testHelpers/apolloTestServerHelpers";
import {
addMockShuttleToRepository,
addMockStopToRepository,
addMockSystemToRepository
} from "../testHelpers/repositorySetupHelpers";
import assert = require("node:assert");
import { NotificationInput } from "../../src/generated/graphql";
describe("MutationResolvers", () => {
const holder = setupTestServerHolder()
const context = setupTestServerContext();
async function getServerResponse(query: string, notificationInput: { deviceId: string; shuttleId: string; stopId: string }) {
return await holder.testServer.executeOperation({
query,
variables: {
input: notificationInput,
}
}, {
contextValue: context
});
}
describe("scheduleNotification", () => {
const query = `
mutation ScheduleNotification($input: NotificationInput!) {
scheduleNotification(input: $input) {
success
message
data {
deviceId
shuttleId
stopId
}
}
}
`
function assertFailedResponse(response: any, notificationInput: NotificationInput) {
assert(response.body.kind === "single");
expect(response.body.singleResult.errors).toBeUndefined();
const notificationResponse = response.body.singleResult.data?.scheduleNotification as any;
expect(notificationResponse.success).toBe(false);
expect(context.notificationService.isNotificationScheduled(notificationInput)).toBe(false);
}
it("adds a notification to the notification service", async () => {
const system = await addMockSystemToRepository(context.repository);
const shuttle = await addMockShuttleToRepository(context.repository, system.id);
const stop = await addMockStopToRepository(context.repository, system.id);
const notificationInput = {
deviceId: "1",
shuttleId: shuttle.id,
stopId: stop.id,
};
const response = await getServerResponse(query, notificationInput);
assert(response.body.kind === "single");
expect(response.body.singleResult.errors).toBeUndefined();
const notificationResponse = response.body.singleResult.data?.scheduleNotification as any;
expect(notificationResponse?.success).toBe(true);
expect(notificationResponse?.data).toEqual(notificationInput);
expect(context.notificationService.isNotificationScheduled(notificationInput)).toBe(true);
});
it("fails if the shuttle ID doesn't exist", async () => {
const system = await addMockSystemToRepository(context.repository);
const stop = await addMockStopToRepository(context.repository, system.id);
const notificationInput = {
deviceId: "1",
shuttleId: "1",
stopId: stop.id,
}
const response = await getServerResponse(query, notificationInput);
assertFailedResponse(response, notificationInput);
});
it("fails if the stop ID doesn't exist", async () => {
const system = await addMockSystemToRepository(context.repository);
const shuttle = await addMockShuttleToRepository(context.repository, system.id);
const notificationInput = {
deviceId: "1",
shuttleId: shuttle.id,
stopId: "1",
}
const response = await getServerResponse(query, notificationInput);
assertFailedResponse(response, notificationInput);
});
});
describe("cancelNotification", () => {
const query = `
mutation CancelNotification($input: NotificationInput!) {
cancelNotification(input: $input) {
success
message
data {
deviceId
shuttleId
stopId
}
}
}
`
it("removes the notification from the notification service", async () => {
const system = await addMockSystemToRepository(context.repository);
const shuttle = await addMockShuttleToRepository(context.repository, system.id);
const stop = await addMockStopToRepository(context.repository, system.id);
const notificationInput = {
deviceId: "1",
shuttleId: shuttle.id,
stopId: stop.id,
}
await context.notificationService.scheduleNotification(notificationInput);
const response = await getServerResponse(query, notificationInput);
assert(response.body.kind === "single");
expect(response.body.singleResult.errors).toBeUndefined();
const notificationResponse = response.body.singleResult.data?.cancelNotification as any;
expect(notificationResponse.success).toBe(true);
expect(notificationResponse.data).toEqual(notificationInput);
expect(context.notificationService.isNotificationScheduled(notificationInput)).toBe(false);
});
it("fails if the notification doesn't exist", async () => {
const notificationInput = {
deviceId: "1",
shuttleId: "1",
stopId: "1",
}
const response = await getServerResponse(query, notificationInput);
assert(response.body.kind === "single");
expect(response.body.singleResult.errors).toBeUndefined();
const notificationResponse = response.body.singleResult.data?.cancelNotification as any;
expect(notificationResponse.success).toBe(false);
});
});
});

View File

@@ -1,11 +1,12 @@
import { beforeEach, describe, expect, it } from "@jest/globals"; import { beforeEach, describe, expect, it } from "@jest/globals";
import { setupTestServerContext } from "../testHelpers/apolloTestServerHelpers"; import { setupTestServerContext, setupTestServerHolder } from "../testHelpers/apolloTestServerHelpers";
import { IRoute, IStop, ISystem } from "../../src/entities/entities"; import { IRoute, IStop, ISystem } from "../../src/entities/entities";
import { generateMockOrderedStops, generateMockStops } from "../testHelpers/mockDataGenerators"; import { generateMockOrderedStops, generateMockStops } from "../testHelpers/mockDataGenerators";
import { addMockRouteToRepository, addMockSystemToRepository } from "../testHelpers/repositorySetupHelpers"; import { addMockRouteToRepository, addMockSystemToRepository } from "../testHelpers/repositorySetupHelpers";
import assert = require("node:assert"); import assert = require("node:assert");
describe("OrderedStopResolvers", () => { describe("OrderedStopResolvers", () => {
const holder = setupTestServerHolder();
const context = setupTestServerContext(); const context = setupTestServerContext();
let mockSystem: ISystem; let mockSystem: ISystem;
@@ -59,7 +60,7 @@ describe("OrderedStopResolvers", () => {
} }
`; `;
return await context.testServer.executeOperation({ return await holder.testServer.executeOperation({
query, query,
variables: { variables: {
systemId: mockSystem.id, systemId: mockSystem.id,
@@ -131,7 +132,7 @@ describe("OrderedStopResolvers", () => {
} }
`; `;
return await context.testServer.executeOperation({ return await holder.testServer.executeOperation({
query, query,
variables: { variables: {
systemId: mockSystem.id, systemId: mockSystem.id,
@@ -206,7 +207,7 @@ describe("OrderedStopResolvers", () => {
} }
`; `;
return await context.testServer.executeOperation({ return await holder.testServer.executeOperation({
query, query,
variables: { variables: {
systemId: mockSystem.id, systemId: mockSystem.id,
@@ -256,7 +257,7 @@ describe("OrderedStopResolvers", () => {
} }
`; `;
return await context.testServer.executeOperation({ return await holder.testServer.executeOperation({
query, query,
variables: { variables: {
systemId: mockSystem.id, systemId: mockSystem.id,

View File

@@ -1,12 +1,13 @@
import { describe, expect, it } from "@jest/globals"; import { describe, expect, it } from "@jest/globals";
import { generateMockSystems } from "../testHelpers/mockDataGenerators"; import { generateMockSystems } from "../testHelpers/mockDataGenerators";
import { setupTestServerContext } from "../testHelpers/apolloTestServerHelpers"; import { setupTestServerContext, setupTestServerHolder } from "../testHelpers/apolloTestServerHelpers";
import assert = require("node:assert"); import assert = require("node:assert");
// See Apollo documentation for integration test guide // See Apollo documentation for integration test guide
// https://www.apollographql.com/docs/apollo-server/testing/testing // https://www.apollographql.com/docs/apollo-server/testing/testing
describe("QueryResolvers", () => { describe("QueryResolvers", () => {
const holder = setupTestServerHolder();
const context = setupTestServerContext(); const context = setupTestServerContext();
async function addMockSystems() { async function addMockSystems() {
@@ -30,7 +31,7 @@ describe("QueryResolvers", () => {
} }
`; `;
const response = await context.testServer.executeOperation({ const response = await holder.testServer.executeOperation({
query, query,
}, { }, {
contextValue: { contextValue: {
@@ -59,7 +60,7 @@ describe("QueryResolvers", () => {
const systems = await addMockSystems(); const systems = await addMockSystems();
const systemToGet = systems[1]; const systemToGet = systems[1];
const response = await context.testServer.executeOperation({ const response = await holder.testServer.executeOperation({
query, query,
variables: { variables: {
id: systemToGet.id, id: systemToGet.id,
@@ -76,7 +77,7 @@ describe("QueryResolvers", () => {
}); });
it("returns null if there is no system", async () => { it("returns null if there is no system", async () => {
const response = await context.testServer.executeOperation({ const response = await holder.testServer.executeOperation({
query, query,
variables: { variables: {
id: "nonexistent-id", id: "nonexistent-id",

View File

@@ -1,5 +1,5 @@
import { beforeEach, describe, expect, it } from "@jest/globals"; import { beforeEach, describe, expect, it } from "@jest/globals";
import { setupTestServerContext } from "../testHelpers/apolloTestServerHelpers"; import { setupTestServerContext, setupTestServerHolder } from "../testHelpers/apolloTestServerHelpers";
import { import {
addMockRouteToRepository, addMockRouteToRepository,
addMockStopToRepository, addMockStopToRepository,
@@ -10,6 +10,7 @@ import { IRoute, IStop, ISystem } from "../../src/entities/entities";
import assert = require("node:assert"); import assert = require("node:assert");
describe("RouteResolvers", () => { describe("RouteResolvers", () => {
const holder = setupTestServerHolder();
const context = setupTestServerContext(); const context = setupTestServerContext();
let mockSystem: ISystem; let mockSystem: ISystem;
@@ -40,7 +41,7 @@ describe("RouteResolvers", () => {
} }
`; `;
return await context.testServer.executeOperation({ return await holder.testServer.executeOperation({
query, query,
variables: { variables: {
systemId: mockSystem.id, systemId: mockSystem.id,
@@ -95,7 +96,7 @@ describe("RouteResolvers", () => {
} }
`; `;
return await context.testServer.executeOperation({ return await holder.testServer.executeOperation({
query, query,
variables: { variables: {
systemId: mockSystem.id, systemId: mockSystem.id,

View File

@@ -1,12 +1,13 @@
import { beforeEach, describe, expect, it } from "@jest/globals"; import { beforeEach, describe, expect, it } from "@jest/globals";
import { generateMockEtas, generateMockRoutes } from "../testHelpers/mockDataGenerators"; import { generateMockEtas, generateMockRoutes } from "../testHelpers/mockDataGenerators";
import { IShuttle, ISystem } from "../../src/entities/entities"; import { IShuttle, ISystem } from "../../src/entities/entities";
import { setupTestServerContext } from "../testHelpers/apolloTestServerHelpers"; import { setupTestServerContext, setupTestServerHolder } from "../testHelpers/apolloTestServerHelpers";
import { addMockShuttleToRepository, addMockSystemToRepository } from "../testHelpers/repositorySetupHelpers"; import { addMockShuttleToRepository, addMockSystemToRepository } from "../testHelpers/repositorySetupHelpers";
import assert = require("node:assert"); import assert = require("node:assert");
describe("ShuttleResolvers", () => { describe("ShuttleResolvers", () => {
const holder = setupTestServerHolder();
const context = setupTestServerContext(); const context = setupTestServerContext();
let mockSystem: ISystem; let mockSystem: ISystem;
@@ -47,7 +48,7 @@ describe("ShuttleResolvers", () => {
const mockEta = etas[1]; const mockEta = etas[1];
// Act // Act
const response = await context.testServer.executeOperation({ const response = await holder.testServer.executeOperation({
query, query,
variables: { variables: {
systemId: mockSystem.id, systemId: mockSystem.id,
@@ -68,7 +69,7 @@ describe("ShuttleResolvers", () => {
}); });
it("returns null if it doesn't exist", async () => { it("returns null if it doesn't exist", async () => {
const response = await context.testServer.executeOperation({ const response = await holder.testServer.executeOperation({
query, query,
variables: { variables: {
systemId: mockSystem.id, systemId: mockSystem.id,
@@ -106,7 +107,7 @@ describe("ShuttleResolvers", () => {
it("returns associated ETAs if they exist for the shuttle", async () => { it("returns associated ETAs if they exist for the shuttle", async () => {
const etas = await addMockEtas(mockShuttle.id); const etas = await addMockEtas(mockShuttle.id);
const response = await context.testServer.executeOperation({ const response = await holder.testServer.executeOperation({
query, query,
variables: { variables: {
systemId: mockSystem.id, systemId: mockSystem.id,
@@ -125,7 +126,7 @@ describe("ShuttleResolvers", () => {
}); });
it("returns empty array if no ETAs exist", async () => { it("returns empty array if no ETAs exist", async () => {
const response = await context.testServer.executeOperation({ const response = await holder.testServer.executeOperation({
query, query,
variables: { variables: {
systemId: mockSystem.id, systemId: mockSystem.id,
@@ -164,7 +165,7 @@ describe("ShuttleResolvers", () => {
` `
async function getResponseForQuery() { async function getResponseForQuery() {
return await context.testServer.executeOperation({ return await holder.testServer.executeOperation({
query, query,
variables: { variables: {
systemId: mockSystem.id, systemId: mockSystem.id,

View File

@@ -1,11 +1,12 @@
import { beforeEach, describe, expect, it } from "@jest/globals"; import { beforeEach, describe, expect, it } from "@jest/globals";
import { setupTestServerContext } from "../testHelpers/apolloTestServerHelpers"; import { setupTestServerContext, setupTestServerHolder } from "../testHelpers/apolloTestServerHelpers";
import { generateMockEtas, generateMockOrderedStops } from "../testHelpers/mockDataGenerators"; import { generateMockEtas, generateMockOrderedStops } from "../testHelpers/mockDataGenerators";
import { IStop, ISystem } from "../../src/entities/entities"; import { IStop, ISystem } from "../../src/entities/entities";
import { addMockStopToRepository, addMockSystemToRepository } from "../testHelpers/repositorySetupHelpers"; import { addMockStopToRepository, addMockSystemToRepository } from "../testHelpers/repositorySetupHelpers";
import assert = require("node:assert"); import assert = require("node:assert");
describe("StopResolvers", () => { describe("StopResolvers", () => {
const holder = setupTestServerHolder();
const context = setupTestServerContext(); const context = setupTestServerContext();
let mockStop: IStop; let mockStop: IStop;
@@ -17,7 +18,7 @@ describe("StopResolvers", () => {
}) })
async function getResponseForQuery(query: string) { async function getResponseForQuery(query: string) {
return await context.testServer.executeOperation({ return await holder.testServer.executeOperation({
query, query,
variables: { variables: {
systemId: mockSystem.id, systemId: mockSystem.id,

View File

@@ -1,5 +1,5 @@
import { beforeEach, describe, expect, it } from "@jest/globals"; import { beforeEach, describe, expect, it } from "@jest/globals";
import { setupTestServerContext } from "../testHelpers/apolloTestServerHelpers"; import { setupTestServerContext, setupTestServerHolder } from "../testHelpers/apolloTestServerHelpers";
import { generateMockRoutes, generateMockShuttles, generateMockStops } from "../testHelpers/mockDataGenerators"; import { generateMockRoutes, generateMockShuttles, generateMockStops } from "../testHelpers/mockDataGenerators";
import { import {
addMockRouteToRepository, addMockRouteToRepository,
@@ -11,6 +11,7 @@ import { ISystem } from "../../src/entities/entities";
import assert = require("node:assert"); import assert = require("node:assert");
describe("SystemResolvers", () => { describe("SystemResolvers", () => {
const holder = setupTestServerHolder();
const context = setupTestServerContext(); const context = setupTestServerContext();
let mockSystem: ISystem; let mockSystem: ISystem;
@@ -21,7 +22,7 @@ describe("SystemResolvers", () => {
// TODO: Consolidate these into one single method taking an object // TODO: Consolidate these into one single method taking an object
async function getResponseFromQueryNeedingSystemId(query: string) { async function getResponseFromQueryNeedingSystemId(query: string) {
return await context.testServer.executeOperation({ return await holder.testServer.executeOperation({
query, query,
variables: { variables: {
systemId: mockSystem.id, systemId: mockSystem.id,
@@ -102,7 +103,7 @@ describe("SystemResolvers", () => {
} }
`; `;
return await context.testServer.executeOperation({ return await holder.testServer.executeOperation({
query, query,
variables: { variables: {
systemId: mockSystem.id, systemId: mockSystem.id,
@@ -169,7 +170,7 @@ describe("SystemResolvers", () => {
} }
`; `;
return await context.testServer.executeOperation({ return await holder.testServer.executeOperation({
query, query,
variables: { variables: {
systemId: mockSystem.id, systemId: mockSystem.id,
@@ -237,7 +238,7 @@ describe("SystemResolvers", () => {
} }
`; `;
return await context.testServer.executeOperation({ return await holder.testServer.executeOperation({
query, query,
variables: { variables: {
systemId: mockSystem.id, systemId: mockSystem.id,

View File

@@ -4,6 +4,7 @@ import { MergedResolvers } from "../../src/MergedResolvers";
import { UnoptimizedInMemoryRepository } from "../../src/repositories/UnoptimizedInMemoryRepository"; import { UnoptimizedInMemoryRepository } from "../../src/repositories/UnoptimizedInMemoryRepository";
import { beforeEach } from "@jest/globals"; import { beforeEach } from "@jest/globals";
import { ServerContext } from "../../src/ServerContext"; import { ServerContext } from "../../src/ServerContext";
import { NotificationService } from "../../src/services/NotificationService";
function setUpTestServer() { function setUpTestServer() {
@@ -16,15 +17,33 @@ function setUpTestServer() {
}); });
} }
/**
* Returns a `ServerContext` object which can be passed to requests
* for testing.
*/
export function setupTestServerContext() { export function setupTestServerContext() {
// @ts-ignore const context: { [key: string] : any } = {};
const context: { testServer: ApolloServer<ServerContext>; repository: UnoptimizedInMemoryRepository } = {};
beforeEach(() => { beforeEach(() => {
context.testServer = setUpTestServer();
context.repository = new UnoptimizedInMemoryRepository(); context.repository = new UnoptimizedInMemoryRepository();
context.notificationService = new NotificationService(context.repository);
}); });
// Return a reference, not destructured values return context as ServerContext;
return context; }
/**
* Returns an object which holds a test server.
* This server is reset before every test.
* Tests should keep a reference to the holder object,
* and not destructure it.
*/
export function setupTestServerHolder() {
const holder: { [key: string]: any } = {};
beforeEach(() => {
holder.testServer = setUpTestServer();
});
return holder as { testServer: ApolloServer };
} }

View File

@@ -1,4 +1,3 @@
import { UnoptimizedInMemoryRepository } from "../../src/repositories/UnoptimizedInMemoryRepository";
import { import {
generateMockEtas, generateMockEtas,
generateMockRoutes, generateMockRoutes,
@@ -6,8 +5,9 @@ import {
generateMockStops, generateMockStops,
generateMockSystems generateMockSystems
} from "./mockDataGenerators"; } from "./mockDataGenerators";
import { GetterSetterRepository } from "../../src/repositories/GetterSetterRepository";
export async function addMockSystemToRepository(repository: UnoptimizedInMemoryRepository) { export async function addMockSystemToRepository(repository: GetterSetterRepository) {
const mockSystems = generateMockSystems(); const mockSystems = generateMockSystems();
const mockSystem = mockSystems[0]; const mockSystem = mockSystems[0];
mockSystem.id = "1"; mockSystem.id = "1";
@@ -16,7 +16,7 @@ export async function addMockSystemToRepository(repository: UnoptimizedInMemoryR
return mockSystem; return mockSystem;
} }
export async function addMockRouteToRepository(repository: UnoptimizedInMemoryRepository, systemId: string) { export async function addMockRouteToRepository(repository: GetterSetterRepository, systemId: string) {
const mockRoutes = generateMockRoutes(); const mockRoutes = generateMockRoutes();
const mockRoute = mockRoutes[0]; const mockRoute = mockRoutes[0];
mockRoute.systemId = systemId; mockRoute.systemId = systemId;
@@ -25,7 +25,7 @@ export async function addMockRouteToRepository(repository: UnoptimizedInMemoryRe
return mockRoute; return mockRoute;
} }
export async function addMockStopToRepository(repository: UnoptimizedInMemoryRepository, systemId: string) { export async function addMockStopToRepository(repository: GetterSetterRepository, systemId: string) {
const mockStops = generateMockStops(); const mockStops = generateMockStops();
const mockStop = mockStops[0]; const mockStop = mockStops[0];
mockStop.systemId = systemId; mockStop.systemId = systemId;
@@ -34,7 +34,7 @@ export async function addMockStopToRepository(repository: UnoptimizedInMemoryRep
return mockStop; return mockStop;
} }
export async function addMockShuttleToRepository(repository: UnoptimizedInMemoryRepository, systemId: string) { export async function addMockShuttleToRepository(repository: GetterSetterRepository, systemId: string) {
const mockShuttles = generateMockShuttles(); const mockShuttles = generateMockShuttles();
const mockShuttle = mockShuttles[0]; const mockShuttle = mockShuttles[0];
mockShuttle.systemId = systemId; mockShuttle.systemId = systemId;
@@ -42,7 +42,7 @@ export async function addMockShuttleToRepository(repository: UnoptimizedInMemory
return mockShuttle; return mockShuttle;
} }
export async function addMockEtaToRepository(repository: UnoptimizedInMemoryRepository, stopId: string, shuttleId: string) { export async function addMockEtaToRepository(repository: GetterSetterRepository, stopId: string, shuttleId: string) {
const etas = generateMockEtas(); const etas = generateMockEtas();
const expectedEta = etas[0]; const expectedEta = etas[0];
expectedEta.stopId = stopId; expectedEta.stopId = stopId;