import { describe, it, expect, vi, beforeEach } from "vitest" import { renderHook } from "@testing-library/react" import React from "react" import { ClientManager, Reactor } from "@ic-reactor/core" import { QueryClient, QueryClientProvider } from "@tanstack/react-query" import { Actor, ActorMethod } from "@icp-sdk/core/agent" import { useActorMutation } from "./useActorMutation" // Mock IDL factory const idlFactory = ({ IDL }: any) => { return IDL.Service({ create_user: IDL.Func( [IDL.Record({ name: IDL.Text })], [IDL.Record({ id: IDL.Text, name: IDL.Text })], ["update"] ), update_user: IDL.Func( [IDL.Record({ id: IDL.Text, name: IDL.Text })], [IDL.Record({ id: IDL.Text, name: IDL.Text })], ["update"] ), delete_user: IDL.Func([IDL.Text], [IDL.Bool], ["update"]), }) } // Define Actor Interface interface CreateUserInput { name: string } interface UpdateUserInput { id: string name: string } interface User { id: string name: string } // Actor type with methods interface TestActor { get_user: ActorMethod<[string], User> create_user: ActorMethod<[CreateUserInput], User> update_user: ActorMethod<[UpdateUserInput], User> delete_user: ActorMethod<[string], boolean> } // Mock data const mockUserCreated: User = { id: "user-1", name: "Alice" } const mockUserUpdated: User = { id: "user-1", name: "Bob" } // Mock Actor.createActor vi.mock("@icp-sdk/core/agent", async () => { const actual = await vi.importActual( "@icp-sdk/core/agent" ) return { ...actual, Actor: class extends actual.Actor { static createActor = vi.fn() static canisterIdOf = vi.fn().mockReturnValue({ toString: () => "test-canister-id", }) static interfaceOf = vi.fn().mockReturnValue({ _fields: [], }) }, } }) describe("useActorMutation", () => { let queryClient: QueryClient let clientManager: ClientManager let mockActor: TestActor let reactor: Reactor beforeEach(() => { queryClient = new QueryClient({ defaultOptions: { mutations: { retry: false, }, }, }) clientManager = new ClientManager({ queryClient }) // Setup mock actor mockActor = { create_user: vi.fn().mockResolvedValue(mockUserCreated), update_user: vi.fn().mockResolvedValue(mockUserUpdated), delete_user: vi.fn().mockResolvedValue(true), } as unknown as TestActor ;(Actor.createActor as any).mockReturnValue(mockActor) reactor = new Reactor({ clientManager, canisterId: "rrkah-fqaaa-aaaaa-aaaaq-cai", idlFactory, name: "test", }) }) const wrapper = ({ children }: { children: React.ReactNode }) => ( {children} ) describe("basic functionality", () => { it("should create a mutation hook", () => { const { result } = renderHook( () => useActorMutation({ reactor, functionName: "create_user", }), { wrapper } ) expect(result.current).toBeDefined() }) it("should return correct hook structure", () => { const { result } = renderHook( () => useActorMutation({ reactor, functionName: "create_user", }), { wrapper } ) expect(result.current.mutate).toBeInstanceOf(Function) expect(result.current.mutateAsync).toBeInstanceOf(Function) expect(typeof result.current.isPending).toBe("boolean") expect(typeof result.current.isSuccess).toBe("boolean") expect(typeof result.current.isError).toBe("boolean") }) it("should have correct initial state", () => { const { result } = renderHook( () => useActorMutation({ reactor, functionName: "create_user", }), { wrapper } ) expect(result.current.isPending).toBe(false) expect(result.current.isSuccess).toBe(false) expect(result.current.isError).toBe(false) expect(result.current.data).toBeUndefined() }) }) describe("hook options", () => { it("should accept mutation options like onSuccess and onError", () => { const onSuccess = vi.fn() const onError = vi.fn() const { result } = renderHook( () => useActorMutation({ reactor, functionName: "create_user", onSuccess, onError, }), { wrapper } ) expect(result.current.mutate).toBeInstanceOf(Function) }) it("should expose mutate and mutateAsync methods", () => { const { result } = renderHook( () => useActorMutation({ reactor, functionName: "create_user", }), { wrapper } ) expect(result.current.mutate).toBeInstanceOf(Function) expect(result.current.mutateAsync).toBeInstanceOf(Function) }) }) describe("invalidateQueries functionality", () => { it("should accept invalidateQueries option", () => { const { result } = renderHook( () => useActorMutation({ reactor, functionName: "update_user", invalidateQueries: [["test-canister", "get_user"]], }), { wrapper } ) expect(result.current.mutate).toBeInstanceOf(Function) }) it("should support invalidateQueries with args", () => { const { result } = renderHook( () => useActorMutation({ reactor, functionName: "update_user", invalidateQueries: [["test-canister", "get_user", "user-1"]], }), { wrapper } ) expect(result.current.mutate).toBeInstanceOf(Function) }) }) })