@@ -4,13 +4,26 @@ import { waitFor } from "@testing-library/react";
44import { afterEach , describe , expect , it , vi } from "vitest" ;
55import { renderHook , act } from "@/test/renderHook" ;
66import { sorobanService } from "@/lib/soroban" ;
7- import { usePoolDepositors , useLockAssetsFeePreview } from "./useSorobanQuery" ;
7+ import {
8+ usePoolDepositors ,
9+ useLockAssetsFeePreview ,
10+ useSetBoost ,
11+ } from "./useSorobanQuery" ;
812
913vi . mock ( "@/lib/soroban" , async ( importOriginal ) => {
1014 const actual = await importOriginal < typeof import ( "@/lib/soroban" ) > ( ) ;
1115 return { ...actual , simulateLockAssets : vi . fn ( ) } ;
1216} ) ;
1317
18+ vi . mock ( "@/context/StellarWalletContext" , ( ) => ( {
19+ useStellarWallet : vi . fn ( ) ,
20+ } ) ) ;
21+
22+ vi . mock ( "@chakra-ui/react" , async ( importOriginal ) => {
23+ const actual = await importOriginal < typeof import ( "@chakra-ui/react" ) > ( ) ;
24+ return { ...actual , useToast : vi . fn ( ) } ;
25+ } ) ;
26+
1427const { simulateLockAssets } = await import ( "@/lib/soroban" ) ;
1528const simulateLockAssetsMock = vi . mocked ( simulateLockAssets ) ;
1629
@@ -232,3 +245,172 @@ describe("useLockAssetsFeePreview (#134)", () => {
232245 expect ( result . current . data ?. feePreview ) . toBe ( "second" ) ;
233246 } ) ;
234247} ) ;
248+
249+ describe ( "useSetBoost (#92)" , ( ) => {
250+ const TEST_PUBLIC_KEY =
251+ "GAAZI4TCR3TY5OJHCTJC2A4QSY6CJWJH5IAJTGKIN2ER7LBNVKOCCWN" ;
252+ const POOL_ID = "pool-xlm" ;
253+ let queryClient : QueryClient ;
254+ let toastMock : ReturnType < typeof vi . fn > ;
255+
256+ beforeEach ( ( ) => {
257+ queryClient = new QueryClient ( {
258+ defaultOptions : { queries : { retry : false } } ,
259+ } ) ;
260+ } ) ;
261+
262+ function boostWrapper ( { children } : { children : ReactNode } ) {
263+ return createElement ( QueryClientProvider , { client : queryClient } , children ) ;
264+ }
265+
266+ beforeEach ( async ( ) => {
267+ const { useStellarWallet } = await import (
268+ "@/context/StellarWalletContext"
269+ ) ;
270+ vi . mocked ( useStellarWallet ) . mockReturnValue ( {
271+ publicKey : TEST_PUBLIC_KEY ,
272+ isConnected : true ,
273+ walletApi : { signTransaction : vi . fn ( ) . mockResolvedValue ( "signed-xdr" ) } ,
274+ connect : vi . fn ( ) ,
275+ disconnect : vi . fn ( ) ,
276+ } ) ;
277+
278+ const { useToast } = await import ( "@chakra-ui/react" ) ;
279+ toastMock = vi . fn ( ) ;
280+ vi . mocked ( useToast ) . mockReturnValue ( toastMock ) ;
281+ } ) ;
282+
283+ it ( "calls sorobanService.setBoost with correct arguments" , async ( ) => {
284+ const spy = vi
285+ . spyOn ( sorobanService , "setBoost" )
286+ . mockResolvedValue ( { success : true , transactionHash : "txhash" } ) ;
287+
288+ const { result } = renderHook ( ( ) => useSetBoost ( ) , {
289+ wrapper : boostWrapper ,
290+ } ) ;
291+
292+ await act ( async ( ) => {
293+ result . current . mutate ( { poolId : POOL_ID , allocationPercentage : 50 } ) ;
294+ } ) ;
295+
296+ await waitFor ( ( ) => expect ( result . current . isSuccess ) . toBe ( true ) ) ;
297+
298+ expect ( spy ) . toHaveBeenCalledWith (
299+ POOL_ID ,
300+ TEST_PUBLIC_KEY ,
301+ 50 ,
302+ expect . objectContaining ( { signTransaction : expect . any ( Function ) } ) ,
303+ ) ;
304+ } ) ;
305+
306+ it ( "shows success toast and invalidates queries on success" , async ( ) => {
307+ vi . spyOn ( sorobanService , "setBoost" ) . mockResolvedValue ( {
308+ success : true ,
309+ transactionHash : "txhash123" ,
310+ } ) ;
311+ const invalidateSpy = vi . spyOn ( queryClient , "invalidateQueries" ) ;
312+
313+ const { result } = renderHook ( ( ) => useSetBoost ( ) , {
314+ wrapper : boostWrapper ,
315+ } ) ;
316+
317+ await act ( async ( ) => {
318+ result . current . mutate ( { poolId : POOL_ID , allocationPercentage : 75 } ) ;
319+ } ) ;
320+
321+ await waitFor ( ( ) => expect ( result . current . isSuccess ) . toBe ( true ) ) ;
322+
323+ expect ( toastMock ) . toHaveBeenCalledWith (
324+ expect . objectContaining ( {
325+ title : "Boost Configuration Updated" ,
326+ description : "Boost set to 75%" ,
327+ status : "success" ,
328+ } ) ,
329+ ) ;
330+
331+ expect ( invalidateSpy ) . toHaveBeenCalledWith ( {
332+ queryKey : [ "userPosition" , POOL_ID ] ,
333+ } ) ;
334+ expect ( invalidateSpy ) . toHaveBeenCalledWith ( {
335+ queryKey : [ "userCredits" , POOL_ID ] ,
336+ } ) ;
337+ expect ( invalidateSpy ) . toHaveBeenCalledWith ( {
338+ queryKey : [ "boostConfig" , POOL_ID ] ,
339+ } ) ;
340+ } ) ;
341+
342+ it ( "shows error toast when setBoost fails" , async ( ) => {
343+ vi . spyOn ( sorobanService , "setBoost" ) . mockResolvedValue ( {
344+ success : false ,
345+ error : "Simulation failed" ,
346+ } ) ;
347+
348+ const { result } = renderHook ( ( ) => useSetBoost ( ) , {
349+ wrapper : boostWrapper ,
350+ } ) ;
351+
352+ await act ( async ( ) => {
353+ result . current . mutate ( { poolId : POOL_ID , allocationPercentage : 50 } ) ;
354+ } ) ;
355+
356+ await waitFor ( ( ) => expect ( result . current . isSuccess ) . toBe ( true ) ) ;
357+
358+ expect ( toastMock ) . toHaveBeenCalledWith (
359+ expect . objectContaining ( {
360+ title : "Boost Configuration Failed" ,
361+ status : "error" ,
362+ } ) ,
363+ ) ;
364+ } ) ;
365+
366+ it ( "shows error toast when wallet is not connected" , async ( ) => {
367+ const { useStellarWallet } = await import (
368+ "@/context/StellarWalletContext"
369+ ) ;
370+ vi . mocked ( useStellarWallet ) . mockReturnValue ( {
371+ publicKey : null ,
372+ isConnected : false ,
373+ walletApi : null ,
374+ connect : vi . fn ( ) ,
375+ disconnect : vi . fn ( ) ,
376+ } ) ;
377+
378+ const { result } = renderHook ( ( ) => useSetBoost ( ) , {
379+ wrapper : boostWrapper ,
380+ } ) ;
381+
382+ await act ( async ( ) => {
383+ result . current . mutate ( { poolId : POOL_ID , allocationPercentage : 50 } ) ;
384+ } ) ;
385+
386+ await waitFor ( ( ) => expect ( result . current . isError ) . toBe ( true ) ) ;
387+
388+ expect ( result . current . error ?. message ) . toBe (
389+ "Wallet not connected" ,
390+ ) ;
391+ } ) ;
392+
393+ it ( "shows error toast when service throws" , async ( ) => {
394+ vi . spyOn ( sorobanService , "setBoost" ) . mockRejectedValue (
395+ new Error ( "Network error" ) ,
396+ ) ;
397+
398+ const { result } = renderHook ( ( ) => useSetBoost ( ) , {
399+ wrapper : boostWrapper ,
400+ } ) ;
401+
402+ await act ( async ( ) => {
403+ result . current . mutate ( { poolId : POOL_ID , allocationPercentage : 50 } ) ;
404+ } ) ;
405+
406+ await waitFor ( ( ) => expect ( result . current . isError ) . toBe ( true ) ) ;
407+
408+ expect ( toastMock ) . toHaveBeenCalledWith (
409+ expect . objectContaining ( {
410+ title : "Transaction Error" ,
411+ description : "Network error" ,
412+ status : "error" ,
413+ } ) ,
414+ ) ;
415+ } ) ;
416+ } ) ;
0 commit comments