From 268291715c09cde786f8532e7d34073470e00c8e Mon Sep 17 00:00:00 2001 From: jaybuidl Date: Mon, 13 Jan 2025 18:57:48 +0000 Subject: [PATCH 1/4] fix: ensure that getDispute() includes the answer ID 0 --- kleros-sdk/src/utils/getDispute.ts | 13 ++ kleros-sdk/test/getDispute.test.ts | 222 +++++++++++++++++++++++++++++ 2 files changed, 235 insertions(+) create mode 100644 kleros-sdk/test/getDispute.test.ts diff --git a/kleros-sdk/src/utils/getDispute.ts b/kleros-sdk/src/utils/getDispute.ts index c73fa1f24..4631e005c 100644 --- a/kleros-sdk/src/utils/getDispute.ts +++ b/kleros-sdk/src/utils/getDispute.ts @@ -56,5 +56,18 @@ export const getDispute = async (disputeParameters: GetDisputeParameters): Promi const populatedTemplate = populateTemplate(templateData, data); + // Ensure Refuse to Arbitrate option exists + if (!populatedTemplate.answers?.some((answer) => answer.id && Number(answer.id) === 0)) { + populatedTemplate.answers = [ + { + id: "0x0", + title: "Refuse to Arbitrate / Invalid", + description: "Refuse to Arbitrate / Invalid", + reserved: true, + }, + ...(populatedTemplate.answers || []), + ]; + } + return populatedTemplate; }; diff --git a/kleros-sdk/test/getDispute.test.ts b/kleros-sdk/test/getDispute.test.ts new file mode 100644 index 000000000..555b2a4d9 --- /dev/null +++ b/kleros-sdk/test/getDispute.test.ts @@ -0,0 +1,222 @@ +import { describe, it, expect, vi, beforeEach } from "vitest"; +import { getDispute } from "../src/utils/getDispute"; +import fetchDisputeDetails from "../src/requests/fetchDisputeDetails"; +import fetchDisputeTemplateFromId from "../src/requests/fetchDisputeTemplateFromId"; + +// Mock the dependencies +vi.mock("../src/requests/fetchDisputeDetails"); +vi.mock("../src/requests/fetchDisputeTemplateFromId"); + +describe("getDispute", () => { + const mockDisputeId = 123n; + const mockCoreSubgraph = "https://api.thegraph.com/subgraphs/name/kleros/core"; + const mockDtrSubgraph = "https://api.thegraph.com/subgraphs/name/kleros/dtr"; + + const mockDisputeDetails = { + dispute: { + templateId: 1, + arbitrated: { id: "0x1234" }, + arbitrableChainId: 1, + externalDisputeId: 123, + }, + }; + + beforeEach(() => { + vi.resetAllMocks(); + }); + + it("should add Refuse to Arbitrate option when answers array is empty", async () => { + const mockTemplate = { + disputeTemplate: { + templateData: JSON.stringify({ + title: "Test Dispute", + description: "Test Description", + question: "Test Question", + answers: [], + policyURI: "/ipfs/test", + arbitratorChainID: "1", + arbitratorAddress: "0x1234567890123456789012345678901234567890", + version: "1.0.0", + }), + templateDataMappings: "", + }, + }; + + vi.mocked(fetchDisputeDetails).mockResolvedValue(mockDisputeDetails); + vi.mocked(fetchDisputeTemplateFromId).mockResolvedValue(mockTemplate); + + const result = await getDispute({ + disputeId: mockDisputeId, + coreSubgraph: mockCoreSubgraph, + dtrSubgraph: mockDtrSubgraph, + }); + + expect(result?.answers).toHaveLength(1); + expect(result?.answers[0]).toEqual({ + id: "0x0", + title: "Refuse to Arbitrate / Invalid", + description: "Refuse to Arbitrate / Invalid", + reserved: true, + }); + }); + + it("should add Refuse to Arbitrate option when it doesn't exist in answers", async () => { + const mockTemplate = { + disputeTemplate: { + templateData: JSON.stringify({ + title: "Test Dispute", + description: "Test Description", + question: "Test Question", + answers: [ + { + id: "0x1", + title: "Yes", + description: "Yes Description", + }, + { + id: "0x2", + title: "No", + description: "No Description", + }, + ], + policyURI: "/ipfs/test", + arbitratorChainID: "1", + arbitratorAddress: "0x1234567890123456789012345678901234567890", + version: "1.0.0", + }), + templateDataMappings: "", + }, + }; + + vi.mocked(fetchDisputeDetails).mockResolvedValue(mockDisputeDetails); + vi.mocked(fetchDisputeTemplateFromId).mockResolvedValue(mockTemplate); + + const result = await getDispute({ + disputeId: mockDisputeId, + coreSubgraph: mockCoreSubgraph, + dtrSubgraph: mockDtrSubgraph, + }); + + expect(result?.answers).toHaveLength(3); + expect(result?.answers[0]).toEqual({ + id: "0x0", + title: "Refuse to Arbitrate / Invalid", + description: "Refuse to Arbitrate / Invalid", + reserved: true, + }); + expect(result?.answers[1].id).toBe("0x1"); + expect(result?.answers[2].id).toBe("0x2"); + }); + + it("should not add Refuse to Arbitrate option when it already exists with id 0x0", async () => { + const mockTemplate = { + disputeTemplate: { + templateData: JSON.stringify({ + title: "Test Dispute", + description: "Test Description", + question: "Test Question", + answers: [ + { + id: "0x0", + title: "Refuse to Arbitrate / Invalid", + description: "Refuse to Arbitrate / Invalid", + reserved: true, + }, + { + id: "0x1", + title: "Yes", + description: "Yes Description", + }, + ], + policyURI: "/ipfs/test", + arbitratorChainID: "1", + arbitratorAddress: "0x1234567890123456789012345678901234567890", + version: "1.0.0", + }), + templateDataMappings: "", + }, + }; + + vi.mocked(fetchDisputeDetails).mockResolvedValue(mockDisputeDetails); + vi.mocked(fetchDisputeTemplateFromId).mockResolvedValue(mockTemplate); + + const result = await getDispute({ + disputeId: mockDisputeId, + coreSubgraph: mockCoreSubgraph, + dtrSubgraph: mockDtrSubgraph, + }); + + expect(result?.answers).toHaveLength(2); + expect(result?.answers[0].id).toBe("0x0"); + expect(result?.answers[1].id).toBe("0x1"); + }); + + it("should not add Refuse to Arbitrate option when it already exists with id 0x00", async () => { + const mockTemplate = { + disputeTemplate: { + templateData: JSON.stringify({ + title: "Test Dispute", + description: "Test Description", + question: "Test Question", + answers: [ + { + id: "0x00", + title: "Custom Refuse Title", + description: "Custom Refuse Description", + reserved: true, + }, + { + id: "0x1", + title: "Yes", + description: "Yes Description", + }, + ], + policyURI: "/ipfs/test", + arbitratorChainID: "1", + arbitratorAddress: "0x1234567890123456789012345678901234567890", + version: "1.0.0", + }), + templateDataMappings: "", + }, + }; + + vi.mocked(fetchDisputeDetails).mockResolvedValue(mockDisputeDetails); + vi.mocked(fetchDisputeTemplateFromId).mockResolvedValue(mockTemplate); + + const result = await getDispute({ + disputeId: mockDisputeId, + coreSubgraph: mockCoreSubgraph, + dtrSubgraph: mockDtrSubgraph, + }); + + expect(result?.answers).toHaveLength(2); + expect(result?.answers[0].id).toBe("0x00"); + expect(result?.answers[0].title).toBe("Custom Refuse Title"); + expect(result?.answers[1].id).toBe("0x1"); + }); + + it("should throw NotFoundError when dispute details are not found", async () => { + vi.mocked(fetchDisputeDetails).mockResolvedValue({ dispute: null } as any); + + await expect( + getDispute({ + disputeId: mockDisputeId, + coreSubgraph: mockCoreSubgraph, + dtrSubgraph: mockDtrSubgraph, + }) + ).rejects.toThrow("Dispute details not found"); + }); + + it("should throw NotFoundError when template is not found", async () => { + vi.mocked(fetchDisputeDetails).mockResolvedValue(mockDisputeDetails); + vi.mocked(fetchDisputeTemplateFromId).mockResolvedValue(undefined); + + await expect( + getDispute({ + disputeId: mockDisputeId, + coreSubgraph: mockCoreSubgraph, + dtrSubgraph: mockDtrSubgraph, + }) + ).rejects.toThrow("Template not found"); + }); +}); From 6d0cbebc4d254be7730662df991ed2be345cd7d9 Mon Sep 17 00:00:00 2001 From: jaybuidl Date: Mon, 13 Jan 2025 18:58:39 +0000 Subject: [PATCH 2/4] chore(sdk): release @kleros/kleros-sdk@2.1.11 --- kleros-sdk/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kleros-sdk/package.json b/kleros-sdk/package.json index 8b29618ac..3b5b1c25c 100644 --- a/kleros-sdk/package.json +++ b/kleros-sdk/package.json @@ -1,6 +1,6 @@ { "name": "@kleros/kleros-sdk", - "version": "2.1.10", + "version": "2.1.11", "description": "SDK for Kleros version 2", "repository": "git@github.com:kleros/kleros-v2.git", "homepage": "https://github.com/kleros/kleros-v2/tree/master/kleros-sdk#readme", From 2c9ea339b8a10d8dd2eecbee5adcab8c40e7aa70 Mon Sep 17 00:00:00 2001 From: jaybuidl Date: Tue, 14 Jan 2025 17:39:35 +0000 Subject: [PATCH 3/4] fix: unconditionally add the standard answer with id 0 --- kleros-sdk/src/utils/getDispute.ts | 22 ++++++-------- kleros-sdk/test/getDispute.test.ts | 49 ++++++++++++++---------------- 2 files changed, 32 insertions(+), 39 deletions(-) diff --git a/kleros-sdk/src/utils/getDispute.ts b/kleros-sdk/src/utils/getDispute.ts index 4631e005c..ca7a500fe 100644 --- a/kleros-sdk/src/utils/getDispute.ts +++ b/kleros-sdk/src/utils/getDispute.ts @@ -56,18 +56,16 @@ export const getDispute = async (disputeParameters: GetDisputeParameters): Promi const populatedTemplate = populateTemplate(templateData, data); - // Ensure Refuse to Arbitrate option exists - if (!populatedTemplate.answers?.some((answer) => answer.id && Number(answer.id) === 0)) { - populatedTemplate.answers = [ - { - id: "0x0", - title: "Refuse to Arbitrate / Invalid", - description: "Refuse to Arbitrate / Invalid", - reserved: true, - }, - ...(populatedTemplate.answers || []), - ]; - } + // Filter out any existing answer with id 0 and add our standard Refuse to Arbitrate option + populatedTemplate.answers = [ + { + id: "0x0", + title: "Refuse to Arbitrate / Invalid", + description: "Refuse to Arbitrate / Invalid", + reserved: true, + }, + ...(populatedTemplate.answers?.filter((answer) => answer.id && Number(answer.id) !== 0) || []), + ]; return populatedTemplate; }; diff --git a/kleros-sdk/test/getDispute.test.ts b/kleros-sdk/test/getDispute.test.ts index 555b2a4d9..0d1fadc0f 100644 --- a/kleros-sdk/test/getDispute.test.ts +++ b/kleros-sdk/test/getDispute.test.ts @@ -12,6 +12,13 @@ describe("getDispute", () => { const mockCoreSubgraph = "https://api.thegraph.com/subgraphs/name/kleros/core"; const mockDtrSubgraph = "https://api.thegraph.com/subgraphs/name/kleros/dtr"; + const standardRefuseToArbitrateAnswer = { + id: "0x0", + title: "Refuse to Arbitrate / Invalid", + description: "Refuse to Arbitrate / Invalid", + reserved: true, + }; + const mockDisputeDetails = { dispute: { templateId: 1, @@ -52,12 +59,7 @@ describe("getDispute", () => { }); expect(result?.answers).toHaveLength(1); - expect(result?.answers[0]).toEqual({ - id: "0x0", - title: "Refuse to Arbitrate / Invalid", - description: "Refuse to Arbitrate / Invalid", - reserved: true, - }); + expect(result?.answers[0]).toEqual(standardRefuseToArbitrateAnswer); }); it("should add Refuse to Arbitrate option when it doesn't exist in answers", async () => { @@ -98,18 +100,14 @@ describe("getDispute", () => { }); expect(result?.answers).toHaveLength(3); - expect(result?.answers[0]).toEqual({ - id: "0x0", - title: "Refuse to Arbitrate / Invalid", - description: "Refuse to Arbitrate / Invalid", - reserved: true, - }); + expect(result?.answers[0]).toEqual(standardRefuseToArbitrateAnswer); expect(result?.answers[1].id).toBe("0x1"); expect(result?.answers[2].id).toBe("0x2"); }); - it("should not add Refuse to Arbitrate option when it already exists with id 0x0", async () => { - const mockTemplate = { + it("should overwrite existing answer with id 0x0 or 0x00", async () => { + // Test with 0x0 + const mockTemplate0x0 = { disputeTemplate: { templateData: JSON.stringify({ title: "Test Dispute", @@ -118,8 +116,8 @@ describe("getDispute", () => { answers: [ { id: "0x0", - title: "Refuse to Arbitrate / Invalid", - description: "Refuse to Arbitrate / Invalid", + title: "Custom Refuse Title", + description: "Custom Refuse Description", reserved: true, }, { @@ -138,21 +136,20 @@ describe("getDispute", () => { }; vi.mocked(fetchDisputeDetails).mockResolvedValue(mockDisputeDetails); - vi.mocked(fetchDisputeTemplateFromId).mockResolvedValue(mockTemplate); + vi.mocked(fetchDisputeTemplateFromId).mockResolvedValue(mockTemplate0x0); - const result = await getDispute({ + let result = await getDispute({ disputeId: mockDisputeId, coreSubgraph: mockCoreSubgraph, dtrSubgraph: mockDtrSubgraph, }); expect(result?.answers).toHaveLength(2); - expect(result?.answers[0].id).toBe("0x0"); + expect(result?.answers[0]).toEqual(standardRefuseToArbitrateAnswer); expect(result?.answers[1].id).toBe("0x1"); - }); - it("should not add Refuse to Arbitrate option when it already exists with id 0x00", async () => { - const mockTemplate = { + // Test with 0x00 + const mockTemplate0x00 = { disputeTemplate: { templateData: JSON.stringify({ title: "Test Dispute", @@ -180,18 +177,16 @@ describe("getDispute", () => { }, }; - vi.mocked(fetchDisputeDetails).mockResolvedValue(mockDisputeDetails); - vi.mocked(fetchDisputeTemplateFromId).mockResolvedValue(mockTemplate); + vi.mocked(fetchDisputeTemplateFromId).mockResolvedValue(mockTemplate0x00); - const result = await getDispute({ + result = await getDispute({ disputeId: mockDisputeId, coreSubgraph: mockCoreSubgraph, dtrSubgraph: mockDtrSubgraph, }); expect(result?.answers).toHaveLength(2); - expect(result?.answers[0].id).toBe("0x00"); - expect(result?.answers[0].title).toBe("Custom Refuse Title"); + expect(result?.answers[0]).toEqual(standardRefuseToArbitrateAnswer); expect(result?.answers[1].id).toBe("0x1"); }); From 147215a78772b5dfd11418446cad29f3e8e4b25f Mon Sep 17 00:00:00 2001 From: jaybuidl Date: Tue, 14 Jan 2025 17:39:56 +0000 Subject: [PATCH 4/4] chore(sdk): release @kleros/kleros-sdk@2.1.12 --- kleros-sdk/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kleros-sdk/package.json b/kleros-sdk/package.json index 3b5b1c25c..25f8014b4 100644 --- a/kleros-sdk/package.json +++ b/kleros-sdk/package.json @@ -1,6 +1,6 @@ { "name": "@kleros/kleros-sdk", - "version": "2.1.11", + "version": "2.1.12", "description": "SDK for Kleros version 2", "repository": "git@github.com:kleros/kleros-v2.git", "homepage": "https://github.com/kleros/kleros-v2/tree/master/kleros-sdk#readme",