Skip to content

Commit

Permalink
feat: handle requests with no responses after deadline (#91)
Browse files Browse the repository at this point in the history
  • Loading branch information
jahabeebs authored Nov 20, 2024
1 parent 96a84fc commit 811bb8b
Show file tree
Hide file tree
Showing 10 changed files with 401 additions and 185 deletions.
183 changes: 150 additions & 33 deletions packages/automated-dispute/src/services/eboActor.ts

Large diffs are not rendered by default.

14 changes: 7 additions & 7 deletions packages/automated-dispute/tests/guards.spec.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { Caip2ChainId } from "@ebo-agent/shared";
import { UnixTimestamp } from "@ebo-agent/shared";
import { Hex } from "viem";
import { describe, expect, it } from "vitest";

import { isRequestCreatedEvent } from "../src/guards.js";
Expand All @@ -8,18 +9,16 @@ import { DEFAULT_MOCKED_REQUEST_CREATED_DATA } from "./services/eboActor/fixture

describe("isRequestCreatedEvent", () => {
it("returns true when passing a RequestCreatedd event", () => {
const id: RequestId = "0x01";

const event: EboEvent<"RequestCreated"> = {
name: "RequestCreated",
blockNumber: 1n,
logIndex: 1,
requestId: id,
timestamp: BigInt(Date.now()) as UnixTimestamp,
requestId: "0x01" as RequestId,
metadata: {
chainId: "eip155:1" as Caip2ChainId,
epoch: 1n,
requestId: id,
requestId: "0x01" as RequestId,
request: DEFAULT_MOCKED_REQUEST_CREATED_DATA.prophetData,
ipfsHash: "0x01" as Hex,
},
};

Expand All @@ -34,6 +33,7 @@ describe("isRequestCreatedEvent", () => {
name: "ResponseProposed",
blockNumber: 1n,
logIndex: 1,
timestamp: BigInt(Date.now()) as UnixTimestamp,
requestId: request.id,
metadata: {
requestId: request.id,
Expand Down
31 changes: 17 additions & 14 deletions packages/automated-dispute/tests/mocks/eboActor.mocks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -72,10 +72,26 @@ export function buildEboActor(request: Request, logger: ILogger) {
timestamp: BigInt(Date.now()) as UnixTimestamp,
} as unknown as Block<bigint, false, "finalized">);

vi.spyOn(protocolProvider, "finalize").mockResolvedValue(undefined);

const blockNumberRpcUrls = new Map<Caip2ChainId, string[]>([
[chainId, ["http://localhost:8539"]],
]);

const notificationService: NotificationService = {
send: vi.fn().mockResolvedValue(undefined),
sendOrThrow: vi.fn().mockResolvedValue(undefined),
createErrorMessage: vi
.fn()
.mockImplementation((defaultMessage: string, context?: unknown, err?: unknown) => {
return {
title: defaultMessage,
description: err instanceof Error ? err.message : undefined,
};
}),
sendError: vi.fn().mockResolvedValue(undefined),
};

const blockNumberService = new BlockNumberService(
blockNumberRpcUrls,
{
Expand All @@ -88,6 +104,7 @@ export function buildEboActor(request: Request, logger: ILogger) {
},
},
logger,
notificationService,
);

vi.spyOn(blockNumberService, "getEpochBlockNumber").mockResolvedValue(BigInt(12345));
Expand All @@ -96,20 +113,6 @@ export function buildEboActor(request: Request, logger: ILogger) {

const eventProcessingMutex = new Mutex();

const notificationService: NotificationService = {
send: vi.fn().mockResolvedValue(undefined),
sendOrThrow: vi.fn().mockResolvedValue(undefined),
createErrorMessage: vi
.fn()
.mockImplementation((defaultMessage: string, context?: unknown, err?: unknown) => {
return {
title: defaultMessage,
description: err instanceof Error ? err.message : undefined,
};
}),
sendError: vi.fn().mockResolvedValue(undefined),
};

const actor = new EboActor(
{ id, epoch, chainId },
protocolProvider,
Expand Down
16 changes: 11 additions & 5 deletions packages/automated-dispute/tests/services/eboActor.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import {
} from "../../src/exceptions/index.js";
import { EboEvent, Request, RequestId, ResponseId } from "../../src/types/index.js";
import mocks from "../mocks/index.js";
import { DEFAULT_MOCKED_REQUEST_CREATED_DATA } from "../services/eboActor/fixtures.js";
import { DEFAULT_MOCKED_REQUEST_CREATED_DATA } from "./eboActor/fixtures.js";

const logger = mocks.mockLogger();

Expand Down Expand Up @@ -499,7 +499,7 @@ describe("EboActor", () => {

actor["proposeResponse"] = vi.fn().mockRejectedValue(contractError);

const customError = new CustomContractError("SomeError", {
const customError = new CustomContractError("UnknownError", {
shouldNotify: false,
shouldReenqueue: true,
shouldTerminate: false,
Expand Down Expand Up @@ -542,7 +542,9 @@ describe("EboActor", () => {
dispute.prophetData,
);

expect(logger.info).toHaveBeenCalledWith(`Dispute ${dispute.id} escalated.`);
expect(logger.info).toHaveBeenCalledWith(`Dispute ${dispute.id} escalated.`, {
disputeId: dispute.id,
});
});

it("rethrows error when settleDispute fails", async () => {
Expand Down Expand Up @@ -591,7 +593,9 @@ describe("EboActor", () => {
);
expect(escalateDisputeMock).not.toHaveBeenCalled();

expect(logger.info).toHaveBeenCalledWith(`Dispute ${dispute.id} settled.`);
expect(logger.info).toHaveBeenCalledWith(`Dispute ${dispute.id} settled.`, {
disputeId: dispute.id,
});
});

it("settles dispute when amountOfPledgesForDispute < amountOfPledgesAgainstDispute", async () => {
Expand Down Expand Up @@ -622,7 +626,9 @@ describe("EboActor", () => {
);
expect(escalateDisputeMock).not.toHaveBeenCalled();

expect(logger.info).toHaveBeenCalledWith(`Dispute ${dispute.id} settled.`);
expect(logger.info).toHaveBeenCalledWith(`Dispute ${dispute.id} settled.`, {
disputeId: dispute.id,
});
});
});
});
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import { ILogger } from "@ebo-agent/shared";
import { ILogger, UnixTimestamp } from "@ebo-agent/shared";
import { Address } from "viem";
import { describe, expect, it, vi } from "vitest";

import { EboEvent } from "../../../src/types/index.js";
import mocks from "../../mocks/index.ts";
import { DEFAULT_MOCKED_REQUEST_CREATED_DATA } from "./fixtures.ts";
import mocks from "../../mocks/index.js";
import { DEFAULT_MOCKED_REQUEST_CREATED_DATA } from "./fixtures.js";

const logger: ILogger = {
info: vi.fn(),
Expand All @@ -19,14 +19,15 @@ describe("onDisputeEscalated", () => {

it("creates the dispute if it does not exist", async () => {
const dispute = mocks.buildDispute(actorRequest, response, {
decodedData: { status: "Active" },
decodedData: { status: "Escalated" },
});

const event: EboEvent<"DisputeEscalated"> = {
name: "DisputeEscalated",
requestId: actorRequest.id,
blockNumber: 1n,
logIndex: 1,
timestamp: BigInt(Date.now()) as UnixTimestamp,
metadata: {
caller: "0x01" as Address,
disputeId: dispute.id,
Expand All @@ -39,7 +40,15 @@ describe("onDisputeEscalated", () => {
vi.spyOn(registry, "getRequest").mockReturnValue(actorRequest);
vi.spyOn(registry, "getDispute").mockReturnValue(undefined);

const mockAddDispute = vi.spyOn(registry, "addDispute");
const disputesMap: Record<string, any> = {};

const mockAddDispute = vi.spyOn(registry, "addDispute").mockImplementation((newDispute) => {
disputesMap[newDispute.id] = newDispute;
});

vi.spyOn(registry, "getDispute").mockImplementation((id: string) => {
return disputesMap[id];
});

actor.enqueue(event);

Expand All @@ -53,6 +62,15 @@ describe("onDisputeEscalated", () => {
},
}),
);

expect(registry.getDispute(dispute.id)).toEqual(
expect.objectContaining({
id: dispute.id,
decodedData: {
status: "Escalated",
},
}),
);
});

it("logs even when there is a ResponseDisputed event after", async () => {
Expand All @@ -65,6 +83,7 @@ describe("onDisputeEscalated", () => {
requestId: actorRequest.id,
blockNumber: 1n,
logIndex: 1,
timestamp: BigInt(Date.now()) as UnixTimestamp,
metadata: {
caller: "0x01" as Address,
disputeId: dispute.id,
Expand All @@ -77,6 +96,7 @@ describe("onDisputeEscalated", () => {
requestId: actorRequest.id,
blockNumber: disputeEscalatedEvent.blockNumber,
logIndex: disputeEscalatedEvent.logIndex + 1,
timestamp: BigInt(Date.now()) as UnixTimestamp,
metadata: {
disputeId: dispute.id,
responseId: response.id,
Expand All @@ -90,7 +110,15 @@ describe("onDisputeEscalated", () => {
vi.spyOn(registry, "getResponse").mockReturnValue(response);
vi.spyOn(registry, "getDispute").mockReturnValueOnce(undefined).mockReturnValue(dispute);

vi.spyOn(registry, "addDispute").mockImplementation(() => {});
const disputesMap: Record<string, any> = {};

vi.spyOn(registry, "addDispute").mockImplementation((newDispute) => {
disputesMap[newDispute.id] = newDispute;
});

vi.spyOn(registry, "getDispute").mockImplementation((id: string) => {
return disputesMap[id];
});

// Simulating the DisputeEscalated event coming before the ResponseDisputed event
actor.enqueue(disputeEscalatedEvent);
Expand All @@ -100,6 +128,7 @@ describe("onDisputeEscalated", () => {

expect(logger.info).toHaveBeenCalledWith(
`Dispute ${dispute.id} for request ${actorRequest.id} has been escalated.`,
{ disputeId: dispute.id, requestId: actorRequest.id },
);
});

Expand Down
Loading

0 comments on commit 811bb8b

Please sign in to comment.