apacuana-sdk-core 0.1.0 → 0.3.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 (35) hide show
  1. package/README.md +175 -55
  2. package/coverage/clover.xml +249 -101
  3. package/coverage/coverage-final.json +9 -6
  4. package/coverage/lcov-report/index.html +45 -30
  5. package/coverage/lcov-report/src/api/certs.js.html +117 -81
  6. package/coverage/lcov-report/src/api/index.html +40 -25
  7. package/coverage/lcov-report/src/api/revocations.js.html +1 -1
  8. package/coverage/lcov-report/src/api/signatures.js.html +546 -72
  9. package/coverage/lcov-report/src/api/users.js.html +25 -61
  10. package/coverage/lcov-report/src/config/index.html +21 -21
  11. package/coverage/lcov-report/src/config/index.js.html +65 -32
  12. package/coverage/lcov-report/src/errors/index.html +5 -5
  13. package/coverage/lcov-report/src/errors/index.js.html +13 -10
  14. package/coverage/lcov-report/src/index.html +1 -1
  15. package/coverage/lcov-report/src/index.js.html +13 -4
  16. package/coverage/lcov-report/src/utils/constant.js.html +4 -4
  17. package/coverage/lcov-report/src/utils/helpers.js.html +290 -116
  18. package/coverage/lcov-report/src/utils/httpClient.js.html +646 -0
  19. package/coverage/lcov-report/src/utils/index.html +30 -15
  20. package/coverage/lcov.info +515 -181
  21. package/dist/index.js +323 -189
  22. package/dist/index.js.map +1 -1
  23. package/dist/index.mjs +323 -189
  24. package/dist/index.mjs.map +1 -1
  25. package/package.json +1 -1
  26. package/src/api/certs.js +49 -37
  27. package/src/api/signatures.js +213 -49
  28. package/src/api/users.js +17 -29
  29. package/src/errors/index.js +1 -0
  30. package/src/index.js +4 -1
  31. package/src/utils/helpers.js +156 -98
  32. package/tests/api/certs.test.js +111 -5
  33. package/tests/api/signatures.test.js +152 -4
  34. package/tests/api/users.test.js +11 -10
  35. package/src/auth/index.js +0 -24
@@ -1,9 +1,10 @@
1
- import * as pkijs from "pkijs";
2
- import * as asn1js from "asn1js";
1
+ import CryptoJS from "crypto-js";
3
2
  import { getCrypto } from "pkijs";
4
3
  import { STATUS_CERTIFICATE, VERIFICATION_STATUS } from "./constant";
5
4
  import { ApacuanaAPIError } from "../errors/index";
6
5
 
6
+ const KEY_HEX = "dRgUkXp2s5v8y/B?";
7
+
7
8
  const getCertificateStatus = (userData, certificateInDevice) => {
8
9
  if (!userData) {
9
10
  throw new Error("El parámetro userData es requerido.");
@@ -41,126 +42,183 @@ const getCertificateStatus = (userData, certificateInDevice) => {
41
42
  return STATUS_CERTIFICATE.revoque;
42
43
  };
43
44
 
44
- const generateKeyPair = async (
45
- signAlg = "RSASSA-PKCS1-v1_5",
46
- hashAlg = "SHA-256"
47
- ) => {
48
- try {
49
- const crypto = getCrypto();
50
- if (!crypto) {
51
- throw new Error("API criptográfica no disponible a través de pkijs.");
52
- }
45
+ // Función para convertir ArrayBuffer a Base64
46
+ const arrayBufferToBase64 = (buffer) => {
47
+ const binary = String.fromCharCode.apply(null, new Uint8Array(buffer));
48
+ return window.btoa(binary);
49
+ };
53
50
 
54
- const keyPair = await crypto.generateKey(
55
- {
56
- name: signAlg,
57
- modulusLength: 2048,
58
- publicExponent: new Uint8Array([1, 0, 1]),
59
- hash: { name: hashAlg },
60
- },
61
- true,
62
- ["sign", "verify"]
51
+ async function exportPrivateKey(key) {
52
+ const crypto = getCrypto();
53
+ const exported = await crypto.exportKey("pkcs8", key);
54
+ const exportablePrivateKey = new Uint8Array(exported);
55
+ return arrayBufferToBase64(exportablePrivateKey);
56
+ }
57
+
58
+ const encryptedCsr = (csr, encryptKey = KEY_HEX) => {
59
+ const body = {
60
+ csr: CryptoJS.AES.encrypt(csr, encryptKey, {
61
+ mode: CryptoJS.mode.ECB,
62
+ padding: CryptoJS.pad.Pkcs7,
63
+ }).toString(),
64
+ };
65
+ return body;
66
+ };
67
+
68
+ const validateOnBoardingSignerData = (signerData) => {
69
+ if (!signerData.name || typeof signerData.name !== "string") {
70
+ throw new ApacuanaAPIError(
71
+ 'El campo "name" es requerido y debe ser una cadena de texto.',
72
+ 400,
73
+ "INVALID_PARAMETER_FORMAT"
63
74
  );
75
+ }
64
76
 
65
- return keyPair;
66
- } catch (error) {
67
- console.error("Error durante la generación del par de claves:", error);
77
+ if (!signerData.reference || typeof signerData.reference !== "string") {
68
78
  throw new ApacuanaAPIError(
69
- `Fallo en la generación del par de claves: ${error.message}`,
70
- 0,
71
- "KEY_PAIR_GENERATION_ERROR"
79
+ 'El campo "reference" es requerido y debe ser una cadena de texto.',
80
+ 400,
81
+ "INVALID_PARAMETER_FORMAT"
82
+ );
83
+ }
84
+
85
+ const validDocTypes = ["V", "P", "E"];
86
+ if (!signerData.typedoc || !validDocTypes.includes(signerData.typedoc)) {
87
+ throw new ApacuanaAPIError(
88
+ 'El campo "typedoc" es requerido y debe ser uno de los siguientes valores: V, P, E.',
89
+ 400,
90
+ "INVALID_PARAMETER_FORMAT"
91
+ );
92
+ }
93
+
94
+ if (!signerData.doc || typeof signerData.doc !== "string") {
95
+ throw new ApacuanaAPIError(
96
+ 'El campo "doc" es requerido y debe ser una cadena de texto.',
97
+ 400,
98
+ "INVALID_PARAMETER_FORMAT"
72
99
  );
73
100
  }
74
- };
75
101
 
76
- const generateCSR = async (keyPair, userData, hashAlg = "SHA-256") => {
77
- try {
78
- const { publicKey, privateKey } = keyPair;
102
+ if (
103
+ !Array.isArray(signerData.signature) ||
104
+ signerData.signature.length === 0
105
+ ) {
106
+ throw new ApacuanaAPIError(
107
+ 'El campo "signature" es requerido y debe ser un array no vacío.',
108
+ 400,
109
+ "INVALID_PARAMETER_FORMAT"
110
+ );
111
+ }
79
112
 
80
- // 1. Validar los parámetros de entrada.
81
- if (!keyPair || !publicKey || !privateKey) {
82
- throw new Error(
83
- "Par de claves no válido. Es necesario un CryptoKeyPair."
113
+ signerData.signature.forEach((sig, index) => {
114
+ if (!Number.isInteger(sig.page) || sig.page < 1) {
115
+ throw new ApacuanaAPIError(
116
+ `La página de la firma ${
117
+ index + 1
118
+ } debe ser un número entero positivo.`,
119
+ 400,
120
+ "INVALID_PARAMETER_FORMAT"
84
121
  );
85
122
  }
86
- if (!userData || !userData.email) {
87
- throw new Error(
88
- "Datos de usuario incompletos. El correo electrónico es requerido."
123
+
124
+ if (typeof sig.x !== "number" || sig.x < 0 || sig.x > 1) {
125
+ throw new ApacuanaAPIError(
126
+ `La coordenada X de la firma ${
127
+ index + 1
128
+ } debe ser un número entre 0 y 1.`,
129
+ 400,
130
+ "INVALID_PARAMETER_FORMAT"
89
131
  );
90
132
  }
91
133
 
92
- const pkcs10 = new pkijs.CertificationRequest();
93
- pkcs10.version = 0;
94
-
95
- const subjectAttributes = [
96
- {
97
- type: "2.5.4.6", // OID para 'Country' (C)
98
- value: new asn1js.PrintableString({ value: userData.country || "VE" }),
99
- },
100
- {
101
- type: "2.5.4.3", // OID para 'Common Name' (CN)
102
- value: new asn1js.Utf8String({ value: userData.commonName || "N/A" }),
103
- },
104
- {
105
- type: "1.2.840.113549.1.9.1", // OID para 'E-mail' (E)
106
- value: new asn1js.IA5String({ value: userData.email }),
107
- },
108
- // Puedes agregar más atributos aquí, como:
109
- // {
110
- // type: '2.5.4.8', // OID para 'State or Province' (ST)
111
- // value: new asn1js.Utf8String({ value: userData.state || 'N/A' }),
112
- // },
113
- // {
114
- // type: '2.5.4.7', // OID para 'Locality' (L)
115
- // value: new asn1js.Utf8String({ value: userData.locality || 'N/A' }),
116
- // },
117
- ].filter((attr) => attr.value.value); // Filtra atributos sin valor.
118
-
119
- // 3. Asignar los atributos al Subject.
120
- subjectAttributes.forEach((attr) =>
121
- pkcs10.subject.typesAndValues.push(new pkijs.AttributeTypeAndValue(attr))
134
+ if (typeof sig.y !== "number" || sig.y < 0 || sig.y > 1) {
135
+ throw new ApacuanaAPIError(
136
+ `La coordenada Y de la firma ${
137
+ index + 1
138
+ } debe ser un número entre 0 y 1.`,
139
+ 400,
140
+ "INVALID_PARAMETER_FORMAT"
141
+ );
142
+ }
143
+ });
144
+ };
145
+
146
+ const validateCsr = (csr) => {
147
+ const base64Regex = /^[A-Za-z0-9+/=]+$/;
148
+
149
+ if (!csr) {
150
+ throw new ApacuanaAPIError(
151
+ "La solicitud de firma de certificado (CSR) es requerida.",
152
+ 400,
153
+ "INVALID_PARAMETER"
154
+ );
155
+ }
156
+
157
+ if (typeof csr !== "string" || csr.trim() === "") {
158
+ throw new ApacuanaAPIError(
159
+ "El CSR debe ser una cadena de texto válida y no puede estar vacía.",
160
+ 400,
161
+ "INVALID_PARAMETER_FORMAT"
122
162
  );
163
+ }
123
164
 
124
- // 4. Importar la clave pública y firmar la solicitud con la clave privada.
125
- await pkcs10.subjectPublicKeyInfo.importKey(publicKey);
126
- await pkcs10.sign(privateKey, hashAlg);
127
-
128
- // 5. Codificar el CSR para el envío.
129
- const csr = pkcs10.toSchema().toBER(false);
130
-
131
- // 6. Retornar el CSR y el Subject.
132
- return {
133
- csr,
134
- subject: pkcs10.subject,
135
- };
136
- } catch (error) {
137
- // Manejo de errores centralizado.
138
- console.error("Error durante la generación del CSR:", error);
165
+ if (!base64Regex.test(csr)) {
139
166
  throw new ApacuanaAPIError(
140
- `Fallo en la generación del CSR: ${error.message}`,
141
- 0,
142
- "CSR_GENERATION_ERROR"
167
+ "El CSR debe estar codificado en formato base64 válido.",
168
+ 400,
169
+ "INVALID_PARAMETER_FORMAT"
143
170
  );
144
171
  }
145
172
  };
146
173
 
147
- // Función para convertir ArrayBuffer a Base64
148
- const arrayBufferToBase64 = (buffer) => {
149
- const binary = String.fromCharCode.apply(null, new Uint8Array(buffer));
150
- return window.btoa(binary);
174
+ const validateGetDocsData = (data) => {
175
+ if (
176
+ !data ||
177
+ typeof data.page === "undefined" ||
178
+ typeof data.size === "undefined"
179
+ ) {
180
+ throw new ApacuanaAPIError(
181
+ "Los parámetros 'page' y 'size' son requeridos.",
182
+ 400,
183
+ "INVALID_PARAMETER"
184
+ );
185
+ }
186
+
187
+ if (
188
+ typeof data.status !== "undefined" &&
189
+ ![-1, 0, 1, 2].includes(data.status)
190
+ ) {
191
+ throw new ApacuanaAPIError(
192
+ "El parámetro 'status' no es válido. Los valores permitidos son: -1, 0, 1, 2.",
193
+ 400,
194
+ "INVALID_PARAMETER"
195
+ );
196
+ }
151
197
  };
152
198
 
153
- export async function exportPrivateKey(key) {
154
- const crypto = getCrypto();
155
- const exported = await crypto.exportKey("pkcs8", key);
156
- const exportablePrivateKey = new Uint8Array(exported);
157
- return arrayBufferToBase64(exportablePrivateKey);
158
- }
199
+ const validateGetDigestData = (signData) => {
200
+ if (
201
+ !signData ||
202
+ !signData.cert ||
203
+ typeof signData.cert !== "string" ||
204
+ !signData.signatureId ||
205
+ typeof signData.signatureId !== "string"
206
+ ) {
207
+ throw new ApacuanaAPIError(
208
+ "Los parámetros 'cert' y 'signatureId' son requeridos y deben ser cadenas de texto.",
209
+ 400,
210
+ "INVALID_PARAMETER"
211
+ );
212
+ }
213
+ };
159
214
 
160
- export const helpers = {
215
+ export default {
161
216
  getCertificateStatus,
162
- generateKeyPair,
163
- generateCSR,
164
217
  exportPrivateKey,
165
218
  arrayBufferToBase64,
219
+ encryptedCsr,
220
+ validateOnBoardingSignerData,
221
+ validateCsr,
222
+ validateGetDocsData,
223
+ validateGetDigestData,
166
224
  };
@@ -1,6 +1,112 @@
1
- // tests/api/certs.test.js
2
- describe('Certificates API', () => {
3
- test('placeholder test', () => {
4
- expect(true).toBe(true);
1
+ import { getConfig } from "../../src/config/index";
2
+ import { httpRequest } from "../../src/utils/httpClient";
3
+ import helpers from "../../src/utils/helpers";
4
+ import { ApacuanaAPIError } from "../../src/errors";
5
+ import { generateCert, getCertStatus } from "../../src/api/certs";
6
+ import { INTEGRATION_TYPE } from "../../src/utils/constant";
7
+
8
+ jest.mock("../../src/config/index");
9
+ jest.mock("../../src/utils/httpClient");
10
+ jest.mock("../../src/utils/helpers");
11
+
12
+ describe("Certificate API - certs.js", () => {
13
+ beforeEach(() => {
14
+ jest.clearAllMocks();
5
15
  });
6
- });
16
+
17
+ describe("generateCert", () => {
18
+ it("should generate a certificate for ONBOARDING integration", async () => {
19
+ getConfig.mockReturnValue({
20
+ integrationType: INTEGRATION_TYPE.ONBOARDING,
21
+ });
22
+ helpers.encryptedCsr.mockReturnValue("encrypted-csr");
23
+ httpRequest.mockResolvedValue({ cert: "new-cert", success: true });
24
+
25
+ const result = await generateCert("some-csr");
26
+
27
+ expect(helpers.validateCsr).toHaveBeenCalledWith("some-csr");
28
+ expect(helpers.encryptedCsr).toHaveBeenCalledWith("some-csr");
29
+ expect(httpRequest).toHaveBeenCalledWith(
30
+ "services/api/register/certificate",
31
+ "encrypted-csr",
32
+ "POST"
33
+ );
34
+ expect(result).toEqual({ cert: "new-cert", success: true });
35
+ });
36
+
37
+ it("should throw an error if API response does not contain a certificate", async () => {
38
+ getConfig.mockReturnValue({
39
+ integrationType: INTEGRATION_TYPE.ONBOARDING,
40
+ });
41
+ helpers.encryptedCsr.mockReturnValue("encrypted-csr");
42
+ httpRequest.mockResolvedValue({ success: false });
43
+
44
+ await expect(generateCert("some-csr")).rejects.toThrow(
45
+ new ApacuanaAPIError(
46
+ "The API response does not contain the certificate.",
47
+ undefined,
48
+ "INVALID_API_RESPONSE"
49
+ )
50
+ );
51
+ });
52
+
53
+ it("should throw an error for ONPREMISE", async () => {
54
+ getConfig.mockReturnValue({
55
+ integrationType: INTEGRATION_TYPE.ONPREMISE,
56
+ });
57
+
58
+ await expect(generateCert("some-csr")).rejects.toThrow(
59
+ new ApacuanaAPIError(
60
+ "Certificate generation is not supported for integration type: ONPREMISE",
61
+ 501,
62
+ "NOT_IMPLEMENTED"
63
+ )
64
+ );
65
+ });
66
+
67
+ it("should throw an error if integration type is not supported", async () => {
68
+ getConfig.mockReturnValue({ integrationType: "INVALID_TYPE" });
69
+ await expect(generateCert("some-csr")).rejects.toThrow(
70
+ "Unsupported integration type: INVALID_TYPE"
71
+ );
72
+ });
73
+
74
+ it("should re-throw ApacuanaAPIError if httpRequest fails", async () => {
75
+ getConfig.mockReturnValue({
76
+ integrationType: INTEGRATION_TYPE.ONBOARDING,
77
+ });
78
+ const apiError = new ApacuanaAPIError("API Error", 500, "API_ERROR");
79
+ httpRequest.mockRejectedValue(apiError);
80
+
81
+ await expect(generateCert("some-csr")).rejects.toThrow(apiError);
82
+ });
83
+
84
+ it("should throw a generic error if something else fails", async () => {
85
+ getConfig.mockReturnValue({
86
+ integrationType: INTEGRATION_TYPE.ONBOARDING,
87
+ });
88
+ const genericError = new Error("Something failed");
89
+ httpRequest.mockRejectedValue(genericError);
90
+
91
+ await expect(generateCert("some-csr")).rejects.toThrow(
92
+ "Certificate generation failed: Something failed"
93
+ );
94
+ });
95
+ });
96
+
97
+ describe("getCertStatus", () => {
98
+ it("should return the certificate status from helpers", () => {
99
+ const mockUserData = { certStatus: "VALID" };
100
+ getConfig.mockReturnValue({ userData: mockUserData });
101
+ helpers.getCertificateStatus.mockReturnValue("VALID");
102
+
103
+ const result = getCertStatus(true);
104
+
105
+ expect(helpers.getCertificateStatus).toHaveBeenCalledWith(
106
+ mockUserData,
107
+ true
108
+ );
109
+ expect(result).toEqual({ status: "VALID", success: true });
110
+ });
111
+ });
112
+ });
@@ -1,6 +1,154 @@
1
- // tests/api/signatures.test.js
2
- describe('Signatures API', () => {
3
- test('placeholder test', () => {
4
- expect(true).toBe(true);
1
+ import { getConfig } from "../../src/config";
2
+ import { httpRequest } from "../../src/utils/httpClient";
3
+ import { ApacuanaAPIError } from "../../src/errors";
4
+ import { addSigner, getDocs, getDigest, signDocument } from "../../src/api/signatures";
5
+ import helpers from "../../src/utils/helpers";
6
+ import { INTEGRATION_TYPE } from "../../src/utils/constant";
7
+
8
+ jest.mock("../../src/config");
9
+ jest.mock("../../src/utils/httpClient");
10
+ jest.mock("../../src/utils/helpers", () => ({
11
+ validateOnBoardingSignerData: jest.fn(),
12
+ validateSignOnBoardingData: jest.fn(),
13
+ validateSignOnPremiseData: jest.fn(),
14
+ signDigest: jest.fn(),
15
+ validateGetDocsData: jest.fn(),
16
+ validateGetDigestData: jest.fn(),
17
+ }));
18
+
19
+ describe("API - Signatures", () => {
20
+ afterEach(() => {
21
+ jest.resetAllMocks();
22
+ });
23
+
24
+ describe("addSigner", () => {
25
+ it("should call addSignerOnBoarding for ONBOARDING integration", async () => {
26
+ getConfig.mockReturnValue({ integrationType: INTEGRATION_TYPE.ONBOARDING });
27
+ const signerData = { typedoc: "V", doc: "12345678" };
28
+ httpRequest.mockResolvedValue({ success: true });
29
+
30
+ await addSigner(signerData);
31
+
32
+ expect(helpers.validateOnBoardingSignerData).toHaveBeenCalledWith(
33
+ signerData
34
+ );
35
+ expect(httpRequest).toHaveBeenCalledWith(
36
+ "services/api/documents/signing",
37
+ signerData,
38
+ "POST"
39
+ );
40
+ });
41
+
42
+ it("should throw not implemented for ONPREMISE integration", async () => {
43
+ getConfig.mockReturnValue({
44
+ integrationType: INTEGRATION_TYPE.ONPREMISE,
45
+ });
46
+ const signerData = { name: "John Doe" };
47
+
48
+ await expect(addSigner(signerData)).rejects.toThrow(
49
+ new ApacuanaAPIError(
50
+ "Adding signers is not supported for integration type: ONPREMISE",
51
+ 501,
52
+ "NOT_IMPLEMENTED"
53
+ )
54
+ );
55
+ });
56
+
57
+ it("should throw an error for unsupported integration type", async () => {
58
+ getConfig.mockReturnValue({ integrationType: "UNSUPPORTED" });
59
+ const signerData = { name: "test" };
60
+ await expect(addSigner(signerData)).rejects.toThrow(
61
+ new ApacuanaAPIError(
62
+ "Unsupported integration type: UNSUPPORTED",
63
+ 400,
64
+ "UNSUPPORTED_INTEGRATION_TYPE"
65
+ )
66
+ );
67
+ });
68
+
69
+ it("should throw an error if signerData is invalid", async () => {
70
+ await expect(addSigner(null)).rejects.toThrow(
71
+ new ApacuanaAPIError(
72
+ "Signer data (signerData) is required and must be a non-empty object.",
73
+ 400,
74
+ "INVALID_PARAMETER"
75
+ )
76
+ );
77
+ });
78
+ });
79
+
80
+ describe("getDocs", () => {
81
+ const paginationData = { page: 1, size: 10 };
82
+
83
+ it("should call httpRequest with correct URL when status is provided", async () => {
84
+ getConfig.mockReturnValue({
85
+ integrationType: INTEGRATION_TYPE.ONBOARDING,
86
+ customerId: "test-customer",
87
+ });
88
+ const dataWithStatus = { ...paginationData, status: 1 };
89
+ httpRequest.mockResolvedValue({ docs: [] });
90
+
91
+ await getDocs(dataWithStatus);
92
+
93
+ expect(helpers.validateGetDocsData).toHaveBeenCalledWith(dataWithStatus);
94
+ expect(httpRequest).toHaveBeenCalledWith(
95
+ "services/api/documents/listcustomer?page=1&customerid=test-customer&size=10&status=1",
96
+ {},
97
+ "GET"
98
+ );
99
+ });
100
+
101
+ it("should throw an error for ONPREMISE integration", async () => {
102
+ getConfig.mockReturnValue({
103
+ integrationType: INTEGRATION_TYPE.ONPREMISE,
104
+ customerId: "test-customer",
105
+ });
106
+ httpRequest.mockResolvedValue({ docs: [] });
107
+
108
+ await expect(getDocs(paginationData)).rejects.toThrow(
109
+ "Document retrieval is not supported for integration type: ONBOARDING"
110
+ );
111
+ });
112
+
113
+ it("should throw an error if page or size are missing", async () => {
114
+ const validationError = new ApacuanaAPIError(
115
+ "Parameters 'page' and 'size' are required.",
116
+ 400,
117
+ "INVALID_PARAMETER"
118
+ );
119
+ helpers.validateGetDocsData.mockImplementation(() => {
120
+ throw validationError;
121
+ });
122
+
123
+ await expect(getDocs({})).rejects.toThrow(validationError);
124
+ });
125
+
126
+ it("should throw an error if status is invalid", async () => {
127
+ const validationError = new ApacuanaAPIError(
128
+ "Parameter 'status' is not valid.",
129
+ 400,
130
+ "INVALID_PARAMETER"
131
+ );
132
+ helpers.validateGetDocsData.mockImplementationOnce(() => {
133
+ throw validationError;
134
+ });
135
+
136
+ await expect(getDocs({ ...paginationData, status: 99 })).rejects.toThrow(
137
+ validationError
138
+ );
139
+ });
140
+
141
+ it("should throw an error for non-on-premise integrations when customerId is missing", async () => {
142
+ getConfig.mockReturnValue({
143
+ integrationType: INTEGRATION_TYPE.ONBOARDING,
144
+ });
145
+ await expect(getDocs(paginationData)).rejects.toThrow(
146
+ new ApacuanaAPIError(
147
+ "'customerId' is not configured. Please configure the SDK.",
148
+ 400,
149
+ "CONFIGURATION_ERROR"
150
+ )
151
+ );
152
+ });
5
153
  });
6
154
  });
@@ -57,6 +57,7 @@ describe("getCustomer", () => {
57
57
  );
58
58
 
59
59
  expect(result).toEqual({
60
+ success: true,
60
61
  token: "mock-session-id-12345",
61
62
  userData: {
62
63
  userId: "mock-user",
@@ -66,34 +67,34 @@ describe("getCustomer", () => {
66
67
  });
67
68
  });
68
69
 
69
- test("debe lanzar un error si la configuración está incompleta", async () => {
70
+ test("should throw an error if configuration is incomplete", async () => {
70
71
  getConfig.mockReturnValue({
71
72
  verificationId: "mock-verify-id",
72
73
  customerId: null,
73
74
  });
74
75
 
75
76
  await expect(getCustomer()).rejects.toThrow(
76
- "El 'body' con 'userId' es un parámetro requerido para getCustomer."
77
+ "Both 'verificationId' and 'customerId' must be configured."
77
78
  );
78
79
  expect(httpRequest).not.toHaveBeenCalled();
79
80
  });
80
81
 
81
- test("debe lanzar un ApacuanaAPIError si el backend no devuelve sessionid o entry", async () => {
82
+ test("should throw ApacuanaAPIError if backend does not return sessionid or entry", async () => {
82
83
  httpRequest.mockResolvedValueOnce({
83
84
  success: true,
84
- message: "Error en el backend, no se pudo generar el token.",
85
+ message: "Backend error, token could not be generated.",
85
86
  entry: { userId: "mock-user" },
86
87
  });
87
88
 
88
89
  await expect(getCustomer()).rejects.toThrow(
89
- "La respuesta de la API no contiene el usuario."
90
+ "The API response does not contain the user."
90
91
  );
91
92
  expect(httpRequest).toHaveBeenCalledTimes(1);
92
93
  });
93
94
 
94
- test("debe relanzar el ApacuanaAPIError si la petición falla en el httpClient", async () => {
95
+ test("should rethrow ApacuanaAPIError if httpClient request fails", async () => {
95
96
  const apiError = new ApacuanaAPIError(
96
- "Error de autenticación",
97
+ "Authentication error",
97
98
  401,
98
99
  "AUTH_ERROR"
99
100
  );
@@ -103,12 +104,12 @@ describe("getCustomer", () => {
103
104
  expect(httpRequest).toHaveBeenCalledTimes(1);
104
105
  });
105
106
 
106
- test("debe lanzar un ApacuanaAPIError si ocurre un error inesperado", async () => {
107
- const genericError = new Error("Error de red o desconocido");
107
+ test("should throw ApacuanaAPIError on unexpected error", async () => {
108
+ const genericError = new Error("Network or unknown error");
108
109
  httpRequest.mockRejectedValueOnce(genericError);
109
110
 
110
111
  await expect(getCustomer()).rejects.toThrow(
111
- "Fallo inesperado al obtener el token: Error de red o desconocido"
112
+ "Unexpected failure getting token: Network or unknown error"
112
113
  );
113
114
  expect(httpRequest).toHaveBeenCalledTimes(1);
114
115
  });
package/src/auth/index.js DELETED
@@ -1,24 +0,0 @@
1
- // src/auth/index.js
2
- // Si httpClient construye los headers directamente de la config,
3
- // este módulo se vuelve menos crítico o puede desaparecer por ahora.
4
- // Lo dejaremos por si hay lógica de auth más compleja después.
5
- import { getConfig } from '../config/index';
6
-
7
- export const getAuthHeaders = () => {
8
- const { secretKey, apiKey } = getConfig();
9
- if (!secretKey || !apiKey) {
10
- throw new Error('Apacuana SDK: secretKey o apiKey no configuradas.');
11
- }
12
- return {
13
- 'X-Api-Key': apiKey,
14
- Authorization: `Bearer ${secretKey}`,
15
- };
16
- };
17
-
18
- export const getVerificationId = () => {
19
- const { verificationId } = getConfig();
20
- if (!verificationId) {
21
- throw new Error('Apacuana SDK: verificationId no configurado.');
22
- }
23
- return verificationId;
24
- };