masterrecord 0.3.37 → 0.3.39

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.
@@ -0,0 +1,538 @@
1
+ /**
2
+ * Test: Global Model Registry
3
+ * Verifies that multiple context instances don't trigger duplicate warnings (CLI pattern)
4
+ * while genuine duplicates in constructors still warn properly.
5
+ */
6
+
7
+ console.log("╔════════════════════════════════════════════════════════════════╗");
8
+ console.log("║ Global Model Registry Test - Context Class ║");
9
+ console.log("╚════════════════════════════════════════════════════════════════╝\n");
10
+
11
+ let passed = 0;
12
+ let failed = 0;
13
+
14
+ // Simulate context class with global model registry
15
+ class SimulatedContext {
16
+ static _globalModelRegistry = {};
17
+
18
+ constructor() {
19
+ this.__name = this.constructor.name;
20
+ this.__entities = [];
21
+ this.__builderEntities = [];
22
+
23
+ // Track if this is the first instance of this context class
24
+ const globalRegistry = SimulatedContext._globalModelRegistry[this.__name];
25
+ this.__isFirstInstance = !globalRegistry || globalRegistry.size === 0;
26
+
27
+ // Initialize global model registry for this context class if not exists
28
+ if (!SimulatedContext._globalModelRegistry[this.__name]) {
29
+ SimulatedContext._globalModelRegistry[this.__name] = new Set();
30
+ }
31
+ }
32
+
33
+ dbset(model, tableName = null) {
34
+ const entityName = tableName || model.name;
35
+
36
+ // Create a simple model object
37
+ const validModel = {
38
+ __name: entityName,
39
+ ...model.schema
40
+ };
41
+
42
+ // Check if model is registered in this specific instance
43
+ const existingIndex = this.__entities.findIndex(e => e.__name === entityName);
44
+
45
+ if (existingIndex !== -1) {
46
+ // Model already registered in THIS instance - duplicate within same constructor
47
+ // Only warn on first instance (subsequent instances expected to have same pattern)
48
+ if (this.__isFirstInstance) {
49
+ console.warn(`Warning: dbset() called multiple times for table '${entityName}' in constructor - updating existing registration`);
50
+ }
51
+ // Update existing registration
52
+ this.__entities[existingIndex] = validModel;
53
+ this.__builderEntities[existingIndex] = { type: 'builder', model: validModel };
54
+ } else {
55
+ // Model not registered in this instance - add it
56
+ this.__entities.push(validModel);
57
+ this.__builderEntities.push({ type: 'builder', model: validModel });
58
+ }
59
+
60
+ // Always mark as globally seen (after handling instance registration)
61
+ const globalRegistry = SimulatedContext._globalModelRegistry[this.__name];
62
+ globalRegistry.add(entityName);
63
+
64
+ return {
65
+ seed: (data) => {} // Mock seed method
66
+ };
67
+ }
68
+ }
69
+
70
+ // Test entity models
71
+ const User = { name: 'User', schema: { id: 'int', name: 'string' } };
72
+ const Auth = { name: 'Auth', schema: { id: 'int', token: 'string' } };
73
+ const Settings = { name: 'Settings', schema: { id: 'int', key: 'string' } };
74
+
75
+ // Helper to capture console warnings
76
+ function captureWarnings(fn) {
77
+ const warnings = [];
78
+ const originalWarn = console.warn;
79
+ console.warn = (msg) => warnings.push(msg);
80
+
81
+ try {
82
+ fn();
83
+ } finally {
84
+ console.warn = originalWarn;
85
+ }
86
+
87
+ return warnings;
88
+ }
89
+
90
+ // Test helper
91
+ function test(description, fn) {
92
+ try {
93
+ // Clear registry before each test
94
+ SimulatedContext._globalModelRegistry = {};
95
+
96
+ fn();
97
+ passed++;
98
+ console.log(`✓ ${description}`);
99
+ } catch (error) {
100
+ failed++;
101
+ console.log(`✗ ${description}`);
102
+ console.log(` Error: ${error.message}`);
103
+ }
104
+ }
105
+
106
+ // ============================================================================
107
+ // TEST 1: Multiple Context Instances (CLI Pattern) - No Warnings
108
+ // ============================================================================
109
+
110
+ test('Multiple instances should not warn (CLI pattern)', () => {
111
+ class TestContext extends SimulatedContext {
112
+ constructor() {
113
+ super();
114
+ this.dbset(User);
115
+ this.dbset(Auth);
116
+ this.dbset(Settings);
117
+ }
118
+ }
119
+
120
+ const warnings = captureWarnings(() => {
121
+ const ctx1 = new TestContext();
122
+ const ctx2 = new TestContext();
123
+ const ctx3 = new TestContext();
124
+
125
+ if (ctx1.__entities.length !== 3) throw new Error('ctx1 should have 3 entities');
126
+ if (ctx2.__entities.length !== 3) throw new Error('ctx2 should have 3 entities');
127
+ if (ctx3.__entities.length !== 3) throw new Error('ctx3 should have 3 entities');
128
+ });
129
+
130
+ if (warnings.length !== 0) {
131
+ throw new Error(`Should not emit warnings, but got ${warnings.length}`);
132
+ }
133
+ });
134
+
135
+ // ============================================================================
136
+ // TEST 2: Models Registered in Global Registry
137
+ // ============================================================================
138
+
139
+ test('Models should be added to global registry on first instance', () => {
140
+ class TestContext extends SimulatedContext {
141
+ constructor() {
142
+ super();
143
+ this.dbset(User);
144
+ this.dbset(Auth);
145
+ }
146
+ }
147
+
148
+ const ctx1 = new TestContext();
149
+
150
+ const registry = SimulatedContext._globalModelRegistry['TestContext'];
151
+ if (!registry) throw new Error('Global registry should exist');
152
+ if (!registry.has('User')) throw new Error('Registry should have User');
153
+ if (!registry.has('Auth')) throw new Error('Registry should have Auth');
154
+ if (registry.size !== 2) throw new Error('Registry should have 2 models');
155
+ });
156
+
157
+ // ============================================================================
158
+ // TEST 3: No Duplicates in Global Registry
159
+ // ============================================================================
160
+
161
+ test('Global registry should not have duplicates after multiple instances', () => {
162
+ class TestContext extends SimulatedContext {
163
+ constructor() {
164
+ super();
165
+ this.dbset(User);
166
+ }
167
+ }
168
+
169
+ const ctx1 = new TestContext();
170
+ const ctx2 = new TestContext();
171
+ const ctx3 = new TestContext();
172
+
173
+ const registry = SimulatedContext._globalModelRegistry['TestContext'];
174
+ if (registry.size !== 1) {
175
+ throw new Error('Registry should have 1 model, not ' + registry.size);
176
+ }
177
+ });
178
+
179
+ // ============================================================================
180
+ // TEST 4: Genuine Duplicate in Constructor - Should Warn
181
+ // ============================================================================
182
+
183
+ test('Genuine duplicate in constructor should warn', () => {
184
+ class BuggyContext extends SimulatedContext {
185
+ constructor() {
186
+ super();
187
+ this.dbset(User);
188
+ this.dbset(User); // Duplicate
189
+ }
190
+ }
191
+
192
+ const warnings = captureWarnings(() => {
193
+ const ctx = new BuggyContext();
194
+ });
195
+
196
+ if (warnings.length !== 1) {
197
+ throw new Error(`Should emit 1 warning, but got ${warnings.length}`);
198
+ }
199
+
200
+ if (!warnings[0].includes('Warning: dbset() called multiple times')) {
201
+ throw new Error('Warning should mention duplicate dbset call');
202
+ }
203
+
204
+ if (!warnings[0].includes('User')) {
205
+ throw new Error('Warning should mention table name');
206
+ }
207
+ });
208
+
209
+ // ============================================================================
210
+ // TEST 5: Warn Only Once for Duplicate
211
+ // ============================================================================
212
+
213
+ test('Duplicate should warn only on first instance', () => {
214
+ class BuggyContext extends SimulatedContext {
215
+ constructor() {
216
+ super();
217
+ this.dbset(User);
218
+ this.dbset(User);
219
+ }
220
+ }
221
+
222
+ const warnings = captureWarnings(() => {
223
+ const ctx1 = new BuggyContext();
224
+ const ctx2 = new BuggyContext();
225
+ const ctx3 = new BuggyContext();
226
+ });
227
+
228
+ if (warnings.length !== 1) {
229
+ throw new Error(`Should warn only once, but got ${warnings.length} warnings`);
230
+ }
231
+ });
232
+
233
+ // ============================================================================
234
+ // TEST 6: Entity Count Correct Despite Duplicate
235
+ // ============================================================================
236
+
237
+ test('Entity should be registered once despite duplicate in constructor', () => {
238
+ class BuggyContext extends SimulatedContext {
239
+ constructor() {
240
+ super();
241
+ this.dbset(User);
242
+ this.dbset(User);
243
+ }
244
+ }
245
+
246
+ const ctx = new BuggyContext();
247
+
248
+ if (ctx.__entities.length !== 1) {
249
+ throw new Error('Should have 1 entity, not ' + ctx.__entities.length);
250
+ }
251
+
252
+ if (ctx.__entities[0].__name !== 'User') {
253
+ throw new Error('Entity should be User');
254
+ }
255
+ });
256
+
257
+ // ============================================================================
258
+ // TEST 7: Different Context Classes - No Warnings
259
+ // ============================================================================
260
+
261
+ test('Same model in different context classes should not warn', () => {
262
+ class UserContext extends SimulatedContext {
263
+ constructor() {
264
+ super();
265
+ this.dbset(User);
266
+ }
267
+ }
268
+
269
+ class AdminContext extends SimulatedContext {
270
+ constructor() {
271
+ super();
272
+ this.dbset(User);
273
+ }
274
+ }
275
+
276
+ const warnings = captureWarnings(() => {
277
+ const userCtx = new UserContext();
278
+ const adminCtx = new AdminContext();
279
+ });
280
+
281
+ if (warnings.length !== 0) {
282
+ throw new Error('Different context classes should not warn');
283
+ }
284
+ });
285
+
286
+ // ============================================================================
287
+ // TEST 8: Separate Registries Per Context Class
288
+ // ============================================================================
289
+
290
+ test('Different context classes should have separate registries', () => {
291
+ class UserContext extends SimulatedContext {
292
+ constructor() {
293
+ super();
294
+ this.dbset(User);
295
+ this.dbset(Auth);
296
+ }
297
+ }
298
+
299
+ class AdminContext extends SimulatedContext {
300
+ constructor() {
301
+ super();
302
+ this.dbset(User);
303
+ this.dbset(Settings);
304
+ }
305
+ }
306
+
307
+ const userCtx = new UserContext();
308
+ const adminCtx = new AdminContext();
309
+
310
+ const userRegistry = SimulatedContext._globalModelRegistry['UserContext'];
311
+ const adminRegistry = SimulatedContext._globalModelRegistry['AdminContext'];
312
+
313
+ if (!userRegistry.has('User')) throw new Error('UserContext should have User');
314
+ if (!userRegistry.has('Auth')) throw new Error('UserContext should have Auth');
315
+ if (userRegistry.has('Settings')) throw new Error('UserContext should not have Settings');
316
+
317
+ if (!adminRegistry.has('User')) throw new Error('AdminContext should have User');
318
+ if (!adminRegistry.has('Settings')) throw new Error('AdminContext should have Settings');
319
+ if (adminRegistry.has('Auth')) throw new Error('AdminContext should not have Auth');
320
+ });
321
+
322
+ // ============================================================================
323
+ // TEST 9: Multiple Instances of Different Contexts
324
+ // ============================================================================
325
+
326
+ test('Multiple instances of different contexts should not warn', () => {
327
+ class UserContext extends SimulatedContext {
328
+ constructor() {
329
+ super();
330
+ this.dbset(User);
331
+ }
332
+ }
333
+
334
+ class AdminContext extends SimulatedContext {
335
+ constructor() {
336
+ super();
337
+ this.dbset(User);
338
+ }
339
+ }
340
+
341
+ const warnings = captureWarnings(() => {
342
+ const userCtx1 = new UserContext();
343
+ const adminCtx1 = new AdminContext();
344
+ const userCtx2 = new UserContext();
345
+ const adminCtx2 = new AdminContext();
346
+ });
347
+
348
+ if (warnings.length !== 0) {
349
+ throw new Error('Multiple instances of different contexts should not warn');
350
+ }
351
+ });
352
+
353
+ // ============================================================================
354
+ // TEST 10: qaContext Pattern (dbset then dbset.seed)
355
+ // ============================================================================
356
+
357
+ test('qaContext pattern (dbset then dbset.seed) should warn about duplicate', () => {
358
+ class QAContext extends SimulatedContext {
359
+ constructor() {
360
+ super();
361
+ this.dbset(User);
362
+ // ... imagine 150 lines ...
363
+ this.dbset(User).seed([{ id: 1, name: 'Test' }]);
364
+ }
365
+ }
366
+
367
+ const warnings = captureWarnings(() => {
368
+ const ctx = new QAContext();
369
+ });
370
+
371
+ if (warnings.length !== 1) {
372
+ throw new Error('Should warn about duplicate in constructor');
373
+ }
374
+ });
375
+
376
+ // ============================================================================
377
+ // TEST 11: Mixed Registration (Some New, Some Duplicate)
378
+ // ============================================================================
379
+
380
+ test('Mixed registration should warn only about duplicates', () => {
381
+ class MixedContext extends SimulatedContext {
382
+ constructor() {
383
+ super();
384
+ this.dbset(User); // New
385
+ this.dbset(Auth); // New
386
+ this.dbset(User); // Duplicate
387
+ this.dbset(Settings); // New
388
+ this.dbset(Auth); // Duplicate
389
+ }
390
+ }
391
+
392
+ const warnings = captureWarnings(() => {
393
+ const ctx = new MixedContext();
394
+ });
395
+
396
+ if (warnings.length !== 2) {
397
+ throw new Error(`Should warn about 2 duplicates, got ${warnings.length}`);
398
+ }
399
+
400
+ const ctx = new MixedContext();
401
+ if (ctx.__entities.length !== 3) {
402
+ throw new Error('Should have 3 unique entities');
403
+ }
404
+ });
405
+
406
+ // ============================================================================
407
+ // TEST 12: Empty Context
408
+ // ============================================================================
409
+
410
+ test('Empty context should not warn', () => {
411
+ class EmptyContext extends SimulatedContext {
412
+ constructor() {
413
+ super();
414
+ }
415
+ }
416
+
417
+ const warnings = captureWarnings(() => {
418
+ const ctx1 = new EmptyContext();
419
+ const ctx2 = new EmptyContext();
420
+ });
421
+
422
+ if (warnings.length !== 0) {
423
+ throw new Error('Empty context should not warn');
424
+ }
425
+
426
+ const registry = SimulatedContext._globalModelRegistry['EmptyContext'];
427
+ if (!registry) throw new Error('Registry should exist');
428
+ if (registry.size !== 0) throw new Error('Registry should be empty');
429
+ });
430
+
431
+ // ============================================================================
432
+ // TEST 13: Large Context (50 models)
433
+ // ============================================================================
434
+
435
+ test('Large context with 50 models should not warn on multiple instances', () => {
436
+ class LargeContext extends SimulatedContext {
437
+ constructor() {
438
+ super();
439
+ for (let i = 0; i < 50; i++) {
440
+ this.dbset({ name: `Model${i}`, schema: { id: 'int' } });
441
+ }
442
+ }
443
+ }
444
+
445
+ const warnings = captureWarnings(() => {
446
+ const ctx1 = new LargeContext();
447
+ const ctx2 = new LargeContext();
448
+ });
449
+
450
+ if (warnings.length !== 0) {
451
+ throw new Error('Large context should not warn');
452
+ }
453
+
454
+ const registry = SimulatedContext._globalModelRegistry['LargeContext'];
455
+ if (registry.size !== 50) {
456
+ throw new Error(`Registry should have 50 models, got ${registry.size}`);
457
+ }
458
+ });
459
+
460
+ // ============================================================================
461
+ // TEST 14: Registry Isolation
462
+ // ============================================================================
463
+
464
+ test('Registry should not pollute other context classes', () => {
465
+ class ContextA extends SimulatedContext {
466
+ constructor() {
467
+ super();
468
+ this.dbset(User);
469
+ }
470
+ }
471
+
472
+ class ContextB extends SimulatedContext {
473
+ constructor() {
474
+ super();
475
+ this.dbset(Auth);
476
+ }
477
+ }
478
+
479
+ const ctxA = new ContextA();
480
+ const ctxB = new ContextB();
481
+
482
+ const registryA = SimulatedContext._globalModelRegistry['ContextA'];
483
+ const registryB = SimulatedContext._globalModelRegistry['ContextB'];
484
+
485
+ if (!registryA.has('User')) throw new Error('ContextA should have User');
486
+ if (registryA.has('Auth')) throw new Error('ContextA should not have Auth');
487
+
488
+ if (!registryB.has('Auth')) throw new Error('ContextB should have Auth');
489
+ if (registryB.has('User')) throw new Error('ContextB should not have User');
490
+ });
491
+
492
+ // ============================================================================
493
+ // TEST 15: Many Context Classes
494
+ // ============================================================================
495
+
496
+ test('Many context classes should work independently', () => {
497
+ const warnings = captureWarnings(() => {
498
+ for (let i = 0; i < 10; i++) {
499
+ const ContextClass = class extends SimulatedContext {
500
+ constructor() {
501
+ super();
502
+ this.dbset(User);
503
+ }
504
+ };
505
+ Object.defineProperty(ContextClass, 'name', { value: `Context${i}` });
506
+
507
+ // Create 3 instances of each
508
+ new ContextClass();
509
+ new ContextClass();
510
+ new ContextClass();
511
+ }
512
+ });
513
+
514
+ if (warnings.length !== 0) {
515
+ throw new Error('Multiple context classes should not warn');
516
+ }
517
+
518
+ if (Object.keys(SimulatedContext._globalModelRegistry).length !== 10) {
519
+ throw new Error('Should have 10 registries');
520
+ }
521
+ });
522
+
523
+ // ============================================================================
524
+ // RESULTS
525
+ // ============================================================================
526
+
527
+ console.log("\n" + "=".repeat(70));
528
+ console.log(`Tests Passed: ${passed}`);
529
+ console.log(`Tests Failed: ${failed}`);
530
+ console.log("=".repeat(70));
531
+
532
+ if (failed > 0) {
533
+ console.log("\n❌ Some tests failed!\n");
534
+ process.exit(1);
535
+ } else {
536
+ console.log("\n✅ All tests passed!\n");
537
+ process.exit(0);
538
+ }