@ledgerhq/coin-canton
Version:
160 lines (138 loc) • 5.53 kB
text/typescript
import {
createMockCantonSigner,
createMockOnboardingPrepareResponse,
createMockPrepareTransferResponse,
} from "../../test/fixtures";
import type { CantonSigner } from "../../types/signer";
import { signTransaction } from "./sign";
const createMockSigner = (signTransactionImpl: CantonSigner["signTransaction"]): CantonSigner => {
const signer = createMockCantonSigner();
return {
getAddress: signer.getAddress.bind(signer),
signTransaction: signTransactionImpl,
};
};
describe("signTransaction", () => {
const mockSigner = createMockSigner(
jest.fn().mockResolvedValue({
signature: "a1b2c3d4e5f678901234567890abcdef1234567890abcdef1234567890abcdef12",
applicationSignature: "fedcba0987654321fedcba0987654321fedcba0987654321fedcba0987654321",
}),
);
const mockDerivationPath = "44'/6767'/0'/0'/0'";
it("should sign prepared transaction", async () => {
// GIVEN
const mockPrepareTransferResponse = createMockPrepareTransferResponse();
// WHEN
const result = await signTransaction(
mockSigner,
mockDerivationPath,
mockPrepareTransferResponse,
);
// THEN
expect(result.signature).toMatch(/^[0-9a-f]+$/i);
expect(result.signature.length).toBeGreaterThan(0);
});
it("should sign untyped versioned message", async () => {
// GIVEN
const mockOnboardingPrepare = createMockOnboardingPrepareResponse();
// WHEN
const result = await signTransaction(mockSigner, mockDerivationPath, mockOnboardingPrepare);
// THEN
expect(result.signature).toMatch(/^[0-9a-f]+$/i);
expect(result.applicationSignature).toMatch(/^[0-9a-f]+$/i);
});
it("should handle empty signature from signer", async () => {
// GIVEN
const mockSignerSpy = jest.fn().mockResolvedValue({ signature: "" });
const mockSignerWithEmptySignature = createMockSigner(mockSignerSpy);
const mockPrepareTransferResponse = createMockPrepareTransferResponse();
// WHEN & THEN
await expect(
signTransaction(
mockSignerWithEmptySignature,
mockDerivationPath,
mockPrepareTransferResponse,
),
).rejects.toThrow("Device returned empty signature");
});
it("should handle signer errors", async () => {
// GIVEN
const mockSignerSpy = jest.fn().mockRejectedValue(new Error("Signer error"));
const mockSignerWithError = createMockSigner(mockSignerSpy);
const mockPrepareTransferResponse = createMockPrepareTransferResponse();
// WHEN & THEN
await expect(
signTransaction(mockSignerWithError, mockDerivationPath, mockPrepareTransferResponse),
).rejects.toThrow("Signer error");
});
it("should call signer with correct parameters for prepared transaction", async () => {
// GIVEN
const mockSignerSpy = jest.fn().mockResolvedValue({
signature: "a1b2c3d4e5f678901234567890abcdef1234567890abcdef1234567890abcdef12",
});
const mockSignerWithSpy = createMockSigner(mockSignerSpy);
const mockPrepareTransferResponse = createMockPrepareTransferResponse();
// WHEN
await signTransaction(mockSignerWithSpy, mockDerivationPath, mockPrepareTransferResponse);
// THEN
expect(mockSignerSpy).toHaveBeenCalledWith(
mockDerivationPath,
expect.objectContaining({
damlTransaction: expect.any(Uint8Array),
nodes: expect.any(Array),
metadata: expect.any(Uint8Array),
inputContracts: expect.any(Array),
}),
);
});
it("should call signer with correct parameters for untyped versioned message without challenge", async () => {
// GIVEN
const mockSignerSpy = jest.fn().mockResolvedValue({
signature: "a1b2c3d4e5f678901234567890abcdef1234567890abcdef1234567890abcdef12",
});
const mockSignerWithSpy = createMockSigner(mockSignerSpy);
const mockOnboardingPrepareResponse = createMockOnboardingPrepareResponse({
challenge_nonce: "",
challenge_deadline: 0,
});
// WHEN
await signTransaction(mockSignerWithSpy, mockDerivationPath, mockOnboardingPrepareResponse);
// THEN
expect(mockSignerSpy).toHaveBeenCalledWith(mockDerivationPath, {
transactions: [
"namespace-transaction-data",
"party-to-key-transaction-data",
"party-to-participant-transaction-data",
],
});
});
it("should call signer with correct parameters for untyped versioned message with challenge", async () => {
// GIVEN
const mockSignerSpy = jest.fn().mockResolvedValue({
signature: "a1b2c3d4e5f678901234567890abcdef1234567890abcdef1234567890abcdef12",
applicationSignature: "fedcba0987654321fedcba0987654321fedcba0987654321fedcba0987654321",
});
const mockSignerWithSpy = createMockSigner(mockSignerSpy);
const mockOnboardingPrepareResponse = createMockOnboardingPrepareResponse();
// WHEN
const result = await signTransaction(
mockSignerWithSpy,
mockDerivationPath,
mockOnboardingPrepareResponse,
);
// THEN
expect(result).toEqual({
signature: "a1b2c3d4e5f678901234567890abcdef1234567890abcdef1234567890abcdef12",
applicationSignature: "fedcba0987654321fedcba0987654321fedcba0987654321fedcba0987654321",
});
expect(mockSignerSpy).toHaveBeenCalledWith(mockDerivationPath, {
transactions: [
"namespace-transaction-data",
"party-to-key-transaction-data",
"party-to-participant-transaction-data",
],
challenge: "181234567890abcdef000000006774857f",
});
});
});