diff --git a/apps/deploy-web/src/hooks/useManagedWallet.spec.tsx b/apps/deploy-web/src/hooks/useManagedWallet.spec.tsx index 2c813b904..9e86420af 100644 --- a/apps/deploy-web/src/hooks/useManagedWallet.spec.tsx +++ b/apps/deploy-web/src/hooks/useManagedWallet.spec.tsx @@ -1,10 +1,11 @@ -import type { ManagedWalletHttpService } from "@akashnetwork/http-sdk"; +import type { ApiManagedWalletOutput, ManagedWalletHttpService } from "@akashnetwork/http-sdk"; import type { UserProfile } from "@auth0/nextjs-auth0/client"; import { UserProvider } from "@auth0/nextjs-auth0/client"; import { describe, expect, it, vi } from "vitest"; import { mock } from "vitest-mock-extended"; import { useCreateManagedWalletMutation } from "@src/queries/useManagedWalletQuery"; +import { getStorageManagedWallet, updateStorageManagedWallet } from "@src/utils/walletUtils"; import { useManagedWallet } from "./useManagedWallet"; import { act } from "@testing-library/react"; @@ -33,12 +34,74 @@ describe(useManagedWallet.name, () => { }); }); - function setup() { + it("keeps the stored wallet untouched when the API returns a wallet without an address", async () => { + const userId = "user-guard-merge"; + updateStorageManagedWallet({ userId, address: "akash1existing", creditAmount: 100, isTrialing: true, selected: true }); + + const { result } = setup({ userId, apiWallet: buildApiWallet({ userId, address: null, creditAmount: 0 }) }); + + await vi.waitFor(() => { + expect(result.current.managed.wallet).toBeDefined(); + }); + expect(getStorageManagedWallet(userId)).toMatchObject({ address: "akash1existing", creditAmount: 100, isTrialing: true }); + }); + + it("does not persist a wallet without an address to storage", async () => { + const userId = "user-guard-empty"; + + const { result } = setup({ userId, apiWallet: buildApiWallet({ userId, address: null }) }); + + await vi.waitFor(() => { + expect(result.current.managed.wallet).toBeDefined(); + }); + expect(getStorageManagedWallet(userId)).toBeUndefined(); + }); + + it("persists the queried wallet to storage once it has an address", async () => { + const userId = "user-sync"; + + setup({ userId, apiWallet: buildApiWallet({ userId, address: "akash1queried", creditAmount: 25 }) }); + + await vi.waitFor(() => { + expect(getStorageManagedWallet(userId)).toMatchObject({ address: "akash1queried", creditAmount: 25, isTrialing: true }); + }); + }); + + it("persists the created wallet as selected after a successful create", async () => { + const userId = "user-create"; + const createdWallet = buildApiWallet({ userId, address: "akash1created", creditAmount: 50 }); + + const { result } = setup({ userId, createdWallet }); + + act(() => { + result.current.managed.create(); + }); + + await vi.waitFor(() => { + expect(getStorageManagedWallet(userId)).toMatchObject({ address: "akash1created", creditAmount: 50, selected: true }); + }); + }); + + /** Mirrors the real API contract: `address` is nullable while a wallet is mid-provisioning, even though the SDK type claims `string`. */ + function buildApiWallet(overrides: { userId: string; address: string | null; creditAmount?: number }) { + return { + ...mock(), + isTrialing: true, + creditAmount: overrides.creditAmount ?? 0, + userId: overrides.userId, + address: overrides.address + } as ApiManagedWalletOutput; + } + + function setup(input?: { userId?: string; apiWallet?: ApiManagedWalletOutput; createdWallet?: ApiManagedWalletOutput }) { const managedWalletService = mock({ + getWallet: vi.fn().mockResolvedValue(input?.apiWallet ?? null), // A never-settling create keeps the mutation pending for the duration of the assertion. - createWallet: vi.fn().mockReturnValue(new Promise(() => {})) + createWallet: input?.createdWallet ? vi.fn().mockResolvedValue(input.createdWallet) : vi.fn().mockReturnValue(new Promise(() => {})) }); + const user = { email: "test@akash.network", id: input?.userId, userId: input?.userId } as UserProfile; + return setupQuery( () => { const createMutation = useCreateManagedWalletMutation(); @@ -47,7 +110,7 @@ describe(useManagedWallet.name, () => { }, { services: { managedWalletService: () => managedWalletService }, - wrapper: ({ children }) => {children} + wrapper: ({ children }) => {children} } ); } diff --git a/apps/deploy-web/src/hooks/useManagedWallet.ts b/apps/deploy-web/src/hooks/useManagedWallet.ts index 9e5301af1..a7e165572 100644 --- a/apps/deploy-web/src/hooks/useManagedWallet.ts +++ b/apps/deploy-web/src/hooks/useManagedWallet.ts @@ -39,9 +39,13 @@ export const useManagedWallet = () => { }, [signedInUser?.id, queried, created, setIsSignedInWithTrial]); useEffect(() => { - if (wallet && isCreated) { + if (!wallet?.address) { + return; + } + + if (isCreated) { updateStorageManagedWallet({ ...wallet, selected: true }); - } else if (wallet) { + } else { updateStorageManagedWallet(wallet); } }, [isCreated, wallet]);