diff --git a/src/lib/mock-client.test.ts b/src/lib/mock-client.test.ts index 673c769..27b1a3d 100644 --- a/src/lib/mock-client.test.ts +++ b/src/lib/mock-client.test.ts @@ -44,12 +44,50 @@ describe("mock-client", () => { it("verifies getHistory respects the limit parameter", async () => { const { createMockClient } = await import("./mock-client"); const client = createMockClient(); - + const limit = 3; const res = await client.transaction.getHistory("address", 1, limit); - + expect(res.data).toBeDefined(); expect(res.data?.length).toBe(limit); + expect(res.total).toBe(25); + }); + + it("verifies getHistory correctly paginates across multiple pages with page and limit", async () => { + const { createMockClient } = await import("./mock-client"); + const client = createMockClient(); + + const pageSize = 10; + const page1 = await client.transaction.getHistory("address", 1, pageSize); + const page2 = await client.transaction.getHistory("address", 2, pageSize); + const page3 = await client.transaction.getHistory("address", 3, pageSize); + const page4 = await client.transaction.getHistory("address", 4, pageSize); + + expect(page1.data).toHaveLength(10); + expect(page2.data).toHaveLength(10); + expect(page3.data).toHaveLength(5); + expect(page4.data).toHaveLength(0); + + expect(page1.total).toBe(25); + expect(page2.total).toBe(25); + expect(page3.total).toBe(25); + + // Verify page slices do not overlap + const page1Hashes = page1.data?.map((tx) => tx.hash); + const page2Hashes = page2.data?.map((tx) => tx.hash); + const page3Hashes = page3.data?.map((tx) => tx.hash); + + expect(page1Hashes?.some((h) => page2Hashes?.includes(h))).toBe(false); + expect(page2Hashes?.some((h) => page3Hashes?.includes(h))).toBe(false); + }); + + it("verifies getHistory defaults to page 1 and all items when page/limit not provided", async () => { + const { createMockClient, MOCK_HISTORY } = await import("./mock-client"); + const client = createMockClient(); + + const res = await client.transaction.getHistory("address"); + expect(res.data).toHaveLength(MOCK_HISTORY.length); + expect(res.total).toBe(MOCK_HISTORY.length); }); it("verifies getHistory returns distinct pages via the page parameter", async () => { diff --git a/src/lib/mock-client.ts b/src/lib/mock-client.ts index 40bb179..c9f4d6b 100644 --- a/src/lib/mock-client.ts +++ b/src/lib/mock-client.ts @@ -21,7 +21,7 @@ export const MOCK_ADDRESS = "GBRPYHIL2CI3WHGSUJGY6O7SROQOMJG7QBCACN4QPKUOQNXJDGONXHPA"; // Generate deterministic mock data (consistent across test runs) -export const MOCK_HISTORY = deterministicMock.generateMockHistory(5); +export const MOCK_HISTORY = deterministicMock.generateMockHistory(25); export const MOCK_EVENTS = deterministicMock.generateMockEvents(3); export const NETWORKS = { @@ -285,19 +285,17 @@ export function createMockClient( getStatus: async () => ({ data: "success" as TxStatus, error: null }), getHistory: async ( _address: string, - page?: number, + page: number = 1, limit?: number, ) => { - const pageSize = limit ?? MOCK_HISTORY.length; - const currentPage = page ?? 1; - const start = (currentPage - 1) * pageSize; + const safePage = Math.max(1, page || 1); + const pageSize = + limit !== undefined && limit > 0 ? limit : MOCK_HISTORY.length; + const total = MOCK_HISTORY.length; + const start = (safePage - 1) * pageSize; const end = start + pageSize; - const data = MOCK_HISTORY.slice(start, end); - return { - data: data.length > 0 ? data : null, - error: null, - total: MOCK_HISTORY.length, - }; + const history = MOCK_HISTORY.slice(start, end); + return { data: history, error: null, total }; }, estimateFee: async () => ({ data: { baseFee: "100", recommended: "1000" }, diff --git a/src/main.tsx b/src/main.tsx index 9c9445a..9e3d60a 100644 --- a/src/main.tsx +++ b/src/main.tsx @@ -8,7 +8,9 @@ import { ErrorBoundary } from './components/ErrorBoundary' import type { SorokitClient } from './lib/client.ts' import { createMockClient } from './lib/mock-client' -// Initialize mock client for development +// Client Strategy: Using createMockClient() from mock-client.ts for local development and demo mode. +// This single authoritative mock provides realistic delays, pagination, deterministic state, and balances. +// To connect to a live Soroban/Stellar network adapter in production, replace this factory with a real ClientAdapter. const createClient = (): SorokitClient => createMockClient() as SorokitClient function Root() {