UNPKG

@webda/core

Version:

Expose API with Lambda

399 lines 13.1 kB
import { execSync } from "child_process"; import { existsSync, unlinkSync } from "fs"; import * as path from "path"; import { register } from "prom-client"; import { Core, HttpContext, WebContext } from "./index.js"; import { PrometheusService } from "./services/prometheus.js"; import { ConsoleLoggerService } from "./utils/logger.js"; import { FileUtils } from "./utils/serializers.js"; // Separation on purpose to keep application import separated import { SectionEnum } from "./application.js"; import { UnpackedApplication } from "./unpackedapplication.js"; /** * TestApplication ensure we load the typescript sources instead of compiled version * * Test use ts-node so to share same prototypes we need to load from the sources */ export class TestApplication extends UnpackedApplication { constructor(file, logger) { super(file || "./", logger); /** * Flag if application has been compiled already */ this.compiled = false; } /** * Force the namespace to WebdaDemo * @returns */ getNamespace() { return "WebdaDemo"; } /** * Set the status of the compilation * * @param compile true will avoid trigger new compilation */ preventCompilation(compile) { this.compiled = compile; } /** * Compile the application */ compile() { if (this.compiled) { return; } // exec typescript this.log("DEBUG", "Compiling application"); try { execSync(`tsc -p ${this.appPath}`); } catch (err) { (err.stdout.toString() + err.stderr.toString()) .split("\n") .filter(l => l !== "") .forEach(l => { this.log("ERROR", "tsc:", l); }); } this.compiled = true; } /** * Load a webda.module.json file * Resolve the linked file to current application * * @param moduleFile to load * @returns */ loadWebdaModule(moduleFile) { // Test are using ts-node so local source should be loaded from .ts with ts-node aswell if (process.cwd() === path.dirname(moduleFile)) { let module = FileUtils.load(moduleFile); Object.keys(SectionEnum) .filter(k => Number.isNaN(+k)) .forEach(p => { for (let key in module[SectionEnum[p]]) { module[SectionEnum[p]][key] = path.join(path.relative(this.getAppPath(), path.dirname(moduleFile)), module[SectionEnum[p]][key].replace(/^lib\//, "src/")); } }); for (let key in module.models.list) { module.models.list[key] = path.join(path.relative(this.getAppPath(), path.dirname(moduleFile)), module.models.list[key].replace(/^lib\//, "src/")); } return module; } return super.loadWebdaModule(moduleFile); } } /** * Utility class for UnitTest * * @category CoreFeatures */ class WebdaTest { constructor() { this.addConsoleLogger = true; /** * Files to clean after test */ this.cleanFiles = []; } /** * Get the configuration file to use for the test * * @returns absolute path to configuration file */ getTestConfiguration() { return process.cwd() + "/test/config.json"; } /** * Allow test to add custom made service * @param app */ async tweakApp(app) { app.addService("WebdaTest/VoidStore", (await import("../test/moddas/voidstore")).VoidStore); app.addService("WebdaTest/FakeService", (await import("../test/moddas/fakeservice")).FakeService); app.addService("WebdaTest/Mailer", (await import("../test/moddas/debugmailer")).DebugMailer); app.addModel("WebdaTest/Task", (await import("../test/models/task")).Task); app.addModel("WebdaTest/Ident", (await import("../test/models/ident")).Ident); } /** * Build the webda application * * Add a ConsoleLogger if addConsoleLogger is true */ async buildWebda() { let app = new TestApplication(this.getTestConfiguration()); await app.load(); await this.tweakApp(app); this.webda = new Core(app); if (this.addConsoleLogger) { // @ts-ignore - Hack a ConsoleLogger in this.webda.services["ConsoleLogger"] = new ConsoleLoggerService(this.webda, "ConsoleLogger", {}); } } /** * Rebuild Webda application before each test * * @param init wait for the full init */ async before(init = true) { // Reset any prometheus // @ts-ignore PrometheusService.nodeMetricsRegistered = false; // @ts-ignore PrometheusService.requestMetricsRegistered = false; register.clear(); await this.buildWebda(); if (init) { await this.webda.init(); // Prevent persistance for tests this.webda.getRegistry().persist = async () => { }; } } after() { // Clean all remaining files this.cleanFiles.filter(f => existsSync(f)).forEach(f => unlinkSync(f)); this.cleanFiles = []; // this.webda.stop(); } /** * * @param level * @param args */ log(level, ...args) { if (this.webda) { this.webda.log(level, "TEST", ...args); } else { console.log(level, "WEBDA NOT INITATED TEST", ...args); } } /** * Create a new Context object * * The context is initialized to GET test.webda.io/ * * @param body to add to the context * @returns */ async newContext(body = {}) { let res = await this.webda.newWebContext(new HttpContext("test.webda.io", "GET", "/")); res.getHttpContext().setBody(body); return res; } /** * Get an Executor from Webda * * @param ctx * @param host * @param method * @param url * @param body * @param headers * @returns */ getExecutor(ctx = undefined, host = "test.webda.io", method = "GET", url = "/", body = {}, headers = {}) { let httpContext = new HttpContext(host, method, url, "http", 80, headers); httpContext.setBody(body); httpContext.setClientIp("127.0.0.1"); if (!ctx) { // @ts-ignore ctx = new WebContext(this.webda, httpContext); } else { ctx.setHttpContext(httpContext); } if (this.webda.updateContextWithRoute(ctx)) { return ctx; } } /** * Execute a test request * @param params * @returns */ async http(params = {}) { if (params.context) { params.context.resetResponse(); } params.context ?? (params.context = await this.newContext()); params.method ?? (params.method = "GET"); params.url ?? (params.url = "/"); return await this.execute(params.context, "test.webda.io", params.method, params.url, params.body, params.headers); } async execute(context = undefined, host = "test.webda.io", method = "GET", url = "/", body = {}, headers = {}) { const exec = this.getExecutor(context, host, method, url, body, headers); if (!exec) { throw new Error(`${method} ${url} route not found`); } await exec.execute(context); let res = context.getResponseBody(); if (res) { try { return JSON.parse(res); } catch (err) { return res; } } } /** * Pause for time ms * * @param time ms */ async sleep(time) { return Core.sleep(time); } /** * Create a graph of objets from sample-app to be able to test graph */ async createGraphObjects() { const Teacher = this.webda.getModel("Teacher"); const Course = this.webda.getModel("Course"); const Classroom = this.webda.getModel("Classroom"); const Student = this.webda.getModel("Student"); const Hardware = this.webda.getModel("Hardware"); const ComputerScreen = this.webda.getModel("ComputerScreen"); const Company = this.webda.getModel("Company"); const User = this.webda.getModel("User"); // 2 Companies const companies = [await Company.create({ name: "company 1" }), await Company.create({ name: "company 2" })]; const users = []; for (let company of companies) { for (let i = 1; i < 6; i++) { // 2 User per company users.push(await User.create({ name: `User ${users.length + 1}`, _company: company.uuid })); } } // 2 Teachers const teachers = [await Teacher.create({ name: "test" }), await Teacher.create({ name: "test2", senior: true })]; const students = []; const courses = []; // 10 Students for (let i = 1; i < 11; i++) { students.push(await Student.create({ email: `student${i}@webda.io`, firstName: `Student ${i}`, lastName: `Lastname ${i}`, order: i })); } // 10 Topics const topics = ["Math", "French", "English", "Physics", "Computer Science"]; for (let i = 1; i < 13; i++) { let courseStudents = []; for (let j = i; j < i + 6; j++) { let s = students[j % 10]; courseStudents.push({ uuid: s.getUuid(), email: s.email, firstName: s.firstName, lastName: s.lastName }); } courses.push(await Course.create({ name: `${topics[i % 5]} ${i}`, teacher: teachers[i % 2].uuid, students: courseStudents })); } // 3 classrooms const classrooms = []; for (let i = 1; i < 4; i++) { let classCourses = []; classCourses.push({ uuid: courses[i].uuid, name: courses[i].name }); classCourses.push({ uuid: courses[i * 2].uuid, name: courses[i * 2].name }); classCourses.push({ uuid: courses[i * 3].uuid, name: courses[i * 3].name }); classrooms.push(await Classroom.create({ name: `Classroom ${i}`, courses: classCourses })); } let count = 1; for (let course of courses) { course.classroom.set(classrooms[count++ % 3].uuid); await course.save(); } // 12 Hardware const hardwares = []; for (let i = 1; i < 12; i++) { if (i % 2) { hardwares.push(await ComputerScreen.create({ classroom: classrooms[i % 3].uuid, name: `Computer Screen ${i}` })); } else { hardwares.push(await Hardware.create({ classroom: classrooms[i % 3].uuid, name: `Hardware ${i}` })); } } count = 1; for (let classroom of classrooms) { let classCourses = []; for (let i = 0; i < 3; i++) { classCourses.push({ uuid: courses[count++ % 12].uuid, name: courses[count % 12].name }); } await classroom.patch({ courses: classCourses }); } } /** * Wait for the next tick(s) * @param ticks if you want to wait for more than one tick */ async nextTick(ticks = 1) { while (ticks-- > 0) { await new Promise(resolve => setImmediate(resolve)); } } /** * Get service from Webda * @param service name * @returns */ getService(service) { return this.webda.getService(service); } /** * Dynamic add a service to webda * * @param name of the service to add * @param service to add */ registerService(service, name = service.getName()) { // Have to override protected // @ts-ignore this.webda.services[name] = service; return service; } /** * Dynamic add a model to webda * @param model * @param klass */ registerModel(model, name = model.constructor.name, graph = {}) { this.webda.getApplication().addModel(name, model); this.webda.getApplication().getGraph()[name] = graph; } } class WebdaSimpleTest extends WebdaTest { getTestConfiguration() { return undefined; } } export { WebdaSimpleTest, WebdaTest }; //# sourceMappingURL=test.js.map