mirror of
https://github.com/brendan-ch/project-inter-server.git
synced 2026-04-19 08:50:29 +00:00
Merge pull request #18 from brendan-ch/feat/notification-service-integration
feat/notification-service-integration
This commit is contained in:
6
package-lock.json
generated
6
package-lock.json
generated
@@ -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",
|
||||||
|
|||||||
@@ -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!
|
||||||
|
}
|
||||||
|
|||||||
@@ -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,
|
||||||
};
|
};
|
||||||
@@ -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;
|
||||||
}
|
}
|
||||||
@@ -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,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
47
src/resolvers/MutationResolvers.ts
Normal file
47
src/resolvers/MutationResolvers.ts
Normal 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"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
@@ -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,
|
||||||
|
|||||||
156
test/resolvers/MutationResolverTests.test.ts
Normal file
156
test/resolvers/MutationResolverTests.test.ts
Normal 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);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -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,
|
||||||
|
|||||||
@@ -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",
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -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 };
|
||||||
}
|
}
|
||||||
@@ -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;
|
||||||
|
|||||||
Reference in New Issue
Block a user