crud test controller and class validator mocker generate classes
This commit is contained in:
parent
6c85616c99
commit
9617d313a1
24 changed files with 242 additions and 108 deletions
2
.vscode/settings.json
vendored
2
.vscode/settings.json
vendored
|
@ -15,6 +15,6 @@
|
||||||
"*ui": false,
|
"*ui": false,
|
||||||
"*ui.*": false
|
"*ui.*": false
|
||||||
},
|
},
|
||||||
"cSpell.words": ["antd", "fileupload", "metadatas", "uuidv"],
|
"cSpell.words": ["antd", "fileupload", "metadatas", "undici", "uuidv"],
|
||||||
"editor.rulers": [100]
|
"editor.rulers": [100]
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,9 +5,9 @@
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"pretest": "tsc",
|
"pretest": "tsc",
|
||||||
"test": "ts-node ./build/test/test.js",
|
"test:unit": "NODE_ENV=unit tsc-watch --onSuccess 'ts-node ./build/test/test.js'",
|
||||||
"test:watch": "tsc-watch --onSuccess 'ts-node ./build/test/test.js'",
|
"test:e2e": "NODE_ENV=e2e tsc-watch --onSuccess 'ts-node ./build/test/test.js'",
|
||||||
"dev": "tsc-watch --onSuccess 'ts-node ./build/src/main.js'"
|
"dev": "NODE_ENV=dev tsc-watch --onSuccess 'ts-node ./build/src/main.js'"
|
||||||
},
|
},
|
||||||
"author": "IDONTSUDO",
|
"author": "IDONTSUDO",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
@ -31,10 +31,10 @@
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@grpc/grpc-js": "^1.9.0",
|
"@grpc/grpc-js": "^1.9.0",
|
||||||
|
"axios": "^1.6.2",
|
||||||
"babel-register": "^6.26.0",
|
"babel-register": "^6.26.0",
|
||||||
"class-transformer": "^0.5.1",
|
"class-transformer": "^0.5.1",
|
||||||
"class-validator": "^0.14.0",
|
"class-validator": "^0.14.0",
|
||||||
"concurrently": "^8.2.0",
|
|
||||||
"cors": "^2.8.5",
|
"cors": "^2.8.5",
|
||||||
"express": "^4.18.2",
|
"express": "^4.18.2",
|
||||||
"express-fileupload": "^1.4.2",
|
"express-fileupload": "^1.4.2",
|
||||||
|
|
|
@ -9,25 +9,46 @@ import fileUpload from "express-fileupload";
|
||||||
import { SetLastActivePipelineToRealTimeServiceScenario } from "../scenarios/set_active_pipeline_to_realtime_service_scenario";
|
import { SetLastActivePipelineToRealTimeServiceScenario } from "../scenarios/set_active_pipeline_to_realtime_service_scenario";
|
||||||
import { CheckAndCreateStaticFilesFolderUseCase } from "../usecases/check_and_create_static_files_folder_usecase";
|
import { CheckAndCreateStaticFilesFolderUseCase } from "../usecases/check_and_create_static_files_folder_usecase";
|
||||||
import { DataBaseConnectUseCase } from "../usecases/database_connect_usecase";
|
import { DataBaseConnectUseCase } from "../usecases/database_connect_usecase";
|
||||||
|
import { TypedEvent } from "../helpers/typed_event";
|
||||||
|
|
||||||
export class App {
|
export enum ServerStatus {
|
||||||
|
init = "init",
|
||||||
|
finished = "finshed",
|
||||||
|
error = "error",
|
||||||
|
}
|
||||||
|
export enum Environment {
|
||||||
|
DEV = "DEV",
|
||||||
|
E2E_TEST = "E2E_TEST",
|
||||||
|
}
|
||||||
|
|
||||||
|
export class App extends TypedEvent<ServerStatus> {
|
||||||
public app: express.Application;
|
public app: express.Application;
|
||||||
public port: number;
|
public port: number;
|
||||||
public env: string;
|
public env: Environment;
|
||||||
public socketSubscribers: SocketSubscriber<any>[];
|
public socketSubscribers: SocketSubscriber<any>[];
|
||||||
public io: Server;
|
public io: Server;
|
||||||
|
status: ServerStatus;
|
||||||
|
|
||||||
constructor(routes: Routes[], socketSubscribers: SocketSubscriber<any>[]) {
|
constructor(routes: Routes[] = [], socketSubscribers: SocketSubscriber<any>[] = [], env = Environment.DEV) {
|
||||||
|
super();
|
||||||
|
this.init(routes, socketSubscribers, env);
|
||||||
|
}
|
||||||
|
|
||||||
|
public init(routes: Routes[], socketSubscribers: SocketSubscriber<any>[], env: Environment) {
|
||||||
this.port = 4001;
|
this.port = 4001;
|
||||||
this.socketSubscribers = socketSubscribers;
|
this.socketSubscribers = socketSubscribers;
|
||||||
this.env = "dev";
|
this.env = env;
|
||||||
this.app = express();
|
this.app = express();
|
||||||
|
this.setServerStatus(ServerStatus.init);
|
||||||
|
|
||||||
this.loadAppDependencies().then(() => {
|
this.loadAppDependencies().then(() => {
|
||||||
this.initializeMiddlewares();
|
this.initializeMiddlewares();
|
||||||
this.initializeRoutes(routes);
|
this.initializeRoutes(routes);
|
||||||
|
if (this.status !== ServerStatus.error) {
|
||||||
|
this.setServerStatus(ServerStatus.finished);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public listen() {
|
public listen() {
|
||||||
const httpServer = createServer(this.app);
|
const httpServer = createServer(this.app);
|
||||||
const io = new Server(httpServer, {
|
const io = new Server(httpServer, {
|
||||||
|
@ -49,9 +70,13 @@ export class App {
|
||||||
console.info(`🚀 WS ws://localhost:${this.port}`);
|
console.info(`🚀 WS ws://localhost:${this.port}`);
|
||||||
console.info(`=================================`);
|
console.info(`=================================`);
|
||||||
});
|
});
|
||||||
|
|
||||||
this.io = io;
|
this.io = io;
|
||||||
}
|
}
|
||||||
|
setServerStatus(status: ServerStatus) {
|
||||||
|
this.emit(status);
|
||||||
|
this.status = status;
|
||||||
|
}
|
||||||
public getServer() {
|
public getServer() {
|
||||||
return this.app;
|
return this.app;
|
||||||
}
|
}
|
||||||
|
@ -75,12 +100,19 @@ export class App {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async loadAppDependencies() {
|
async loadAppDependencies(): Promise<void> {
|
||||||
if ((await new DataBaseConnectUseCase().call()).isFailure()) {
|
const dataBaseName = this.env === Environment.E2E_TEST ? "e2e_test" : "dev";
|
||||||
console.log("database connect error");
|
// TODO(IDONTSUDO):maybe convert it to a class and map it there
|
||||||
}
|
const result = await new DataBaseConnectUseCase().call(dataBaseName);
|
||||||
await new CheckAndCreateStaticFilesFolderUseCase().call();
|
await result.fold(
|
||||||
await new SetLastActivePipelineToRealTimeServiceScenario().call();
|
async (_s) => {
|
||||||
|
await new CheckAndCreateStaticFilesFolderUseCase().call();
|
||||||
|
await new SetLastActivePipelineToRealTimeServiceScenario().call();
|
||||||
|
},
|
||||||
|
async (_e) => {
|
||||||
|
this.setServerStatus(ServerStatus.error);
|
||||||
|
}
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
static staticFilesStoreDir = () => {
|
static staticFilesStoreDir = () => {
|
||||||
|
|
|
@ -3,7 +3,7 @@ import { Result } from "../helpers/result";
|
||||||
import { Router, Request, Response } from "express";
|
import { Router, Request, Response } from "express";
|
||||||
import { IRouteModel, Routes } from "../interfaces/router";
|
import { IRouteModel, Routes } from "../interfaces/router";
|
||||||
|
|
||||||
export type Method = "all" | "get" | "post" | "put" | "delete" | "patch" | "options" | "head";
|
export type HttpMethodType = "GET" | "POST" | "PUT" | "DELETE" | "PATCH" | "PATCH" | "HEAD";
|
||||||
|
|
||||||
export type ResponseBase = Promise<Result<any, any>>;
|
export type ResponseBase = Promise<Result<any, any>>;
|
||||||
|
|
||||||
|
@ -29,7 +29,7 @@ export abstract class CallbackStrategyWithFileUpload {
|
||||||
}
|
}
|
||||||
|
|
||||||
interface ISubSetFeatureRouter<T> {
|
interface ISubSetFeatureRouter<T> {
|
||||||
method: Method;
|
method: HttpMethodType;
|
||||||
subUrl: string;
|
subUrl: string;
|
||||||
fn:
|
fn:
|
||||||
| CallbackStrategyWithValidationModel<T>
|
| CallbackStrategyWithValidationModel<T>
|
||||||
|
@ -78,7 +78,7 @@ export class CoreHttpController<V> implements ICoreHttpController {
|
||||||
call(): Routes {
|
call(): Routes {
|
||||||
if (this.subRoutes.isNotEmpty()) {
|
if (this.subRoutes.isNotEmpty()) {
|
||||||
this.subRoutes.map((el) => {
|
this.subRoutes.map((el) => {
|
||||||
this.router[el.method](this.mainURL + "/" + el.subUrl, async (req, res) => {
|
this.router[el.method.toLowerCase()](this.mainURL + "/" + el.subUrl, async (req, res) => {
|
||||||
if (el.fn instanceof CallbackStrategyWithValidationModel) {
|
if (el.fn instanceof CallbackStrategyWithValidationModel) {
|
||||||
// TODO(IDONTSUDO):
|
// TODO(IDONTSUDO):
|
||||||
throw Error("needs to be implimed");
|
throw Error("needs to be implimed");
|
||||||
|
|
26
server/src/core/controllers/routes.ts
Normal file
26
server/src/core/controllers/routes.ts
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
import { NixStoreManagerPresentation } from "../../features/nix_store_manager/nix_store_manager";
|
||||||
|
import { PipelinePresentation } from "../../features/pipelines/pipeline_presentation";
|
||||||
|
import { ProcessPresentation } from "../../features/process/process_presentation";
|
||||||
|
import { ProjectInstancePresentation } from "../../features/project_instance/project_instance_presentation";
|
||||||
|
import { ProjectsPresentation } from "../../features/projects/projects_presentation";
|
||||||
|
import { RealTimePresentation } from "../../features/realtime/realtime_presentation";
|
||||||
|
import { TriggerPresentation } from "../../features/triggers/triggers_presentation";
|
||||||
|
import { extensions } from "../extensions/extensions";
|
||||||
|
import { Routes } from "../interfaces/router";
|
||||||
|
|
||||||
|
extensions();
|
||||||
|
|
||||||
|
export const routersImplementPureCrud = [
|
||||||
|
new TriggerPresentation(),
|
||||||
|
new ProjectsPresentation(),
|
||||||
|
new ProcessPresentation(),
|
||||||
|
new PipelinePresentation(),
|
||||||
|
];
|
||||||
|
|
||||||
|
export const httpRoutes: Routes[] = [
|
||||||
|
new RealTimePresentation(),
|
||||||
|
new ProjectInstancePresentation(),
|
||||||
|
new NixStoreManagerPresentation(),
|
||||||
|
]
|
||||||
|
.concat(routersImplementPureCrud)
|
||||||
|
.map((el) => el.call());
|
|
@ -1,5 +1,10 @@
|
||||||
/* eslint-disable @typescript-eslint/no-this-alias */
|
/* eslint-disable @typescript-eslint/no-this-alias */
|
||||||
export const ArrayExtensions = () => {
|
export const ArrayExtensions = () => {
|
||||||
|
if ([].firstElement === undefined) {
|
||||||
|
Array.prototype.firstElement = function () {
|
||||||
|
return this[0];
|
||||||
|
};
|
||||||
|
}
|
||||||
if ([].equals === undefined) {
|
if ([].equals === undefined) {
|
||||||
// eslint-disable-next-line no-extend-native
|
// eslint-disable-next-line no-extend-native
|
||||||
Array.prototype.equals = function (array, strict = true) {
|
Array.prototype.equals = function (array, strict = true) {
|
||||||
|
|
|
@ -6,6 +6,7 @@ declare global {
|
||||||
// @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.
|
// @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;
|
equals(array: Array<T>, strict: boolean): boolean;
|
||||||
lastElement(): T | undefined;
|
lastElement(): T | undefined;
|
||||||
|
firstElement(): T | undefined;
|
||||||
isEmpty(): boolean;
|
isEmpty(): boolean;
|
||||||
isNotEmpty(): boolean;
|
isNotEmpty(): boolean;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
import { randomBytes, randomInt, randomUUID } from "crypto";
|
import { randomBytes, randomInt, randomUUID } from "crypto";
|
||||||
import { getMetadataStorage, IS_BOOLEAN, IS_MONGO_ID, IS_NUMBER, IS_STRING, IS_UUID } from "class-validator";
|
import { getMetadataStorage, IS_ARRAY, IS_BOOLEAN, IS_MONGO_ID, IS_NUMBER, IS_STRING, IS_UUID } from "class-validator";
|
||||||
import { ValidationMetadata } from "class-validator/types/metadata/ValidationMetadata";
|
import { ValidationMetadata } from "class-validator/types/metadata/ValidationMetadata";
|
||||||
|
|
||||||
type AvailableTypes = string | number | boolean | undefined;
|
type AvailableTypes = string | number | boolean | undefined | [];
|
||||||
|
|
||||||
export class ClassValidatorMocker {
|
export class ClassValidatorMocker {
|
||||||
// eslint-disable-next-line @typescript-eslint/ban-types
|
// eslint-disable-next-line @typescript-eslint/ban-types
|
||||||
|
@ -16,7 +16,6 @@ export class ClassValidatorMocker {
|
||||||
const targetMetadatas = metadataStorage.getTargetValidationMetadatas(constructor, "", false, false);
|
const targetMetadatas = metadataStorage.getTargetValidationMetadatas(constructor, "", false, false);
|
||||||
const groupedMetadatas = metadataStorage.groupByPropertyName(targetMetadatas);
|
const groupedMetadatas = metadataStorage.groupByPropertyName(targetMetadatas);
|
||||||
// nestedValidation
|
// nestedValidation
|
||||||
console.log(targetMetadatas);
|
|
||||||
let randomFixture = {} as T;
|
let randomFixture = {} as T;
|
||||||
|
|
||||||
for (const propertyName of Object.keys(groupedMetadatas)) {
|
for (const propertyName of Object.keys(groupedMetadatas)) {
|
||||||
|
@ -40,6 +39,10 @@ export class ClassValidatorMocker {
|
||||||
|
|
||||||
for (const constraint of constraints) {
|
for (const constraint of constraints) {
|
||||||
switch (constraint.name) {
|
switch (constraint.name) {
|
||||||
|
case IS_ARRAY:
|
||||||
|
return [];
|
||||||
|
case "isEnum":
|
||||||
|
return Object.keys(metadata.constraints.firstElement()).firstElement();
|
||||||
case IS_MONGO_ID:
|
case IS_MONGO_ID:
|
||||||
return this.randomUUID();
|
return this.randomUUID();
|
||||||
case IS_STRING:
|
case IS_STRING:
|
|
@ -1,3 +1,7 @@
|
||||||
|
import { extensions } from "../extensions/extensions";
|
||||||
|
|
||||||
|
extensions();
|
||||||
|
|
||||||
export class ExecError extends Error {
|
export class ExecError extends Error {
|
||||||
static isExecError(e: any): ExecError | void {
|
static isExecError(e: any): ExecError | void {
|
||||||
if ("type" in e && "script" in e && "unixTime" in e && "error" in e) {
|
if ("type" in e && "script" in e && "unixTime" in e && "error" in e) {
|
||||||
|
@ -13,7 +17,7 @@ export class ExecError extends Error {
|
||||||
super(...args);
|
super(...args);
|
||||||
this.script = script;
|
this.script = script;
|
||||||
this.unixTime = Date.now();
|
this.unixTime = Date.now();
|
||||||
this.error = args[0];
|
this.error = args.firstElement();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
24
server/src/core/repository/http_repository.ts
Normal file
24
server/src/core/repository/http_repository.ts
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
import axios from "axios";
|
||||||
|
import { Result } from "../helpers/result";
|
||||||
|
import { HttpMethodType } from "../controllers/http_controller";
|
||||||
|
|
||||||
|
export class HttpRepository {
|
||||||
|
serverUrl = "http://localhost:4001";
|
||||||
|
|
||||||
|
constructor(serverURL: string) {
|
||||||
|
this.serverUrl = serverURL;
|
||||||
|
}
|
||||||
|
|
||||||
|
async jsonRequest<T>(url: string, method: HttpMethodType, reqBody?: any): Promise<Result<Error, T>> {
|
||||||
|
try {
|
||||||
|
const result = await axios(this.serverUrl + url, { method: method, data: reqBody });
|
||||||
|
if (result.status !== 200) {
|
||||||
|
return Result.error(Error("status code" + String(result.status)));
|
||||||
|
}
|
||||||
|
return Result.ok(result.data);
|
||||||
|
} catch (error) {
|
||||||
|
console.log(error);
|
||||||
|
return Result.error(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -2,11 +2,13 @@ import mongoose from "mongoose";
|
||||||
import { Result } from "../helpers/result";
|
import { Result } from "../helpers/result";
|
||||||
|
|
||||||
export class DataBaseConnectUseCase {
|
export class DataBaseConnectUseCase {
|
||||||
call = async (): Promise<Result<Error, void>> => {
|
call = async (dataBaseName: string = "test"): Promise<Result<Error, void>> => {
|
||||||
try {
|
try {
|
||||||
await mongoose.connect("mongodb://127.0.0.1:27017/test");
|
await mongoose.connect(`mongodb://127.0.0.1:27017/${dataBaseName}`);
|
||||||
return Result.ok();
|
return Result.ok();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
console.log(error);
|
||||||
|
console.log("database connect error");
|
||||||
return Result.error(error as Error);
|
return Result.error(error as Error);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -5,12 +5,17 @@ export class DeleteDataBaseModelUseCase<D> {
|
||||||
constructor(model) {
|
constructor(model) {
|
||||||
this.databaseModel = model;
|
this.databaseModel = model;
|
||||||
}
|
}
|
||||||
call = async (id: string): Promise<Result<Error, boolean>> => {
|
call = async (id: string): Promise<Result<Error, any>> => {
|
||||||
try {
|
try {
|
||||||
const model = new this.databaseModel({ _id: id });
|
const model = this.databaseModel as any;
|
||||||
await model.deleteOne();
|
|
||||||
|
|
||||||
return Result.ok(true);
|
const result = await model.deleteOne({ _id: id });
|
||||||
|
|
||||||
|
if (result.deletedCount === 0) {
|
||||||
|
return Result.error(Error(`the database does not have a collection with this ID:${id}`));
|
||||||
|
}
|
||||||
|
|
||||||
|
return Result.ok({ ok: "model delete" });
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
return Result.error(error);
|
return Result.error(error);
|
||||||
}
|
}
|
||||||
|
|
7
server/src/core/usecases/drop_database_usecase.ts
Normal file
7
server/src/core/usecases/drop_database_usecase.ts
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
import mongoose from "mongoose";
|
||||||
|
|
||||||
|
export class DropDataBaseUseCase {
|
||||||
|
call = async () => {
|
||||||
|
await mongoose.connection.dropDatabase();
|
||||||
|
};
|
||||||
|
}
|
5
server/src/core/usecases/exit_app_usecase.ts
Normal file
5
server/src/core/usecases/exit_app_usecase.ts
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
export class ExitAppUseCase {
|
||||||
|
call = () => {
|
||||||
|
process.exit();
|
||||||
|
};
|
||||||
|
}
|
|
@ -1,5 +1,4 @@
|
||||||
|
import { Result } from "../helpers/result";
|
||||||
import { Result } from "../helper/result";
|
|
||||||
|
|
||||||
export class ReadByIdDataBaseModelUseCase<D> {
|
export class ReadByIdDataBaseModelUseCase<D> {
|
||||||
databaseModel: D;
|
databaseModel: D;
|
||||||
|
|
|
@ -35,7 +35,7 @@ class GetNixStorePackagesUseCase extends CallbackStrategyWithEmpty {
|
||||||
|
|
||||||
const promise = new Promise((resolve, _reject) => {
|
const promise = new Promise((resolve, _reject) => {
|
||||||
stackService.on((e) => {
|
stackService.on((e) => {
|
||||||
const iteration = e[0];
|
const iteration = e.firstElement();
|
||||||
if (iteration.result instanceof ExecutorResult) {
|
if (iteration.result instanceof ExecutorResult) {
|
||||||
const nixPackage = iteration.result.data;
|
const nixPackage = iteration.result.data;
|
||||||
resolve(nixPackage.split("\n").filter((e) => e.hasNoPattern(".drv")));
|
resolve(nixPackage.split("\n").filter((e) => e.hasNoPattern(".drv")));
|
||||||
|
|
|
@ -1,11 +1,8 @@
|
||||||
import { IsMongoId, IsEnum } from "class-validator";
|
import { IsMongoId, IsEnum } from "class-validator";
|
||||||
import { Schema, model } from "mongoose";
|
import { Schema, model } from "mongoose";
|
||||||
import { StackGenerateType } from "../../core/model/process_model";
|
import { TriggerModel, triggerSchema } from "../triggers/trigger_model";
|
||||||
import {
|
|
||||||
TriggerModel,
|
|
||||||
triggerSchema,
|
|
||||||
} from "../triggers/trigger_model";
|
|
||||||
import { schemaProcess } from "../process/process_model";
|
import { schemaProcess } from "../process/process_model";
|
||||||
|
import { StackGenerateType } from "../../core/models/process_model";
|
||||||
|
|
||||||
export const PipelineSchema = new Schema({
|
export const PipelineSchema = new Schema({
|
||||||
process: {
|
process: {
|
||||||
|
@ -27,10 +24,7 @@ export const PipelineSchema = new Schema({
|
||||||
|
|
||||||
export const schemaPipeline = "Pipeline";
|
export const schemaPipeline = "Pipeline";
|
||||||
|
|
||||||
export const PipelineDBModel = model<PipelineModel>(
|
export const PipelineDBModel = model<PipelineModel>(schemaPipeline, PipelineSchema);
|
||||||
schemaPipeline,
|
|
||||||
PipelineSchema
|
|
||||||
);
|
|
||||||
|
|
||||||
export class PipelineModel {
|
export class PipelineModel {
|
||||||
@IsMongoId()
|
@IsMongoId()
|
||||||
|
|
|
@ -1,16 +1,7 @@
|
||||||
import {
|
import { IsString, IsOptional, IsEnum, IsNumber, IsBoolean } from "class-validator";
|
||||||
IsString,
|
|
||||||
IsOptional,
|
|
||||||
IsEnum,
|
|
||||||
IsNumber,
|
|
||||||
IsBoolean,
|
|
||||||
} from "class-validator";
|
|
||||||
import { Schema, model } from "mongoose";
|
import { Schema, model } from "mongoose";
|
||||||
import {
|
import { IProcess, IssueType } from "../../core/models/process_model";
|
||||||
IProcess,
|
import { EXEC_TYPE } from "../../core/models/exec_error_model";
|
||||||
IssueType,
|
|
||||||
} from "../../core/model/process_model";
|
|
||||||
import { EXEC_TYPE } from "../../core/model/exec_error_model";
|
|
||||||
|
|
||||||
export const ProcessSchema = new Schema({
|
export const ProcessSchema = new Schema({
|
||||||
type: {
|
type: {
|
||||||
|
@ -61,9 +52,8 @@ export class ProcessModel implements IProcess {
|
||||||
@IsOptional()
|
@IsOptional()
|
||||||
@IsNumber()
|
@IsNumber()
|
||||||
public timeout?: number;
|
public timeout?: number;
|
||||||
|
|
||||||
@IsOptional()
|
@IsOptional()
|
||||||
@IsString()
|
@IsString()
|
||||||
public commit?: string;
|
public commit?: string;
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,7 +18,7 @@ export class ProjectInstancePresentation extends CrudController<
|
||||||
|
|
||||||
super.subRoutes = [
|
super.subRoutes = [
|
||||||
{
|
{
|
||||||
method: "post",
|
method: "POST",
|
||||||
subUrl: "upload",
|
subUrl: "upload",
|
||||||
fn: new UploadCadFileToProjectScenario(),
|
fn: new UploadCadFileToProjectScenario(),
|
||||||
},
|
},
|
||||||
|
|
|
@ -1,28 +1,12 @@
|
||||||
import "reflect-metadata";
|
import "reflect-metadata";
|
||||||
import { App } from "./core/controllers/app";
|
import { App } from "./core/controllers/app";
|
||||||
import { SocketSubscriber } from "./core/controllers/socket_controller";
|
import { SocketSubscriber } from "./core/controllers/socket_controller";
|
||||||
import { Routes } from "./core/interfaces/router";
|
|
||||||
import { TriggerPresentation } from "./features/triggers/triggers_presentation";
|
|
||||||
import { ProjectsPresentation } from "./features/projects/projects_presentation";
|
|
||||||
import { PipelinePresentation } from "./features/pipelines/pipeline_presentation";
|
|
||||||
import { ProcessPresentation } from "./features/process/process_presentation";
|
|
||||||
import { RealTimePresentation, pipelineRealTimeService } from "./features/realtime/realtime_presentation";
|
|
||||||
import { extensions } from "./core/extensions/extensions";
|
import { extensions } from "./core/extensions/extensions";
|
||||||
import { ProjectInstancePresentation } from "./features/project_instance/project_instance_presentation";
|
import { httpRoutes } from "./core/controllers/routes";
|
||||||
import { NixStoreManagerPresentation } from "./features/nix_store_manager/nix_store_manager";
|
import { pipelineRealTimeService } from "./features/realtime/realtime_presentation";
|
||||||
|
|
||||||
extensions();
|
extensions();
|
||||||
|
|
||||||
export const httpRoutes: Routes[] = [
|
|
||||||
new TriggerPresentation(),
|
|
||||||
new ProjectsPresentation(),
|
|
||||||
new ProcessPresentation(),
|
|
||||||
new PipelinePresentation(),
|
|
||||||
new RealTimePresentation(),
|
|
||||||
new ProjectInstancePresentation(),
|
|
||||||
new NixStoreManagerPresentation(),
|
|
||||||
].map((el) => el.call());
|
|
||||||
|
|
||||||
const socketSubscribers = [new SocketSubscriber(pipelineRealTimeService, "realtime")];
|
const socketSubscribers = [new SocketSubscriber(pipelineRealTimeService, "realtime")];
|
||||||
|
|
||||||
new App(httpRoutes, socketSubscribers).listen();
|
new App(httpRoutes, socketSubscribers).listen();
|
||||||
|
|
49
server/test/controllers/crud_controller_test.ts
Normal file
49
server/test/controllers/crud_controller_test.ts
Normal file
|
@ -0,0 +1,49 @@
|
||||||
|
import { CrudController } from "../../src/core/controllers/crud_controller";
|
||||||
|
import { ClassValidatorMocker } from "../../src/core/helpers/class_validator_mocker";
|
||||||
|
import { HttpRepository } from "../../src/core/repository/http_repository";
|
||||||
|
|
||||||
|
function instanceOfObjectAndHaveId(s: any): string {
|
||||||
|
if (s instanceof Object && "id" in s) {
|
||||||
|
return s.id;
|
||||||
|
}
|
||||||
|
if (s instanceof Object && "_id" in s) {
|
||||||
|
return s._id;
|
||||||
|
}
|
||||||
|
throw Error(`${s} is not instance object or not have property _id`);
|
||||||
|
}
|
||||||
|
|
||||||
|
export class CrudControllerTest {
|
||||||
|
controllerTest: CrudController<any, any>;
|
||||||
|
httpRepository: HttpRepository;
|
||||||
|
|
||||||
|
constructor(port: number, controller: CrudController<any, any>) {
|
||||||
|
this.controllerTest = controller;
|
||||||
|
this.httpRepository = new HttpRepository(`http://localhost:${port}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
async call() {
|
||||||
|
let result = false;
|
||||||
|
const mockModel = ClassValidatorMocker.create<any>(this.controllerTest.validationModel);
|
||||||
|
const postRequestBody = await this.httpRepository.jsonRequest(this.controllerTest.mainURL, "POST", mockModel);
|
||||||
|
|
||||||
|
await postRequestBody.map(async (s) => {
|
||||||
|
const id = instanceOfObjectAndHaveId(s);
|
||||||
|
const getRequestBody = await this.httpRepository.jsonRequest(this.controllerTest.mainURL, "GET");
|
||||||
|
await getRequestBody.map(async (el) => {
|
||||||
|
if (el instanceof Array) {
|
||||||
|
const firstElement = el.firstElement();
|
||||||
|
const mockModelUpdate = ClassValidatorMocker.create<any>(this.controllerTest.validationModel);
|
||||||
|
Object.assign(firstElement, mockModelUpdate);
|
||||||
|
delete firstElement.__v;
|
||||||
|
const putReqBody = await this.httpRepository.jsonRequest(this.controllerTest.mainURL, "PUT", firstElement);
|
||||||
|
await putReqBody.map(async () => {
|
||||||
|
(await this.httpRepository.jsonRequest(this.controllerTest.mainURL + "?id=" + id, "DELETE")).map(() => {
|
||||||
|
result = true;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
|
@ -2,9 +2,12 @@ import mongoose from "mongoose";
|
||||||
import { delay } from "../../src/core/helpers/delay";
|
import { delay } from "../../src/core/helpers/delay";
|
||||||
import { Result } from "../../src/core/helpers/result";
|
import { Result } from "../../src/core/helpers/result";
|
||||||
import { TypedEvent } from "../../src/core/helpers/typed_event";
|
import { TypedEvent } from "../../src/core/helpers/typed_event";
|
||||||
|
import { DropDataBaseUseCase } from "../../src/core/usecases/drop_database_usecase";
|
||||||
|
import { ExitAppUseCase } from "../../src/core/usecases/exit_app_usecase";
|
||||||
|
|
||||||
export const before = async () => {
|
export const before = async () => {
|
||||||
await mongoose.connection.dropDatabase();
|
new DropDataBaseUseCase().call();
|
||||||
|
new ExitAppUseCase().call();
|
||||||
};
|
};
|
||||||
|
|
||||||
export class TestCore {
|
export class TestCore {
|
||||||
|
|
|
@ -1,25 +0,0 @@
|
||||||
import { Type } from "class-transformer";
|
|
||||||
import { ClassValidatorMocker } from "../../src/core/helpers/class_validator_mocket";
|
|
||||||
import { IsString, IsNumber, IsBoolean, IsUUID, IsMongoId, ValidateNested } from "class-validator";
|
|
||||||
|
|
||||||
class Foo {}
|
|
||||||
class MyClass {
|
|
||||||
@ValidateNested()
|
|
||||||
@Type(() => Foo)
|
|
||||||
model: Foo;
|
|
||||||
|
|
||||||
@IsNumber()
|
|
||||||
numberProperty: number;
|
|
||||||
|
|
||||||
@IsBoolean()
|
|
||||||
booleanProperty: boolean;
|
|
||||||
|
|
||||||
@IsUUID()
|
|
||||||
uuidProperty: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
const myClassDataMock = ClassValidatorMocker.create<MyClass>(MyClass);
|
|
||||||
|
|
||||||
export const mainTest = () => {
|
|
||||||
console.log(myClassDataMock);
|
|
||||||
};
|
|
|
@ -13,10 +13,12 @@ import { ReadDataBaseModelUseCaseTest } from "./usecases/read_database_model_use
|
||||||
import { UpdateDataBaseModelUseCaseTest } from "./usecases/update_database_model_usecase";
|
import { UpdateDataBaseModelUseCaseTest } from "./usecases/update_database_model_usecase";
|
||||||
import { PaginationDataBaseModelUseCaseTest } from "./usecases/pagination_database_model_usecase_test";
|
import { PaginationDataBaseModelUseCaseTest } from "./usecases/pagination_database_model_usecase_test";
|
||||||
import { extensions } from "../src/core/extensions/extensions";
|
import { extensions } from "../src/core/extensions/extensions";
|
||||||
|
import { CrudControllerTest } from "./controllers/crud_controller_test";
|
||||||
|
import { TriggerPresentation } from "../src/features/triggers/triggers_presentation";
|
||||||
|
import { App, Environment, ServerStatus } from "../src/core/controllers/app";
|
||||||
|
import { httpRoutes } from "../src/core/controllers/routes";
|
||||||
import { DataBaseConnectUseCase } from "../src/core/usecases/database_connect_usecase";
|
import { DataBaseConnectUseCase } from "../src/core/usecases/database_connect_usecase";
|
||||||
|
|
||||||
extensions();
|
|
||||||
|
|
||||||
const testCore = TestCore.instance;
|
const testCore = TestCore.instance;
|
||||||
export const dirname__: string = dirname(__filename);
|
export const dirname__: string = dirname(__filename);
|
||||||
export const assert = testCore.assert;
|
export const assert = testCore.assert;
|
||||||
|
@ -33,13 +35,13 @@ const init = async () => {
|
||||||
await new DataBaseConnectUseCase().call();
|
await new DataBaseConnectUseCase().call();
|
||||||
};
|
};
|
||||||
|
|
||||||
const test = async () => {
|
const unitTest = async () => {
|
||||||
|
await init();
|
||||||
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 StackServiceTest(dirname__ + "/context/").test();
|
||||||
await new TriggerServiceTest().test();
|
await new TriggerServiceTest().test();
|
||||||
await new CreateDataBaseModelUseCaseTest().test();
|
await new CreateDataBaseModelUseCaseTest().test();
|
||||||
|
|
||||||
await new CreateDataBaseModelUseCaseTest().test();
|
await new CreateDataBaseModelUseCaseTest().test();
|
||||||
await new DeleteDataBaseModelUseCaseTest().test();
|
await new DeleteDataBaseModelUseCaseTest().test();
|
||||||
await new ReadDataBaseModelUseCaseTest().test();
|
await new ReadDataBaseModelUseCaseTest().test();
|
||||||
|
@ -48,11 +50,35 @@ const test = async () => {
|
||||||
for await (const usecase of tests) {
|
for await (const usecase of tests) {
|
||||||
testCore.assert(await new usecase().test(), usecase.name);
|
testCore.assert(await new usecase().test(), usecase.name);
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
const presentationCrudControllers = [new TriggerPresentation()];
|
||||||
|
const e2eTest = async () => {
|
||||||
|
const app = new App(httpRoutes, [], Environment.E2E_TEST);
|
||||||
|
app.listen();
|
||||||
|
await new Promise((resolve, reject) => {
|
||||||
|
app.on(async (e) => {
|
||||||
|
if (e === ServerStatus.finished) {
|
||||||
|
for await (const el of presentationCrudControllers) {
|
||||||
|
testCore.assert(await new CrudControllerTest(app.port, el).call(), el.constructor.name);
|
||||||
|
}
|
||||||
|
resolve(e);
|
||||||
|
}
|
||||||
|
if (e === ServerStatus.error) {
|
||||||
|
console.log(e);
|
||||||
|
reject(e);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
};
|
};
|
||||||
const main = async () => {
|
const main = async () => {
|
||||||
await init();
|
extensions();
|
||||||
await test();
|
if (process.env.NODE_ENV === "unit") {
|
||||||
|
await unitTest();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (process.env.NODE_ENV === "e2e") {
|
||||||
|
await e2eTest();
|
||||||
|
}
|
||||||
await testCore.testResult();
|
await testCore.testResult();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue