Added fake data for test and dev environment #17
|
@ -12,7 +12,6 @@ services:
|
||||||
MONGO_INITDB_DATABASE: bot
|
MONGO_INITDB_DATABASE: bot
|
||||||
volumes:
|
volumes:
|
||||||
- mongodb_data:/data/db
|
- mongodb_data:/data/db
|
||||||
- ./mongo-init.js:/docker-entrypoint-initdb.d/mongo-init.js:ro
|
|
||||||
|
|
||||||
bot-api:
|
bot-api:
|
||||||
image: oven/bun:1
|
image: oven/bun:1
|
||||||
|
@ -28,6 +27,8 @@ services:
|
||||||
MONGODB_USER: "root"
|
MONGODB_USER: "root"
|
||||||
MONGODB_PASS: "password"
|
MONGODB_PASS: "password"
|
||||||
JWTSECRET: "cooljwtsecret"
|
JWTSECRET: "cooljwtsecret"
|
||||||
|
# DEDICATED_MONGODB_SERVER is for integration testing, for disabling memory server in tests
|
||||||
|
DEDICATED_MONGODB_SERVER: "true"
|
||||||
volumes:
|
volumes:
|
||||||
- ./:/usr/src/app:ro
|
- ./:/usr/src/app:ro
|
||||||
|
|
||||||
|
|
|
@ -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",
|
|
||||||
});
|
|
|
@ -4,9 +4,11 @@
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/jest": "^29.5.11",
|
"@types/jest": "^29.5.11",
|
||||||
|
"@types/mongodb-memory-server": "^2.3.0",
|
||||||
"@types/supertest": "^6.0.1",
|
"@types/supertest": "^6.0.1",
|
||||||
"bun-types": "latest",
|
"bun-types": "latest",
|
||||||
"jest": "^29.7.0",
|
"jest": "^29.7.0",
|
||||||
|
"mongodb-memory-server": "^9.1.3",
|
||||||
"supertest": "^6.3.3",
|
"supertest": "^6.3.3",
|
||||||
"ts-jest": "^29.1.1"
|
"ts-jest": "^29.1.1"
|
||||||
},
|
},
|
||||||
|
|
|
@ -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;
|
43
src/index.ts
43
src/index.ts
|
@ -1,42 +1,3 @@
|
||||||
import express from "express";
|
import { startApp } from "./app";
|
||||||
import mongoose from "mongoose";
|
|
||||||
import listEndpoints from "express-list-endpoints";
|
|
||||||
import imageController from "./controllers/ImageController";
|
|
||||||
import authControler from "./controllers/AuthControler";
|
|
||||||
|
|
||||||
export const app = express();
|
startApp();
|
||||||
|
|
||||||
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();
|
|
|
@ -9,11 +9,12 @@ const AuthSchema = new mongoose.Schema({
|
||||||
app: {
|
app: {
|
||||||
type: String,
|
type: String,
|
||||||
required: true,
|
required: true,
|
||||||
|
index: true,
|
||||||
},
|
},
|
||||||
secret: {
|
secret: {
|
||||||
type: String,
|
type: String,
|
||||||
required: true,
|
required: true,
|
||||||
},
|
},
|
||||||
});
|
}, { collection: "authorizations" });
|
||||||
|
|
||||||
export default mongoose.model("authorizations", AuthSchema);
|
export default mongoose.model("authorizations", AuthSchema);
|
||||||
|
|
|
@ -10,6 +10,8 @@ const ImageSchema = new mongoose.Schema({
|
||||||
url: {
|
url: {
|
||||||
type: String,
|
type: String,
|
||||||
required: true,
|
required: true,
|
||||||
|
index: true,
|
||||||
|
unique: true
|
||||||
},
|
},
|
||||||
status: {
|
status: {
|
||||||
type: String,
|
type: String,
|
||||||
|
@ -17,10 +19,11 @@ const ImageSchema = new mongoose.Schema({
|
||||||
values: ["consumed", "unavailable", "available"],
|
values: ["consumed", "unavailable", "available"],
|
||||||
},
|
},
|
||||||
required: true,
|
required: true,
|
||||||
|
index: true,
|
||||||
},
|
},
|
||||||
tags: {
|
tags: {
|
||||||
type: [String],
|
type: [String],
|
||||||
},
|
},
|
||||||
});
|
}, { collection: "images" });
|
||||||
|
|
||||||
export default mongoose.model<Image>("images", ImageSchema);
|
export default mongoose.model<Image>("images", ImageSchema);
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
import AuthModel, { Auth } from "../models/AuthModel";
|
import AuthModel, { Auth } from "../models/AuthModel";
|
||||||
|
|
||||||
class AuthService {
|
class AuthService {
|
||||||
async find(app: String, secret: String): Promise<Auth | null> {
|
async find(app: string, secret: string): Promise<Auth | null> {
|
||||||
const auth = await AuthModel.findOne({ app: app, secret: secret }).exec();
|
const auth = await AuthModel.findOne({ app: app, secret: secret });
|
||||||
return auth;
|
return auth;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 request from "supertest";
|
||||||
import { app } from "../src";
|
import app, { startApp } from "../src/app";
|
||||||
import imageService from "../src/services/ImageService";
|
import imageService from "../src/services/ImageService";
|
||||||
|
import memoryServer from "./memoryServer";
|
||||||
|
import populateDatabase from "./populateDatabase";
|
||||||
|
|
||||||
const imageServiceOriginal = imageService;
|
const imageServiceOriginal = imageService;
|
||||||
const tok = await request(app)
|
|
||||||
.post("/login")
|
let token: string;
|
||||||
.send({ app: "tester", secret: "test" });
|
|
||||||
const token = tok.body.token;
|
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(() => {
|
afterEach(() => {
|
||||||
mock.restore();
|
mock.restore();
|
||||||
mock.module("../src/services/ImageService", () => ({
|
mock.module("../src/services/ImageService", () => ({
|
||||||
default: imageServiceOriginal,
|
default: imageServiceOriginal,
|
||||||
}));
|
}));
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("GET / shows all of the endpoints", async () => {
|
describe("GET / shows all of the endpoints", async () => {
|
||||||
const res = await request(app).get("/");
|
const res = await request(app).get("/");
|
||||||
|
|
||||||
it("should be", async () => {
|
it("should be", async () => {
|
||||||
expect(res.body).toHaveProperty("endpoints");
|
expect(res.body).toHaveProperty("endpoints");
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should be an array", () => {
|
it("should be an array", () => {
|
||||||
expect(Array.isArray(res.body.endpoints)).toBeTrue();
|
expect(Array.isArray(res.body.endpoints)).toBeTrue();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("GET /images works properly", async () => {
|
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", () => {
|
it("should be an array", () => {
|
||||||
expect(Array.isArray(res.body.images)).toBeTrue();
|
expect(Array.isArray(res.body.images)).toBeTrue();
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should return a 200", async () => {
|
it("should return a 200", async () => {
|
||||||
expect(res.statusCode).toBe(200);
|
expect(res.statusCode).toBe(200);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("POST /images works properly", () => {
|
describe("POST /images works properly", () => {
|
||||||
it("should return 401 for unauthenticated requests", async () => {
|
it("should return 401 for unauthenticated requests", async () => {
|
||||||
const res = await request(app)
|
const res = await request(app)
|
||||||
.post("/images")
|
.post("/images")
|
||||||
.send({
|
.send({
|
||||||
url: "https://test.url.com/0",
|
url: "https://test.url.com/0",
|
||||||
status: "available",
|
status: "available",
|
||||||
tags: ["2girls", "touhou"],
|
tags: ["2girls", "touhou"],
|
||||||
});
|
});
|
||||||
expect(res.status).toBe(401);
|
expect(res.status).toBe(401);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should return 403 for invalid tokens", async () => {
|
it("should return 403 for invalid tokens", async () => {
|
||||||
const res = await request(app)
|
const res = await request(app)
|
||||||
.post("/images")
|
.post("/images")
|
||||||
.set("authorization", `Bearer token`)
|
.set("authorization", `Bearer token`)
|
||||||
.send({
|
.send({
|
||||||
url: "https://test.url.com/0",
|
url: "https://test.url.com/0",
|
||||||
status: "available",
|
status: "available",
|
||||||
tags: ["2girls", "touhou"],
|
tags: ["2girls", "touhou"],
|
||||||
});
|
});
|
||||||
expect(res.status).toBe(403);
|
expect(res.status).toBe(403);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should return 201 for new image", async () => {
|
it("should return 201 for new image", async () => {
|
||||||
const res = await request(app)
|
const res = await request(app)
|
||||||
.post("/images")
|
.post("/images")
|
||||||
.set("authorization", `Bearer ${token}`)
|
.set("authorization", `Bearer ${token}`)
|
||||||
.send({
|
.send({
|
||||||
url: "https://test.url.com/1",
|
url: "https://test.url.com/1",
|
||||||
status: "available",
|
status: "available",
|
||||||
tags: ["2girls", "touhou"],
|
tags: ["2girls", "touhou"],
|
||||||
});
|
});
|
||||||
expect(res.status).toBe(201);
|
expect(res.status).toBe(201);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should return 409 for a repeated images", async () => {
|
it("should return 409 for a repeated images", async () => {
|
||||||
await request(app)
|
await request(app)
|
||||||
.post("/images")
|
.post("/images")
|
||||||
.set("authorization", `Bearer ${token}`)
|
.set("authorization", `Bearer ${token}`)
|
||||||
.send({
|
.send({
|
||||||
url: "https://test.url.com/2",
|
url: "https://test.url.com/2",
|
||||||
status: "available",
|
status: "available",
|
||||||
tags: ["2girls", "touhou"],
|
tags: ["2girls", "touhou"],
|
||||||
});
|
});
|
||||||
|
|
||||||
const res = await request(app)
|
const res = await request(app)
|
||||||
.post("/images")
|
.post("/images")
|
||||||
.set("authorization", `Bearer ${token}`)
|
.set("authorization", `Bearer ${token}`)
|
||||||
.send({
|
.send({
|
||||||
url: "https://test.url.com/2",
|
url: "https://test.url.com/2",
|
||||||
status: "available",
|
status: "available",
|
||||||
tags: ["2girls", "touhou"],
|
tags: ["2girls", "touhou"],
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(res.status).toBe(409);
|
expect(res.status).toBe(409);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should return 500 for an error on the service", async () => {
|
it("should return 500 for an error on the service", async () => {
|
||||||
mock.module("../src/services/ImageService", () => ({
|
mock.module("../src/services/ImageService", () => ({
|
||||||
default: {
|
default: {
|
||||||
add: () => {
|
add: () => {
|
||||||
throw new Error("This is an expected testing error");
|
throw new Error("This is an expected testing error");
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}));
|
}));
|
||||||
|
|
||||||
const res = await request(app)
|
const res = await request(app)
|
||||||
.post("/images")
|
.post("/images")
|
||||||
.set("authorization", `Bearer ${token}`)
|
.set("authorization", `Bearer ${token}`)
|
||||||
.send({
|
.send({
|
||||||
url: "https://test.url.com/3",
|
url: "https://test.url.com/3",
|
||||||
status: "available",
|
status: "available",
|
||||||
tags: ["2girls", "touhou"],
|
tags: ["2girls", "touhou"],
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(res.status).toBe(500);
|
expect(res.status).toBe(500);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should return 400 for malformed requests", async () => {
|
it("should return 400 for malformed requests", async () => {
|
||||||
mock.restore();
|
mock.restore();
|
||||||
const res = await request(app)
|
const res = await request(app)
|
||||||
.post("/images")
|
.post("/images")
|
||||||
.set("authorization", `Bearer ${token}`)
|
.set("authorization", `Bearer ${token}`)
|
||||||
.send({
|
.send({
|
||||||
url: "https://test.url.com/4",
|
url: "https://test.url.com/4",
|
||||||
status: "wrong",
|
status: "wrong",
|
||||||
tags: ["2girls", "touhou"],
|
tags: ["2girls", "touhou"],
|
||||||
});
|
});
|
||||||
expect(res.status).toBe(400);
|
expect(res.status).toBe(400);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -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 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 () => {
|
describe("/login", async () => {
|
||||||
const correctRespose = await request(app).post("/login").send({
|
const correctRespose = await request(app)
|
||||||
app: "tester",
|
.post("/login")
|
||||||
secret: "test",
|
.send({ app: "tester", secret: "test" });
|
||||||
});
|
|
||||||
it("should return 200 for correct login", () => {
|
it("should return 200 for correct login", () => {
|
||||||
expect(correctRespose.status).toBe(200);
|
expect(correctRespose.status).toBe(200);
|
||||||
});
|
});
|
||||||
|
|
|
@ -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();
|
|
@ -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",
|
||||||
|
});
|
||||||
|
}
|
Loading…
Reference in New Issue