Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 24 additions & 0 deletions src/lib/__tests__/mock-client.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -124,4 +124,28 @@ describe('Mock Client - Issue #30 Fixes', () => {
expect(snapshot1).toEqual(snapshot2);
});
});

describe('Fix 4: Pagination support in getHistory (Issue #588)', () => {
it('should paginate results by page and limit with accurate total', async () => {
const client = createMockClient() as SorokitClient;

const page1 = await client.transaction.getHistory('GADDR', 1, 10);
const page2 = await client.transaction.getHistory('GADDR', 2, 10);
const page3 = await client.transaction.getHistory('GADDR', 3, 10);

expect(page1.data).toHaveLength(10);
expect(page2.data).toHaveLength(10);
expect(page3.data).toHaveLength(5);

expect(page1.total).toBe(25);
expect(page2.total).toBe(25);
expect(page3.total).toBe(25);

// Verify that page 1 and page 2 return completely different transactions
const page1Ids = page1.data?.map(t => t.id);
const page2Ids = page2.data?.map(t => t.id);
expect(page1Ids).not.toEqual(page2Ids);
});
});
});

19 changes: 16 additions & 3 deletions src/lib/mock-client.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,9 +52,8 @@ describe("mock-client", () => {
expect(res.data?.length).toBe(limit);
});

it("verifies getHistory returns distinct pages via the page parameter", async () => {
const { createMockClient } = await import("./mock-client");
const { MOCK_HISTORY } = await import("./mock-client");
it("verifies getHistory paginates correctly across multiple pages", async () => {
const { createMockClient, MOCK_HISTORY } = await import("./mock-client");
const client = createMockClient();

const limit = 2;
Expand All @@ -70,4 +69,18 @@ describe("mock-client", () => {
expect(page2.data?.[0].hash).toBe(MOCK_HISTORY[limit].hash);
expect(page1.total).toBe(MOCK_HISTORY.length);
});

it("verifies instance isolation between multiple createMockClient invocations", async () => {
const { createMockClient } = await import("./mock-client");
const clientA = createMockClient("testnet");
const clientB = createMockClient("public");

if ("network" in clientA && "network" in clientB) {
const netA = await clientA.network.getNetwork();
const netB = await clientB.network.getNetwork();
expect(netA.data?.name).toBe("testnet");
expect(netB.data?.name).toBe("mainnet");
}
});
});

25 changes: 17 additions & 8 deletions src/lib/mock-client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,8 @@ import { deterministicMock } from "./deterministic-mock";
export const MOCK_ADDRESS =
"GBRPYHIL2CI3WHGSUJGY6O7SROQOMJG7QBCACN4QPKUOQNXJDGONXHPA";

// Generate deterministic mock data (consistent across test runs)
export const MOCK_HISTORY = deterministicMock.generateMockHistory(5);
// Generate deterministic mock data (consistent across test runs, 25 items to support multi-page pagination)
export const MOCK_HISTORY = deterministicMock.generateMockHistory(25);
export const MOCK_EVENTS = deterministicMock.generateMockEvents(3);

export const NETWORKS = {
Expand Down Expand Up @@ -230,9 +230,17 @@ const MOCK_ALLOWANCES: AllowanceEntry[] = [
];

/**
* Create a mock client that satisfies the SorokitClient interface.
* If called with an invalid network name, returns the error object
* (backward compatible with simple-error tests).
* Mock Strategy:
*
* Provides a canonical, standalone implementation of SorokitClient for development,
* demo mode (in main.tsx), and component/screen unit testing.
*
* Design:
* - Single source of truth for mock blockchain data across the entire repository.
* - Instance-scoped state: Each invocation of createMockClient() creates an independent
* instance with its own network and connection state, preventing test cross-contamination.
* - Proper pagination support: getHistory slices deterministic transaction records by
* page and limit, providing distinct pages and accurate total count for TransactionHistory.
*/
export function createMockClient(): SorokitClient;
export function createMockClient(
Expand All @@ -241,8 +249,9 @@ export function createMockClient(
export function createMockClient(
networkName?: string,
): SorokitClient | { data: null; error: string } {
const activeNetwork =
let activeNetwork =
networkName && networkName in NETWORKS ? networkName : "testnet";
let connectedAddress = MOCK_ADDRESS;

if (networkName && !(networkName in NETWORKS)) {
const validNetworks = Object.keys(NETWORKS).join(", ");
Expand All @@ -255,12 +264,12 @@ export function createMockClient(
return {
wallet: {
connect: async () => ({
data: { address: MOCK_ADDRESS },
data: { address: connectedAddress },
error: null,
status: "success" as const,
}),
disconnect: async () => {},
getAddress: async () => ({ data: MOCK_ADDRESS, error: null }),
getAddress: async () => ({ data: connectedAddress, error: null }),
},
account: {
getAccount: async () => ({
Expand Down