@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,77 @@
1
+ // import { RequestOptions } from "../types/common";
2
+
3
+ // Construye URL con parámetros query
4
+ export function buildUrl(
5
+ baseUrl: string,
6
+ endpoint: string,
7
+ params?: Record<string, any>
8
+ ): string {
9
+ const url = new URL(`${baseUrl}${endpoint}`);
10
+
11
+ if (params) {
12
+ Object.entries(params).forEach(([key, value]) => {
13
+ if (value !== undefined && value !== null) {
14
+ url.searchParams.append(key, String(value));
15
+ }
16
+ });
17
+ }
18
+
19
+ return url.toString();
20
+ }
21
+
22
+ // Construye cabeceras HTTP
23
+ export function buildHeaders(
24
+ token?: string | null,
25
+ customHeaders?: Record<string, string>
26
+ ): Headers {
27
+ const headers = new Headers({
28
+ "Content-Type": "application/json",
29
+ Accept: "application/json",
30
+ ...customHeaders,
31
+ });
32
+
33
+ if (token && !headers.get("Authorization")) {
34
+ headers.append("Authorization", `Bearer ${token}`);
35
+ }
36
+
37
+ return headers;
38
+ }
39
+
40
+ // Genera una clave de caché única para cada petición
41
+ export function generateCacheKey(
42
+ method: string,
43
+ url: string,
44
+ data?: any
45
+ ): string {
46
+ const dataString = data ? JSON.stringify(data) : "";
47
+ return `${method}:${url}:${dataString}`;
48
+ }
49
+
50
+ // Parse respuesta JSON con manejo de errores
51
+ export async function parseResponse<T>(response: Response): Promise<T> {
52
+ const responseText = await response.text();
53
+ try {
54
+ // Si no hay contenido, devolver objeto vacío
55
+ if (response.status === 204) {
56
+ return {} as T;
57
+ }
58
+
59
+ // Intentar parsear como JSON
60
+ const data = JSON.parse(responseText);
61
+ return data as T;
62
+ } catch (error) {
63
+ return responseText as T;
64
+ }
65
+ }
66
+
67
+ export function buildQueryParams(params: object | null): string {
68
+ if (!params) return "";
69
+ const queryParams = Object.entries(params)
70
+ .filter((line) => Boolean(line[1]))
71
+ .map(
72
+ ([key, value]) =>
73
+ `${encodeURIComponent(key)}=${encodeURIComponent(value)}`
74
+ );
75
+ if (queryParams.length === 0) return "";
76
+ return "?" + queryParams.join("&");
77
+ }
@@ -0,0 +1,115 @@
1
+ // tests/integration/auth-areas.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('Integración Auth + Areas', () => {
7
+ let sdk: ClmSdk;
8
+
9
+ beforeEach(() => {
10
+ sdk = new ClmSdk({
11
+ baseUrl: 'http://216.250.117.119/ZeroServicesQA/api/v1',
12
+ organization: 'default-org'
13
+ });
14
+ });
15
+
16
+ it('debe autenticar y luego obtener áreas', async () => {
17
+ setupFetchMock([
18
+ {
19
+ url: '/auth/login',
20
+ method: 'GET',
21
+ response: {
22
+ status: 200,
23
+ data: AUTH_TEST_DATA.loginToken
24
+ }
25
+ },
26
+ {
27
+ url: '/catalog/areas',
28
+ method: 'GET',
29
+ response: {
30
+ status: 200,
31
+ data: AREAS_TEST_DATA.areasList
32
+ }
33
+ }
34
+ ]);
35
+
36
+ const token = await sdk.auth.login({
37
+ email: 'ivan@zeroclm.com',
38
+ password: 'Pass#Dev2024'
39
+ });
40
+
41
+ expect(token).toBe(AUTH_TEST_DATA.loginToken);
42
+
43
+ const areasResponse = await sdk.areas.getAreas();
44
+
45
+ expect(areasResponse.dataResult).toBeDefined();
46
+ expect(areasResponse.statusResponse.success).toBe(true);
47
+ });
48
+
49
+ it('debe realizar el flujo CRUD completo', async () => {
50
+ setupFetchMock([
51
+ {
52
+ url: '/auth/login',
53
+ method: 'GET',
54
+ response: {
55
+ status: 200,
56
+ data: AUTH_TEST_DATA.loginToken
57
+ }
58
+ },
59
+ {
60
+ url: '/catalog/areas',
61
+ method: 'GET',
62
+ response: {
63
+ status: 200,
64
+ data: AREAS_TEST_DATA.areasList
65
+ }
66
+ },
67
+ {
68
+ url: '/catalog/areas',
69
+ method: 'POST',
70
+ response: {
71
+ status: 200,
72
+ data: AREAS_TEST_DATA.createAreaResponse
73
+ }
74
+ },
75
+ {
76
+ url: '/catalog/areas',
77
+ method: 'PATCH',
78
+ response: {
79
+ status: 200,
80
+ data: AREAS_TEST_DATA.updateAreaResponse
81
+ }
82
+ },
83
+ {
84
+ url: '/catalog/areas',
85
+ method: 'DELETE',
86
+ response: {
87
+ status: 200,
88
+ data: AREAS_TEST_DATA.deleteAreaResponse
89
+ }
90
+ }
91
+ ]);
92
+
93
+ // 1. Login
94
+ await sdk.auth.login({
95
+ email: 'ivan@zeroclm.com',
96
+ password: 'Pass#Dev2024'
97
+ });
98
+
99
+ // 2. Listar áreas
100
+ const areas = await sdk.areas.getAreas();
101
+ expect(areas.dataResult.length).toBeGreaterThan(0);
102
+
103
+ // 3. Crear área
104
+ const created = await sdk.areas.createArea(AREAS_TEST_DATA.newAreaRequest);
105
+ expect(created.statusResponse.success).toBe(true);
106
+
107
+ // 4. Actualizar área
108
+ const updated = await sdk.areas.updateArea(AREAS_TEST_DATA.updateAreaRequest);
109
+ expect(updated.statusResponse.success).toBe(true);
110
+
111
+ // 5. Eliminar área
112
+ const deleted = await sdk.areas.deleteArea({ id: 26 });
113
+ expect(deleted.statusResponse.success).toBe(true);
114
+ });
115
+ });
@@ -0,0 +1,80 @@
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('Integración Auth + ClassificationTypes', () => {
6
+ let sdk: ClmSdk;
7
+
8
+ beforeEach(() => {
9
+ sdk = new ClmSdk({
10
+ baseUrl: 'http://216.250.117.119/ZeroServicesQA/api/v1',
11
+ organization: 'default-org'
12
+ });
13
+ });
14
+
15
+ it('debe realizar el flujo CRUD completo', async () => {
16
+ setupFetchMock([
17
+ {
18
+ url: '/auth/login',
19
+ method: 'GET',
20
+ response: {
21
+ status: 200,
22
+ data: AUTH_TEST_DATA.loginToken
23
+ }
24
+ },
25
+ {
26
+ url: '/catalog/clasificationtype',
27
+ method: 'GET',
28
+ response: {
29
+ status: 200,
30
+ data: CLASSIFICATION_TYPES_TEST_DATA.classificationTypesList
31
+ }
32
+ },
33
+ {
34
+ url: '/catalog/clasificationtype',
35
+ method: 'POST',
36
+ response: {
37
+ status: 200,
38
+ data: CLASSIFICATION_TYPES_TEST_DATA.createClassificationTypeResponse
39
+ }
40
+ },
41
+ {
42
+ url: '/catalog/clasificationtype',
43
+ method: 'PUT',
44
+ response: {
45
+ status: 200,
46
+ data: CLASSIFICATION_TYPES_TEST_DATA.updateClassificationTypeResponse
47
+ }
48
+ },
49
+ {
50
+ url: '/catalog/clasificationtype',
51
+ method: 'DELETE',
52
+ response: {
53
+ status: 200,
54
+ data: CLASSIFICATION_TYPES_TEST_DATA.deleteClassificationTypeResponse
55
+ }
56
+ }
57
+ ]);
58
+
59
+ await sdk.auth.login({
60
+ email: 'ivan@zeroclm.com',
61
+ password: 'Pass#Dev2024'
62
+ });
63
+
64
+ const list = await sdk.classificationTypes.getClassificationTypes();
65
+ expect(list.dataResult.length).toBeGreaterThan(0);
66
+
67
+ const created = await sdk.classificationTypes.createClassificationType(
68
+ CLASSIFICATION_TYPES_TEST_DATA.newClassificationTypeRequest
69
+ );
70
+ expect(created.statusResponse.success).toBe(true);
71
+
72
+ const updated = await sdk.classificationTypes.updateClassificationType(
73
+ CLASSIFICATION_TYPES_TEST_DATA.updateClassificationTypeRequest
74
+ );
75
+ expect(updated.statusResponse.success).toBe(true);
76
+
77
+ const deleted = await sdk.classificationTypes.deleteClassificationType({ id: 20 });
78
+ expect(deleted.statusResponse.success).toBe(true);
79
+ });
80
+ });
@@ -0,0 +1,145 @@
1
+ // tests/integration/v1/auth-logs.test.ts
2
+ import { describe, it, expect, vi, beforeEach } from 'vitest';
3
+ import { ClmSdk } from '../../../src/index';
4
+ import { setupFetchMock, AUTH_TEST_DATA, LOGS_TEST_DATA } from '../../utils/test-utils';
5
+
6
+ describe('Integración Auth + Logs', () => {
7
+ let sdk: ClmSdk;
8
+
9
+ beforeEach(() => {
10
+ sdk = new ClmSdk({
11
+ baseUrl: 'https://dev-api.zeroclm.io',
12
+ organization: 'default-org'
13
+ });
14
+ });
15
+
16
+ it('debe autenticar y luego obtener logs', async () => {
17
+ setupFetchMock([
18
+ {
19
+ url: '/auth/login',
20
+ method: 'GET',
21
+ response: {
22
+ status: 200,
23
+ data: AUTH_TEST_DATA.loginToken
24
+ }
25
+ },
26
+ {
27
+ url: '/logs',
28
+ method: 'GET',
29
+ response: {
30
+ status: 200,
31
+ data: LOGS_TEST_DATA.logsList
32
+ }
33
+ }
34
+ ]);
35
+
36
+ const token = await sdk.auth.login({
37
+ email: 'ivan@zeroclm.com',
38
+ password: 'Pass#Dev2024'
39
+ });
40
+
41
+ expect(token).toBe(AUTH_TEST_DATA.loginToken);
42
+ expect(sdk.auth.isAuthenticated()).toBe(true);
43
+
44
+ expect(global.fetch).toHaveBeenCalledTimes(1);
45
+ const loginHeaders = (global.fetch as any).mock.calls[0][1].headers;
46
+ expect(loginHeaders.get('Authorization')).toMatch(/^Basic /);
47
+
48
+ const logsResponse = await sdk.logs.findLogs({});
49
+
50
+ expect(global.fetch).toHaveBeenCalledTimes(2);
51
+ const logsHeaders = (global.fetch as any).mock.calls[1][1].headers;
52
+ expect(logsHeaders.get('Authorization')).toBe(`Bearer ${token}`);
53
+
54
+ expect(logsResponse).toEqual(LOGS_TEST_DATA.logsList);
55
+ expect(logsResponse.results.length).toBe(2);
56
+ });
57
+
58
+ it('debe manejar el flujo completo con parámetros de consulta', async () => {
59
+ setupFetchMock([
60
+ {
61
+ url: '/auth/login',
62
+ method: 'GET',
63
+ response: {
64
+ status: 200,
65
+ data: AUTH_TEST_DATA.loginToken
66
+ }
67
+ },
68
+ {
69
+ url: '/logs',
70
+ method: 'GET',
71
+ response: {
72
+ status: 200,
73
+ data: LOGS_TEST_DATA.logsFiltered
74
+ }
75
+ }
76
+ ]);
77
+
78
+ await sdk.auth.login({
79
+ email: 'ivan@zeroclm.com',
80
+ password: 'Pass#Dev2024'
81
+ });
82
+
83
+ const logsResponse = await sdk.logs.findLogs({
84
+ level: 'info',
85
+ page: 1,
86
+ limit: 1
87
+ });
88
+
89
+ const calledUrl = (global.fetch as any).mock.calls[1][0];
90
+ expect(calledUrl).toContain('level=info');
91
+ expect(calledUrl).toContain('page=1');
92
+ expect(calledUrl).toContain('limit=1');
93
+
94
+ expect(logsResponse.results[0].level).toBe('info');
95
+ expect(logsResponse.pagination.limit).toBe(1);
96
+ });
97
+
98
+ it('debe mantener la sesión después de autenticar', async () => {
99
+ setupFetchMock([
100
+ {
101
+ url: '/auth/login',
102
+ method: 'GET',
103
+ response: {
104
+ status: 200,
105
+ data: AUTH_TEST_DATA.loginToken
106
+ }
107
+ },
108
+ {
109
+ url: '/logs',
110
+ method: 'GET',
111
+ response: {
112
+ status: 200,
113
+ data: LOGS_TEST_DATA.logsList
114
+ }
115
+ }
116
+ ]);
117
+
118
+ await sdk.auth.login({
119
+ email: 'ivan@zeroclm.com',
120
+ password: 'Pass#Dev2024'
121
+ });
122
+
123
+ await sdk.logs.findLogs({});
124
+
125
+ vi.resetAllMocks();
126
+ setupFetchMock([
127
+ {
128
+ url: '/logs',
129
+ method: 'GET',
130
+ response: {
131
+ status: 200,
132
+ data: LOGS_TEST_DATA.logsList
133
+ }
134
+ }
135
+ ]);
136
+
137
+ await sdk.logs.findLogs({ page: 2 });
138
+
139
+ const headers = (global.fetch as any).mock.calls[0][1].headers;
140
+ expect(headers.get('Authorization')).toBe(`Bearer ${AUTH_TEST_DATA.loginToken}`);
141
+
142
+ const calledUrl = (global.fetch as any).mock.calls[0][0];
143
+ expect(calledUrl).toContain('page=2');
144
+ });
145
+ });
@@ -0,0 +1,189 @@
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('Integración Auth + Users', () => {
6
+ let sdk: ClmSdk;
7
+
8
+ beforeEach(() => {
9
+ // Inicializar SDK sin token (lo obtendremos al hacer login)
10
+ sdk = new ClmSdk({
11
+ baseUrl: 'https://dev-api.zeroclm.io',
12
+ organization: 'default-org'
13
+ });
14
+ });
15
+
16
+ it('debe autenticar y luego obtener usuarios', async () => {
17
+ // 1. Configurar mock para login
18
+ setupFetchMock([
19
+ {
20
+ url: '/auth/login',
21
+ method: 'GET',
22
+ response: {
23
+ status: 200,
24
+ data: AUTH_TEST_DATA.loginToken
25
+ }
26
+ },
27
+ // 2. Configurar mock para obtener usuarios (se usará después del login)
28
+ {
29
+ url: '/users',
30
+ method: 'GET',
31
+ response: {
32
+ status: 200,
33
+ data: USERS_TEST_DATA.usersList
34
+ }
35
+ }
36
+ ]);
37
+
38
+ // 3. Hacer login primero
39
+ const token = await sdk.auth.login({
40
+ email: 'ivan@zeroclm.com',
41
+ password: 'Pass#Dev2024'
42
+ });
43
+
44
+ // Verificar que el login funcionó
45
+ expect(token).toBe(AUTH_TEST_DATA.loginToken);
46
+ expect(sdk.auth.isAuthenticated()).toBe(true);
47
+
48
+ // Verificar que se hizo la petición de login con Basic Auth
49
+ expect(global.fetch).toHaveBeenCalledTimes(1);
50
+ const loginHeaders = (global.fetch as any).mock.calls[0][1].headers;
51
+ expect(loginHeaders.get('Authorization')).toMatch(/^Basic /);
52
+
53
+ // 4. Ahora obtener usuarios (con el token ya establecido)
54
+ const usersResponse = await sdk.users.getAll();
55
+
56
+ // Verificar que se hizo la petición de usuarios con el token Bearer
57
+ expect(global.fetch).toHaveBeenCalledTimes(2);
58
+ const usersHeaders = (global.fetch as any).mock.calls[1][1].headers;
59
+ expect(usersHeaders.get('Authorization')).toBe(`Bearer ${token}`);
60
+
61
+ // Verificar la respuesta de usuarios
62
+ expect(usersResponse).toEqual(USERS_TEST_DATA.usersList);
63
+ expect(usersResponse.data.length).toBe(1);
64
+ });
65
+
66
+ it('debe manejar el flujo completo con parámetros de consulta', async () => {
67
+ // Configurar mocks para ambas peticiones
68
+ setupFetchMock([
69
+ {
70
+ url: '/auth/login',
71
+ method: 'GET',
72
+ response: {
73
+ status: 200,
74
+ data: AUTH_TEST_DATA.loginToken
75
+ }
76
+ },
77
+ {
78
+ url: '/users',
79
+ method: 'GET',
80
+ response: {
81
+ status: 200,
82
+ data: {
83
+ data: [
84
+ {
85
+ id: 'filtered-user-id',
86
+ email: 'ivan@zeroclm.com',
87
+ full_name: 'Iván Test',
88
+ role_id: 1,
89
+ is_deleted: false,
90
+ deleted_at: null,
91
+ is_active: true,
92
+ created_at: '2025-05-01T13:48:57.411Z',
93
+ updated_at: '2025-05-01T13:48:57.411Z',
94
+ phone: null,
95
+ area: null,
96
+ area_id: null,
97
+ organization_id: 1
98
+ }
99
+ ],
100
+ meta: {
101
+ total: 1,
102
+ page: 1,
103
+ limit: 10,
104
+ pages: 1
105
+ }
106
+ }
107
+ }
108
+ }
109
+ ]);
110
+
111
+ // 1. Hacer login
112
+ await sdk.auth.login({
113
+ email: 'ivan@zeroclm.com',
114
+ password: 'Passsssss8080'
115
+ });
116
+
117
+ // 2. Obtener usuarios con filtros
118
+ const usersResponse = await sdk.users.getAll({
119
+ email: 'ivan@zeroclm.com',
120
+ page: 1,
121
+ limit: 10
122
+ });
123
+
124
+ // Verificar que la URL contiene los parámetros de consulta
125
+ const calledUrl = (global.fetch as any).mock.calls[1][0];
126
+ expect(calledUrl).toContain('email=ivan%40zeroclm.com');
127
+ expect(calledUrl).toContain('page=1');
128
+ expect(calledUrl).toContain('limit=10');
129
+
130
+ // Verificar la respuesta filtrada
131
+ expect(usersResponse.data[0].email).toBe('ivan@zeroclm.com');
132
+ expect(usersResponse.meta.limit).toBe(10);
133
+ });
134
+
135
+ it('debe mantener la sesión después de autenticar', async () => {
136
+ // Configurar mocks
137
+ setupFetchMock([
138
+ {
139
+ url: '/auth/login',
140
+ method: 'GET',
141
+ response: {
142
+ status: 200,
143
+ data: AUTH_TEST_DATA.loginToken
144
+ }
145
+ },
146
+ {
147
+ url: '/users',
148
+ method: 'GET',
149
+ response: {
150
+ status: 200,
151
+ data: USERS_TEST_DATA.usersList
152
+ }
153
+ }
154
+ ]);
155
+
156
+ // 1. Hacer login
157
+ await sdk.auth.login({
158
+ email: 'ivan@zeroclm.com',
159
+ password: 'Pass#Dev2024'
160
+ });
161
+
162
+ // 2. Hacer varias peticiones sin volver a autenticar
163
+ await sdk.users.getAll();
164
+
165
+ // Resetear el mock para la siguiente petición
166
+ vi.resetAllMocks();
167
+ setupFetchMock([
168
+ {
169
+ url: '/users',
170
+ method: 'GET',
171
+ response: {
172
+ status: 200,
173
+ data: USERS_TEST_DATA.usersList
174
+ }
175
+ }
176
+ ]);
177
+
178
+ // 3. Hacer otra petición para verificar que el token sigue siendo válido
179
+ await sdk.users.getAll({ page: 2 });
180
+
181
+ // Verificar que el token se mantiene en las peticiones
182
+ const headers = (global.fetch as any).mock.calls[0][1].headers;
183
+ expect(headers.get('Authorization')).toBe(`Bearer ${AUTH_TEST_DATA.loginToken}`);
184
+
185
+ // La URL debería contener el nuevo parámetro de página
186
+ const calledUrl = (global.fetch as any).mock.calls[0][0];
187
+ expect(calledUrl).toContain('page=2');
188
+ });
189
+ });