@ledgerhq/coin-canton
Version:
171 lines (151 loc) • 5.12 kB
text/typescript
import prepareTransferMock from "@ledgerhq/hw-app-canton/tests/fixtures/prepare-transfer.json";
import { craftTransaction } from "../common-logic";
import {
createMockCantonAccount,
createMockCantonSigner,
createMockSignerContext,
createMockTransaction,
} from "../test/fixtures";
import { Transaction } from "../types";
import { buildSignOperation } from "./signOperation";
jest.mock("../common-logic", () => {
const actual = jest.requireActual("../common-logic");
return {
...actual,
craftTransaction: jest.fn(),
};
});
const {
json: { transaction, metadata },
} = prepareTransferMock;
const mockCraftTransaction = craftTransaction as jest.MockedFunction<typeof craftTransaction>;
describe("buildSignOperation", () => {
beforeEach(() => {
jest.clearAllMocks();
mockCraftTransaction.mockReset();
});
const mockAccount = createMockCantonAccount();
const mockTransaction = createMockTransaction();
const mockDeviceId = "test-device-id";
it("should use default expireInSeconds (1 day) when not provided in transaction", async () => {
// GIVEN
const mockSigner = createMockCantonSigner();
const mockSignerContext = jest.fn().mockImplementation(async (_deviceId, callback) => {
return await callback(mockSigner);
});
mockCraftTransaction.mockResolvedValue({
nativeTransaction: prepareTransferMock.json,
serializedTransaction: prepareTransferMock.serialized,
hash: prepareTransferMock.hash,
});
const signOperation = buildSignOperation(mockSignerContext);
const transactionWithoutExpiry: Transaction = { ...mockTransaction };
delete transactionWithoutExpiry.expireInSeconds;
// WHEN
await new Promise((resolve, reject) => {
signOperation({
account: mockAccount,
deviceId: mockDeviceId,
transaction: transactionWithoutExpiry,
}).subscribe({
next: value => {
if (value.type === "signed") {
resolve(value);
}
},
error: reject,
});
});
// THEN
expect(mockCraftTransaction).toHaveBeenCalledWith(
mockAccount.currency,
expect.any(Object),
expect.objectContaining({
expireInSeconds: 24 * 60 * 60, // Default 1 day
}),
);
});
it("should use custom expireInSeconds when provided in transaction", async () => {
// GIVEN
const mockSigner = createMockCantonSigner();
const mockSignerContext = jest.fn().mockImplementation(async (_deviceId, callback) => {
return await callback(mockSigner);
});
mockCraftTransaction.mockResolvedValue({
nativeTransaction: prepareTransferMock.json,
serializedTransaction: prepareTransferMock.serialized,
hash: prepareTransferMock.hash,
});
const signOperation = buildSignOperation(mockSignerContext);
const customExpireSeconds = 3 * 60 * 60; // 3 hours
const transactionWithExpiry: Transaction = {
...mockTransaction,
expireInSeconds: customExpireSeconds,
};
// WHEN
await new Promise((resolve, reject) => {
signOperation({
account: mockAccount,
deviceId: mockDeviceId,
transaction: transactionWithExpiry,
}).subscribe({
next: value => {
if (value.type === "signed") {
resolve(value);
}
},
error: reject,
});
});
// THEN
expect(mockCraftTransaction).toHaveBeenCalledWith(
mockAccount.currency,
expect.any(Object),
expect.objectContaining({
expireInSeconds: customExpireSeconds,
}),
);
});
it("should handle prepared transaction signing", async () => {
// GIVEN
const mockSigner = createMockCantonSigner();
const mockSignerContext = createMockSignerContext(mockSigner);
mockCraftTransaction.mockResolvedValue({
nativeTransaction: {
json: { transaction, metadata },
serialized: "serialized-transaction",
hash: "mock-hash",
step: { type: "single-step" },
},
serializedTransaction: "serialized-transaction",
hash: "mock-hash",
});
const signOperation = buildSignOperation(mockSignerContext);
// WHEN
const result = await new Promise((resolve, reject) => {
signOperation({
account: mockAccount,
deviceId: mockDeviceId,
transaction: mockTransaction,
}).subscribe({
next: value => {
if (value.type === "signed") {
resolve(value);
}
},
error: reject,
});
});
// THEN
expect(mockCraftTransaction).toHaveBeenCalled();
expect(result).toMatchObject({ signedOperation: { signature: expect.any(String) } });
const signedResult = result as { signedOperation: { signature: string } };
const parsedSignature = JSON.parse(signedResult.signedOperation.signature);
expect(parsedSignature).toMatchObject({
serialized: "serialized-transaction",
signature: expect.stringMatching(
new RegExp(`^[0-9a-f]+__PARTY__${mockAccount.freshAddress}$`, "i"),
),
});
});
});