dte-signer-sv
Version:
Sign Digital Tax Documents (DTE) for El Salvador's Ministry of Finance
165 lines • 6.71 kB
JavaScript
import { signDTE } from "../index";
import { readFileSync } from "fs";
import path from "path";
// Use process.cwd() and relative path for Jest compatibility
const tempDir = path.join(process.cwd(), "temp");
describe("signDTE", () => {
const validCertificatePath = path.join(tempDir, "88888888888888.crt");
const validCertificateXML = readFileSync(validCertificatePath, "utf8");
const validPassword = "newpassword123";
const testDTE = {
identificacion: {
version: 1,
ambiente: "00",
tipoDte: "01",
numeroControl: "DTE-01-00000001-000000000000001",
codigoGeneracion: "TEST-CODE-001"
}
};
describe("with certificate file path", () => {
it("should sign DTE successfully with valid inputs", async () => {
const result = await signDTE({
dte: testDTE,
privatePassword: validPassword,
certificate: validCertificatePath,
loadCertificateFromPath: true
});
expect(typeof result).toBe("string");
expect(result.split(".")).toHaveLength(3); // JWT has 3 parts
});
it("should throw error for invalid password", async () => {
await expect(signDTE({
dte: testDTE,
privatePassword: "wrongpassword",
certificate: validCertificatePath,
loadCertificateFromPath: true
})).rejects.toThrow("Invalid private password");
});
it("should throw error for non-existent certificate file", async () => {
await expect(signDTE({
dte: testDTE,
privatePassword: validPassword,
certificate: "./non-existent.crt",
loadCertificateFromPath: true
})).rejects.toThrow();
});
});
describe("with certificate XML content", () => {
it("should sign DTE successfully with valid inputs", async () => {
const result = await signDTE({
dte: testDTE,
privatePassword: validPassword,
certificate: validCertificateXML
});
expect(typeof result).toBe("string");
expect(result.split(".")).toHaveLength(3);
});
it("should work without loadCertificateFromPath flag", async () => {
const result = await signDTE({
dte: testDTE,
privatePassword: validPassword,
certificate: validCertificateXML,
loadCertificateFromPath: false
});
expect(typeof result).toBe("string");
expect(result.split(".")).toHaveLength(3);
});
it("should throw error for invalid XML", async () => {
await expect(signDTE({
dte: testDTE,
privatePassword: validPassword,
certificate: "<invalid>not a certificate</invalid>"
})).rejects.toThrow();
});
});
describe("DTE variations", () => {
it("should sign empty object", async () => {
const result = await signDTE({
dte: {},
privatePassword: validPassword,
certificate: validCertificateXML
});
expect(typeof result).toBe("string");
const parts = result.split(".");
expect(parts).toHaveLength(3);
// Decode payload to verify
const payload = JSON.parse(Buffer.from(parts[1], "base64url").toString());
expect(payload).toEqual({});
});
it("should sign complex nested object", async () => {
const complexDTE = {
nivel1: {
nivel2: {
nivel3: {
datos: ["a", "b", "c"],
numero: 123.45,
booleano: true,
nulo: null
}
}
},
fecha: new Date().toISOString()
};
const result = await signDTE({
dte: complexDTE,
privatePassword: validPassword,
certificate: validCertificateXML
});
expect(typeof result).toBe("string");
const parts = result.split(".");
expect(parts).toHaveLength(3);
});
it("should handle special characters in DTE", async () => {
const specialDTE = {
texto: "Ñoño's café © 2024",
simbolos: "!@#$%^&*()_+-=[]{}|;':\",./<>?"
};
const result = await signDTE({
dte: specialDTE,
privatePassword: validPassword,
certificate: validCertificateXML
});
expect(typeof result).toBe("string");
expect(result.split(".")).toHaveLength(3);
});
});
describe("JWT structure validation", () => {
it("should create valid JWT structure", async () => {
const result = await signDTE({
dte: testDTE,
privatePassword: validPassword,
certificate: validCertificateXML
});
const [header, payload, signature] = result.split(".");
// Validate header
const decodedHeader = JSON.parse(Buffer.from(header, "base64url").toString());
expect(decodedHeader.alg).toBe("RS512");
// Validate payload
const decodedPayload = JSON.parse(Buffer.from(payload, "base64url").toString());
expect(decodedPayload).toEqual(testDTE);
// Validate signature exists and has content
expect(signature).toBeDefined();
expect(signature.length).toBeGreaterThan(0);
});
it("should produce different signatures for different DTEs", async () => {
const dte1 = { id: 1 };
const dte2 = { id: 2 };
const result1 = await signDTE({
dte: dte1,
privatePassword: validPassword,
certificate: validCertificateXML
});
const result2 = await signDTE({
dte: dte2,
privatePassword: validPassword,
certificate: validCertificateXML
});
expect(result1).not.toBe(result2);
// But headers should be the same
const header1 = result1.split(".")[0];
const header2 = result2.split(".")[0];
expect(header1).toBe(header2);
});
});
});
//# sourceMappingURL=signer.test.js.map