mirror of
https://github.com/arkorty/B.Tech-Project-III.git
synced 2026-04-19 20:51:49 +00:00
360 lines
12 KiB
TypeScript
360 lines
12 KiB
TypeScript
import { SignerWithAddress } from "@nomicfoundation/hardhat-ethers/signers";
|
|
import { time } from "@nomicfoundation/hardhat-network-helpers";
|
|
import { expect } from "chai";
|
|
import { ethers } from "hardhat";
|
|
import { TaskEscrow } from "../typechain-types";
|
|
|
|
describe("TaskEscrow", function () {
|
|
let taskEscrow: TaskEscrow;
|
|
let cUSDMock: any;
|
|
let owner: SignerWithAddress;
|
|
let requester: SignerWithAddress;
|
|
let worker: SignerWithAddress;
|
|
|
|
const PAYMENT_AMOUNT = ethers.parseEther("10"); // 10 cUSD
|
|
const DURATION_DAYS = 7;
|
|
|
|
beforeEach(async function () {
|
|
[owner, requester, worker] = await ethers.getSigners();
|
|
|
|
// Deploy mock cUSD token
|
|
const MockERC20 = await ethers.getContractFactory("MockERC20");
|
|
cUSDMock = await MockERC20.deploy("Celo Dollar", "cUSD");
|
|
await cUSDMock.waitForDeployment();
|
|
|
|
// Deploy TaskEscrow
|
|
const TaskEscrow = await ethers.getContractFactory("TaskEscrow");
|
|
taskEscrow = await TaskEscrow.deploy(await cUSDMock.getAddress());
|
|
await taskEscrow.waitForDeployment();
|
|
|
|
// Mint cUSD to requester
|
|
await cUSDMock.mint(requester.address, ethers.parseEther("1000"));
|
|
});
|
|
|
|
describe("Deployment", function () {
|
|
it("Should set the correct owner", async function () {
|
|
expect(await taskEscrow.owner()).to.equal(owner.address);
|
|
});
|
|
|
|
it("Should set the correct cUSD address", async function () {
|
|
expect(await taskEscrow.cUSD()).to.equal(await cUSDMock.getAddress());
|
|
});
|
|
|
|
it("Should initialize with zero platform fees", async function () {
|
|
expect(await taskEscrow.platformFeesAccumulated()).to.equal(0);
|
|
});
|
|
});
|
|
|
|
describe("Create Task", function () {
|
|
it("Should create a task successfully", async function () {
|
|
// Approve and create task
|
|
await cUSDMock
|
|
.connect(requester)
|
|
.approve(await taskEscrow.getAddress(), PAYMENT_AMOUNT);
|
|
|
|
await expect(
|
|
taskEscrow.connect(requester).createTask(PAYMENT_AMOUNT, DURATION_DAYS)
|
|
)
|
|
.to.emit(taskEscrow, "TaskCreated")
|
|
.withArgs(
|
|
1,
|
|
requester.address,
|
|
PAYMENT_AMOUNT,
|
|
(await time.latest()) + DURATION_DAYS * 24 * 60 * 60
|
|
);
|
|
|
|
const task = await taskEscrow.getTask(1);
|
|
expect(task.requester).to.equal(requester.address);
|
|
expect(task.paymentAmount).to.equal(PAYMENT_AMOUNT);
|
|
expect(task.status).to.equal(0); // Open
|
|
});
|
|
|
|
it("Should fail with zero payment amount", async function () {
|
|
await expect(
|
|
taskEscrow.connect(requester).createTask(0, DURATION_DAYS)
|
|
).to.be.revertedWith("Payment must be > 0");
|
|
});
|
|
|
|
it("Should fail with invalid duration", async function () {
|
|
await expect(
|
|
taskEscrow.connect(requester).createTask(PAYMENT_AMOUNT, 0)
|
|
).to.be.revertedWith("Invalid duration");
|
|
|
|
await expect(
|
|
taskEscrow.connect(requester).createTask(PAYMENT_AMOUNT, 91)
|
|
).to.be.revertedWith("Invalid duration");
|
|
});
|
|
|
|
it("Should transfer cUSD to contract", async function () {
|
|
await cUSDMock
|
|
.connect(requester)
|
|
.approve(await taskEscrow.getAddress(), PAYMENT_AMOUNT);
|
|
await taskEscrow
|
|
.connect(requester)
|
|
.createTask(PAYMENT_AMOUNT, DURATION_DAYS);
|
|
|
|
expect(await cUSDMock.balanceOf(await taskEscrow.getAddress())).to.equal(
|
|
PAYMENT_AMOUNT
|
|
);
|
|
});
|
|
});
|
|
|
|
describe("Assign Worker", function () {
|
|
beforeEach(async function () {
|
|
await cUSDMock
|
|
.connect(requester)
|
|
.approve(await taskEscrow.getAddress(), PAYMENT_AMOUNT);
|
|
await taskEscrow
|
|
.connect(requester)
|
|
.createTask(PAYMENT_AMOUNT, DURATION_DAYS);
|
|
});
|
|
|
|
it("Should assign worker successfully", async function () {
|
|
await expect(
|
|
taskEscrow.connect(requester).assignWorker(1, worker.address)
|
|
)
|
|
.to.emit(taskEscrow, "WorkerAssigned")
|
|
.withArgs(1, worker.address);
|
|
|
|
const task = await taskEscrow.getTask(1);
|
|
expect(task.worker).to.equal(worker.address);
|
|
expect(task.status).to.equal(1); // InProgress
|
|
});
|
|
|
|
it("Should fail if not requester", async function () {
|
|
await expect(
|
|
taskEscrow.connect(worker).assignWorker(1, worker.address)
|
|
).to.be.revertedWith("Not task requester");
|
|
});
|
|
|
|
it("Should fail with invalid worker address", async function () {
|
|
await expect(
|
|
taskEscrow.connect(requester).assignWorker(1, ethers.ZeroAddress)
|
|
).to.be.revertedWith("Invalid worker address");
|
|
});
|
|
|
|
it("Should fail if requester tries to be worker", async function () {
|
|
await expect(
|
|
taskEscrow.connect(requester).assignWorker(1, requester.address)
|
|
).to.be.revertedWith("Requester cannot be worker");
|
|
});
|
|
});
|
|
|
|
describe("Approve Submission", function () {
|
|
beforeEach(async function () {
|
|
await cUSDMock
|
|
.connect(requester)
|
|
.approve(await taskEscrow.getAddress(), PAYMENT_AMOUNT);
|
|
await taskEscrow
|
|
.connect(requester)
|
|
.createTask(PAYMENT_AMOUNT, DURATION_DAYS);
|
|
await taskEscrow.connect(requester).assignWorker(1, worker.address);
|
|
});
|
|
|
|
it("Should approve and release payment", async function () {
|
|
const workerBalanceBefore = await cUSDMock.balanceOf(worker.address);
|
|
|
|
await taskEscrow.connect(owner).approveSubmission(1);
|
|
|
|
const workerBalanceAfter = await cUSDMock.balanceOf(worker.address);
|
|
const expectedWorkerPayment = (PAYMENT_AMOUNT * 9500n) / 10000n; // 95%
|
|
|
|
expect(workerBalanceAfter - workerBalanceBefore).to.equal(
|
|
expectedWorkerPayment
|
|
);
|
|
|
|
const task = await taskEscrow.getTask(1);
|
|
expect(task.status).to.equal(2); // Completed
|
|
});
|
|
|
|
it("Should accumulate platform fees", async function () {
|
|
await taskEscrow.connect(owner).approveSubmission(1);
|
|
|
|
const expectedFee = (PAYMENT_AMOUNT * 500n) / 10000n; // 5%
|
|
expect(await taskEscrow.platformFeesAccumulated()).to.equal(expectedFee);
|
|
});
|
|
|
|
it("Should fail if not owner", async function () {
|
|
await expect(taskEscrow.connect(requester).approveSubmission(1))
|
|
.to.be.revertedWithCustomError(taskEscrow, "OwnableUnauthorizedAccount")
|
|
.withArgs(requester.address);
|
|
});
|
|
|
|
it("Should fail if task not in progress", async function () {
|
|
await taskEscrow.connect(owner).approveSubmission(1);
|
|
|
|
await expect(
|
|
taskEscrow.connect(owner).approveSubmission(1)
|
|
).to.be.revertedWith("Task not in progress");
|
|
});
|
|
});
|
|
|
|
describe("Reject Submission", function () {
|
|
beforeEach(async function () {
|
|
await cUSDMock
|
|
.connect(requester)
|
|
.approve(await taskEscrow.getAddress(), PAYMENT_AMOUNT);
|
|
await taskEscrow
|
|
.connect(requester)
|
|
.createTask(PAYMENT_AMOUNT, DURATION_DAYS);
|
|
await taskEscrow.connect(requester).assignWorker(1, worker.address);
|
|
});
|
|
|
|
it("Should reject and refund requester", async function () {
|
|
const requesterBalanceBefore = await cUSDMock.balanceOf(
|
|
requester.address
|
|
);
|
|
|
|
await taskEscrow.connect(owner).rejectSubmission(1);
|
|
|
|
const requesterBalanceAfter = await cUSDMock.balanceOf(requester.address);
|
|
expect(requesterBalanceAfter - requesterBalanceBefore).to.equal(
|
|
PAYMENT_AMOUNT
|
|
);
|
|
|
|
const task = await taskEscrow.getTask(1);
|
|
expect(task.status).to.equal(3); // Cancelled
|
|
});
|
|
|
|
it("Should fail if not owner", async function () {
|
|
await expect(
|
|
taskEscrow.connect(requester).rejectSubmission(1)
|
|
).to.be.revertedWithCustomError(taskEscrow, "OwnableUnauthorizedAccount")
|
|
.withArgs(requester.address);
|
|
});
|
|
});
|
|
|
|
describe("Cancel Task", function () {
|
|
beforeEach(async function () {
|
|
await cUSDMock
|
|
.connect(requester)
|
|
.approve(await taskEscrow.getAddress(), PAYMENT_AMOUNT);
|
|
await taskEscrow
|
|
.connect(requester)
|
|
.createTask(PAYMENT_AMOUNT, DURATION_DAYS);
|
|
});
|
|
|
|
it("Should allow requester to cancel open task", async function () {
|
|
const requesterBalanceBefore = await cUSDMock.balanceOf(
|
|
requester.address
|
|
);
|
|
|
|
await taskEscrow.connect(requester).cancelTask(1);
|
|
|
|
const requesterBalanceAfter = await cUSDMock.balanceOf(requester.address);
|
|
expect(requesterBalanceAfter - requesterBalanceBefore).to.equal(
|
|
PAYMENT_AMOUNT
|
|
);
|
|
|
|
const task = await taskEscrow.getTask(1);
|
|
expect(task.status).to.equal(3); // Cancelled
|
|
});
|
|
|
|
it("Should fail if not requester", async function () {
|
|
await expect(taskEscrow.connect(worker).cancelTask(1)).to.be.revertedWith(
|
|
"Not task requester"
|
|
);
|
|
});
|
|
});
|
|
|
|
describe("Expired Task", function () {
|
|
beforeEach(async function () {
|
|
await cUSDMock
|
|
.connect(requester)
|
|
.approve(await taskEscrow.getAddress(), PAYMENT_AMOUNT);
|
|
await taskEscrow
|
|
.connect(requester)
|
|
.createTask(PAYMENT_AMOUNT, DURATION_DAYS);
|
|
});
|
|
|
|
it("Should allow claiming expired task", async function () {
|
|
// Fast forward time
|
|
await time.increase(DURATION_DAYS * 24 * 60 * 60 + 1);
|
|
|
|
const requesterBalanceBefore = await cUSDMock.balanceOf(
|
|
requester.address
|
|
);
|
|
|
|
await taskEscrow.connect(requester).claimExpiredTask(1);
|
|
|
|
const requesterBalanceAfter = await cUSDMock.balanceOf(requester.address);
|
|
expect(requesterBalanceAfter - requesterBalanceBefore).to.equal(
|
|
PAYMENT_AMOUNT
|
|
);
|
|
|
|
const task = await taskEscrow.getTask(1);
|
|
expect(task.status).to.equal(4); // Expired
|
|
});
|
|
|
|
it("Should fail if task not expired", async function () {
|
|
await expect(
|
|
taskEscrow.connect(requester).claimExpiredTask(1)
|
|
).to.be.revertedWith("Task not expired yet");
|
|
});
|
|
});
|
|
|
|
describe("Withdraw Platform Fees", function () {
|
|
beforeEach(async function () {
|
|
await cUSDMock
|
|
.connect(requester)
|
|
.approve(await taskEscrow.getAddress(), PAYMENT_AMOUNT);
|
|
await taskEscrow
|
|
.connect(requester)
|
|
.createTask(PAYMENT_AMOUNT, DURATION_DAYS);
|
|
await taskEscrow.connect(requester).assignWorker(1, worker.address);
|
|
await taskEscrow.connect(owner).approveSubmission(1);
|
|
});
|
|
|
|
it("Should allow owner to withdraw fees", async function () {
|
|
const ownerBalanceBefore = await cUSDMock.balanceOf(owner.address);
|
|
const expectedFee = (PAYMENT_AMOUNT * 500n) / 10000n;
|
|
|
|
await taskEscrow.connect(owner).withdrawPlatformFees();
|
|
|
|
const ownerBalanceAfter = await cUSDMock.balanceOf(owner.address);
|
|
expect(ownerBalanceAfter - ownerBalanceBefore).to.equal(expectedFee);
|
|
expect(await taskEscrow.platformFeesAccumulated()).to.equal(0);
|
|
});
|
|
|
|
it("Should fail if no fees to withdraw", async function () {
|
|
await taskEscrow.connect(owner).withdrawPlatformFees();
|
|
|
|
await expect(
|
|
taskEscrow.connect(owner).withdrawPlatformFees()
|
|
).to.be.revertedWith("No fees to withdraw");
|
|
});
|
|
|
|
it("Should fail if not owner", async function () {
|
|
await expect(
|
|
taskEscrow.connect(requester).withdrawPlatformFees()
|
|
).to.be.revertedWithCustomError(taskEscrow, "OwnableUnauthorizedAccount")
|
|
.withArgs(requester.address);
|
|
});
|
|
});
|
|
|
|
describe("Pause Functionality", function () {
|
|
it("Should allow owner to pause", async function () {
|
|
await taskEscrow.connect(owner).pause();
|
|
expect(await taskEscrow.paused()).to.be.true;
|
|
});
|
|
|
|
it("Should prevent task creation when paused", async function () {
|
|
await taskEscrow.connect(owner).pause();
|
|
|
|
await cUSDMock
|
|
.connect(requester)
|
|
.approve(await taskEscrow.getAddress(), PAYMENT_AMOUNT);
|
|
|
|
await expect(
|
|
taskEscrow.connect(requester).createTask(PAYMENT_AMOUNT, DURATION_DAYS)
|
|
).to.be.revertedWithCustomError(taskEscrow, "EnforcedPause");
|
|
});
|
|
|
|
it("Should allow owner to unpause", async function () {
|
|
await taskEscrow.connect(owner).pause();
|
|
await taskEscrow.connect(owner).unpause();
|
|
expect(await taskEscrow.paused()).to.be.false;
|
|
});
|
|
});
|
|
});
|