@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.
- package/.docs/publicacion-npm.md +111 -0
- package/.env.example +14 -0
- package/.gitlab-ci.yml +23 -0
- package/README.md +202 -0
- package/dist/config/config.d.ts +3 -0
- package/dist/config/config.js +21 -0
- package/dist/core/api-client.d.ts +27 -0
- package/dist/core/api-client.js +183 -0
- package/dist/core/api-error.d.ts +15 -0
- package/dist/core/api-error.js +46 -0
- package/dist/core/event-emitter.d.ts +11 -0
- package/dist/core/event-emitter.js +32 -0
- package/dist/index.d.ts +41 -0
- package/dist/index.js +59 -0
- package/dist/modules/legacy/areas/areas-api.d.ts +34 -0
- package/dist/modules/legacy/areas/areas-api.js +44 -0
- package/dist/modules/legacy/areas/types.d.ts +37 -0
- package/dist/modules/legacy/areas/types.js +1 -0
- package/dist/modules/legacy/classificationtypes/classificationtypes-api.d.ts +34 -0
- package/dist/modules/legacy/classificationtypes/classificationtypes-api.js +46 -0
- package/dist/modules/legacy/classificationtypes/types.d.ts +41 -0
- package/dist/modules/legacy/classificationtypes/types.js +1 -0
- package/dist/modules/v1/auth/auth-api.d.ts +17 -0
- package/dist/modules/v1/auth/auth-api.js +63 -0
- package/dist/modules/v1/auth/types.d.ts +18 -0
- package/dist/modules/v1/auth/types.js +1 -0
- package/dist/modules/v1/main/main-api.d.ts +11 -0
- package/dist/modules/v1/main/main-api.js +14 -0
- package/dist/modules/v1/main/types.d.ts +3 -0
- package/dist/modules/v1/main/types.js +1 -0
- package/dist/modules/v1/notifications/notification-api.d.ts +16 -0
- package/dist/modules/v1/notifications/notification-api.js +26 -0
- package/dist/modules/v1/notifications/types.d.ts +53 -0
- package/dist/modules/v1/notifications/types.js +1 -0
- package/dist/modules/v1/users/types.d.ts +64 -0
- package/dist/modules/v1/users/types.js +1 -0
- package/dist/modules/v1/users/users-api.d.ts +81 -0
- package/dist/modules/v1/users/users-api.js +113 -0
- package/dist/types/common.d.ts +18 -0
- package/dist/types/common.js +1 -0
- package/dist/types/sdk.d.ts +42 -0
- package/dist/types/sdk.js +11 -0
- package/dist/utils/cache.d.ts +10 -0
- package/dist/utils/cache.js +43 -0
- package/dist/utils/http.d.ts +5 -0
- package/dist/utils/http.js +56 -0
- package/package.json +38 -0
- package/src/config/config.ts +24 -0
- package/src/core/api-client.ts +272 -0
- package/src/core/api-error.ts +54 -0
- package/src/core/event-emitter.ts +43 -0
- package/src/index.ts +89 -0
- package/src/modules/legacy/areas/areas-api.ts +73 -0
- package/src/modules/legacy/areas/types.ts +49 -0
- package/src/modules/legacy/classificationtypes/classificationtypes-api.ts +80 -0
- package/src/modules/legacy/classificationtypes/types.ts +52 -0
- package/src/modules/v1/auth/auth-api.ts +75 -0
- package/src/modules/v1/auth/types.ts +20 -0
- package/src/modules/v1/main/main-api.ts +20 -0
- package/src/modules/v1/main/types.ts +3 -0
- package/src/modules/v1/notifications/notification-api.ts +55 -0
- package/src/modules/v1/notifications/types.ts +58 -0
- package/src/modules/v1/users/types.ts +83 -0
- package/src/modules/v1/users/users-api.ts +148 -0
- package/src/types/common.ts +22 -0
- package/src/types/sdk.ts +38 -0
- package/src/utils/cache.ts +58 -0
- package/src/utils/http.ts +77 -0
- package/tests/integration/legacy/auth-areas.test.ts +115 -0
- package/tests/integration/legacy/auth-classification-types.test.ts +80 -0
- package/tests/integration/v1/auth-logs.test.ts +145 -0
- package/tests/integration/v1/auth-users.test.ts +189 -0
- package/tests/modules/legacy/areas/areas-api.test.ts +232 -0
- package/tests/modules/legacy/classification-types/classification-types-api.test.ts +100 -0
- package/tests/modules/v1/auth/auth-api.test.ts +134 -0
- package/tests/modules/v1/users/users-api.test.ts +176 -0
- package/tests/setup.ts +12 -0
- package/tests/utils/test-utils.ts +453 -0
- package/tsconfig.json +16 -0
- package/tsconfig.test.json +13 -0
- 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
|
+
});
|