@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
|
@@ -17,6 +17,7 @@ class TestRunner
|
|
|
17
17
|
this.failedCount = 0;
|
|
18
18
|
this.currentSuite = '';
|
|
19
19
|
this.currentGroup = '';
|
|
20
|
+
this.totalDuration = 0;
|
|
20
21
|
}
|
|
21
22
|
|
|
22
23
|
suite(name)
|
|
@@ -38,22 +39,42 @@ class TestRunner
|
|
|
38
39
|
try {
|
|
39
40
|
await testFn();
|
|
40
41
|
this.passedCount++;
|
|
41
|
-
|
|
42
|
-
Logger.info(' ✔ '+name+' ('+duration+'ms)');
|
|
42
|
+
this.logTestResult(name, startTime, false);
|
|
43
43
|
} catch(error) {
|
|
44
44
|
this.failedCount++;
|
|
45
|
-
|
|
46
|
-
Logger.error(' ✖ '+name+' ('+duration+'ms)');
|
|
45
|
+
this.logTestResult(name, startTime, true);
|
|
47
46
|
Logger.error(' Error: '+error.message);
|
|
48
47
|
}
|
|
49
48
|
}
|
|
50
49
|
|
|
50
|
+
fail(name, message)
|
|
51
|
+
{
|
|
52
|
+
this.testCount++;
|
|
53
|
+
this.failedCount++;
|
|
54
|
+
Logger.error(' ✖ '+name);
|
|
55
|
+
Logger.error(' Error: '+message);
|
|
56
|
+
return this.getResults();
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
logTestResult(name, startTime, hasFailed)
|
|
60
|
+
{
|
|
61
|
+
let duration = Date.now() - startTime;
|
|
62
|
+
this.totalDuration += duration;
|
|
63
|
+
if(hasFailed){
|
|
64
|
+
Logger.error(' ✖ '+name+' ('+duration+'ms)');
|
|
65
|
+
return duration;
|
|
66
|
+
}
|
|
67
|
+
Logger.info(' ✔ '+name+' ('+duration+'ms)');
|
|
68
|
+
return duration;
|
|
69
|
+
}
|
|
70
|
+
|
|
51
71
|
getResults()
|
|
52
72
|
{
|
|
53
73
|
return {
|
|
54
74
|
total: this.testCount,
|
|
55
75
|
passed: this.passedCount,
|
|
56
|
-
failed: this.failedCount
|
|
76
|
+
failed: this.failedCount,
|
|
77
|
+
duration: this.totalDuration
|
|
57
78
|
};
|
|
58
79
|
}
|
|
59
80
|
|
|
@@ -0,0 +1,243 @@
|
|
|
1
|
+
/**
|
|
2
|
+
*
|
|
3
|
+
* Reldens - Whole Result Assert
|
|
4
|
+
* Full output assertions shared by every driver integration suite: the exact own enumerable key set of a
|
|
5
|
+
* record, the exact value of every column against the inserted fixture row, the exact length and full
|
|
6
|
+
* content of a list, the exact shape and content of every relation, and the exact count numbers. The very
|
|
7
|
+
* same expectations run on every driver, no driver is allowed an exception.
|
|
8
|
+
*
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
const assert = require('node:assert');
|
|
12
|
+
const { ColumnValueAssert } = require('./column-value-assert');
|
|
13
|
+
const { ObservedValue } = require('./observed-value');
|
|
14
|
+
const { TableColumnsFixtures } = require('../fixtures/table-columns-fixtures');
|
|
15
|
+
const { sc } = require('@reldens/utils');
|
|
16
|
+
|
|
17
|
+
class WholeResultAssert
|
|
18
|
+
{
|
|
19
|
+
|
|
20
|
+
static expectedRow(tableName, fixtureRow, overrides)
|
|
21
|
+
{
|
|
22
|
+
let tableSpec = TableColumnsFixtures[tableName];
|
|
23
|
+
let expectedRow = {};
|
|
24
|
+
for(let columnName of Object.keys(tableSpec.columns)){
|
|
25
|
+
expectedRow[columnName] = this.expectedColumnValue(tableSpec, fixtureRow, columnName);
|
|
26
|
+
}
|
|
27
|
+
if(sc.isObject(overrides)){
|
|
28
|
+
Object.assign(expectedRow, overrides);
|
|
29
|
+
}
|
|
30
|
+
return expectedRow;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
static expectedColumnValue(tableSpec, fixtureRow, columnName)
|
|
34
|
+
{
|
|
35
|
+
if(sc.hasOwn(fixtureRow, columnName)){
|
|
36
|
+
return fixtureRow[columnName];
|
|
37
|
+
}
|
|
38
|
+
if(sc.hasOwn(tableSpec.defaults, columnName)){
|
|
39
|
+
return tableSpec.defaults[columnName];
|
|
40
|
+
}
|
|
41
|
+
return ColumnValueAssert.anyValue;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
static expectedRows(tableName, fixtureRows)
|
|
45
|
+
{
|
|
46
|
+
let expectedRows = [];
|
|
47
|
+
for(let fixtureRow of fixtureRows){
|
|
48
|
+
expectedRows.push(this.expectedRow(tableName, fixtureRow, null));
|
|
49
|
+
}
|
|
50
|
+
return expectedRows;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
static fixtureRecord(actualRecord, tableName, fixtureRow, label)
|
|
54
|
+
{
|
|
55
|
+
return this.record(actualRecord, {
|
|
56
|
+
table: tableName,
|
|
57
|
+
expected: this.expectedRow(tableName, fixtureRow, null),
|
|
58
|
+
label: label
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
static fixtureList(actualList, tableName, fixtureRows, label)
|
|
63
|
+
{
|
|
64
|
+
return this.list(actualList, {
|
|
65
|
+
table: tableName,
|
|
66
|
+
expected: this.expectedRows(tableName, fixtureRows),
|
|
67
|
+
label: label
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
static record(actualRecord, props)
|
|
72
|
+
{
|
|
73
|
+
let label = props.label;
|
|
74
|
+
assert.ok(actualRecord, label+' was not returned, observed: '+ObservedValue.describe(actualRecord));
|
|
75
|
+
assert.strictEqual(
|
|
76
|
+
typeof actualRecord,
|
|
77
|
+
'object',
|
|
78
|
+
label+' is not a record object, observed: '+ObservedValue.describe(actualRecord)
|
|
79
|
+
);
|
|
80
|
+
let relations = sc.get(props, 'relations', {});
|
|
81
|
+
this.assertKeySet(actualRecord, props.table, relations, label);
|
|
82
|
+
ColumnValueAssert.assertColumns(actualRecord, props.table, props.expected, label);
|
|
83
|
+
this.assertRelations(actualRecord, relations, label);
|
|
84
|
+
return true;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
static list(actualList, props)
|
|
88
|
+
{
|
|
89
|
+
let label = props.label;
|
|
90
|
+
assert.ok(sc.isArray(actualList), label+' is not an array, observed: '+ObservedValue.describe(actualList));
|
|
91
|
+
assert.strictEqual(
|
|
92
|
+
actualList.length,
|
|
93
|
+
props.expected.length,
|
|
94
|
+
label+' length mismatch, expected '+props.expected.length+' records but observed '+actualList.length
|
|
95
|
+
+' with ids: ['+this.listIds(actualList).join(', ')+']'
|
|
96
|
+
);
|
|
97
|
+
let orderedList = sc.isTrue(props, 'keepOrder') ? actualList : this.sortedById(actualList);
|
|
98
|
+
for(let i = 0; i < props.expected.length; i++){
|
|
99
|
+
this.recordAt(orderedList, props, i);
|
|
100
|
+
}
|
|
101
|
+
return true;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
static recordAt(orderedList, props, index)
|
|
105
|
+
{
|
|
106
|
+
return this.record(orderedList[index], {
|
|
107
|
+
table: props.table,
|
|
108
|
+
expected: props.expected[index],
|
|
109
|
+
relations: this.relationsAt(sc.get(props, 'relations', {}), index),
|
|
110
|
+
label: props.label+'['+index+']'
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
static relationsAt(relations, index)
|
|
115
|
+
{
|
|
116
|
+
if(sc.isArray(relations)){
|
|
117
|
+
return relations[index];
|
|
118
|
+
}
|
|
119
|
+
return relations;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
static count(actualCount, expectedCount, label)
|
|
123
|
+
{
|
|
124
|
+
assert.strictEqual(
|
|
125
|
+
typeof actualCount,
|
|
126
|
+
'number',
|
|
127
|
+
label+' is not a number, observed: '+ObservedValue.describe(actualCount)
|
|
128
|
+
);
|
|
129
|
+
assert.strictEqual(
|
|
130
|
+
actualCount,
|
|
131
|
+
expectedCount,
|
|
132
|
+
label+' expected '+expectedCount+' but observed: '+ObservedValue.describe(actualCount)
|
|
133
|
+
);
|
|
134
|
+
return true;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
static failedOperation(actualError, label)
|
|
138
|
+
{
|
|
139
|
+
assert.ok(
|
|
140
|
+
actualError,
|
|
141
|
+
label+' must be rejected by the database but no error was thrown'
|
|
142
|
+
);
|
|
143
|
+
return true;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
static assertKeySet(actualRecord, tableName, relations, label)
|
|
147
|
+
{
|
|
148
|
+
let expectedKeys = Object.keys(TableColumnsFixtures[tableName].columns);
|
|
149
|
+
let allExpectedKeys = expectedKeys.concat(Object.keys(sc.isObject(relations) ? relations : {}));
|
|
150
|
+
let observedKeys = Object.keys(actualRecord);
|
|
151
|
+
let missingKeys = this.missingFrom(allExpectedKeys, observedKeys);
|
|
152
|
+
let notExpectedKeys = this.missingFrom(observedKeys, allExpectedKeys);
|
|
153
|
+
assert.strictEqual(
|
|
154
|
+
missingKeys.join(',')+'|'+notExpectedKeys.join(','),
|
|
155
|
+
'|',
|
|
156
|
+
label+' key set mismatch, missing: ['+missingKeys.join(', ')+'], not expected: ['
|
|
157
|
+
+notExpectedKeys.join(', ')+'], observed: ['+observedKeys.join(', ')+']'
|
|
158
|
+
);
|
|
159
|
+
return true;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
static missingFrom(sourceKeys, targetKeys)
|
|
163
|
+
{
|
|
164
|
+
let missingKeys = [];
|
|
165
|
+
for(let key of sourceKeys){
|
|
166
|
+
if(-1 === targetKeys.indexOf(key)){
|
|
167
|
+
missingKeys.push(key);
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
return missingKeys;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
static assertRelations(actualRecord, relations, label)
|
|
174
|
+
{
|
|
175
|
+
if(!sc.isObject(relations)){
|
|
176
|
+
return true;
|
|
177
|
+
}
|
|
178
|
+
for(let relationKey of Object.keys(relations)){
|
|
179
|
+
this.assertRelation(actualRecord[relationKey], relations[relationKey], label+'.'+relationKey);
|
|
180
|
+
}
|
|
181
|
+
return true;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
static assertRelation(relationValue, relationProps, label)
|
|
185
|
+
{
|
|
186
|
+
if(null === relationProps.expected){
|
|
187
|
+
assert.strictEqual(
|
|
188
|
+
relationValue,
|
|
189
|
+
null,
|
|
190
|
+
label+' expected an explicit null relation, observed: '+ObservedValue.describe(relationValue)
|
|
191
|
+
);
|
|
192
|
+
return true;
|
|
193
|
+
}
|
|
194
|
+
if(sc.isArray(relationProps.expected)){
|
|
195
|
+
assert.ok(
|
|
196
|
+
sc.isArray(relationValue),
|
|
197
|
+
label+' expected an array relation, observed: '+ObservedValue.describe(relationValue)
|
|
198
|
+
);
|
|
199
|
+
return this.list(relationValue, this.relationProps(relationProps, label));
|
|
200
|
+
}
|
|
201
|
+
assert.ok(
|
|
202
|
+
!sc.isArray(relationValue),
|
|
203
|
+
label+' expected a single record relation but observed an array: '
|
|
204
|
+
+ObservedValue.describe(relationValue)
|
|
205
|
+
);
|
|
206
|
+
return this.record(relationValue, this.relationProps(relationProps, label));
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
static relationProps(relationProps, label)
|
|
210
|
+
{
|
|
211
|
+
return {
|
|
212
|
+
table: relationProps.table,
|
|
213
|
+
expected: relationProps.expected,
|
|
214
|
+
relations: sc.get(relationProps, 'relations', {}),
|
|
215
|
+
keepOrder: sc.get(relationProps, 'keepOrder', false),
|
|
216
|
+
label: label
|
|
217
|
+
};
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
static listIds(actualList)
|
|
221
|
+
{
|
|
222
|
+
let ids = [];
|
|
223
|
+
for(let actualRecord of actualList){
|
|
224
|
+
ids.push(sc.get(actualRecord, 'id', ObservedValue.describe(actualRecord)));
|
|
225
|
+
}
|
|
226
|
+
return ids;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
static sortedById(actualList)
|
|
230
|
+
{
|
|
231
|
+
let orderedList = [];
|
|
232
|
+
for(let actualRecord of actualList){
|
|
233
|
+
orderedList.push(actualRecord);
|
|
234
|
+
}
|
|
235
|
+
orderedList.sort((firstRecord, secondRecord) => {
|
|
236
|
+
return Number(sc.get(firstRecord, 'id', 0)) - Number(sc.get(secondRecord, 'id', 0));
|
|
237
|
+
});
|
|
238
|
+
return orderedList;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
module.exports.WholeResultAssert = WholeResultAssert;
|