@obisey/nest 0.1.35 → 0.1.37

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 (38) hide show
  1. package/package.json +11 -3
  2. package/prototipos/__tests__/baseResolver.e2e.spec.d.ts +9 -0
  3. package/prototipos/__tests__/baseResolver.e2e.spec.js +172 -0
  4. package/prototipos/__tests__/baseResolver.integration.spec.d.ts +8 -0
  5. package/prototipos/__tests__/baseResolver.integration.spec.js +105 -0
  6. package/prototipos/__tests__/baseResolver.spec.d.ts +14 -0
  7. package/prototipos/__tests__/baseResolver.spec.js +305 -0
  8. package/prototipos/__tests__/baseService.e2e.simple.spec.d.ts +7 -0
  9. package/prototipos/__tests__/baseService.e2e.simple.spec.js +92 -0
  10. package/prototipos/__tests__/baseService.e2e.validation.spec.d.ts +7 -0
  11. package/prototipos/__tests__/baseService.e2e.validation.spec.js +157 -0
  12. package/prototipos/__tests__/baseService.integration.spec.d.ts +8 -0
  13. package/prototipos/__tests__/baseService.integration.spec.js +115 -0
  14. package/prototipos/__tests__/baseService.spec.d.ts +14 -0
  15. package/prototipos/__tests__/baseService.spec.js +253 -0
  16. package/prototipos/__tests__/cachear-types.spec.d.ts +8 -0
  17. package/prototipos/__tests__/cachear-types.spec.js +79 -0
  18. package/prototipos/__tests__/graphql-flow.e2e.spec.d.ts +9 -0
  19. package/prototipos/__tests__/graphql-flow.e2e.spec.js +270 -0
  20. package/prototipos/__tests__/test-helpers/mock-sequelize.d.ts +83 -0
  21. package/prototipos/__tests__/test-helpers/mock-sequelize.js +138 -0
  22. package/prototipos/baseResolver.d.ts +8 -8
  23. package/prototipos/baseResolver.js +23 -17
  24. package/prototipos/baseService.js +23 -75
  25. package/prototipos/utils/cachear.d.ts +12 -2
  26. package/prototipos/utils/cachear.js +14 -9
  27. package/adapters/index.d.ts +0 -11
  28. package/adapters/index.js +0 -27
  29. package/adapters/sequelize/index.d.ts +0 -5
  30. package/adapters/sequelize/index.js +0 -9
  31. package/adapters/sequelize/property.adapter.d.ts +0 -79
  32. package/adapters/sequelize/property.adapter.js +0 -134
  33. package/prototipos/index.d.ts +0 -9
  34. package/prototipos/index.js +0 -26
  35. package/receptor.d.ts +0 -25
  36. package/receptor.js +0 -153
  37. package/redisStore.d.ts +0 -11
  38. package/redisStore.js +0 -78
@@ -0,0 +1,79 @@
1
+ "use strict";
2
+ /**
3
+ * Type-Safe Cachear Usage Examples
4
+ *
5
+ * Validates the <TResult, TCache> generic design:
6
+ * - TResult = live type (Sequelize instance, number, etc.)
7
+ * - TCache = serialized type (string, string[], etc.)
8
+ */
9
+ Object.defineProperty(exports, "__esModule", { value: true });
10
+ const sequelize_1 = require("sequelize");
11
+ const cachear_1 = require("../utils/cachear");
12
+ describe('Cachear - Type Safety with <TResult, TCache>', () => {
13
+ let sequelize;
14
+ let Usuario;
15
+ beforeAll(async () => {
16
+ sequelize = new sequelize_1.Sequelize('sqlite::memory:', { logging: false });
17
+ Usuario = sequelize.define('Usuario', {
18
+ id: { type: sequelize_1.DataTypes.INTEGER, primaryKey: true, autoIncrement: true },
19
+ nombre: sequelize_1.DataTypes.STRING,
20
+ });
21
+ await sequelize.sync();
22
+ await Usuario.create({ id: 1, nombre: 'Juan' });
23
+ });
24
+ afterAll(async () => {
25
+ await sequelize.close();
26
+ });
27
+ describe('Single item: <Model, string>', () => {
28
+ it('buscarXid pattern: TResult=Model, TCache=string', async () => {
29
+ const mockCacheManager = {
30
+ get: jest.fn().mockResolvedValue(null),
31
+ set: jest.fn(),
32
+ };
33
+ const result = await (0, cachear_1.cachear)(true, mockCacheManager, 'test-key', (cache) => Promise.resolve(Usuario.build(JSON.parse(cache), { raw: false, isNewRecord: false })), (resultado) => Promise.resolve(JSON.stringify(resultado.get({ plain: true }))), () => Usuario.findByPk(1), () => mockCacheManager.get('test-key'), (key, datos) => mockCacheManager.set(key, datos));
34
+ expect(result.resultado).toBeDefined();
35
+ expect(typeof result.resultado.get).toBe('function');
36
+ });
37
+ it('cache HIT path: revisar reconstructs instance', async () => {
38
+ const cachedJson = JSON.stringify({ id: 1, nombre: 'Juan' });
39
+ const mockCacheManager = {
40
+ get: jest.fn().mockResolvedValue(cachedJson),
41
+ set: jest.fn(),
42
+ };
43
+ const result = await (0, cachear_1.cachear)(true, mockCacheManager, 'test-key', (cache) => Promise.resolve(Usuario.build(JSON.parse(cache), { raw: false, isNewRecord: false })), (resultado) => Promise.resolve(JSON.stringify(resultado.get({ plain: true }))), () => Usuario.findByPk(1), () => mockCacheManager.get('test-key'), (key, datos) => mockCacheManager.set(key, datos));
44
+ expect(result.cache).toBe(true);
45
+ expect(typeof result.resultado.get).toBe('function');
46
+ });
47
+ });
48
+ describe('Array items: <Model[], string[]>', () => {
49
+ it('buscarTodos pattern: TResult=Model[], TCache=string[]', async () => {
50
+ const mockCacheManager = {
51
+ get: jest.fn().mockResolvedValue(null),
52
+ set: jest.fn(),
53
+ };
54
+ const mockRepository = {
55
+ findAll: jest.fn().mockResolvedValue([
56
+ Usuario.build({ id: 1, nombre: 'Juan' }),
57
+ ]),
58
+ };
59
+ const result = await (0, cachear_1.cachear)(true, mockCacheManager, 'usuarios:all', (cache) => Promise.resolve(cache.map((n) => Usuario.build(JSON.parse(n), { raw: false, isNewRecord: false }))), (resultado) => Promise.resolve(resultado.map((n) => JSON.stringify(n.get({ plain: true })))), () => mockRepository.findAll(), () => mockCacheManager.get('usuarios:all'), (key, datos) => mockCacheManager.set(key, datos));
60
+ expect(Array.isArray(result.resultado)).toBe(true);
61
+ result.resultado.forEach(item => {
62
+ expect(typeof item.get).toBe('function');
63
+ });
64
+ });
65
+ });
66
+ describe('Number: <number, string>', () => {
67
+ it('contar pattern: TResult=number, TCache=string', async () => {
68
+ const mockCacheManager = {
69
+ get: jest.fn().mockResolvedValue(null),
70
+ set: jest.fn(),
71
+ };
72
+ const mockRepository = {
73
+ count: jest.fn().mockResolvedValue(42),
74
+ };
75
+ const result = await (0, cachear_1.cachear)(true, mockCacheManager, 'count-key', (cache) => Promise.resolve(JSON.parse(cache)), (resultado) => Promise.resolve(JSON.stringify(resultado)), () => mockRepository.count(), () => mockCacheManager.get('count-key'), (key, datos) => mockCacheManager.set(key, datos));
76
+ expect(result.resultado).toBe(42);
77
+ });
78
+ });
79
+ });
@@ -0,0 +1,9 @@
1
+ /**
2
+ * GraphQL Flow E2E Test
3
+ *
4
+ * ✅ Reproduces EXACT production flow:
5
+ * GraphQL Query → Resolver → Service → BaseService → ResolveField → Relation loading
6
+ *
7
+ * ✅ THIS TEST SHOULD FAIL if item.$get is not callable
8
+ */
9
+ export {};
@@ -0,0 +1,270 @@
1
+ "use strict";
2
+ /**
3
+ * GraphQL Flow E2E Test
4
+ *
5
+ * ✅ Reproduces EXACT production flow:
6
+ * GraphQL Query → Resolver → Service → BaseService → ResolveField → Relation loading
7
+ *
8
+ * ✅ THIS TEST SHOULD FAIL if item.$get is not callable
9
+ */
10
+ Object.defineProperty(exports, "__esModule", { value: true });
11
+ const sequelize_1 = require("sequelize");
12
+ const baseService_1 = require("../baseService");
13
+ const baseResolver_1 = require("../baseResolver");
14
+ const mock_sequelize_1 = require("./test-helpers/mock-sequelize");
15
+ describe('GraphQL Flow E2E - paginacionUsuario with empresas', () => {
16
+ let sequelize;
17
+ let Usuario;
18
+ let Empresa;
19
+ let usuarioService;
20
+ let usuarioResolver;
21
+ let mockRedis;
22
+ let mockCacheManager;
23
+ beforeAll(async () => {
24
+ // Setup real database
25
+ sequelize = new sequelize_1.Sequelize('sqlite::memory:', { logging: false });
26
+ // Define models
27
+ Usuario = sequelize.define('Usuario', {
28
+ id: { type: sequelize_1.DataTypes.INTEGER, primaryKey: true, autoIncrement: true },
29
+ nombre: sequelize_1.DataTypes.STRING,
30
+ email: sequelize_1.DataTypes.STRING,
31
+ });
32
+ Empresa = sequelize.define('Empresa', {
33
+ id: { type: sequelize_1.DataTypes.INTEGER, primaryKey: true, autoIncrement: true },
34
+ nombre: sequelize_1.DataTypes.STRING,
35
+ });
36
+ // Define relations
37
+ Usuario.belongsToMany(Empresa, {
38
+ through: 'UsuarioEmpresa',
39
+ as: 'empresas',
40
+ foreignKey: 'usuarioId',
41
+ });
42
+ Empresa.belongsToMany(Usuario, {
43
+ through: 'UsuarioEmpresa',
44
+ as: 'usuarios',
45
+ foreignKey: 'empresaId',
46
+ });
47
+ await sequelize.sync();
48
+ // Create test data
49
+ const emp1 = await Empresa.create({ id: 1, nombre: 'Empresa A' });
50
+ const emp2 = await Empresa.create({ id: 2, nombre: 'Empresa B' });
51
+ const user1 = await Usuario.create({ id: 381, nombre: 'Juan', email: 'juan@test.com' });
52
+ const user2 = await Usuario.create({ id: 382, nombre: 'Maria', email: 'maria@test.com' });
53
+ await user1.addEmpresas([emp1, emp2]);
54
+ await user2.addEmpresas([emp1]);
55
+ // Setup mocks
56
+ mockRedis = (0, mock_sequelize_1.createMockRedisClient)();
57
+ mockCacheManager = (0, mock_sequelize_1.createMockCacheManager)();
58
+ // Create real service and resolver
59
+ const BaseServiceClass = (0, baseService_1.BaseService)('usuario');
60
+ usuarioService = new BaseServiceClass(mockRedis, mockCacheManager);
61
+ const BaseResolverClass = (0, baseResolver_1.BaseResolver)('usuario');
62
+ usuarioResolver = new BaseResolverClass(mockRedis, mockCacheManager);
63
+ });
64
+ afterAll(async () => {
65
+ await sequelize.close();
66
+ });
67
+ describe('🔴 GraphQL Query: paginacionUsuario', () => {
68
+ it('REPRODUCES: paginacionUsuario(pagina: 2, limite: 100)', async () => {
69
+ // Arrange - Setup mock repository
70
+ const mockRepository = {
71
+ sequelize,
72
+ findAll: jest.fn(async (options) => {
73
+ return Usuario.findAll(options);
74
+ }),
75
+ count: jest.fn(async () => Usuario.count()),
76
+ };
77
+ mockCacheManager.get = jest.fn().mockResolvedValue(null);
78
+ mockRedis.hExists = jest.fn().mockResolvedValue(false);
79
+ mockRedis.hSet = jest.fn();
80
+ // Act 1: Call buscarTodos (simulating resolver calling service)
81
+ const paginationResult = await usuarioService.buscarTodos({
82
+ value: { cacheable: false },
83
+ key: {
84
+ universo: 'starconsole',
85
+ IdEmpresa: 1,
86
+ fragmentos: [{ nombre: 'usuario' }],
87
+ tipo: 'query',
88
+ },
89
+ Clase: Usuario,
90
+ repository: mockRepository,
91
+ });
92
+ // Assert 1: Should return items
93
+ expect(paginationResult).toBeDefined();
94
+ // THIS IS THE CRITICAL PART - if items are JSON, the next step fails
95
+ const items = paginationResult?.items || paginationResult;
96
+ expect(Array.isArray(items)).toBe(true);
97
+ // Act 2: For each item, load empresas (simulating @ResolveField)
98
+ // THIS IS WHERE IT FAILS IN PRODUCTION: item.$get is not a function
99
+ if (Array.isArray(items) && items.length > 0) {
100
+ const firstItem = items[0];
101
+ // THIS IS THE LINE THAT FAILS IN PRODUCTION
102
+ // At baseResolver.js:486 -> item.$get is not a function
103
+ expect(typeof firstItem.get).toBe('function'); // Item must be instance
104
+ // Try to load relation (would call $get in real code)
105
+ if (typeof firstItem.$get === 'function') {
106
+ const empresas = await firstItem.$get('empresas');
107
+ expect(Array.isArray(empresas)).toBe(true);
108
+ expect(empresas.length).toBeGreaterThan(0);
109
+ }
110
+ else {
111
+ // THIS IS WHAT FAILS IN PRODUCTION:
112
+ throw new Error('item.$get is not a function - ' +
113
+ 'item is JSON plano, not Sequelize instance');
114
+ }
115
+ }
116
+ });
117
+ it('CRITICAL: Each item in paginacionUsuario MUST be Sequelize instance', async () => {
118
+ // Arrange
119
+ const mockRepository = {
120
+ sequelize,
121
+ findAll: jest.fn(async (options) => {
122
+ return Usuario.findAll(options);
123
+ }),
124
+ count: jest.fn(async () => Usuario.count()),
125
+ };
126
+ mockCacheManager.get = jest.fn().mockResolvedValue(null);
127
+ mockRedis.hExists = jest.fn().mockResolvedValue(false);
128
+ // Act
129
+ const result = await usuarioService.buscarTodos({
130
+ value: { cacheable: false },
131
+ key: {
132
+ universo: 'starconsole',
133
+ IdEmpresa: 1,
134
+ fragmentos: [{ nombre: 'usuario' }],
135
+ tipo: 'query',
136
+ },
137
+ Clase: Usuario,
138
+ repository: mockRepository,
139
+ });
140
+ const items = result?.items || result;
141
+ // Assert - Each item MUST be instance
142
+ if (Array.isArray(items)) {
143
+ items.forEach((item, idx) => {
144
+ try {
145
+ // THIS IS WHAT FAILS IN PRODUCTION
146
+ expect(item).toBeInstanceOf(sequelize_1.Model);
147
+ expect(typeof item.get).toBe('function');
148
+ expect(typeof item.$get).toBe('function');
149
+ }
150
+ catch (e) {
151
+ throw new Error(`Item ${idx} is not Sequelize instance. ` +
152
+ `Error at: baseResolver.js:486 -> item.$get is not a function`);
153
+ }
154
+ });
155
+ }
156
+ });
157
+ });
158
+ describe('⚠️ Production Error Simulation', () => {
159
+ it('DETECTS: item.$get is not a function (the actual error)', async () => {
160
+ // This simulates what happens in production
161
+ const items = [
162
+ { id: 381, nombre: 'Juan', empresas: [] }, // ❌ JSON plano
163
+ ];
164
+ const firstItem = items[0];
165
+ // THIS SHOULD FAIL (like in production):
166
+ try {
167
+ if (typeof firstItem.$get !== 'function') {
168
+ throw new TypeError('item.$get is not a function');
169
+ }
170
+ }
171
+ catch (err) {
172
+ expect(err.message).toContain('item.$get is not a function');
173
+ // ✅ Test DETECTS the problem
174
+ }
175
+ });
176
+ it('PREVENTS: With proper instance, $get works', async () => {
177
+ // Get real instance
178
+ const usuario = await Usuario.findByPk(381);
179
+ // THIS SHOULD WORK:
180
+ expect(typeof usuario.$get).toBe('function');
181
+ // Can load relations
182
+ const empresas = await usuario.$get('empresas');
183
+ expect(Array.isArray(empresas)).toBe(true);
184
+ });
185
+ });
186
+ describe('📊 Comparison: What Works vs What Fails', () => {
187
+ it('WORKS: Service returns Sequelize instances', async () => {
188
+ const mockRepository = {
189
+ sequelize,
190
+ findAll: jest.fn(async (options) => {
191
+ return Usuario.findAll(options);
192
+ }),
193
+ count: jest.fn(async () => Usuario.count()),
194
+ };
195
+ mockCacheManager.get = jest.fn().mockResolvedValue(null);
196
+ mockRedis.hExists = jest.fn().mockResolvedValue(false);
197
+ const result = await usuarioService.buscarTodos({
198
+ value: { cacheable: false },
199
+ key: {
200
+ universo: 'starconsole',
201
+ IdEmpresa: 1,
202
+ fragmentos: [{ nombre: 'usuario' }],
203
+ tipo: 'query',
204
+ },
205
+ Clase: Usuario,
206
+ repository: mockRepository,
207
+ });
208
+ const items = result?.items || result;
209
+ // ✅ WORKS
210
+ if (Array.isArray(items) && items[0]) {
211
+ expect(typeof items[0].$get).toBe('function');
212
+ expect(typeof items[0].get).toBe('function');
213
+ }
214
+ });
215
+ it('FAILS: Service returns JSON plano', () => {
216
+ // Simulate wrong implementation
217
+ const wrongResult = [
218
+ { id: 381, nombre: 'Juan', email: 'juan@test.com' }, // ❌ JSON
219
+ ];
220
+ // ❌ FAILS
221
+ expect(typeof wrongResult[0].$get).not.toBe('function');
222
+ // This is the error in production:
223
+ // await wrongResult[0].$get('empresas') → TypeError!
224
+ });
225
+ });
226
+ describe('🎯 Summary: GraphQL Flow Validation', () => {
227
+ it('Full flow: Query → Service → Relation loading works only with instances', async () => {
228
+ // Step 1: Query paginacionUsuario
229
+ const mockRepository = {
230
+ sequelize,
231
+ findAll: jest.fn(async (options) => Usuario.findAll(options)),
232
+ count: jest.fn(async () => Usuario.count()),
233
+ };
234
+ mockCacheManager.get = jest.fn().mockResolvedValue(null);
235
+ mockRedis.hExists = jest.fn().mockResolvedValue(false);
236
+ // Step 2: Service returns items
237
+ const result = await usuarioService.buscarTodos({
238
+ value: { cacheable: false },
239
+ key: {
240
+ universo: 'starconsole',
241
+ IdEmpresa: 1,
242
+ fragmentos: [{ nombre: 'usuario' }],
243
+ tipo: 'query',
244
+ },
245
+ Clase: Usuario,
246
+ repository: mockRepository,
247
+ });
248
+ const items = result?.items || result;
249
+ // Step 3: For @ResolveField empresas
250
+ if (Array.isArray(items)) {
251
+ for (const item of items) {
252
+ // This is where production fails:
253
+ // If item is JSON: TypeError: item.$get is not a function
254
+ // If item is instance: ✅ Works
255
+ if (typeof item.$get === 'function') {
256
+ // ✅ WORKS - item is instance
257
+ const empresas = await item.$get('empresas');
258
+ expect(Array.isArray(empresas)).toBe(true);
259
+ }
260
+ else {
261
+ // ❌ FAILS - item is JSON
262
+ throw new Error('Cannot load empresas: item.$get is not a function\n' +
263
+ 'This is the exact error from production:\n' +
264
+ 'baseResolver.js:486 -> item.$get is not a function');
265
+ }
266
+ }
267
+ }
268
+ });
269
+ });
270
+ });
@@ -0,0 +1,83 @@
1
+ /**
2
+ * Mock Sequelize Model Factory
3
+ * Creates fake Sequelize instances with all required methods for testing
4
+ */
5
+ export interface MockSequelizeOptions {
6
+ [key: string]: any;
7
+ }
8
+ /**
9
+ * Creates a mock Sequelize Model instance
10
+ * ✅ Has sequelize property (marks it as instance)
11
+ * ✅ Has .get() method
12
+ * ✅ Has .$get(), .$set(), .$add(), .$remove() methods
13
+ * ✅ Can be used with JSON.stringify/parse cycle
14
+ */
15
+ export declare function createMockSequelizeModel(data?: MockSequelizeOptions): any;
16
+ /**
17
+ * Mock Cache Manager for testing caching behavior
18
+ * Simulates cache-manager operations
19
+ */
20
+ export declare const createMockCacheManager: () => {
21
+ get: jest.Mock<any, any, any>;
22
+ set: jest.Mock<any, any, any>;
23
+ del: jest.Mock<any, any, any>;
24
+ reset: jest.Mock<any, any, any>;
25
+ hget: jest.Mock<any, any, any>;
26
+ hset: jest.Mock<any, any, any>;
27
+ hdel: jest.Mock<any, any, any>;
28
+ keys: jest.Mock<any, any, any>;
29
+ };
30
+ /**
31
+ * Mock Redis Client for testing Redis operations
32
+ * Simulates redis client operations
33
+ */
34
+ export declare const createMockRedisClient: () => {
35
+ connect: jest.Mock<any, any, any>;
36
+ disconnect: jest.Mock<any, any, any>;
37
+ get: jest.Mock<any, any, any>;
38
+ set: jest.Mock<any, any, any>;
39
+ del: jest.Mock<any, any, any>;
40
+ hget: jest.Mock<any, any, any>;
41
+ hset: jest.Mock<any, any, any>;
42
+ hdel: jest.Mock<any, any, any>;
43
+ hExists: jest.Mock<any, any, any>;
44
+ expire: jest.Mock<any, any, any>;
45
+ flushDb: jest.Mock<any, any, any>;
46
+ ping: jest.Mock<any, any, any>;
47
+ keys: jest.Mock<any, any, any>;
48
+ };
49
+ /**
50
+ * Mock Sequelize Repository for testing database operations
51
+ */
52
+ export declare const createMockRepository: (Model?: any) => {
53
+ sequelize: {
54
+ models: {
55
+ Usuario: any;
56
+ Empresa: any;
57
+ Conversacion: any;
58
+ };
59
+ define: jest.Mock<any, any, any>;
60
+ };
61
+ findByPk: jest.Mock<any, any, any>;
62
+ findAll: jest.Mock<any, any, any>;
63
+ findOne: jest.Mock<any, any, any>;
64
+ create: jest.Mock<any, any, any>;
65
+ update: jest.Mock<any, any, any>;
66
+ destroy: jest.Mock<any, any, any>;
67
+ count: jest.Mock<any, any, any>;
68
+ findAndCountAll: jest.Mock<any, any, any>;
69
+ };
70
+ /**
71
+ * Helper to simulate cache hit with JSON data
72
+ * Used to test: JSON → Sequelize Instance reconstruction
73
+ */
74
+ export declare function simulateCacheHit(data: any): string;
75
+ /**
76
+ * Helper to simulate cache miss (null)
77
+ * Used to test: Database → Service → Cache path
78
+ */
79
+ export declare function simulateCacheMiss(): null;
80
+ /**
81
+ * Helper to create instance array for pagination tests
82
+ */
83
+ export declare function createMockInstanceArray(count: number, baseId?: number): any[];
@@ -0,0 +1,138 @@
1
+ "use strict";
2
+ /**
3
+ * Mock Sequelize Model Factory
4
+ * Creates fake Sequelize instances with all required methods for testing
5
+ */
6
+ Object.defineProperty(exports, "__esModule", { value: true });
7
+ exports.createMockRepository = exports.createMockRedisClient = exports.createMockCacheManager = void 0;
8
+ exports.createMockSequelizeModel = createMockSequelizeModel;
9
+ exports.simulateCacheHit = simulateCacheHit;
10
+ exports.simulateCacheMiss = simulateCacheMiss;
11
+ exports.createMockInstanceArray = createMockInstanceArray;
12
+ /**
13
+ * Creates a mock Sequelize Model instance
14
+ * ✅ Has sequelize property (marks it as instance)
15
+ * ✅ Has .get() method
16
+ * ✅ Has .$get(), .$set(), .$add(), .$remove() methods
17
+ * ✅ Can be used with JSON.stringify/parse cycle
18
+ */
19
+ function createMockSequelizeModel(data = {}) {
20
+ const instance = {
21
+ ...data,
22
+ // 🔴 CRITICAL: sequelize property marks this as a Sequelize instance
23
+ sequelize: {
24
+ models: {},
25
+ define: jest.fn(),
26
+ },
27
+ // Instance methods
28
+ get: jest.fn((options) => {
29
+ if (options?.plain) {
30
+ const { sequelize, ...plainData } = instance;
31
+ return plainData;
32
+ }
33
+ return instance;
34
+ }),
35
+ // Relation methods - Critical for @ResolveField
36
+ $get: jest.fn(),
37
+ $set: jest.fn(),
38
+ $add: jest.fn(),
39
+ $remove: jest.fn(),
40
+ // Persistence methods
41
+ save: jest.fn(function () {
42
+ return Promise.resolve(this);
43
+ }),
44
+ destroy: jest.fn(function () {
45
+ return Promise.resolve();
46
+ }),
47
+ reload: jest.fn(function () {
48
+ return Promise.resolve(this);
49
+ }),
50
+ // Helper to mark as JSON for serialization tests
51
+ constructor: {
52
+ name: 'SequelizeModel',
53
+ },
54
+ };
55
+ return instance;
56
+ }
57
+ /**
58
+ * Mock Cache Manager for testing caching behavior
59
+ * Simulates cache-manager operations
60
+ */
61
+ const createMockCacheManager = () => ({
62
+ get: jest.fn(),
63
+ set: jest.fn(),
64
+ del: jest.fn(),
65
+ reset: jest.fn(),
66
+ hget: jest.fn(),
67
+ hset: jest.fn(),
68
+ hdel: jest.fn(),
69
+ keys: jest.fn(),
70
+ });
71
+ exports.createMockCacheManager = createMockCacheManager;
72
+ /**
73
+ * Mock Redis Client for testing Redis operations
74
+ * Simulates redis client operations
75
+ */
76
+ const createMockRedisClient = () => ({
77
+ connect: jest.fn().mockResolvedValue(undefined),
78
+ disconnect: jest.fn().mockResolvedValue(undefined),
79
+ get: jest.fn().mockResolvedValue(null),
80
+ set: jest.fn().mockResolvedValue('OK'),
81
+ del: jest.fn().mockResolvedValue(1),
82
+ hget: jest.fn().mockResolvedValue(null),
83
+ hset: jest.fn().mockResolvedValue('OK'),
84
+ hdel: jest.fn().mockResolvedValue(1),
85
+ hExists: jest.fn().mockResolvedValue(false),
86
+ expire: jest.fn().mockResolvedValue(1),
87
+ flushDb: jest.fn().mockResolvedValue('OK'),
88
+ ping: jest.fn().mockResolvedValue('PONG'),
89
+ keys: jest.fn().mockResolvedValue([]), // ← Redis.keys() for cache invalidation
90
+ });
91
+ exports.createMockRedisClient = createMockRedisClient;
92
+ /**
93
+ * Mock Sequelize Repository for testing database operations
94
+ */
95
+ const createMockRepository = (Model = createMockSequelizeModel()) => ({
96
+ sequelize: {
97
+ models: {
98
+ 'Usuario': Model,
99
+ 'Empresa': Model,
100
+ 'Conversacion': Model,
101
+ },
102
+ define: jest.fn(),
103
+ },
104
+ findByPk: jest.fn(),
105
+ findAll: jest.fn(),
106
+ findOne: jest.fn(),
107
+ create: jest.fn(),
108
+ update: jest.fn(),
109
+ destroy: jest.fn(),
110
+ count: jest.fn(),
111
+ findAndCountAll: jest.fn(),
112
+ });
113
+ exports.createMockRepository = createMockRepository;
114
+ /**
115
+ * Helper to simulate cache hit with JSON data
116
+ * Used to test: JSON → Sequelize Instance reconstruction
117
+ */
118
+ function simulateCacheHit(data) {
119
+ return JSON.stringify(data);
120
+ }
121
+ /**
122
+ * Helper to simulate cache miss (null)
123
+ * Used to test: Database → Service → Cache path
124
+ */
125
+ function simulateCacheMiss() {
126
+ return null;
127
+ }
128
+ /**
129
+ * Helper to create instance array for pagination tests
130
+ */
131
+ function createMockInstanceArray(count, baseId = 1) {
132
+ return Array.from({ length: count }, (_, i) => createMockSequelizeModel({
133
+ id: baseId + i,
134
+ name: `Item${baseId + i}`,
135
+ createdAt: new Date(),
136
+ updatedAt: new Date(),
137
+ }));
138
+ }
@@ -17,13 +17,13 @@ export declare function BaseResolver(modelo: string): {
17
17
  desligar: (servicio: any, alias: any, IdModelo: any, IdPlaneta: any, borrarSSR?: boolean) => Promise<boolean>;
18
18
  };
19
19
  hasManyBelongsToMany(universo: any, planeta: any, IdEmpresa: any, pagina?: any): {
20
- resolvefield: (item: any, where: any, cacheable: any, Clase: any) => Promise<any>;
20
+ resolvefield: (item: any, where: any, cacheable: any, Clase: any) => Promise<any[]>;
21
21
  query: (servicio: any, IdModelo: any, Clase: any) => Promise<any>;
22
22
  ligar: (servicio: any, alias: any, IdModelo: any, IdPlaneta: any, through?: any, borrarSSR?: boolean) => Promise<boolean>;
23
23
  desligar: (servicio: any, alias: any, IdModelo: any, IdPlaneta: any, borrarSSR?: boolean) => Promise<boolean>;
24
24
  };
25
25
  nodo(universo: any, planeta: any, IdEmpresa: any, pagina?: any): {
26
- resolvefield: (item: any, where: any, cacheable: any, Clase: any) => Promise<any>;
26
+ resolvefield: (item: any, where: any, cacheable: any, Clase: any) => Promise<any[]>;
27
27
  query: (servicio: any, IdModelo: any, Clase: any) => Promise<any>;
28
28
  ligar: (servicio: any, alias: any, IdModelo: any, IdPlaneta: any, through: any, borrarSSR?: boolean) => Promise<boolean>;
29
29
  desligar: (servicio: any, alias: any, IdModelo: any, IdPlaneta: any, borrarSSR?: boolean) => Promise<boolean>;
@@ -40,7 +40,7 @@ export declare function BaseResolver(modelo: string): {
40
40
  arbol: (item: any, where: any, cacheable: any, Clase: any) => Promise<void>;
41
41
  preResolvefield: (item: any, where: any, cacheable: any, Clase: any) => Promise<any>;
42
42
  preQuery: (servicio: any, IdModelo: any, Clase: any) => Promise<any>;
43
- subResolvefield: (item: any, where: any, cacheable: any, Clase: any) => Promise<any>;
43
+ subResolvefield: (item: any, where: any, cacheable: any, Clase: any) => Promise<any[]>;
44
44
  subQuery: (servicio: any, IdModelo: any, Clase: any) => Promise<any>;
45
45
  ligar: (IdModelo: any, IdPlaneta: any, borrarSSR: any) => Promise<void>;
46
46
  desligar: (IdModelo: any, IdPlaneta: any, borrarSSR: any) => Promise<void>;
@@ -68,7 +68,7 @@ export declare function BaseResolver(modelo: string): {
68
68
  Clase: any;
69
69
  tipo: any;
70
70
  relacion: any;
71
- }, pagina?: any): Promise<any>;
71
+ }, pagina?: any): Promise<any[]>;
72
72
  ligar({ universo, servicio, alias, IdPlaneta, IdModelo, tipo, planeta, through, borrarSSR, }: {
73
73
  universo: any;
74
74
  servicio: any;
@@ -108,13 +108,13 @@ export declare function BaseResolver(modelo: string): {
108
108
  desligar: (servicio: any, alias: any, IdModelo: any, IdPlaneta: any, borrarSSR?: boolean) => Promise<boolean>;
109
109
  };
110
110
  hasManyBelongsToMany(universo: any, planeta: any, IdEmpresa: any, pagina?: any): {
111
- resolvefield: (item: any, where: any, cacheable: any, Clase: any) => Promise<any>;
111
+ resolvefield: (item: any, where: any, cacheable: any, Clase: any) => Promise<any[]>;
112
112
  query: (servicio: any, IdModelo: any, Clase: any) => Promise<any>;
113
113
  ligar: (servicio: any, alias: any, IdModelo: any, IdPlaneta: any, through?: any, borrarSSR?: boolean) => Promise<boolean>;
114
114
  desligar: (servicio: any, alias: any, IdModelo: any, IdPlaneta: any, borrarSSR?: boolean) => Promise<boolean>;
115
115
  };
116
116
  nodo(universo: any, planeta: any, IdEmpresa: any, pagina?: any): {
117
- resolvefield: (item: any, where: any, cacheable: any, Clase: any) => Promise<any>;
117
+ resolvefield: (item: any, where: any, cacheable: any, Clase: any) => Promise<any[]>;
118
118
  query: (servicio: any, IdModelo: any, Clase: any) => Promise<any>;
119
119
  ligar: (servicio: any, alias: any, IdModelo: any, IdPlaneta: any, through: any, borrarSSR?: boolean) => Promise<boolean>;
120
120
  desligar: (servicio: any, alias: any, IdModelo: any, IdPlaneta: any, borrarSSR?: boolean) => Promise<boolean>;
@@ -131,7 +131,7 @@ export declare function BaseResolver(modelo: string): {
131
131
  arbol: (item: any, where: any, cacheable: any, Clase: any) => Promise<void>;
132
132
  preResolvefield: (item: any, where: any, cacheable: any, Clase: any) => Promise<any>;
133
133
  preQuery: (servicio: any, IdModelo: any, Clase: any) => Promise<any>;
134
- subResolvefield: (item: any, where: any, cacheable: any, Clase: any) => Promise<any>;
134
+ subResolvefield: (item: any, where: any, cacheable: any, Clase: any) => Promise<any[]>;
135
135
  subQuery: (servicio: any, IdModelo: any, Clase: any) => Promise<any>;
136
136
  ligar: (IdModelo: any, IdPlaneta: any, borrarSSR: any) => Promise<void>;
137
137
  desligar: (IdModelo: any, IdPlaneta: any, borrarSSR: any) => Promise<void>;
@@ -159,7 +159,7 @@ export declare function BaseResolver(modelo: string): {
159
159
  Clase: any;
160
160
  tipo: any;
161
161
  relacion: any;
162
- }, pagina?: any): Promise<any>;
162
+ }, pagina?: any): Promise<any[]>;
163
163
  ligar({ universo, servicio, alias, IdPlaneta, IdModelo, tipo, planeta, through, borrarSSR, }: {
164
164
  universo: any;
165
165
  servicio: any;