From 1b45481d279172f3be5c1ad0767b43a1b115706a Mon Sep 17 00:00:00 2001 From: Sugui Date: Fri, 29 Dec 2023 15:24:29 +0100 Subject: [PATCH] commit all changes to tests --- compose.yaml | 3 +- mongo-init.js | 30 ----- package.json | 2 + src/app.ts | 41 +++++++ src/index.ts | 43 +------ src/models/AuthModel.ts | 3 +- src/models/ImageModel.ts | 5 +- src/services/AuthService.ts | 4 +- tests/app.test.ts | 227 +++++++++++++++++++----------------- tests/auth.test.ts | 25 +++- tests/memoryServer.ts | 19 +++ tests/populateDatabase.ts | 15 +++ 12 files changed, 230 insertions(+), 187 deletions(-) delete mode 100644 mongo-init.js create mode 100644 src/app.ts create mode 100644 tests/memoryServer.ts create mode 100644 tests/populateDatabase.ts diff --git a/compose.yaml b/compose.yaml index 964794b..2ca513c 100644 --- a/compose.yaml +++ b/compose.yaml @@ -12,7 +12,6 @@ services: MONGO_INITDB_DATABASE: bot volumes: - mongodb_data:/data/db - - ./mongo-init.js:/docker-entrypoint-initdb.d/mongo-init.js:ro bot-api: image: oven/bun:1 @@ -28,6 +27,8 @@ services: MONGODB_USER: "root" MONGODB_PASS: "password" JWTSECRET: "cooljwtsecret" + # DEDICATED_MONGODB_SERVER is for integration testing, for disabling memory server in tests + DEDICATED_MONGODB_SERVER: "true" volumes: - ./:/usr/src/app:ro diff --git a/mongo-init.js b/mongo-init.js deleted file mode 100644 index 67b53c9..0000000 --- a/mongo-init.js +++ /dev/null @@ -1,30 +0,0 @@ -db.createUser({ - user: "root", - pwd: "password", - roles: [ - { - role: "readWrite", - db: "admin", - }, - { - role: "readWrite", - db: "bot", - }, - ], -}); - -db = new Mongo().getDB("bot"); - -db.images.createIndex({ status: 1 }); -db.images.createIndex({ url: 1 }, { unique: true }); -db.images.insert({ - url: "https://example.com", - status: "consumed", - tags: ["2girls", "sleeping"], -}); - -db.authorizations.createIndex({ app: 1 }); -db.authorizations.insert({ - app: "tester", - secret: "test", -}); diff --git a/package.json b/package.json index 2080221..6d9c33c 100644 --- a/package.json +++ b/package.json @@ -4,9 +4,11 @@ "type": "module", "devDependencies": { "@types/jest": "^29.5.11", + "@types/mongodb-memory-server": "^2.3.0", "@types/supertest": "^6.0.1", "bun-types": "latest", "jest": "^29.7.0", + "mongodb-memory-server": "^9.1.3", "supertest": "^6.3.3", "ts-jest": "^29.1.1" }, diff --git a/src/app.ts b/src/app.ts new file mode 100644 index 0000000..e132aa4 --- /dev/null +++ b/src/app.ts @@ -0,0 +1,41 @@ +import express from "express"; +import listEndpoints from "express-list-endpoints"; +import imageController from "./controllers/ImageController"; +import authControler from "./controllers/AuthControler"; +import mongoose from "mongoose"; + +export const app = express(); + +app.use(express.json()); + +app.get("/", (_, res) => { + const endpoints = listEndpoints(app); + res.json({ endpoints }); +}); + +app.get("/images", imageController.getAllImages); +app.post("/images", authControler.authorize, imageController.addImage); +app.post("/login", authControler.login); + +export const startApp = async () => { + const port = process.env.PORT || 8080; + const mongo_uri: string = process.env.MONGODB_URI || ""; + const mongo_user = process.env.MONGODB_USER; + const mongo_pass = process.env.MONGODB_PASS; + + try { + await mongoose.connect(mongo_uri, { + authSource: "admin", + user: mongo_user, + pass: mongo_pass, + }); + app.listen(port, () => + console.log(`Express server listening on port ${port}`) + ); + } catch (error) { + console.error(error); + process.exit(1); + } + }; + +export default app; \ No newline at end of file diff --git a/src/index.ts b/src/index.ts index d74231b..cdff0b9 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,42 +1,3 @@ -import express from "express"; -import mongoose from "mongoose"; -import listEndpoints from "express-list-endpoints"; -import imageController from "./controllers/ImageController"; -import authControler from "./controllers/AuthControler"; +import { startApp } from "./app"; -export const app = express(); - -app.use(express.json()); - -app.get("/", (_, res) => { - const endpoints = listEndpoints(app); - res.json({ endpoints }); -}); - -app.get("/images", imageController.getAllImages); -app.post("/images", authControler.authorize, imageController.addImage); -app.post("/login", authControler.login); - -const start = async () => { - // Set the default port to 8080, or use the PORT environment variable - const port = process.env.PORT || 8080; - const mongo_uri: string = process.env.MONGODB_URI || ""; - const mongo_user = process.env.MONGODB_USER; - const mongo_pass = process.env.MONGODB_PASS; - - try { - await mongoose.connect(mongo_uri, { - authSource: "admin", - user: mongo_user, - pass: mongo_pass, - }); - app.listen(port, () => - console.log(`Express server listening on port ${port}`) - ); - } catch (error) { - console.error(error); - process.exit(1); - } -}; - -start(); +startApp(); \ No newline at end of file diff --git a/src/models/AuthModel.ts b/src/models/AuthModel.ts index df71afe..96ce5e5 100644 --- a/src/models/AuthModel.ts +++ b/src/models/AuthModel.ts @@ -9,11 +9,12 @@ const AuthSchema = new mongoose.Schema({ app: { type: String, required: true, + index: true, }, secret: { type: String, required: true, }, -}); +}, { collection: "authorizations" }); export default mongoose.model("authorizations", AuthSchema); diff --git a/src/models/ImageModel.ts b/src/models/ImageModel.ts index d91ab18..b3bff2c 100644 --- a/src/models/ImageModel.ts +++ b/src/models/ImageModel.ts @@ -10,6 +10,8 @@ const ImageSchema = new mongoose.Schema({ url: { type: String, required: true, + index: true, + unique: true }, status: { type: String, @@ -17,10 +19,11 @@ const ImageSchema = new mongoose.Schema({ values: ["consumed", "unavailable", "available"], }, required: true, + index: true, }, tags: { type: [String], }, -}); +}, { collection: "images" }); export default mongoose.model("images", ImageSchema); diff --git a/src/services/AuthService.ts b/src/services/AuthService.ts index aed8d12..d44e7eb 100644 --- a/src/services/AuthService.ts +++ b/src/services/AuthService.ts @@ -1,8 +1,8 @@ import AuthModel, { Auth } from "../models/AuthModel"; class AuthService { - async find(app: String, secret: String): Promise { - const auth = await AuthModel.findOne({ app: app, secret: secret }).exec(); + async find(app: string, secret: string): Promise { + const auth = await AuthModel.findOne({ app: app, secret: secret }); return auth; } } diff --git a/tests/app.test.ts b/tests/app.test.ts index ac40ace..72a6ebc 100644 --- a/tests/app.test.ts +++ b/tests/app.test.ts @@ -1,134 +1,151 @@ -import { afterEach, beforeAll, describe, expect, it, mock } from "bun:test"; +import { afterAll, afterEach, beforeAll, describe, expect, it, mock } from "bun:test"; import request from "supertest"; -import { app } from "../src"; +import app, { startApp } from "../src/app"; import imageService from "../src/services/ImageService"; +import memoryServer from "./memoryServer"; +import populateDatabase from "./populateDatabase"; const imageServiceOriginal = imageService; -const tok = await request(app) - .post("/login") - .send({ app: "tester", secret: "test" }); -const token = tok.body.token; + +let token: string; + +beforeAll(async () => { + if (!process.env.DEDICATED_MONGODB_SERVER) + await memoryServer.start(); + await startApp(); + await populateDatabase(); + + const tok = await request(app) + .post("/login") + .send({ app: "tester", secret: "test" }); + token = tok.body.token; +}); + +afterAll(async () => { + if (!process.env.DEDICATED_MONGODB_SERVER) + await memoryServer.stop(); +}) afterEach(() => { - mock.restore(); - mock.module("../src/services/ImageService", () => ({ - default: imageServiceOriginal, - })); + mock.restore(); + mock.module("../src/services/ImageService", () => ({ + default: imageServiceOriginal, + })); }); describe("GET / shows all of the endpoints", async () => { - const res = await request(app).get("/"); + const res = await request(app).get("/"); - it("should be", async () => { - expect(res.body).toHaveProperty("endpoints"); - }); + it("should be", async () => { + expect(res.body).toHaveProperty("endpoints"); + }); - it("should be an array", () => { - expect(Array.isArray(res.body.endpoints)).toBeTrue(); - }); + it("should be an array", () => { + expect(Array.isArray(res.body.endpoints)).toBeTrue(); + }); }); describe("GET /images works properly", async () => { - const res = await request(app).get("/images"); + const res = await request(app).get("/images"); - it("should be an array", () => { - expect(Array.isArray(res.body.images)).toBeTrue(); - }); + it("should be an array", () => { + expect(Array.isArray(res.body.images)).toBeTrue(); + }); - it("should return a 200", async () => { - expect(res.statusCode).toBe(200); - }); + it("should return a 200", async () => { + expect(res.statusCode).toBe(200); + }); }); describe("POST /images works properly", () => { - it("should return 401 for unauthenticated requests", async () => { - const res = await request(app) - .post("/images") - .send({ - url: "https://test.url.com/0", - status: "available", - tags: ["2girls", "touhou"], - }); - expect(res.status).toBe(401); - }); + it("should return 401 for unauthenticated requests", async () => { + const res = await request(app) + .post("/images") + .send({ + url: "https://test.url.com/0", + status: "available", + tags: ["2girls", "touhou"], + }); + expect(res.status).toBe(401); + }); - it("should return 403 for invalid tokens", async () => { - const res = await request(app) - .post("/images") - .set("authorization", `Bearer token`) - .send({ - url: "https://test.url.com/0", - status: "available", - tags: ["2girls", "touhou"], - }); - expect(res.status).toBe(403); - }); + it("should return 403 for invalid tokens", async () => { + const res = await request(app) + .post("/images") + .set("authorization", `Bearer token`) + .send({ + url: "https://test.url.com/0", + status: "available", + tags: ["2girls", "touhou"], + }); + expect(res.status).toBe(403); + }); - it("should return 201 for new image", async () => { - const res = await request(app) - .post("/images") - .set("authorization", `Bearer ${token}`) - .send({ - url: "https://test.url.com/1", - status: "available", - tags: ["2girls", "touhou"], - }); - expect(res.status).toBe(201); - }); + it("should return 201 for new image", async () => { + const res = await request(app) + .post("/images") + .set("authorization", `Bearer ${token}`) + .send({ + url: "https://test.url.com/1", + status: "available", + tags: ["2girls", "touhou"], + }); + expect(res.status).toBe(201); + }); - it("should return 409 for a repeated images", async () => { - await request(app) - .post("/images") - .set("authorization", `Bearer ${token}`) - .send({ - url: "https://test.url.com/2", - status: "available", - tags: ["2girls", "touhou"], - }); + it("should return 409 for a repeated images", async () => { + await request(app) + .post("/images") + .set("authorization", `Bearer ${token}`) + .send({ + url: "https://test.url.com/2", + status: "available", + tags: ["2girls", "touhou"], + }); - const res = await request(app) - .post("/images") - .set("authorization", `Bearer ${token}`) - .send({ - url: "https://test.url.com/2", - status: "available", - tags: ["2girls", "touhou"], - }); + const res = await request(app) + .post("/images") + .set("authorization", `Bearer ${token}`) + .send({ + url: "https://test.url.com/2", + status: "available", + tags: ["2girls", "touhou"], + }); - expect(res.status).toBe(409); - }); + expect(res.status).toBe(409); + }); - it("should return 500 for an error on the service", async () => { - mock.module("../src/services/ImageService", () => ({ - default: { - add: () => { - throw new Error("This is an expected testing error"); - }, - }, - })); + it("should return 500 for an error on the service", async () => { + mock.module("../src/services/ImageService", () => ({ + default: { + add: () => { + throw new Error("This is an expected testing error"); + }, + }, + })); - const res = await request(app) - .post("/images") - .set("authorization", `Bearer ${token}`) - .send({ - url: "https://test.url.com/3", - status: "available", - tags: ["2girls", "touhou"], - }); + const res = await request(app) + .post("/images") + .set("authorization", `Bearer ${token}`) + .send({ + url: "https://test.url.com/3", + status: "available", + tags: ["2girls", "touhou"], + }); - expect(res.status).toBe(500); - }); + expect(res.status).toBe(500); + }); - it("should return 400 for malformed requests", async () => { - mock.restore(); - const res = await request(app) - .post("/images") - .set("authorization", `Bearer ${token}`) - .send({ - url: "https://test.url.com/4", - status: "wrong", - tags: ["2girls", "touhou"], - }); - expect(res.status).toBe(400); - }); + it("should return 400 for malformed requests", async () => { + mock.restore(); + const res = await request(app) + .post("/images") + .set("authorization", `Bearer ${token}`) + .send({ + url: "https://test.url.com/4", + status: "wrong", + tags: ["2girls", "touhou"], + }); + expect(res.status).toBe(400); + }); }); diff --git a/tests/auth.test.ts b/tests/auth.test.ts index bd20cd2..18c3789 100644 --- a/tests/auth.test.ts +++ b/tests/auth.test.ts @@ -1,12 +1,25 @@ -import { describe, expect, it, mock } from "bun:test"; +import { afterAll, beforeAll, describe, expect, it } from "bun:test"; import request from "supertest"; -import { app } from "../src"; +import app, { startApp } from "../src/app"; +import memoryServer from "./memoryServer"; +import populateDatabase from "./populateDatabase"; + +beforeAll(async () => { + if (!process.env.DEDICATED_MONGODB_SERVER) + await memoryServer.start(); + await startApp(); + await populateDatabase(); +}); + +afterAll(async () => { + if (!process.env.DEDICATED_MONGODB_SERVER) + await memoryServer.stop(); +}); describe("/login", async () => { - const correctRespose = await request(app).post("/login").send({ - app: "tester", - secret: "test", - }); + const correctRespose = await request(app) + .post("/login") + .send({ app: "tester", secret: "test" }); it("should return 200 for correct login", () => { expect(correctRespose.status).toBe(200); }); diff --git a/tests/memoryServer.ts b/tests/memoryServer.ts new file mode 100644 index 0000000..8dd7513 --- /dev/null +++ b/tests/memoryServer.ts @@ -0,0 +1,19 @@ +import { MongoMemoryServer } from "mongodb-memory-server"; + +class MemoryServer { + mongod: MongoMemoryServer | undefined; + + async start() { + this.mongod = await MongoMemoryServer.create(); + const uri = this.mongod.getUri("bot"); + process.env.MONGODB_URI = uri; + } + + async stop() { + if (this.mongod) { + await this.mongod.stop(); + } + } +} + +export default new MemoryServer(); \ No newline at end of file diff --git a/tests/populateDatabase.ts b/tests/populateDatabase.ts new file mode 100644 index 0000000..93f63c1 --- /dev/null +++ b/tests/populateDatabase.ts @@ -0,0 +1,15 @@ +import authModel from "../src/models/AuthModel"; +import imageModel from "../src/models/ImageModel"; + +export default async function () { + await imageModel.create({ + url: "https://example.com", + status: "consumed", + tags: ["2girls", "sleeping"], + }); + + await authModel.create({ + app: "tester", + secret: "test", + }); +}