@reldens/storage 0.87.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 +141 -7
- package/README.md +14 -2
- 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-client-loader.js +4 -0
- 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,232 @@
|
|
|
1
|
+
/**
|
|
2
|
+
*
|
|
3
|
+
* Reldens - TypeMapper Test
|
|
4
|
+
*
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
const { TestRunner, assert } = require('../utils/test-runner');
|
|
8
|
+
const { TypeMapper } = require('../../lib/type-mapper');
|
|
9
|
+
|
|
10
|
+
class TypeMapperTest
|
|
11
|
+
{
|
|
12
|
+
|
|
13
|
+
constructor()
|
|
14
|
+
{
|
|
15
|
+
this.runner = new TestRunner();
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
async run()
|
|
19
|
+
{
|
|
20
|
+
this.runner.suite('TypeMapper');
|
|
21
|
+
await this.testMapDbTypeToJsTypeNumeric();
|
|
22
|
+
await this.testMapDbTypeToJsTypeString();
|
|
23
|
+
await this.testMapDbTypeToJsTypeDate();
|
|
24
|
+
await this.testMapDbTypeToJsTypeBoolean();
|
|
25
|
+
await this.testMapDbTypeToJsTypeJson();
|
|
26
|
+
await this.testMapDbTypeToJsTypeBinary();
|
|
27
|
+
await this.testMapDbTypeToJsTypeCaseInsensitive();
|
|
28
|
+
await this.testMapDbTypeToPrismaTypeNumeric();
|
|
29
|
+
await this.testMapDbTypeToPrismaTypeString();
|
|
30
|
+
await this.testMapDbTypeToPrismaTypeDate();
|
|
31
|
+
await this.testMapDbTypeToPrismaTypeBoolean();
|
|
32
|
+
await this.testMapDbTypeToPrismaTypeJson();
|
|
33
|
+
await this.testMapDbTypeToPrismaTypeBinary();
|
|
34
|
+
await this.testMapDbTypeToPrismaTypeUnknown();
|
|
35
|
+
await this.testMapDbTypeToPrismaTypeCaseInsensitive();
|
|
36
|
+
return this.runner.getResults();
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
async testMapDbTypeToJsTypeNumeric()
|
|
40
|
+
{
|
|
41
|
+
this.runner.group('mapDbTypeToJsType - numeric types');
|
|
42
|
+
await this.runner.test('should map int types to number', async () => {
|
|
43
|
+
assert.strictEqual(TypeMapper.mapDbTypeToJsType('int'), 'number');
|
|
44
|
+
assert.strictEqual(TypeMapper.mapDbTypeToJsType('integer'), 'number');
|
|
45
|
+
assert.strictEqual(TypeMapper.mapDbTypeToJsType('tinyint'), 'number');
|
|
46
|
+
assert.strictEqual(TypeMapper.mapDbTypeToJsType('smallint'), 'number');
|
|
47
|
+
assert.strictEqual(TypeMapper.mapDbTypeToJsType('mediumint'), 'number');
|
|
48
|
+
assert.strictEqual(TypeMapper.mapDbTypeToJsType('bigint'), 'number');
|
|
49
|
+
});
|
|
50
|
+
await this.runner.test('should map decimal types to number', async () => {
|
|
51
|
+
assert.strictEqual(TypeMapper.mapDbTypeToJsType('decimal'), 'number');
|
|
52
|
+
assert.strictEqual(TypeMapper.mapDbTypeToJsType('float'), 'number');
|
|
53
|
+
assert.strictEqual(TypeMapper.mapDbTypeToJsType('double'), 'number');
|
|
54
|
+
});
|
|
55
|
+
await this.runner.test('should map year to number', async () => {
|
|
56
|
+
assert.strictEqual(TypeMapper.mapDbTypeToJsType('year'), 'number');
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
async testMapDbTypeToJsTypeString()
|
|
61
|
+
{
|
|
62
|
+
this.runner.group('mapDbTypeToJsType - string types');
|
|
63
|
+
await this.runner.test('should map varchar and char to string', async () => {
|
|
64
|
+
assert.strictEqual(TypeMapper.mapDbTypeToJsType('varchar'), 'string');
|
|
65
|
+
assert.strictEqual(TypeMapper.mapDbTypeToJsType('char'), 'string');
|
|
66
|
+
});
|
|
67
|
+
await this.runner.test('should map text types to string', async () => {
|
|
68
|
+
assert.strictEqual(TypeMapper.mapDbTypeToJsType('text'), 'string');
|
|
69
|
+
assert.strictEqual(TypeMapper.mapDbTypeToJsType('tinytext'), 'string');
|
|
70
|
+
assert.strictEqual(TypeMapper.mapDbTypeToJsType('mediumtext'), 'string');
|
|
71
|
+
assert.strictEqual(TypeMapper.mapDbTypeToJsType('longtext'), 'string');
|
|
72
|
+
});
|
|
73
|
+
await this.runner.test('should map time to string', async () => {
|
|
74
|
+
assert.strictEqual(TypeMapper.mapDbTypeToJsType('time'), 'string');
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
async testMapDbTypeToJsTypeDate()
|
|
79
|
+
{
|
|
80
|
+
this.runner.group('mapDbTypeToJsType - date types');
|
|
81
|
+
await this.runner.test('should map date types to Date', async () => {
|
|
82
|
+
assert.strictEqual(TypeMapper.mapDbTypeToJsType('date'), 'Date');
|
|
83
|
+
assert.strictEqual(TypeMapper.mapDbTypeToJsType('datetime'), 'Date');
|
|
84
|
+
assert.strictEqual(TypeMapper.mapDbTypeToJsType('timestamp'), 'Date');
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
async testMapDbTypeToJsTypeBoolean()
|
|
89
|
+
{
|
|
90
|
+
this.runner.group('mapDbTypeToJsType - boolean types');
|
|
91
|
+
await this.runner.test('should map boolean types to boolean', async () => {
|
|
92
|
+
assert.strictEqual(TypeMapper.mapDbTypeToJsType('boolean'), 'boolean');
|
|
93
|
+
assert.strictEqual(TypeMapper.mapDbTypeToJsType('bool'), 'boolean');
|
|
94
|
+
assert.strictEqual(TypeMapper.mapDbTypeToJsType('bit'), 'boolean');
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
async testMapDbTypeToJsTypeJson()
|
|
99
|
+
{
|
|
100
|
+
this.runner.group('mapDbTypeToJsType - json type');
|
|
101
|
+
await this.runner.test('should map json to object', async () => {
|
|
102
|
+
assert.strictEqual(TypeMapper.mapDbTypeToJsType('json'), 'object');
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
async testMapDbTypeToJsTypeBinary()
|
|
107
|
+
{
|
|
108
|
+
this.runner.group('mapDbTypeToJsType - binary types');
|
|
109
|
+
await this.runner.test('should map binary types to Buffer', async () => {
|
|
110
|
+
assert.strictEqual(TypeMapper.mapDbTypeToJsType('binary'), 'Buffer');
|
|
111
|
+
assert.strictEqual(TypeMapper.mapDbTypeToJsType('varbinary'), 'Buffer');
|
|
112
|
+
assert.strictEqual(TypeMapper.mapDbTypeToJsType('blob'), 'Buffer');
|
|
113
|
+
assert.strictEqual(TypeMapper.mapDbTypeToJsType('tinyblob'), 'Buffer');
|
|
114
|
+
assert.strictEqual(TypeMapper.mapDbTypeToJsType('mediumblob'), 'Buffer');
|
|
115
|
+
assert.strictEqual(TypeMapper.mapDbTypeToJsType('longblob'), 'Buffer');
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
async testMapDbTypeToJsTypeCaseInsensitive()
|
|
120
|
+
{
|
|
121
|
+
this.runner.group('mapDbTypeToJsType - case insensitive');
|
|
122
|
+
await this.runner.test('should handle uppercase type names', async () => {
|
|
123
|
+
assert.strictEqual(TypeMapper.mapDbTypeToJsType('INT'), 'number');
|
|
124
|
+
assert.strictEqual(TypeMapper.mapDbTypeToJsType('VARCHAR'), 'string');
|
|
125
|
+
assert.strictEqual(TypeMapper.mapDbTypeToJsType('DATETIME'), 'Date');
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
async testMapDbTypeToPrismaTypeNumeric()
|
|
130
|
+
{
|
|
131
|
+
this.runner.group('mapDbTypeToPrismaType - numeric types');
|
|
132
|
+
await this.runner.test('should map int types to Int', async () => {
|
|
133
|
+
assert.strictEqual(TypeMapper.mapDbTypeToPrismaType('int'), 'Int');
|
|
134
|
+
assert.strictEqual(TypeMapper.mapDbTypeToPrismaType('integer'), 'Int');
|
|
135
|
+
assert.strictEqual(TypeMapper.mapDbTypeToPrismaType('tinyint'), 'Int');
|
|
136
|
+
assert.strictEqual(TypeMapper.mapDbTypeToPrismaType('smallint'), 'Int');
|
|
137
|
+
assert.strictEqual(TypeMapper.mapDbTypeToPrismaType('mediumint'), 'Int');
|
|
138
|
+
assert.strictEqual(TypeMapper.mapDbTypeToPrismaType('year'), 'Int');
|
|
139
|
+
});
|
|
140
|
+
await this.runner.test('should map bigint to BigInt', async () => {
|
|
141
|
+
assert.strictEqual(TypeMapper.mapDbTypeToPrismaType('bigint'), 'BigInt');
|
|
142
|
+
});
|
|
143
|
+
await this.runner.test('should map decimal to Decimal', async () => {
|
|
144
|
+
assert.strictEqual(TypeMapper.mapDbTypeToPrismaType('decimal'), 'Decimal');
|
|
145
|
+
});
|
|
146
|
+
await this.runner.test('should map float types to Float', async () => {
|
|
147
|
+
assert.strictEqual(TypeMapper.mapDbTypeToPrismaType('float'), 'Float');
|
|
148
|
+
assert.strictEqual(TypeMapper.mapDbTypeToPrismaType('double'), 'Float');
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
async testMapDbTypeToPrismaTypeString()
|
|
153
|
+
{
|
|
154
|
+
this.runner.group('mapDbTypeToPrismaType - string types');
|
|
155
|
+
await this.runner.test('should map varchar and char to String', async () => {
|
|
156
|
+
assert.strictEqual(TypeMapper.mapDbTypeToPrismaType('varchar'), 'String');
|
|
157
|
+
assert.strictEqual(TypeMapper.mapDbTypeToPrismaType('char'), 'String');
|
|
158
|
+
});
|
|
159
|
+
await this.runner.test('should map text types to String', async () => {
|
|
160
|
+
assert.strictEqual(TypeMapper.mapDbTypeToPrismaType('text'), 'String');
|
|
161
|
+
assert.strictEqual(TypeMapper.mapDbTypeToPrismaType('tinytext'), 'String');
|
|
162
|
+
assert.strictEqual(TypeMapper.mapDbTypeToPrismaType('mediumtext'), 'String');
|
|
163
|
+
assert.strictEqual(TypeMapper.mapDbTypeToPrismaType('longtext'), 'String');
|
|
164
|
+
});
|
|
165
|
+
await this.runner.test('should map time to String', async () => {
|
|
166
|
+
assert.strictEqual(TypeMapper.mapDbTypeToPrismaType('time'), 'String');
|
|
167
|
+
});
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
async testMapDbTypeToPrismaTypeDate()
|
|
171
|
+
{
|
|
172
|
+
this.runner.group('mapDbTypeToPrismaType - date types');
|
|
173
|
+
await this.runner.test('should map date types to DateTime', async () => {
|
|
174
|
+
assert.strictEqual(TypeMapper.mapDbTypeToPrismaType('date'), 'DateTime');
|
|
175
|
+
assert.strictEqual(TypeMapper.mapDbTypeToPrismaType('datetime'), 'DateTime');
|
|
176
|
+
assert.strictEqual(TypeMapper.mapDbTypeToPrismaType('timestamp'), 'DateTime');
|
|
177
|
+
});
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
async testMapDbTypeToPrismaTypeBoolean()
|
|
181
|
+
{
|
|
182
|
+
this.runner.group('mapDbTypeToPrismaType - boolean types');
|
|
183
|
+
await this.runner.test('should map boolean types to Boolean', async () => {
|
|
184
|
+
assert.strictEqual(TypeMapper.mapDbTypeToPrismaType('boolean'), 'Boolean');
|
|
185
|
+
assert.strictEqual(TypeMapper.mapDbTypeToPrismaType('bool'), 'Boolean');
|
|
186
|
+
assert.strictEqual(TypeMapper.mapDbTypeToPrismaType('bit'), 'Boolean');
|
|
187
|
+
});
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
async testMapDbTypeToPrismaTypeJson()
|
|
191
|
+
{
|
|
192
|
+
this.runner.group('mapDbTypeToPrismaType - json type');
|
|
193
|
+
await this.runner.test('should map json to Json', async () => {
|
|
194
|
+
assert.strictEqual(TypeMapper.mapDbTypeToPrismaType('json'), 'Json');
|
|
195
|
+
});
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
async testMapDbTypeToPrismaTypeBinary()
|
|
199
|
+
{
|
|
200
|
+
this.runner.group('mapDbTypeToPrismaType - binary types');
|
|
201
|
+
await this.runner.test('should map binary types to Bytes', async () => {
|
|
202
|
+
assert.strictEqual(TypeMapper.mapDbTypeToPrismaType('binary'), 'Bytes');
|
|
203
|
+
assert.strictEqual(TypeMapper.mapDbTypeToPrismaType('varbinary'), 'Bytes');
|
|
204
|
+
assert.strictEqual(TypeMapper.mapDbTypeToPrismaType('blob'), 'Bytes');
|
|
205
|
+
assert.strictEqual(TypeMapper.mapDbTypeToPrismaType('tinyblob'), 'Bytes');
|
|
206
|
+
assert.strictEqual(TypeMapper.mapDbTypeToPrismaType('mediumblob'), 'Bytes');
|
|
207
|
+
assert.strictEqual(TypeMapper.mapDbTypeToPrismaType('longblob'), 'Bytes');
|
|
208
|
+
});
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
async testMapDbTypeToPrismaTypeUnknown()
|
|
212
|
+
{
|
|
213
|
+
this.runner.group('mapDbTypeToPrismaType - unknown types');
|
|
214
|
+
await this.runner.test('should default to String for unknown types', async () => {
|
|
215
|
+
assert.strictEqual(TypeMapper.mapDbTypeToPrismaType('unknowntype'), 'String');
|
|
216
|
+
assert.strictEqual(TypeMapper.mapDbTypeToPrismaType('customtype'), 'String');
|
|
217
|
+
});
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
async testMapDbTypeToPrismaTypeCaseInsensitive()
|
|
221
|
+
{
|
|
222
|
+
this.runner.group('mapDbTypeToPrismaType - case insensitive');
|
|
223
|
+
await this.runner.test('should handle uppercase type names', async () => {
|
|
224
|
+
assert.strictEqual(TypeMapper.mapDbTypeToPrismaType('INT'), 'Int');
|
|
225
|
+
assert.strictEqual(TypeMapper.mapDbTypeToPrismaType('VARCHAR'), 'String');
|
|
226
|
+
assert.strictEqual(TypeMapper.mapDbTypeToPrismaType('DATETIME'), 'DateTime');
|
|
227
|
+
});
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
module.exports = TypeMapperTest;
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
/**
|
|
2
|
+
*
|
|
3
|
+
* Reldens - Custom Test Reporter
|
|
4
|
+
* Filters test output to remove suites, cancelled (treats as fail), skipped, todo
|
|
5
|
+
*
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
const { Transform } = require('node:stream');
|
|
9
|
+
|
|
10
|
+
class OutputFilter extends Transform
|
|
11
|
+
{
|
|
12
|
+
constructor()
|
|
13
|
+
{
|
|
14
|
+
super();
|
|
15
|
+
this.buffer = '';
|
|
16
|
+
this.stats = {
|
|
17
|
+
tests: 0,
|
|
18
|
+
pass: 0,
|
|
19
|
+
fail: 0,
|
|
20
|
+
cancelled: 0
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
_transform(chunk, encoding, callback)
|
|
25
|
+
{
|
|
26
|
+
this.buffer += chunk.toString();
|
|
27
|
+
let lines = this.buffer.split('\n');
|
|
28
|
+
this.buffer = lines.pop();
|
|
29
|
+
for(let line of lines){
|
|
30
|
+
// Extract stats before filtering
|
|
31
|
+
if(line.includes('ℹ tests ')){
|
|
32
|
+
this.stats.tests = parseInt(line.match(/\d+/)[0]);
|
|
33
|
+
}
|
|
34
|
+
if(line.includes('ℹ pass ')){
|
|
35
|
+
this.stats.pass = parseInt(line.match(/\d+/)[0]);
|
|
36
|
+
}
|
|
37
|
+
if(line.includes('ℹ fail ')){
|
|
38
|
+
this.stats.fail = parseInt(line.match(/\d+/)[0]);
|
|
39
|
+
}
|
|
40
|
+
if(line.includes('ℹ cancelled ')){
|
|
41
|
+
this.stats.cancelled = parseInt(line.match(/\d+/)[0]);
|
|
42
|
+
continue; // Skip this line
|
|
43
|
+
}
|
|
44
|
+
if(line.includes('ℹ skipped ')){
|
|
45
|
+
continue; // Skip this line
|
|
46
|
+
}
|
|
47
|
+
if(line.includes('ℹ todo ')){
|
|
48
|
+
continue; // Skip this line
|
|
49
|
+
}
|
|
50
|
+
if(line.includes('ℹ suites ')){
|
|
51
|
+
continue; // Skip this line
|
|
52
|
+
}
|
|
53
|
+
this.push(line + '\n');
|
|
54
|
+
}
|
|
55
|
+
callback();
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
_flush(callback)
|
|
59
|
+
{
|
|
60
|
+
if(this.buffer){
|
|
61
|
+
this.push(this.buffer);
|
|
62
|
+
}
|
|
63
|
+
if(0 < this.stats.tests){
|
|
64
|
+
this.push('\n');
|
|
65
|
+
this.push('ℹ tests ' + this.stats.tests + '\n');
|
|
66
|
+
this.push('ℹ pass ' + this.stats.pass + '\n');
|
|
67
|
+
this.push('ℹ fail ' + (this.stats.fail + this.stats.cancelled) + '\n');
|
|
68
|
+
}
|
|
69
|
+
callback();
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
module.exports = { OutputFilter };
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
/**
|
|
2
|
+
*
|
|
3
|
+
* Reldens - Driver Registry
|
|
4
|
+
* Singleton registry for shared driver instances across integration tests
|
|
5
|
+
* Reduces test setup/teardown from 9x to 3x (70% performance improvement)
|
|
6
|
+
*
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
const { Logger } = require('@reldens/utils');
|
|
10
|
+
const { FileHandler } = require('@reldens/server-utils');
|
|
11
|
+
const { TestHelpers } = require('./test-helpers');
|
|
12
|
+
|
|
13
|
+
class DriverRegistry
|
|
14
|
+
{
|
|
15
|
+
|
|
16
|
+
constructor()
|
|
17
|
+
{
|
|
18
|
+
this.sharedState = {
|
|
19
|
+
drivers: {},
|
|
20
|
+
repos: {},
|
|
21
|
+
initialized: false,
|
|
22
|
+
instanceId: Math.random()
|
|
23
|
+
};
|
|
24
|
+
this.schemaPath = FileHandler.joinPaths(__dirname, '..', 'fixtures', 'sql', 'test-schema.sql');
|
|
25
|
+
this.repoNames = ['testCategories', 'testProducts', 'testReviews'];
|
|
26
|
+
this.driverNames = ['objection-js', 'mikro-orm', 'prisma'];
|
|
27
|
+
this.skipGeneration = false;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
async initialize()
|
|
31
|
+
{
|
|
32
|
+
if(this.sharedState.initialized){
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
Logger.info('='.repeat(60));
|
|
36
|
+
Logger.info('INITIALIZING SHARED DRIVER REGISTRY');
|
|
37
|
+
Logger.info('='.repeat(60));
|
|
38
|
+
for(let driverName of this.driverNames){
|
|
39
|
+
let dataServer = null;
|
|
40
|
+
try {
|
|
41
|
+
Logger.info('REGISTRY: Setting up driver: '+driverName);
|
|
42
|
+
let rawEntities = {};
|
|
43
|
+
dataServer = await TestHelpers.setupDriver(driverName, rawEntities);
|
|
44
|
+
let schemaSql = FileHandler.readFile(this.schemaPath);
|
|
45
|
+
await TestHelpers.executeRawSQL(dataServer, schemaSql);
|
|
46
|
+
await this.generateOrLoadEntities(dataServer, driverName);
|
|
47
|
+
let repos = {};
|
|
48
|
+
for(let entityName of this.repoNames){
|
|
49
|
+
let repo = dataServer.getEntity(entityName);
|
|
50
|
+
Logger.info('REGISTRY: Got entity '+entityName+' for '+driverName+': '+(repo ? 'EXISTS' : 'NULL'));
|
|
51
|
+
repos[entityName] = repo;
|
|
52
|
+
}
|
|
53
|
+
Logger.info('REGISTRY: About to assign driver '+driverName+' to registry');
|
|
54
|
+
this.sharedState.drivers[driverName] = dataServer;
|
|
55
|
+
this.sharedState.repos[driverName] = repos;
|
|
56
|
+
Logger.info(
|
|
57
|
+
'REGISTRY: Assigned! drivers['+driverName+'] = '
|
|
58
|
+
+(this.sharedState.drivers[driverName] ? 'EXISTS' : 'NULL')
|
|
59
|
+
);
|
|
60
|
+
Logger.info(
|
|
61
|
+
'REGISTRY: Assigned! repos['+driverName+'] = '
|
|
62
|
+
+(this.sharedState.repos[driverName] ? 'EXISTS' : 'NULL')
|
|
63
|
+
);
|
|
64
|
+
Logger.info('REGISTRY: Driver '+driverName+' initialized successfully');
|
|
65
|
+
} catch(error) {
|
|
66
|
+
Logger.critical('REGISTRY: Failed to initialize driver '+driverName);
|
|
67
|
+
Logger.critical('ERROR: '+error.message);
|
|
68
|
+
Logger.critical('STACK: '+error.stack);
|
|
69
|
+
if(dataServer){
|
|
70
|
+
try {
|
|
71
|
+
await TestHelpers.teardownDriver(dataServer);
|
|
72
|
+
} catch(cleanupError) {
|
|
73
|
+
Logger.warning('Failed to disconnect '+driverName+' after init error: '+cleanupError.message);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
this.sharedState.drivers[driverName] = null;
|
|
77
|
+
this.sharedState.repos[driverName] = {};
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
this.sharedState.initialized = true;
|
|
81
|
+
Logger.info('='.repeat(60));
|
|
82
|
+
Logger.info('DRIVER REGISTRY INITIALIZED - instanceId = '+this.sharedState.instanceId);
|
|
83
|
+
Logger.info('REGISTRY DEBUG: drivers keys = '+Object.keys(this.sharedState.drivers).join(', '));
|
|
84
|
+
Logger.info('REGISTRY DEBUG: repos keys = '+Object.keys(this.sharedState.repos).join(', '));
|
|
85
|
+
Logger.info('='.repeat(60));
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
async generateOrLoadEntities(dataServer, driverName)
|
|
89
|
+
{
|
|
90
|
+
if(this.skipGeneration){
|
|
91
|
+
Logger.info('REGISTRY: Skipping entity generation for '+driverName+' (using existing files)');
|
|
92
|
+
return await TestHelpers.loadGeneratedEntities(dataServer, driverName);
|
|
93
|
+
}
|
|
94
|
+
return await TestHelpers.generateTestEntities(dataServer, driverName);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
getDriver(driverName)
|
|
98
|
+
{
|
|
99
|
+
Logger.info('REGISTRY: getDriver called - instanceId = '+this.sharedState.instanceId);
|
|
100
|
+
let driverInstance = this.sharedState.drivers[driverName];
|
|
101
|
+
Logger.info(
|
|
102
|
+
'REGISTRY: getDriver('+driverName+') typeof = '
|
|
103
|
+
+(typeof driverInstance)+', null? '+(driverInstance === null)+', undefined? '+(driverInstance === undefined)
|
|
104
|
+
);
|
|
105
|
+
return driverInstance;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
getRepos(driverName)
|
|
109
|
+
{
|
|
110
|
+
Logger.info('REGISTRY: getRepos called - instanceId = '+this.sharedState.instanceId);
|
|
111
|
+
let reposObject = this.sharedState.repos[driverName];
|
|
112
|
+
Logger.info(
|
|
113
|
+
'REGISTRY: getRepos('+driverName+') typeof = '
|
|
114
|
+
+(typeof reposObject)+', keys = '+(reposObject ? JSON.stringify(Object.keys(reposObject)) : 'NO VALUE')
|
|
115
|
+
);
|
|
116
|
+
return reposObject;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
async cleanup()
|
|
120
|
+
{
|
|
121
|
+
Logger.info('='.repeat(60));
|
|
122
|
+
Logger.info('CLEANING UP DRIVER REGISTRY');
|
|
123
|
+
Logger.info('='.repeat(60));
|
|
124
|
+
for(let driverName of this.driverNames){
|
|
125
|
+
let dataServer = this.sharedState.drivers[driverName];
|
|
126
|
+
if(dataServer){
|
|
127
|
+
try {
|
|
128
|
+
await TestHelpers.dropTestTables(dataServer);
|
|
129
|
+
await TestHelpers.teardownDriver(dataServer);
|
|
130
|
+
} catch(error) {
|
|
131
|
+
Logger.warning('Failed to cleanup driver '+driverName+': '+error.message);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
TestHelpers.cleanupGeneratedFiles();
|
|
136
|
+
this.sharedState.drivers = {};
|
|
137
|
+
this.sharedState.repos = {};
|
|
138
|
+
this.sharedState.initialized = false;
|
|
139
|
+
Logger.info('Driver registry cleanup complete');
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
module.exports.DriverRegistry = DriverRegistry;
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
/**
|
|
2
|
+
*
|
|
3
|
+
* Reldens - Storage - Prisma Test Subprocess Worker
|
|
4
|
+
*
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
const { FileHandler } = require('@reldens/server-utils');
|
|
8
|
+
const { Logger, sc } = require('@reldens/utils');
|
|
9
|
+
const { exec } = require('child_process');
|
|
10
|
+
const { promisify } = require('util');
|
|
11
|
+
const execAsync = promisify(exec);
|
|
12
|
+
|
|
13
|
+
class PrismaTestSubprocessWorker
|
|
14
|
+
{
|
|
15
|
+
|
|
16
|
+
constructor()
|
|
17
|
+
{
|
|
18
|
+
this.setupProcessHandlers();
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
setupProcessHandlers()
|
|
22
|
+
{
|
|
23
|
+
process.on('message', async (message) => {
|
|
24
|
+
try {
|
|
25
|
+
await this.processIncomingMessage(message);
|
|
26
|
+
} catch(error) {
|
|
27
|
+
Logger.error('PrismaTestSubprocessWorker error: '+error.message);
|
|
28
|
+
this.sendErrorResponse(error.message);
|
|
29
|
+
setTimeout(() => process.exit(1), 100);
|
|
30
|
+
}
|
|
31
|
+
});
|
|
32
|
+
process.on('uncaughtException', (error) => {
|
|
33
|
+
Logger.error('PrismaTestSubprocessWorker uncaught exception: '+error.message);
|
|
34
|
+
this.sendErrorResponse(error.message);
|
|
35
|
+
setTimeout(() => process.exit(1), 100);
|
|
36
|
+
});
|
|
37
|
+
process.on('unhandledRejection', (error) => {
|
|
38
|
+
Logger.error('PrismaTestSubprocessWorker unhandled rejection: '+error.message);
|
|
39
|
+
this.sendErrorResponse(error.message);
|
|
40
|
+
setTimeout(() => process.exit(1), 100);
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
async ensureStubFiles(projectRoot)
|
|
45
|
+
{
|
|
46
|
+
let stubPath = FileHandler.joinPaths(projectRoot, 'node_modules', '.prisma', 'client', 'default.js');
|
|
47
|
+
if(FileHandler.exists(stubPath)){
|
|
48
|
+
Logger.info('Subprocess: Prisma stub files already exist');
|
|
49
|
+
return true;
|
|
50
|
+
}
|
|
51
|
+
Logger.info('Subprocess: Creating Prisma stub files...');
|
|
52
|
+
try {
|
|
53
|
+
let postinstallPath = FileHandler.joinPaths(
|
|
54
|
+
projectRoot,
|
|
55
|
+
'node_modules',
|
|
56
|
+
'@prisma',
|
|
57
|
+
'client',
|
|
58
|
+
'scripts',
|
|
59
|
+
'postinstall.js'
|
|
60
|
+
);
|
|
61
|
+
if(!FileHandler.exists(postinstallPath)){
|
|
62
|
+
Logger.warning('Subprocess: Prisma postinstall script not found at: '+postinstallPath);
|
|
63
|
+
return false;
|
|
64
|
+
}
|
|
65
|
+
let { execSync } = require('child_process');
|
|
66
|
+
execSync('node "' + postinstallPath + '"', {
|
|
67
|
+
cwd: projectRoot,
|
|
68
|
+
stdio: 'inherit'
|
|
69
|
+
});
|
|
70
|
+
Logger.info('Subprocess: Prisma stub files created successfully');
|
|
71
|
+
return true;
|
|
72
|
+
} catch(error) {
|
|
73
|
+
Logger.error('Subprocess: Failed to create stub files: '+error.message);
|
|
74
|
+
return false;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
async processIncomingMessage(message)
|
|
79
|
+
{
|
|
80
|
+
let projectRoot = sc.get(message, 'projectRoot', './');
|
|
81
|
+
let config = sc.get(message, 'config', {});
|
|
82
|
+
let stubsCreated = await this.ensureStubFiles(projectRoot);
|
|
83
|
+
if(!stubsCreated){
|
|
84
|
+
Logger.warning('Subprocess: Stub files could not be created, proceeding anyway');
|
|
85
|
+
}
|
|
86
|
+
let generatedClient = await this.generateMinimalPrismaClient(projectRoot, config);
|
|
87
|
+
if(!generatedClient){
|
|
88
|
+
this.sendErrorResponse('Failed to generate Prisma client.');
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
await generatedClient.$disconnect();
|
|
92
|
+
this.sendSuccessResponse('Subprocess Prisma client generation completed.');
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
async generateMinimalPrismaClient(projectRoot, config)
|
|
96
|
+
{
|
|
97
|
+
let prismaPath = FileHandler.joinPaths(projectRoot, 'prisma');
|
|
98
|
+
let schemaPath = FileHandler.joinPaths(prismaPath, 'schema.prisma');
|
|
99
|
+
FileHandler.createFolder(prismaPath);
|
|
100
|
+
let dbUrl = 'mysql://'+config.user+':'+config.password+'@'+config.host+':'+config.port+'/'+config.database;
|
|
101
|
+
let schemaContent = 'generator client {\n'
|
|
102
|
+
+ ' provider = "prisma-client-js"\n'
|
|
103
|
+
+ ' output = "./client"\n'
|
|
104
|
+
+ '}\n'
|
|
105
|
+
+ '\n'
|
|
106
|
+
+ 'datasource db {\n'
|
|
107
|
+
+ ' provider = "mysql"\n'
|
|
108
|
+
+ ' url = "'+dbUrl+'"\n'
|
|
109
|
+
+ '}';
|
|
110
|
+
FileHandler.writeFile(schemaPath, schemaContent);
|
|
111
|
+
Logger.info('Subprocess: Running prisma db pull to introspect database...');
|
|
112
|
+
try {
|
|
113
|
+
let { stdout: pullStdout, stderr: pullStderr } = await execAsync('npx prisma db pull', {cwd: projectRoot});
|
|
114
|
+
if(pullStderr && !pullStderr.includes('Introspected')){
|
|
115
|
+
Logger.warning('Subprocess: Prisma db pull stderr: '+pullStderr);
|
|
116
|
+
}
|
|
117
|
+
Logger.info('Subprocess: Database introspection completed');
|
|
118
|
+
} catch(error) {
|
|
119
|
+
Logger.critical('Subprocess: Prisma db pull failed: '+error.message);
|
|
120
|
+
return false;
|
|
121
|
+
}
|
|
122
|
+
Logger.info('Subprocess: Running prisma generate...');
|
|
123
|
+
try {
|
|
124
|
+
let { stdout, stderr } = await execAsync('npx prisma generate', {cwd: projectRoot});
|
|
125
|
+
if(stderr && !stderr.includes('Generated Prisma Client')){
|
|
126
|
+
Logger.warning('Subprocess: Prisma generate stderr: '+stderr);
|
|
127
|
+
}
|
|
128
|
+
Logger.info('Subprocess: Prisma client generated successfully');
|
|
129
|
+
let clientPath = FileHandler.joinPaths(projectRoot, 'prisma', 'client');
|
|
130
|
+
let { PrismaClient } = require(clientPath);
|
|
131
|
+
return new PrismaClient();
|
|
132
|
+
} catch(error) {
|
|
133
|
+
Logger.critical('Subprocess: Prisma generate failed: '+error.message);
|
|
134
|
+
return false;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
sendSuccessResponse(message)
|
|
139
|
+
{
|
|
140
|
+
process.send({success: true, message: message});
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
sendErrorResponse(errorMessage)
|
|
144
|
+
{
|
|
145
|
+
process.send({success: false, error: errorMessage});
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
new PrismaTestSubprocessWorker();
|