restructure to single project

This commit is contained in:
Flam3rboy
2022-08-13 02:00:50 +02:00
committed by TheArcaneBrony
parent 5abd6bb7e0
commit 5e86d7ab9c
626 changed files with 7256 additions and 2449 deletions
+43
View File
@@ -0,0 +1,43 @@
const { initDatabase, closeDatabase } = require("../dist/util/Database");
const { User } = require("../dist/entities/User");
jest.setTimeout(20000);
beforeAll((done) => {
initDatabase().then(() => {
done();
});
});
afterAll(() => {
closeDatabase();
});
describe("User", () => {
test("valid discriminator: 1", async () => {
new User({ discriminator: "1" }).validate();
});
test("invalid discriminator: test", async () => {
expect(() => {
new User({ discriminator: "test" }).validate();
}).toThrow();
});
test("invalid discriminator: 0", async () => {
expect(() => {
new User({ discriminator: "0" }).validate();
}).toThrow();
});
test("add guild", async () => {
try {
await new User({ guilds: [], discriminator: "1" }, { id: "0" }).save();
const user = await User.find("0");
user.guilds.push(new Guild({ name: "test" }));
user.save();
} catch (error) {
console.error(error);
}
});
});
BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 253 KiB

+238
View File
@@ -0,0 +1,238 @@
const dotenv = require("dotenv");
const path = require("path");
const fs = require("fs");
dotenv.config();
// TODO: write unittest to check if FileStorage.ts is working
// TODO: write unitest to check if env vars are defined
if (!process.env.STORAGE_PROVIDER) process.env.STORAGE_PROVIDER = "file";
// TODO:nodejs path.join trailing slash windows compatible
if (process.env.STORAGE_PROVIDER === "file") {
if (process.env.STORAGE_LOCATION) {
if (!process.env.STORAGE_LOCATION.startsWith("/")) {
process.env.STORAGE_LOCATION = path.join(
__dirname,
"..",
process.env.STORAGE_LOCATION,
"/"
);
}
} else {
process.env.STORAGE_LOCATION = path.join(__dirname, "..", "files", "/");
}
if(!fs.existsSync(process.env.STORAGE_LOCATION)) fs.mkdirSync(process.env.STORAGE_LOCATION, {recursive:true});
}
const { CDNServer } = require("../dist/Server");
const { Config } = require("@fosscord/util");
const supertest = require("supertest");
const request = supertest("http://localhost:3003");
const server = new CDNServer({ port: Number(process.env.PORT) || 3003 });
beforeAll(async () => {
await server.start();
return server;
});
afterAll(() => {
return server.stop();
});
describe("/ping", () => {
describe("GET", () => {
describe("without signature specified", () => {
test("route should respond with 200", async () => {
let response = await request.get("/ping");
expect(response.text).toBe("pong");
});
});
});
});
describe("/attachments", () => {
describe("POST", () => {
describe("without signature specified", () => {
test("route should respond with 400", async () => {
const response = await request.post("/attachments/123456789");
expect(response.statusCode).toBe(400);
});
});
describe("with signature specified, without file specified", () => {
test("route should respond with 400", async () => {
const response = await request
.post("/attachments/123456789")
.set({ signature: Config.get().security.requestSignature });
expect(response.statusCode).toBe(400);
});
});
describe("with signature specified, with file specified ", () => {
test("route should respond with Content-type: application/json, 200 and res.body.url", async () => {
const response = await request
.post("/attachments/123456789")
.set({ signature: Config.get().security.requestSignature })
.attach("file", __dirname + "/antman.jpg");
expect(response.statusCode).toBe(200);
expect(response.headers["content-type"]).toEqual(
expect.stringContaining("json")
);
expect(response.body.url).toBeDefined();
});
});
});
describe("GET", () => {
describe("getting uploaded image by url returned by POST /attachments", () => {
test("route should respond with 200", async () => {
let response = await request
.post("/attachments/123456789")
.set({ signature: Config.get().security.requestSignature })
.attach("file", __dirname + "/antman.jpg");
request
.get(response.body.url.replace("http://localhost:3003", ""))
.then((x) => {
expect(x.statusCode).toBe(200);
});
});
});
});
describe("DELETE", () => {
describe("deleting uploaded image by url returned by POST /attachments", () => {
test("route should respond with res.body.success", async () => {
let response = await request
.post("/attachments/123456789")
.set({ signature: Config.get().security.requestSignature })
.attach("file", __dirname + "/antman.jpg");
request
.delete(
response.body.url.replace("http://localhost:3003", "")
)
.then((x) => {
expect(x.body.success).toBeDefined();
});
});
});
});
});
describe("/avatars", () => {
describe("POST", () => {
describe("without signature specified", () => {
test("route should respond with 400", async () => {
const response = await request.post("/avatars/123456789");
expect(response.statusCode).toBe(400);
});
});
describe("with signature specified, without file specified", () => {
test("route should respond with 400", async () => {
const response = await request
.post("/avatars/123456789")
.set({ signature: Config.get().security.requestSignature });
expect(response.statusCode).toBe(400);
});
});
describe("with signature specified, with file specified ", () => {
test("route should respond with Content-type: application/json, 200 and res.body.url", async () => {
const response = await request
.post("/avatars/123456789")
.set({ signature: Config.get().security.requestSignature })
.attach("file", __dirname + "/antman.jpg");
expect(response.statusCode).toBe(200);
expect(response.headers["content-type"]).toEqual(
expect.stringContaining("json")
);
expect(response.body.url).toBeDefined();
});
});
});
describe("GET", () => {
describe("getting uploaded image by url returned by POST /avatars", () => {
test("route should respond with 200", async () => {
let response = await request
.post("/avatars/123456789")
.set({ signature: Config.get().security.requestSignature })
.attach("file", __dirname + "/antman.jpg");
request
.get(response.body.url.replace("http://localhost:3003", ""))
.then((x) => {
expect(x.statusCode).toBe(200);
});
});
});
});
describe("DELETE", () => {
describe("deleting uploaded image by url returned by POST /avatars", () => {
test("route should respond with res.body.success", async () => {
let response = await request
.post("/avatars/123456789")
.set({ signature: Config.get().security.requestSignature })
.attach("file", __dirname + "/antman.jpg");
request
.delete(
response.body.url.replace("http://localhost:3003", "")
)
.then((x) => {
expect(x.body.success).toBeDefined();
});
});
});
});
});
describe("/external", () => {
describe("POST", () => {
describe("without signature specified", () => {
test("route should respond with 400", async () => {
const response = await request.post("/external");
expect(response.statusCode).toBe(400);
});
});
describe("with signature specified, without file specified", () => {
test("route should respond with 400", async () => {
const response = await request
.post("/external")
.set({ signature: Config.get().security.requestSignature });
expect(response.statusCode).toBe(400);
});
});
describe("with signature specified, with file specified ", () => {
test("route should respond with Content-type: application/json, 200 and res.body.url", async () => {
const response = await request
.post("/external")
.set({ signature: Config.get().security.requestSignature })
.send({
url: "https://i.ytimg.com/vi_webp/TiXzhQr5AUc/mqdefault.webp",
});
expect(response.statusCode).toBe(200);
expect(response.headers["content-type"]).toEqual(
expect.stringContaining("json")
);
expect(response.body.id).toBeDefined();
});
});
describe("with signature specified, with falsy url specified ", () => {
test("route should respond with 400", async () => {
const response = await request
.post("/external")
.set({ signature: Config.get().security.requestSignature })
.send({
url: "notavalidurl.123",
});
expect(response.statusCode).toBe(400);
});
});
});
describe("GET", () => {
describe("getting uploaded image by url returned by POST /avatars", () => {
test("route should respond with 200", async () => {
let response = await request
.post("/external")
.set({ signature: Config.get().security.requestSignature })
.send({
url: "https://i.ytimg.com/vi_webp/TiXzhQr5AUc/mqdefault.webp",
});
request.get(`external/${response.body.id}`).then((x) => {
expect(x.statusCode).toBe(200);
});
});
});
});
});
+27
View File
@@ -0,0 +1,27 @@
const path = require("path");
process.env.STORAGE_LOCATION = path.join(__dirname, "..", "files", "/");
const { FileStorage } = require("../dist/util/FileStorage");
const storage = new FileStorage();
const fs = require("fs");
const file = fs.readFileSync(path.join(__dirname, "antman.jpg"));
describe("FileStorage", () => {
describe("saving a file", () => {
test("saving a buffer", async () => {
await storage.set("test_saving_file", file);
});
});
describe("getting a file", () => {
test("getting buffer with given name", async () => {
const buffer2 = await storage.get("test_saving_file");
expect(Buffer.compare(file, buffer2)).toBeTruthy();
});
});
describe("deleting a file", () => {
test("deleting buffer with given name", async () => {
await storage.delete("test_saving_file");
});
});
});
+155
View File
@@ -0,0 +1,155 @@
// TODO: check every route based on route() parameters: https://github.com/fosscord/fosscord-server/issues/308
// TODO: check every route with different database engine
import getRouteDescriptions from "../jest/getRouteDescriptions";
import { join } from "path";
import fs from "fs";
import Ajv from "ajv";
import addFormats from "ajv-formats";
import fetch from "node-fetch";
import { Event, User, events, Guild, Channel } from "@fosscord/util";
const SchemaPath = join(__dirname, "..", "assets", "schemas.json");
const schemas = JSON.parse(fs.readFileSync(SchemaPath, { encoding: "utf8" }));
export const ajv = new Ajv({
allErrors: true,
parseDate: true,
allowDate: true,
schemas,
messages: true,
strict: true,
strictRequired: true,
coerceTypes: true
});
addFormats(ajv);
let token: string;
let user: User;
let guild: Guild;
let channel: Channel;
const request = async (path: string, opts: any = {}): Promise<any> => {
const response = await fetch(`http://localhost:3001/api${path}`, {
...opts,
method: opts.method || opts.body ? "POST" : "GET",
body: opts.body && JSON.stringify(opts.body),
headers: {
authorization: token,
...(opts.body ? { "content-type": "application/json" } : {}),
...(opts.header || {})
}
});
if (response.status === 204) return;
let data = await response.text();
try {
data = JSON.parse(data);
if (response.status >= 400) throw data;
return data;
} catch (error) {
throw data;
}
};
beforeAll(async (done) => {
try {
const response = await request("/auth/register", {
body: {
fingerprint: "805826570869932034.wR8vi8lGlFBJerErO9LG5NViJFw",
username: "tester",
invite: null,
consent: true,
date_of_birth: "2000-01-01",
gift_code_sku_id: null,
captcha_key: null
}
});
token = response.token;
user = await request(`/users/@me`);
const { id: guild_id } = await request("/guilds", { body: { name: "test server" } });
guild = await request(`/guilds/${guild_id}`);
channel = (await request(`/guilds/${guild_id}/channels`))[0];
done();
} catch (error) {
done(error);
}
});
const emit = events.emit;
events.emit = (event: string | symbol, ...args: any[]) => {
events.emit("event", args[0]);
return emit(event, ...args);
};
describe("Automatic unit tests with route description middleware", () => {
const routes = getRouteDescriptions();
routes.forEach((route, pathAndMethod) => {
const [path, method] = pathAndMethod.split("|");
test(`${method.toUpperCase()} ${path}`, async (done) => {
if (!route.test) {
console.log(`${(route as any).file}\nrouter.${method} is missing the test property`);
return done();
}
const urlPath =
path.replace(":id", user.id).replace(":guild_id", guild.id).replace(":channel_id", channel.id) || route.test?.path;
let validate: any;
if (route.test.body) {
validate = ajv.getSchema(route.test.body);
if (!validate) return done(new Error(`Response schema ${route.test.body} not found`));
}
let body = "";
let eventEmitted = Promise.resolve();
if (route.test.event) {
if (!Array.isArray(route.test.event)) route.test.event = [route.test.event];
eventEmitted = new Promise((resolve, reject) => {
const timeout = setTimeout(() => reject, 1000);
const received = [];
events.on("event", (event: Event) => {
if (!route.test.event.includes(event.event)) return;
received.push(event.event);
if (received.length === route.test.event.length) resolve();
});
});
}
try {
const response = await fetch(`http://localhost:3001/api${urlPath}`, {
method: method.toUpperCase(),
body: JSON.stringify(route.test.body),
headers: { ...route.test.headers, authorization: token }
});
body = await response.text();
expect(response.status, body).toBe(route.test.response.status || 200);
// TODO: check headers
// TODO: expect event
if (validate) {
body = JSON.parse(body);
const valid = validate(body);
if (!valid) return done(validate.errors);
}
} catch (error) {
return done(error);
}
try {
await eventEmitted;
} catch (error) {
return done(new Error(`Event ${route.test.event} was not emitted`));
}
return done();
});
});
});
+33
View File
@@ -0,0 +1,33 @@
const supertest = require("supertest");
const request = supertest("http://localhost:3001");
describe("/api/auth/login", () => {
describe("POST", () => {
test("without body", async () => {
const response = await request.post("/api/auth/login").send({});
expect(response.statusCode).toBe(400);
});
test("with body", async () => {
const user = {
login: "fortnitefortnite@gmail.com",
password: "verysecurepassword"
};
await request.post("/api/auth/register").send({
fingerprint: "805826570869932034.wR8vi8lGlFBJerErO9LG5NViJFw",
email: user.login,
username: user.login.split("@")[0],
password: user.password,
invite: null,
consent: true,
date_of_birth: "2000-04-04",
gift_code_sku_id: null,
captcha_key: null
});
const response = await request.post("/api/auth/login").send(user);
expect(response.statusCode).toBe(200);
});
});
});
+27
View File
@@ -0,0 +1,27 @@
const supertest = require("supertest");
const request = supertest("http://localhost:3001");
describe("/api/auth/register", () => {
describe("POST", () => {
test("without body", async () => {
const response = await request.post("/api/auth/register").send({});
expect(response.statusCode).toBe(400);
});
test("with body", async () => {
const response = await request.post("/api/auth/register").send({
fingerprint: "805826570869932034.wR8vi8lGlFBJerErO9LG5NViJFw",
email: "qo8etzvaf@gmail.com",
username: "qp39gr98",
password: "wtp9gep9gw",
invite: null,
consent: true,
date_of_birth: "2000-04-04",
gift_code_sku_id: null,
captcha_key: null
});
expect(response.statusCode).toBe(200);
});
});
});
+12
View File
@@ -0,0 +1,12 @@
const supertest = require("supertest");
const request = supertest("http://localhost:3001");
describe("/ping", () => {
describe("GET", () => {
test("should return 200 and pong", async () => {
let response = await request.get("/api/ping");
expect(response.text).toBe("pong");
expect(response.statusCode).toBe(200);
});
});
});
+23
View File
@@ -0,0 +1,23 @@
const { performance } = require("perf_hooks");
const fs = require("fs");
const path = require("path");
// fs.unlinkSync(path.join(__dirname, "..", "database.db"));
global.expect.extend({
toBeFasterThan: async (func, target) => {
const start = performance.now();
let error;
try {
await func();
} catch (e) {
error = e.toString();
}
const time = performance.now() - start;
return {
pass: time < target && !error,
message: () => error || `${func.name} took ${time}ms of maximum ${target}`,
};
},
});