Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
66 changes: 66 additions & 0 deletions src/hooks/__tests__/useDisputeDetail.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import { renderHook, waitFor } from "@testing-library/react";
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import { server } from "../../mocks/server";
import { http, HttpResponse } from "msw";
import React from "react";
import { useDisputeDetail } from "../useDisputeDetail";

const createWrapper = () => {
const queryClient = new QueryClient({
defaultOptions: {
queries: {
retry: false,
},
},
});

return ({ children }: { children: React.ReactNode }) => (
<QueryClientProvider client={queryClient}>{children}</QueryClientProvider>
);
};

describe("useDisputeDetail", () => {
it("returns correct shape for an OPEN dispute", async () => {
const { result } = renderHook(() => useDisputeDetail("dispute-001"), {
wrapper: createWrapper(),
});

await waitFor(() => expect(result.current.isLoading).toBe(false));
expect(result.current.isError).toBe(false);

const data = result.current.data!;
expect(data.status).toBe("open");
expect(data.escrowOnChainStatus).toBe("HELD");
expect(data.stellarExplorerUrl).toContain("stellar.expert");
expect(data.resolutionTxHash).toBeNull();
});

it("returns correct shape for a RESOLVED dispute", async () => {
const { result } = renderHook(() => useDisputeDetail("dispute-002"), {
wrapper: createWrapper(),
});

await waitFor(() => expect(result.current.isLoading).toBe(false));
expect(result.current.isError).toBe(false);

const data = result.current.data!;
expect(data.status).toBe("resolved");
expect(data.escrowOnChainStatus).toBe("RELEASED");
expect(data.resolutionTxHash).toBe("abc123def456789");
});

it("isNotFound is true on 404", async () => {
server.use(
http.get("/api/disputes/bad-id", () =>
HttpResponse.json({ message: "Not found" }, { status: 404 }),
),
);

const { result } = renderHook(() => useDisputeDetail("bad-id"), {
wrapper: createWrapper(),
});

await waitFor(() => expect(result.current.isError).toBe(true));
expect(result.current.isNotFound).toBe(true);
});
});
34 changes: 34 additions & 0 deletions src/hooks/useDisputeDetail.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { useApiQuery } from "./useApiQuery";

// On-chain enrichment extends the existing dispute shape
export interface DisputeOnChain {
escrowOnChainStatus: string;
stellarExplorerUrl: string;
resolutionTxHash: string | null;
}

export interface DisputeDetail extends DisputeOnChain {
id: string;
adoptionId: string;
raisedBy: string;
reason: string;
description: string;
status: "open" | "under_review" | "resolved" | "closed";
resolution: string | null;
createdAt: string;
updatedAt: string;
}

export function useDisputeDetail(disputeId: string) {
return useApiQuery<DisputeDetail>(
["dispute", disputeId],
async () => {
const res = await fetch(`/api/disputes/${disputeId}`);
if (!res.ok) {
const err = await res.json();
throw Object.assign(new Error(err.message), { status: res.status });
}
return res.json();
},
);
}
Loading
Loading