@zerosls/clm-sdk 1.0.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 (81) hide show
  1. package/.docs/publicacion-npm.md +111 -0
  2. package/.env.example +14 -0
  3. package/.gitlab-ci.yml +23 -0
  4. package/README.md +202 -0
  5. package/dist/config/config.d.ts +3 -0
  6. package/dist/config/config.js +21 -0
  7. package/dist/core/api-client.d.ts +27 -0
  8. package/dist/core/api-client.js +183 -0
  9. package/dist/core/api-error.d.ts +15 -0
  10. package/dist/core/api-error.js +46 -0
  11. package/dist/core/event-emitter.d.ts +11 -0
  12. package/dist/core/event-emitter.js +32 -0
  13. package/dist/index.d.ts +41 -0
  14. package/dist/index.js +59 -0
  15. package/dist/modules/legacy/areas/areas-api.d.ts +34 -0
  16. package/dist/modules/legacy/areas/areas-api.js +44 -0
  17. package/dist/modules/legacy/areas/types.d.ts +37 -0
  18. package/dist/modules/legacy/areas/types.js +1 -0
  19. package/dist/modules/legacy/classificationtypes/classificationtypes-api.d.ts +34 -0
  20. package/dist/modules/legacy/classificationtypes/classificationtypes-api.js +46 -0
  21. package/dist/modules/legacy/classificationtypes/types.d.ts +41 -0
  22. package/dist/modules/legacy/classificationtypes/types.js +1 -0
  23. package/dist/modules/v1/auth/auth-api.d.ts +17 -0
  24. package/dist/modules/v1/auth/auth-api.js +63 -0
  25. package/dist/modules/v1/auth/types.d.ts +18 -0
  26. package/dist/modules/v1/auth/types.js +1 -0
  27. package/dist/modules/v1/main/main-api.d.ts +11 -0
  28. package/dist/modules/v1/main/main-api.js +14 -0
  29. package/dist/modules/v1/main/types.d.ts +3 -0
  30. package/dist/modules/v1/main/types.js +1 -0
  31. package/dist/modules/v1/notifications/notification-api.d.ts +16 -0
  32. package/dist/modules/v1/notifications/notification-api.js +26 -0
  33. package/dist/modules/v1/notifications/types.d.ts +53 -0
  34. package/dist/modules/v1/notifications/types.js +1 -0
  35. package/dist/modules/v1/users/types.d.ts +64 -0
  36. package/dist/modules/v1/users/types.js +1 -0
  37. package/dist/modules/v1/users/users-api.d.ts +81 -0
  38. package/dist/modules/v1/users/users-api.js +113 -0
  39. package/dist/types/common.d.ts +18 -0
  40. package/dist/types/common.js +1 -0
  41. package/dist/types/sdk.d.ts +42 -0
  42. package/dist/types/sdk.js +11 -0
  43. package/dist/utils/cache.d.ts +10 -0
  44. package/dist/utils/cache.js +43 -0
  45. package/dist/utils/http.d.ts +5 -0
  46. package/dist/utils/http.js +56 -0
  47. package/package.json +38 -0
  48. package/src/config/config.ts +24 -0
  49. package/src/core/api-client.ts +272 -0
  50. package/src/core/api-error.ts +54 -0
  51. package/src/core/event-emitter.ts +43 -0
  52. package/src/index.ts +89 -0
  53. package/src/modules/legacy/areas/areas-api.ts +73 -0
  54. package/src/modules/legacy/areas/types.ts +49 -0
  55. package/src/modules/legacy/classificationtypes/classificationtypes-api.ts +80 -0
  56. package/src/modules/legacy/classificationtypes/types.ts +52 -0
  57. package/src/modules/v1/auth/auth-api.ts +75 -0
  58. package/src/modules/v1/auth/types.ts +20 -0
  59. package/src/modules/v1/main/main-api.ts +20 -0
  60. package/src/modules/v1/main/types.ts +3 -0
  61. package/src/modules/v1/notifications/notification-api.ts +55 -0
  62. package/src/modules/v1/notifications/types.ts +58 -0
  63. package/src/modules/v1/users/types.ts +83 -0
  64. package/src/modules/v1/users/users-api.ts +148 -0
  65. package/src/types/common.ts +22 -0
  66. package/src/types/sdk.ts +38 -0
  67. package/src/utils/cache.ts +58 -0
  68. package/src/utils/http.ts +77 -0
  69. package/tests/integration/legacy/auth-areas.test.ts +115 -0
  70. package/tests/integration/legacy/auth-classification-types.test.ts +80 -0
  71. package/tests/integration/v1/auth-logs.test.ts +145 -0
  72. package/tests/integration/v1/auth-users.test.ts +189 -0
  73. package/tests/modules/legacy/areas/areas-api.test.ts +232 -0
  74. package/tests/modules/legacy/classification-types/classification-types-api.test.ts +100 -0
  75. package/tests/modules/v1/auth/auth-api.test.ts +134 -0
  76. package/tests/modules/v1/users/users-api.test.ts +176 -0
  77. package/tests/setup.ts +12 -0
  78. package/tests/utils/test-utils.ts +453 -0
  79. package/tsconfig.json +16 -0
  80. package/tsconfig.test.json +13 -0
  81. package/vitest.config.ts +16 -0
@@ -0,0 +1,232 @@
1
+ // tests/modules/areas/areas-api.test.ts
2
+ import { describe, it, expect, beforeEach } from 'vitest';
3
+ import { ClmSdk } from '../../../../src/index';
4
+ import { setupFetchMock, AUTH_TEST_DATA, AREAS_TEST_DATA } from '../../../utils/test-utils';
5
+
6
+ describe('AreasApi', () => {
7
+ let sdk: ClmSdk;
8
+ const TOKEN = AUTH_TEST_DATA.loginToken;
9
+
10
+ beforeEach(() => {
11
+ sdk = new ClmSdk({
12
+ baseUrl: 'http://216.250.117.119/ZeroServicesQA/api/v1',
13
+ organization: 'default-org',
14
+ token: TOKEN
15
+ });
16
+ });
17
+
18
+ describe('getAreas', () => {
19
+ it('debe obtener todas las áreas correctamente', async () => {
20
+ setupFetchMock([
21
+ {
22
+ url: '/catalog/areas',
23
+ method: 'GET',
24
+ response: {
25
+ status: 200,
26
+ data: AREAS_TEST_DATA.areasList
27
+ }
28
+ }
29
+ ]);
30
+
31
+ const result = await sdk.areas.getAreas();
32
+
33
+ expect(global.fetch).toHaveBeenCalledTimes(1);
34
+
35
+ const calledUrl = (global.fetch as any).mock.calls[0][0];
36
+ expect(calledUrl).toBe('http://216.250.117.119/ZeroServicesQA/api/v1/catalog/areas');
37
+
38
+ const options = (global.fetch as any).mock.calls[0][1];
39
+ expect(options.method).toBe('GET');
40
+
41
+ expect(result).toEqual(AREAS_TEST_DATA.areasList);
42
+ expect(result.dataResult.length).toBeGreaterThan(0);
43
+ expect(result.statusResponse.success).toBe(true);
44
+ });
45
+
46
+ it('debe manejar respuestas vacías', async () => {
47
+ setupFetchMock([
48
+ {
49
+ url: '/catalog/areas',
50
+ method: 'GET',
51
+ response: {
52
+ status: 200,
53
+ data: {
54
+ dataResult: [],
55
+ statusResponse: {
56
+ code: 200,
57
+ success: true,
58
+ message: 'success'
59
+ }
60
+ }
61
+ }
62
+ }
63
+ ]);
64
+
65
+ const result = await sdk.areas.getAreas();
66
+
67
+ expect(result.dataResult).toEqual([]);
68
+ expect(result.statusResponse.success).toBe(true);
69
+ });
70
+
71
+ it('debe recibir respuesta de error del servidor', async () => {
72
+ setupFetchMock([
73
+ {
74
+ url: '/catalog/areas',
75
+ method: 'GET',
76
+ response: {
77
+ status: 500,
78
+ data: {
79
+ dataResult: [],
80
+ statusResponse: {
81
+ code: 500,
82
+ success: false,
83
+ message: 'Internal server error'
84
+ }
85
+ }
86
+ }
87
+ }
88
+ ]);
89
+
90
+ const result = await sdk.areas.getAreas();
91
+
92
+ // Verificar que la respuesta contiene el error
93
+ expect(result.statusResponse.success).toBe(false);
94
+ expect(result.statusResponse.code).toBe(500);
95
+ expect(result.statusResponse.message).toContain('error');
96
+ });
97
+ });
98
+
99
+ describe('createArea', () => {
100
+ it('debe crear un área correctamente', async () => {
101
+ setupFetchMock([
102
+ {
103
+ url: '/catalog/areas',
104
+ method: 'POST',
105
+ response: {
106
+ status: 200,
107
+ data: AREAS_TEST_DATA.createAreaResponse
108
+ }
109
+ }
110
+ ]);
111
+
112
+ const result = await sdk.areas.createArea(AREAS_TEST_DATA.newAreaRequest);
113
+
114
+ const options = (global.fetch as any).mock.calls[0][1];
115
+ expect(options.method).toBe('POST');
116
+
117
+ const body = JSON.parse(options.body);
118
+ expect(body.name).toBe('Testing QA');
119
+ expect(body.description).toBe('Área de pruebas y aseguramiento de calidad');
120
+
121
+ expect(result.statusResponse.success).toBe(true);
122
+ expect(result.statusResponse.code).toBe(200);
123
+ });
124
+
125
+ it('debe enviar todos los campos requeridos', async () => {
126
+ setupFetchMock([
127
+ {
128
+ url: '/catalog/areas',
129
+ method: 'POST',
130
+ response: {
131
+ status: 200,
132
+ data: AREAS_TEST_DATA.createAreaResponse
133
+ }
134
+ }
135
+ ]);
136
+
137
+ await sdk.areas.createArea(AREAS_TEST_DATA.newAreaRequest);
138
+
139
+ const options = (global.fetch as any).mock.calls[0][1];
140
+ const body = JSON.parse(options.body);
141
+
142
+ expect(body).toHaveProperty('createdBy');
143
+ expect(body).toHaveProperty('createdByName');
144
+ expect(body).toHaveProperty('createdOn');
145
+ expect(body).toHaveProperty('lastModBy');
146
+ expect(body).toHaveProperty('lastModByName');
147
+ expect(body).toHaveProperty('lastModOn');
148
+ expect(body).toHaveProperty('name');
149
+ expect(body).toHaveProperty('description');
150
+ });
151
+ });
152
+
153
+ describe('updateArea', () => {
154
+ it('debe actualizar un área correctamente', async () => {
155
+ setupFetchMock([
156
+ {
157
+ url: '/catalog/areas',
158
+ method: 'PATCH',
159
+ response: {
160
+ status: 200,
161
+ data: AREAS_TEST_DATA.updateAreaResponse
162
+ }
163
+ }
164
+ ]);
165
+
166
+ const result = await sdk.areas.updateArea(AREAS_TEST_DATA.updateAreaRequest);
167
+
168
+ const options = (global.fetch as any).mock.calls[0][1];
169
+ expect(options.method).toBe('PATCH');
170
+
171
+ const body = JSON.parse(options.body);
172
+ expect(body.id).toBe(26);
173
+ expect(body.name).toBe('Testing QA - Actualizado');
174
+
175
+ expect(result.statusResponse.success).toBe(true);
176
+ });
177
+ });
178
+
179
+ describe('deleteArea', () => {
180
+ it('debe eliminar un área correctamente', async () => {
181
+ setupFetchMock([
182
+ {
183
+ url: '/catalog/areas',
184
+ method: 'DELETE',
185
+ response: {
186
+ status: 200,
187
+ data: AREAS_TEST_DATA.deleteAreaResponse
188
+ }
189
+ }
190
+ ]);
191
+
192
+ const result = await sdk.areas.deleteArea({ id: 26 });
193
+
194
+ const options = (global.fetch as any).mock.calls[0][1];
195
+ const calledUrl = (global.fetch as any).mock.calls[0][0];
196
+
197
+ expect(options.method).toBe('DELETE');
198
+
199
+ // Tu ApiClient envía el ID como query param
200
+ expect(calledUrl).toContain('id=26');
201
+
202
+ expect(result.statusResponse.success).toBe(true);
203
+ });
204
+ });
205
+
206
+ describe('generateAuditData', () => {
207
+ it('debe generar datos de auditoría correctamente', () => {
208
+ const auditData = sdk.areas.generateAuditData('USR001', 'Juan Pérez');
209
+
210
+ expect(auditData).toHaveProperty('createdBy', 'USR001');
211
+ expect(auditData).toHaveProperty('createdByName', 'Juan Pérez');
212
+ expect(auditData).toHaveProperty('createdOn');
213
+ expect(auditData).toHaveProperty('lastModBy', 'USR001');
214
+ expect(auditData).toHaveProperty('lastModByName', 'Juan Pérez');
215
+ expect(auditData).toHaveProperty('lastModOn');
216
+
217
+ expect(new Date(auditData.createdOn).toISOString()).toBe(auditData.createdOn);
218
+ expect(new Date(auditData.lastModOn).toISOString()).toBe(auditData.lastModOn);
219
+ });
220
+
221
+ it('debe generar fechas actuales', () => {
222
+ const before = new Date().getTime();
223
+ const auditData = sdk.areas.generateAuditData('USR001', 'Test User');
224
+ const after = new Date().getTime();
225
+
226
+ const createdTime = new Date(auditData.createdOn).getTime();
227
+
228
+ expect(createdTime).toBeGreaterThanOrEqual(before);
229
+ expect(createdTime).toBeLessThanOrEqual(after);
230
+ });
231
+ });
232
+ });
@@ -0,0 +1,100 @@
1
+ import { describe, it, expect, beforeEach } from 'vitest';
2
+ import { ClmSdk } from '../../../../src/index';
3
+ import { setupFetchMock, AUTH_TEST_DATA, CLASSIFICATION_TYPES_TEST_DATA } from '../../../utils/test-utils';
4
+
5
+ describe('ClassificationTypesApi', () => {
6
+ let sdk: ClmSdk;
7
+ const TOKEN = AUTH_TEST_DATA.loginToken;
8
+
9
+ beforeEach(() => {
10
+ sdk = new ClmSdk({
11
+ baseUrl: 'http://216.250.117.119/ZeroServicesQA/api/v1',
12
+ organization: 'default-org',
13
+ token: TOKEN
14
+ });
15
+ });
16
+
17
+ describe('getClassificationTypes', () => {
18
+ it('debe obtener todos los tipos de clasificación', async () => {
19
+ setupFetchMock([
20
+ {
21
+ url: '/catalog/clasificationtype',
22
+ method: 'GET',
23
+ response: {
24
+ status: 200,
25
+ data: CLASSIFICATION_TYPES_TEST_DATA.classificationTypesList
26
+ }
27
+ }
28
+ ]);
29
+
30
+ const result = await sdk.classificationTypes.getClassificationTypes();
31
+
32
+ expect(result.dataResult.length).toBeGreaterThan(0);
33
+ expect(result.statusResponse.success).toBe(true);
34
+ expect(result.dataResult[0]).toHaveProperty('id');
35
+ expect(result.dataResult[0]).toHaveProperty('name');
36
+ });
37
+ });
38
+
39
+ describe('createClassificationType', () => {
40
+ it('debe crear un tipo de clasificación correctamente', async () => {
41
+ setupFetchMock([
42
+ {
43
+ url: '/catalog/clasificationtype',
44
+ method: 'POST',
45
+ response: {
46
+ status: 200,
47
+ data: CLASSIFICATION_TYPES_TEST_DATA.createClassificationTypeResponse
48
+ }
49
+ }
50
+ ]);
51
+
52
+ const result = await sdk.classificationTypes.createClassificationType(
53
+ CLASSIFICATION_TYPES_TEST_DATA.newClassificationTypeRequest
54
+ );
55
+
56
+ expect(result.statusResponse.success).toBe(true);
57
+ expect(result.dataResult?.name).toBe("Contrato de Prueba");
58
+ });
59
+ });
60
+
61
+ describe('updateClassificationType', () => {
62
+ it('debe actualizar un tipo de clasificación correctamente', async () => {
63
+ setupFetchMock([
64
+ {
65
+ url: '/catalog/clasificationtype',
66
+ method: 'PUT',
67
+ response: {
68
+ status: 200,
69
+ data: CLASSIFICATION_TYPES_TEST_DATA.updateClassificationTypeResponse
70
+ }
71
+ }
72
+ ]);
73
+
74
+ const result = await sdk.classificationTypes.updateClassificationType(
75
+ CLASSIFICATION_TYPES_TEST_DATA.updateClassificationTypeRequest
76
+ );
77
+
78
+ expect(result.statusResponse.success).toBe(true);
79
+ });
80
+ });
81
+
82
+ describe('deleteClassificationType', () => {
83
+ it('debe eliminar un tipo de clasificación correctamente', async () => {
84
+ setupFetchMock([
85
+ {
86
+ url: '/catalog/clasificationtype',
87
+ method: 'DELETE',
88
+ response: {
89
+ status: 200,
90
+ data: CLASSIFICATION_TYPES_TEST_DATA.deleteClassificationTypeResponse
91
+ }
92
+ }
93
+ ]);
94
+
95
+ const result = await sdk.classificationTypes.deleteClassificationType({ id: 20 });
96
+
97
+ expect(result.statusResponse.success).toBe(true);
98
+ });
99
+ });
100
+ });
@@ -0,0 +1,134 @@
1
+ import { describe, it, expect, vi, beforeEach } from 'vitest';
2
+ import { ClmSdk } from '../../../../src/index';
3
+ import { setupFetchMock, AUTH_TEST_DATA } from '../../../utils/test-utils';
4
+
5
+ describe('AuthApi', () => {
6
+ let sdk: ClmSdk;
7
+
8
+ beforeEach(() => {
9
+ // Crear una instancia nueva del SDK para cada prueba
10
+ sdk = new ClmSdk({
11
+ baseUrl: 'https://dev-api.zeroclm.io',
12
+ organization: 'default-org'
13
+ });
14
+ });
15
+
16
+ describe('login', () => {
17
+ it('debe autenticar correctamente con credenciales válidas', async () => {
18
+ // Configurar el mock de fetch para la petición de login
19
+ setupFetchMock([
20
+ {
21
+ url: '/auth/login',
22
+ method: 'GET',
23
+ response: {
24
+ status: 200,
25
+ data: AUTH_TEST_DATA.loginToken
26
+ }
27
+ }
28
+ ]);
29
+
30
+ // Realizar login
31
+ const token = await sdk.auth.login({
32
+ email: 'test@example.com',
33
+ password: 'password123'
34
+ });
35
+
36
+ // Verificar que se hizo la petición correcta
37
+ expect(global.fetch).toHaveBeenCalledTimes(1);
38
+ expect(global.fetch).toHaveBeenCalledWith(
39
+ 'https://dev-api.zeroclm.io/auth/login',
40
+ expect.objectContaining({
41
+ method: 'GET',
42
+ headers: expect.any(Headers),
43
+ credentials: 'include'
44
+ })
45
+ );
46
+
47
+ // Verificar que el header de autorización usa Basic Auth
48
+ const headers = (global.fetch as any).mock.calls[0][1].headers;
49
+ expect(headers.get('Authorization')).toMatch(/^Basic /);
50
+
51
+ // Verificar que devolvió el token correcto
52
+ expect(token).toBe(AUTH_TEST_DATA.loginToken);
53
+
54
+ // Verificar que el token se guardó en el cliente
55
+ expect(sdk.auth.isAuthenticated()).toBe(true);
56
+ });
57
+
58
+ it('debe manejar errores de autenticación', async () => {
59
+ // Configurar mock para una respuesta de error
60
+ setupFetchMock([
61
+ {
62
+ url: '/auth/login',
63
+ method: 'GET',
64
+ response: {
65
+ status: 401,
66
+ data: {
67
+ statusCode: 401,
68
+ message: 'Invalid credentials',
69
+ error: 'Unauthorized'
70
+ }
71
+ }
72
+ }
73
+ ]);
74
+
75
+ // Intentar login con credenciales incorrectas y esperar que falle
76
+ await expect(
77
+ sdk.auth.login({
78
+ email: 'wrong@example.com',
79
+ password: 'wrongpassword'
80
+ })
81
+ ).rejects.toThrow('Invalid credentials');
82
+
83
+ // Verificar que el usuario no está autenticado después del error
84
+ expect(sdk.auth.isAuthenticated()).toBe(false);
85
+ });
86
+ });
87
+
88
+ describe('setToken', () => {
89
+ it('debe establecer el token correctamente', () => {
90
+ // Establecer el token
91
+ sdk.auth.setToken(AUTH_TEST_DATA.loginToken);
92
+
93
+ // Verificar que el token se estableció
94
+ expect(sdk.auth.isAuthenticated()).toBe(true);
95
+ });
96
+
97
+ it('debe emitir el evento tokenChanged cuando se actualiza el token', () => {
98
+ const eventHandler = vi.fn();
99
+ sdk.on('tokenChanged', eventHandler);
100
+
101
+ // Establecer token
102
+ sdk.auth.setToken(AUTH_TEST_DATA.loginToken);
103
+ expect(eventHandler).toHaveBeenCalledWith(AUTH_TEST_DATA.loginToken);
104
+
105
+ // Limpiar token
106
+ sdk.auth.setToken(null);
107
+ expect(eventHandler).toHaveBeenCalledWith(null);
108
+ });
109
+
110
+ it('debe limpiar el token cuando se pasa null', () => {
111
+ // Primero establecer un token
112
+ console.log(AUTH_TEST_DATA.loginToken);
113
+ sdk.auth.setToken(AUTH_TEST_DATA.loginToken);
114
+ expect(sdk.auth.isAuthenticated()).toBe(true);
115
+
116
+ // Luego limpiarlo
117
+ sdk.auth.setToken(null);
118
+
119
+ // Verificar que el token se eliminó
120
+ expect(sdk.auth.isAuthenticated()).toBe(false);
121
+ });
122
+ });
123
+
124
+ describe('isAuthenticated', () => {
125
+ it('debe devolver true cuando hay un token', () => {
126
+ sdk.auth.setToken(AUTH_TEST_DATA.loginToken);
127
+ expect(sdk.auth.isAuthenticated()).toBe(true);
128
+ });
129
+
130
+ it('debe devolver false cuando no hay token', () => {
131
+ expect(sdk.auth.isAuthenticated()).toBe(false);
132
+ });
133
+ });
134
+ });
@@ -0,0 +1,176 @@
1
+ import { describe, it, expect, vi, beforeEach } from 'vitest';
2
+ import { ClmSdk } from '../../../../src/index';
3
+ import { setupFetchMock, AUTH_TEST_DATA, USERS_TEST_DATA } from '../../../utils/test-utils';
4
+
5
+ describe('UsersApi - getAll', () => {
6
+ let sdk: ClmSdk;
7
+ const TOKEN = AUTH_TEST_DATA.loginToken;
8
+
9
+ beforeEach(() => {
10
+ // Inicializar SDK con un token de autenticación
11
+ sdk = new ClmSdk({
12
+ baseUrl: 'https://dev-api.zeroclm.io',
13
+ organization: 'default-org',
14
+ token: TOKEN
15
+ });
16
+ });
17
+
18
+ it('debe obtener todos los usuarios con paginación predeterminada', async () => {
19
+ // Configurar mock para la petición de usuarios
20
+ setupFetchMock([
21
+ {
22
+ url: '/users',
23
+ method: 'GET',
24
+ response: {
25
+ status: 200,
26
+ data: USERS_TEST_DATA.usersList
27
+ }
28
+ }
29
+ ]);
30
+
31
+ // Obtener usuarios
32
+ const result = await sdk.users.getAll();
33
+
34
+ // Verificar que se hizo la petición correcta
35
+ expect(global.fetch).toHaveBeenCalledTimes(1);
36
+ expect(global.fetch).toHaveBeenCalledWith(
37
+ 'https://dev-api.zeroclm.io/users',
38
+ {
39
+ method: 'GET',
40
+ headers: expect.objectContaining({
41
+ 'Authorization': `Bearer ${TOKEN}`,
42
+ 'Content-Type': 'application/json',
43
+ 'X-Organization': 'default-org'
44
+ }),
45
+ credentials: 'include'
46
+ }
47
+ );
48
+
49
+ // Verificar la respuesta
50
+ expect(result).toEqual(USERS_TEST_DATA.usersList);
51
+ expect(result.data.length).toBe(1);
52
+ expect(result.data[0].email).toBe('test@example.com');
53
+ });
54
+
55
+ it('debe aplicar parámetros de consulta correctamente', async () => {
56
+ // Configurar mock para la petición con parámetros
57
+ setupFetchMock([
58
+ {
59
+ url: '/users',
60
+ method: 'GET',
61
+ response: {
62
+ status: 200,
63
+ data: {
64
+ data: [],
65
+ meta: { total: 0, page: 1, limit: 10, pages: 0 }
66
+ }
67
+ }
68
+ }
69
+ ]);
70
+
71
+ // Parámetros de consulta a enviar
72
+ const queryParams = {
73
+ page: 1,
74
+ limit: 10,
75
+ email: 'ivan@zeroclm.com',
76
+ role_id: 1
77
+ };
78
+
79
+ // Obtener usuarios con parámetros
80
+ await sdk.users.getAll(queryParams);
81
+
82
+ // Verificar que la URL incluye los parámetros correctos
83
+ const calledUrl = (global.fetch as any).mock.calls[0][0];
84
+ expect(calledUrl).toContain('page=1');
85
+ expect(calledUrl).toContain('limit=10');
86
+ expect(calledUrl).toContain('email=ivan%40zeroclm.com');
87
+ expect(calledUrl).toContain('role_id=1');
88
+ });
89
+
90
+ it('debe manejar respuestas vacías', async () => {
91
+ // Configurar mock para respuesta vacía
92
+ setupFetchMock([
93
+ {
94
+ url: '/users',
95
+ method: 'GET',
96
+ response: {
97
+ status: 200,
98
+ data: {
99
+ data: [],
100
+ meta: { total: 0, page: 1, limit: 20, pages: 0 }
101
+ }
102
+ }
103
+ }
104
+ ]);
105
+
106
+ // Obtener usuarios
107
+ const result = await sdk.users.getAll();
108
+
109
+ // Verificar respuesta vacía
110
+ expect(result.data).toEqual([]);
111
+ expect(result.meta.total).toBe(0);
112
+ });
113
+
114
+ it('debe manejar errores del servidor', async () => {
115
+ // Configurar mock para respuesta de error
116
+ setupFetchMock([
117
+ {
118
+ url: '/users',
119
+ method: 'GET',
120
+ response: {
121
+ status: 500,
122
+ data: {
123
+ statusCode: 500,
124
+ message: 'Internal server error',
125
+ error: 'Server Error'
126
+ }
127
+ }
128
+ }
129
+ ]);
130
+
131
+ // Intentar obtener usuarios y esperar que falle
132
+ await expect(sdk.users.getAll()).rejects.toThrow('Internal server error');
133
+ });
134
+
135
+ it('debe manejar errores de autorización', async () => {
136
+ // Configurar mock para respuesta de error de autorización
137
+ setupFetchMock([
138
+ {
139
+ url: '/users',
140
+ method: 'GET',
141
+ response: {
142
+ status: 401,
143
+ data: {
144
+ statusCode: 401,
145
+ message: 'Unauthorized',
146
+ error: 'Unauthorized'
147
+ }
148
+ }
149
+ }
150
+ ]);
151
+
152
+ // Intentar obtener usuarios con token inválido y esperar que falle
153
+ await expect(sdk.users.getAll()).rejects.toThrow('Unauthorized');
154
+ });
155
+
156
+ it('debe manejar errores de permisos', async () => {
157
+ // Configurar mock para respuesta de error de permisos
158
+ setupFetchMock([
159
+ {
160
+ url: '/users',
161
+ method: 'GET',
162
+ response: {
163
+ status: 403,
164
+ data: {
165
+ statusCode: 403,
166
+ message: 'Insufficient scope',
167
+ error: 'Forbidden'
168
+ }
169
+ }
170
+ }
171
+ ]);
172
+
173
+ // Intentar obtener usuarios sin permisos suficientes y esperar que falle
174
+ await expect(sdk.users.getAll()).rejects.toThrow('Insufficient scope');
175
+ });
176
+ });
package/tests/setup.ts ADDED
@@ -0,0 +1,12 @@
1
+ import { beforeAll, afterEach, vi } from 'vitest';
2
+
3
+ // Setup global fetch mock
4
+ beforeAll(() => {
5
+ global.fetch = vi.fn();
6
+ global.btoa = vi.fn((str) => Buffer.from(str).toString('base64'));
7
+ });
8
+
9
+ // Reset all mocks after each test
10
+ afterEach(() => {
11
+ vi.resetAllMocks();
12
+ });