Page MenuHomeSealhub

test-on-real-app.ts
No OneTemporary

test-on-real-app.ts

/* eslint-disable @typescript-eslint/no-unsafe-member-access */
/* eslint-disable @typescript-eslint/no-unsafe-call */
import { execAsync } from "./exec_async.js";
import { spawn } from "child_process";
import getPort from "get-port";
import waitPort from "wait-port";
import { promises as fs } from "fs";
import _locreq from "locreq";
import { module_dirname } from "../utils/module-dirname.js";
import EventEmitter from "events";
const locreq = _locreq(module_dirname(import.meta.url));
export const callSealgenBin = () => `node ${locreq.resolve("lib/cli.js")}`;
const sealious_minimal_commit = "b0d5bf5";
export class RealAppTest {
public node_pid: number;
public app_port: number;
public mongo_port: number;
public app_path: string;
public container_name: string;
public emitter = new EventEmitter();
static async init(): Promise<RealAppTest> {
const test_app = new RealAppTest();
test_app.node_pid = 0;
const timestamp = Date.now();
test_app.app_path = `/tmp/sealious-minimal-${timestamp}`;
test_app.container_name = `sealgen-test-mongo-${timestamp}`;
console.log("\tcloning sealious-minimal...");
await execAsync(
`git clone https://hub.sealcode.org/source/sealious-minimal.git ${test_app.app_path} && cd ${test_app.app_path} && git checkout ${sealious_minimal_commit}`
);
console.log("\tinstalling dependencies and sealgen...");
await execAsync(
`cd ${
test_app.app_path
} && npm ci rm -rf node_modules/@sealcode/sealgen && ln -s $OLDPWD node_modules/@sealcode/sealgen && ${callSealgenBin()} make-env`
);
console.log(
"\tstarting the database...(if timeouts here, run 'docker pull mongo:4.4-bionic' to solve)"
);
test_app.app_port = await getPort();
return test_app;
}
async start() {
this.mongo_port = await getPort();
await execAsync(
`docker run -p ${this.mongo_port}:27017 --name ${this.container_name} -d mongo:4.4-bionic`
);
const env = {
...process.env,
SEALIOUS_MONGO_PORT: this.mongo_port.toString(),
SEALIOUS_PORT: this.app_port.toString(),
};
const node_args = [`${this.app_path}/dist/back/index.js`];
console.log(
"EXECUTING APP",
`${Object.entries(env)
.map(([key, value]) => `${key}=${value}`)
.join(" ")} node ${node_args.join(" ")}`
);
const app_start = spawn(`node`, node_args, {
env,
});
app_start.stdout.on("data", (data) =>
console.log("APP OUTPUT:", data.toString())
);
app_start.stderr.on("data", (data) => console.log(data.toString()));
app_start.on("exit", (code) => {
this.emitter.emit("exit", code);
this.emitter.emit("process_error");
});
if (app_start.pid == undefined) throw new Error("node failed to start");
this.node_pid = app_start.pid;
await waitPort({
host: "localhost",
port: this.app_port,
output: "silent",
timeout: 6000,
});
}
async close() {
try {
if (this.node_pid != 0) await execAsync(`kill -9 ${this.node_pid}`);
} catch (error) {
throw new Error(
"node process exited unexpectedly, please inspect the output"
);
}
await Promise.all([
fs.rm(this.app_path, { recursive: true, force: true }),
execAsync(`docker rm -f ${this.container_name}`),
]);
}
async runSealgenCommand(command: string) {
await execAsync(
`cd ${
this.app_path
} && ${callSealgenBin()} ${command} && ${callSealgenBin()} build`
);
}
async throwIfItKillsTheApp<T>(cb: () => Promise<T>): Promise<T> {
let finished = false;
// eslint-disable-next-line @typescript-eslint/no-misused-promises, no-async-promise-executor
return new Promise(async (resolve, reject) => {
const error_handler = () => {
if (!finished) {
reject();
}
finished = true;
};
this.emitter.addListener("process_error", error_handler);
try {
const result = await cb();
if (!finished) {
resolve(result);
finished = true;
}
} catch (e) {
if (!finished) {
reject(e);
finished = true;
}
}
this.emitter.removeListener("process_error", error_handler);
});
}
async httpGet(path: string): Promise<{ status: number; response: string }> {
return this.throwIfItKillsTheApp(async () => {
const ret = await fetch(`http://localhost:${this.app_port}${path}`);
const status = ret.status;
const response = await ret.text();
return { status, response };
});
}
async httpPOST(
path: string,
body: Record<string, unknown>
): Promise<{ status: number; response: string }> {
return this.throwIfItKillsTheApp(async () => {
const ret = await fetch(
`http://localhost:${this.app_port}${path}`,
{
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(body),
}
);
const status = ret.status;
const response = await ret.text();
console.log("request done!");
console.log({ response });
return { status, response };
});
}
}

File Metadata

Mime Type
text/x-java
Expires
Mon, Dec 23, 00:57 (4 h, 34 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
556779
Default Alt Text
test-on-real-app.ts (4 KB)

Event Timeline