@avleon/core 0.0.17 → 0.0.19
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/collection.d.ts +2 -4
- package/dist/collection.js +48 -150
- package/dist/constants.d.ts +1 -0
- package/dist/constants.js +4 -0
- package/dist/icore.d.ts +5 -3
- package/dist/icore.js +23 -32
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/testing.d.ts +55 -0
- package/dist/testing.js +193 -0
- package/package.json +1 -1
package/dist/collection.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { EntityTarget, FindOneOptions, ObjectLiteral, Repository
|
|
1
|
+
import { EntityTarget, FindOneOptions, ObjectLiteral, Repository } from "typeorm";
|
|
2
2
|
type PaginationOptions = {
|
|
3
3
|
take: number;
|
|
4
4
|
skip?: number;
|
|
@@ -13,9 +13,6 @@ export type PaginationResult<T> = {
|
|
|
13
13
|
last?: number | null;
|
|
14
14
|
totalPage?: number;
|
|
15
15
|
};
|
|
16
|
-
export declare class Repository<Entity extends ObjectLiteral> extends TypeOrmRepository<Entity> {
|
|
17
|
-
paginate(options?: PaginationOptions): Promise<PaginationResult<Entity>>;
|
|
18
|
-
}
|
|
19
16
|
type ICollection<T> = {
|
|
20
17
|
findAll(): T[] | Promise<T[]>;
|
|
21
18
|
};
|
|
@@ -49,4 +46,5 @@ export declare class Collection {
|
|
|
49
46
|
static from<T>(items: T[]): BasicCollection<T>;
|
|
50
47
|
static fromRepository<T extends ObjectLiteral>(entity: EntityTarget<T>): Repository<T>;
|
|
51
48
|
}
|
|
49
|
+
export declare function InjectRepository<T extends Repository<T>>(model: EntityTarget<T>): (object: any, propertyName: string | undefined, index?: number) => void;
|
|
52
50
|
export {};
|
package/dist/collection.js
CHANGED
|
@@ -3,7 +3,8 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
3
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.Collection =
|
|
6
|
+
exports.Collection = void 0;
|
|
7
|
+
exports.InjectRepository = InjectRepository;
|
|
7
8
|
/**
|
|
8
9
|
* @copyright 2024
|
|
9
10
|
* @author Tareq Hossain
|
|
@@ -12,18 +13,6 @@ exports.Collection = exports.Repository = void 0;
|
|
|
12
13
|
*/
|
|
13
14
|
const typedi_1 = __importDefault(require("typedi"));
|
|
14
15
|
const exceptions_1 = require("./exceptions");
|
|
15
|
-
const typeorm_1 = require("typeorm");
|
|
16
|
-
class Repository extends typeorm_1.Repository {
|
|
17
|
-
async paginate(options = { take: 10, skip: 0 }) {
|
|
18
|
-
const total = await this.count();
|
|
19
|
-
const data = await this.find({
|
|
20
|
-
take: options.take || 10,
|
|
21
|
-
skip: options.skip || 0,
|
|
22
|
-
});
|
|
23
|
-
return { total, data };
|
|
24
|
-
}
|
|
25
|
-
}
|
|
26
|
-
exports.Repository = Repository;
|
|
27
16
|
class BasicCollection {
|
|
28
17
|
constructor(items) {
|
|
29
18
|
this.items = items;
|
|
@@ -119,10 +108,9 @@ class AsynchronousCollection {
|
|
|
119
108
|
}
|
|
120
109
|
getRepository() {
|
|
121
110
|
if (!this.repo) {
|
|
122
|
-
const
|
|
123
|
-
const
|
|
124
|
-
|
|
125
|
-
});
|
|
111
|
+
const dataSourceKey = process.env.NODE_ENV === "test" ? "itestdatasource" : "idatasource";
|
|
112
|
+
const dataSource = typedi_1.default.get(dataSourceKey);
|
|
113
|
+
const repository = dataSource.getRepository(this.model);
|
|
126
114
|
this.repo = repository;
|
|
127
115
|
return repository;
|
|
128
116
|
}
|
|
@@ -144,146 +132,56 @@ class AsynchronousCollection {
|
|
|
144
132
|
data,
|
|
145
133
|
};
|
|
146
134
|
}
|
|
147
|
-
createQueryBuilder(alias, queryRunner) {
|
|
148
|
-
return this.getRepository().createQueryBuilder(alias, queryRunner);
|
|
149
|
-
}
|
|
150
|
-
hasId(entity) {
|
|
151
|
-
return this.getRepository().hasId(entity);
|
|
152
|
-
}
|
|
153
|
-
getId(entity) {
|
|
154
|
-
return this.getRepository().getId(entity);
|
|
155
|
-
}
|
|
156
|
-
create(entityLike) {
|
|
157
|
-
return this.getRepository().create(entityLike);
|
|
158
|
-
}
|
|
159
|
-
merge(mergeIntoEntity, ...entityLikes) {
|
|
160
|
-
return this.getRepository().merge(mergeIntoEntity, ...entityLikes);
|
|
161
|
-
}
|
|
162
|
-
async preload(entityLike) {
|
|
163
|
-
return this.getRepository().preload(entityLike);
|
|
164
|
-
}
|
|
165
|
-
async save(entities, options) {
|
|
166
|
-
return this.getRepository().save(entities, options);
|
|
167
|
-
}
|
|
168
|
-
async remove(entity) {
|
|
169
|
-
return this.getRepository().remove(entity);
|
|
170
|
-
}
|
|
171
|
-
async softRemove(entity) {
|
|
172
|
-
return this.getRepository().softRemove(entity);
|
|
173
|
-
}
|
|
174
|
-
async recover(entity) {
|
|
175
|
-
return this.getRepository().recover(entity);
|
|
176
|
-
}
|
|
177
|
-
async insert(entity) {
|
|
178
|
-
await this.getRepository().insert(entity);
|
|
179
|
-
}
|
|
180
|
-
async update(criteria, partialEntity) {
|
|
181
|
-
return this.getRepository().update(criteria, partialEntity);
|
|
182
|
-
}
|
|
183
|
-
async upsert(entityOrEntities, conflictPathsOrOptions) {
|
|
184
|
-
await this.getRepository().upsert(entityOrEntities, conflictPathsOrOptions);
|
|
185
|
-
}
|
|
186
|
-
async delete(criteria) {
|
|
187
|
-
return this.getRepository().delete(criteria);
|
|
188
|
-
}
|
|
189
|
-
async softDelete(criteria) {
|
|
190
|
-
return this.getRepository().softDelete(criteria);
|
|
191
|
-
}
|
|
192
|
-
async restore(criteria) {
|
|
193
|
-
return this.getRepository().restore(criteria);
|
|
194
|
-
}
|
|
195
|
-
async exist(options) {
|
|
196
|
-
return (await this.getRepository().count(options)) > 0;
|
|
197
|
-
}
|
|
198
|
-
async exists(options) {
|
|
199
|
-
return this.exist(options);
|
|
200
|
-
}
|
|
201
|
-
async existsBy(where) {
|
|
202
|
-
return (await this.getRepository().count({ where })) > 0;
|
|
203
|
-
}
|
|
204
|
-
async count(options) {
|
|
205
|
-
return this.getRepository().count(options);
|
|
206
|
-
}
|
|
207
|
-
async countBy(where) {
|
|
208
|
-
return this.getRepository().count({ where });
|
|
209
|
-
}
|
|
210
|
-
async sum(columnName, where) {
|
|
211
|
-
return this.getRepository()
|
|
212
|
-
.createQueryBuilder()
|
|
213
|
-
.select(`SUM(${columnName})`, "sum")
|
|
214
|
-
.where(where || {})
|
|
215
|
-
.getRawOne()
|
|
216
|
-
.then((res) => (res === null || res === void 0 ? void 0 : res.sum) || null);
|
|
217
|
-
}
|
|
218
|
-
async average(columnName, where) {
|
|
219
|
-
return this.getRepository()
|
|
220
|
-
.createQueryBuilder()
|
|
221
|
-
.select(`AVG(${columnName})`, "average")
|
|
222
|
-
.where(where || {})
|
|
223
|
-
.getRawOne()
|
|
224
|
-
.then((res) => (res === null || res === void 0 ? void 0 : res.average) || null);
|
|
225
|
-
}
|
|
226
|
-
async minimum(columnName, where) {
|
|
227
|
-
return this.getRepository()
|
|
228
|
-
.createQueryBuilder()
|
|
229
|
-
.select(`MIN(${columnName})`, "minimum")
|
|
230
|
-
.where(where || {})
|
|
231
|
-
.getRawOne()
|
|
232
|
-
.then((res) => (res === null || res === void 0 ? void 0 : res.minimum) || null);
|
|
233
|
-
}
|
|
234
|
-
async maximum(columnName, where) {
|
|
235
|
-
return this.getRepository().maximum(columnName, where);
|
|
236
|
-
}
|
|
237
|
-
async find(options) {
|
|
238
|
-
return await this.getRepository().find(options);
|
|
239
|
-
}
|
|
240
|
-
async findBy(where) {
|
|
241
|
-
return this.getRepository().findBy(where);
|
|
242
|
-
}
|
|
243
|
-
async findAndCount(options) {
|
|
244
|
-
return this.getRepository().findAndCount(options);
|
|
245
|
-
}
|
|
246
|
-
async findAndCountBy(where) {
|
|
247
|
-
return this.getRepository().findAndCount({ where });
|
|
248
|
-
}
|
|
249
|
-
async findByIds(ids) {
|
|
250
|
-
return this.getRepository().findBy(ids);
|
|
251
|
-
}
|
|
252
|
-
async findOne(options) {
|
|
253
|
-
return this.getRepository().findOne(options);
|
|
254
|
-
}
|
|
255
|
-
async findOneBy(where) {
|
|
256
|
-
return this.getRepository().findOneBy(where);
|
|
257
|
-
}
|
|
258
|
-
async findOneById(id) {
|
|
259
|
-
return this.getRepository().findOneBy({ id });
|
|
260
|
-
}
|
|
261
|
-
async findOneOrFail(options) {
|
|
262
|
-
return this.getRepository().findOneOrFail(options);
|
|
263
|
-
}
|
|
264
|
-
async findOneByOrFail(where) {
|
|
265
|
-
return this.getRepository().findOneByOrFail(where);
|
|
266
|
-
}
|
|
267
|
-
async query(query, parameters) {
|
|
268
|
-
return this.getRepository().query(query, parameters);
|
|
269
|
-
}
|
|
270
|
-
async clear() {
|
|
271
|
-
await this.getRepository().clear();
|
|
272
|
-
}
|
|
273
|
-
async increment(conditions, propertyPath, value) {
|
|
274
|
-
return this.getRepository().increment(conditions, propertyPath, value);
|
|
275
|
-
}
|
|
276
|
-
async decrement(conditions, propertyPath, value) {
|
|
277
|
-
return this.getRepository().decrement(conditions, propertyPath, value);
|
|
278
|
-
}
|
|
279
135
|
}
|
|
280
136
|
class Collection {
|
|
281
137
|
constructor() { }
|
|
282
138
|
static from(items) {
|
|
283
139
|
return BasicCollection.from(items);
|
|
284
140
|
}
|
|
141
|
+
// Example refactoring of Collection.fromRepository for better type safety
|
|
285
142
|
static fromRepository(entity) {
|
|
286
|
-
|
|
143
|
+
const asyncCollection = AsynchronousCollection.fromRepository(entity);
|
|
144
|
+
// Assuming AsynchronousCollection has a method to get the Repository<T>
|
|
145
|
+
return asyncCollection.getRepository();
|
|
287
146
|
}
|
|
288
147
|
}
|
|
289
148
|
exports.Collection = Collection;
|
|
149
|
+
function InjectRepository(model) {
|
|
150
|
+
return function (object, propertyName, index) {
|
|
151
|
+
let repo;
|
|
152
|
+
try {
|
|
153
|
+
typedi_1.default.registerHandler({
|
|
154
|
+
object,
|
|
155
|
+
propertyName,
|
|
156
|
+
index,
|
|
157
|
+
value: (containerInstance) => {
|
|
158
|
+
const dataSource = containerInstance.get("idatasource");
|
|
159
|
+
repo = dataSource
|
|
160
|
+
.getRepository(model)
|
|
161
|
+
.extend({ paginate: () => { } });
|
|
162
|
+
repo.paginate = async function (options = { take: 10, skip: 0 }) {
|
|
163
|
+
const [data, total] = await this.findAndCount({
|
|
164
|
+
take: options.take || 10,
|
|
165
|
+
skip: options.skip || 0,
|
|
166
|
+
});
|
|
167
|
+
return {
|
|
168
|
+
total,
|
|
169
|
+
totalPage: Math.ceil(total / (options.take || 10)),
|
|
170
|
+
next: options.skip + options.take < total
|
|
171
|
+
? options.skip + options.take
|
|
172
|
+
: null,
|
|
173
|
+
data,
|
|
174
|
+
};
|
|
175
|
+
};
|
|
176
|
+
return repo;
|
|
177
|
+
},
|
|
178
|
+
});
|
|
179
|
+
}
|
|
180
|
+
catch (error) {
|
|
181
|
+
console.log(error);
|
|
182
|
+
if (error.name && error.name == "ServiceNotFoundError") {
|
|
183
|
+
console.log("Database didn't initialized.");
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
};
|
|
187
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const TEST_DATASOURCE_OPTIONS_KEY: unique symbol;
|
package/dist/icore.d.ts
CHANGED
|
@@ -40,6 +40,7 @@ export interface TestApplication {
|
|
|
40
40
|
patch: (url: string, options?: InjectOptions) => TestResponse;
|
|
41
41
|
delete: (url: string, options?: InjectOptions) => TestResponse;
|
|
42
42
|
options: (url: string, options?: InjectOptions) => TestResponse;
|
|
43
|
+
getController?: <T>(controller: Constructor<T>) => T;
|
|
43
44
|
}
|
|
44
45
|
export interface ParamMetaOptions {
|
|
45
46
|
index: number;
|
|
@@ -101,7 +102,7 @@ export interface IAvleonApplication {
|
|
|
101
102
|
run(port?: number): Promise<void>;
|
|
102
103
|
getTestApp(): TestApplication;
|
|
103
104
|
}
|
|
104
|
-
declare class AvleonApplication implements IAvleonApplication {
|
|
105
|
+
export declare class AvleonApplication implements IAvleonApplication {
|
|
105
106
|
private static instance;
|
|
106
107
|
private static buildOptions;
|
|
107
108
|
private app;
|
|
@@ -120,6 +121,7 @@ declare class AvleonApplication implements IAvleonApplication {
|
|
|
120
121
|
private metaCache;
|
|
121
122
|
private multipartOptions;
|
|
122
123
|
private constructor();
|
|
124
|
+
private isTest;
|
|
123
125
|
static getInternalApp(buildOptions: any): AvleonApplication;
|
|
124
126
|
isDevelopment(): boolean;
|
|
125
127
|
private initSwagger;
|
|
@@ -202,8 +204,8 @@ export declare class TestBuilder {
|
|
|
202
204
|
private constructor();
|
|
203
205
|
static createBuilder(): TestBuilder;
|
|
204
206
|
addDatasource(options: DataSourceOptions): void;
|
|
205
|
-
getController
|
|
206
|
-
getService
|
|
207
|
+
private getController;
|
|
208
|
+
private getService;
|
|
207
209
|
getTestApplication(options: TestAppOptions): TestApplication;
|
|
208
210
|
build(app: IAvleonApplication): TestApplication;
|
|
209
211
|
fromApplication(app: IAvleonApplication): TestApplication;
|
package/dist/icore.js
CHANGED
|
@@ -54,7 +54,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
54
54
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
55
55
|
};
|
|
56
56
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
57
|
-
exports.Builder = exports.TestBuilder = void 0;
|
|
57
|
+
exports.Builder = exports.TestBuilder = exports.AvleonApplication = void 0;
|
|
58
58
|
/**
|
|
59
59
|
* @copyright 2024
|
|
60
60
|
* @author Tareq Hossain
|
|
@@ -94,25 +94,23 @@ class AvleonApplication {
|
|
|
94
94
|
this.authorizeMiddleware = undefined;
|
|
95
95
|
this.dataSource = undefined;
|
|
96
96
|
this.metaCache = new Map();
|
|
97
|
-
this.app = (0, fastify_1.default)(
|
|
98
|
-
frameworkErrors: (error, req, res) => { },
|
|
99
|
-
});
|
|
97
|
+
this.app = (0, fastify_1.default)();
|
|
100
98
|
this.appConfig = new config_1.AppConfig();
|
|
101
99
|
// this.app.setValidatorCompiler(() => () => true);
|
|
102
100
|
}
|
|
101
|
+
isTest() { }
|
|
103
102
|
static getInternalApp(buildOptions) {
|
|
103
|
+
let isTestEnv = process.env.NODE_ENV == "test";
|
|
104
104
|
if (!AvleonApplication.instance) {
|
|
105
105
|
AvleonApplication.instance = new AvleonApplication();
|
|
106
106
|
}
|
|
107
107
|
AvleonApplication.buildOptions = buildOptions;
|
|
108
|
-
if (buildOptions.controllers) {
|
|
109
|
-
}
|
|
110
108
|
if (buildOptions.dataSourceOptions) {
|
|
111
109
|
AvleonApplication.instance.dataSourceOptions =
|
|
112
110
|
buildOptions.dataSourceOptions;
|
|
113
111
|
const typeorm = require("typeorm");
|
|
114
112
|
const datasource = new typeorm.DataSource(buildOptions.dataSourceOptions);
|
|
115
|
-
typedi_1.default.set("idatasource", datasource);
|
|
113
|
+
typedi_1.default.set(isTestEnv ? "itestdatasource" : "idatasource", datasource);
|
|
116
114
|
AvleonApplication.instance.dataSource = datasource;
|
|
117
115
|
}
|
|
118
116
|
return AvleonApplication.instance;
|
|
@@ -590,22 +588,8 @@ class AvleonApplication {
|
|
|
590
588
|
}
|
|
591
589
|
getTestApp(buildOptions) {
|
|
592
590
|
try {
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
if (!typeorm) {
|
|
596
|
-
throw new system_exception_1.SystemUseError("TypeOrm not installed");
|
|
597
|
-
}
|
|
598
|
-
typeorm.then(async (t) => {
|
|
599
|
-
try {
|
|
600
|
-
const datasource = new t.DataSource(buildOptions.addDataSource);
|
|
601
|
-
typedi_1.default.set("idatasource", datasource);
|
|
602
|
-
await datasource.initialize();
|
|
603
|
-
}
|
|
604
|
-
catch (error) {
|
|
605
|
-
console.error("Database can't initialized.", error);
|
|
606
|
-
}
|
|
607
|
-
});
|
|
608
|
-
}
|
|
591
|
+
// }
|
|
592
|
+
// this.initializeDatabase();
|
|
609
593
|
this._mapControllers();
|
|
610
594
|
this.rMap.forEach((value, key) => {
|
|
611
595
|
const [m, r] = key.split(":");
|
|
@@ -632,23 +616,31 @@ class AvleonApplication {
|
|
|
632
616
|
return res.status(handledErr.code).send(handledErr);
|
|
633
617
|
});
|
|
634
618
|
// return this.app as any;
|
|
619
|
+
//
|
|
635
620
|
return {
|
|
636
|
-
get: (url, options) => this.app.inject(Object.assign({ method: "GET", url }, options)),
|
|
637
|
-
post: (url, options) => this.app.inject(Object.assign({ method: "POST", url }, options)),
|
|
638
|
-
put: (url, options) => this.app.inject(Object.assign({ method: "PUT", url }, options)),
|
|
639
|
-
patch: (url, options) => this.app.inject(Object.assign({ method: "PATCH", url }, options)),
|
|
640
|
-
delete: (url, options) => this.app.inject(Object.assign({ method: "DELETE", url }, options)),
|
|
641
|
-
options: (url, options) => this.app.inject(Object.assign({ method: "OPTIONS", url }, options)),
|
|
621
|
+
get: async (url, options) => this.app.inject(Object.assign({ method: "GET", url }, options)),
|
|
622
|
+
post: async (url, options) => this.app.inject(Object.assign({ method: "POST", url }, options)),
|
|
623
|
+
put: async (url, options) => this.app.inject(Object.assign({ method: "PUT", url }, options)),
|
|
624
|
+
patch: async (url, options) => this.app.inject(Object.assign({ method: "PATCH", url }, options)),
|
|
625
|
+
delete: async (url, options) => this.app.inject(Object.assign({ method: "DELETE", url }, options)),
|
|
626
|
+
options: async (url, options) => this.app.inject(Object.assign({ method: "OPTIONS", url }, options)),
|
|
627
|
+
getController: (controller) => {
|
|
628
|
+
return typedi_1.default.get(controller);
|
|
629
|
+
},
|
|
642
630
|
};
|
|
643
631
|
}
|
|
644
632
|
catch (error) {
|
|
633
|
+
console.log(error);
|
|
645
634
|
throw new system_exception_1.SystemUseError("Can't get test appliction");
|
|
646
635
|
}
|
|
647
636
|
}
|
|
648
637
|
}
|
|
638
|
+
exports.AvleonApplication = AvleonApplication;
|
|
649
639
|
AvleonApplication.buildOptions = {};
|
|
650
640
|
class TestBuilder {
|
|
651
|
-
constructor() {
|
|
641
|
+
constructor() {
|
|
642
|
+
process.env.NODE_ENV = "test";
|
|
643
|
+
}
|
|
652
644
|
static createBuilder() {
|
|
653
645
|
if (!TestBuilder.instance) {
|
|
654
646
|
TestBuilder.instance = new TestBuilder();
|
|
@@ -669,8 +661,7 @@ class TestBuilder {
|
|
|
669
661
|
dataSourceOptions: this.dataSourceOptions,
|
|
670
662
|
});
|
|
671
663
|
app.mapControllers([...options.controllers]);
|
|
672
|
-
|
|
673
|
-
return fa;
|
|
664
|
+
return app.getTestApp();
|
|
674
665
|
}
|
|
675
666
|
build(app) {
|
|
676
667
|
return app.getTestApp();
|
package/dist/index.d.ts
CHANGED
package/dist/index.js
CHANGED
|
@@ -48,6 +48,7 @@ exports.Container = exports.GetSchema = exports.validateRequestBody = exports.in
|
|
|
48
48
|
*/
|
|
49
49
|
const sw = __importStar(require("./swagger-schema"));
|
|
50
50
|
__exportStar(require("./icore"), exports);
|
|
51
|
+
__exportStar(require("./testing"), exports);
|
|
51
52
|
var helpers_1 = require("./helpers");
|
|
52
53
|
Object.defineProperty(exports, "inject", { enumerable: true, get: function () { return helpers_1.inject; } });
|
|
53
54
|
Object.defineProperty(exports, "validateRequestBody", { enumerable: true, get: function () { return helpers_1.validateRequestBody; } });
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import "reflect-metadata";
|
|
2
|
+
import { DataSourceOptions } from "typeorm";
|
|
3
|
+
export declare class AvleonTestUtility {
|
|
4
|
+
private static testDataSource;
|
|
5
|
+
private static testContainer;
|
|
6
|
+
/**
|
|
7
|
+
* Initialize test environment
|
|
8
|
+
*/
|
|
9
|
+
static init(options?: {
|
|
10
|
+
dataSourceOptions?: DataSourceOptions;
|
|
11
|
+
resetContainer?: boolean;
|
|
12
|
+
}): Promise<typeof AvleonTestUtility>;
|
|
13
|
+
/**
|
|
14
|
+
* Mock a dependency for testing
|
|
15
|
+
* @param token Dependency token
|
|
16
|
+
* @param mockImplementation Mock implementation
|
|
17
|
+
*/
|
|
18
|
+
static mockDependency<T>(token: any, mockImplementation: T): T;
|
|
19
|
+
/**
|
|
20
|
+
* Create an isolated test instance of a class
|
|
21
|
+
* @param ClassType Class to instantiate
|
|
22
|
+
* @param overrides Optional property overrides
|
|
23
|
+
*/
|
|
24
|
+
static createTestInstance<T>(ClassType: new (...args: any[]) => T, overrides?: Partial<T>): T;
|
|
25
|
+
/**
|
|
26
|
+
* Cleanup test environment
|
|
27
|
+
*/
|
|
28
|
+
static cleanup(): Promise<void>;
|
|
29
|
+
}
|
|
30
|
+
export declare class AvleonTestBuilder {
|
|
31
|
+
private controllers;
|
|
32
|
+
private testOptions;
|
|
33
|
+
private mocks;
|
|
34
|
+
/**
|
|
35
|
+
* Add controllers for testing
|
|
36
|
+
* @param controllers Controllers to add
|
|
37
|
+
*/
|
|
38
|
+
addControllers(...controllers: any[]): this;
|
|
39
|
+
/**
|
|
40
|
+
* Mock a dependency
|
|
41
|
+
* @param token Dependency token
|
|
42
|
+
* @param mockImplementation Mock implementation
|
|
43
|
+
*/
|
|
44
|
+
mockDependency(token: any, mockImplementation: any): this;
|
|
45
|
+
/**
|
|
46
|
+
* Set test options
|
|
47
|
+
* @param options Test configuration options
|
|
48
|
+
*/
|
|
49
|
+
setOptions(options: any): this;
|
|
50
|
+
/**
|
|
51
|
+
* Build test application
|
|
52
|
+
*/
|
|
53
|
+
build(): Promise<import("./icore").TestApplication>;
|
|
54
|
+
}
|
|
55
|
+
export declare function UnitTest(): (target: any, propertyKey: string, descriptor: PropertyDescriptor) => PropertyDescriptor;
|
package/dist/testing.js
ADDED
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.AvleonTestBuilder = exports.AvleonTestUtility = void 0;
|
|
4
|
+
exports.UnitTest = UnitTest;
|
|
5
|
+
require("reflect-metadata");
|
|
6
|
+
const typedi_1 = require("typedi");
|
|
7
|
+
const typeorm_1 = require("typeorm");
|
|
8
|
+
const icore_1 = require("./icore");
|
|
9
|
+
// Enhanced Test Utilities
|
|
10
|
+
class AvleonTestUtility {
|
|
11
|
+
/**
|
|
12
|
+
* Initialize test environment
|
|
13
|
+
*/
|
|
14
|
+
static async init(options) {
|
|
15
|
+
// Reset container if specified
|
|
16
|
+
if (options === null || options === void 0 ? void 0 : options.resetContainer) {
|
|
17
|
+
this.testContainer = typedi_1.Container;
|
|
18
|
+
this.testContainer.reset();
|
|
19
|
+
}
|
|
20
|
+
// Initialize test database if options provided
|
|
21
|
+
if (options === null || options === void 0 ? void 0 : options.dataSourceOptions) {
|
|
22
|
+
this.testDataSource = new typeorm_1.DataSource(Object.assign(Object.assign({}, options.dataSourceOptions), { logging: false }));
|
|
23
|
+
await this.testDataSource.initialize();
|
|
24
|
+
await this.testDataSource.synchronize(true); // Create schema
|
|
25
|
+
}
|
|
26
|
+
return this;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Mock a dependency for testing
|
|
30
|
+
* @param token Dependency token
|
|
31
|
+
* @param mockImplementation Mock implementation
|
|
32
|
+
*/
|
|
33
|
+
static mockDependency(token, mockImplementation) {
|
|
34
|
+
typedi_1.Container.set(token, mockImplementation);
|
|
35
|
+
return mockImplementation;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Create an isolated test instance of a class
|
|
39
|
+
* @param ClassType Class to instantiate
|
|
40
|
+
* @param overrides Optional property overrides
|
|
41
|
+
*/
|
|
42
|
+
static createTestInstance(ClassType, overrides = {}) {
|
|
43
|
+
const instance = typedi_1.Container.get(ClassType);
|
|
44
|
+
// Apply overrides
|
|
45
|
+
Object.keys(overrides).forEach((key) => {
|
|
46
|
+
instance[key] = overrides[key];
|
|
47
|
+
});
|
|
48
|
+
return instance;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Cleanup test environment
|
|
52
|
+
*/
|
|
53
|
+
static async cleanup() {
|
|
54
|
+
if (this.testDataSource) {
|
|
55
|
+
await this.testDataSource.dropDatabase();
|
|
56
|
+
await this.testDataSource.destroy();
|
|
57
|
+
this.testDataSource = null;
|
|
58
|
+
}
|
|
59
|
+
// Reset container
|
|
60
|
+
typedi_1.Container.reset();
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
exports.AvleonTestUtility = AvleonTestUtility;
|
|
64
|
+
AvleonTestUtility.testDataSource = null;
|
|
65
|
+
// Enhanced Test Builder
|
|
66
|
+
class AvleonTestBuilder {
|
|
67
|
+
constructor() {
|
|
68
|
+
this.controllers = [];
|
|
69
|
+
this.testOptions = {};
|
|
70
|
+
this.mocks = new Map();
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Add controllers for testing
|
|
74
|
+
* @param controllers Controllers to add
|
|
75
|
+
*/
|
|
76
|
+
addControllers(...controllers) {
|
|
77
|
+
this.controllers.push(...controllers);
|
|
78
|
+
return this;
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Mock a dependency
|
|
82
|
+
* @param token Dependency token
|
|
83
|
+
* @param mockImplementation Mock implementation
|
|
84
|
+
*/
|
|
85
|
+
mockDependency(token, mockImplementation) {
|
|
86
|
+
this.mocks.set(token, mockImplementation);
|
|
87
|
+
return this;
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Set test options
|
|
91
|
+
* @param options Test configuration options
|
|
92
|
+
*/
|
|
93
|
+
setOptions(options) {
|
|
94
|
+
this.testOptions = Object.assign(Object.assign({}, this.testOptions), options);
|
|
95
|
+
return this;
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Build test application
|
|
99
|
+
*/
|
|
100
|
+
async build() {
|
|
101
|
+
// Apply mocks
|
|
102
|
+
this.mocks.forEach((mock, token) => {
|
|
103
|
+
typedi_1.Container.set(token, mock);
|
|
104
|
+
});
|
|
105
|
+
// Initialize test utility
|
|
106
|
+
await AvleonTestUtility.init({
|
|
107
|
+
dataSourceOptions: this.testOptions.dataSourceOptions,
|
|
108
|
+
resetContainer: true,
|
|
109
|
+
});
|
|
110
|
+
// Create test application
|
|
111
|
+
const app = icore_1.AvleonApplication.getInternalApp({
|
|
112
|
+
dataSourceOptions: this.testOptions.dataSourceOptions,
|
|
113
|
+
});
|
|
114
|
+
// Map controllers
|
|
115
|
+
app.mapControllers(this.controllers);
|
|
116
|
+
// Get test application
|
|
117
|
+
return app.getTestApp();
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
exports.AvleonTestBuilder = AvleonTestBuilder;
|
|
121
|
+
// Example Usage Decorator
|
|
122
|
+
function UnitTest() {
|
|
123
|
+
return (target, propertyKey, descriptor) => {
|
|
124
|
+
const originalMethod = descriptor.value;
|
|
125
|
+
descriptor.value = async function (...args) {
|
|
126
|
+
try {
|
|
127
|
+
// Pre-test setup
|
|
128
|
+
await AvleonTestUtility.init();
|
|
129
|
+
// Execute test
|
|
130
|
+
const result = await originalMethod.apply(this, args);
|
|
131
|
+
// Post-test cleanup
|
|
132
|
+
await AvleonTestUtility.cleanup();
|
|
133
|
+
return result;
|
|
134
|
+
}
|
|
135
|
+
catch (error) {
|
|
136
|
+
// Ensure cleanup even if test fails
|
|
137
|
+
await AvleonTestUtility.cleanup();
|
|
138
|
+
throw error;
|
|
139
|
+
}
|
|
140
|
+
};
|
|
141
|
+
return descriptor;
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
//
|
|
145
|
+
// // Example of Unit and Integration Test
|
|
146
|
+
// class UserServiceTest {
|
|
147
|
+
// @UnitTest()
|
|
148
|
+
// async testUserCreation() {
|
|
149
|
+
// // Mock UserRepository
|
|
150
|
+
// const mockRepo = AvleonTestUtility.mockDependency(
|
|
151
|
+
// UserRepository,
|
|
152
|
+
// { create: jest.fn() }
|
|
153
|
+
// );
|
|
154
|
+
//
|
|
155
|
+
// // Create test instance
|
|
156
|
+
// const userService = AvleonTestUtility.createTestInstance(UserService);
|
|
157
|
+
//
|
|
158
|
+
// // Perform test
|
|
159
|
+
// const result = await userService.createUser({
|
|
160
|
+
// name: 'Test User',
|
|
161
|
+
// email: 'test@example.com'
|
|
162
|
+
// });
|
|
163
|
+
//
|
|
164
|
+
// // Assertions
|
|
165
|
+
// expect(mockRepo.create).toHaveBeenCalledWith(expect.any(Object));
|
|
166
|
+
// }
|
|
167
|
+
// }
|
|
168
|
+
//
|
|
169
|
+
// // Enhanced E2E Testing Example
|
|
170
|
+
// class E2EUserControllerTest {
|
|
171
|
+
// async testUserRegistration() {
|
|
172
|
+
// // Build test application
|
|
173
|
+
// const testApp = await new AvleonTestBuilder()
|
|
174
|
+
// .addControllers(UserController)
|
|
175
|
+
// .mockDependency(AuthService, mockAuthService)
|
|
176
|
+
// .setOptions({
|
|
177
|
+
// dataSourceOptions: testDatabaseConfig
|
|
178
|
+
// })
|
|
179
|
+
// .build();
|
|
180
|
+
//
|
|
181
|
+
// // Perform HTTP request
|
|
182
|
+
// const response = await testApp.post('/users/register', {
|
|
183
|
+
// payload: {
|
|
184
|
+
// name: 'John Doe',
|
|
185
|
+
// email: 'john@example.com'
|
|
186
|
+
// }
|
|
187
|
+
// });
|
|
188
|
+
//
|
|
189
|
+
// // Assertions
|
|
190
|
+
// expect(response.statusCode).toBe(201);
|
|
191
|
+
// expect(response.json()).toHaveProperty('userId');
|
|
192
|
+
// }
|
|
193
|
+
// }
|