stack service and trigger service
This commit is contained in:
parent
1ed6449ac6
commit
cba12be4b1
16 changed files with 784 additions and 177 deletions
|
@ -1,14 +1,17 @@
|
||||||
import { override } from "first-di";
|
import { override } from "first-di";
|
||||||
import { DevEnv, IEnv , UnitTestEnv } from "./env.js";
|
import { DevEnv, IEnv, UnitTestEnv } from "./env.js";
|
||||||
import { MetaDataFileManagerModel } from "../model/meta_data_file_manager_model.js";
|
import { MetaDataFileManagerModel } from "../model/meta_data_file_manager_model.js";
|
||||||
|
import { extensions } from "../extensions/extensions.js";
|
||||||
export default function locator(env: IEnv){
|
|
||||||
|
export default function locator(env: IEnv) {
|
||||||
|
extensions();
|
||||||
envRegister(env);
|
envRegister(env);
|
||||||
registerRepository(env);
|
registerRepository(env);
|
||||||
registerController(env);
|
registerController(env);
|
||||||
registerService(env);
|
registerService(env);
|
||||||
override(MetaDataFileManagerModel, MetaDataFileManagerModel)
|
override(MetaDataFileManagerModel, MetaDataFileManagerModel);
|
||||||
}
|
}
|
||||||
|
|
||||||
const envRegister = (env: IEnv) => {
|
const envRegister = (env: IEnv) => {
|
||||||
switch (env.toStringEnv()) {
|
switch (env.toStringEnv()) {
|
||||||
case UnitTestEnv.env():
|
case UnitTestEnv.env():
|
||||||
|
@ -19,6 +22,7 @@ const envRegister = (env: IEnv) => {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const registerRepository = (env: IEnv) => {
|
const registerRepository = (env: IEnv) => {
|
||||||
switch (env.toStringEnv()) {
|
switch (env.toStringEnv()) {
|
||||||
case UnitTestEnv.env():
|
case UnitTestEnv.env():
|
||||||
|
@ -30,6 +34,7 @@ const registerRepository = (env: IEnv) => {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const registerController = (env: IEnv) => {
|
const registerController = (env: IEnv) => {
|
||||||
switch (env.toStringEnv()) {
|
switch (env.toStringEnv()) {
|
||||||
case UnitTestEnv.env():
|
case UnitTestEnv.env():
|
||||||
|
@ -38,6 +43,7 @@ const registerController = (env: IEnv) => {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const registerService = (env: IEnv) => {
|
const registerService = (env: IEnv) => {
|
||||||
switch (env.toStringEnv()) {
|
switch (env.toStringEnv()) {
|
||||||
case UnitTestEnv.env():
|
case UnitTestEnv.env():
|
||||||
|
|
31
server/src/core/extensions/array.ts
Normal file
31
server/src/core/extensions/array.ts
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
export {};
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
interface Array<T> {
|
||||||
|
// @strict: The parameter is determined whether the arrays must be exactly the same in content and order of this relationship or simply follow the same requirements.
|
||||||
|
equals(array: Array<T>, strict: boolean): boolean;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const ArrayEquals = () => {
|
||||||
|
if ([].equals == undefined) {
|
||||||
|
Array.prototype.equals = function (array, strict = true) {
|
||||||
|
if (!array) return false;
|
||||||
|
|
||||||
|
if (arguments.length == 1) strict = true;
|
||||||
|
|
||||||
|
if (this.length != array.length) return false;
|
||||||
|
|
||||||
|
for (let i = 0; i < this.length; i++) {
|
||||||
|
if (this[i] instanceof Array && array[i] instanceof Array) {
|
||||||
|
if (!this[i].equals(array[i], strict)) return false;
|
||||||
|
} else if (strict && this[i] != array[i]) {
|
||||||
|
return false;
|
||||||
|
} else if (!strict) {
|
||||||
|
return this.sort().equals(array.sort(), true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
5
server/src/core/extensions/extensions.ts
Normal file
5
server/src/core/extensions/extensions.ts
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
import { ArrayEquals } from "./array.js";
|
||||||
|
|
||||||
|
export const extensions = () =>{
|
||||||
|
ArrayEquals()
|
||||||
|
}
|
|
@ -9,7 +9,7 @@ export interface Disposable {
|
||||||
|
|
||||||
export class TypedEvent<T> {
|
export class TypedEvent<T> {
|
||||||
private listeners: Listener<T>[] = [];
|
private listeners: Listener<T>[] = [];
|
||||||
public listenersOncer: Listener<T>[] = [];
|
public listenersOnces: Listener<T>[] = [];
|
||||||
|
|
||||||
on = (listener: Listener<T>): Disposable => {
|
on = (listener: Listener<T>): Disposable => {
|
||||||
this.listeners.push(listener);
|
this.listeners.push(listener);
|
||||||
|
@ -19,7 +19,7 @@ export class TypedEvent<T> {
|
||||||
};
|
};
|
||||||
|
|
||||||
once = (listener: Listener<T>): void => {
|
once = (listener: Listener<T>): void => {
|
||||||
this.listenersOncer.push(listener);
|
this.listenersOnces.push(listener);
|
||||||
};
|
};
|
||||||
|
|
||||||
off = (listener: Listener<T>) => {
|
off = (listener: Listener<T>) => {
|
||||||
|
@ -31,15 +31,14 @@ export class TypedEvent<T> {
|
||||||
};
|
};
|
||||||
|
|
||||||
emit = (event: T) => {
|
emit = (event: T) => {
|
||||||
/** Обновить слушателей */
|
|
||||||
this.listeners.forEach((listener) =>
|
this.listeners.forEach((listener) =>
|
||||||
listener(event)
|
listener(event)
|
||||||
);
|
);
|
||||||
|
|
||||||
/** Очистить очередь единожды */
|
if (this.listenersOnces.length > 0) {
|
||||||
if (this.listenersOncer.length > 0) {
|
const toCall = this.listenersOnces;
|
||||||
const toCall = this.listenersOncer;
|
this.listenersOnces = [];
|
||||||
this.listenersOncer = [];
|
|
||||||
toCall.forEach((listener) => listener(event));
|
toCall.forEach((listener) => listener(event));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
export class ExecError extends Error {
|
export class ExecError extends Error {
|
||||||
static isExecError(e: any) {
|
static isExecError(e: any) {
|
||||||
if ("type" in e && "script" in e && "unixTime" in e) {
|
if ("type" in e && "script" in e && "unixTime" in e && 'error' in e) {
|
||||||
return new ExecError(e.type, e.event, e.data);
|
return new ExecError(e.type, e.event, e.data);
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
|
@ -8,14 +8,15 @@ export class ExecError extends Error {
|
||||||
script: string;
|
script: string;
|
||||||
unixTime: number;
|
unixTime: number;
|
||||||
type = EXEC_TYPE.EXEC;
|
type = EXEC_TYPE.EXEC;
|
||||||
|
error:any;
|
||||||
constructor(
|
constructor(
|
||||||
script: string,
|
script: string,
|
||||||
...args: any
|
...args: any
|
||||||
) {
|
) {
|
||||||
super(...args);
|
super(...args);
|
||||||
this.script = script;
|
this.script = script;
|
||||||
this.unixTime = Date.now();
|
this.unixTime = Date.now();
|
||||||
|
this.error = args[0]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -50,10 +51,12 @@ export class SpawnError extends Error {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export enum EXEC_TYPE {
|
export enum EXEC_TYPE {
|
||||||
SPAWN = "SPAWN",
|
SPAWN = "SPAWN",
|
||||||
EXEC = "EXEC",
|
EXEC = "EXEC",
|
||||||
}
|
}
|
||||||
|
|
||||||
export enum EXEC_EVENT {
|
export enum EXEC_EVENT {
|
||||||
PROGRESS = "PROGRESS",
|
PROGRESS = "PROGRESS",
|
||||||
ERROR = "ERROR",
|
ERROR = "ERROR",
|
||||||
|
|
|
@ -1,20 +1,18 @@
|
||||||
export class CalculationProcess {
|
import { EXEC_TYPE } from "./exec_error_model.js";
|
||||||
process: ProcessMetaData[];
|
|
||||||
constructor(process: ProcessMetaData[]) {
|
|
||||||
this.process = process;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
export interface ProcessMetaData {
|
export interface ProcessMetaData {
|
||||||
process: Process;
|
process: Process;
|
||||||
trigger: Trigger;
|
trigger: Trigger;
|
||||||
env: Env | null;
|
env: Env | null;
|
||||||
|
stackGenerateType:StackGenerateType;
|
||||||
}
|
}
|
||||||
|
|
||||||
export enum ProcessType {
|
export enum StackGenerateType{
|
||||||
EXEC = "EXEC",
|
MAP = 'MAP',
|
||||||
SPAWN = "SPAWN",
|
SINGLETON = 'SINGLETON'
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Env {
|
export interface Env {
|
||||||
ssh_key: string;
|
ssh_key: string;
|
||||||
isUserInput: boolean;
|
isUserInput: boolean;
|
||||||
|
@ -22,12 +20,13 @@ export interface Env {
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Process {
|
export interface Process {
|
||||||
type: ProcessType;
|
type: EXEC_TYPE;
|
||||||
command: string;
|
command: string;
|
||||||
isGenerating: boolean;
|
isGenerating: boolean;
|
||||||
isLocaleCode: boolean;
|
isLocaleCode: boolean;
|
||||||
issueType: IssueType;
|
issueType: IssueType;
|
||||||
timeout: number;
|
timeout?: number;
|
||||||
|
commit?:string | undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
export enum IssueType {
|
export enum IssueType {
|
||||||
|
@ -41,5 +40,5 @@ export enum TriggerType {
|
||||||
}
|
}
|
||||||
export interface Trigger {
|
export interface Trigger {
|
||||||
type: TriggerType;
|
type: TriggerType;
|
||||||
value: string;
|
value: string[];
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,7 +24,12 @@ export class ExecutorProgramService
|
||||||
this.execPath = execPath;
|
this.execPath = execPath;
|
||||||
this.maxTime = maxTime;
|
this.maxTime = maxTime;
|
||||||
}
|
}
|
||||||
private async workerExecuted(command: string, workerType: WorkerType,args:Array<string> | undefined = undefined) {
|
|
||||||
|
private async workerExecuted(
|
||||||
|
command: string,
|
||||||
|
workerType: WorkerType,
|
||||||
|
args: Array<string> | undefined = undefined
|
||||||
|
) {
|
||||||
cluster.setupPrimary({
|
cluster.setupPrimary({
|
||||||
exec: "./src/core/helper/worker_computed.js",
|
exec: "./src/core/helper/worker_computed.js",
|
||||||
});
|
});
|
||||||
|
@ -39,22 +44,25 @@ export class ExecutorProgramService
|
||||||
command: command,
|
command: command,
|
||||||
execPath: this.execPath,
|
execPath: this.execPath,
|
||||||
type: workerType,
|
type: workerType,
|
||||||
cliArgs:args
|
cliArgs: args,
|
||||||
};
|
};
|
||||||
worker.send(workerDataExec);
|
worker.send(workerDataExec);
|
||||||
worker.on("message", (e) => {
|
worker.on("message", (e) => {
|
||||||
const spawnError = SpawnError.isError(e);
|
const spawnError = SpawnError.isError(e);
|
||||||
if (spawnError instanceof SpawnError) {
|
if (spawnError instanceof SpawnError) {
|
||||||
this.emit(Result.error(spawnError));
|
this.emit(Result.error(spawnError));
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
const executorResult = ExecutorResult.isExecutorResult(e);
|
const executorResult = ExecutorResult.isExecutorResult(e);
|
||||||
if (executorResult instanceof ExecutorResult) {
|
if (executorResult instanceof ExecutorResult) {
|
||||||
this.emit(Result.ok(executorResult));
|
this.emit(Result.ok(executorResult));
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const execError = ExecError.isExecError(e);
|
const execError = ExecError.isExecError(e);
|
||||||
if (execError instanceof ExecError) {
|
if (execError instanceof ExecError) {
|
||||||
this.emit(Result.error(execError));
|
this.emit(Result.error(execError));
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
if (this.maxTime != null) {
|
if (this.maxTime != null) {
|
||||||
|
@ -70,13 +78,18 @@ export class ExecutorProgramService
|
||||||
}, this.maxTime!);
|
}, this.maxTime!);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
public async call(type: EXEC_TYPE, command: string, args:Array<string> | undefined = undefined): Promise<void> {
|
|
||||||
|
public async call(
|
||||||
|
type: EXEC_TYPE,
|
||||||
|
command: string,
|
||||||
|
args: Array<string> | undefined = undefined
|
||||||
|
): Promise<void> {
|
||||||
if (type == EXEC_TYPE.EXEC) {
|
if (type == EXEC_TYPE.EXEC) {
|
||||||
this.workerExecuted(command, WorkerType.EXEC);
|
this.workerExecuted(command, WorkerType.EXEC);
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.workerExecuted(command, WorkerType.SPAWN,args);
|
this.workerExecuted(command, WorkerType.SPAWN, args);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,21 +4,48 @@ import { promisify } from "node:util";
|
||||||
import { createHash } from "node:crypto";
|
import { createHash } from "node:crypto";
|
||||||
import "reflect-metadata";
|
import "reflect-metadata";
|
||||||
import { BinaryLike } from "crypto";
|
import { BinaryLike } from "crypto";
|
||||||
import { EventsFileChanger, MetaDataFileManagerModel } from "../model/meta_data_file_manager_model.js";
|
import {
|
||||||
|
EventsFileChanger,
|
||||||
|
MetaDataFileManagerModel,
|
||||||
|
} from "../model/meta_data_file_manager_model.js";
|
||||||
import { Result } from "../helper/result.js";
|
import { Result } from "../helper/result.js";
|
||||||
import { TypedEvent } from "../helper/typed_event.js";
|
import { TypedEvent } from "../helper/typed_event.js";
|
||||||
|
|
||||||
const readFileAsync = promisify(fs.readFile);
|
const readFileAsync = promisify(fs.readFile);
|
||||||
const readdir = promisify(fs.readdir);
|
const readdir = promisify(fs.readdir);
|
||||||
const stat = promisify(fs.stat);
|
const stat = promisify(fs.stat);
|
||||||
|
const lsStat = promisify(fs.lstat);
|
||||||
|
|
||||||
function md5(content: Buffer | BinaryLike) {
|
function joinBuffers(buffers, delimiter = " ") {
|
||||||
return createHash("md5").update(content).digest("hex");
|
const d = Buffer.from(delimiter);
|
||||||
|
return buffers.reduce((prev, b) => Buffer.concat([prev, d, b]));
|
||||||
}
|
}
|
||||||
|
|
||||||
interface IHashesCache {
|
async function readFileAtBuffer(path: string): Promise<Buffer> {
|
||||||
|
if ((await lsStat(path)).isDirectory()) {
|
||||||
|
return (
|
||||||
|
await readdir(path, {
|
||||||
|
encoding: "buffer",
|
||||||
|
})
|
||||||
|
).reduce(
|
||||||
|
(accumulator, currentValue) => joinBuffers([accumulator, currentValue]),
|
||||||
|
Buffer.from("")
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return await readFileAsync(path);
|
||||||
|
}
|
||||||
|
function md5(content: Buffer | BinaryLike): Promise<string> {
|
||||||
|
return new Promise((resolve, _reject) => {
|
||||||
|
return resolve(createHash("md5").update(content).digest("hex"));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IHashesCache {
|
||||||
[key: string]: MetaDataFileManagerModel;
|
[key: string]: MetaDataFileManagerModel;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
export abstract class IFilesChangeNotifierService {
|
export abstract class IFilesChangeNotifierService {
|
||||||
abstract directory: string;
|
abstract directory: string;
|
||||||
}
|
}
|
||||||
|
@ -42,7 +69,7 @@ export class FilesChangeNotifierService
|
||||||
}
|
}
|
||||||
async setHash(file: string) {
|
async setHash(file: string) {
|
||||||
const data = await readFileAsync(file);
|
const data = await readFileAsync(file);
|
||||||
const md5Current = md5(data);
|
const md5Current = await md5(data);
|
||||||
|
|
||||||
this.hashes[file] = new MetaDataFileManagerModel(
|
this.hashes[file] = new MetaDataFileManagerModel(
|
||||||
file,
|
file,
|
||||||
|
@ -77,16 +104,16 @@ export class FilesChangeNotifierService
|
||||||
fsWait = false;
|
fsWait = false;
|
||||||
}, 100);
|
}, 100);
|
||||||
try {
|
try {
|
||||||
const file = await readFileAsync(filePath);
|
const file = await readFileAtBuffer(filePath);
|
||||||
const md5Current = md5(file);
|
const md5Current = await md5(file);
|
||||||
|
|
||||||
if (md5Current === md5Previous) {
|
if (md5Current === md5Previous) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const status = this.hashes[filePath] === undefined
|
const status =
|
||||||
|
this.hashes[filePath] === undefined
|
||||||
? EventsFileChanger.create
|
? EventsFileChanger.create
|
||||||
: EventsFileChanger.update;
|
: EventsFileChanger.update;
|
||||||
|
|
||||||
const model = new MetaDataFileManagerModel(
|
const model = new MetaDataFileManagerModel(
|
||||||
filePath,
|
filePath,
|
||||||
md5Current,
|
md5Current,
|
||||||
|
@ -115,8 +142,7 @@ export class FilesChangeNotifierService
|
||||||
cancel() {
|
cancel() {
|
||||||
if (this.watcher != undefined) {
|
if (this.watcher != undefined) {
|
||||||
this.watcher.close();
|
this.watcher.close();
|
||||||
this.listenersOncer = []
|
this.listenersOnces = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
137
server/src/core/services/stack_service.ts
Normal file
137
server/src/core/services/stack_service.ts
Normal file
|
@ -0,0 +1,137 @@
|
||||||
|
import {
|
||||||
|
FilesChangeNotifierService,
|
||||||
|
IHashesCache,
|
||||||
|
} from "./files_change_notifier_service.js";
|
||||||
|
import { ProcessMetaData, Trigger } from "../model/process_model.js";
|
||||||
|
import { ExecutorProgramService } from "./executor_program_service.js";
|
||||||
|
import {
|
||||||
|
EXEC_EVENT,
|
||||||
|
ExecError,
|
||||||
|
SpawnError,
|
||||||
|
} from "../model/exec_error_model.js";
|
||||||
|
import { TypedEvent } from "../helper/typed_event.js";
|
||||||
|
import { Result } from "../helper/result.js";
|
||||||
|
import { ExecutorResult } from "../model/executor_result.js";
|
||||||
|
import { delay } from "../helper/delay.js";
|
||||||
|
import { TriggerErrorReport, TriggerService } from "./trigger_service.js";
|
||||||
|
|
||||||
|
export interface Iteration {
|
||||||
|
hashes: IHashesCache | null;
|
||||||
|
process: ProcessMetaData;
|
||||||
|
result?: ExecError | SpawnError | ExecutorResult;
|
||||||
|
}
|
||||||
|
|
||||||
|
export abstract class IStackService {
|
||||||
|
abstract callStack: Iteration[];
|
||||||
|
abstract path: string;
|
||||||
|
abstract init(processed: ProcessMetaData[], path: string): void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class StackService extends TypedEvent<string> implements IStackService {
|
||||||
|
callStack: Iteration[];
|
||||||
|
path: string;
|
||||||
|
constructor(processed: ProcessMetaData[], path: string) {
|
||||||
|
super();
|
||||||
|
this.path = path;
|
||||||
|
this.callStack = [];
|
||||||
|
this.init(processed);
|
||||||
|
}
|
||||||
|
|
||||||
|
public init(processed: ProcessMetaData[]) {
|
||||||
|
for (let el of processed) {
|
||||||
|
el = this.commandHandler(el);
|
||||||
|
this.callStack.push({
|
||||||
|
hashes: null,
|
||||||
|
process: el,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
private commandHandler(processMetaData: ProcessMetaData) {
|
||||||
|
processMetaData.process.command = processMetaData.process.command.replace(
|
||||||
|
"$PATH",
|
||||||
|
this.path
|
||||||
|
);
|
||||||
|
return processMetaData;
|
||||||
|
}
|
||||||
|
public async call() {
|
||||||
|
let inc = 0;
|
||||||
|
|
||||||
|
for await (const el of this.callStack!) {
|
||||||
|
await this.execStack(inc, el);
|
||||||
|
inc += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
async execStack(
|
||||||
|
stackNumber: number,
|
||||||
|
stackLayer: Iteration
|
||||||
|
): Promise<void | boolean> {
|
||||||
|
const executorService = new ExecutorProgramService(this.path);
|
||||||
|
|
||||||
|
executorService.call(
|
||||||
|
stackLayer.process.process.type,
|
||||||
|
stackLayer.process.process.command
|
||||||
|
);
|
||||||
|
|
||||||
|
const filesChangeNotifierService = new FilesChangeNotifierService(
|
||||||
|
this.path
|
||||||
|
);
|
||||||
|
|
||||||
|
filesChangeNotifierService.call();
|
||||||
|
const result = await this.waitEvent<
|
||||||
|
Result<ExecError | SpawnError, ExecutorResult>
|
||||||
|
>(executorService);
|
||||||
|
await delay(100);
|
||||||
|
if (result.isSuccess()) {
|
||||||
|
this.callStack[stackNumber].result = result.value;
|
||||||
|
this.callStack[stackNumber].hashes = filesChangeNotifierService.hashes;
|
||||||
|
|
||||||
|
const triggerResult = await this.triggerExec(
|
||||||
|
stackLayer.process.trigger,
|
||||||
|
stackNumber
|
||||||
|
);
|
||||||
|
triggerResult.fold(
|
||||||
|
(s) => {
|
||||||
|
s
|
||||||
|
},
|
||||||
|
(e) => {
|
||||||
|
e;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
filesChangeNotifierService.cancel();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
public waitEvent<T>(stream: TypedEvent<T>): Promise<T> {
|
||||||
|
const promise = new Promise<T>((resolve, reject) => {
|
||||||
|
const addListener = () => {
|
||||||
|
stream.on((e) => {
|
||||||
|
const event = e as Result<ExecError | SpawnError, ExecutorResult>;
|
||||||
|
event.fold(
|
||||||
|
(s) => {
|
||||||
|
if (s.event === EXEC_EVENT.END) {
|
||||||
|
resolve(e);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
(e) => {
|
||||||
|
reject(e);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
addListener();
|
||||||
|
});
|
||||||
|
return promise;
|
||||||
|
}
|
||||||
|
private async triggerExec(
|
||||||
|
trigger: Trigger,
|
||||||
|
stackNumber: number
|
||||||
|
): Promise<Result<boolean, boolean>> {
|
||||||
|
const hashes = this.callStack[stackNumber].hashes;
|
||||||
|
|
||||||
|
if (hashes != null) {
|
||||||
|
return await new TriggerService(trigger, hashes, this.path).call();
|
||||||
|
}
|
||||||
|
throw new Error("Hashes is null");
|
||||||
|
}
|
||||||
|
}
|
140
server/src/core/services/trigger_service.ts
Normal file
140
server/src/core/services/trigger_service.ts
Normal file
|
@ -0,0 +1,140 @@
|
||||||
|
import { Trigger, TriggerType } from "../model/process_model.js";
|
||||||
|
import * as vm from "node:vm";
|
||||||
|
import { IHashesCache } from "./files_change_notifier_service.js";
|
||||||
|
import { EventsFileChanger } from "../model/meta_data_file_manager_model.js";
|
||||||
|
import { Result } from "../helper/result.js";
|
||||||
|
import { TypedEvent } from "../helper/typed_event.js";
|
||||||
|
|
||||||
|
export class TriggerCallResult {
|
||||||
|
results: Array<TriggerSuccessResult | TriggerErrorReport>;
|
||||||
|
|
||||||
|
constructor(results: Array<TriggerSuccessResult | TriggerErrorReport>) {
|
||||||
|
this.results = results;
|
||||||
|
}
|
||||||
|
|
||||||
|
get isErrorComputed(): Result<boolean, boolean> {
|
||||||
|
for (const el of this.results) {
|
||||||
|
if (el instanceof TriggerErrorReport) {
|
||||||
|
return Result.error(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Result.ok(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
export class TriggerSuccessResult {
|
||||||
|
status: boolean;
|
||||||
|
processOutput: any;
|
||||||
|
trigger: string;
|
||||||
|
constructor(status: boolean, trigger: string, processOutput?: any) {
|
||||||
|
this.status = status;
|
||||||
|
this.processOutput = processOutput;
|
||||||
|
this.trigger = trigger;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
export class TriggerErrorReport extends Error {
|
||||||
|
hashes: IHashesCache;
|
||||||
|
trigger: string | Trigger;
|
||||||
|
processOutput: any;
|
||||||
|
constructor(
|
||||||
|
hashes: IHashesCache,
|
||||||
|
trigger: string | Trigger,
|
||||||
|
processOutput?: any
|
||||||
|
) {
|
||||||
|
super();
|
||||||
|
this.hashes = hashes;
|
||||||
|
this.trigger = trigger;
|
||||||
|
this.processOutput = processOutput;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
export class TriggerService extends TypedEvent<TriggerCallResult> {
|
||||||
|
context = {};
|
||||||
|
|
||||||
|
constructor(trigger: Trigger, hashes: IHashesCache, path: string) {
|
||||||
|
super();
|
||||||
|
this.trigger = trigger;
|
||||||
|
this.hashes = hashes;
|
||||||
|
this.path = path;
|
||||||
|
this.triggerResult = null;
|
||||||
|
this.init();
|
||||||
|
}
|
||||||
|
triggerResult: null | TriggerCallResult;
|
||||||
|
path: string;
|
||||||
|
hashes: IHashesCache;
|
||||||
|
trigger: Trigger;
|
||||||
|
private init(): void {
|
||||||
|
this.context["hashes"] = this.hashes;
|
||||||
|
}
|
||||||
|
private getAllHashesDeleteWithouts(): string[] {
|
||||||
|
return Object.entries(this.hashes).map(([k, v]) => {
|
||||||
|
if (v.event !== EventsFileChanger.delete) {
|
||||||
|
return k.replace(new RegExp(`${this.path}`), "");
|
||||||
|
}
|
||||||
|
return "";
|
||||||
|
});
|
||||||
|
}
|
||||||
|
public async call(): Promise<Result<boolean, boolean>> {
|
||||||
|
if (this.trigger.type === TriggerType.PROCESS) {
|
||||||
|
const triggerResult = await this.triggerTypeProcess();
|
||||||
|
this.emit(triggerResult);
|
||||||
|
return triggerResult.isErrorComputed;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.trigger.type === TriggerType.FILE) {
|
||||||
|
const triggerResult = await this.triggerTypeFile();
|
||||||
|
this.emit(triggerResult);
|
||||||
|
return triggerResult.isErrorComputed;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Result.error(false);
|
||||||
|
}
|
||||||
|
private triggerTypeProcess(): TriggerCallResult {
|
||||||
|
const triggerResult: TriggerSuccessResult[] = [];
|
||||||
|
|
||||||
|
for (const el of this.trigger.value) {
|
||||||
|
const processOutput = this.processCall(el);
|
||||||
|
|
||||||
|
triggerResult.push({
|
||||||
|
status: processOutput ? processOutput : false,
|
||||||
|
processOutput: processOutput,
|
||||||
|
trigger: el,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return this.reportTriggerTypeProcess(triggerResult);
|
||||||
|
}
|
||||||
|
private triggerTypeFile(): TriggerCallResult {
|
||||||
|
const files = this.getAllHashesDeleteWithouts();
|
||||||
|
|
||||||
|
return new TriggerCallResult(
|
||||||
|
this.trigger.value.map((el) => {
|
||||||
|
let result = false;
|
||||||
|
for (const file of files) {
|
||||||
|
if (result != true) {
|
||||||
|
result = new RegExp(`${el}`).test(file);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (result === false) {
|
||||||
|
return new TriggerErrorReport(this.hashes, el);
|
||||||
|
}
|
||||||
|
return new TriggerSuccessResult(result, el);
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
private reportTriggerTypeProcess(
|
||||||
|
triggerResult: Array<TriggerSuccessResult>
|
||||||
|
): TriggerCallResult {
|
||||||
|
return new TriggerCallResult(
|
||||||
|
triggerResult.map((el) => {
|
||||||
|
if (el.status) {
|
||||||
|
return el;
|
||||||
|
} else {
|
||||||
|
return new TriggerErrorReport(this.hashes, el.trigger);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
private processCall(code: string): undefined | boolean | any {
|
||||||
|
const ctx = vm.createContext(this.context);
|
||||||
|
return vm.runInContext(code, ctx);
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,96 +0,0 @@
|
||||||
import { delay } from "../src/core/helper/delay.js";
|
|
||||||
import { EXEC_TYPE } from "../src/core/model/exec_error_model.js";
|
|
||||||
import { ExecutorResult } from "../src/core/model/executor_result.js";
|
|
||||||
import { ExecutorProgramService } from "../src/core/services/executor_program_service.js";
|
|
||||||
import { TestCore } from "./core/test_core.js";
|
|
||||||
import { resultTest as resultTest, __dirname } from "./test.js";
|
|
||||||
import { Worker } from "node:cluster";
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
export class ExecutorProgramServiceTest extends ExecutorProgramService {
|
|
||||||
timeCancel = 1000;
|
|
||||||
public test = async () => {
|
|
||||||
await this.resultsTests();
|
|
||||||
await this.longTimeCancelTest()
|
|
||||||
await this.logWriteAndEventEndTestTypeExec()
|
|
||||||
await this.logWriteAndEventEndTypeSpawn()
|
|
||||||
};
|
|
||||||
private async logWriteAndEventEndTypeSpawn(){
|
|
||||||
const executorProgramService = await new ExecutorProgramService(__dirname + '/')
|
|
||||||
executorProgramService.call(EXEC_TYPE.SPAWN, 'node',['./mocks/log_code.js'])
|
|
||||||
const test = TestCore.instance
|
|
||||||
let testIsOk = false
|
|
||||||
let logEvent = false
|
|
||||||
|
|
||||||
executorProgramService.on((e) =>{
|
|
||||||
if(e.isSuccess()) {
|
|
||||||
const executorResult = e.value as ExecutorResult
|
|
||||||
if(logEvent == false){
|
|
||||||
logEvent = executorResult.data != null && executorResult.data != undefined
|
|
||||||
}
|
|
||||||
testIsOk = executorResult.event == 'END' && logEvent
|
|
||||||
}
|
|
||||||
})
|
|
||||||
await delay(8000)
|
|
||||||
test.assert(testIsOk,'ExecutorProgramService EXEC_TYPE.SPAWN end event and log write')
|
|
||||||
}
|
|
||||||
private async logWriteAndEventEndTestTypeExec(){
|
|
||||||
const executorProgramService = await new ExecutorProgramService(__dirname)
|
|
||||||
executorProgramService.call(EXEC_TYPE.EXEC, 'node ./test/mocks/log_code.js' )
|
|
||||||
const test = TestCore.instance
|
|
||||||
executorProgramService.on((e) =>{
|
|
||||||
if(e.isSuccess()) {
|
|
||||||
const executorResult = e.value as ExecutorResult
|
|
||||||
test.assert(executorResult.data != undefined && executorResult.event == 'END','ExecutorProgramService EXEC_TYPE.EXEC end event and log write')
|
|
||||||
}
|
|
||||||
})
|
|
||||||
await delay(7000)
|
|
||||||
}
|
|
||||||
private async longTimeCancelTest(){
|
|
||||||
const executorProgramService = await new ExecutorProgramService('',1000)
|
|
||||||
executorProgramService.call(EXEC_TYPE.EXEC, 'node ./test/mocks/long_code.js' )
|
|
||||||
await delay(1500)
|
|
||||||
const worker = executorProgramService.worker as Worker
|
|
||||||
const test = TestCore.instance
|
|
||||||
test.assert(worker.isDead(),'ExecutorProgramService long time cancel')
|
|
||||||
|
|
||||||
}
|
|
||||||
private resultsTests = async () => {
|
|
||||||
await resultTest(
|
|
||||||
new ExecutorProgramService(__dirname),
|
|
||||||
[EXEC_TYPE.EXEC, "node ./mocks/error.js"],
|
|
||||||
"ExecutorProgramService EXEC_TYPE.EXEC on Result.error",
|
|
||||||
false,
|
|
||||||
2000
|
|
||||||
);
|
|
||||||
await delay(400)
|
|
||||||
await resultTest(
|
|
||||||
new ExecutorProgramService(__dirname),
|
|
||||||
[EXEC_TYPE.EXEC, "ls"],
|
|
||||||
"ExecutorProgramService EXEC_TYPE.EXEC on Result.ok",
|
|
||||||
true
|
|
||||||
);
|
|
||||||
|
|
||||||
await resultTest(
|
|
||||||
new ExecutorProgramService(__dirname),
|
|
||||||
[EXEC_TYPE.SPAWN, "ls"],
|
|
||||||
"ExecutorProgramService EXEC_TYPE.SPAWN on Result.ok",
|
|
||||||
true
|
|
||||||
);
|
|
||||||
await resultTest(
|
|
||||||
new ExecutorProgramService(__dirname),
|
|
||||||
[EXEC_TYPE.SPAWN, "python3 ./mocks/s.js"],
|
|
||||||
"ExecutorProgramService EXEC_TYPE.SPAWN on Result.error",
|
|
||||||
false,
|
|
||||||
2000
|
|
||||||
);
|
|
||||||
await resultTest(
|
|
||||||
new ExecutorProgramService(__dirname),
|
|
||||||
[EXEC_TYPE.SPAWN, "ls"],
|
|
||||||
"ExecutorProgramService EXEC_TYPE.SPAWN on Result.ok",
|
|
||||||
true,
|
|
||||||
2000
|
|
||||||
);
|
|
||||||
};
|
|
||||||
}
|
|
110
server/test/features/executor_program_service_test.ts
Normal file
110
server/test/features/executor_program_service_test.ts
Normal file
|
@ -0,0 +1,110 @@
|
||||||
|
import { delay } from "../../src/core/helper/delay.js";
|
||||||
|
import { EXEC_TYPE } from "../../src/core/model/exec_error_model.js";
|
||||||
|
import { ExecutorResult } from "../../src/core/model/executor_result.js";
|
||||||
|
import { ExecutorProgramService } from "../../src/core/services/executor_program_service.js";
|
||||||
|
import { TestCore } from "../core/test_core.js";
|
||||||
|
import { resultTest as resultTest, dirname__ } from "../test.js";
|
||||||
|
import { Worker } from "node:cluster";
|
||||||
|
|
||||||
|
export class ExecutorProgramServiceTest extends ExecutorProgramService {
|
||||||
|
timeCancel = 1000;
|
||||||
|
public test = async () => {
|
||||||
|
await this.resultsTests();
|
||||||
|
await this.longTimeCancelTest();
|
||||||
|
await this.logWriteAndEventEndTestTypeExec();
|
||||||
|
await this.logWriteAndEventEndTypeSpawn();
|
||||||
|
};
|
||||||
|
private async logWriteAndEventEndTypeSpawn() {
|
||||||
|
const executorProgramService = await new ExecutorProgramService(
|
||||||
|
dirname__ + "/"
|
||||||
|
);
|
||||||
|
executorProgramService.call(EXEC_TYPE.SPAWN, "node", [
|
||||||
|
"./mocks/log_code.js",
|
||||||
|
]);
|
||||||
|
const test = TestCore.instance;
|
||||||
|
let testIsOk = false;
|
||||||
|
let logEvent = false;
|
||||||
|
|
||||||
|
executorProgramService.on((e) => {
|
||||||
|
if (e.isSuccess()) {
|
||||||
|
const executorResult = e.value as ExecutorResult;
|
||||||
|
if (logEvent == false) {
|
||||||
|
logEvent =
|
||||||
|
executorResult.data != null && executorResult.data != undefined;
|
||||||
|
}
|
||||||
|
testIsOk = executorResult.event == "END" && logEvent;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
await delay(8000);
|
||||||
|
test.assert(
|
||||||
|
testIsOk,
|
||||||
|
"ExecutorProgramService EXEC_TYPE.SPAWN end event and log write"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
private async logWriteAndEventEndTestTypeExec() {
|
||||||
|
const executorProgramService = await new ExecutorProgramService(dirname__);
|
||||||
|
executorProgramService.call(
|
||||||
|
EXEC_TYPE.EXEC,
|
||||||
|
"node ./test/mocks/log_code.js"
|
||||||
|
);
|
||||||
|
const test = TestCore.instance;
|
||||||
|
executorProgramService.on((e) => {
|
||||||
|
if (e.isSuccess()) {
|
||||||
|
const executorResult = e.value as ExecutorResult;
|
||||||
|
test.assert(
|
||||||
|
executorResult.data != undefined && executorResult.event == "END",
|
||||||
|
"ExecutorProgramService EXEC_TYPE.EXEC end event and log write"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
await delay(7000);
|
||||||
|
}
|
||||||
|
private async longTimeCancelTest() {
|
||||||
|
const executorProgramService = await new ExecutorProgramService("", 1000);
|
||||||
|
executorProgramService.call(
|
||||||
|
EXEC_TYPE.EXEC,
|
||||||
|
"node ./test/mocks/long_code.js"
|
||||||
|
);
|
||||||
|
await delay(1500);
|
||||||
|
const worker = executorProgramService.worker as Worker;
|
||||||
|
const test = TestCore.instance;
|
||||||
|
test.assert(worker.isDead(), "ExecutorProgramService long time cancel");
|
||||||
|
}
|
||||||
|
private resultsTests = async () => {
|
||||||
|
await resultTest(
|
||||||
|
new ExecutorProgramService(dirname__),
|
||||||
|
[EXEC_TYPE.EXEC, "node ./mocks/error.js"],
|
||||||
|
"ExecutorProgramService EXEC_TYPE.EXEC on Result.error",
|
||||||
|
false,
|
||||||
|
4000
|
||||||
|
);
|
||||||
|
await delay(400);
|
||||||
|
await resultTest(
|
||||||
|
new ExecutorProgramService(dirname__),
|
||||||
|
[EXEC_TYPE.EXEC, "ls"],
|
||||||
|
"ExecutorProgramService EXEC_TYPE.EXEC on Result.ok",
|
||||||
|
true
|
||||||
|
);
|
||||||
|
|
||||||
|
await resultTest(
|
||||||
|
new ExecutorProgramService(dirname__),
|
||||||
|
[EXEC_TYPE.SPAWN, "ls"],
|
||||||
|
"ExecutorProgramService EXEC_TYPE.SPAWN on Result.ok",
|
||||||
|
true
|
||||||
|
);
|
||||||
|
await resultTest(
|
||||||
|
new ExecutorProgramService(dirname__),
|
||||||
|
[EXEC_TYPE.SPAWN, "python3 ./mocks/s.js"],
|
||||||
|
"ExecutorProgramService EXEC_TYPE.SPAWN on Result.error",
|
||||||
|
false,
|
||||||
|
2000
|
||||||
|
);
|
||||||
|
await resultTest(
|
||||||
|
new ExecutorProgramService(dirname__),
|
||||||
|
[EXEC_TYPE.SPAWN, "ls"],
|
||||||
|
"ExecutorProgramService EXEC_TYPE.SPAWN on Result.ok",
|
||||||
|
true,
|
||||||
|
2000
|
||||||
|
);
|
||||||
|
};
|
||||||
|
}
|
|
@ -1,11 +1,11 @@
|
||||||
import { delay } from "../src/core/helper/delay.js";
|
|
||||||
import { EventsFileChanger } from "../src/core/model/meta_data_file_manager_model.js";
|
|
||||||
import { FilesChangeNotifierService } from "../src/core/services/files_change_notifier_service.js";
|
|
||||||
import { assert, __dirname } from "./test.js";
|
|
||||||
import * as fs from "fs";
|
import * as fs from "fs";
|
||||||
|
import { FilesChangeNotifierService } from "../../src/core/services/files_change_notifier_service.js";
|
||||||
|
import { EventsFileChanger } from "../../src/core/model/meta_data_file_manager_model.js";
|
||||||
|
import { assert, dirname__ } from "../test.js";
|
||||||
|
import { delay } from "../../src/core/helper/delay.js";
|
||||||
|
|
||||||
export class FilesChangerTest extends FilesChangeNotifierService {
|
export class FilesChangerTest extends FilesChangeNotifierService {
|
||||||
directory = __dirname + "/context/";
|
directory = dirname__ + "/context/";
|
||||||
data = () => {
|
data = () => {
|
||||||
return "This is a file containing a collection";
|
return "This is a file containing a collection";
|
||||||
};
|
};
|
||||||
|
@ -68,14 +68,14 @@ export class FilesChangerTest extends FilesChangeNotifierService {
|
||||||
this.directory = "";
|
this.directory = "";
|
||||||
const result = this.call();
|
const result = this.call();
|
||||||
assert(result.isFailure(), "Not exists directory");
|
assert(result.isFailure(), "Not exists directory");
|
||||||
this.directory = __dirname + "/context/";
|
this.directory = dirname__ + "/context/";
|
||||||
}
|
}
|
||||||
public async test() {
|
public async test() {
|
||||||
await this.createFile();
|
await this.createFile();
|
||||||
await this.updateFile()
|
await this.updateFile();
|
||||||
await this.initFile()
|
await this.initFile();
|
||||||
await this.deleteFile()
|
await this.deleteFile();
|
||||||
await this.notExistsDirectory()
|
await this.notExistsDirectory();
|
||||||
await this.testClear();
|
await this.testClear();
|
||||||
}
|
}
|
||||||
public testClear() {
|
public testClear() {
|
114
server/test/features/stack_service_test.ts
Normal file
114
server/test/features/stack_service_test.ts
Normal file
|
@ -0,0 +1,114 @@
|
||||||
|
import { rmSync } from "fs";
|
||||||
|
import * as fs from "fs";
|
||||||
|
|
||||||
|
import {
|
||||||
|
IssueType,
|
||||||
|
StackGenerateType,
|
||||||
|
TriggerType,
|
||||||
|
} from "../../src/core/model/process_model.js";
|
||||||
|
import { EXEC_TYPE } from "../../src/core/model/exec_error_model.js";
|
||||||
|
import { StackService } from "../../src/core/services/stack_service.js";
|
||||||
|
import { delay } from "../../src/core/helper/delay.js";
|
||||||
|
import { assert, dirname__ } from "../test.js";
|
||||||
|
|
||||||
|
abstract class IStackServiceTest {
|
||||||
|
abstract test(): Promise<boolean>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function readDirRecursive(path: string, filesToDir: string[] = []) {
|
||||||
|
const files = fs.readdirSync(path);
|
||||||
|
files.forEach((file) => {
|
||||||
|
let filePath = "";
|
||||||
|
if (path[path.length - 1] !== "/") {
|
||||||
|
filePath = `${path}/${file}`;
|
||||||
|
} else {
|
||||||
|
filePath = `${path}${file}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
const stats = fs.statSync(filePath);
|
||||||
|
if (stats.isDirectory()) {
|
||||||
|
readDirRecursive(filePath, filesToDir);
|
||||||
|
} else {
|
||||||
|
filesToDir.push(file);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return filesToDir;
|
||||||
|
}
|
||||||
|
|
||||||
|
class SimpleTestStackServiceTest
|
||||||
|
extends StackService
|
||||||
|
implements IStackServiceTest
|
||||||
|
{
|
||||||
|
constructor() {
|
||||||
|
super(
|
||||||
|
[
|
||||||
|
{
|
||||||
|
process: {
|
||||||
|
type: EXEC_TYPE.EXEC,
|
||||||
|
command: `nix run gitlab:robossembler/nix-robossembler-overlay#test-script '{
|
||||||
|
"filesMeta":[
|
||||||
|
{"type":"folder","name":"example", "path": null,"rewrite":true}
|
||||||
|
],
|
||||||
|
"path":"$PATH"
|
||||||
|
}'`,
|
||||||
|
isGenerating: true,
|
||||||
|
isLocaleCode: false,
|
||||||
|
issueType: IssueType.WARNING,
|
||||||
|
},
|
||||||
|
trigger: {
|
||||||
|
type: TriggerType.FILE,
|
||||||
|
value: ["context"],
|
||||||
|
},
|
||||||
|
env: null,
|
||||||
|
stackGenerateType: StackGenerateType.SINGLETON,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
process: {
|
||||||
|
type: EXEC_TYPE.EXEC,
|
||||||
|
command: `nix run gitlab:robossembler/nix-robossembler-overlay#test-script '{
|
||||||
|
"filesMeta":[
|
||||||
|
{"type":"file","name":"1.txt", "path":"example","rewrite":true}
|
||||||
|
],
|
||||||
|
"path":"$PATH"
|
||||||
|
}'`,
|
||||||
|
isGenerating: true,
|
||||||
|
isLocaleCode: false,
|
||||||
|
issueType: IssueType.WARNING,
|
||||||
|
},
|
||||||
|
trigger: {
|
||||||
|
type: TriggerType.FILE,
|
||||||
|
value: ["1.txt"],
|
||||||
|
},
|
||||||
|
env: null,
|
||||||
|
stackGenerateType: StackGenerateType.SINGLETON,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
dirname__ + "/context/"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
async test(): Promise<boolean> {
|
||||||
|
await this.call();
|
||||||
|
const testResult = readDirRecursive(this.path).equals(
|
||||||
|
["1.txt", "test.txt"],
|
||||||
|
true
|
||||||
|
);
|
||||||
|
await delay(100);
|
||||||
|
rmSync(this.path + "example/", { recursive: true });
|
||||||
|
return testResult;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class StackServiceTest {
|
||||||
|
dirName: string;
|
||||||
|
|
||||||
|
constructor(dirName: string) {
|
||||||
|
this.dirName = dirName;
|
||||||
|
}
|
||||||
|
public async test() {
|
||||||
|
const tests = [new SimpleTestStackServiceTest()];
|
||||||
|
for await (const el of tests) {
|
||||||
|
assert((await el.test()) === true, el.constructor.name);
|
||||||
|
await delay(3000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
137
server/test/features/trigger_service_test.ts
Normal file
137
server/test/features/trigger_service_test.ts
Normal file
|
@ -0,0 +1,137 @@
|
||||||
|
import {
|
||||||
|
EventsFileChanger,
|
||||||
|
MetaDataFileManagerModel,
|
||||||
|
} from "../../src/core/model/meta_data_file_manager_model.js";
|
||||||
|
import { TriggerType } from "../../src/core/model/process_model.js";
|
||||||
|
import { TriggerService } from "../../src/core/services/trigger_service.js";
|
||||||
|
import { assert } from "../test.js";
|
||||||
|
abstract class TriggerTest {
|
||||||
|
abstract test(): Promise<boolean>;
|
||||||
|
}
|
||||||
|
class TriggerServiceFileOkTest extends TriggerService implements TriggerTest {
|
||||||
|
constructor() {
|
||||||
|
super(
|
||||||
|
{
|
||||||
|
type: TriggerType.FILE,
|
||||||
|
value: ["context"],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"/context/": new MetaDataFileManagerModel(
|
||||||
|
"",
|
||||||
|
"",
|
||||||
|
EventsFileChanger.create
|
||||||
|
),
|
||||||
|
},
|
||||||
|
""
|
||||||
|
);
|
||||||
|
}
|
||||||
|
async test(): Promise<boolean> {
|
||||||
|
const r = await this.call();
|
||||||
|
|
||||||
|
return r.isSuccess();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
class TriggerServiceFileErrorTest
|
||||||
|
extends TriggerService
|
||||||
|
implements TriggerTest
|
||||||
|
{
|
||||||
|
constructor() {
|
||||||
|
super(
|
||||||
|
{
|
||||||
|
type: TriggerType.FILE,
|
||||||
|
value: ["123"],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"/ctx/": new MetaDataFileManagerModel("", "", EventsFileChanger.create),
|
||||||
|
"/context/": new MetaDataFileManagerModel(
|
||||||
|
"",
|
||||||
|
"",
|
||||||
|
EventsFileChanger.create
|
||||||
|
),
|
||||||
|
},
|
||||||
|
|
||||||
|
""
|
||||||
|
);
|
||||||
|
}
|
||||||
|
async test(): Promise<boolean> {
|
||||||
|
const r = await this.call();
|
||||||
|
|
||||||
|
return r.isFailure();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
class TriggerServiceProcessOkTest
|
||||||
|
extends TriggerService
|
||||||
|
implements TriggerTest
|
||||||
|
{
|
||||||
|
constructor() {
|
||||||
|
super(
|
||||||
|
{
|
||||||
|
type: TriggerType.PROCESS,
|
||||||
|
value: [
|
||||||
|
`function main(){
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
main()`,
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"/context/": new MetaDataFileManagerModel(
|
||||||
|
"",
|
||||||
|
"",
|
||||||
|
EventsFileChanger.create
|
||||||
|
),
|
||||||
|
},
|
||||||
|
""
|
||||||
|
);
|
||||||
|
}
|
||||||
|
async test(): Promise<boolean> {
|
||||||
|
const r = await this.call();
|
||||||
|
return r.isSuccess();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class TriggerServiceProcessErrorTest
|
||||||
|
extends TriggerService
|
||||||
|
implements TriggerTest
|
||||||
|
{
|
||||||
|
constructor() {
|
||||||
|
super(
|
||||||
|
{
|
||||||
|
type: TriggerType.PROCESS,
|
||||||
|
value: [
|
||||||
|
`function main(){
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
`,
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"/context/": new MetaDataFileManagerModel(
|
||||||
|
"",
|
||||||
|
"",
|
||||||
|
EventsFileChanger.create
|
||||||
|
),
|
||||||
|
},
|
||||||
|
""
|
||||||
|
);
|
||||||
|
}
|
||||||
|
async test(): Promise<boolean> {
|
||||||
|
const r = await this.call();
|
||||||
|
return r.isFailure();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
export class TriggerServiceTest {
|
||||||
|
public async test() {
|
||||||
|
const tests: TriggerTest[] = [
|
||||||
|
new TriggerServiceFileOkTest(),
|
||||||
|
new TriggerServiceFileErrorTest(),
|
||||||
|
new TriggerServiceProcessOkTest(),
|
||||||
|
new TriggerServiceProcessErrorTest(),
|
||||||
|
];
|
||||||
|
for await (const el of tests) {
|
||||||
|
assert((await el.test()) === true, el.constructor.name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -2,46 +2,29 @@ import locator from "../src/core/di/register_di.js";
|
||||||
import { UnitTestEnv } from "../src/core/di/env.js";
|
import { UnitTestEnv } from "../src/core/di/env.js";
|
||||||
import { fileURLToPath } from "url";
|
import { fileURLToPath } from "url";
|
||||||
import { dirname } from "path";
|
import { dirname } from "path";
|
||||||
import { ExecutorProgramServiceTest } from "./executor_program_service_test.js";
|
import { ExecutorProgramServiceTest } from "./features/executor_program_service_test.js";
|
||||||
import { FilesChangerTest } from "./files_change_notifier_service_test.js";
|
import { FilesChangerTest } from "./features/files_change_notifier_service_test.js";
|
||||||
import { TestCore } from "./core/test_core.js";
|
import { TestCore } from "./core/test_core.js";
|
||||||
import "reflect-metadata";
|
import "reflect-metadata";
|
||||||
import { CalculationProcess, IssueType, ProcessMetaData, ProcessType, TriggerType } from "../src/core/model/process_model.js";
|
import { StackServiceTest } from "./features/stack_service_test.js";
|
||||||
|
import { TriggerServiceTest } from "./features/trigger_service_test.js";
|
||||||
|
|
||||||
const testCore = TestCore.instance;
|
const testCore = TestCore.instance;
|
||||||
const __filename: string = fileURLToPath(import.meta.url);
|
const __filename: string = fileURLToPath(import.meta.url);
|
||||||
|
|
||||||
export const __dirname: string = dirname(__filename);
|
export const dirname__: string = dirname(__filename);
|
||||||
export const assert = testCore.assert;
|
export const assert = testCore.assert;
|
||||||
export const resultTest = testCore.resultTest;
|
export const resultTest = testCore.resultTest;
|
||||||
const env = new UnitTestEnv(__dirname);
|
const env = new UnitTestEnv(dirname__);
|
||||||
|
|
||||||
locator(env);
|
locator(env);
|
||||||
|
|
||||||
const main = async () => {
|
const main = async () => {
|
||||||
await new ExecutorProgramServiceTest(__dirname).test();
|
await new ExecutorProgramServiceTest(dirname__).test();
|
||||||
await new FilesChangerTest(__dirname).test()
|
await new FilesChangerTest(dirname__).test();
|
||||||
|
await new StackServiceTest(dirname__ + "/context/").test();
|
||||||
|
await new TriggerServiceTest().test();
|
||||||
await testCore.testResult();
|
await testCore.testResult();
|
||||||
};
|
};
|
||||||
|
|
||||||
main();
|
main();
|
||||||
// const logProcess:ProcessMetaData = {
|
|
||||||
// process: {
|
|
||||||
// type: ProcessType.EXEC,
|
|
||||||
// command: `nix-shell -p 'python39.withPackages(ps: with ps; [ ])' --run 'python3 p.py 1'`,
|
|
||||||
// isGenerating: false,
|
|
||||||
// isLocaleCode: false,
|
|
||||||
// issueType: IssueType.WARNING,
|
|
||||||
// timeout: 10000000,
|
|
||||||
// },
|
|
||||||
|
|
||||||
// trigger: {
|
|
||||||
// type: TriggerType.PROCESS,
|
|
||||||
// value: 'code',
|
|
||||||
// },
|
|
||||||
// env: null,
|
|
||||||
|
|
||||||
// }
|
|
||||||
// const calculationProcess = new CalculationProcess([])
|
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue