@webda/core
Version:
Expose API with Lambda
399 lines • 13.1 kB
JavaScript
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