Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions backend/eslint.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,9 @@ import pluginJs from "@eslint/js";
export default [
{languageOptions: { globals: globals.node }},
pluginJs.configs.recommended,
{
rules: {
"no-unused-vars": ["error", { argsIgnorePattern: "^_" }],
},
},
];
47 changes: 5 additions & 42 deletions backend/src/db/daos/__tests__/sceneDao.test.js
Original file line number Diff line number Diff line change
@@ -1,20 +1,13 @@
import {
describe,
beforeAll,
beforeEach,
afterEach,
afterAll,
it,
expect,
} from "@jest/globals";

import { MongoMemoryServer } from "mongodb-memory-server";
import { describe, beforeEach, it, expect } from "@jest/globals";

import mongoose from "mongoose";

import Scene from "../../models/scene.js";
import { patchScene } from "../sceneDao.js";
import { useMongoMemoryServer } from "../../../test/testSetup.js";

describe("Scene DAO patchScene tests", () => {
let mongoServer;
useMongoMemoryServer();

const sceneId = new mongoose.Types.ObjectId("000000000000000000000001");

Expand Down Expand Up @@ -42,40 +35,10 @@ describe("Scene DAO patchScene tests", () => {
],
};

beforeAll(async () => {
try {
mongoServer = await MongoMemoryServer.create();
const uri = mongoServer.getUri();
await mongoose.connect(uri, {
useNewUrlParser: true,
useUnifiedTopology: true,
});
} catch (err) {
console.error("MongoMemoryServer failed:", err);
throw err;
}
});

beforeEach(async () => {
await Scene.create(baseScene);
});

afterEach(async () => {
if (mongoose.connection.readyState === 1) {
await mongoose.connection.db.dropDatabase();
}
});

afterAll(async () => {
if (mongoose.connection.readyState === 1) {
await mongoose.disconnect();
}

if (mongoServer) {
await mongoServer.stop();
}
});

it("updates multiple changed components in one patch", async () => {
await patchScene(sceneId, {
fields: {},
Expand Down
36 changes: 4 additions & 32 deletions backend/src/middleware/__tests__/scenarioAuth.test.js
Original file line number Diff line number Diff line change
@@ -1,24 +1,15 @@
import {
jest,
describe,
beforeAll,
beforeEach,
afterEach,
afterAll,
it,
expect,
} from "@jest/globals";
import { jest, describe, beforeEach, it, expect } from "@jest/globals";

import { MongoMemoryServer } from "mongodb-memory-server";
import mongoose from "mongoose";
import Scenario from "../../db/models/scenario.js";
import scenarioAuth from "../scenarioAuth.js";
import { useMongoMemoryServer } from "../../test/testSetup.js";

describe("Scenario Auth Middleware tests", () => {
const HTTP_UNAUTHORISED = 401;
const HTTP_NOT_FOUND = 404;

let mongoServer;
useMongoMemoryServer();

const scenario1 = {
_id: new mongoose.mongo.ObjectId("000000000000000000000001"),
Expand All @@ -39,30 +30,11 @@ describe("Scenario Auth Middleware tests", () => {

const nextFunction = jest.fn();

// setup in-memory mongodb and express API
beforeAll(async () => {
mongoServer = await MongoMemoryServer.create();
const uri = mongoServer.getUri();

await mongoose.connect(uri);
});

beforeEach(async () => {
// Add scenario to database
nextFunction.mockClear();
await Scenario.create(scenario1);
});

// clear the database
afterEach(async () => {
await mongoose.connection.db.dropDatabase();
});

// close the mongodb and express servers
afterAll(async () => {
await mongoose.disconnect();
await mongoServer.stop();
});

it("successfully authorise user for scenario", async () => {
const req = mockRequest("000000000000000000000001", { uid: "user1" });
const res = mockResponse();
Expand Down
58 changes: 13 additions & 45 deletions backend/src/routes/api/__tests__/imageApi.test.js
Original file line number Diff line number Diff line change
@@ -1,61 +1,29 @@
import {
jest,
describe,
beforeAll,
afterEach,
afterAll,
it,
expect,
} from "@jest/globals";

import { MongoMemoryServer } from "mongodb-memory-server";
import { jest, describe, it, expect } from "@jest/globals";

import express from "express";
import mongoose from "mongoose";
import axios from "axios";
import routes from "../../index.js";
import Image from "../../../db/models/image.js";
import {
useMongoMemoryServer,
useExpressServer,
} from "../../../test/testSetup.js";

jest.mock("firebase-admin"); // Needed to mock the firebase-admin dependency in firebase-auth.js which is in routes

describe("Image API tests", () => {
const HTTP_OK = 200;

let mongoServer;
let server;
let port;

// setup in-memory mongodb and express API
beforeAll(async () => {
mongoServer = await MongoMemoryServer.create();
const uri = mongoServer.getUri();

await mongoose.connect(uri);

useMongoMemoryServer();
const ctx = useExpressServer(() => {
const app = express();
app.use(express.json());
app.use("/", routes);

// Add safe error handler to avoid circular JSON errors
app.use((err, res) => {
app.use((err, req, res, _next) => {
console.error("Unhandled Express error:", err.message);
res.status(500).json({ error: "Internal Server Error" });
});

server = app.listen(0);
port = server.address().port;
});

// clear the database
afterEach(async () => {
await mongoose.connection.db.dropDatabase();
});

// close the mongodb and express servers
afterAll(async () => {
server.close(async () => {
await mongoose.disconnect();
await mongoServer.stop();
});
return app;
});

it("creates images in the database", async () => {
Expand All @@ -77,7 +45,7 @@ describe("Image API tests", () => {
};

const response = await axios.post(
`http://localhost:${port}/api/image/`,
`http://localhost:${ctx.port}/api/image/`,
body
);
expect(response.status).toBe(HTTP_OK);
Expand All @@ -98,7 +66,7 @@ describe("Image API tests", () => {

await Promise.all(urls.map((url) => new Image({ url }).save()));

const response = await axios.get(`http://localhost:${port}/api/image/`);
const response = await axios.get(`http://localhost:${ctx.port}/api/image/`);
expect(response.status).toBe(HTTP_OK);

// check correct images are returned
Expand All @@ -123,7 +91,7 @@ describe("Image API tests", () => {
await Image.create([image1, image2]);

const response = await axios.get(
`http://localhost:${port}/api/image/${image2.id}`
`http://localhost:${ctx.port}/api/image/${image2.id}`
);
expect(response.status).toBe(HTTP_OK);

Expand Down
73 changes: 24 additions & 49 deletions backend/src/routes/api/__tests__/scenarioApi.test.js
Original file line number Diff line number Diff line change
@@ -1,15 +1,5 @@
import {
jest,
describe,
beforeAll,
beforeEach,
afterEach,
afterAll,
it,
expect,
} from "@jest/globals";

import { MongoMemoryServer } from "mongodb-memory-server";
import { jest, describe, beforeEach, it, expect } from "@jest/globals";

import express from "express";
import mongoose from "mongoose";
import axios from "axios";
Expand All @@ -19,6 +9,10 @@ import Scene from "../../../db/models/scene.js";
import auth from "../../../middleware/firebaseAuth.js";
import scenarioAuth from "../../../middleware/scenarioAuth.js";
import Access from "../../../db/models/access.js";
import {
useMongoMemoryServer,
useExpressServer,
} from "../../../test/testSetup.js";

jest.mock("../../../middleware/firebaseAuth");
jest.mock("../../../middleware/scenarioAuth");
Expand All @@ -45,9 +39,13 @@ function authHeaders(id) {
describe("Scenario API tests", () => {
const HTTP_OK = 200;

let mongoServer;
let server;
let port;
useMongoMemoryServer();
const ctx = useExpressServer(() => {
const app = express();
app.use(express.json());
app.use("/", routes);
return app;
});

const scene1 = {
_id: new mongoose.mongo.ObjectId("000000000000000000000003"),
Expand Down Expand Up @@ -85,48 +83,19 @@ describe("Scenario API tests", () => {
users: {},
};

// setup in-memory mongodb and express API
beforeAll(async () => {
mongoServer = await MongoMemoryServer.create();
const uri = mongoServer.getUri();

await mongoose.connect(uri);

const app = express();
app.use(express.json());
app.use("/", routes);

server = app.listen(0);
port = server.address().port;
});

beforeEach(async () => {
// Add scenario to database
await Scenario.create([scenario1, scenario2]);
await Access.create([access1, access2]);
await Scene.create([scene1, scene2]);
});

// clear the database
afterEach(async () => {
await mongoose.connection.db.dropDatabase();
});

// close the mongodb and express servers
afterAll(async () => {
server.close(async () => {
await mongoose.disconnect();
await mongoServer.stop();
});
});

it("creates and returns the newly persisted scenario", async () => {
const reqData = {
name: "Test Scenario 1",
};

const response = await axios.post(
`http://localhost:${port}/api/scenario/`,
`http://localhost:${ctx.port}/api/scenario/`,
reqData,
authHeaders("user1")
);
Expand All @@ -153,7 +122,7 @@ describe("Scenario API tests", () => {

it("GET /scenario: retrieve all scenarios successfully", async () => {
const response = await axios.get(
`http://localhost:${port}/api/scenario/`,
`http://localhost:${ctx.port}/api/scenario/`,
authHeaders("user1")
);
expect(response.status).toBe(HTTP_OK);
Expand All @@ -173,7 +142,7 @@ describe("Scenario API tests", () => {

it("DELETE api/scenario/:scenarioId deletes a valid scenario", async () => {
const response = await axios.delete(
`http://localhost:${port}/api/scenario/${scenario2._id}/`,
`http://localhost:${ctx.port}/api/scenario/${scenario2._id}/`,
authHeaders("user1")
);
expect(response.status).toBe(HTTP_OK);
Expand All @@ -188,22 +157,28 @@ describe("Scenario API tests", () => {
expect(dbScene1).toEqual(null);
expect(dbScene2).toEqual(null);

// check corresponding access list is removed
const dbAccess2 = await Access.findOne({
scenarioId: scenario2._id.toString(),
});
expect(dbAccess2).toBeNull();

// TODO: check corresponding components are removed
});

it("DELETE api/scenario/:scenarioId returns 404 with invalid ID", async () => {
// bad scenarioId
await expect(
axios.delete(
`http://localhost:${port}/api/scenario/000000000000000000000009/`,
`http://localhost:${ctx.port}/api/scenario/000000000000000000000009/`,
authHeaders("user1")
)
).rejects.toThrow();
});

it("update a scenarios name", async () => {
const response = await axios.put(
`http://localhost:${port}/api/scenario/${scenario1._id}`,
`http://localhost:${ctx.port}/api/scenario/${scenario1._id}`,
{ name: "Scenario 2" },
authHeaders("user1")
);
Expand Down
Loading
Loading