Added concurrency test, and added CORS and CORS test
This commit is contained in:
parent
fac0a38858
commit
2f140996d5
|
@ -14,8 +14,10 @@
|
||||||
"typescript": "^5.0.0"
|
"typescript": "^5.0.0"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@types/cors": "^2.8.17",
|
||||||
"async-mutex": "^0.5.0",
|
"async-mutex": "^0.5.0",
|
||||||
"compression": "^1.7.4",
|
"compression": "^1.7.4",
|
||||||
|
"cors": "^2.8.5",
|
||||||
"express": "^4.19.2",
|
"express": "^4.19.2",
|
||||||
"express-list-endpoints": "^6.0.0",
|
"express-list-endpoints": "^6.0.0",
|
||||||
"winston": "^3.13.0"
|
"winston": "^3.13.0"
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import compression from "compression";
|
import compression from "compression";
|
||||||
|
import cors from "cors";
|
||||||
import express from "express";
|
import express from "express";
|
||||||
import listEndpoints from "express-list-endpoints";
|
import listEndpoints from "express-list-endpoints";
|
||||||
import * as imageController from "src/controllers/imageController";
|
import * as imageController from "src/controllers/imageController";
|
||||||
|
@ -7,6 +8,7 @@ const app = express();
|
||||||
|
|
||||||
app.use(express.json());
|
app.use(express.json());
|
||||||
app.use(compression());
|
app.use(compression());
|
||||||
|
app.use(cors());
|
||||||
|
|
||||||
app.get("/", (_, res) => {
|
app.get("/", (_, res) => {
|
||||||
const endpoints = listEndpoints(app);
|
const endpoints = listEndpoints(app);
|
||||||
|
|
|
@ -3,9 +3,10 @@ import logger from "src/logger";
|
||||||
import GelbooruApiResponse from "src/types/GelbooruApiResponse";
|
import GelbooruApiResponse from "src/types/GelbooruApiResponse";
|
||||||
import GelbooruServiceResponse from "src/types/GelbooruServiceResponse";
|
import GelbooruServiceResponse from "src/types/GelbooruServiceResponse";
|
||||||
|
|
||||||
|
export const LIMIT = env.GELBOORU_IMAGES_PER_REQUEST || 100;
|
||||||
|
export const TAGS = encodeURIComponent(env.GELBOORU_TAGS || "");
|
||||||
|
|
||||||
export async function get(): Promise<GelbooruServiceResponse> {
|
export async function get(): Promise<GelbooruServiceResponse> {
|
||||||
const LIMIT = env.GELBOORU_IMAGES_PER_REQUEST || 100;
|
|
||||||
const TAGS = encodeURIComponent(env.GELBOORU_TAGS || "");
|
|
||||||
const url: string = `https://gelbooru.com/index.php?page=dapi&s=post&q=index&limit=${LIMIT}&json=1&tags=${TAGS}`;
|
const url: string = `https://gelbooru.com/index.php?page=dapi&s=post&q=index&limit=${LIMIT}&json=1&tags=${TAGS}`;
|
||||||
|
|
||||||
const response: GelbooruApiResponse = (await fetch(url).then(
|
const response: GelbooruApiResponse = (await fetch(url).then(
|
||||||
|
|
|
@ -0,0 +1,10 @@
|
||||||
|
import { describe, expect, it } from "bun:test";
|
||||||
|
import app from "src/app";
|
||||||
|
import request from "supertest";
|
||||||
|
|
||||||
|
describe("CORS implementation", () => {
|
||||||
|
it("should implement CORS", async () => {
|
||||||
|
const { headers } = await request(app).get('/');
|
||||||
|
expect(headers['access-control-allow-origin']).toEqual('*');
|
||||||
|
})
|
||||||
|
})
|
|
@ -9,8 +9,6 @@ afterAll(() => {
|
||||||
|
|
||||||
describe("endpoint returns the correct status codes", () => {
|
describe("endpoint returns the correct status codes", () => {
|
||||||
it("should return 200 if successful", async () => {
|
it("should return 200 if successful", async () => {
|
||||||
// Can't mock ImageService partially because of bun incomplete implementation of testing :(
|
|
||||||
// It goes to the network directly to test
|
|
||||||
const res = await request(app).get("/image");
|
const res = await request(app).get("/image");
|
||||||
expect(res.status).toBe(200);
|
expect(res.status).toBe(200);
|
||||||
});
|
});
|
|
@ -1,16 +1,28 @@
|
||||||
import { afterAll, describe, expect, it, jest, mock, spyOn } from "bun:test";
|
import { afterAll, describe, expect, it, jest, mock, spyOn } from "bun:test";
|
||||||
import Image from "src/types/Image";
|
import Image from "src/types/Image";
|
||||||
|
import request from "supertest";
|
||||||
import * as gelbooruApiService from "src/services/gelbooruApiService";
|
import * as gelbooruApiService from "src/services/gelbooruApiService";
|
||||||
import * as botApiService from "src/services/botApiService";
|
import * as botApiService from "src/services/botApiService";
|
||||||
import * as imageService from "src/services/imageService";
|
import * as imageService from "src/services/imageService";
|
||||||
|
import app from "src/app";
|
||||||
|
import { response } from "express";
|
||||||
|
|
||||||
afterAll(() => {
|
afterAll(() => {
|
||||||
jest.restoreAllMocks();
|
jest.restoreAllMocks();
|
||||||
})
|
})
|
||||||
|
|
||||||
describe("the service is thread-safe", () => {
|
describe("the service is thread-safe", () => {
|
||||||
it("should run normally when 2 processes call the get() method with 1 remaining image in the queue", async () => {
|
it("should not crash when multiple processes call the get() method with 1 remaining image in the queue", async () => {
|
||||||
// TODO
|
const NUM_OF_REQUESTS = 110;
|
||||||
|
|
||||||
|
const getFn = spyOn(imageService, "get");
|
||||||
|
|
||||||
|
const promises = Array(NUM_OF_REQUESTS).fill(null)
|
||||||
|
.map(_ => request(app).get("/image"));
|
||||||
|
const responses = await Promise.all(promises);
|
||||||
|
|
||||||
|
expect(getFn).toHaveBeenCalledTimes(NUM_OF_REQUESTS);
|
||||||
|
responses.forEach(res => expect(res.status).toBe(200));
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
24
yarn.lock
24
yarn.lock
|
@ -1,6 +1,6 @@
|
||||||
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
|
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
|
||||||
# yarn lockfile v1
|
# yarn lockfile v1
|
||||||
# bun ./bun.lockb --hash: 9A5A612BBFD3E7ED-76a31de2b7c4bbe8-8DB93E498B9CBB30-9cf1116024820c4f
|
# bun ./bun.lockb --hash: 4B368415A9637562-70781726b75e86ab-6261B8AB82430501-a22712f647bcbfe3
|
||||||
|
|
||||||
|
|
||||||
"@colors/colors@1.6.0", "@colors/colors@^1.6.0":
|
"@colors/colors@1.6.0", "@colors/colors@^1.6.0":
|
||||||
|
@ -44,6 +44,13 @@
|
||||||
resolved "https://registry.npmjs.org/@types/cookiejar/-/cookiejar-2.1.5.tgz"
|
resolved "https://registry.npmjs.org/@types/cookiejar/-/cookiejar-2.1.5.tgz"
|
||||||
integrity sha512-he+DHOWReW0nghN24E1WUqM0efK4kI9oTqDm6XmK8ZPe2djZ90BSNdGnIyCLzCPw7/pogPlGbzI2wHGGmi4O/Q==
|
integrity sha512-he+DHOWReW0nghN24E1WUqM0efK4kI9oTqDm6XmK8ZPe2djZ90BSNdGnIyCLzCPw7/pogPlGbzI2wHGGmi4O/Q==
|
||||||
|
|
||||||
|
"@types/cors@^2.8.17":
|
||||||
|
version "2.8.17"
|
||||||
|
resolved "https://registry.npmjs.org/@types/cors/-/cors-2.8.17.tgz"
|
||||||
|
integrity sha512-8CGDvrBj1zgo2qE+oS3pOCyYNqCPryMWY2bGfwA0dcfopWGgxs+78df0Rs3rc9THP4JkOhLsAa+15VdpAqkcUA==
|
||||||
|
dependencies:
|
||||||
|
"@types/node" "*"
|
||||||
|
|
||||||
"@types/express@*", "@types/express@^4.17.21":
|
"@types/express@*", "@types/express@^4.17.21":
|
||||||
version "4.17.21"
|
version "4.17.21"
|
||||||
resolved "https://registry.npmjs.org/@types/express/-/express-4.17.21.tgz"
|
resolved "https://registry.npmjs.org/@types/express/-/express-4.17.21.tgz"
|
||||||
|
@ -336,6 +343,14 @@ cookiejar@^2.1.4:
|
||||||
resolved "https://registry.npmjs.org/cookiejar/-/cookiejar-2.1.4.tgz"
|
resolved "https://registry.npmjs.org/cookiejar/-/cookiejar-2.1.4.tgz"
|
||||||
integrity sha512-LDx6oHrK+PhzLKJU9j5S7/Y3jM/mUHvD/DeI1WQmJn652iPC5Y4TBzC9l+5OMOXlyTTA+SmVUPm0HQUwpD5Jqw==
|
integrity sha512-LDx6oHrK+PhzLKJU9j5S7/Y3jM/mUHvD/DeI1WQmJn652iPC5Y4TBzC9l+5OMOXlyTTA+SmVUPm0HQUwpD5Jqw==
|
||||||
|
|
||||||
|
cors@^2.8.5:
|
||||||
|
version "2.8.5"
|
||||||
|
resolved "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz"
|
||||||
|
integrity sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==
|
||||||
|
dependencies:
|
||||||
|
object-assign "^4"
|
||||||
|
vary "^1"
|
||||||
|
|
||||||
debug@2.6.9:
|
debug@2.6.9:
|
||||||
version "2.6.9"
|
version "2.6.9"
|
||||||
resolved "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz"
|
resolved "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz"
|
||||||
|
@ -689,6 +704,11 @@ negotiator@0.6.3:
|
||||||
resolved "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz"
|
resolved "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz"
|
||||||
integrity sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==
|
integrity sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==
|
||||||
|
|
||||||
|
object-assign@^4:
|
||||||
|
version "4.1.1"
|
||||||
|
resolved "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz"
|
||||||
|
integrity sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==
|
||||||
|
|
||||||
object-inspect@^1.13.1:
|
object-inspect@^1.13.1:
|
||||||
version "1.13.1"
|
version "1.13.1"
|
||||||
resolved "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.1.tgz"
|
resolved "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.1.tgz"
|
||||||
|
@ -953,7 +973,7 @@ utils-merge@1.0.1:
|
||||||
resolved "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz"
|
resolved "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz"
|
||||||
integrity sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==
|
integrity sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==
|
||||||
|
|
||||||
vary@~1.1.2:
|
vary@^1, vary@~1.1.2:
|
||||||
version "1.1.2"
|
version "1.1.2"
|
||||||
resolved "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz"
|
resolved "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz"
|
||||||
integrity sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==
|
integrity sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==
|
||||||
|
|
Loading…
Reference in New Issue