@reldens/storage 0.88.0 → 0.89.0
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/.claude/driver-methods-complete-analysis.md +1978 -0
- package/.claude/test-architecture.md +973 -0
- package/CLAUDE.md +121 -1
- package/bin/reldens-storage.js +1 -1
- package/lib/base-driver.js +8 -0
- package/lib/entities-generator.js +1 -3
- package/lib/entity-templates/mikro-orm-model.template +2 -1
- package/lib/generators/models-generation.js +126 -5
- package/lib/mikro-orm/mikro-orm-data-server.js +24 -10
- package/lib/mikro-orm/mikro-orm-driver.js +184 -116
- package/lib/objection-js/objection-js-driver.js +111 -19
- package/lib/prisma/prisma-data-server.js +3 -2
- package/lib/prisma/prisma-driver.js +12 -10
- package/lib/prisma/prisma-filter-processor.js +193 -146
- package/lib/prisma/prisma-metadata-loader.js +0 -4
- package/lib/prisma/prisma-relation-resolver.js +292 -267
- package/lib/prisma/prisma-type-caster.js +280 -260
- package/package.json +15 -6
- package/tests/.env.test.example +7 -0
- package/tests/fixtures/categories-fixtures.js +164 -0
- package/tests/fixtures/expected-entities-mikro-orm/entities/test-categories-entity.js +70 -0
- package/tests/fixtures/expected-entities-mikro-orm/entities/test-products-entity.js +95 -0
- package/tests/fixtures/expected-entities-mikro-orm/entities/test-reviews-entity.js +84 -0
- package/tests/fixtures/expected-entities-mikro-orm/entities-config.js +17 -0
- package/tests/fixtures/expected-entities-mikro-orm/entities-translations.js +13 -0
- package/tests/fixtures/expected-entities-mikro-orm/models/mikro-orm/registered-models-mikro-orm.js +23 -0
- package/tests/fixtures/expected-entities-mikro-orm/models/mikro-orm/test-categories-model.js +57 -0
- package/tests/fixtures/expected-entities-mikro-orm/models/mikro-orm/test-products-model.js +79 -0
- package/tests/fixtures/expected-entities-mikro-orm/models/mikro-orm/test-reviews-model.js +70 -0
- package/tests/fixtures/expected-entities-objection-js/entities/test-categories-entity.js +70 -0
- package/tests/fixtures/expected-entities-objection-js/entities/test-products-entity.js +95 -0
- package/tests/fixtures/expected-entities-objection-js/entities/test-reviews-entity.js +84 -0
- package/tests/fixtures/expected-entities-objection-js/entities-config.js +17 -0
- package/tests/fixtures/expected-entities-objection-js/entities-translations.js +13 -0
- package/tests/fixtures/expected-entities-objection-js/models/objection-js/registered-models-objection-js.js +23 -0
- package/tests/fixtures/expected-entities-objection-js/models/objection-js/test-categories-model.js +33 -0
- package/tests/fixtures/expected-entities-objection-js/models/objection-js/test-products-model.js +42 -0
- package/tests/fixtures/expected-entities-objection-js/models/objection-js/test-reviews-model.js +33 -0
- package/tests/fixtures/expected-entities-prisma/entities/test-categories-entity.js +70 -0
- package/tests/fixtures/expected-entities-prisma/entities/test-products-entity.js +95 -0
- package/tests/fixtures/expected-entities-prisma/entities/test-reviews-entity.js +84 -0
- package/tests/fixtures/expected-entities-prisma/entities-config.js +17 -0
- package/tests/fixtures/expected-entities-prisma/entities-translations.js +13 -0
- package/tests/fixtures/expected-entities-prisma/models/prisma/registered-models-prisma.js +23 -0
- package/tests/fixtures/expected-entities-prisma/models/prisma/test-categories-model.js +43 -0
- package/tests/fixtures/expected-entities-prisma/models/prisma/test-products-model.js +50 -0
- package/tests/fixtures/expected-entities-prisma/models/prisma/test-reviews-model.js +46 -0
- package/tests/fixtures/products-fixtures.js +119 -0
- package/tests/fixtures/reviews-fixtures.js +42 -0
- package/tests/fixtures/sql/test-schema.sql +59 -0
- package/tests/integration/test-drivers.js +257 -0
- package/tests/integration/test-nested-filters.js +408 -0
- package/tests/integration/test-relations.js +349 -0
- package/tests/run-tests.js +213 -0
- package/tests/unit/test-drivers.js +89 -0
- package/tests/unit/test-entities-generator.js +533 -0
- package/tests/unit/test-entity-manager.js +128 -0
- package/tests/unit/test-type-mapper.js +232 -0
- package/tests/utils/custom-reporter.js +73 -0
- package/tests/utils/driver-registry.js +144 -0
- package/tests/utils/prisma-subprocess-worker.js +150 -0
- package/tests/utils/test-helpers.js +726 -0
- package/tests/utils/test-runner.js +63 -0
|
@@ -0,0 +1,349 @@
|
|
|
1
|
+
/**
|
|
2
|
+
*
|
|
3
|
+
* Reldens - Relations Test
|
|
4
|
+
* Tests relation loading across all three storage drivers
|
|
5
|
+
*
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
const { TestRunner, assert } = require('../utils/test-runner');
|
|
9
|
+
const { TestHelpers } = require('../utils/test-helpers');
|
|
10
|
+
const { CategoriesFixtures } = require('../fixtures/categories-fixtures');
|
|
11
|
+
const { ProductsFixtures } = require('../fixtures/products-fixtures');
|
|
12
|
+
const { ReviewsFixtures } = require('../fixtures/reviews-fixtures');
|
|
13
|
+
|
|
14
|
+
class RelationsTest
|
|
15
|
+
{
|
|
16
|
+
|
|
17
|
+
constructor(dataServer, repos, driverName)
|
|
18
|
+
{
|
|
19
|
+
this.dataServer = dataServer;
|
|
20
|
+
this.categoriesRepo = repos.testCategories;
|
|
21
|
+
this.productsRepo = repos.testProducts;
|
|
22
|
+
this.driverName = driverName;
|
|
23
|
+
this.runner = new TestRunner();
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
async run()
|
|
27
|
+
{
|
|
28
|
+
this.runner.suite('Relations - Driver: '+this.driverName);
|
|
29
|
+
await this.testLoadWithRelations();
|
|
30
|
+
await this.testLoadAllWithRelations();
|
|
31
|
+
await this.testLoadByWithRelations();
|
|
32
|
+
await this.testLoadByIdWithRelations();
|
|
33
|
+
await this.testLoadOneWithRelations();
|
|
34
|
+
await this.testLoadOneByWithRelations();
|
|
35
|
+
await this.testCountWithRelations();
|
|
36
|
+
await this.testCreateWithRelations();
|
|
37
|
+
await this.testNestedRelations();
|
|
38
|
+
await this.testRelationStringParsing();
|
|
39
|
+
return this.runner.getResults();
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
async testLoadWithRelations()
|
|
43
|
+
{
|
|
44
|
+
this.runner.group('loadWithRelations');
|
|
45
|
+
await TestHelpers.cleanDatabase(this.dataServer);
|
|
46
|
+
await TestHelpers.insertFixturesViaRawSQL(this.dataServer, 'test_categories', [
|
|
47
|
+
CategoriesFixtures.category_relations_1
|
|
48
|
+
]);
|
|
49
|
+
await TestHelpers.insertFixturesViaRawSQL(this.dataServer, 'test_products', [
|
|
50
|
+
ProductsFixtures.product_relations_1
|
|
51
|
+
]);
|
|
52
|
+
await TestHelpers.insertFixturesViaRawSQL(this.dataServer, 'test_reviews', [
|
|
53
|
+
ReviewsFixtures.review_relations_1
|
|
54
|
+
]);
|
|
55
|
+
await this.runner.test('should load records with single relation', async () => {
|
|
56
|
+
let results = await this.categoriesRepo.loadWithRelations({id: 1600}, ['related_test_products']);
|
|
57
|
+
assert.ok(results);
|
|
58
|
+
assert.ok(results.length > 0);
|
|
59
|
+
let category = results[0];
|
|
60
|
+
assert.ok(category.related_test_products);
|
|
61
|
+
assert.ok(Array.isArray(category.related_test_products));
|
|
62
|
+
assert.strictEqual(category.related_test_products.length, 1);
|
|
63
|
+
});
|
|
64
|
+
await this.runner.test('should load records with multiple relations', async () => {
|
|
65
|
+
let results = await this.productsRepo.loadWithRelations(
|
|
66
|
+
{id: 2600},
|
|
67
|
+
['related_test_categories', 'related_test_reviews']
|
|
68
|
+
);
|
|
69
|
+
assert.ok(results);
|
|
70
|
+
assert.ok(results.length > 0);
|
|
71
|
+
let product = results[0];
|
|
72
|
+
assert.ok(product.related_test_categories);
|
|
73
|
+
assert.ok(product.related_test_reviews);
|
|
74
|
+
assert.ok(Array.isArray(product.related_test_reviews));
|
|
75
|
+
});
|
|
76
|
+
await this.runner.test('should load with empty filters', async () => {
|
|
77
|
+
let results = await this.categoriesRepo.loadWithRelations({}, ['related_test_products']);
|
|
78
|
+
assert.ok(results);
|
|
79
|
+
assert.strictEqual(results.length, 1);
|
|
80
|
+
});
|
|
81
|
+
await this.runner.test('should filter by nested relation properties', async () => {
|
|
82
|
+
await TestHelpers.cleanDatabase(this.dataServer);
|
|
83
|
+
await TestHelpers.insertFixturesViaRawSQL(this.dataServer, 'test_categories', [
|
|
84
|
+
CategoriesFixtures.category_relations_1,
|
|
85
|
+
CategoriesFixtures.category_relations_2
|
|
86
|
+
]);
|
|
87
|
+
await TestHelpers.insertFixturesViaRawSQL(this.dataServer, 'test_products', [
|
|
88
|
+
{...ProductsFixtures.product_relations_1, id: 2600, category_id: 1600, price: 149.99},
|
|
89
|
+
{...ProductsFixtures.product_relations_2, id: 2601, category_id: 1601, price: 49.99}
|
|
90
|
+
]);
|
|
91
|
+
let results = await this.categoriesRepo.loadWithRelations({
|
|
92
|
+
related_test_products: {
|
|
93
|
+
price: {operator: 'gt', value: 100}
|
|
94
|
+
}
|
|
95
|
+
}, ['related_test_products']);
|
|
96
|
+
assert.ok(results);
|
|
97
|
+
assert.strictEqual(results.length, 1);
|
|
98
|
+
assert.strictEqual(results[0].id, 1600);
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
async testLoadAllWithRelations()
|
|
103
|
+
{
|
|
104
|
+
this.runner.group('loadAllWithRelations');
|
|
105
|
+
await TestHelpers.cleanDatabase(this.dataServer);
|
|
106
|
+
await TestHelpers.insertFixturesViaRawSQL(this.dataServer, 'test_categories', [
|
|
107
|
+
CategoriesFixtures.category_relations_1,
|
|
108
|
+
CategoriesFixtures.category_relations_2
|
|
109
|
+
]);
|
|
110
|
+
await TestHelpers.insertFixturesViaRawSQL(this.dataServer, 'test_products', [
|
|
111
|
+
ProductsFixtures.product_relations_1,
|
|
112
|
+
ProductsFixtures.product_relations_2
|
|
113
|
+
]);
|
|
114
|
+
await this.runner.test('should load all records with relations', async () => {
|
|
115
|
+
let results = await this.categoriesRepo.loadAllWithRelations(['related_test_products']);
|
|
116
|
+
assert.ok(results);
|
|
117
|
+
assert.strictEqual(results.length, 2);
|
|
118
|
+
for(let category of results){
|
|
119
|
+
assert.ok(category.related_test_products);
|
|
120
|
+
assert.ok(Array.isArray(category.related_test_products));
|
|
121
|
+
}
|
|
122
|
+
});
|
|
123
|
+
await this.runner.test('should respect limit with relations', async () => {
|
|
124
|
+
this.categoriesRepo.limit = 1;
|
|
125
|
+
let results = await this.categoriesRepo.loadWithRelations({}, ['related_test_products']);
|
|
126
|
+
assert.strictEqual(results.length, 1);
|
|
127
|
+
assert.ok(results[0].related_test_products);
|
|
128
|
+
this.categoriesRepo.limit = 0;
|
|
129
|
+
});
|
|
130
|
+
await this.runner.test('should respect sortBy with relations', async () => {
|
|
131
|
+
this.categoriesRepo.sortBy = 'name';
|
|
132
|
+
this.categoriesRepo.sortDirection = 'DESC';
|
|
133
|
+
let results = await this.categoriesRepo.loadWithRelations({}, ['related_test_products']);
|
|
134
|
+
assert.ok(results.length > 0);
|
|
135
|
+
assert.ok(results[0].related_test_products);
|
|
136
|
+
this.categoriesRepo.sortBy = false;
|
|
137
|
+
this.categoriesRepo.sortDirection = 'ASC';
|
|
138
|
+
});
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
async testLoadByWithRelations()
|
|
142
|
+
{
|
|
143
|
+
this.runner.group('loadByWithRelations');
|
|
144
|
+
await TestHelpers.cleanDatabase(this.dataServer);
|
|
145
|
+
await TestHelpers.insertFixturesViaRawSQL(this.dataServer, 'test_categories', [
|
|
146
|
+
CategoriesFixtures.category_relations_1
|
|
147
|
+
]);
|
|
148
|
+
await TestHelpers.insertFixturesViaRawSQL(this.dataServer, 'test_products', [
|
|
149
|
+
ProductsFixtures.product_relations_1
|
|
150
|
+
]);
|
|
151
|
+
await this.runner.test('should load records by field with relations', async () => {
|
|
152
|
+
let results = await this.categoriesRepo.loadByWithRelations('slug', 'relations-test-1', ['related_test_products']);
|
|
153
|
+
assert.ok(results);
|
|
154
|
+
assert.ok(results.length > 0);
|
|
155
|
+
let category = results[0];
|
|
156
|
+
assert.strictEqual(category.slug, 'relations-test-1');
|
|
157
|
+
assert.ok(category.related_test_products);
|
|
158
|
+
assert.ok(Array.isArray(category.related_test_products));
|
|
159
|
+
});
|
|
160
|
+
await this.runner.test('should return empty array when no match with relations', async () => {
|
|
161
|
+
let results = await this.categoriesRepo.loadByWithRelations('slug', 'nonexistent', ['related_test_products']);
|
|
162
|
+
assert.ok(results);
|
|
163
|
+
assert.strictEqual(results.length, 0);
|
|
164
|
+
});
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
async testLoadByIdWithRelations()
|
|
168
|
+
{
|
|
169
|
+
this.runner.group('loadByIdWithRelations');
|
|
170
|
+
await TestHelpers.cleanDatabase(this.dataServer);
|
|
171
|
+
await TestHelpers.insertFixturesViaRawSQL(this.dataServer, 'test_categories', [
|
|
172
|
+
CategoriesFixtures.category_relations_1,
|
|
173
|
+
CategoriesFixtures.category_relations_2
|
|
174
|
+
]);
|
|
175
|
+
await TestHelpers.insertFixturesViaRawSQL(this.dataServer, 'test_products', [
|
|
176
|
+
ProductsFixtures.product_relations_1,
|
|
177
|
+
ProductsFixtures.product_relations_2
|
|
178
|
+
]);
|
|
179
|
+
await this.runner.test('should load single record by ID with relations', async () => {
|
|
180
|
+
let result = await this.categoriesRepo.loadByIdWithRelations(1600, ['related_test_products']);
|
|
181
|
+
assert.ok(result);
|
|
182
|
+
assert.strictEqual(result.id, 1600);
|
|
183
|
+
assert.ok(result.related_test_products);
|
|
184
|
+
assert.ok(Array.isArray(result.related_test_products));
|
|
185
|
+
assert.strictEqual(result.related_test_products.length, 1);
|
|
186
|
+
});
|
|
187
|
+
await this.runner.test('should return null when ID not found with relations', async () => {
|
|
188
|
+
let result = await this.categoriesRepo.loadByIdWithRelations(99999, ['related_test_products']);
|
|
189
|
+
assert.ok(!result);
|
|
190
|
+
});
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
async testLoadOneWithRelations()
|
|
194
|
+
{
|
|
195
|
+
this.runner.group('loadOneWithRelations');
|
|
196
|
+
await TestHelpers.cleanDatabase(this.dataServer);
|
|
197
|
+
await TestHelpers.insertFixturesViaRawSQL(this.dataServer, 'test_categories', [
|
|
198
|
+
CategoriesFixtures.category_relations_1,
|
|
199
|
+
CategoriesFixtures.category_relations_2
|
|
200
|
+
]);
|
|
201
|
+
await TestHelpers.insertFixturesViaRawSQL(this.dataServer, 'test_products', [
|
|
202
|
+
ProductsFixtures.product_relations_1
|
|
203
|
+
]);
|
|
204
|
+
await this.runner.test('should load one record with relations', async () => {
|
|
205
|
+
let result = await this.categoriesRepo.loadOneWithRelations({is_active: 1}, ['related_test_products']);
|
|
206
|
+
assert.ok(result);
|
|
207
|
+
assert.strictEqual(result.is_active, 1);
|
|
208
|
+
assert.ok(result.related_test_products);
|
|
209
|
+
assert.ok(Array.isArray(result.related_test_products));
|
|
210
|
+
});
|
|
211
|
+
await this.runner.test('should return null when no match with relations', async () => {
|
|
212
|
+
let result = await this.categoriesRepo.loadOneWithRelations({slug: 'nonexistent'}, ['related_test_products']);
|
|
213
|
+
assert.ok(!result);
|
|
214
|
+
});
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
async testLoadOneByWithRelations()
|
|
218
|
+
{
|
|
219
|
+
this.runner.group('loadOneByWithRelations');
|
|
220
|
+
await TestHelpers.cleanDatabase(this.dataServer);
|
|
221
|
+
await TestHelpers.insertFixturesViaRawSQL(this.dataServer, 'test_categories', [
|
|
222
|
+
CategoriesFixtures.category_relations_1
|
|
223
|
+
]);
|
|
224
|
+
await TestHelpers.insertFixturesViaRawSQL(this.dataServer, 'test_products', [
|
|
225
|
+
ProductsFixtures.product_relations_1
|
|
226
|
+
]);
|
|
227
|
+
await this.runner.test('should load one record by field with relations', async () => {
|
|
228
|
+
let result = await this.categoriesRepo.loadOneByWithRelations('slug', 'relations-test-1', ['related_test_products']);
|
|
229
|
+
assert.ok(result);
|
|
230
|
+
assert.strictEqual(result.slug, 'relations-test-1');
|
|
231
|
+
assert.ok(result.related_test_products);
|
|
232
|
+
assert.ok(Array.isArray(result.related_test_products));
|
|
233
|
+
});
|
|
234
|
+
await this.runner.test('should return null when no match by field with relations', async () => {
|
|
235
|
+
let result = await this.categoriesRepo.loadOneByWithRelations('slug', 'nonexistent', ['related_test_products']);
|
|
236
|
+
assert.ok(!result);
|
|
237
|
+
});
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
async testCountWithRelations()
|
|
241
|
+
{
|
|
242
|
+
this.runner.group('countWithRelations');
|
|
243
|
+
await TestHelpers.cleanDatabase(this.dataServer);
|
|
244
|
+
await TestHelpers.insertFixturesViaRawSQL(this.dataServer, 'test_categories', [
|
|
245
|
+
CategoriesFixtures.category_relations_1,
|
|
246
|
+
CategoriesFixtures.category_relations_2,
|
|
247
|
+
CategoriesFixtures.category_filters_3
|
|
248
|
+
]);
|
|
249
|
+
await TestHelpers.insertFixturesViaRawSQL(this.dataServer, 'test_products', [
|
|
250
|
+
ProductsFixtures.product_relations_1
|
|
251
|
+
]);
|
|
252
|
+
await this.runner.test('should count records with relation filter', async () => {
|
|
253
|
+
let count = await this.categoriesRepo.countWithRelations({is_active: 1}, ['related_test_products']);
|
|
254
|
+
assert.strictEqual(count, 2);
|
|
255
|
+
});
|
|
256
|
+
await this.runner.test('should count all records with relations', async () => {
|
|
257
|
+
let count = await this.categoriesRepo.countWithRelations({}, ['related_test_products']);
|
|
258
|
+
assert.strictEqual(count, 3);
|
|
259
|
+
});
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
async testCreateWithRelations()
|
|
263
|
+
{
|
|
264
|
+
this.runner.group('createWithRelations');
|
|
265
|
+
await TestHelpers.cleanDatabase(this.dataServer);
|
|
266
|
+
await this.runner.test('should create record with nested relations', async () => {
|
|
267
|
+
let categoryWithProducts = {
|
|
268
|
+
...CategoriesFixtures.category_create_nested,
|
|
269
|
+
related_test_products: [ProductsFixtures.product_create_nested]
|
|
270
|
+
};
|
|
271
|
+
let created = await this.categoriesRepo.createWithRelations(categoryWithProducts, ['related_test_products']);
|
|
272
|
+
assert.ok(created);
|
|
273
|
+
assert.ok(created.id);
|
|
274
|
+
let loaded = await this.categoriesRepo.loadByIdWithRelations(created.id, ['related_test_products']);
|
|
275
|
+
assert.ok(loaded);
|
|
276
|
+
assert.ok(loaded.related_test_products);
|
|
277
|
+
assert.ok(loaded.related_test_products.length > 0);
|
|
278
|
+
});
|
|
279
|
+
await TestHelpers.cleanDatabase(this.dataServer);
|
|
280
|
+
await this.runner.test('should create record without relations when not provided', async () => {
|
|
281
|
+
let created = await this.categoriesRepo.createWithRelations(
|
|
282
|
+
CategoriesFixtures.category_create_nested,
|
|
283
|
+
['related_test_products']
|
|
284
|
+
);
|
|
285
|
+
assert.ok(created);
|
|
286
|
+
assert.ok(created.id);
|
|
287
|
+
});
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
async testNestedRelations()
|
|
291
|
+
{
|
|
292
|
+
this.runner.group('Nested Relations');
|
|
293
|
+
await TestHelpers.cleanDatabase(this.dataServer);
|
|
294
|
+
await TestHelpers.insertFixturesViaRawSQL(this.dataServer, 'test_categories', [
|
|
295
|
+
CategoriesFixtures.category_relations_1
|
|
296
|
+
]);
|
|
297
|
+
await TestHelpers.insertFixturesViaRawSQL(this.dataServer, 'test_products', [
|
|
298
|
+
ProductsFixtures.product_relations_1
|
|
299
|
+
]);
|
|
300
|
+
await TestHelpers.insertFixturesViaRawSQL(this.dataServer, 'test_reviews', [
|
|
301
|
+
ReviewsFixtures.review_relations_1
|
|
302
|
+
]);
|
|
303
|
+
await this.runner.test('should load nested relations (category > products > reviews)', async () => {
|
|
304
|
+
let results = await this.categoriesRepo.loadWithRelations(
|
|
305
|
+
{id: 1600},
|
|
306
|
+
['related_test_products.related_test_reviews']
|
|
307
|
+
);
|
|
308
|
+
assert.ok(results);
|
|
309
|
+
assert.ok(results.length > 0);
|
|
310
|
+
let category = results[0];
|
|
311
|
+
assert.ok(category.related_test_products);
|
|
312
|
+
assert.ok(category.related_test_products.length > 0);
|
|
313
|
+
let product = category.related_test_products[0];
|
|
314
|
+
assert.ok(product.related_test_reviews);
|
|
315
|
+
});
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
async testRelationStringParsing()
|
|
319
|
+
{
|
|
320
|
+
this.runner.group('Relation String Parsing');
|
|
321
|
+
await TestHelpers.cleanDatabase(this.dataServer);
|
|
322
|
+
await TestHelpers.insertFixturesViaRawSQL(this.dataServer, 'test_categories', [
|
|
323
|
+
CategoriesFixtures.category_relations_1
|
|
324
|
+
]);
|
|
325
|
+
await TestHelpers.insertFixturesViaRawSQL(this.dataServer, 'test_products', [
|
|
326
|
+
ProductsFixtures.product_relations_1
|
|
327
|
+
]);
|
|
328
|
+
await this.runner.test('should parse comma-separated relations string', async () => {
|
|
329
|
+
let results = await this.productsRepo.loadWithRelations(
|
|
330
|
+
{id: 2600},
|
|
331
|
+
'related_test_categories,related_test_reviews'
|
|
332
|
+
);
|
|
333
|
+
assert.ok(results);
|
|
334
|
+
assert.ok(results.length > 0);
|
|
335
|
+
});
|
|
336
|
+
await this.runner.test('should handle single relation as string', async () => {
|
|
337
|
+
let results = await this.productsRepo.loadWithRelations(
|
|
338
|
+
{id: 2600},
|
|
339
|
+
'related_test_categories'
|
|
340
|
+
);
|
|
341
|
+
assert.ok(results);
|
|
342
|
+
assert.ok(results.length > 0);
|
|
343
|
+
assert.ok(results[0].related_test_categories);
|
|
344
|
+
});
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
module.exports = RelationsTest;
|
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
/**
|
|
2
|
+
*
|
|
3
|
+
* Reldens - Storage Test Runner
|
|
4
|
+
*
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
const { Logger, sc } = require('@reldens/utils');
|
|
8
|
+
const { FileHandler } = require('@reldens/server-utils');
|
|
9
|
+
const { TestHelpers } = require('./utils/test-helpers');
|
|
10
|
+
const { DriverRegistry } = require('./utils/driver-registry');
|
|
11
|
+
const DriversTest = require('./integration/test-drivers');
|
|
12
|
+
const NestedFiltersTest = require('./integration/test-nested-filters');
|
|
13
|
+
const RelationsTest = require('./integration/test-relations');
|
|
14
|
+
const EntityManagerTest = require('./unit/test-entity-manager');
|
|
15
|
+
const TypeMapperTest = require('./unit/test-type-mapper');
|
|
16
|
+
const DriversUnitTest = require('./unit/test-drivers');
|
|
17
|
+
|
|
18
|
+
if(!process.env.RELDENS_TEST_DB_HOST){
|
|
19
|
+
let envPath = FileHandler.joinPaths(__dirname, '.env.test');
|
|
20
|
+
if(FileHandler.exists(envPath)){
|
|
21
|
+
require('dotenv').config({path: envPath});
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
class RunTests
|
|
26
|
+
{
|
|
27
|
+
|
|
28
|
+
constructor()
|
|
29
|
+
{
|
|
30
|
+
this.allCounts = {total: 0, passed: 0, failed: 0};
|
|
31
|
+
this.filter = null;
|
|
32
|
+
this.suite = null;
|
|
33
|
+
this.driver = null;
|
|
34
|
+
this.skipCleanup = false;
|
|
35
|
+
this.skipGeneration = false;
|
|
36
|
+
this.driverRegistry = new DriverRegistry();
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
parseCommandLineArgs()
|
|
40
|
+
{
|
|
41
|
+
for(let arg of process.argv){
|
|
42
|
+
if(arg.startsWith('--filter=')){
|
|
43
|
+
this.filter = arg.split('=')[1];
|
|
44
|
+
Logger.info('Filter applied: '+this.filter+'\n');
|
|
45
|
+
}
|
|
46
|
+
if(arg.startsWith('--suite=')){
|
|
47
|
+
this.suite = arg.split('=')[1];
|
|
48
|
+
Logger.info('Suite: '+this.suite+'\n');
|
|
49
|
+
}
|
|
50
|
+
if(arg.startsWith('--driver=')){
|
|
51
|
+
this.driver = arg.split('=')[1];
|
|
52
|
+
Logger.info('Driver: '+this.driver+'\n');
|
|
53
|
+
}
|
|
54
|
+
if('--skip-cleanup' === arg){
|
|
55
|
+
this.skipCleanup = true;
|
|
56
|
+
Logger.info('Skipping cleanup: YES\n');
|
|
57
|
+
}
|
|
58
|
+
if('--skip-generation' === arg){
|
|
59
|
+
this.skipGeneration = true;
|
|
60
|
+
Logger.info('Skipping entity generation: YES\n');
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
async run()
|
|
66
|
+
{
|
|
67
|
+
Logger.info('='.repeat(60));
|
|
68
|
+
Logger.info('@RELDENS/STORAGE - TEST SUITE');
|
|
69
|
+
Logger.info('='.repeat(60));
|
|
70
|
+
Logger.info('Test execution started: '+sc.formatDate(new Date()));
|
|
71
|
+
Logger.info('');
|
|
72
|
+
this.parseCommandLineArgs();
|
|
73
|
+
let config = TestHelpers.getTestDbConfig();
|
|
74
|
+
if(!this.skipCleanup){
|
|
75
|
+
TestHelpers.cleanupGeneratedFiles();
|
|
76
|
+
}
|
|
77
|
+
await this.runPreFlightChecks(config);
|
|
78
|
+
let hasIntegrationTests = !this.suite || this.suite === 'integration';
|
|
79
|
+
let hasUnitTests = !this.suite || this.suite === 'unit';
|
|
80
|
+
if(this.filter){
|
|
81
|
+
if(this.filter.includes('integration')){
|
|
82
|
+
hasUnitTests = false;
|
|
83
|
+
}
|
|
84
|
+
if(this.filter.includes('unit')){
|
|
85
|
+
hasIntegrationTests = false;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
Logger.info('Integration tests: '+(hasIntegrationTests ? 'YES' : 'NO'));
|
|
89
|
+
Logger.info('Unit tests: '+(hasUnitTests ? 'YES' : 'NO'));
|
|
90
|
+
Logger.info('');
|
|
91
|
+
if(hasIntegrationTests){
|
|
92
|
+
this.driverRegistry.skipGeneration = this.skipGeneration;
|
|
93
|
+
await this.driverRegistry.initialize();
|
|
94
|
+
await this.runIntegrationTests();
|
|
95
|
+
}
|
|
96
|
+
if(hasUnitTests){
|
|
97
|
+
await this.runUnitTests();
|
|
98
|
+
}
|
|
99
|
+
Logger.info('');
|
|
100
|
+
Logger.info('='.repeat(60));
|
|
101
|
+
Logger.info('FINAL TEST RESULTS');
|
|
102
|
+
Logger.info('='.repeat(60));
|
|
103
|
+
Logger.info('Total tests executed: '+this.allCounts.total);
|
|
104
|
+
Logger.info('Tests passed: '+this.allCounts.passed);
|
|
105
|
+
Logger.info('Tests failed: '+this.allCounts.failed);
|
|
106
|
+
Logger.info('='.repeat(60));
|
|
107
|
+
if(hasIntegrationTests){
|
|
108
|
+
await this.driverRegistry.cleanup();
|
|
109
|
+
}
|
|
110
|
+
if(0 < this.allCounts.failed){
|
|
111
|
+
process.exitCode = 1;
|
|
112
|
+
}
|
|
113
|
+
return this.allCounts;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
async runIntegrationTests()
|
|
117
|
+
{
|
|
118
|
+
let driverNames = ['objection-js', 'mikro-orm', 'prisma'];
|
|
119
|
+
if(this.driver){
|
|
120
|
+
driverNames = [this.driver];
|
|
121
|
+
}
|
|
122
|
+
for(let driverName of driverNames){
|
|
123
|
+
let dataServer = this.driverRegistry.getDriver(driverName);
|
|
124
|
+
let repos = this.driverRegistry.getRepos(driverName);
|
|
125
|
+
if(!dataServer){
|
|
126
|
+
Logger.warning('Driver '+driverName+' not available, skipping tests');
|
|
127
|
+
continue;
|
|
128
|
+
}
|
|
129
|
+
try {
|
|
130
|
+
let driversTest = new DriversTest(dataServer, repos, driverName);
|
|
131
|
+
let driversResult = await driversTest.run();
|
|
132
|
+
this.allCounts.total += driversResult.total;
|
|
133
|
+
this.allCounts.passed += driversResult.passed;
|
|
134
|
+
this.allCounts.failed += driversResult.failed;
|
|
135
|
+
} catch(error) {
|
|
136
|
+
Logger.critical('Driver '+driverName+' tests crashed: '+error.message);
|
|
137
|
+
Logger.critical(error.stack);
|
|
138
|
+
}
|
|
139
|
+
try {
|
|
140
|
+
let nestedFiltersTest = new NestedFiltersTest(dataServer, repos, driverName);
|
|
141
|
+
let nestedFiltersResult = await nestedFiltersTest.run();
|
|
142
|
+
this.allCounts.total += nestedFiltersResult.total;
|
|
143
|
+
this.allCounts.passed += nestedFiltersResult.passed;
|
|
144
|
+
this.allCounts.failed += nestedFiltersResult.failed;
|
|
145
|
+
} catch(error) {
|
|
146
|
+
Logger.critical('Driver '+driverName+' nested filters tests crashed: '+error.message);
|
|
147
|
+
Logger.critical(error.stack);
|
|
148
|
+
}
|
|
149
|
+
try {
|
|
150
|
+
let relationsTest = new RelationsTest(dataServer, repos, driverName);
|
|
151
|
+
let relationsResult = await relationsTest.run();
|
|
152
|
+
this.allCounts.total += relationsResult.total;
|
|
153
|
+
this.allCounts.passed += relationsResult.passed;
|
|
154
|
+
this.allCounts.failed += relationsResult.failed;
|
|
155
|
+
} catch(error) {
|
|
156
|
+
Logger.critical('Driver '+driverName+' relations tests crashed: '+error.message);
|
|
157
|
+
Logger.critical(error.stack);
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
async runUnitTests()
|
|
163
|
+
{
|
|
164
|
+
let entityManagerTest = new EntityManagerTest();
|
|
165
|
+
let entityManagerResult = await entityManagerTest.run();
|
|
166
|
+
this.allCounts.total += entityManagerResult.total;
|
|
167
|
+
this.allCounts.passed += entityManagerResult.passed;
|
|
168
|
+
this.allCounts.failed += entityManagerResult.failed;
|
|
169
|
+
let typeMapperTest = new TypeMapperTest();
|
|
170
|
+
let typeMapperResult = await typeMapperTest.run();
|
|
171
|
+
this.allCounts.total += typeMapperResult.total;
|
|
172
|
+
this.allCounts.passed += typeMapperResult.passed;
|
|
173
|
+
this.allCounts.failed += typeMapperResult.failed;
|
|
174
|
+
let driversUnitTest = new DriversUnitTest();
|
|
175
|
+
let driversUnitResult = await driversUnitTest.run();
|
|
176
|
+
this.allCounts.total += driversUnitResult.total;
|
|
177
|
+
this.allCounts.passed += driversUnitResult.passed;
|
|
178
|
+
this.allCounts.failed += driversUnitResult.failed;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
async runPreFlightChecks(config)
|
|
182
|
+
{
|
|
183
|
+
Logger.info('='.repeat(60));
|
|
184
|
+
Logger.info('Pre-Flight Checks');
|
|
185
|
+
Logger.info('='.repeat(60));
|
|
186
|
+
if(!TestHelpers.verifyAllPackages()){
|
|
187
|
+
Logger.critical('Package verification failed');
|
|
188
|
+
process.exit(1);
|
|
189
|
+
}
|
|
190
|
+
Logger.info('All pre-flight checks passed');
|
|
191
|
+
Logger.info('='.repeat(60));
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
process.on('unhandledRejection', (reason, promise) => {
|
|
197
|
+
Logger.info('Unhandled Rejection at: '+promise+' reason: '+reason+'\n');
|
|
198
|
+
Logger.info('(Test suite will continue)\n');
|
|
199
|
+
});
|
|
200
|
+
|
|
201
|
+
process.on('uncaughtException', (error) => {
|
|
202
|
+
Logger.info('Uncaught Exception: '+error.message+'\n');
|
|
203
|
+
Logger.info(error.stack+'\n');
|
|
204
|
+
Logger.info('(Test suite will continue)\n');
|
|
205
|
+
});
|
|
206
|
+
|
|
207
|
+
let runner = new RunTests();
|
|
208
|
+
runner.run().catch(error => {
|
|
209
|
+
Logger.info('CATASTROPHIC ERROR: Test runner failed completely\n');
|
|
210
|
+
Logger.info('Error: '+error.message+'\n');
|
|
211
|
+
Logger.info(error.stack+'\n');
|
|
212
|
+
process.exit(1);
|
|
213
|
+
});
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
/**
|
|
2
|
+
*
|
|
3
|
+
* Reldens - Drivers Test
|
|
4
|
+
* Tests shared public methods across all storage drivers
|
|
5
|
+
*
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
const { TestRunner, assert } = require('../utils/test-runner');
|
|
9
|
+
const { ObjectionJsDriver } = require('../../lib/objection-js/objection-js-driver');
|
|
10
|
+
const { MikroOrmDriver } = require('../../lib/mikro-orm/mikro-orm-driver');
|
|
11
|
+
const { PrismaDriver } = require('../../lib/prisma/prisma-driver');
|
|
12
|
+
|
|
13
|
+
class DriversUnitTest
|
|
14
|
+
{
|
|
15
|
+
|
|
16
|
+
constructor()
|
|
17
|
+
{
|
|
18
|
+
this.runner = new TestRunner();
|
|
19
|
+
this.DRIVERS = [
|
|
20
|
+
{name: 'objection-js', class: ObjectionJsDriver},
|
|
21
|
+
{name: 'mikro-orm', class: MikroOrmDriver},
|
|
22
|
+
{name: 'prisma', class: PrismaDriver}
|
|
23
|
+
];
|
|
24
|
+
this.SHARED_PUBLIC_METHODS = [
|
|
25
|
+
'databaseName',
|
|
26
|
+
'id',
|
|
27
|
+
'name',
|
|
28
|
+
'tableName',
|
|
29
|
+
'property',
|
|
30
|
+
'create',
|
|
31
|
+
'createWithRelations',
|
|
32
|
+
'update',
|
|
33
|
+
'updateBy',
|
|
34
|
+
'updateById',
|
|
35
|
+
'upsert',
|
|
36
|
+
'delete',
|
|
37
|
+
'deleteById',
|
|
38
|
+
'count',
|
|
39
|
+
'countWithRelations',
|
|
40
|
+
'loadAll',
|
|
41
|
+
'loadAllWithRelations',
|
|
42
|
+
'load',
|
|
43
|
+
'loadWithRelations',
|
|
44
|
+
'loadBy',
|
|
45
|
+
'loadByWithRelations',
|
|
46
|
+
'loadById',
|
|
47
|
+
'loadByIdWithRelations',
|
|
48
|
+
'loadByIds',
|
|
49
|
+
'loadOne',
|
|
50
|
+
'loadOneWithRelations',
|
|
51
|
+
'loadOneBy',
|
|
52
|
+
'loadOneByWithRelations',
|
|
53
|
+
'rawQuery',
|
|
54
|
+
'executeCustomQuery',
|
|
55
|
+
'isJsonField',
|
|
56
|
+
'parseRelationsString',
|
|
57
|
+
'applyQueryOptions',
|
|
58
|
+
'preserveEntityState',
|
|
59
|
+
'restoreEntityState',
|
|
60
|
+
'loadEntityData'
|
|
61
|
+
];
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
async run()
|
|
65
|
+
{
|
|
66
|
+
this.runner.suite('Drivers Unit Test');
|
|
67
|
+
for(let driver of this.DRIVERS){
|
|
68
|
+
await this.testDriverMethods(driver);
|
|
69
|
+
}
|
|
70
|
+
return this.runner.getResults();
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
async testDriverMethods(driver)
|
|
74
|
+
{
|
|
75
|
+
this.runner.group('Driver: '+driver.name+' - Shared public methods');
|
|
76
|
+
for(let methodName of this.SHARED_PUBLIC_METHODS){
|
|
77
|
+
await this.runner.test('should have method '+methodName, async () => {
|
|
78
|
+
assert.strictEqual(
|
|
79
|
+
typeof driver.class.prototype[methodName],
|
|
80
|
+
'function',
|
|
81
|
+
driver.name+' should implement '+methodName
|
|
82
|
+
);
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
module.exports = DriversUnitTest;
|