@reldens/storage 0.116.0 → 0.118.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/test-architecture.md +27 -17
- package/CLAUDE.md +67 -11
- package/README.md +204 -22
- package/bin/reldens-storage.js +43 -10
- package/index.js +53 -12
- package/lib/drizzle/drizzle-data-server.js +123 -0
- package/lib/drizzle/drizzle-driver.js +228 -0
- package/lib/drizzle/drizzle-models-generation.js +49 -0
- package/lib/drizzle/drizzle-modules-loader.js +32 -0
- package/lib/drizzle/drizzle-modules-validator.js +74 -0
- package/lib/entities-generator.js +36 -7
- package/lib/entity-templates/drizzle-model.template +28 -0
- package/lib/entity-templates/query-builder-model.template +17 -0
- package/lib/generators/base-generator.js +13 -0
- package/lib/generators/entities-generation.js +0 -13
- package/lib/generators/models-generation.js +179 -693
- package/lib/generators/relations-detection.js +171 -0
- package/lib/knex/knex-data-server.js +128 -0
- package/lib/knex/knex-driver.js +157 -0
- package/lib/knex/knex-modules-loader.js +28 -0
- package/lib/knex/knex-modules-validator.js +45 -0
- package/lib/kysely/kysely-data-server.js +128 -0
- package/lib/kysely/kysely-driver.js +176 -0
- package/lib/kysely/kysely-modules-loader.js +28 -0
- package/lib/kysely/kysely-modules-validator.js +53 -0
- package/lib/mikro-orm/mikro-orm-data-server.js +48 -29
- package/lib/mikro-orm/mikro-orm-driver.js +385 -121
- package/lib/mikro-orm/mikro-orm-models-generation.js +178 -0
- package/lib/mikro-orm/mikro-orm-modules-loader.js +50 -0
- package/lib/mikro-orm/mikro-orm-modules-validator.js +46 -0
- package/lib/mysql2-connection-config.js +38 -0
- package/lib/objection-js/objection-js-data-server.js +71 -125
- package/lib/objection-js/objection-js-driver.js +4 -1
- package/lib/objection-js/objection-js-models-generation.js +98 -0
- package/lib/objection-js/objection-modules-loader.js +28 -0
- package/lib/objection-js/objection-modules-validator.js +31 -0
- package/lib/package-resolver.js +45 -0
- package/lib/prisma/prisma-client-loader.js +11 -1
- package/lib/prisma/prisma-driver.js +16 -2
- package/lib/prisma/prisma-filter-processor.js +5 -6
- package/lib/prisma/prisma-models-generation.js +236 -0
- package/lib/prisma/prisma-schema-generator.js +5 -1
- package/lib/prisma/prisma-type-caster.js +14 -0
- package/lib/query-builder-driver.js +210 -0
- package/lib/query-builder-models-generation.js +80 -0
- package/lib/relations-loader.js +192 -0
- package/lib/type-mapper.js +38 -0
- package/package.json +7 -7
- package/tests/.env.test.example +6 -0
- package/tests/fixtures/categories-fixtures.js +8 -0
- package/tests/fixtures/expected-entities/entities/test-product-details-entity.js +72 -0
- package/tests/fixtures/expected-entities/entities-config.js +6 -4
- package/tests/fixtures/expected-entities/entities-translations.js +25 -14
- package/tests/fixtures/expected-entities-mikro-orm/models/mikro-orm/registered-models-mikro-orm.js +2 -0
- package/tests/fixtures/expected-entities-mikro-orm/models/mikro-orm/test-categories-model.js +13 -8
- package/tests/fixtures/expected-entities-mikro-orm/models/mikro-orm/test-product-details-model.js +80 -0
- package/tests/fixtures/expected-entities-mikro-orm/models/mikro-orm/test-products-model.js +20 -14
- package/tests/fixtures/expected-entities-mikro-orm/models/mikro-orm/test-reviews-model.js +13 -12
- package/tests/fixtures/expected-entities-objection-js/models/objection-js/registered-models-objection-js.js +2 -0
- package/tests/fixtures/expected-entities-objection-js/models/objection-js/test-product-details-model.js +33 -0
- package/tests/fixtures/expected-entities-objection-js/models/objection-js/test-products-model.js +9 -0
- package/tests/fixtures/expected-entities-prisma/models/prisma/registered-models-prisma.js +2 -0
- package/tests/fixtures/expected-entities-prisma/models/prisma/test-product-details-model.js +43 -0
- package/tests/fixtures/expected-entities-prisma/models/prisma/test-products-model.js +2 -0
- package/tests/fixtures/product-details-fixtures.js +22 -0
- package/tests/fixtures/reviews-fixtures.js +2 -1
- package/tests/fixtures/sql/test-schema.sql +23 -1
- package/tests/fixtures/table-columns-fixtures.js +97 -0
- package/tests/integration/test-cross-driver-equivalence.js +286 -0
- package/tests/integration/test-nested-filters.js +265 -408
- package/tests/integration/test-relations.js +199 -0
- package/tests/integration/test-reldens-sample-data.js +255 -0
- package/tests/integration/test-reldens-shape-relations.js +292 -0
- package/tests/run-tests.js +112 -60
- package/tests/unit/test-drivers.js +7 -1
- package/tests/unit/test-models-generation.js +174 -0
- package/tests/utils/column-value-assert.js +139 -0
- package/tests/utils/driver-registry.js +1 -1
- package/tests/utils/entity-projection.js +111 -0
- package/tests/utils/observed-value.js +41 -0
- package/tests/utils/projection-difference.js +151 -0
- package/tests/utils/relations-shape-support.js +104 -0
- package/tests/utils/reldens-schema-loader.js +274 -0
- package/tests/utils/test-helpers.js +152 -19
- package/tests/utils/test-runner.js +26 -5
- package/tests/utils/whole-result-assert.js +243 -0
|
@@ -1,408 +1,265 @@
|
|
|
1
|
-
/**
|
|
2
|
-
*
|
|
3
|
-
* Reldens - Nested Filters Integration Test
|
|
4
|
-
* Tests complex filter syntax across all
|
|
5
|
-
*
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
const {
|
|
11
|
-
const {
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
{
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
this.
|
|
22
|
-
this.
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
this.
|
|
43
|
-
await
|
|
44
|
-
await
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
await
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
let results = await this.categoriesRepo.load(
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
this.runner.
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
let results = await this.categoriesRepo.load({
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
});
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
let results = await this.categoriesRepo.load({
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
});
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
let results = await this.categoriesRepo.load({
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
});
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
});
|
|
204
|
-
await this.runner.test('should filter with
|
|
205
|
-
let results = await this.categoriesRepo.load({
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
this.
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
}
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
});
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
slug: {operator: 'like', value: '%filters%'}
|
|
267
|
-
});
|
|
268
|
-
assert.ok(results);
|
|
269
|
-
assert.ok(results.length > 0);
|
|
270
|
-
});
|
|
271
|
-
await this.runner.test('should work with LIKE inside AND', async () => {
|
|
272
|
-
let results = await this.categoriesRepo.load({
|
|
273
|
-
AND: [
|
|
274
|
-
{is_active: 1},
|
|
275
|
-
{name: {operator: 'like', value: '%Filters%'}}
|
|
276
|
-
]
|
|
277
|
-
});
|
|
278
|
-
assert.ok(results);
|
|
279
|
-
assert.ok(results.length > 0);
|
|
280
|
-
});
|
|
281
|
-
}
|
|
282
|
-
|
|
283
|
-
async testComparisonOperators()
|
|
284
|
-
{
|
|
285
|
-
this.runner.group('Comparison Operators');
|
|
286
|
-
await TestHelpers.cleanDatabase(this.dataServer);
|
|
287
|
-
await TestHelpers.insertFixturesViaRawSQL(this.dataServer, 'test_categories', [
|
|
288
|
-
CategoriesFixtures.category_filters_1,
|
|
289
|
-
CategoriesFixtures.category_filters_2,
|
|
290
|
-
CategoriesFixtures.category_filters_3
|
|
291
|
-
]);
|
|
292
|
-
await this.runner.test('should filter with gt operator', async () => {
|
|
293
|
-
let results = await this.categoriesRepo.load({
|
|
294
|
-
display_order: {operator: 'gt', value: 1}
|
|
295
|
-
});
|
|
296
|
-
assert.ok(results);
|
|
297
|
-
assert.strictEqual(results.length, 2);
|
|
298
|
-
});
|
|
299
|
-
await this.runner.test('should filter with gte operator', async () => {
|
|
300
|
-
let results = await this.categoriesRepo.load({
|
|
301
|
-
display_order: {operator: 'gte', value: 2}
|
|
302
|
-
});
|
|
303
|
-
assert.ok(results);
|
|
304
|
-
assert.strictEqual(results.length, 2);
|
|
305
|
-
});
|
|
306
|
-
await this.runner.test('should filter with lt operator', async () => {
|
|
307
|
-
let results = await this.categoriesRepo.load({
|
|
308
|
-
display_order: {operator: 'lt', value: 3}
|
|
309
|
-
});
|
|
310
|
-
assert.ok(results);
|
|
311
|
-
assert.strictEqual(results.length, 2);
|
|
312
|
-
});
|
|
313
|
-
await this.runner.test('should filter with lte operator', async () => {
|
|
314
|
-
let results = await this.categoriesRepo.load({
|
|
315
|
-
display_order: {operator: 'lte', value: 2}
|
|
316
|
-
});
|
|
317
|
-
assert.ok(results);
|
|
318
|
-
assert.strictEqual(results.length, 2);
|
|
319
|
-
});
|
|
320
|
-
}
|
|
321
|
-
|
|
322
|
-
async testComplexNestedScenarios()
|
|
323
|
-
{
|
|
324
|
-
this.runner.group('Complex Nested Scenarios');
|
|
325
|
-
await TestHelpers.cleanDatabase(this.dataServer);
|
|
326
|
-
await TestHelpers.insertFixturesViaRawSQL(this.dataServer, 'test_categories', [
|
|
327
|
-
CategoriesFixtures.category_filters_1
|
|
328
|
-
]);
|
|
329
|
-
await TestHelpers.insertFixturesViaRawSQL(this.dataServer, 'test_products', [
|
|
330
|
-
ProductsFixtures.product_filters_1,
|
|
331
|
-
ProductsFixtures.product_filters_2
|
|
332
|
-
]);
|
|
333
|
-
await this.runner.test('should handle complex filter with multiple operators', async () => {
|
|
334
|
-
let results = await this.productsRepo.load({
|
|
335
|
-
AND: [
|
|
336
|
-
{status: 'published'},
|
|
337
|
-
{
|
|
338
|
-
OR: [
|
|
339
|
-
{price: {operator: 'lt', value: 1000}},
|
|
340
|
-
{stock_quantity: {operator: 'gte', value: 50}}
|
|
341
|
-
]
|
|
342
|
-
}
|
|
343
|
-
]
|
|
344
|
-
});
|
|
345
|
-
assert.ok(results);
|
|
346
|
-
assert.ok(results.length > 0);
|
|
347
|
-
});
|
|
348
|
-
await this.runner.test('should handle filter with IN inside OR', async () => {
|
|
349
|
-
let results = await this.productsRepo.load({
|
|
350
|
-
OR: [
|
|
351
|
-
{status: {operator: 'in', value: ['published', 'draft']}},
|
|
352
|
-
{stock_quantity: {operator: 'gt', value: 0}}
|
|
353
|
-
]
|
|
354
|
-
});
|
|
355
|
-
assert.ok(results);
|
|
356
|
-
assert.strictEqual(results.length, 2);
|
|
357
|
-
});
|
|
358
|
-
await this.runner.test('should handle nested AND/OR with LIKE and comparison operators', async () => {
|
|
359
|
-
let results = await this.productsRepo.load({
|
|
360
|
-
AND: [
|
|
361
|
-
{
|
|
362
|
-
OR: [
|
|
363
|
-
{name: {operator: 'like', value: '%Phone%'}},
|
|
364
|
-
{name: {operator: 'like', value: '%Product%'}}
|
|
365
|
-
]
|
|
366
|
-
},
|
|
367
|
-
{price: {operator: 'gte', value: 0}}
|
|
368
|
-
]
|
|
369
|
-
});
|
|
370
|
-
assert.ok(results);
|
|
371
|
-
assert.ok(results.length > 0);
|
|
372
|
-
});
|
|
373
|
-
}
|
|
374
|
-
|
|
375
|
-
async testEdgeCases()
|
|
376
|
-
{
|
|
377
|
-
this.runner.group('Edge Cases');
|
|
378
|
-
await TestHelpers.cleanDatabase(this.dataServer);
|
|
379
|
-
await TestHelpers.insertFixturesViaRawSQL(this.dataServer, 'test_categories', [
|
|
380
|
-
CategoriesFixtures.category_filters_1
|
|
381
|
-
]);
|
|
382
|
-
await this.runner.test('should handle empty AND array', async () => {
|
|
383
|
-
let results = await this.categoriesRepo.load({AND: []});
|
|
384
|
-
assert.ok(results);
|
|
385
|
-
});
|
|
386
|
-
await this.runner.test('should handle empty OR array', async () => {
|
|
387
|
-
let results = await this.categoriesRepo.load({OR: []});
|
|
388
|
-
assert.ok(results);
|
|
389
|
-
});
|
|
390
|
-
await this.runner.test('should handle single condition in AND', async () => {
|
|
391
|
-
let results = await this.categoriesRepo.load({
|
|
392
|
-
AND: [{is_active: 1}]
|
|
393
|
-
});
|
|
394
|
-
assert.ok(results);
|
|
395
|
-
assert.strictEqual(results.length, 1);
|
|
396
|
-
});
|
|
397
|
-
await this.runner.test('should handle single condition in OR', async () => {
|
|
398
|
-
let results = await this.categoriesRepo.load({
|
|
399
|
-
OR: [{is_active: 1}]
|
|
400
|
-
});
|
|
401
|
-
assert.ok(results);
|
|
402
|
-
assert.strictEqual(results.length, 1);
|
|
403
|
-
});
|
|
404
|
-
}
|
|
405
|
-
|
|
406
|
-
}
|
|
407
|
-
|
|
408
|
-
module.exports = NestedFiltersTest;
|
|
1
|
+
/**
|
|
2
|
+
*
|
|
3
|
+
* Reldens - Nested Filters Integration Test
|
|
4
|
+
* Tests complex filter syntax across all the storage drivers with an actual database, asserting the whole
|
|
5
|
+
* result of every filter: the exact amount of records and every column of every returned record against the
|
|
6
|
+
* fixture rows that were inserted.
|
|
7
|
+
*
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
const { TestRunner } = require('../utils/test-runner');
|
|
11
|
+
const { TestHelpers } = require('../utils/test-helpers');
|
|
12
|
+
const { WholeResultAssert } = require('../utils/whole-result-assert');
|
|
13
|
+
const { CategoriesFixtures } = require('../fixtures/categories-fixtures');
|
|
14
|
+
const { ProductsFixtures } = require('../fixtures/products-fixtures');
|
|
15
|
+
|
|
16
|
+
class NestedFiltersTest
|
|
17
|
+
{
|
|
18
|
+
|
|
19
|
+
constructor(dataServer, repos, driverName)
|
|
20
|
+
{
|
|
21
|
+
this.dataServer = dataServer;
|
|
22
|
+
this.categoriesRepo = repos.testCategories;
|
|
23
|
+
this.productsRepo = repos.testProducts;
|
|
24
|
+
this.driverName = driverName;
|
|
25
|
+
this.runner = new TestRunner();
|
|
26
|
+
this.categoriesById = {
|
|
27
|
+
1500: CategoriesFixtures.category_filters_1,
|
|
28
|
+
1501: CategoriesFixtures.category_filters_2,
|
|
29
|
+
1502: CategoriesFixtures.category_filters_3
|
|
30
|
+
};
|
|
31
|
+
this.productsById = {
|
|
32
|
+
2500: ProductsFixtures.product_filters_1,
|
|
33
|
+
2501: ProductsFixtures.product_filters_2
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
async run()
|
|
38
|
+
{
|
|
39
|
+
this.runner.suite('Nested Filters - Driver: '+this.driverName);
|
|
40
|
+
await this.testAndOperator();
|
|
41
|
+
await this.testOrOperator();
|
|
42
|
+
await this.testNestedAndOr();
|
|
43
|
+
await this.testNotOperator();
|
|
44
|
+
await this.testInOperator();
|
|
45
|
+
await this.testLikeOperator();
|
|
46
|
+
await this.testComparisonOperators();
|
|
47
|
+
await this.testComplexNestedScenarios();
|
|
48
|
+
await this.testEdgeCases();
|
|
49
|
+
return this.runner.getResults();
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
async insertCategories(fixtureRows)
|
|
53
|
+
{
|
|
54
|
+
await TestHelpers.cleanDatabase(this.dataServer);
|
|
55
|
+
await TestHelpers.insertFixturesViaRawSQL(this.dataServer, 'test_categories', fixtureRows);
|
|
56
|
+
return true;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
allFilterCategories()
|
|
60
|
+
{
|
|
61
|
+
return [
|
|
62
|
+
CategoriesFixtures.category_filters_1,
|
|
63
|
+
CategoriesFixtures.category_filters_2,
|
|
64
|
+
CategoriesFixtures.category_filters_3
|
|
65
|
+
];
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
assertFilteredRecords(results, tableName, expectedIds, label)
|
|
69
|
+
{
|
|
70
|
+
let fixturesById = 'test_categories' === tableName ? this.categoriesById : this.productsById;
|
|
71
|
+
let expectedFixtures = [];
|
|
72
|
+
for(let expectedId of expectedIds){
|
|
73
|
+
expectedFixtures.push(fixturesById[expectedId]);
|
|
74
|
+
}
|
|
75
|
+
return WholeResultAssert.fixtureList(results, tableName, expectedFixtures, label);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
async testAndOperator()
|
|
79
|
+
{
|
|
80
|
+
this.runner.group('AND Operator');
|
|
81
|
+
await this.insertCategories(this.allFilterCategories());
|
|
82
|
+
await this.runner.test('should filter with simple AND condition', async () => {
|
|
83
|
+
let results = await this.categoriesRepo.load({AND: [{is_active: 1}, {display_order: 1}]});
|
|
84
|
+
this.assertFilteredRecords(results, 'test_categories', [1500], 'AND is_active 1 and order 1');
|
|
85
|
+
});
|
|
86
|
+
await this.runner.test('should filter with AND containing multiple conditions', async () => {
|
|
87
|
+
let orderFilter = {operator: 'gte', value: 1};
|
|
88
|
+
let results = await this.categoriesRepo.load({AND: [{is_active: 1}, {display_order: orderFilter}]});
|
|
89
|
+
this.assertFilteredRecords(results, 'test_categories', [1500, 1501], 'AND is_active 1 and order gte 1');
|
|
90
|
+
});
|
|
91
|
+
await this.runner.test('should return empty when AND conditions do not match', async () => {
|
|
92
|
+
let results = await this.categoriesRepo.load({AND: [{is_active: 1}, {slug: 'nonexistent'}]});
|
|
93
|
+
this.assertFilteredRecords(results, 'test_categories', [], 'AND with a slug that does not exist');
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
async testOrOperator()
|
|
98
|
+
{
|
|
99
|
+
this.runner.group('OR Operator');
|
|
100
|
+
await this.insertCategories(this.allFilterCategories());
|
|
101
|
+
await this.runner.test('should filter with simple OR condition', async () => {
|
|
102
|
+
let filters = {OR: [{slug: 'filters-test-1'}, {slug: 'filters-test-2'}]};
|
|
103
|
+
let results = await this.categoriesRepo.load(filters);
|
|
104
|
+
this.assertFilteredRecords(results, 'test_categories', [1500, 1501], 'OR on two slugs');
|
|
105
|
+
});
|
|
106
|
+
await this.runner.test('should filter with OR containing operator conditions', async () => {
|
|
107
|
+
let filters = {OR: [{display_order: {operator: 'lt', value: 2}}, {is_active: 0}]};
|
|
108
|
+
let results = await this.categoriesRepo.load(filters);
|
|
109
|
+
this.assertFilteredRecords(results, 'test_categories', [1500, 1502], 'OR order lt 2 or inactive');
|
|
110
|
+
});
|
|
111
|
+
await this.runner.test('should return all matching records with OR', async () => {
|
|
112
|
+
let results = await this.categoriesRepo.load({OR: [{is_active: 1}, {is_active: 0}]});
|
|
113
|
+
this.assertFilteredRecords(results, 'test_categories', [1500, 1501, 1502], 'OR on both is_active');
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
async testNestedAndOr()
|
|
118
|
+
{
|
|
119
|
+
this.runner.group('Nested AND/OR');
|
|
120
|
+
await this.insertCategories(this.allFilterCategories());
|
|
121
|
+
await this.runner.test('should filter with OR inside AND', async () => {
|
|
122
|
+
let orderOptions = {OR: [{display_order: 1}, {display_order: 2}]};
|
|
123
|
+
let results = await this.categoriesRepo.load({AND: [{is_active: 1}, orderOptions]});
|
|
124
|
+
this.assertFilteredRecords(results, 'test_categories', [1500, 1501], 'OR inside AND');
|
|
125
|
+
});
|
|
126
|
+
await this.runner.test('should filter with AND inside OR', async () => {
|
|
127
|
+
let activeAndFirst = {AND: [{is_active: 1}, {display_order: 1}]};
|
|
128
|
+
let results = await this.categoriesRepo.load({OR: [activeAndFirst, {is_active: 0}]});
|
|
129
|
+
this.assertFilteredRecords(results, 'test_categories', [1500, 1502], 'AND inside OR');
|
|
130
|
+
});
|
|
131
|
+
await this.runner.test('should handle deep nesting of AND/OR', async () => {
|
|
132
|
+
let orderOptions = {OR: [{display_order: 1}, {display_order: 2}]};
|
|
133
|
+
let activeOptions = {OR: [{is_active: 1}, {is_active: 0}]};
|
|
134
|
+
let results = await this.categoriesRepo.load({AND: [orderOptions, activeOptions]});
|
|
135
|
+
this.assertFilteredRecords(results, 'test_categories', [1500, 1501], 'two OR groups inside AND');
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
async testNotOperator()
|
|
140
|
+
{
|
|
141
|
+
this.runner.group('NOT Operator');
|
|
142
|
+
await this.insertCategories(this.allFilterCategories());
|
|
143
|
+
await this.runner.test('should filter with NOT operator', async () => {
|
|
144
|
+
let results = await this.categoriesRepo.load({is_active: {operator: 'not', value: 0}});
|
|
145
|
+
this.assertFilteredRecords(results, 'test_categories', [1500, 1501], 'NOT is_active 0');
|
|
146
|
+
});
|
|
147
|
+
await this.runner.test('should filter with NOT inside AND', async () => {
|
|
148
|
+
let activeFilter = {is_active: {operator: 'not', value: 0}};
|
|
149
|
+
let results = await this.categoriesRepo.load({AND: [activeFilter, {display_order: 1}]});
|
|
150
|
+
this.assertFilteredRecords(results, 'test_categories', [1500], 'NOT inside AND');
|
|
151
|
+
});
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
async testInOperator()
|
|
155
|
+
{
|
|
156
|
+
this.runner.group('IN Operator');
|
|
157
|
+
await this.insertCategories(this.allFilterCategories());
|
|
158
|
+
await this.runner.test('should filter with IN operator', async () => {
|
|
159
|
+
let results = await this.categoriesRepo.load({display_order: {operator: 'in', value: [1, 2]}});
|
|
160
|
+
this.assertFilteredRecords(results, 'test_categories', [1500, 1501], 'IN display_order 1 and 2');
|
|
161
|
+
});
|
|
162
|
+
await this.runner.test('should filter with IN operator on string field', async () => {
|
|
163
|
+
let slugsFilter = {operator: 'in', value: ['filters-test-1', 'filters-test-2']};
|
|
164
|
+
let results = await this.categoriesRepo.load({slug: slugsFilter});
|
|
165
|
+
this.assertFilteredRecords(results, 'test_categories', [1500, 1501], 'IN slug');
|
|
166
|
+
});
|
|
167
|
+
await this.runner.test('should return empty with IN operator when no match', async () => {
|
|
168
|
+
let results = await this.categoriesRepo.load({display_order: {operator: 'in', value: [99, 100]}});
|
|
169
|
+
this.assertFilteredRecords(results, 'test_categories', [], 'IN display_order without match');
|
|
170
|
+
});
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
async testLikeOperator()
|
|
174
|
+
{
|
|
175
|
+
this.runner.group('LIKE Operator');
|
|
176
|
+
await this.insertCategories([CategoriesFixtures.category_filters_1, CategoriesFixtures.category_filters_2]);
|
|
177
|
+
await this.runner.test('should filter with LIKE operator', async () => {
|
|
178
|
+
let results = await this.categoriesRepo.load({name: {operator: 'like', value: '%Filters%'}});
|
|
179
|
+
this.assertFilteredRecords(results, 'test_categories', [1500, 1501], 'LIKE on name');
|
|
180
|
+
});
|
|
181
|
+
await this.runner.test('should filter with LIKE and wildcards', async () => {
|
|
182
|
+
let results = await this.categoriesRepo.load({slug: {operator: 'like', value: '%filters%'}});
|
|
183
|
+
this.assertFilteredRecords(results, 'test_categories', [1500, 1501], 'LIKE on slug');
|
|
184
|
+
});
|
|
185
|
+
await this.runner.test('should work with LIKE inside AND', async () => {
|
|
186
|
+
let nameFilter = {name: {operator: 'like', value: '%Filters%'}};
|
|
187
|
+
let results = await this.categoriesRepo.load({AND: [{is_active: 1}, nameFilter]});
|
|
188
|
+
this.assertFilteredRecords(results, 'test_categories', [1500, 1501], 'LIKE inside AND');
|
|
189
|
+
});
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
async testComparisonOperators()
|
|
193
|
+
{
|
|
194
|
+
this.runner.group('Comparison Operators');
|
|
195
|
+
await this.insertCategories(this.allFilterCategories());
|
|
196
|
+
await this.runner.test('should filter with gt operator', async () => {
|
|
197
|
+
let results = await this.categoriesRepo.load({display_order: {operator: 'gt', value: 1}});
|
|
198
|
+
this.assertFilteredRecords(results, 'test_categories', [1501, 1502], 'display_order gt 1');
|
|
199
|
+
});
|
|
200
|
+
await this.runner.test('should filter with gte operator', async () => {
|
|
201
|
+
let results = await this.categoriesRepo.load({display_order: {operator: 'gte', value: 2}});
|
|
202
|
+
this.assertFilteredRecords(results, 'test_categories', [1501, 1502], 'display_order gte 2');
|
|
203
|
+
});
|
|
204
|
+
await this.runner.test('should filter with lt operator', async () => {
|
|
205
|
+
let results = await this.categoriesRepo.load({display_order: {operator: 'lt', value: 3}});
|
|
206
|
+
this.assertFilteredRecords(results, 'test_categories', [1500, 1501], 'display_order lt 3');
|
|
207
|
+
});
|
|
208
|
+
await this.runner.test('should filter with lte operator', async () => {
|
|
209
|
+
let results = await this.categoriesRepo.load({display_order: {operator: 'lte', value: 2}});
|
|
210
|
+
this.assertFilteredRecords(results, 'test_categories', [1500, 1501], 'display_order lte 2');
|
|
211
|
+
});
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
async testComplexNestedScenarios()
|
|
215
|
+
{
|
|
216
|
+
this.runner.group('Complex Nested Scenarios');
|
|
217
|
+
await this.insertCategories([CategoriesFixtures.category_filters_1]);
|
|
218
|
+
await TestHelpers.insertFixturesViaRawSQL(this.dataServer, 'test_products', [
|
|
219
|
+
ProductsFixtures.product_filters_1,
|
|
220
|
+
ProductsFixtures.product_filters_2
|
|
221
|
+
]);
|
|
222
|
+
await this.runner.test('should handle complex filter with multiple operators', async () => {
|
|
223
|
+
let priceOrStock = {OR: [{price: {operator: 'lt', value: 1000}}, {stock_quantity: {operator: 'gte', value: 50}}]};
|
|
224
|
+
let results = await this.productsRepo.load({AND: [{status: 'published'}, priceOrStock]});
|
|
225
|
+
this.assertFilteredRecords(results, 'test_products', [2500], 'published with price or stock');
|
|
226
|
+
});
|
|
227
|
+
await this.runner.test('should handle filter with IN inside OR', async () => {
|
|
228
|
+
let statusFilter = {status: {operator: 'in', value: ['published', 'draft']}};
|
|
229
|
+
let stockFilter = {stock_quantity: {operator: 'gt', value: 0}};
|
|
230
|
+
let results = await this.productsRepo.load({OR: [statusFilter, stockFilter]});
|
|
231
|
+
this.assertFilteredRecords(results, 'test_products', [2500, 2501], 'IN inside OR');
|
|
232
|
+
});
|
|
233
|
+
await this.runner.test('should handle nested AND/OR with LIKE and comparison operators', async () => {
|
|
234
|
+
let nameOptions = {OR: [{name: {operator: 'like', value: '%Phone%'}}, {name: {operator: 'like', value: '%Product%'}}]};
|
|
235
|
+
let priceFilter = {price: {operator: 'gte', value: 0}};
|
|
236
|
+
let results = await this.productsRepo.load({AND: [nameOptions, priceFilter]});
|
|
237
|
+
this.assertFilteredRecords(results, 'test_products', [2500, 2501], 'LIKE options with price');
|
|
238
|
+
});
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
async testEdgeCases()
|
|
242
|
+
{
|
|
243
|
+
this.runner.group('Edge Cases');
|
|
244
|
+
await this.insertCategories([CategoriesFixtures.category_filters_1]);
|
|
245
|
+
await this.runner.test('should handle empty AND array', async () => {
|
|
246
|
+
let results = await this.categoriesRepo.load({AND: []});
|
|
247
|
+
this.assertFilteredRecords(results, 'test_categories', [1500], 'empty AND returns every record');
|
|
248
|
+
});
|
|
249
|
+
await this.runner.test('should handle empty OR array', async () => {
|
|
250
|
+
let results = await this.categoriesRepo.load({OR: []});
|
|
251
|
+
this.assertFilteredRecords(results, 'test_categories', [1500], 'empty OR returns every record');
|
|
252
|
+
});
|
|
253
|
+
await this.runner.test('should handle single condition in AND', async () => {
|
|
254
|
+
let results = await this.categoriesRepo.load({AND: [{is_active: 1}]});
|
|
255
|
+
this.assertFilteredRecords(results, 'test_categories', [1500], 'single condition in AND');
|
|
256
|
+
});
|
|
257
|
+
await this.runner.test('should handle single condition in OR', async () => {
|
|
258
|
+
let results = await this.categoriesRepo.load({OR: [{is_active: 1}]});
|
|
259
|
+
this.assertFilteredRecords(results, 'test_categories', [1500], 'single condition in OR');
|
|
260
|
+
});
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
module.exports = NestedFiltersTest;
|