@reldens/storage 0.95.0 → 0.96.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.
@@ -1667,38 +1667,47 @@ async rawQuery(content)
1667
1667
 
1668
1668
  **Return type:** Array or false
1669
1669
 
1670
- #### Prisma Implementation (prisma-data-server.js:118-134)
1670
+ #### Prisma Implementation (prisma-data-server.js:119-138)
1671
1671
 
1672
1672
  ```javascript
1673
1673
  async rawQuery(content)
1674
1674
  {
1675
1675
  try {
1676
- let statements = this.splitSqlStatements(content);
1677
- let results = [];
1678
- for(let statement of statements){
1679
- if(!statement.trim()){
1680
- continue;
1681
- }
1682
- let result = await this.prisma.$executeRawUnsafe(statement);
1683
- results.push(result);
1676
+ let statements = this.splitSqlStatements(content)
1677
+ .map(s => s.trim())
1678
+ .filter(s => '' !== s);
1679
+ if(0 === statements.length){
1680
+ return false;
1684
1681
  }
1685
- return results;
1686
- } catch(error){
1687
- Logger.error('Prisma raw query error:', error.message);
1682
+ let results = [];
1683
+ await this.prisma.$transaction(
1684
+ async (tx) => { results = await this.executeStatements(statements, tx); },
1685
+ {timeout: 60000}
1686
+ );
1687
+ return 1 === results.length ? results[0] : results;
1688
+ } catch(error) {
1689
+ Logger.error('Raw query failed: '+error.message);
1688
1690
  return false;
1689
1691
  }
1690
1692
  }
1691
1693
  ```
1692
1694
 
1693
- **Uses:** Prisma.$executeRawUnsafe()
1694
- **Special:** Splits statements and executes separately
1695
+ **Uses:** `$transaction` wrapping `executeStatements()`, which calls `executeStatement()` per statement:
1696
+ - SELECT / SHOW `$queryRawUnsafe` → returns rows array
1697
+ - CREATE / ALTER / DROP → `$executeRawUnsafe` → returns `{affectedRows: N}`
1698
+ - INSERT / UPDATE / DELETE → `$executeRawUnsafe` → returns affected row count
1695
1699
 
1696
- **Return type:** Array or false
1700
+ **Return type:**
1701
+ - Empty content → `false`
1702
+ - Single statement → the result directly (not wrapped in array)
1703
+ - Multiple statements → array of results
1704
+ - Error → `false`
1697
1705
 
1698
1706
  **Consistency Analysis:**
1699
- - ✅ **ALL CONSISTENT** - Return array or false
1700
1707
  - ✅ All execute raw SQL
1701
- - ⚠️ Prisma splits multi-statement queries
1708
+ - All return false on error or empty input
1709
+ - ⚠️ **RETURN TYPE DIFFERS for single statements**: ObjectionJS and MikroORM always return array or false; Prisma returns the result directly (not in an array) for a single statement, and an array only for multiple statements
1710
+ - ⚠️ Prisma is the only driver that explicitly splits multi-statement strings and runs them atomically in a transaction
1702
1711
 
1703
1712
  ---
1704
1713
 
@@ -365,107 +365,48 @@ async fetchEntitiesFromDatabase() {
365
365
 
366
366
  ### Critical: Tables Must Exist First!
367
367
 
368
- **File:** `tests/utils/test-helpers.js` (lines 389-416)
368
+ **File:** `tests/utils/test-helpers.js`
369
369
 
370
370
  ```javascript
371
371
  static async generateTestEntities(dataServer, driverName)
372
372
  {
373
- // Step 1: Introspect database (REQUIRES tables to exist!)
374
- let models = await this.createModelsFromDatabase(dataServer, driverName);
375
- if(!models || 0 === Object.keys(models).length){
376
- throw new Error('Failed to create models from database for '+driverName);
377
- }
378
-
379
- // Step 2: PRISMA ONLY - Generate schema + client
373
+ // Prisma only: generate schema + client if not already present
380
374
  if('prisma' === driverName){
381
375
  let config = this.getTestDbConfig();
382
376
  config.client = 'mysql';
383
-
384
- // Subprocess 1: Generate schema.prisma from database
385
- if(!await this.generatePrismaSchema(config)){
386
- throw new Error('Failed to generate Prisma schema');
387
- }
388
-
389
- // Subprocess 2: Generate PrismaClient code
390
- if(!await this.generatePrismaClient()){
391
- throw new Error('Failed to generate Prisma client');
392
- }
393
-
394
- // Step 3: Disconnect old client
395
- await dataServer.disconnect();
396
-
397
- // Step 4: Clear Node.js module cache
398
- delete require.cache[require.resolve('@prisma/client')];
399
-
400
- // Step 5: Reconnect with new client
401
- if(!await dataServer.connect()){
402
- throw new Error('Failed to reconnect Prisma client');
377
+ let schemaPath = FileHandler.joinPaths(process.cwd(), 'prisma', 'schema.prisma');
378
+ if(!FileHandler.exists(schemaPath)){
379
+ if(!await this.generatePrismaSchema(config)){
380
+ throw new Error('Failed to generate Prisma schema');
381
+ }
382
+ if(!await this.generatePrismaClient()){
383
+ throw new Error('Failed to generate Prisma client');
384
+ }
385
+ await dataServer.disconnect();
386
+ delete require.cache[require.resolve(FileHandler.joinPaths(process.cwd(), 'prisma', 'client'))];
387
+ await dataServer.connect();
403
388
  }
404
389
  }
405
-
406
- // Step 3: Set rawEntities and generate driver instances
407
- dataServer.rawEntities = models;
408
- let result = await dataServer.generateEntities();
409
- if(0 === Object.keys(dataServer.entities).length){
410
- throw new Error('No entities were generated for '+driverName);
390
+ // Run EntitiesGenerator — introspects DB, writes entity + model files to generated-entities/
391
+ await this.runEntitiesGenerator(dataServer, driverName);
392
+ this.fixGeneratedRequirePaths();
393
+ this.compareGeneratedWithExpected(driverName, 'entities');
394
+ this.compareGeneratedWithExpected(driverName, 'models/'+driverName);
395
+ if('objection-js' === driverName){
396
+ this.compareGeneratedWithExpected(driverName, 'entities-config.js');
397
+ this.compareGeneratedWithExpected(driverName, 'entities-translations.js');
411
398
  }
412
-
399
+ // Load generated registered-models file and populate entity manager
400
+ await this.loadGeneratedEntities(dataServer, driverName);
413
401
  return true;
414
402
  }
415
403
  ```
416
404
 
417
- ### createModelsFromDatabase Flow
418
-
419
- **Lines 31-69:**
420
-
421
- ```javascript
422
- static async createModelsFromDatabase(dataServer, driverName)
423
- {
424
- // CRITICAL: This calls fetchEntitiesFromDatabase which reads actual tables
425
- let tables = await dataServer.fetchEntitiesFromDatabase();
426
- if(!tables){
427
- return {};
428
- }
429
-
430
- let models = {};
431
-
432
- if('objection-js' === driverName){
433
- for(let tableName of Object.keys(tables)){
434
- let entityName = sc.camelCase(tableName.replace('test_', ''));
435
- // Creates dynamic Model class extending Objection Model
436
- class DynamicModel extends Model {
437
- static get tableName(){
438
- return tableName;
439
- }
440
- }
441
- if(dataServer.knex){
442
- DynamicModel.knex(dataServer.knex);
443
- }
444
- let modelKey = 'test'+sc.capitalizedCamelCase(entityName);
445
- models[modelKey] = DynamicModel;
446
- }
447
- return models;
448
- }
449
-
450
- if('mikro-orm' === driverName){
451
- for(let tableName of Object.keys(tables)){
452
- let entityName = sc.camelCase(tableName.replace('test_', ''));
453
- models['test'+sc.capitalizedCamelCase(entityName)] = {tableName: tableName};
454
- }
455
- return models;
456
- }
405
+ ### Entity Generation Flow
457
406
 
458
- if('prisma' === driverName){
459
- for(let tableName of Object.keys(tables)){
460
- let entityName = sc.camelCase(tableName.replace('test_', ''));
461
- models['test'+sc.capitalizedCamelCase(entityName)] = {tableName: tableName};
462
- }
463
- return models;
464
- }
407
+ `runEntitiesGenerator()` creates an `EntitiesGenerator` instance pointed at the connected `dataServer`, calls `generator.generate()`, which internally calls `dataServer.fetchEntitiesFromDatabase()` to read real table metadata from `information_schema`. This is why tables must exist before calling `generateTestEntities()` — without tables, introspection returns empty and generation fails.
465
408
 
466
- return {};
467
- }
468
- ```
409
+ After generation, `loadGeneratedEntities()` loads the `generated-entities/models/[driver]/registered-models-[driver].js` file, sets `dataServer.rawEntities`, and calls `dataServer.generateEntities()` to register all driver instances in the entity manager.
469
410
 
470
411
  **Why tables must exist first:**
471
412
  - `fetchEntitiesFromDatabase()` queries MySQL `information_schema`
@@ -673,65 +614,49 @@ static async generatePrismaClient(){
673
614
 
674
615
  ### Per Driver Test Suite
675
616
 
617
+ **1. Initialization — runs once per driver (`DriverRegistry.initialize()`):**
618
+ - `setupDriver()` — connect to database
619
+ - `executeRawSQL()` — create tables from SQL schema
620
+ - `generateTestEntities()` — introspect DB, run EntitiesGenerator, load entities
621
+ - `dataServer.getEntity()` — obtain repository references
622
+
623
+ **2. Test execution — per group method in each test class:**
624
+
625
+ Each test class (DriversTest, NestedFiltersTest, RelationsTest, RawQueriesTest) has group methods. Each group method begins with `await TestHelpers.cleanDatabase(this.dataServer)` to DELETE all rows, then runs its tests via `runner.test()`.
626
+
627
+ Example:
628
+ ```javascript
629
+ async testCreateOperations() {
630
+ this.runner.group('CREATE Operations');
631
+ await TestHelpers.cleanDatabase(this.dataServer);
632
+ // insert fixture data, then call runner.test() for each assertion
633
+ }
676
634
  ```
677
- 1. before() hook - RUNS ONCE
678
- ├─ setupDriver (connect to database)
679
- ├─ executeRawSQL (create tables)
680
- ├─ generateTestEntities (introspect + generate models)
681
- └─ getEntity (get repository references)
682
-
683
- 2. Test Suite Execution
684
-
685
- ├─ describe('CREATE Operations')
686
- │ ├─ beforeEach() → cleanDatabase (DELETE data)
687
- │ ├─ it('should create single record')
688
- │ ├─ beforeEach() → cleanDatabase (DELETE data)
689
- │ ├─ it('should create with JSON field')
690
- │ └─ ...
691
-
692
- ├─ describe('UPDATE Operations')
693
- │ ├─ beforeEach() → cleanDatabase (DELETE data)
694
- │ ├─ it('should update by ID')
695
- │ └─ ...
696
-
697
- ├─ describe('QUERY Operations')
698
- │ ├─ beforeEach() → cleanDatabase (DELETE data)
699
- │ ├─ it('should load all records')
700
- │ └─ ...
701
-
702
- └─ describe('DELETE Operations')
703
- ├─ beforeEach() → cleanDatabase (DELETE data)
704
- ├─ it('should delete by ID')
705
- └─ ...
706
-
707
- 3. after() hook - RUNS ONCE
708
- ├─ dropTestTables (DROP all tables)
709
- └─ teardownDriver (disconnect from database)
710
- ```
635
+
636
+ **3. Teardown runs once per driver (`DriverRegistry.cleanup()`):**
637
+ - `dropTestTables()` — DROP all test tables
638
+ - `teardownDriver()` disconnect from database
711
639
 
712
640
  ### All Drivers Execution
713
641
 
714
- ```
715
- Driver: objection-js
716
- ├─ before() Connect, Create Tables, Generate Entities
717
- ├─ Run 147 tests (with beforeEach DELETE between each)
718
- └─ after() → Drop Tables, Disconnect
719
-
720
- Driver: mikro-orm
721
- ├─ before() Connect, Create Tables, Generate Entities
722
- ├─ Run 147 tests (with beforeEach DELETE between each)
723
- └─ after() → Drop Tables, Disconnect
724
-
725
- Driver: prisma
726
- ├─ before() Connect, Create Tables, Generate Schema, Generate Client, Reconnect, Generate Entities
727
- ├─ Run 147 tests (with beforeEach DELETE between each)
728
- └─ after() → Drop Tables, Disconnect
729
- ```
642
+ **Driver: objection-js**
643
+ - Initialize: connect, create tables, generate entities
644
+ - Run all tests (cleanDatabase at start of each group)
645
+ - Teardown: drop tables, disconnect
646
+
647
+ **Driver: mikro-orm**
648
+ - Initialize: connect, create tables, generate entities
649
+ - Run all tests (cleanDatabase at start of each group)
650
+ - Teardown: drop tables, disconnect
651
+
652
+ **Driver: prisma**
653
+ - Initialize: connect, create tables, generate Prisma schema (subprocess), generate Prisma client (subprocess), reconnect, generate entities
654
+ - Run all tests (cleanDatabase at start of each group)
655
+ - Teardown: drop tables, disconnect
730
656
 
731
657
  **Total connections:** 3 (one per driver)
732
658
  **Total table creations:** 3 (one per driver)
733
659
  **Total entity generations:** 3 (one per driver)
734
- **Total test executions:** 441 (147 tests × 3 drivers)
735
660
 
736
661
  ---
737
662
 
@@ -775,7 +700,7 @@ Driver: prisma
775
700
 
776
701
  **Symptoms:**
777
702
  - `fetchEntitiesFromDatabase()` returns empty/null
778
- - `createModelsFromDatabase()` returns empty object
703
+ - `EntitiesGenerator.generate()` fails or produces no entities
779
704
  - Entity generation fails with "No entities generated"
780
705
 
781
706
  **Cause:**
@@ -828,59 +753,48 @@ Driver: prisma
828
753
 
829
754
  ## Summary: The Complete Flow
830
755
 
831
- ```
832
- SESSION START
833
-
834
- FOR EACH DRIVER (objection-js, mikro-orm, prisma):
835
-
836
- before() hook - RUNS ONCE:
837
- 1. Connect to database
838
- ├─ ObjectionJS: Create Knex instance
839
- ├─ MikroORM: Initialize MikroORM
840
- └─ Prisma: Create PrismaClient (if generated)
841
-
842
- 2. Create tables from SQL file
843
- ├─ SET FOREIGN_KEY_CHECKS=0
844
- ├─ DROP TABLE IF EXISTS (in order)
845
- ├─ CREATE TABLE (with constraints)
846
- └─ SET FOREIGN_KEY_CHECKS=1
847
-
848
- 3. Generate entities from database
849
- ├─ Introspect database schema
850
- ├─ Create model classes/objects
851
- ├─ [Prisma only]: Generate schema Generate client Reconnect
852
- ├─ Call dataServer.generateEntities()
853
- └─ Get repository references
854
-
855
- FOR EACH TEST:
856
- beforeEach() hook:
857
- ├─ SET FOREIGN_KEY_CHECKS=0
858
- ├─ DELETE FROM test_reviews
859
- ├─ DELETE FROM test_products
860
- ├─ DELETE FROM test_categories
861
- └─ SET FOREIGN_KEY_CHECKS=1
862
-
863
- Run test:
864
- ├─ Create test data
865
- ├─ Perform operations
866
- ├─ Assert results
867
- └─ (Data will be cleaned in next beforeEach)
868
-
869
- after() hook - RUNS ONCE:
870
- 1. Drop all tables
871
- ├─ SET FOREIGN_KEY_CHECKS=0
872
- ├─ DROP TABLE IF EXISTS test_reviews
873
- ├─ DROP TABLE IF EXISTS test_products
874
- ├─ DROP TABLE IF EXISTS test_categories
875
- └─ SET FOREIGN_KEY_CHECKS=1
876
-
877
- 2. Disconnect from database
878
- ├─ ObjectionJS: await knex.destroy()
879
- ├─ MikroORM: await orm.close()
880
- └─ Prisma: await prisma.$disconnect()
881
-
882
- SESSION END
883
- ```
756
+ **For each driver (objection-js, mikro-orm, prisma):**
757
+
758
+ **Initialization — once per driver:**
759
+
760
+ 1. Connect to database
761
+ - ObjectionJS: create Knex instance
762
+ - MikroORM: initialize MikroORM
763
+ - Prisma: create PrismaClient (if already generated)
764
+
765
+ 2. Create tables from SQL file
766
+ - SET FOREIGN_KEY_CHECKS=0
767
+ - DROP TABLE IF EXISTS (cleanup from previous run)
768
+ - CREATE TABLE (with all constraints)
769
+ - SET FOREIGN_KEY_CHECKS=1
770
+
771
+ 3. Generate entities from database
772
+ - Run EntitiesGenerator (introspects DB, writes files to generated-entities/)
773
+ - Prisma only: generate schema.prisma (subprocess), generate PrismaClient (subprocess), reconnect
774
+ - Load registered-models file, call dataServer.generateEntities()
775
+
776
+ **Per test group cleanDatabase() at start of each group method:**
777
+ - SET FOREIGN_KEY_CHECKS=0
778
+ - DELETE FROM test_reviews
779
+ - DELETE FROM test_products
780
+ - DELETE FROM test_categories
781
+ - SET FOREIGN_KEY_CHECKS=1
782
+
783
+ **Per test:** create test data, perform operations, assert results.
784
+
785
+ **Teardown once per driver:**
786
+
787
+ 1. Drop all tables
788
+ - SET FOREIGN_KEY_CHECKS=0
789
+ - DROP TABLE IF EXISTS test_reviews
790
+ - DROP TABLE IF EXISTS test_products
791
+ - DROP TABLE IF EXISTS test_categories
792
+ - SET FOREIGN_KEY_CHECKS=1
793
+
794
+ 2. Disconnect from database
795
+ - ObjectionJS: `await knex.destroy()`
796
+ - MikroORM: `await orm.close()`
797
+ - Prisma: `await prisma.$disconnect()`
884
798
 
885
799
  ---
886
800
 
@@ -888,7 +802,7 @@ SESSION END
888
802
 
889
803
  ### Integration Test Files (All Fixed)
890
804
 
891
- **test-cross-driver-compatibility.js:**
805
+ **test-drivers.js:**
892
806
  - ✅ Changed `beforeEach` → `before` for setup
893
807
  - ✅ Added `beforeEach` for data cleanup only
894
808
  - ✅ Changed `afterEach` → `after` for teardown
@@ -906,6 +820,9 @@ SESSION END
906
820
  - ✅ Changed `afterEach` → `after` for teardown
907
821
  - ✅ Added error checking for entity generation
908
822
 
823
+ **test-raw-queries.js:**
824
+ - ✅ Added — covers rawQuery with single and multiple SQL statements
825
+
909
826
  ### Test Helpers (All Fixed)
910
827
 
911
828
  **test-helpers.js:**
@@ -913,7 +830,7 @@ SESSION END
913
830
  - ✅ Changed `cleanDatabase()` to use DELETE instead of DROP
914
831
  - ✅ Created `dropTestTables()` for final cleanup
915
832
  - ✅ Changed `generateTestEntities()` to throw errors instead of returning false
916
- - ✅ Removed logging from `createModelsFromDatabase()`
833
+ - ✅ Replaced `createModelsFromDatabase()` with `runEntitiesGenerator()` + `loadGeneratedEntities()`
917
834
  - ✅ Removed logging from `setupDriver()`
918
835
  - ✅ Removed logging from `executeRawSQL()`
919
836
 
@@ -930,30 +847,25 @@ SESSION END
930
847
 
931
848
  ### Before Fixes (Wrong Pattern)
932
849
 
933
- ```
934
- Driver: objection-js
935
- ├─ Test 1: Connect (500ms) + Create Tables (1000ms) + Generate Entities (500ms) + Run (10ms) + Teardown (100ms) = 2110ms
936
- ├─ Test 2: Connect (500ms) + Create Tables (1000ms) + Generate Entities (500ms) + Run (10ms) + Teardown (100ms) = 2110ms
937
- └─ ... × 147 tests = 310,170ms (5+ minutes per driver)
938
-
939
- Total for 3 drivers: ~15+ minutes
940
- ```
850
+ Each test reconnected, recreated tables, and regenerated entities independently:
851
+ - Connect: ~500ms
852
+ - Create tables: ~1000ms
853
+ - Generate entities: ~500ms
854
+ - Run test: ~10ms
855
+ - Teardown: ~100ms
856
+ - **Total per test: ~2110ms**
857
+ - Total for 100 tests, 3 drivers: ~10+ minutes
941
858
 
942
859
  ### After Fixes (Correct Pattern)
943
860
 
944
- ```
945
- Driver: objection-js
946
- ├─ before(): Connect (500ms) + Create Tables (1000ms) + Generate Entities (500ms) = 2000ms
947
- ├─ Test 1: DELETE (5ms) + Run (10ms) = 15ms
948
- ├─ Test 2: DELETE (5ms) + Run (10ms) = 15ms
949
- ├─ ... × 147 tests = 2205ms
950
- └─ after(): Drop Tables (100ms) + Teardown (100ms) = 200ms
951
-
952
- Total for 1 driver: ~2.4 seconds
953
- Total for 3 drivers: ~7-10 seconds (includes Prisma subprocess)
954
- ```
861
+ Setup and teardown happen once per driver. Only data cleanup runs between tests:
862
+ - Setup once: connect + create tables + generate entities = ~2 seconds
863
+ - Each test: DELETE data (~5ms) + run test (~10ms) = ~15ms
864
+ - Teardown once: drop tables + disconnect = ~200ms
865
+ - **Total for 100 tests, 1 driver: ~3.7 seconds**
866
+ - Total for 3 drivers: ~10-15 seconds (includes Prisma subprocess generation)
955
867
 
956
- **Speed improvement: 90x faster** 🚀
868
+ **Speed improvement: ~60-90x faster**
957
869
 
958
870
  ---
959
871
 
@@ -119,43 +119,43 @@ class PrismaDataServer extends BaseDataServer
119
119
  async rawQuery(content)
120
120
  {
121
121
  try {
122
- let statements = this.splitSqlStatements(content);
123
- let results = [];
124
- for(let statement of statements){
125
- let cleanStatement = statement.trim();
126
- if('' === cleanStatement){
127
- continue;
128
- }
129
- try {
130
- let result = await this.executeStatement(cleanStatement);
131
- results.push(result);
132
- //Logger.debug('Statement executed, result: ' + JSON.stringify(result));
133
- } catch(stmtError) {
134
- Logger.error('Statement "'+cleanStatement+'" execution failed: '+stmtError.message);
135
- return false;
136
- }
137
- }
138
- if(0 === results.length){
139
- //Logger.error('Statement results length is zero.', results);
122
+ let statements = this.splitSqlStatements(content)
123
+ .map(s => s.trim())
124
+ .filter(s => '' !== s);
125
+ if(0 === statements.length){
140
126
  return false;
141
127
  }
142
- return 1 === results.length ? [...results].shift() : results;
128
+ let results = [];
129
+ await this.prisma.$transaction(
130
+ async (tx) => { results = await this.executeStatements(statements, tx); },
131
+ {timeout: 60000}
132
+ );
133
+ return 1 === results.length ? results[0] : results;
143
134
  } catch(error) {
144
135
  Logger.error('Raw query failed: '+error.message);
145
136
  return false;
146
137
  }
147
138
  }
148
139
 
149
- async executeStatement(statement)
140
+ async executeStatements(statements, tx)
150
141
  {
151
- let trimmedStatement = statement.trim().toUpperCase();
152
- if(trimmedStatement.startsWith('SELECT')){
153
- return await this.prisma.$queryRawUnsafe(statement);
142
+ let results = [];
143
+ for(let statement of statements){
144
+ let result = await this.executeStatement(statement, tx);
145
+ //Logger.debug('Statement executed, result: ' + JSON.stringify(result));
146
+ results.push(result);
154
147
  }
155
- if(trimmedStatement.startsWith('SHOW')){
156
- return await this.prisma.$queryRawUnsafe(statement);
148
+ return results;
149
+ }
150
+
151
+ async executeStatement(statement, prismaClient)
152
+ {
153
+ let client = prismaClient || this.prisma;
154
+ let trimmedStatement = statement.trim().toUpperCase();
155
+ if(trimmedStatement.startsWith('SELECT') || trimmedStatement.startsWith('SHOW')){
156
+ return await client.$queryRawUnsafe(statement);
157
157
  }
158
- let result = await this.prisma.$executeRawUnsafe(statement);
158
+ let result = await client.$executeRawUnsafe(statement);
159
159
  //Logger.debug('Raw result from Prisma: ' + result);
160
160
  if(
161
161
  trimmedStatement.startsWith('CREATE')
@@ -26,6 +26,33 @@ class PrismaSchemaGenerator
26
26
  this.dbParams = sc.get(props, 'dbParams', process.env.RELDENS_DB_PARAMS || '');
27
27
  }
28
28
 
29
+ static ensureStubFiles(projectRoot)
30
+ {
31
+ let stubPath = FileHandler.joinPaths(projectRoot, 'node_modules', '.prisma', 'client', 'default.js');
32
+ if(FileHandler.exists(stubPath)){
33
+ Logger.info('Prisma stub files already exist.');
34
+ return true;
35
+ }
36
+ Logger.info('Creating Prisma stub files...');
37
+ try {
38
+ let defaultIndexPath = FileHandler.joinPaths(
39
+ projectRoot, 'node_modules', '@prisma', 'client', 'scripts', 'default-index.js'
40
+ );
41
+ if(!FileHandler.exists(defaultIndexPath)){
42
+ Logger.warning('Prisma default-index script not found at: '+defaultIndexPath);
43
+ return false;
44
+ }
45
+ let stubDir = FileHandler.joinPaths(projectRoot, 'node_modules', '.prisma', 'client');
46
+ FileHandler.createFolder(stubDir);
47
+ FileHandler.copyFile(defaultIndexPath, stubPath);
48
+ Logger.info('Prisma stub files created successfully.');
49
+ return true;
50
+ } catch(error) {
51
+ Logger.error('Failed to create stub files: '+error.message);
52
+ return false;
53
+ }
54
+ }
55
+
29
56
  async generate()
30
57
  {
31
58
  FileHandler.createFolder(this.prismaSchemaPath);
@@ -39,6 +66,11 @@ class PrismaSchemaGenerator
39
66
  Logger.critical('Failed to pull database schema: '+error.message);
40
67
  return false;
41
68
  }
69
+ return this.generateClient();
70
+ }
71
+
72
+ generateClient()
73
+ {
42
74
  let generateCommand = 'npx prisma generate';
43
75
  if(this.dataProxy){
44
76
  generateCommand += ' --data-proxy';
@@ -58,7 +90,6 @@ class PrismaSchemaGenerator
58
90
  Logger.critical('Generate command failed: '+error.message);
59
91
  return false;
60
92
  }
61
- await this.waitForSchemaGeneration();
62
93
  return true;
63
94
  }
64
95
 
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@reldens/storage",
3
3
  "scope": "@reldens",
4
- "version": "0.95.0",
4
+ "version": "0.96.0",
5
5
  "description": "Reldens - Storage",
6
6
  "author": "Damian A. Pastorini",
7
7
  "license": "MIT",
@@ -0,0 +1,117 @@
1
+ /**
2
+ *
3
+ * Reldens - Raw Queries Integration Test
4
+ * Tests rawQuery with single and multiple SQL statements across all drivers
5
+ *
6
+ */
7
+
8
+ const { TestRunner, assert } = require('../utils/test-runner');
9
+ const { TestHelpers } = require('../utils/test-helpers');
10
+
11
+ class RawQueriesTest
12
+ {
13
+
14
+ constructor(dataServer, repos, driverName)
15
+ {
16
+ this.dataServer = dataServer;
17
+ this.driverName = driverName;
18
+ this.runner = new TestRunner();
19
+ }
20
+
21
+ async run()
22
+ {
23
+ this.runner.suite('Raw Queries - Driver: '+this.driverName);
24
+ await this.testSingleStatements();
25
+ await this.testMultipleStatements();
26
+ return this.runner.getResults();
27
+ }
28
+
29
+ async testSingleStatements()
30
+ {
31
+ this.runner.group('Single Statement');
32
+ await TestHelpers.cleanDatabase(this.dataServer);
33
+ await this.dataServer.rawQuery(
34
+ 'INSERT INTO test_categories (id, name, slug, is_active, display_order)'
35
+ +' VALUES (9001, \'Raw Single Test\', \'raw-single-test\', 1, 99)'
36
+ );
37
+ await this.runner.test('should return rows from SELECT single statement', async () => {
38
+ let result = await this.dataServer.rawQuery(
39
+ 'SELECT id, name, slug FROM test_categories WHERE id = 9001'
40
+ );
41
+ assert.ok(result);
42
+ assert.ok(Array.isArray(result));
43
+ assert.strictEqual(result.length, 1);
44
+ assert.strictEqual(result[0].name, 'Raw Single Test');
45
+ assert.strictEqual(result[0].slug, 'raw-single-test');
46
+ });
47
+ await this.runner.test('should execute UPDATE single statement and reflect changes', async () => {
48
+ await this.dataServer.rawQuery(
49
+ 'UPDATE test_categories SET name = \'Updated Raw Single\' WHERE id = 9001'
50
+ );
51
+ let result = await this.dataServer.rawQuery(
52
+ 'SELECT id, name FROM test_categories WHERE id = 9001'
53
+ );
54
+ assert.ok(result);
55
+ assert.ok(Array.isArray(result));
56
+ assert.strictEqual(result.length, 1);
57
+ assert.strictEqual(result[0].name, 'Updated Raw Single');
58
+ });
59
+ await this.runner.test('should execute DELETE single statement and remove record', async () => {
60
+ await this.dataServer.rawQuery('DELETE FROM test_categories WHERE id = 9001');
61
+ let result = await this.dataServer.rawQuery(
62
+ 'SELECT id FROM test_categories WHERE id = 9001'
63
+ );
64
+ let isEmpty = !result || (Array.isArray(result) && 0 === result.length);
65
+ assert.ok(isEmpty, 'Record should not exist after DELETE');
66
+ });
67
+ }
68
+
69
+ async testMultipleStatements()
70
+ {
71
+ this.runner.group('Multiple Statements');
72
+ await TestHelpers.cleanDatabase(this.dataServer);
73
+ if('prisma' === this.driverName){
74
+ await this.runner.test('should execute multiple statements in one rawQuery call', async () => {
75
+ let results = await this.dataServer.rawQuery(
76
+ 'INSERT INTO test_categories (id, name, slug, is_active, display_order)'
77
+ +' VALUES (9002, \'Raw Multi 1\', \'raw-multi-1\', 1, 1);'
78
+ +'INSERT INTO test_categories (id, name, slug, is_active, display_order)'
79
+ +' VALUES (9003, \'Raw Multi 2\', \'raw-multi-2\', 1, 2);'
80
+ );
81
+ assert.ok(results);
82
+ assert.ok(Array.isArray(results));
83
+ assert.strictEqual(results.length, 2);
84
+ let rows = await this.dataServer.rawQuery(
85
+ 'SELECT id, name FROM test_categories ORDER BY id'
86
+ );
87
+ assert.ok(rows);
88
+ assert.ok(Array.isArray(rows));
89
+ assert.strictEqual(rows.length, 2);
90
+ assert.strictEqual(rows[0].name, 'Raw Multi 1');
91
+ assert.strictEqual(rows[1].name, 'Raw Multi 2');
92
+ });
93
+ } else {
94
+ await this.runner.test('should execute multiple sequential rawQuery calls', async () => {
95
+ await this.dataServer.rawQuery(
96
+ 'INSERT INTO test_categories (id, name, slug, is_active, display_order)'
97
+ +' VALUES (9002, \'Raw Multi 1\', \'raw-multi-1\', 1, 1)'
98
+ );
99
+ await this.dataServer.rawQuery(
100
+ 'INSERT INTO test_categories (id, name, slug, is_active, display_order)'
101
+ +' VALUES (9003, \'Raw Multi 2\', \'raw-multi-2\', 1, 2)'
102
+ );
103
+ let result = await this.dataServer.rawQuery(
104
+ 'SELECT id, name FROM test_categories ORDER BY id'
105
+ );
106
+ assert.ok(result);
107
+ assert.ok(Array.isArray(result));
108
+ assert.strictEqual(result.length, 2);
109
+ assert.strictEqual(result[0].name, 'Raw Multi 1');
110
+ assert.strictEqual(result[1].name, 'Raw Multi 2');
111
+ });
112
+ }
113
+ }
114
+
115
+ }
116
+
117
+ module.exports = RawQueriesTest;
@@ -11,6 +11,7 @@ const { DriverRegistry } = require('./utils/driver-registry');
11
11
  const DriversTest = require('./integration/test-drivers');
12
12
  const NestedFiltersTest = require('./integration/test-nested-filters');
13
13
  const RelationsTest = require('./integration/test-relations');
14
+ const RawQueriesTest = require('./integration/test-raw-queries');
14
15
  const EntityManagerTest = require('./unit/test-entity-manager');
15
16
  const TypeMapperTest = require('./unit/test-type-mapper');
16
17
  const DriversUnitTest = require('./unit/test-drivers');
@@ -156,6 +157,16 @@ class RunTests
156
157
  Logger.critical('Driver '+driverName+' relations tests crashed: '+error.message);
157
158
  Logger.critical(error.stack);
158
159
  }
160
+ try {
161
+ let rawQueriesTest = new RawQueriesTest(dataServer, repos, driverName);
162
+ let rawQueriesResult = await rawQueriesTest.run();
163
+ this.allCounts.total += rawQueriesResult.total;
164
+ this.allCounts.passed += rawQueriesResult.passed;
165
+ this.allCounts.failed += rawQueriesResult.failed;
166
+ } catch(error) {
167
+ Logger.critical('Driver '+driverName+' raw queries tests crashed: '+error.message);
168
+ Logger.critical(error.stack);
169
+ }
159
170
  }
160
171
  }
161
172
 
@@ -1,37 +0,0 @@
1
- const { ObjectionJsDataServer } = require('../lib/objection-js/objection-js-data-server');
2
-
3
- const { Model } = require('objection');
4
-
5
- class UsersModel extends Model
6
- {
7
-
8
- static get tableName()
9
- {
10
- return 'your_table_name';
11
- }
12
-
13
- }
14
-
15
- async function test(){
16
- const entityKey = 'users';
17
-
18
- let server = new ObjectionJsDataServer({
19
- client: 'mysql',
20
- config: {
21
- user: 'user',
22
- password: 'password',
23
- database: 'database_name',
24
- port : 3306,
25
- },
26
- rawEntities: {[entityKey]: UsersModel}
27
- });
28
-
29
- await server.connect();
30
- await server.generateEntities();
31
-
32
- console.log(await server.getEntity(entityKey).loadAll());
33
-
34
- process.exit();
35
- }
36
-
37
- test();