Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat/live 15126 aptos unit tests bride boradcast prepare transactions #8742

106 changes: 106 additions & 0 deletions libs/ledger-live-common/src/families/aptos/bridge/js.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
import BigNumber from "bignumber.js";
import bridge from "./js";
import { getCryptoCurrencyById } from "@ledgerhq/cryptoassets/currencies";

describe("Aptos bridge interface ", () => {
describe("currencyBridge", () => {
it("should have a preload method that returns a promise", async () => {
const cryptoCurrency = getCryptoCurrencyById("aptos");
const result = bridge.currencyBridge.preload(cryptoCurrency);
expect(result).toBeInstanceOf(Promise);
await expect(result).resolves.toEqual({});
});

it("should have a hydrate method that is a function", () => {
expect(bridge.currencyBridge.hydrate).toBeDefined();
expect(typeof bridge.currencyBridge.hydrate).toBe("function");
const cryptoCurrency = getCryptoCurrencyById("aptos");
const result = bridge.currencyBridge.hydrate({}, cryptoCurrency);
expect(result).toBeUndefined();
});

it("should have a scanAccounts method that is a function", () => {
expect(bridge.currencyBridge.scanAccounts).toBeDefined();
expect(typeof bridge.currencyBridge.scanAccounts).toBe("function");
const cryptoCurrency = getCryptoCurrencyById("aptos");
const deviceId = "test-device";
const result = bridge.currencyBridge.scanAccounts({
currency: cryptoCurrency,
deviceId,
syncConfig: { paginationConfig: {} },
});
expect(result).toBeDefined();
});
});

describe("accountBridge ", () => {
it("should contain all methods", () => {
expect(bridge.accountBridge.estimateMaxSpendable).toBeDefined();
expect(typeof bridge.accountBridge.estimateMaxSpendable).toBe("function");
expect(bridge.accountBridge.createTransaction).toBeDefined();
expect(typeof bridge.accountBridge.createTransaction).toBe("function");
expect(bridge.accountBridge.updateTransaction).toBeDefined();
expect(typeof bridge.accountBridge.updateTransaction).toBe("function");
expect(bridge.accountBridge.getTransactionStatus).toBeDefined();
expect(typeof bridge.accountBridge.getTransactionStatus).toBe("function");
expect(bridge.accountBridge.prepareTransaction).toBeDefined();
expect(typeof bridge.accountBridge.prepareTransaction).toBe("function");
expect(bridge.accountBridge.sync).toBeDefined();
expect(typeof bridge.accountBridge.sync).toBe("function");
expect(bridge.accountBridge.receive).toBeDefined();
expect(typeof bridge.accountBridge.receive).toBe("function");
expect(bridge.accountBridge.signOperation).toBeDefined();
expect(typeof bridge.accountBridge.signOperation).toBe("function");
expect(bridge.accountBridge.broadcast).toBeDefined();
expect(typeof bridge.accountBridge.broadcast).toBe("function");
});
});
describe("updateTransaction", () => {
it("should update the transaction with the given patch", () => {
const initialTransaction = {
amount: new BigNumber(100),
recipient: "address1",
mode: "send",
family: "aptos" as const,
options: { maxGasAmount: "", gasUnitPrice: "" },
estimate: { maxGasAmount: "", gasUnitPrice: "" },
firstEmulation: true,
};
const patch = { amount: new BigNumber(200) };
const updatedTransaction = bridge.accountBridge.updateTransaction(initialTransaction, patch);
expect(updatedTransaction).toEqual({
amount: new BigNumber(200),
recipient: "address1",
mode: "send",
family: "aptos" as const,
options: { maxGasAmount: "", gasUnitPrice: "" },
estimate: { maxGasAmount: "", gasUnitPrice: "" },
firstEmulation: true,
});
});

it("should not modify the original transaction object", () => {
const initialTransaction = {
amount: new BigNumber(100),
recipient: "address1",
mode: "send",
family: "aptos" as const,
options: { maxGasAmount: "", gasUnitPrice: "" },
estimate: { maxGasAmount: "", gasUnitPrice: "" },
firstEmulation: true,
};
const patch = { amount: new BigNumber(200) };
const updatedTransaction = bridge.accountBridge.updateTransaction(initialTransaction, patch);
expect(initialTransaction).toEqual({
amount: new BigNumber(100),
recipient: "address1",
mode: "send",
family: "aptos" as const,
options: { maxGasAmount: "", gasUnitPrice: "" },
estimate: { maxGasAmount: "", gasUnitPrice: "" },
firstEmulation: true,
});
expect(updatedTransaction).not.toBe(initialTransaction);
});
});
});
4 changes: 2 additions & 2 deletions libs/ledger-live-common/src/families/aptos/bridge/js.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,6 @@ import estimateMaxSpendable from "../estimateMaxSpendable";
import signOperation from "../signOperation";
import broadcast from "../broadcast";

const receive = makeAccountBridgeReceive();

const currencyBridge: CurrencyBridge = {
preload: () => Promise.resolve({}),
hydrate: () => {},
Expand All @@ -24,6 +22,8 @@ const updateTransaction = (t: Transaction, patch: Partial<Transaction>): Transac
...patch,
});

const receive = makeAccountBridgeReceive();

const accountBridge: AccountBridge<Transaction> = {
estimateMaxSpendable,
createTransaction,
Expand Down
101 changes: 101 additions & 0 deletions libs/ledger-live-common/src/families/aptos/broadcast.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
import broadcast from "./broadcast";
import { AptosAPI } from "./api";
import { patchOperationWithHash } from "./../../operation";
import { log } from "@ledgerhq/logs";
import type { Account, Operation, SignedOperation } from "@ledgerhq/types-live";
import BigNumber from "bignumber.js";

jest.mock("./api");
jest.mock("./../../operation");
jest.mock("@ledgerhq/logs");

describe("broadcast", () => {
const mockAccount: Account = {
type: "Account",
seedIdentifier: "mockSeedIdentifier",
operationsCount: 0,
id: "mockAccountId",
currency: {
type: "CryptoCurrency",
id: "aptos",
name: "Aptos",
ticker: "APT",
units: [{ name: "APT", code: "APT", magnitude: 6 }],
managerAppName: "Aptos",
coinType: 637,
scheme: "aptos",
color: "#000000",
family: "aptos",
blockAvgTime: 5,
explorerViews: [],
},
balance: BigNumber(1000),
spendableBalance: BigNumber(1000),
operations: [],
pendingOperations: [],
lastSyncDate: new Date(),
blockHeight: 0,
index: 0,
derivationMode: "",
freshAddress: "",
freshAddressPath: "",
used: false,
swapHistory: [],
creationDate: new Date(),
balanceHistoryCache: {
HOUR: { latestDate: 0, balances: [] },
DAY: { latestDate: 0, balances: [] },
WEEK: { latestDate: 0, balances: [] },
},
};

const mockOperation: Operation = {
id: "mockOperationId",
hash: "",
type: "OUT",
value: BigNumber(100),
fee: BigNumber(1),
senders: ["sender"],
recipients: ["recipient"],
blockHeight: null,
blockHash: null,
accountId: "mockAccountId",
date: new Date(),
extra: {},
};

const mockSignedOperation: SignedOperation = {
operation: mockOperation,
signature: "mockSignature",
};

it("should broadcast the signed operation and return the patched operation", async () => {
const mockHash = "mockHash";
(AptosAPI.prototype.broadcast as jest.Mock).mockResolvedValue(mockHash);
(patchOperationWithHash as jest.Mock).mockReturnValue({
...mockOperation,
hash: mockHash,
});

const result = await broadcast({
signedOperation: mockSignedOperation,
account: mockAccount,
});

expect(AptosAPI.prototype.broadcast).toHaveBeenCalledWith("mockSignature");
expect(patchOperationWithHash).toHaveBeenCalledWith(mockOperation, mockHash);
expect(log).toHaveBeenCalledWith("INFO", "APTOS_OP", mockOperation);
expect(result).toEqual({ ...mockOperation, hash: mockHash });
});

it("should throw an error if broadcast fails", async () => {
(AptosAPI.prototype.broadcast as jest.Mock).mockRejectedValue(new Error("Broadcast failed"));

await expect(
broadcast({
signedOperation: mockSignedOperation,
account: mockAccount,
}),
).rejects.toThrow("Broadcast failed");
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,12 @@ import type { Account } from "@ledgerhq/types-live";
import type { TransactionStatus } from "../..//generated/types";
import type { Transaction } from "./types";

import { isValidAddress } from "./logic";
import {
SequenceNumberTooNewError,
SequenceNumberTooOldError,
TransactionExpiredError,
} from "./errors";
import { AccountAddress } from "@aptos-labs/ts-sdk";

const getTransactionStatus = async (a: Account, t: Transaction): Promise<TransactionStatus> => {
const errors: Record<string, any> = {};
Expand All @@ -44,7 +44,7 @@ const getTransactionStatus = async (a: Account, t: Transaction): Promise<Transac

if (!t.recipient) {
errors.recipient = new RecipientRequired();
} else if (!isValidAddress(t.recipient)) {
} else if (AccountAddress.isValid({ input: t.recipient }).valid === false) {
errors.recipient = new InvalidAddress("", { currencyName: a.currency.name });
} else if (t.recipient === a.freshAddress) {
errors.recipient = new InvalidAddressBecauseDestinationIsAlsoSource();
Expand Down
Loading
Loading