@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.
Files changed (86) hide show
  1. package/.claude/test-architecture.md +27 -17
  2. package/CLAUDE.md +67 -11
  3. package/README.md +204 -22
  4. package/bin/reldens-storage.js +43 -10
  5. package/index.js +53 -12
  6. package/lib/drizzle/drizzle-data-server.js +123 -0
  7. package/lib/drizzle/drizzle-driver.js +228 -0
  8. package/lib/drizzle/drizzle-models-generation.js +49 -0
  9. package/lib/drizzle/drizzle-modules-loader.js +32 -0
  10. package/lib/drizzle/drizzle-modules-validator.js +74 -0
  11. package/lib/entities-generator.js +36 -7
  12. package/lib/entity-templates/drizzle-model.template +28 -0
  13. package/lib/entity-templates/query-builder-model.template +17 -0
  14. package/lib/generators/base-generator.js +13 -0
  15. package/lib/generators/entities-generation.js +0 -13
  16. package/lib/generators/models-generation.js +179 -693
  17. package/lib/generators/relations-detection.js +171 -0
  18. package/lib/knex/knex-data-server.js +128 -0
  19. package/lib/knex/knex-driver.js +157 -0
  20. package/lib/knex/knex-modules-loader.js +28 -0
  21. package/lib/knex/knex-modules-validator.js +45 -0
  22. package/lib/kysely/kysely-data-server.js +128 -0
  23. package/lib/kysely/kysely-driver.js +176 -0
  24. package/lib/kysely/kysely-modules-loader.js +28 -0
  25. package/lib/kysely/kysely-modules-validator.js +53 -0
  26. package/lib/mikro-orm/mikro-orm-data-server.js +48 -29
  27. package/lib/mikro-orm/mikro-orm-driver.js +385 -121
  28. package/lib/mikro-orm/mikro-orm-models-generation.js +178 -0
  29. package/lib/mikro-orm/mikro-orm-modules-loader.js +50 -0
  30. package/lib/mikro-orm/mikro-orm-modules-validator.js +46 -0
  31. package/lib/mysql2-connection-config.js +38 -0
  32. package/lib/objection-js/objection-js-data-server.js +71 -125
  33. package/lib/objection-js/objection-js-driver.js +4 -1
  34. package/lib/objection-js/objection-js-models-generation.js +98 -0
  35. package/lib/objection-js/objection-modules-loader.js +28 -0
  36. package/lib/objection-js/objection-modules-validator.js +31 -0
  37. package/lib/package-resolver.js +45 -0
  38. package/lib/prisma/prisma-client-loader.js +11 -1
  39. package/lib/prisma/prisma-driver.js +16 -2
  40. package/lib/prisma/prisma-filter-processor.js +5 -6
  41. package/lib/prisma/prisma-models-generation.js +236 -0
  42. package/lib/prisma/prisma-schema-generator.js +5 -1
  43. package/lib/prisma/prisma-type-caster.js +14 -0
  44. package/lib/query-builder-driver.js +210 -0
  45. package/lib/query-builder-models-generation.js +80 -0
  46. package/lib/relations-loader.js +192 -0
  47. package/lib/type-mapper.js +38 -0
  48. package/package.json +7 -7
  49. package/tests/.env.test.example +6 -0
  50. package/tests/fixtures/categories-fixtures.js +8 -0
  51. package/tests/fixtures/expected-entities/entities/test-product-details-entity.js +72 -0
  52. package/tests/fixtures/expected-entities/entities-config.js +6 -4
  53. package/tests/fixtures/expected-entities/entities-translations.js +25 -14
  54. package/tests/fixtures/expected-entities-mikro-orm/models/mikro-orm/registered-models-mikro-orm.js +2 -0
  55. package/tests/fixtures/expected-entities-mikro-orm/models/mikro-orm/test-categories-model.js +13 -8
  56. package/tests/fixtures/expected-entities-mikro-orm/models/mikro-orm/test-product-details-model.js +80 -0
  57. package/tests/fixtures/expected-entities-mikro-orm/models/mikro-orm/test-products-model.js +20 -14
  58. package/tests/fixtures/expected-entities-mikro-orm/models/mikro-orm/test-reviews-model.js +13 -12
  59. package/tests/fixtures/expected-entities-objection-js/models/objection-js/registered-models-objection-js.js +2 -0
  60. package/tests/fixtures/expected-entities-objection-js/models/objection-js/test-product-details-model.js +33 -0
  61. package/tests/fixtures/expected-entities-objection-js/models/objection-js/test-products-model.js +9 -0
  62. package/tests/fixtures/expected-entities-prisma/models/prisma/registered-models-prisma.js +2 -0
  63. package/tests/fixtures/expected-entities-prisma/models/prisma/test-product-details-model.js +43 -0
  64. package/tests/fixtures/expected-entities-prisma/models/prisma/test-products-model.js +2 -0
  65. package/tests/fixtures/product-details-fixtures.js +22 -0
  66. package/tests/fixtures/reviews-fixtures.js +2 -1
  67. package/tests/fixtures/sql/test-schema.sql +23 -1
  68. package/tests/fixtures/table-columns-fixtures.js +97 -0
  69. package/tests/integration/test-cross-driver-equivalence.js +286 -0
  70. package/tests/integration/test-nested-filters.js +265 -408
  71. package/tests/integration/test-relations.js +199 -0
  72. package/tests/integration/test-reldens-sample-data.js +255 -0
  73. package/tests/integration/test-reldens-shape-relations.js +292 -0
  74. package/tests/run-tests.js +112 -60
  75. package/tests/unit/test-drivers.js +7 -1
  76. package/tests/unit/test-models-generation.js +174 -0
  77. package/tests/utils/column-value-assert.js +139 -0
  78. package/tests/utils/driver-registry.js +1 -1
  79. package/tests/utils/entity-projection.js +111 -0
  80. package/tests/utils/observed-value.js +41 -0
  81. package/tests/utils/projection-difference.js +151 -0
  82. package/tests/utils/relations-shape-support.js +104 -0
  83. package/tests/utils/reldens-schema-loader.js +274 -0
  84. package/tests/utils/test-helpers.js +152 -19
  85. package/tests/utils/test-runner.js +26 -5
  86. package/tests/utils/whole-result-assert.js +243 -0
@@ -10,6 +10,7 @@ const { TestHelpers } = require('../utils/test-helpers');
10
10
  const { CategoriesFixtures } = require('../fixtures/categories-fixtures');
11
11
  const { ProductsFixtures } = require('../fixtures/products-fixtures');
12
12
  const { ReviewsFixtures } = require('../fixtures/reviews-fixtures');
13
+ const { ProductDetailsFixtures } = require('../fixtures/product-details-fixtures');
13
14
 
14
15
  class RelationsTest
15
16
  {
@@ -19,6 +20,7 @@ class RelationsTest
19
20
  this.dataServer = dataServer;
20
21
  this.categoriesRepo = repos.testCategories;
21
22
  this.productsRepo = repos.testProducts;
23
+ this.productDetailsRepo = repos.testProductDetails;
22
24
  this.driverName = driverName;
23
25
  this.runner = new TestRunner();
24
26
  }
@@ -36,9 +38,151 @@ class RelationsTest
36
38
  await this.testCreateWithRelations();
37
39
  await this.testNestedRelations();
38
40
  await this.testRelationStringParsing();
41
+ await this.testOneToOneRelations();
42
+ await this.testCamelCaseColumns();
39
43
  return this.runner.getResults();
40
44
  }
41
45
 
46
+ async testCamelCaseColumns()
47
+ {
48
+ this.runner.group('CamelCase Columns');
49
+ await TestHelpers.cleanDatabase(this.dataServer);
50
+ await TestHelpers.insertFixturesViaRawSQL(this.dataServer, 'test_categories', [
51
+ CategoriesFixtures.category_relations_1
52
+ ]);
53
+ await TestHelpers.insertFixturesViaRawSQL(this.dataServer, 'test_products', [
54
+ ProductsFixtures.product_relations_1
55
+ ]);
56
+ await TestHelpers.insertFixturesViaRawSQL(this.dataServer, 'test_product_details', [
57
+ ProductDetailsFixtures.product_details_relations_1
58
+ ]);
59
+ await this.runner.test('should read a camelCase column with the exact database column name', async () => {
60
+ let record = await this.productDetailsRepo.loadById(4600);
61
+ assert.ok(record);
62
+ assert.strictEqual(record.customData, ProductDetailsFixtures.product_details_relations_1.customData);
63
+ assert.strictEqual(record.useTimeOut, ProductDetailsFixtures.product_details_relations_1.useTimeOut);
64
+ });
65
+ await this.runner.test('should read a bigint column as a number', async () => {
66
+ let record = await this.productDetailsRepo.loadById(4600);
67
+ assert.strictEqual(typeof record.total_views, 'number');
68
+ assert.strictEqual(record.total_views, ProductDetailsFixtures.product_details_relations_1.total_views);
69
+ assert.strictEqual(record.total_views + 1, ProductDetailsFixtures.product_details_relations_1.total_views + 1);
70
+ });
71
+ await this.runner.test('should filter by a camelCase column', async () => {
72
+ let records = await this.productDetailsRepo.loadBy('useTimeOut', 45);
73
+ assert.strictEqual(records.length, 1);
74
+ assert.strictEqual(records[0].id, 4600);
75
+ });
76
+ await this.runner.test('should write a camelCase column', async () => {
77
+ await this.productDetailsRepo.updateById(4600, {useTimeOut: 90});
78
+ let updated = await this.productDetailsRepo.loadById(4600);
79
+ assert.strictEqual(updated.useTimeOut, 90);
80
+ });
81
+ await this.runner.test('should populate a relation whose target has camelCase columns', async () => {
82
+ let results = await this.productsRepo.loadWithRelations({id: 2600}, ['related_test_product_details']);
83
+ assert.strictEqual(results.length, 1);
84
+ assert.strictEqual(
85
+ results[0].related_test_product_details.customData,
86
+ ProductDetailsFixtures.product_details_relations_1.customData
87
+ );
88
+ });
89
+ await this.runner.test('should return an explicit null for an empty nullable foreign key', async () => {
90
+ let record = await this.productDetailsRepo.loadById(4600);
91
+ assert.strictEqual(record.category_id, null);
92
+ });
93
+ await this.runner.test('should keep the foreign key column value on a nested populated relation', async () => {
94
+ let results = await this.categoriesRepo.loadWithRelations(
95
+ {id: 1600},
96
+ ['related_test_products.related_test_product_details']
97
+ );
98
+ let product = results[0].related_test_products[0];
99
+ assert.strictEqual(product.category_id, 1600);
100
+ assert.strictEqual(product.related_test_product_details.product_id, 2600);
101
+ });
102
+ await this.runner.test('should keep a nullable foreign key on the leaf of a nested populate', async () => {
103
+ let results = await this.categoriesRepo.loadWithRelations(
104
+ {id: 1600},
105
+ ['related_test_products.related_test_product_details']
106
+ );
107
+ let details = results[0].related_test_products[0].related_test_product_details;
108
+ assert.strictEqual(details.category_id, null);
109
+ });
110
+ await this.runner.test('should keep the foreign key column value on a load without relations', async () => {
111
+ let record = await this.productDetailsRepo.loadById(4600);
112
+ assert.strictEqual(record.product_id, 2600);
113
+ });
114
+ await this.runner.test('should keep the foreign key column value on loadBy without relations', async () => {
115
+ let records = await this.productDetailsRepo.loadBy('id', 4600);
116
+ assert.strictEqual(records[0].product_id, 2600);
117
+ });
118
+ await this.runner.test('should accept an object written into a text column', async () => {
119
+ await this.productDetailsRepo.updateById(4600, {customData: {onlyCurrentPlayer: true}});
120
+ let updated = await this.productDetailsRepo.loadById(4600);
121
+ assert.ok(updated.customData);
122
+ });
123
+ }
124
+
125
+ async testOneToOneRelations()
126
+ {
127
+ this.runner.group('One To One Relations');
128
+ await TestHelpers.cleanDatabase(this.dataServer);
129
+ await TestHelpers.insertFixturesViaRawSQL(this.dataServer, 'test_categories', [
130
+ CategoriesFixtures.category_relations_1
131
+ ]);
132
+ await TestHelpers.insertFixturesViaRawSQL(this.dataServer, 'test_products', [
133
+ ProductsFixtures.product_relations_1,
134
+ {...ProductsFixtures.product_relations_2, category_id: 1600}
135
+ ]);
136
+ await TestHelpers.insertFixturesViaRawSQL(this.dataServer, 'test_product_details', [
137
+ ProductDetailsFixtures.product_details_relations_1
138
+ ]);
139
+ await this.runner.test('should load the one to one relation from the referenced side as a single object', async () => {
140
+ let results = await this.productsRepo.loadWithRelations({id: 2600}, ['related_test_product_details']);
141
+ assert.strictEqual(results.length, 1);
142
+ let details = results[0].related_test_product_details;
143
+ assert.ok(details);
144
+ assert.ok(!Array.isArray(details));
145
+ assert.strictEqual(details.id, 4600);
146
+ });
147
+ await this.runner.test('should load the one to one relation from the owning side as a single object', async () => {
148
+ let results = await this.productDetailsRepo.loadWithRelations({id: 4600}, ['related_test_products']);
149
+ assert.strictEqual(results.length, 1);
150
+ let product = results[0].related_test_products;
151
+ assert.ok(product);
152
+ assert.ok(!Array.isArray(product));
153
+ assert.strictEqual(product.id, 2600);
154
+ });
155
+ await this.runner.test('should return no related record on the one to one relation when none exists', async () => {
156
+ let result = await this.productsRepo.loadByIdWithRelations(2601, ['related_test_product_details']);
157
+ assert.ok(result);
158
+ assert.ok(!result.related_test_product_details);
159
+ });
160
+ await this.runner.test('should count records joining the one to one relation from the referenced side', async () => {
161
+ let count = await this.productsRepo.countWithRelations({}, ['related_test_product_details']);
162
+ assert.strictEqual(count, 2);
163
+ });
164
+ await this.runner.test('should count records joining the one to one relation from the owning side', async () => {
165
+ let count = await this.productDetailsRepo.countWithRelations({}, ['related_test_products']);
166
+ assert.strictEqual(count, 1);
167
+ });
168
+ await this.runner.test('should create a record with a nested one to one relation', async () => {
169
+ let productWithDetails = {
170
+ ...ProductsFixtures.product_create_nested,
171
+ category_id: 1600,
172
+ related_test_product_details: {...ProductDetailsFixtures.product_details_create_nested}
173
+ };
174
+ let created = await this.productsRepo.createWithRelations(
175
+ productWithDetails,
176
+ ['related_test_product_details']
177
+ );
178
+ assert.ok(created);
179
+ assert.ok(created.id);
180
+ let details = await this.productDetailsRepo.loadBy('product_id', created.id);
181
+ assert.strictEqual(details.length, 1);
182
+ assert.strictEqual(details[0].dimensions, ProductDetailsFixtures.product_details_create_nested.dimensions);
183
+ });
184
+ }
185
+
42
186
  async testLoadWithRelations()
43
187
  {
44
188
  this.runner.group('loadWithRelations');
@@ -70,6 +214,10 @@ class RelationsTest
70
214
  assert.ok(results.length > 0);
71
215
  let product = results[0];
72
216
  assert.ok(product.related_test_categories);
217
+ assert.strictEqual(
218
+ product.related_test_categories.name,
219
+ CategoriesFixtures.category_relations_1.name
220
+ );
73
221
  assert.ok(product.related_test_reviews);
74
222
  assert.ok(Array.isArray(product.related_test_reviews));
75
223
  });
@@ -300,6 +448,9 @@ class RelationsTest
300
448
  await TestHelpers.insertFixturesViaRawSQL(this.dataServer, 'test_reviews', [
301
449
  ReviewsFixtures.review_relations_1
302
450
  ]);
451
+ await TestHelpers.insertFixturesViaRawSQL(this.dataServer, 'test_product_details', [
452
+ ProductDetailsFixtures.product_details_relations_1
453
+ ]);
303
454
  await this.runner.test('should load nested relations (category > products > reviews)', async () => {
304
455
  let results = await this.categoriesRepo.loadWithRelations(
305
456
  {id: 1600},
@@ -313,6 +464,50 @@ class RelationsTest
313
464
  let product = category.related_test_products[0];
314
465
  assert.ok(product.related_test_reviews);
315
466
  });
467
+ await this.runner.test('should load a relation whose foreign key references a non primary column', async () => {
468
+ let results = await this.categoriesRepo.loadWithRelations(
469
+ {id: 1600},
470
+ ['related_test_reviews']
471
+ );
472
+ let related = results[0].related_test_reviews;
473
+ assert.ok(Array.isArray(related));
474
+ assert.strictEqual(related.length, 1);
475
+ assert.strictEqual(related[0].reviewer_name, ReviewsFixtures.review_relations_1.reviewer_name);
476
+ });
477
+ await this.runner.test('should load a collection nested under an owning relation', async () => {
478
+ let results = await this.productDetailsRepo.loadAllWithRelations([
479
+ 'related_test_products.related_test_reviews'
480
+ ]);
481
+ let product = results[0].related_test_products;
482
+ assert.strictEqual(typeof product, 'object');
483
+ assert.strictEqual(product.name, ProductsFixtures.product_relations_1.name);
484
+ assert.ok(Array.isArray(product.related_test_reviews));
485
+ assert.strictEqual(
486
+ product.related_test_reviews[0].reviewer_name,
487
+ ReviewsFixtures.review_relations_1.reviewer_name
488
+ );
489
+ });
490
+ await this.runner.test('should load a nested relation through an owning relation as an entity', async () => {
491
+ let results = await this.productDetailsRepo.loadWithRelations(
492
+ {id: 4600},
493
+ ['related_test_products.related_test_categories']
494
+ );
495
+ let product = results[0].related_test_products;
496
+ assert.strictEqual(product.name, ProductsFixtures.product_relations_1.name);
497
+ assert.strictEqual(
498
+ product.related_test_categories.name,
499
+ CategoriesFixtures.category_relations_1.name
500
+ );
501
+ });
502
+ await this.runner.test('should return the nested collection as an array on the deepest level', async () => {
503
+ let results = await this.categoriesRepo.loadWithRelations(
504
+ {id: 1600},
505
+ ['related_test_products.related_test_reviews']
506
+ );
507
+ let product = results[0].related_test_products[0];
508
+ assert.ok(Array.isArray(product.related_test_reviews));
509
+ assert.strictEqual(product.related_test_reviews.length, 1);
510
+ });
316
511
  }
317
512
 
318
513
  async testRelationStringParsing()
@@ -341,6 +536,10 @@ class RelationsTest
341
536
  assert.ok(results);
342
537
  assert.ok(results.length > 0);
343
538
  assert.ok(results[0].related_test_categories);
539
+ assert.strictEqual(
540
+ results[0].related_test_categories.name,
541
+ CategoriesFixtures.category_relations_1.name
542
+ );
344
543
  });
345
544
  }
346
545
 
@@ -0,0 +1,255 @@
1
+ /**
2
+ *
3
+ * Reldens - Reldens Sample Data Test
4
+ * Runs the relation assertions against the real Reldens database: the schema from reldens-install-v4.0.0.sql
5
+ * and the rows inserted by reldens-basic-config-v4.0.0.sql and reldens-sample-data-v4.0.0.sql. Every expected
6
+ * count below is the amount of rows those files insert, so the assertions describe the shipped sample data.
7
+ *
8
+ */
9
+
10
+ const { TestRunner, assert } = require('../utils/test-runner');
11
+ const { ReldensSchemaLoader } = require('../utils/reldens-schema-loader');
12
+ const { ObservedValue } = require('../utils/observed-value');
13
+
14
+ class ReldensSampleDataTest
15
+ {
16
+
17
+ constructor(dataServer, repos, driverName)
18
+ {
19
+ this.driverName = driverName;
20
+ this.runner = new TestRunner();
21
+ this.dataServer = false;
22
+ this.repos = {};
23
+ this.entityNames = ['itemsItem', 'skillsClassPath', 'rooms', 'playersState'];
24
+ this.itemRelationCases = {
25
+ 'the coins requirements from the required item foreign key':
26
+ {itemId: 1, relationKey: 'related_objects_items_requirements_required_item_key', expectedRows: 4},
27
+ 'the coins rewards from the reward item foreign key':
28
+ {itemId: 1, relationKey: 'related_objects_items_rewards_reward_item_key', expectedRows: 5},
29
+ 'no coins requirement from the item foreign key':
30
+ {itemId: 1, relationKey: 'related_objects_items_requirements_item_key', expectedRows: 0},
31
+ 'the axe requirement from the item foreign key':
32
+ {itemId: 4, relationKey: 'related_objects_items_requirements_item_key', expectedRows: 1},
33
+ 'no axe requirement from the required item foreign key':
34
+ {itemId: 4, relationKey: 'related_objects_items_requirements_required_item_key', expectedRows: 0},
35
+ 'the axe reward from the item foreign key':
36
+ {itemId: 4, relationKey: 'related_objects_items_rewards_item_key', expectedRows: 1}
37
+ };
38
+ this.levelsSetKey = 'related_skills_levels_set';
39
+ this.levelsKey = 'related_skills_levels';
40
+ this.levelsModifiersKey = 'related_skills_levels_modifiers';
41
+ this.changePointsKey = 'related_rooms_change_points_room';
42
+ this.nextRoomKey = 'related_rooms_next_room';
43
+ this.skillsLevelsChain = this.levelsSetKey+'.'+this.levelsKey+'.'+this.levelsModifiersKey;
44
+ this.changePointsChain = this.changePointsKey+'.'+this.nextRoomKey;
45
+ this.journeymanClassPathId = 1;
46
+ this.journeymanLevelsSetId = 1;
47
+ this.journeymanLevelsRows = 4;
48
+ this.journeymanModifiersByLevelId = {1: 0, 2: 8, 3: 8, 4: 8};
49
+ this.botsForestRoomId = 9;
50
+ this.botsForestChangePointsRows = 1;
51
+ this.botsForestNextRoomId = 10;
52
+ this.botsForestNextRoomName = 'reldens-bots-forest-house-01-n0';
53
+ this.playersStateId = 1;
54
+ this.playersStatePlayerId = 1;
55
+ this.playersStateRoomId = 5;
56
+ }
57
+
58
+ async run()
59
+ {
60
+ this.runner.suite('Reldens Sample Data - Driver: '+this.driverName);
61
+ if(!ReldensSchemaLoader.isAvailable()){
62
+ return this.runner.fail(
63
+ 'reldens migrations must be available for '+this.driverName,
64
+ 'RELDENS_TEST_MIGRATIONS_PATH does not point to a folder containing the reldens migrations.'
65
+ );
66
+ }
67
+ this.dataServer = await ReldensSchemaLoader.setup(this.driverName);
68
+ if(!this.dataServer){
69
+ return this.runner.fail(
70
+ 'reldens sample data server must connect for '+this.driverName,
71
+ 'The '+this.driverName+' data server could not be set up.'
72
+ );
73
+ }
74
+ for(let entityName of this.entityNames){
75
+ this.repos[entityName] = this.dataServer.getEntity(entityName);
76
+ }
77
+ try {
78
+ await this.testGeneratedRepositories();
79
+ await this.testItemsItemSameTableForeignKeys();
80
+ await this.testSkillsClassPathLevelsChain();
81
+ await this.testRoomsChangePointsNestedRoom();
82
+ await this.testForeignKeyScalarWithoutRelations();
83
+ } finally {
84
+ await ReldensSchemaLoader.teardown(this.dataServer);
85
+ }
86
+ return this.runner.getResults();
87
+ }
88
+
89
+ clearedRepo(entityName)
90
+ {
91
+ if(this.dataServer.orm && this.dataServer.orm.em){
92
+ this.dataServer.orm.em.clear();
93
+ }
94
+ return this.repos[entityName];
95
+ }
96
+
97
+ assertRelationRows(record, relationKey, expectedRows, label)
98
+ {
99
+ let related = record[relationKey];
100
+ ObservedValue.log(label+'.'+relationKey, related);
101
+ assert.ok(
102
+ Array.isArray(related),
103
+ label+'.'+relationKey+' is not an array, observed: '+ObservedValue.describe(related)
104
+ );
105
+ assert.strictEqual(
106
+ related.length,
107
+ expectedRows,
108
+ label+'.'+relationKey+' rows observed: '+related.length+' - expected: '+expectedRows
109
+ );
110
+ return related;
111
+ }
112
+
113
+ async testGeneratedRepositories()
114
+ {
115
+ this.runner.group('Reldens Schema Repositories');
116
+ await this.runner.test('should expose a repository for every asserted entity', async () => {
117
+ for(let entityName of this.entityNames){
118
+ assert.ok(this.repos[entityName], 'entity "'+entityName+'" missing from the reldens schema');
119
+ }
120
+ });
121
+ }
122
+
123
+ async testItemsItemSameTableForeignKeys()
124
+ {
125
+ this.runner.group('Items Item Two Foreign Keys To The Same Table');
126
+ for(let caseName of Object.keys(this.itemRelationCases)){
127
+ let testCase = this.itemRelationCases[caseName];
128
+ await this.runner.test('should return '+caseName, async () => {
129
+ let record = await this.clearedRepo('itemsItem').loadByIdWithRelations(
130
+ testCase.itemId,
131
+ [testCase.relationKey]
132
+ );
133
+ assert.ok(record, 'items_item '+testCase.itemId+' not loaded: '+ObservedValue.describe(record));
134
+ this.assertRelationRows(record, testCase.relationKey, testCase.expectedRows, caseName);
135
+ });
136
+ }
137
+ }
138
+
139
+ async testSkillsClassPathLevelsChain()
140
+ {
141
+ this.runner.group('Skills Class Path Levels Chain');
142
+ await this.runner.test('should load the levels set with every level of the journeyman path', async () => {
143
+ let record = await this.clearedRepo('skillsClassPath').loadByIdWithRelations(
144
+ this.journeymanClassPathId,
145
+ [this.skillsLevelsChain]
146
+ );
147
+ assert.ok(record, 'journeyman class path not loaded: '+ObservedValue.describe(record));
148
+ let levelsSet = record[this.levelsSetKey];
149
+ ObservedValue.log('journeyman.'+this.levelsSetKey, levelsSet);
150
+ assert.strictEqual(
151
+ 'object',
152
+ typeof levelsSet,
153
+ 'the levels set was not populated, observed: '+ObservedValue.describe(levelsSet)
154
+ );
155
+ assert.strictEqual(
156
+ Number(levelsSet.id),
157
+ this.journeymanLevelsSetId,
158
+ 'the levels set id observed: '+ObservedValue.describe(levelsSet.id)
159
+ );
160
+ this.assertRelationRows(levelsSet, this.levelsKey, this.journeymanLevelsRows, 'journeyman levels set');
161
+ });
162
+ await this.runner.test('should load the modifiers of every journeyman level', async () => {
163
+ let record = await this.clearedRepo('skillsClassPath').loadByIdWithRelations(
164
+ this.journeymanClassPathId,
165
+ [this.skillsLevelsChain]
166
+ );
167
+ assert.ok(record, 'journeyman class path not loaded: '+ObservedValue.describe(record));
168
+ let levels = this.assertRelationRows(
169
+ record[this.levelsSetKey],
170
+ this.levelsKey,
171
+ this.journeymanLevelsRows,
172
+ 'journeyman levels set'
173
+ );
174
+ for(let level of levels){
175
+ this.assertRelationRows(
176
+ level,
177
+ this.levelsModifiersKey,
178
+ this.journeymanModifiersByLevelId[level.id],
179
+ 'journeyman level '+level.id
180
+ );
181
+ }
182
+ });
183
+ }
184
+
185
+ async testRoomsChangePointsNestedRoom()
186
+ {
187
+ this.runner.group('Rooms Change Points Nested Room');
188
+ await this.runner.test('should load the change points of the bots forest room', async () => {
189
+ let record = await this.clearedRepo('rooms').loadByIdWithRelations(
190
+ this.botsForestRoomId,
191
+ [this.changePointsChain]
192
+ );
193
+ assert.ok(record, 'bots forest room not loaded: '+ObservedValue.describe(record));
194
+ this.assertRelationRows(record, this.changePointsKey, this.botsForestChangePointsRows, 'bots forest');
195
+ });
196
+ await this.runner.test('should load the change point next room as an entity', async () => {
197
+ let record = await this.clearedRepo('rooms').loadByIdWithRelations(
198
+ this.botsForestRoomId,
199
+ [this.changePointsChain]
200
+ );
201
+ assert.ok(record, 'bots forest room not loaded: '+ObservedValue.describe(record));
202
+ let changePoints = this.assertRelationRows(
203
+ record,
204
+ this.changePointsKey,
205
+ this.botsForestChangePointsRows,
206
+ 'bots forest'
207
+ );
208
+ let nextRoom = [...changePoints].shift()[this.nextRoomKey];
209
+ ObservedValue.log('bots forest change point.'+this.nextRoomKey, nextRoom);
210
+ assert.strictEqual(
211
+ 'object',
212
+ typeof nextRoom,
213
+ 'the next room was not populated, observed: '+ObservedValue.describe(nextRoom)
214
+ );
215
+ assert.strictEqual(
216
+ Number(nextRoom.id),
217
+ this.botsForestNextRoomId,
218
+ 'the next room id observed: '+ObservedValue.describe(nextRoom.id)
219
+ );
220
+ assert.strictEqual(
221
+ nextRoom.name,
222
+ this.botsForestNextRoomName,
223
+ 'the next room name observed: '+ObservedValue.describe(nextRoom.name)
224
+ );
225
+ });
226
+ }
227
+
228
+ async testForeignKeyScalarWithoutRelations()
229
+ {
230
+ this.runner.group('Foreign Key Scalar Without Relations');
231
+ await this.runner.test('should return the players state room id as a scalar', async () => {
232
+ let record = await this.clearedRepo('playersState').loadById(this.playersStateId);
233
+ assert.ok(record, 'players state row not loaded: '+ObservedValue.describe(record));
234
+ ObservedValue.log('players state room_id', record.room_id);
235
+ assert.notStrictEqual(
236
+ 'object',
237
+ typeof record.room_id,
238
+ 'the room_id was hydrated as an entity, observed: '+ObservedValue.describe(record.room_id)
239
+ );
240
+ assert.strictEqual(
241
+ Number(record.room_id),
242
+ this.playersStateRoomId,
243
+ 'the room_id observed: '+ObservedValue.describe(record.room_id)
244
+ );
245
+ assert.strictEqual(
246
+ Number(record.player_id),
247
+ this.playersStatePlayerId,
248
+ 'the player_id observed: '+ObservedValue.describe(record.player_id)
249
+ );
250
+ });
251
+ }
252
+
253
+ }
254
+
255
+ module.exports = ReldensSampleDataTest;