@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.
- package/package.json +11 -3
- package/prototipos/__tests__/baseResolver.e2e.spec.d.ts +9 -0
- package/prototipos/__tests__/baseResolver.e2e.spec.js +172 -0
- package/prototipos/__tests__/baseResolver.integration.spec.d.ts +8 -0
- package/prototipos/__tests__/baseResolver.integration.spec.js +105 -0
- package/prototipos/__tests__/baseResolver.spec.d.ts +14 -0
- package/prototipos/__tests__/baseResolver.spec.js +305 -0
- package/prototipos/__tests__/baseService.e2e.simple.spec.d.ts +7 -0
- package/prototipos/__tests__/baseService.e2e.simple.spec.js +92 -0
- package/prototipos/__tests__/baseService.e2e.validation.spec.d.ts +7 -0
- package/prototipos/__tests__/baseService.e2e.validation.spec.js +157 -0
- package/prototipos/__tests__/baseService.integration.spec.d.ts +8 -0
- package/prototipos/__tests__/baseService.integration.spec.js +115 -0
- package/prototipos/__tests__/baseService.spec.d.ts +14 -0
- package/prototipos/__tests__/baseService.spec.js +253 -0
- package/prototipos/__tests__/cachear-types.spec.d.ts +8 -0
- package/prototipos/__tests__/cachear-types.spec.js +79 -0
- package/prototipos/__tests__/graphql-flow.e2e.spec.d.ts +9 -0
- package/prototipos/__tests__/graphql-flow.e2e.spec.js +270 -0
- package/prototipos/__tests__/test-helpers/mock-sequelize.d.ts +83 -0
- package/prototipos/__tests__/test-helpers/mock-sequelize.js +138 -0
- package/prototipos/baseResolver.d.ts +8 -8
- package/prototipos/baseResolver.js +23 -17
- package/prototipos/baseService.js +23 -75
- package/prototipos/utils/cachear.d.ts +12 -2
- package/prototipos/utils/cachear.js +14 -9
- package/adapters/index.d.ts +0 -11
- package/adapters/index.js +0 -27
- package/adapters/sequelize/index.d.ts +0 -5
- package/adapters/sequelize/index.js +0 -9
- package/adapters/sequelize/property.adapter.d.ts +0 -79
- package/adapters/sequelize/property.adapter.js +0 -134
- package/prototipos/index.d.ts +0 -9
- package/prototipos/index.js +0 -26
- package/receptor.d.ts +0 -25
- package/receptor.js +0 -153
- package/redisStore.d.ts +0 -11
- 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,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;
|