import type { Account } from "@ledgerhq/types-live"; import { BigNumber } from "bignumber.js"; import Prando from "prando"; import mock from "./mock"; import type { AlgorandAccount } from "./types"; // Mock dependencies jest.mock("@ledgerhq/ledger-wallet-framework/mocks/helpers", () => ({ genAddress: jest.fn(() => "MOCK_ADDRESS"), genHex: jest.fn((length: number) => "a".repeat(length)), })); describe("mock", () => { const createMockAccount = ( balance: string = "10000000", operations: unknown[] = [], subAccounts: unknown[] = [], ): Account => ({ id: "mock-account-1", balance: new BigNumber(balance), spendableBalance: new BigNumber(balance), blockHeight: 50000000, operations, operationsCount: operations.length, subAccounts, currency: { id: "algorand", ticker: "ALGO" }, }) as unknown as Account; describe("genAccountEnhanceOperations", () => { it("should add OPT_IN operation", () => { const account = createMockAccount("10000000", []); const rng = new Prando("test-seed"); const result = mock.genAccountEnhanceOperations(account, rng); const optInOps = result.operations.filter(op => op.type === "OPT_IN"); expect(optInOps.length).toBeGreaterThanOrEqual(1); }); it("should add OPT_OUT operation", () => { const account = createMockAccount("10000000", []); const rng = new Prando("test-seed"); const result = mock.genAccountEnhanceOperations(account, rng); const optOutOps = result.operations.filter(op => op.type === "OPT_OUT"); expect(optOutOps.length).toBeGreaterThanOrEqual(1); }); it("should set algorandResources", () => { const account = createMockAccount("10000000", []); const rng = new Prando("test-seed"); const result = mock.genAccountEnhanceOperations(account, rng) as AlgorandAccount; expect(result.algorandResources).not.toBeUndefined(); expect(result.algorandResources.rewards).toBeInstanceOf(BigNumber); expect(result.algorandResources.nbAssets).not.toBeUndefined(); }); it("should set rewards to 1% of balance", () => { const balance = "10000000"; const account = createMockAccount(balance, []); const rng = new Prando("test-seed"); const result = mock.genAccountEnhanceOperations(account, rng) as AlgorandAccount; expect(result.algorandResources.rewards.toString()).toBe( new BigNumber(balance).multipliedBy(0.01).toString(), ); }); it("should set nbAssets to subAccounts length", () => { const subAccounts = [{ id: "sub-1" }, { id: "sub-2" }, { id: "sub-3" }]; const account = createMockAccount("10000000", [], subAccounts); const rng = new Prando("test-seed"); const result = mock.genAccountEnhanceOperations(account, rng) as AlgorandAccount; expect(result.algorandResources.nbAssets).toBe(3); }); it("should increment operationsCount", () => { const account = createMockAccount("10000000", []); const initialCount = account.operationsCount; const rng = new Prando("test-seed"); const result = mock.genAccountEnhanceOperations(account, rng); expect(result.operationsCount).toBeGreaterThan(initialCount); }); it("should generate operations with valid structure", () => { const account = createMockAccount("10000000", []); const rng = new Prando("test-seed"); const result = mock.genAccountEnhanceOperations(account, rng); for (const op of result.operations) { expect(op).toHaveProperty("id"); expect(op).toHaveProperty("hash"); expect(op).toHaveProperty("type"); expect(op).toHaveProperty("value"); expect(op).toHaveProperty("fee"); expect(op).toHaveProperty("senders"); expect(op).toHaveProperty("recipients"); expect(op).toHaveProperty("date"); expect(op).toHaveProperty("extra"); } }); it("should add assetId to OPT_IN and OPT_OUT extras", () => { const account = createMockAccount("10000000", []); const rng = new Prando("test-seed"); const result = mock.genAccountEnhanceOperations(account, rng); const optOps = result.operations.filter(op => op.type === "OPT_IN" || op.type === "OPT_OUT"); for (const op of optOps) { expect(op.extra.assetId).not.toBeUndefined(); } }); }); describe("postSyncAccount", () => { it("should update spendableBalance with rewards", () => { const account = createMockAccount("10000000", []) as AlgorandAccount; account.algorandResources = { rewards: new BigNumber("50000"), nbAssets: 0, }; const result = mock.postSyncAccount(account); expect(result.spendableBalance.toString()).toBe("10050000"); }); it("should handle missing algorandResources", () => { const account = createMockAccount("10000000", []); const result = mock.postSyncAccount(account); expect(result.spendableBalance.toString()).toBe("10000000"); }); it("should handle missing rewards", () => { const account = createMockAccount("10000000", []) as AlgorandAccount; account.algorandResources = { rewards: undefined as unknown as BigNumber, nbAssets: 0, }; const result = mock.postSyncAccount(account); expect(result.spendableBalance.toString()).toBe("10000000"); }); it("should return the same account object", () => { const account = createMockAccount("10000000", []) as AlgorandAccount; account.algorandResources = { rewards: new BigNumber("1000"), nbAssets: 0, }; const result = mock.postSyncAccount(account); expect(result).toBe(account); }); }); describe("postScanAccount", () => { it("should clear operations when isEmpty is true", () => { const account = createMockAccount("10000000", [{ id: "op-1" }, { id: "op-2" }]); const result = mock.postScanAccount(account, { isEmpty: true }); expect(result.operations).toEqual([]); }); it("should clear subAccounts when isEmpty is true", () => { const account = createMockAccount("10000000", [], [{ id: "sub-1" }]); const result = mock.postScanAccount(account, { isEmpty: true }); expect(result.subAccounts).toEqual([]); }); it("should reset algorandResources when isEmpty is true", () => { const account = createMockAccount("10000000", []) as AlgorandAccount; account.algorandResources = { rewards: new BigNumber("50000"), nbAssets: 5, }; const result = mock.postScanAccount(account, { isEmpty: true }) as AlgorandAccount; expect(result.algorandResources.rewards.toString()).toBe("0"); expect(result.algorandResources.nbAssets).toBe(0); }); it("should preserve data when isEmpty is false", () => { const operations = [{ id: "op-1" }, { id: "op-2" }]; const subAccounts = [{ id: "sub-1" }]; const account = createMockAccount("10000000", operations, subAccounts) as AlgorandAccount; account.algorandResources = { rewards: new BigNumber("50000"), nbAssets: 1, }; const result = mock.postScanAccount(account, { isEmpty: false }) as AlgorandAccount; expect(result.operations).toEqual(operations); expect(result.subAccounts).toEqual(subAccounts); expect(result.algorandResources.rewards.toString()).toBe("50000"); }); it("should return the same account object", () => { const account = createMockAccount("10000000", []); const result = mock.postScanAccount(account, { isEmpty: true }); expect(result).toBe(account); }); it("should set nbAssets based on original subAccounts length before clearing", () => { // Note: nbAssets is calculated before subAccounts is cleared const account = createMockAccount("10000000", [], [{ id: "sub-1" }, { id: "sub-2" }]); const result = mock.postScanAccount(account, { isEmpty: true }) as AlgorandAccount; // nbAssets reflects the count at the time algorandResources was set expect(result.algorandResources.nbAssets).toBe(2); // But subAccounts is cleared afterwards expect(result.subAccounts).toEqual([]); }); }); describe("export structure", () => { it("should export genAccountEnhanceOperations", () => { expect(mock.genAccountEnhanceOperations).not.toBeUndefined(); expect(typeof mock.genAccountEnhanceOperations).toBe("function"); }); it("should export postSyncAccount", () => { expect(mock.postSyncAccount).not.toBeUndefined(); expect(typeof mock.postSyncAccount).toBe("function"); }); it("should export postScanAccount", () => { expect(mock.postScanAccount).not.toBeUndefined(); expect(typeof mock.postScanAccount).toBe("function"); }); }); });