mirror of
https://github.com/spacebarchat/server.git
synced 2026-05-25 20:44:59 +00:00
restructure to single project
This commit is contained in:
committed by
TheArcaneBrony
parent
5abd6bb7e0
commit
5e86d7ab9c
@@ -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);
|
||||
}
|
||||
});
|
||||
});
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 253 KiB |
@@ -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);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -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");
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -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();
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -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);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -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);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -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);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -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}`,
|
||||
};
|
||||
},
|
||||
});
|
||||
Reference in New Issue
Block a user