This commit is contained in:
2026-04-05 00:43:23 +05:30
commit 8be37d3e92
425 changed files with 101853 additions and 0 deletions

View File

@@ -0,0 +1,243 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const hardhat_network_helpers_1 = require("@nomicfoundation/hardhat-network-helpers");
const chai_1 = require("chai");
const hardhat_1 = require("hardhat");
describe("TaskEscrow", function () {
let taskEscrow;
let cUSDMock;
let owner;
let requester;
let worker;
const PAYMENT_AMOUNT = hardhat_1.ethers.parseEther("10"); // 10 cUSD
const DURATION_DAYS = 7;
beforeEach(async function () {
[owner, requester, worker] = await hardhat_1.ethers.getSigners();
// Deploy mock cUSD token
const MockERC20 = await hardhat_1.ethers.getContractFactory("MockERC20");
cUSDMock = await MockERC20.deploy("Celo Dollar", "cUSD");
await cUSDMock.waitForDeployment();
// Deploy TaskEscrow
const TaskEscrow = await hardhat_1.ethers.getContractFactory("TaskEscrow");
taskEscrow = await TaskEscrow.deploy(await cUSDMock.getAddress());
await taskEscrow.waitForDeployment();
// Mint cUSD to requester
await cUSDMock.mint(requester.address, hardhat_1.ethers.parseEther("1000"));
});
describe("Deployment", function () {
it("Should set the correct owner", async function () {
(0, chai_1.expect)(await taskEscrow.owner()).to.equal(owner.address);
});
it("Should set the correct cUSD address", async function () {
(0, chai_1.expect)(await taskEscrow.cUSD()).to.equal(await cUSDMock.getAddress());
});
it("Should initialize with zero platform fees", async function () {
(0, chai_1.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 (0, chai_1.expect)(taskEscrow.connect(requester).createTask(PAYMENT_AMOUNT, DURATION_DAYS))
.to.emit(taskEscrow, "TaskCreated")
.withArgs(1, requester.address, PAYMENT_AMOUNT, (await hardhat_network_helpers_1.time.latest()) + DURATION_DAYS * 24 * 60 * 60);
const task = await taskEscrow.getTask(1);
(0, chai_1.expect)(task.requester).to.equal(requester.address);
(0, chai_1.expect)(task.paymentAmount).to.equal(PAYMENT_AMOUNT);
(0, chai_1.expect)(task.status).to.equal(0); // Open
});
it("Should fail with zero payment amount", async function () {
await (0, chai_1.expect)(taskEscrow.connect(requester).createTask(0, DURATION_DAYS)).to.be.revertedWith("Payment must be > 0");
});
it("Should fail with invalid duration", async function () {
await (0, chai_1.expect)(taskEscrow.connect(requester).createTask(PAYMENT_AMOUNT, 0)).to.be.revertedWith("Invalid duration");
await (0, chai_1.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);
(0, chai_1.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 (0, chai_1.expect)(taskEscrow.connect(requester).assignWorker(1, worker.address))
.to.emit(taskEscrow, "WorkerAssigned")
.withArgs(1, worker.address);
const task = await taskEscrow.getTask(1);
(0, chai_1.expect)(task.worker).to.equal(worker.address);
(0, chai_1.expect)(task.status).to.equal(1); // InProgress
});
it("Should fail if not requester", async function () {
await (0, chai_1.expect)(taskEscrow.connect(worker).assignWorker(1, worker.address)).to.be.revertedWith("Not task requester");
});
it("Should fail with invalid worker address", async function () {
await (0, chai_1.expect)(taskEscrow.connect(requester).assignWorker(1, hardhat_1.ethers.ZeroAddress)).to.be.revertedWith("Invalid worker address");
});
it("Should fail if requester tries to be worker", async function () {
await (0, chai_1.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%
(0, chai_1.expect)(workerBalanceAfter - workerBalanceBefore).to.equal(expectedWorkerPayment);
const task = await taskEscrow.getTask(1);
(0, chai_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%
(0, chai_1.expect)(await taskEscrow.platformFeesAccumulated()).to.equal(expectedFee);
});
it("Should fail if not owner", async function () {
await (0, chai_1.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 (0, chai_1.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);
(0, chai_1.expect)(requesterBalanceAfter - requesterBalanceBefore).to.equal(PAYMENT_AMOUNT);
const task = await taskEscrow.getTask(1);
(0, chai_1.expect)(task.status).to.equal(3); // Cancelled
});
it("Should fail if not owner", async function () {
await (0, chai_1.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);
(0, chai_1.expect)(requesterBalanceAfter - requesterBalanceBefore).to.equal(PAYMENT_AMOUNT);
const task = await taskEscrow.getTask(1);
(0, chai_1.expect)(task.status).to.equal(3); // Cancelled
});
it("Should fail if not requester", async function () {
await (0, chai_1.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 hardhat_network_helpers_1.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);
(0, chai_1.expect)(requesterBalanceAfter - requesterBalanceBefore).to.equal(PAYMENT_AMOUNT);
const task = await taskEscrow.getTask(1);
(0, chai_1.expect)(task.status).to.equal(4); // Expired
});
it("Should fail if task not expired", async function () {
await (0, chai_1.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);
(0, chai_1.expect)(ownerBalanceAfter - ownerBalanceBefore).to.equal(expectedFee);
(0, chai_1.expect)(await taskEscrow.platformFeesAccumulated()).to.equal(0);
});
it("Should fail if no fees to withdraw", async function () {
await taskEscrow.connect(owner).withdrawPlatformFees();
await (0, chai_1.expect)(taskEscrow.connect(owner).withdrawPlatformFees()).to.be.revertedWith("No fees to withdraw");
});
it("Should fail if not owner", async function () {
await (0, chai_1.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();
(0, chai_1.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 (0, chai_1.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();
(0, chai_1.expect)(await taskEscrow.paused()).to.be.false;
});
});
});

View File

@@ -0,0 +1,359 @@
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;
});
});
});

View File

@@ -0,0 +1,100 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const chai_1 = require("chai");
const ai_verification_service_1 = require("../src/services/ai-verification.service");
describe('AIVerificationService', function () {
this.timeout(30000); // Increase timeout for API calls
let aiService;
before(() => {
aiService = new ai_verification_service_1.AIVerificationService();
});
describe('Text Verification', function () {
it('should verify a valid text submission', async function () {
const input = {
submissionText: 'The sky is blue because of Rayleigh scattering of sunlight.',
verificationCriteria: 'Answer must explain why the sky is blue with scientific reasoning.',
taskType: 'text_verification',
};
const result = await aiService.verifyTextTask(input);
(0, chai_1.expect)(result).to.have.property('approved');
(0, chai_1.expect)(result).to.have.property('score');
(0, chai_1.expect)(result).to.have.property('reasoning');
(0, chai_1.expect)(result.score).to.be.a('number');
(0, chai_1.expect)(result.score).to.be.greaterThan(0);
(0, chai_1.expect)(result.score).to.be.lessThanOrEqual(100);
});
it('should reject invalid text submission', async function () {
const input = {
submissionText: 'I like pizza',
verificationCriteria: 'Answer must explain photosynthesis in plants.',
taskType: 'text_verification',
};
const result = await aiService.verifyTextTask(input);
(0, chai_1.expect)(result.approved).to.be.false;
(0, chai_1.expect)(result.score).to.be.lessThan(50);
});
it('should cache repeated verification requests', async function () {
const input = {
submissionText: 'Test caching',
verificationCriteria: 'Must contain the word test',
taskType: 'text_verification',
};
const result1 = await aiService.verifyTextTask(input);
const result2 = await aiService.verifyTextTask(input);
(0, chai_1.expect)(result1.timestamp).to.equal(result2.timestamp);
});
});
describe('Survey Verification', function () {
it('should verify survey submissions', async function () {
const answers = {
question1: 'Answer 1',
question2: 'Answer 2',
question3: 'Answer 3',
};
const result = await aiService.verifySurveySubmission(answers, 'Must contain answers for question1, question2, and question3');
(0, chai_1.expect)(result).to.have.property('approved');
(0, chai_1.expect)(result).to.have.property('score');
});
});
describe('Content Moderation', function () {
it('should detect inappropriate content', async function () {
const result = await aiService.verifyContentModeration('This is spam spam spam', 'Flag content that appears to be spam or repetitive');
(0, chai_1.expect)(result).to.have.property('approved');
(0, chai_1.expect)(result).to.have.property('score');
});
it('should approve clean content', async function () {
const result = await aiService.verifyContentModeration('This is a helpful and constructive comment.', 'Flag harmful, abusive, or spam content');
(0, chai_1.expect)(result.approved).to.be.true;
});
});
describe('Health Status', function () {
it('should return service health status', async function () {
const health = await aiService.getHealthStatus();
(0, chai_1.expect)(health).to.have.property('status');
(0, chai_1.expect)(health).to.have.property('rateLimit');
(0, chai_1.expect)(health).to.have.property('cache');
(0, chai_1.expect)(health.status).to.equal('healthy');
});
});
describe('Batch Verification', function () {
it('should batch verify multiple submissions', async function () {
const submissions = [
{
submissionText: 'Valid answer 1',
verificationCriteria: 'Must be a valid answer',
},
{
submissionText: 'Valid answer 2',
verificationCriteria: 'Must be a valid answer',
},
];
const results = await aiService.batchVerify(submissions);
(0, chai_1.expect)(results).to.be.an('array');
(0, chai_1.expect)(results).to.have.lengthOf(2);
results.forEach((result) => {
(0, chai_1.expect)(result).to.have.property('approved');
(0, chai_1.expect)(result).to.have.property('score');
});
});
});
});

View File

@@ -0,0 +1,132 @@
import { expect } from 'chai';
import { AIVerificationService } from '../src/services/ai-verification.service';
import { TextVerificationInput } from '../src/types/ai.types';
describe('AIVerificationService', function () {
this.timeout(30000); // Increase timeout for API calls
let aiService: AIVerificationService;
before(() => {
aiService = new AIVerificationService();
});
describe('Text Verification', function () {
it('should verify a valid text submission', async function () {
const input: TextVerificationInput = {
submissionText: 'The sky is blue because of Rayleigh scattering of sunlight.',
verificationCriteria: 'Answer must explain why the sky is blue with scientific reasoning.',
taskType: 'text_verification',
};
const result = await aiService.verifyTextTask(input);
expect(result).to.have.property('approved');
expect(result).to.have.property('score');
expect(result).to.have.property('reasoning');
expect(result.score).to.be.a('number');
expect(result.score).to.be.greaterThan(0);
expect(result.score).to.be.lessThanOrEqual(100);
});
it('should reject invalid text submission', async function () {
const input: TextVerificationInput = {
submissionText: 'I like pizza',
verificationCriteria: 'Answer must explain photosynthesis in plants.',
taskType: 'text_verification',
};
const result = await aiService.verifyTextTask(input);
expect(result.approved).to.be.false;
expect(result.score).to.be.lessThan(50);
});
it('should cache repeated verification requests', async function () {
const input: TextVerificationInput = {
submissionText: 'Test caching',
verificationCriteria: 'Must contain the word test',
taskType: 'text_verification',
};
const result1 = await aiService.verifyTextTask(input);
const result2 = await aiService.verifyTextTask(input);
expect(result1.timestamp).to.equal(result2.timestamp);
});
});
describe('Survey Verification', function () {
it('should verify survey submissions', async function () {
const answers = {
question1: 'Answer 1',
question2: 'Answer 2',
question3: 'Answer 3',
};
const result = await aiService.verifySurveySubmission(
answers,
'Must contain answers for question1, question2, and question3'
);
expect(result).to.have.property('approved');
expect(result).to.have.property('score');
});
});
describe('Content Moderation', function () {
it('should detect inappropriate content', async function () {
const result = await aiService.verifyContentModeration(
'This is spam spam spam',
'Flag content that appears to be spam or repetitive'
);
expect(result).to.have.property('approved');
expect(result).to.have.property('score');
});
it('should approve clean content', async function () {
const result = await aiService.verifyContentModeration(
'This is a helpful and constructive comment.',
'Flag harmful, abusive, or spam content'
);
expect(result.approved).to.be.true;
});
});
describe('Health Status', function () {
it('should return service health status', async function () {
const health = await aiService.getHealthStatus();
expect(health).to.have.property('status');
expect(health).to.have.property('rateLimit');
expect(health).to.have.property('cache');
expect(health.status).to.equal('healthy');
});
});
describe('Batch Verification', function () {
it('should batch verify multiple submissions', async function () {
const submissions: TextVerificationInput[] = [
{
submissionText: 'Valid answer 1',
verificationCriteria: 'Must be a valid answer',
},
{
submissionText: 'Valid answer 2',
verificationCriteria: 'Must be a valid answer',
},
];
const results = await aiService.batchVerify(submissions);
expect(results).to.be.an('array');
expect(results).to.have.lengthOf(2);
results.forEach((result) => {
expect(result).to.have.property('approved');
expect(result).to.have.property('score');
});
});
});
});

View File

@@ -0,0 +1,173 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const chai_1 = require("chai");
const content_moderation_service_1 = require("../src/services/content-moderation.service");
const moderation_types_1 = require("../src/types/moderation.types");
describe('ContentModerationService', function () {
this.timeout(60000); // Increase timeout for API calls
describe('Safe Content', function () {
it('should approve clean, normal content', async function () {
const result = await content_moderation_service_1.contentModerationService.moderateSubmission({
content: 'This is a helpful and constructive comment about the task.',
});
(0, chai_1.expect)(result.flagged).to.be.false;
(0, chai_1.expect)(result.action).to.equal(moderation_types_1.ModerationAction.APPROVE);
});
it('should approve simple responses', async function () {
const result = await content_moderation_service_1.contentModerationService.moderateSubmission({
content: 'Yes',
});
(0, chai_1.expect)(result.action).to.equal(moderation_types_1.ModerationAction.APPROVE);
});
});
describe('Spam Detection', function () {
it('should detect repetitive spam content', async function () {
const result = await content_moderation_service_1.contentModerationService.moderateSubmission({
content: 'Buy now! Click here! Limited time offer! Buy now! Act fast!',
});
(0, chai_1.expect)(result.flagged).to.be.true;
(0, chai_1.expect)(result.categories.spam.detected).to.be.true;
});
it('should detect bot-generated nonsense', async function () {
const result = await content_moderation_service_1.contentModerationService.moderateSubmission({
content: 'asdfghjkl qwertyuiop zxcvbnm asdfghjkl qwertyuiop',
});
(0, chai_1.expect)(result.flagged).to.be.true;
});
it('should detect character repetition spam', async function () {
const result = await content_moderation_service_1.contentModerationService.moderateSubmission({
content: 'aaaaaaaaaaaaaaaaaa',
});
(0, chai_1.expect)(result.flagged).to.be.true;
(0, chai_1.expect)(result.categories.spam.detected).to.be.true;
});
});
describe('Toxic Content Detection', function () {
it('should detect offensive language', async function () {
const result = await content_moderation_service_1.contentModerationService.moderateSubmission({
content: 'You are stupid and worthless, idiot!',
});
(0, chai_1.expect)(result.flagged).to.be.true;
(0, chai_1.expect)(result.categories.toxic.detected).to.be.true;
});
it('should detect aggressive language', async function () {
const result = await content_moderation_service_1.contentModerationService.moderateSubmission({
content: 'I hate you so much, you are the worst person ever.',
});
(0, chai_1.expect)(result.flagged).to.be.true;
(0, chai_1.expect)(result.categories.toxic.detected).to.be.true;
});
});
describe('Hate Speech Detection', function () {
it('should detect discriminatory content', async function () {
const result = await content_moderation_service_1.contentModerationService.moderateSubmission({
content: 'All [ethnic group] are criminals and should be deported.',
});
(0, chai_1.expect)(result.flagged).to.be.true;
(0, chai_1.expect)(result.categories.hate_speech.detected).to.be.true;
});
});
describe('Fraud Detection', function () {
it('should detect fake data attempts', async function () {
const result = await content_moderation_service_1.contentModerationService.moderateSubmission({
content: 'Random answer random answer random answer to get paid quickly.',
});
(0, chai_1.expect)(result.flagged).to.be.true;
});
it('should detect copy-paste attempts', async function () {
const result = await content_moderation_service_1.contentModerationService.moderateSubmission({
content: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Lorem ipsum.',
});
(0, chai_1.expect)(result.flagged).to.be.true;
});
});
describe('Inappropriate Content Detection', function () {
it('should detect sexual content', async function () {
const result = await content_moderation_service_1.contentModerationService.moderateSubmission({
content: 'Explicit sexual content here [inappropriate text]',
});
(0, chai_1.expect)(result.flagged).to.be.true;
(0, chai_1.expect)(result.categories.inappropriate.detected).to.be.true;
});
it('should detect violent content', async function () {
const result = await content_moderation_service_1.contentModerationService.moderateSubmission({
content: 'Detailed description of violence and harm to others.',
});
(0, chai_1.expect)(result.flagged).to.be.true;
});
});
describe('Edge Cases', function () {
it('should handle sarcasm appropriately', async function () {
const result = await content_moderation_service_1.contentModerationService.moderateSubmission({
content: 'Oh great, another wonderful task. How amazing!',
context: {
taskType: 'survey',
},
});
// Sarcasm might be flagged but should not be auto-rejected
if (result.flagged) {
(0, chai_1.expect)(result.action).to.not.equal(moderation_types_1.ModerationAction.AUTO_REJECT);
}
});
it('should handle educational content about sensitive topics', async function () {
const result = await content_moderation_service_1.contentModerationService.moderateSubmission({
content: 'This historical article discusses discrimination in the 1960s.',
});
// Educational content should be approved or flagged for review, not auto-rejected
(0, chai_1.expect)(result.action).to.not.equal(moderation_types_1.ModerationAction.AUTO_REJECT);
});
it('should handle quotes and citations', async function () {
const result = await content_moderation_service_1.contentModerationService.moderateSubmission({
content: 'As quoted in the article: "controversial statement here"',
});
// Quotes should be evaluated in context
(0, chai_1.expect)(result).to.have.property('action');
});
it('should handle empty content', async function () {
const result = await content_moderation_service_1.contentModerationService.moderateSubmission({
content: '',
});
(0, chai_1.expect)(result.flagged).to.be.true;
});
it('should handle very long content', async function () {
const longContent = 'This is a test. '.repeat(100);
const result = await content_moderation_service_1.contentModerationService.moderateSubmission({
content: longContent,
});
(0, chai_1.expect)(result).to.have.property('action');
});
});
describe('Blocklist Functionality', function () {
it('should instantly reject blocklisted pharmaceutical spam', async function () {
const result = await content_moderation_service_1.contentModerationService.moderateSubmission({
content: 'Buy cheap viagra online now!',
});
(0, chai_1.expect)(result.flagged).to.be.true;
(0, chai_1.expect)(result.action).to.equal(moderation_types_1.ModerationAction.AUTO_REJECT);
});
});
describe('Batch Moderation', function () {
it('should moderate multiple submissions', async function () {
const inputs = [
{ content: 'This is safe content' },
{ content: 'This is spam spam spam spam' },
{ content: 'Another safe comment' },
];
const results = await content_moderation_service_1.contentModerationService.batchModerate(inputs);
(0, chai_1.expect)(results).to.have.lengthOf(3);
(0, chai_1.expect)(results[0].action).to.equal(moderation_types_1.ModerationAction.APPROVE);
(0, chai_1.expect)(results[1].flagged).to.be.true;
(0, chai_1.expect)(results[2].action).to.equal(moderation_types_1.ModerationAction.APPROVE);
});
});
describe('Statistics', function () {
it('should return moderation statistics', function () {
const stats = content_moderation_service_1.contentModerationService.getStats();
(0, chai_1.expect)(stats).to.have.property('total');
(0, chai_1.expect)(stats).to.have.property('approved');
(0, chai_1.expect)(stats).to.have.property('flagged');
(0, chai_1.expect)(stats).to.have.property('rejected');
(0, chai_1.expect)(stats).to.have.property('byCategory');
});
});
});

View File

@@ -0,0 +1,214 @@
import { expect } from 'chai';
import { contentModerationService } from '../src/services/content-moderation.service';
import { ModerationAction } from '../src/types/moderation.types';
describe('ContentModerationService', function () {
this.timeout(60000); // Increase timeout for API calls
describe('Safe Content', function () {
it('should approve clean, normal content', async function () {
const result = await contentModerationService.moderateSubmission({
content: 'This is a helpful and constructive comment about the task.',
});
expect(result.flagged).to.be.false;
expect(result.action).to.equal(ModerationAction.APPROVE);
});
it('should approve simple responses', async function () {
const result = await contentModerationService.moderateSubmission({
content: 'Yes',
});
expect(result.action).to.equal(ModerationAction.APPROVE);
});
});
describe('Spam Detection', function () {
it('should detect repetitive spam content', async function () {
const result = await contentModerationService.moderateSubmission({
content: 'Buy now! Click here! Limited time offer! Buy now! Act fast!',
});
expect(result.flagged).to.be.true;
expect(result.categories.spam.detected).to.be.true;
});
it('should detect bot-generated nonsense', async function () {
const result = await contentModerationService.moderateSubmission({
content: 'asdfghjkl qwertyuiop zxcvbnm asdfghjkl qwertyuiop',
});
expect(result.flagged).to.be.true;
});
it('should detect character repetition spam', async function () {
const result = await contentModerationService.moderateSubmission({
content: 'aaaaaaaaaaaaaaaaaa',
});
expect(result.flagged).to.be.true;
expect(result.categories.spam.detected).to.be.true;
});
});
describe('Toxic Content Detection', function () {
it('should detect offensive language', async function () {
const result = await contentModerationService.moderateSubmission({
content: 'You are stupid and worthless, idiot!',
});
expect(result.flagged).to.be.true;
expect(result.categories.toxic.detected).to.be.true;
});
it('should detect aggressive language', async function () {
const result = await contentModerationService.moderateSubmission({
content: 'I hate you so much, you are the worst person ever.',
});
expect(result.flagged).to.be.true;
expect(result.categories.toxic.detected).to.be.true;
});
});
describe('Hate Speech Detection', function () {
it('should detect discriminatory content', async function () {
const result = await contentModerationService.moderateSubmission({
content: 'All [ethnic group] are criminals and should be deported.',
});
expect(result.flagged).to.be.true;
expect(result.categories.hate_speech.detected).to.be.true;
});
});
describe('Fraud Detection', function () {
it('should detect fake data attempts', async function () {
const result = await contentModerationService.moderateSubmission({
content: 'Random answer random answer random answer to get paid quickly.',
});
expect(result.flagged).to.be.true;
});
it('should detect copy-paste attempts', async function () {
const result = await contentModerationService.moderateSubmission({
content: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Lorem ipsum.',
});
expect(result.flagged).to.be.true;
});
});
describe('Inappropriate Content Detection', function () {
it('should detect sexual content', async function () {
const result = await contentModerationService.moderateSubmission({
content: 'Explicit sexual content here [inappropriate text]',
});
expect(result.flagged).to.be.true;
expect(result.categories.inappropriate.detected).to.be.true;
});
it('should detect violent content', async function () {
const result = await contentModerationService.moderateSubmission({
content: 'Detailed description of violence and harm to others.',
});
expect(result.flagged).to.be.true;
});
});
describe('Edge Cases', function () {
it('should handle sarcasm appropriately', async function () {
const result = await contentModerationService.moderateSubmission({
content: 'Oh great, another wonderful task. How amazing!',
context: {
taskType: 'survey',
},
});
// Sarcasm might be flagged but should not be auto-rejected
if (result.flagged) {
expect(result.action).to.not.equal(ModerationAction.AUTO_REJECT);
}
});
it('should handle educational content about sensitive topics', async function () {
const result = await contentModerationService.moderateSubmission({
content: 'This historical article discusses discrimination in the 1960s.',
});
// Educational content should be approved or flagged for review, not auto-rejected
expect(result.action).to.not.equal(ModerationAction.AUTO_REJECT);
});
it('should handle quotes and citations', async function () {
const result = await contentModerationService.moderateSubmission({
content: 'As quoted in the article: "controversial statement here"',
});
// Quotes should be evaluated in context
expect(result).to.have.property('action');
});
it('should handle empty content', async function () {
const result = await contentModerationService.moderateSubmission({
content: '',
});
expect(result.flagged).to.be.true;
});
it('should handle very long content', async function () {
const longContent = 'This is a test. '.repeat(100);
const result = await contentModerationService.moderateSubmission({
content: longContent,
});
expect(result).to.have.property('action');
});
});
describe('Blocklist Functionality', function () {
it('should instantly reject blocklisted pharmaceutical spam', async function () {
const result = await contentModerationService.moderateSubmission({
content: 'Buy cheap viagra online now!',
});
expect(result.flagged).to.be.true;
expect(result.action).to.equal(ModerationAction.AUTO_REJECT);
});
});
describe('Batch Moderation', function () {
it('should moderate multiple submissions', async function () {
const inputs = [
{ content: 'This is safe content' },
{ content: 'This is spam spam spam spam' },
{ content: 'Another safe comment' },
];
const results = await contentModerationService.batchModerate(inputs);
expect(results).to.have.lengthOf(3);
expect(results[0].action).to.equal(ModerationAction.APPROVE);
expect(results[1].flagged).to.be.true;
expect(results[2].action).to.equal(ModerationAction.APPROVE);
});
});
describe('Statistics', function () {
it('should return moderation statistics', function () {
const stats = contentModerationService.getStats();
expect(stats).to.have.property('total');
expect(stats).to.have.property('approved');
expect(stats).to.have.property('flagged');
expect(stats).to.have.property('rejected');
expect(stats).to.have.property('byCategory');
});
});
});