From f0ba738c5141da100210d58760b46c7753c47dbd Mon Sep 17 00:00:00 2001 From: "chalex.eth" <43524913+chalex-eth@users.noreply.github.com> Date: Wed, 9 Oct 2024 16:49:26 +0200 Subject: [PATCH] Add TaskRegistry and Tests --- contracts/.github/workflows/test.yml | 3 +- contracts/lcov.info | 88 +++++++++++++++++ contracts/src/TaskRegistry.sol | 1 + contracts/test/TaskRegistry.t.sol | 143 +++++++++++++++++++++++++++ contracts/test/TestOwnable.t.sol | 70 +++++++++++++ contracts/test/TestState.sol | 30 ++++++ 6 files changed, 333 insertions(+), 2 deletions(-) create mode 100644 contracts/lcov.info create mode 100644 contracts/test/TaskRegistry.t.sol create mode 100644 contracts/test/TestOwnable.t.sol create mode 100644 contracts/test/TestState.sol diff --git a/contracts/.github/workflows/test.yml b/contracts/.github/workflows/test.yml index 762a296..33fd141 100644 --- a/contracts/.github/workflows/test.yml +++ b/contracts/.github/workflows/test.yml @@ -2,8 +2,7 @@ name: CI on: push: - pull_request: - workflow_dispatch: + env: FOUNDRY_PROFILE: ci diff --git a/contracts/lcov.info b/contracts/lcov.info new file mode 100644 index 0000000..318e545 --- /dev/null +++ b/contracts/lcov.info @@ -0,0 +1,88 @@ +TN: +SF:src/Ownable.sol +FN:35,Ownable. +FNDA:17,Ownable. +DA:36,17 +DA:38,17 +FN:48,Ownable.transferOwnership +FNDA:3,Ownable.transferOwnership +DA:49,3 +DA:50,3 +FN:54,Ownable.acceptOwnership +FNDA:2,Ownable.acceptOwnership +DA:55,2 +BRDA:55,0,0,1 +DA:56,1 +DA:57,1 +DA:58,1 +DA:59,1 +FN:63,Ownable.cancelTransferOwnership +FNDA:1,Ownable.cancelTransferOwnership +DA:64,1 +DA:65,1 +FN:68,Ownable.onlyOwner +FNDA:5,Ownable.onlyOwner +DA:69,5 +BRDA:69,1,0,2 +FNF:5 +FNH:5 +LF:12 +LH:12 +BRF:2 +BRH:2 +end_of_record +TN: +SF:src/TaskRegistry.sol +FN:45,TaskRegistry. +FNDA:13,TaskRegistry. +DA:46,13 +DA:47,13 +FN:54,TaskRegistry.setAggregatorNode +FNDA:2,TaskRegistry.setAggregatorNode +DA:55,1 +FN:58,TaskRegistry.setClientAppRegistry +FNDA:2,TaskRegistry.setClientAppRegistry +DA:59,1 +FN:66,TaskRegistry.createTask +FNDA:8,TaskRegistry.createTask +DA:68,8 +DA:71,8 +BRDA:71,0,0,1 +DA:72,7 +DA:73,7 +DA:74,7 +FN:77,TaskRegistry.respondToTask +FNDA:8,TaskRegistry.respondToTask +DA:79,7 +BRDA:79,1,0,1 +DA:80,6 +BRDA:80,2,0,1 +DA:83,5 +BRDA:83,3,0,1 +DA:84,4 +BRDA:84,4,0,1 +DA:86,3 +DA:87,3 +FN:94,TaskRegistry.onlyAggregatorNode +FNDA:8,TaskRegistry.onlyAggregatorNode +DA:95,8 +BRDA:95,5,0,1 +FNF:6 +FNH:6 +LF:16 +LH:16 +BRF:6 +BRH:6 +end_of_record +TN: +SF:test/TestOwnable.t.sol +FN:10,MockOwnable.updateFlag +FNDA:3,MockOwnable.updateFlag +DA:11,2 +FNF:1 +FNH:1 +LF:1 +LH:1 +BRF:0 +BRH:0 +end_of_record diff --git a/contracts/src/TaskRegistry.sol b/contracts/src/TaskRegistry.sol index 5ea7f79..6807e97 100644 --- a/contracts/src/TaskRegistry.sol +++ b/contracts/src/TaskRegistry.sol @@ -83,6 +83,7 @@ contract TaskRegistry is Ownable { if (tasks[taskId] == TaskStatus.COMPLETED) revert InvalidTaskOperation(); if (tasks[taskId] == TaskStatus.FAILED) revert InvalidTaskOperation(); + tasks[taskId] = status; emit TaskResponded(taskId, status); } diff --git a/contracts/test/TaskRegistry.t.sol b/contracts/test/TaskRegistry.t.sol new file mode 100644 index 0000000..70dfe3f --- /dev/null +++ b/contracts/test/TaskRegistry.t.sol @@ -0,0 +1,143 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity 0.8.26; + +import {Test, console2} from "forge-std/Test.sol"; +import {TaskRegistry} from "../src/TaskRegistry.sol"; +import {Ownable} from "src/Ownable.sol"; +import {TestState} from "./TestState.sol"; + +contract TaskRegistryTest is TestState { + function setUp() public override { + super.setUp(); + } + + function testSetAggregatorNode() public { + address newAggregatorNode = address(5); + + vm.prank(owner); + taskRegistry.setAggregatorNode(newAggregatorNode); + + assertEq(taskRegistry.aggregatorNode(), newAggregatorNode); + } + + function testSetAggregatorNode_RevertWhen_NotOwner() public { + address newAggregatorNode = address(5); + + vm.prank(user); + vm.expectRevert(Ownable.Unauthorized.selector); + taskRegistry.setAggregatorNode(newAggregatorNode); + } + + function testSetClientAppRegistry() public { + address newClientAppRegistry = address(6); + + vm.prank(owner); + taskRegistry.setClientAppRegistry(newClientAppRegistry); + + assertEq(taskRegistry.clientAppRegistry(), newClientAppRegistry); + } + + function testSetClientAppRegistry_RevertWhen_NotOwner() public { + address newClientAppRegistry = address(6); + + vm.prank(user); + vm.expectRevert(Ownable.Unauthorized.selector); + taskRegistry.setClientAppRegistry(newClientAppRegistry); + } + + function testCreateTask() public { + bytes32 agentId = bytes32(uint256(1)); + bytes32 taskId = keccak256(abi.encode(user, agentId, block.timestamp)); + assertEq(uint256(taskRegistry.tasks(taskId)), uint256(TaskRegistry.TaskStatus.EMPTY)); + + vm.prank(user); + taskRegistry.createTask(agentId); + assertEq(uint256(taskRegistry.tasks(taskId)), uint256(TaskRegistry.TaskStatus.PENDING)); + } + + function testCreateTask_RevertWhen_TaskAlreadyExists() public { + bytes32 agentId = bytes32(uint256(1)); + + vm.startPrank(user); + taskRegistry.createTask(agentId); + + vm.expectRevert(TaskRegistry.TaskAlreadyExists.selector); + taskRegistry.createTask(agentId); + vm.stopPrank(); + } + + function testRespondToTask() public { + bytes32 agentId = bytes32(uint256(1)); + + vm.prank(user); + taskRegistry.createTask(agentId); + + bytes32 taskId = keccak256(abi.encode(user, agentId, block.timestamp)); + + vm.prank(aggregatorNode); + + taskRegistry.respondToTask(taskId, TaskRegistry.TaskStatus.COMPLETED); + } + + function testRespondToTask_RevertWhen_NotAggregatorNode() public { + bytes32 agentId = bytes32(uint256(1)); + + vm.prank(user); + taskRegistry.createTask(agentId); + + bytes32 taskId = keccak256(abi.encode(user, agentId, block.timestamp)); + + vm.prank(user); + vm.expectRevert(Ownable.Unauthorized.selector); + taskRegistry.respondToTask(taskId, TaskRegistry.TaskStatus.COMPLETED); + } + + function testRespondToTask_RevertWhen_InvalidStatus() public { + bytes32 agentId = bytes32(uint256(1)); + + vm.prank(user); + taskRegistry.createTask(agentId); + + bytes32 taskId = keccak256(abi.encode(user, agentId, block.timestamp)); + + vm.prank(aggregatorNode); + vm.expectRevert(TaskRegistry.InvalidTaskOperation.selector); + taskRegistry.respondToTask(taskId, TaskRegistry.TaskStatus.EMPTY); + + vm.prank(aggregatorNode); + vm.expectRevert(TaskRegistry.InvalidTaskOperation.selector); + taskRegistry.respondToTask(taskId, TaskRegistry.TaskStatus.PENDING); + } + + function testRespondToTask_RevertWhen_AlreadyRespondedCompleted() public { + bytes32 agentId = bytes32(uint256(1)); + + vm.prank(user); + taskRegistry.createTask(agentId); + + bytes32 taskId = keccak256(abi.encode(user, agentId, block.timestamp)); + + vm.prank(aggregatorNode); + taskRegistry.respondToTask(taskId, TaskRegistry.TaskStatus.COMPLETED); + + vm.prank(aggregatorNode); + vm.expectRevert(TaskRegistry.InvalidTaskOperation.selector); + taskRegistry.respondToTask(taskId, TaskRegistry.TaskStatus.FAILED); + } + + function testRespondToTask_RevertWhen_AlreadyRespondedFailed() public { + bytes32 agentId = bytes32(uint256(1)); + + vm.prank(user); + taskRegistry.createTask(agentId); + + bytes32 taskId = keccak256(abi.encode(user, agentId, block.timestamp)); + + vm.prank(aggregatorNode); + taskRegistry.respondToTask(taskId, TaskRegistry.TaskStatus.FAILED); + + vm.prank(aggregatorNode); + vm.expectRevert(TaskRegistry.InvalidTaskOperation.selector); + taskRegistry.respondToTask(taskId, TaskRegistry.TaskStatus.COMPLETED); + } +} diff --git a/contracts/test/TestOwnable.t.sol b/contracts/test/TestOwnable.t.sol new file mode 100644 index 0000000..558336e --- /dev/null +++ b/contracts/test/TestOwnable.t.sol @@ -0,0 +1,70 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity 0.8.26; + +import "forge-std/Test.sol"; +import {Ownable} from "src/Ownable.sol"; + +contract MockOwnable is Ownable(msg.sender) { + bool public flag; + + function updateFlag() public virtual onlyOwner { + flag = true; + } +} + +contract TestOwnable is Test { + MockOwnable mockOwnable; + address alice; + address bob; + + function setUp() public { + alice = vm.addr(1); + vm.label(alice, "Alice"); + bob = vm.addr(2); + vm.label(bob, "Bob"); + + vm.prank(alice); + mockOwnable = new MockOwnable(); + } + + function test_TransherOwnership() public { + vm.prank(alice); + mockOwnable.transferOwnership(bob); + assertEq(mockOwnable.pendingOwner(), bob); + + vm.prank(bob); + mockOwnable.acceptOwnership(); + assertEq(mockOwnable.owner(), bob); + assertEq(mockOwnable.pendingOwner(), address(0x0)); + + vm.prank(bob); + mockOwnable.updateFlag(); + } + + function test_cancelTransferOwnership() public { + vm.startPrank(alice); + mockOwnable.transferOwnership(bob); + assertEq(mockOwnable.pendingOwner(), bob); + + mockOwnable.cancelTransferOwnership(); + assertEq(mockOwnable.pendingOwner(), address(0x0)); + + mockOwnable.updateFlag(); + } + + function test_RevertWhen_Ownable() public { + vm.startPrank(bob); + vm.expectRevert(Ownable.Unauthorized.selector); + mockOwnable.updateFlag(); + } + + function test_RevertWhen_OnlyPendingOwner() public { + vm.prank(alice); + mockOwnable.transferOwnership(bob); + assertEq(mockOwnable.pendingOwner(), bob); + + vm.prank(alice); + vm.expectRevert(Ownable.Unauthorized.selector); + mockOwnable.acceptOwnership(); + } +} diff --git a/contracts/test/TestState.sol b/contracts/test/TestState.sol new file mode 100644 index 0000000..283a41c --- /dev/null +++ b/contracts/test/TestState.sol @@ -0,0 +1,30 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity 0.8.26; + +import {Test} from "forge-std/Test.sol"; +import {TaskRegistry} from "src/TaskRegistry.sol"; + +contract TestState is Test { + address public owner = makeAddr("owner"); + address public user = makeAddr("user"); + address public aggregatorNode = makeAddr("aggregatorNode"); + address public clientAppRegistry = makeAddr("clientAppRegistry"); + + TaskRegistry public taskRegistry; + + function setUp() public virtual { + vm.startPrank(owner); + taskRegistry = new TaskRegistry(owner, aggregatorNode, clientAppRegistry); + vm.stopPrank(); + vm.label(owner, "owner"); + vm.label(user, "user"); + vm.label(aggregatorNode, "aggregatorNode"); + vm.label(clientAppRegistry, "clientAppRegistry"); + } + + function test_initialState() public view { + assertEq(taskRegistry.owner(), owner); + assertEq(taskRegistry.aggregatorNode(), aggregatorNode); + assertEq(taskRegistry.clientAppRegistry(), clientAppRegistry); + } +}