Skip to content

Commit 09e3971

Browse files
authored
Merge pull request #1831 from kleros/fix/sdk-getdispute-answer0
Fix: Ensure that SDK getDispute() returns the RTA/Invalid answer
2 parents e65377f + 364cb02 commit 09e3971

File tree

3 files changed

+229
-1
lines changed

3 files changed

+229
-1
lines changed

kleros-sdk/package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@kleros/kleros-sdk",
3-
"version": "2.1.10",
3+
"version": "2.1.12",
44
"description": "SDK for Kleros version 2",
55
"repository": "git@github.com:kleros/kleros-v2.git",
66
"homepage": "https://github.com/kleros/kleros-v2/tree/master/kleros-sdk#readme",

kleros-sdk/src/utils/getDispute.ts

+11
Original file line numberDiff line numberDiff line change
@@ -56,5 +56,16 @@ export const getDispute = async (disputeParameters: GetDisputeParameters): Promi
5656

5757
const populatedTemplate = populateTemplate(templateData, data);
5858

59+
// Filter out any existing answer with id 0 and add our standard Refuse to Arbitrate option
60+
populatedTemplate.answers = [
61+
{
62+
id: "0x0",
63+
title: "Refuse to Arbitrate / Invalid",
64+
description: "Refuse to Arbitrate / Invalid",
65+
reserved: true,
66+
},
67+
...(populatedTemplate.answers?.filter((answer) => answer.id && Number(answer.id) !== 0) || []),
68+
];
69+
5970
return populatedTemplate;
6071
};

kleros-sdk/test/getDispute.test.ts

+217
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,217 @@
1+
import { describe, it, expect, vi, beforeEach } from "vitest";
2+
import { getDispute } from "../src/utils/getDispute";
3+
import fetchDisputeDetails from "../src/requests/fetchDisputeDetails";
4+
import fetchDisputeTemplateFromId from "../src/requests/fetchDisputeTemplateFromId";
5+
6+
// Mock the dependencies
7+
vi.mock("../src/requests/fetchDisputeDetails");
8+
vi.mock("../src/requests/fetchDisputeTemplateFromId");
9+
10+
describe("getDispute", () => {
11+
const mockDisputeId = 123n;
12+
const mockCoreSubgraph = "https://api.thegraph.com/subgraphs/name/kleros/core";
13+
const mockDtrSubgraph = "https://api.thegraph.com/subgraphs/name/kleros/dtr";
14+
15+
const standardRefuseToArbitrateAnswer = {
16+
id: "0x0",
17+
title: "Refuse to Arbitrate / Invalid",
18+
description: "Refuse to Arbitrate / Invalid",
19+
reserved: true,
20+
};
21+
22+
const mockDisputeDetails = {
23+
dispute: {
24+
templateId: 1,
25+
arbitrated: { id: "0x1234" },
26+
arbitrableChainId: 1,
27+
externalDisputeId: 123,
28+
},
29+
};
30+
31+
beforeEach(() => {
32+
vi.resetAllMocks();
33+
});
34+
35+
it("should add Refuse to Arbitrate option when answers array is empty", async () => {
36+
const mockTemplate = {
37+
disputeTemplate: {
38+
templateData: JSON.stringify({
39+
title: "Test Dispute",
40+
description: "Test Description",
41+
question: "Test Question",
42+
answers: [],
43+
policyURI: "/ipfs/test",
44+
arbitratorChainID: "1",
45+
arbitratorAddress: "0x1234567890123456789012345678901234567890",
46+
version: "1.0.0",
47+
}),
48+
templateDataMappings: "",
49+
},
50+
};
51+
52+
vi.mocked(fetchDisputeDetails).mockResolvedValue(mockDisputeDetails);
53+
vi.mocked(fetchDisputeTemplateFromId).mockResolvedValue(mockTemplate);
54+
55+
const result = await getDispute({
56+
disputeId: mockDisputeId,
57+
coreSubgraph: mockCoreSubgraph,
58+
dtrSubgraph: mockDtrSubgraph,
59+
});
60+
61+
expect(result?.answers).toHaveLength(1);
62+
expect(result?.answers[0]).toEqual(standardRefuseToArbitrateAnswer);
63+
});
64+
65+
it("should add Refuse to Arbitrate option when it doesn't exist in answers", async () => {
66+
const mockTemplate = {
67+
disputeTemplate: {
68+
templateData: JSON.stringify({
69+
title: "Test Dispute",
70+
description: "Test Description",
71+
question: "Test Question",
72+
answers: [
73+
{
74+
id: "0x1",
75+
title: "Yes",
76+
description: "Yes Description",
77+
},
78+
{
79+
id: "0x2",
80+
title: "No",
81+
description: "No Description",
82+
},
83+
],
84+
policyURI: "/ipfs/test",
85+
arbitratorChainID: "1",
86+
arbitratorAddress: "0x1234567890123456789012345678901234567890",
87+
version: "1.0.0",
88+
}),
89+
templateDataMappings: "",
90+
},
91+
};
92+
93+
vi.mocked(fetchDisputeDetails).mockResolvedValue(mockDisputeDetails);
94+
vi.mocked(fetchDisputeTemplateFromId).mockResolvedValue(mockTemplate);
95+
96+
const result = await getDispute({
97+
disputeId: mockDisputeId,
98+
coreSubgraph: mockCoreSubgraph,
99+
dtrSubgraph: mockDtrSubgraph,
100+
});
101+
102+
expect(result?.answers).toHaveLength(3);
103+
expect(result?.answers[0]).toEqual(standardRefuseToArbitrateAnswer);
104+
expect(result?.answers[1].id).toBe("0x1");
105+
expect(result?.answers[2].id).toBe("0x2");
106+
});
107+
108+
it("should overwrite existing answer with id 0x0 or 0x00", async () => {
109+
// Test with 0x0
110+
const mockTemplate0x0 = {
111+
disputeTemplate: {
112+
templateData: JSON.stringify({
113+
title: "Test Dispute",
114+
description: "Test Description",
115+
question: "Test Question",
116+
answers: [
117+
{
118+
id: "0x0",
119+
title: "Custom Refuse Title",
120+
description: "Custom Refuse Description",
121+
reserved: true,
122+
},
123+
{
124+
id: "0x1",
125+
title: "Yes",
126+
description: "Yes Description",
127+
},
128+
],
129+
policyURI: "/ipfs/test",
130+
arbitratorChainID: "1",
131+
arbitratorAddress: "0x1234567890123456789012345678901234567890",
132+
version: "1.0.0",
133+
}),
134+
templateDataMappings: "",
135+
},
136+
};
137+
138+
vi.mocked(fetchDisputeDetails).mockResolvedValue(mockDisputeDetails);
139+
vi.mocked(fetchDisputeTemplateFromId).mockResolvedValue(mockTemplate0x0);
140+
141+
let result = await getDispute({
142+
disputeId: mockDisputeId,
143+
coreSubgraph: mockCoreSubgraph,
144+
dtrSubgraph: mockDtrSubgraph,
145+
});
146+
147+
expect(result?.answers).toHaveLength(2);
148+
expect(result?.answers[0]).toEqual(standardRefuseToArbitrateAnswer);
149+
expect(result?.answers[1].id).toBe("0x1");
150+
151+
// Test with 0x00
152+
const mockTemplate0x00 = {
153+
disputeTemplate: {
154+
templateData: JSON.stringify({
155+
title: "Test Dispute",
156+
description: "Test Description",
157+
question: "Test Question",
158+
answers: [
159+
{
160+
id: "0x00",
161+
title: "Custom Refuse Title",
162+
description: "Custom Refuse Description",
163+
reserved: true,
164+
},
165+
{
166+
id: "0x1",
167+
title: "Yes",
168+
description: "Yes Description",
169+
},
170+
],
171+
policyURI: "/ipfs/test",
172+
arbitratorChainID: "1",
173+
arbitratorAddress: "0x1234567890123456789012345678901234567890",
174+
version: "1.0.0",
175+
}),
176+
templateDataMappings: "",
177+
},
178+
};
179+
180+
vi.mocked(fetchDisputeTemplateFromId).mockResolvedValue(mockTemplate0x00);
181+
182+
result = await getDispute({
183+
disputeId: mockDisputeId,
184+
coreSubgraph: mockCoreSubgraph,
185+
dtrSubgraph: mockDtrSubgraph,
186+
});
187+
188+
expect(result?.answers).toHaveLength(2);
189+
expect(result?.answers[0]).toEqual(standardRefuseToArbitrateAnswer);
190+
expect(result?.answers[1].id).toBe("0x1");
191+
});
192+
193+
it("should throw NotFoundError when dispute details are not found", async () => {
194+
vi.mocked(fetchDisputeDetails).mockResolvedValue({ dispute: null } as any);
195+
196+
await expect(
197+
getDispute({
198+
disputeId: mockDisputeId,
199+
coreSubgraph: mockCoreSubgraph,
200+
dtrSubgraph: mockDtrSubgraph,
201+
})
202+
).rejects.toThrow("Dispute details not found");
203+
});
204+
205+
it("should throw NotFoundError when template is not found", async () => {
206+
vi.mocked(fetchDisputeDetails).mockResolvedValue(mockDisputeDetails);
207+
vi.mocked(fetchDisputeTemplateFromId).mockResolvedValue(undefined);
208+
209+
await expect(
210+
getDispute({
211+
disputeId: mockDisputeId,
212+
coreSubgraph: mockCoreSubgraph,
213+
dtrSubgraph: mockDtrSubgraph,
214+
})
215+
).rejects.toThrow("Template not found");
216+
});
217+
});

0 commit comments

Comments
 (0)