@ledgerhq/coin-casper
Version:
Ledger Casper integration
212 lines • 9.76 kB
JavaScript
import { BigNumber } from "bignumber.js";
import { getTransactionStatus } from "./getTransactionStatus";
import { CASPER_MINIMUM_VALID_AMOUNT_MOTES, MayBlockAccountError, InvalidMinimumAmountError, } from "../consts";
import { AmountRequired, InvalidAddress, InvalidAddressBecauseDestinationIsAlsoSource, NotEnoughBalance, RecipientRequired, } from "@ledgerhq/errors";
import { CasperInvalidTransferId } from "../errors";
import { createMockAccount, createMockTransaction, TEST_ADDRESSES } from "../test/fixtures";
describe("getTransactionStatus", () => {
// Create fixtures
const mockAccount = createMockAccount();
const validTransaction = createMockTransaction();
beforeEach(() => {
jest.clearAllMocks();
});
test("should validate a valid transaction", async () => {
const status = await getTransactionStatus(mockAccount, validTransaction);
expect(status.errors).toEqual({});
expect(status.warnings).toEqual({});
expect(status.estimatedFees).toEqual(validTransaction.fees);
expect(status.amount).toEqual(validTransaction.amount);
expect(status.totalSpent).toEqual(validTransaction.amount.plus(validTransaction.fees));
});
test("should return error when recipient is missing", async () => {
const txWithoutRecipient = {
...validTransaction,
recipient: "",
};
const status = await getTransactionStatus(mockAccount, txWithoutRecipient);
expect(status.errors.recipient).toBeInstanceOf(RecipientRequired);
});
test("should return error when recipient is invalid", async () => {
const txWithInvalidRecipient = {
...validTransaction,
recipient: TEST_ADDRESSES.INVALID,
};
const status = await getTransactionStatus(mockAccount, txWithInvalidRecipient);
expect(status.errors.recipient).toBeInstanceOf(InvalidAddress);
});
test("should return error when recipient is same as sender", async () => {
const txToSelf = {
...validTransaction,
recipient: mockAccount.freshAddress,
};
const status = await getTransactionStatus(mockAccount, txToSelf);
expect(status.errors.recipient).toBeInstanceOf(InvalidAddressBecauseDestinationIsAlsoSource);
});
test("should return error when transfer ID is invalid", async () => {
const txWithInvalidTransferId = {
...validTransaction,
transferId: "invalid-id",
};
const status = await getTransactionStatus(mockAccount, txWithInvalidTransferId);
expect(status.errors.sender).toBeInstanceOf(CasperInvalidTransferId);
});
test("should handle useAllAmount correctly", async () => {
const useAllAmountTx = {
...validTransaction,
useAllAmount: true,
};
const status = await getTransactionStatus(mockAccount, useAllAmountTx);
expect(status.amount).toEqual(mockAccount.spendableBalance.minus(useAllAmountTx.fees));
expect(status.totalSpent).toEqual(mockAccount.spendableBalance);
expect(status.errors).toEqual({});
});
test("should return error when useAllAmount but not enough balance for fees", async () => {
const lowBalanceAccount = createMockAccount({
spendableBalance: new BigNumber(500000), // Less than the fee
});
const useAllAmountTx = {
...validTransaction,
useAllAmount: true,
fees: new BigNumber(1000000),
};
const status = await getTransactionStatus(lowBalanceAccount, useAllAmountTx);
expect(status.errors.amount).toBeInstanceOf(NotEnoughBalance);
});
test("should return error when amount is zero", async () => {
const zeroAmountTx = {
...validTransaction,
amount: new BigNumber(0),
};
const status = await getTransactionStatus(mockAccount, zeroAmountTx);
expect(status.errors.amount).toBeInstanceOf(AmountRequired);
});
test("should return error when amount is below minimum", async () => {
const lowAmountTx = {
...validTransaction,
amount: new BigNumber(CASPER_MINIMUM_VALID_AMOUNT_MOTES).minus(1),
};
const status = await getTransactionStatus(mockAccount, lowAmountTx);
expect(status.errors.amount).toBe(InvalidMinimumAmountError);
});
test("should return error when not enough balance for transaction", async () => {
const lowBalanceAccount = createMockAccount({
spendableBalance: new BigNumber(5000000),
});
const expensiveTx = {
...validTransaction,
amount: new BigNumber(5000000),
fees: new BigNumber(1000000),
};
const status = await getTransactionStatus(lowBalanceAccount, expensiveTx);
expect(status.errors.amount).toBeInstanceOf(NotEnoughBalance);
});
test("should return warning when spending more than 90% of balance", async () => {
const expensiveTx = {
...validTransaction,
amount: mockAccount.spendableBalance.multipliedBy(0.95), // 95% of balance
};
const status = await getTransactionStatus(mockAccount, expensiveTx);
expect(status.warnings.amount).toBe(MayBlockAccountError);
});
// Test using the predefined test cases
describe("transaction status test cases", () => {
const testCases = [
{
name: "Valid transaction",
account: createMockAccount(),
transaction: createMockTransaction(),
expectedStatus: { errors: {}, warnings: {}, estimatedFees: createMockTransaction().fees },
},
{
name: "Invalid recipient",
account: createMockAccount(),
transaction: createMockTransaction({ recipient: TEST_ADDRESSES.INVALID }),
expectedStatus: {
errors: { recipient: new InvalidAddress() },
warnings: {},
estimatedFees: createMockTransaction().fees,
},
},
{
name: "Zero amount",
account: createMockAccount(),
transaction: createMockTransaction({ amount: new BigNumber("0") }),
expectedStatus: {
errors: { amount: new AmountRequired() },
warnings: {},
},
},
{
name: "High amount (warning)",
account: createMockAccount(),
transaction: createMockTransaction({
amount: new BigNumber(createMockAccount().balance.minus(createMockTransaction().fees).toString()),
}),
expectedStatus: {
errors: {},
warnings: { amount: MayBlockAccountError },
},
},
{
name: "Insufficient funds",
account: createMockAccount({
balance: new BigNumber("2000000"), // 0.002 CSPR
spendableBalance: new BigNumber("2000000"),
}),
transaction: createMockTransaction({
amount: new BigNumber("2000000"),
}),
expectedStatus: {
errors: { amount: new NotEnoughBalance() },
warnings: { amount: MayBlockAccountError },
},
},
{
name: "Empty account",
account: createMockAccount({
balance: new BigNumber("0"),
spendableBalance: new BigNumber("0"),
}),
transaction: createMockTransaction(),
expectedStatus: {
errors: { amount: new NotEnoughBalance() },
warnings: { amount: MayBlockAccountError },
},
},
];
testCases.forEach(testCase => {
test(`should handle ${testCase.name}`, async () => {
const status = await getTransactionStatus(testCase.account, testCase.transaction);
// Check if errors match expected errors
if (Object.keys(testCase.expectedStatus.errors).length) {
// Simplified error checking
expect(Object.keys(status.errors).length).toBeGreaterThan(0);
// Check only specific known error keys
if ("recipient" in testCase.expectedStatus.errors) {
expect(status.errors.recipient).toBeTruthy();
}
if ("amount" in testCase.expectedStatus.errors) {
expect(status.errors.amount).toBeTruthy();
}
}
else {
expect(Object.keys(status.errors).length).toBe(0);
}
// Check if warnings match expected warnings
if (Object.keys(testCase.expectedStatus.warnings).length) {
// Simplified warning checking
expect(Object.keys(status.warnings).length).toBeGreaterThan(0);
// Check only specific known warning keys
if ("amount" in testCase.expectedStatus.warnings) {
expect(status.warnings.amount).toBeTruthy();
}
}
else {
expect(Object.keys(status.warnings).length).toBe(0);
}
});
});
});
});
//# sourceMappingURL=getTransactionStatus.test.js.map