From 00e9b99f126a7d8661c92fbc98a96ce475ede551 Mon Sep 17 00:00:00 2001 From: extolkom Date: Thu, 30 Jul 2026 19:28:23 -1200 Subject: [PATCH 1/2] tests: add emitOnStart: false, interval clamp, and maxPolls: 0 coverage (#276) --- src/tests/account.test.ts | 92 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 91 insertions(+), 1 deletion(-) diff --git a/src/tests/account.test.ts b/src/tests/account.test.ts index b1343ed..d48a0f1 100644 --- a/src/tests/account.test.ts +++ b/src/tests/account.test.ts @@ -1,4 +1,4 @@ -import { beforeEach, describe, expect, it, vi } from "vitest"; +import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; import { formatAddress } from "../shared/utils"; import { ok, err, SorokitErrorCode } from "../shared/response"; import type { AccountInfo } from "../account/types"; @@ -15,6 +15,9 @@ vi.mock("../shared", async () => { ...actual, sleep: vi.fn((ms: number) => { accountMockState.sleepCalls.push(ms); + if (typeof vi.isFakeTimers === "function" && vi.isFakeTimers()) { + return new Promise((resolve) => setTimeout(resolve, ms)); + } return Promise.resolve(); }), }; @@ -207,6 +210,93 @@ describe("account", () => { 3000, ]); }); + + describe("emitOnStart: false and interval clamping behavior (#276)", () => { + afterEach(() => { + vi.useRealTimers(); + }); + + it("emitOnStart: false with maxPolls: 1 yields exactly one result after the first sleep", async () => { + vi.useFakeTimers(); + accountMockState.results = [createAccount("1")]; + + const stream = streamAccount("https://horizon.test", "G...", { + emitOnStart: false, + maxPolls: 1, + intervalMs: 2000, + }); + + let resolved = false; + const promise = stream.next().then((res) => { + resolved = true; + return res; + }); + + await Promise.resolve(); + expect(resolved).toBe(false); + expect(accountMockState.sleepCalls).toEqual([2000]); + + await vi.advanceTimersByTimeAsync(2000); + + const result = await promise; + expect(resolved).toBe(true); + expect(result.done).toBe(false); + expect(result.value?.status).toBe("ok"); + if (result.value?.status === "ok") { + expect(result.value.data.sequence).toBe("1"); + } + + const nextResult = await stream.next(); + expect(nextResult.done).toBe(true); + }); + + it("intervalMs: 500 is clamped to 1000ms (verifiable via fake timers)", async () => { + vi.useFakeTimers(); + accountMockState.results = [createAccount("1")]; + + const warnSpy = vi.spyOn(console, "warn").mockImplementation(() => {}); + + const stream = streamAccount("https://horizon.test", "G...", { + intervalMs: 500, + emitOnStart: false, + maxPolls: 1, + }); + + let resolved = false; + const promise = stream.next().then((res) => { + resolved = true; + return res; + }); + + await Promise.resolve(); + expect(accountMockState.sleepCalls).toEqual([1000]); + expect(resolved).toBe(false); + + await vi.advanceTimersByTimeAsync(500); + expect(resolved).toBe(false); + + await vi.advanceTimersByTimeAsync(500); + const result = await promise; + expect(resolved).toBe(true); + expect(result.done).toBe(false); + + warnSpy.mockRestore(); + }); + + it("emitOnStart: true with maxPolls: 0 yields nothing", async () => { + accountMockState.results = [createAccount("1")]; + + const stream = streamAccount("https://horizon.test", "G...", { + emitOnStart: true, + maxPolls: 0, + }); + + const result = await stream.next(); + expect(result.done).toBe(true); + expect(result.value).toBeUndefined(); + expect(accountMockState.sleepCalls).toEqual([]); + }); + }); }); describe("deepEqual", () => { From d50564c6d3dee8298ad87292d58537f07cbd4db7 Mon Sep 17 00:00:00 2001 From: extolkom Date: Thu, 30 Jul 2026 19:32:40 -1200 Subject: [PATCH 2/2] test(wallet): mock adapter availability in disconnectWallet test --- src/shared/logger.ts | 16 ---------------- src/tests/timeout.test.ts | 2 +- src/tests/wallet.test.ts | 4 +++- 3 files changed, 4 insertions(+), 18 deletions(-) diff --git a/src/shared/logger.ts b/src/shared/logger.ts index 96b8c2d..7643f56 100644 --- a/src/shared/logger.ts +++ b/src/shared/logger.ts @@ -159,22 +159,6 @@ export function createTracedLogger( }; } -/** - * Create a logger instance. - * Pass a custom implementation to redirect logs to your own sink. - */ -export function createLogger( - config?: LoggerConfig | boolean, - custom?: SorokitLogger, -): SorokitLogger { - if (custom) return custom; - if (typeof config === "object" && config?.logger) return config.logger; - - const logLevel = resolveLogLevel(config); - if (logLevel === "off") return noopLogger; - const prefix = typeof config === "object" ? config?.prefix : undefined; - return createConsoleLogger(logLevel, prefix); -} /** * Log the start and result of an async SDK operation. diff --git a/src/tests/timeout.test.ts b/src/tests/timeout.test.ts index c04c38e..96e1420 100644 --- a/src/tests/timeout.test.ts +++ b/src/tests/timeout.test.ts @@ -154,7 +154,7 @@ describe("timeout configuration", () => { }); it("should use global timeout over default", () => { - const global = 10000; + const global = 15000; const defaultTimeout = DEFAULT_TIMEOUTS.account_get; const result = getTimeout("account_get", undefined, global); diff --git a/src/tests/wallet.test.ts b/src/tests/wallet.test.ts index 5f79929..3a66dad 100644 --- a/src/tests/wallet.test.ts +++ b/src/tests/wallet.test.ts @@ -184,7 +184,9 @@ describe("wallet module functions", () => { }); it("disconnectWallet() returns status ok with clean state", async () => { - const result = await disconnectWallet(new FreighterAdapter(mockKit())); + const adapter = new FreighterAdapter(mockKit()); + vi.spyOn(adapter, "isAvailable").mockReturnValue(true); + const result = await disconnectWallet(adapter); expect(result.status).toBe("ok"); if (result.status === "ok") { expect(result.data.connected).toBe(false);