refactor(contracts): remove deprecated AIPowerRental contract in favor of bounty system
- Delete AIPowerRental.sol (566 lines) - replaced by AgentBounty.sol - Remove rental agreement system with provider/consumer model - Remove performance metrics and SLA tracking - Remove dispute resolution mechanism - Remove ZK-proof verification for performance - Remove provider/consumer authorization system - Bounty system provides superior developer incentive structure
This commit is contained in:
307
tests/contracts/AgentBounty.test.js
Normal file
307
tests/contracts/AgentBounty.test.js
Normal file
@@ -0,0 +1,307 @@
|
||||
const { expect } = require("chai");
|
||||
const { ethers } = require("hardhat");
|
||||
|
||||
describe("AgentBounty System", function () {
|
||||
let agentBounty, aitbcToken, performanceVerifier;
|
||||
let owner, bountyCreator, agent, arbitrator;
|
||||
|
||||
beforeEach(async function () {
|
||||
// Get signers
|
||||
[owner, bountyCreator, agent, arbitrator] = await ethers.getSigners();
|
||||
|
||||
// Deploy mock AITBC token
|
||||
const MockERC20 = await ethers.getContractFactory("MockERC20");
|
||||
aitbcToken = await MockERC20.deploy("AITBC Token", "AITBC", ethers.utils.parseEther("1000000"));
|
||||
await aitbcToken.deployed();
|
||||
|
||||
// Deploy mock performance verifier
|
||||
const MockPerformanceVerifier = await ethers.getContractFactory("MockPerformanceVerifier");
|
||||
performanceVerifier = await MockPerformanceVerifier.deploy();
|
||||
await performanceVerifier.deployed();
|
||||
|
||||
// Deploy AgentBounty contract
|
||||
const AgentBounty = await ethers.getContractFactory("AgentBounty");
|
||||
agentBounty = await AgentBounty.deploy(
|
||||
aitbcToken.address,
|
||||
performanceVerifier.address
|
||||
);
|
||||
await agentBounty.deployed();
|
||||
|
||||
// Transfer tokens to bounty creator and agent
|
||||
await aitbcToken.transfer(bountyCreator.address, ethers.utils.parseEther("10000"));
|
||||
await aitbcToken.transfer(agent.address, ethers.utils.parseEther("10000"));
|
||||
});
|
||||
|
||||
describe("Bounty Creation", function () {
|
||||
it("Should create a bounty successfully", async function () {
|
||||
const rewardAmount = ethers.utils.parseEther("100");
|
||||
const deadline = Math.floor(Date.now() / 1000) + 86400; // 24 hours from now
|
||||
|
||||
await aitbcToken.connect(bountyCreator).approve(agentBounty.address, rewardAmount);
|
||||
|
||||
const tx = await agentBounty.connect(bountyCreator).createBounty(
|
||||
"Test Bounty",
|
||||
"A test bounty for AI agents",
|
||||
rewardAmount,
|
||||
deadline,
|
||||
90, // min_accuracy
|
||||
3600, // max_response_time
|
||||
10, // max_submissions
|
||||
false, // requires_zk_proof
|
||||
["test", "ai"],
|
||||
"BRONZE",
|
||||
"easy"
|
||||
);
|
||||
|
||||
const receipt = await tx.wait();
|
||||
const event = receipt.events.find(e => e.event === "BountyCreated");
|
||||
|
||||
expect(event.args.bountyId).to.equal(1);
|
||||
expect(event.args.creator).to.equal(bountyCreator.address);
|
||||
expect(event.args.rewardAmount).to.equal(rewardAmount);
|
||||
});
|
||||
|
||||
it("Should fail if reward amount is zero", async function () {
|
||||
const deadline = Math.floor(Date.now() / 1000) + 86400;
|
||||
|
||||
await aitbcToken.connect(bountyCreator).approve(agentBounty.address, ethers.utils.parseEther("100"));
|
||||
|
||||
await expect(
|
||||
agentBounty.connect(bountyCreator).createBounty(
|
||||
"Test Bounty",
|
||||
"Description",
|
||||
0,
|
||||
deadline,
|
||||
90,
|
||||
3600,
|
||||
10,
|
||||
false,
|
||||
["test"],
|
||||
"BRONZE",
|
||||
"easy"
|
||||
)
|
||||
).to.be.revertedWith("Reward amount must be greater than 0");
|
||||
});
|
||||
|
||||
it("Should fail if deadline is in the past", async function () {
|
||||
const pastDeadline = Math.floor(Date.now() / 1000) - 3600; // 1 hour ago
|
||||
|
||||
await aitbcToken.connect(bountyCreator).approve(agentBounty.address, ethers.utils.parseEther("100"));
|
||||
|
||||
await expect(
|
||||
agentBounty.connect(bountyCreator).createBounty(
|
||||
"Test Bounty",
|
||||
"Description",
|
||||
ethers.utils.parseEther("100"),
|
||||
pastDeadline,
|
||||
90,
|
||||
3600,
|
||||
10,
|
||||
false,
|
||||
["test"],
|
||||
"BRONZE",
|
||||
"easy"
|
||||
)
|
||||
).to.be.revertedWith("Deadline must be in the future");
|
||||
});
|
||||
});
|
||||
|
||||
describe("Bounty Submission", function () {
|
||||
let bountyId;
|
||||
|
||||
beforeEach(async function () {
|
||||
const rewardAmount = ethers.utils.parseEther("100");
|
||||
const deadline = Math.floor(Date.now() / 1000) + 86400;
|
||||
|
||||
await aitbcToken.connect(bountyCreator).approve(agentBounty.address, rewardAmount);
|
||||
|
||||
const tx = await agentBounty.connect(bountyCreator).createBounty(
|
||||
"Test Bounty",
|
||||
"Description",
|
||||
rewardAmount,
|
||||
deadline,
|
||||
90,
|
||||
3600,
|
||||
10,
|
||||
false,
|
||||
["test"],
|
||||
"BRONZE",
|
||||
"easy"
|
||||
);
|
||||
|
||||
const receipt = await tx.wait();
|
||||
bountyId = receipt.events.find(e => e.event === "BountyCreated").args.bountyId;
|
||||
});
|
||||
|
||||
it("Should submit a bounty successfully", async function () {
|
||||
const submissionData = "test submission data";
|
||||
|
||||
const tx = await agentBounty.connect(agent).submitBounty(
|
||||
bountyId,
|
||||
submissionData,
|
||||
[]
|
||||
);
|
||||
|
||||
const receipt = await tx.wait();
|
||||
const event = receipt.events.find(e => e.event === "BountySubmitted");
|
||||
|
||||
expect(event.args.bountyId).to.equal(bountyId);
|
||||
expect(event.args.submitter).to.equal(agent.address);
|
||||
expect(event.args.submissionData).to.equal(submissionData);
|
||||
});
|
||||
|
||||
it("Should fail if bounty doesn't exist", async function () {
|
||||
await expect(
|
||||
agentBounty.connect(agent).submitBounty(
|
||||
999,
|
||||
"test data",
|
||||
[]
|
||||
)
|
||||
).to.be.revertedWith("Bounty does not exist");
|
||||
});
|
||||
|
||||
it("Should fail if bounty is expired", async function () {
|
||||
// Fast forward time past deadline
|
||||
await ethers.provider.send("evm_increaseTime", [86400 * 2]); // 2 days
|
||||
await ethers.provider.send("evm_mine");
|
||||
|
||||
await expect(
|
||||
agentBounty.connect(agent).submitBounty(
|
||||
bountyId,
|
||||
"test data",
|
||||
[]
|
||||
)
|
||||
).to.be.revertedWith("Bounty has expired");
|
||||
});
|
||||
});
|
||||
|
||||
describe("Bounty Verification", function () {
|
||||
let bountyId, submissionId;
|
||||
|
||||
beforeEach(async function () {
|
||||
const rewardAmount = ethers.utils.parseEther("100");
|
||||
const deadline = Math.floor(Date.now() / 1000) + 86400;
|
||||
|
||||
await aitbcToken.connect(bountyCreator).approve(agentBounty.address, rewardAmount);
|
||||
|
||||
const tx = await agentBounty.connect(bountyCreator).createBounty(
|
||||
"Test Bounty",
|
||||
"Description",
|
||||
rewardAmount,
|
||||
deadline,
|
||||
90,
|
||||
3600,
|
||||
10,
|
||||
false,
|
||||
["test"],
|
||||
"BRONZE",
|
||||
"easy"
|
||||
);
|
||||
|
||||
const receipt = await tx.wait();
|
||||
bountyId = receipt.events.find(e => e.event === "BountyCreated").args.bountyId;
|
||||
|
||||
const submitTx = await agentBounty.connect(agent).submitBounty(
|
||||
bountyId,
|
||||
"test submission data",
|
||||
[]
|
||||
);
|
||||
|
||||
const submitReceipt = await submitTx.wait();
|
||||
submissionId = submitReceipt.events.find(e => e.event === "BountySubmitted").args.submissionId;
|
||||
});
|
||||
|
||||
it("Should verify a bounty successfully", async function () {
|
||||
// Mock performance verifier to return true
|
||||
await performanceVerifier.setMockResult(true);
|
||||
|
||||
const tx = await agentBounty.verifyBounty(submissionId);
|
||||
const receipt = await tx.wait();
|
||||
const event = receipt.events.find(e => e.event === "BountyVerified");
|
||||
|
||||
expect(event.args.submissionId).to.equal(submissionId);
|
||||
expect(event.args.success).to.be.true;
|
||||
});
|
||||
|
||||
it("Should distribute rewards upon successful verification", async function () {
|
||||
// Mock performance verifier to return true
|
||||
await performanceVerifier.setMockResult(true);
|
||||
|
||||
const initialBalance = await aitbcToken.balanceOf(agent.address);
|
||||
|
||||
await agentBounty.verifyBounty(submissionId);
|
||||
|
||||
const finalBalance = await aitbcToken.balanceOf(agent.address);
|
||||
expect(finalBalance).to.be.gt(initialBalance);
|
||||
});
|
||||
|
||||
it("Should handle failed verification", async function () {
|
||||
// Mock performance verifier to return false
|
||||
await performanceVerifier.setMockResult(false);
|
||||
|
||||
const tx = await agentBounty.verifyBounty(submissionId);
|
||||
const receipt = await tx.wait();
|
||||
const event = receipt.events.find(e => e.event === "BountyVerified");
|
||||
|
||||
expect(event.args.success).to.be.false;
|
||||
});
|
||||
});
|
||||
|
||||
describe("Fee Management", function () {
|
||||
it("Should allow owner to update fees", async function () {
|
||||
const newCreationFee = 75; // 0.75%
|
||||
|
||||
await agentBounty.updateCreationFee(newCreationFee);
|
||||
|
||||
expect(await agentBounty.creationFeePercentage()).to.equal(newCreationFee);
|
||||
});
|
||||
|
||||
it("Should prevent non-owners from updating fees", async function () {
|
||||
await expect(
|
||||
agentBounty.connect(bountyCreator).updateCreationFee(75)
|
||||
).to.be.revertedWith("Ownable: caller is not the owner");
|
||||
});
|
||||
|
||||
it("Should validate fee ranges", async function () {
|
||||
// Test fee too high (over 1000 basis points = 10%)
|
||||
await expect(
|
||||
agentBounty.updateCreationFee(1001)
|
||||
).to.be.revertedWith("Fee cannot exceed 1000 basis points");
|
||||
});
|
||||
});
|
||||
|
||||
describe("Pausability", function () {
|
||||
it("Should allow owner to pause and unpause", async function () {
|
||||
await agentBounty.pause();
|
||||
expect(await agentBounty.paused()).to.be.true;
|
||||
|
||||
await agentBounty.unpause();
|
||||
expect(await agentBounty.paused()).to.be.false;
|
||||
});
|
||||
|
||||
it("Should prevent operations when paused", async function () {
|
||||
await agentBounty.pause();
|
||||
|
||||
const rewardAmount = ethers.utils.parseEther("100");
|
||||
const deadline = Math.floor(Date.now() / 1000) + 86400;
|
||||
|
||||
await aitbcToken.connect(bountyCreator).approve(agentBounty.address, rewardAmount);
|
||||
|
||||
await expect(
|
||||
agentBounty.connect(bountyCreator).createBounty(
|
||||
"Test Bounty",
|
||||
"Description",
|
||||
rewardAmount,
|
||||
deadline,
|
||||
90,
|
||||
3600,
|
||||
10,
|
||||
false,
|
||||
["test"],
|
||||
"BRONZE",
|
||||
"easy"
|
||||
)
|
||||
).to.be.revertedWith("Pausable: paused");
|
||||
});
|
||||
});
|
||||
});
|
||||
331
tests/contracts/AgentStaking.test.js
Normal file
331
tests/contracts/AgentStaking.test.js
Normal file
@@ -0,0 +1,331 @@
|
||||
const { expect } = require("chai");
|
||||
const { ethers } = require("hardhat");
|
||||
|
||||
describe("AgentStaking System", function () {
|
||||
let agentStaking, aitbcToken;
|
||||
let owner, agent, staker1, staker2;
|
||||
|
||||
beforeEach(async function () {
|
||||
// Get signers
|
||||
[owner, agent, staker1, staker2] = await ethers.getSigners();
|
||||
|
||||
// Deploy mock AITBC token
|
||||
const MockERC20 = await ethers.getContractFactory("MockERC20");
|
||||
aitbcToken = await MockERC20.deploy("AITBC Token", "AITBC", ethers.utils.parseEther("1000000"));
|
||||
await aitbcToken.deployed();
|
||||
|
||||
// Deploy AgentStaking contract
|
||||
const AgentStaking = await ethers.getContractFactory("AgentStaking");
|
||||
agentStaking = await AgentStaking.deploy(aitbcToken.address);
|
||||
await agentStaking.deployed();
|
||||
|
||||
// Transfer tokens to stakers
|
||||
await aitbcToken.transfer(staker1.address, ethers.utils.parseEther("10000"));
|
||||
await aitbcToken.transfer(staker2.address, ethers.utils.parseEther("10000"));
|
||||
});
|
||||
|
||||
describe("Agent Registration", function () {
|
||||
it("Should register an agent successfully", async function () {
|
||||
const tx = await agentStaking.connect(agent).registerAgent(
|
||||
"Test Agent",
|
||||
"https://example.com/metadata",
|
||||
["AI", "ML", "NLP"]
|
||||
);
|
||||
|
||||
const receipt = await tx.wait();
|
||||
const event = receipt.events.find(e => e.event === "AgentRegistered");
|
||||
|
||||
expect(event.args.agentAddress).to.equal(agent.address);
|
||||
expect(event.args.name).to.equal("Test Agent");
|
||||
expect(event.args.metadataURI).to.equal("https://example.com/metadata");
|
||||
});
|
||||
|
||||
it("Should fail if agent is already registered", async function () {
|
||||
await agentStaking.connect(agent).registerAgent(
|
||||
"Test Agent",
|
||||
"metadata",
|
||||
["AI"]
|
||||
);
|
||||
|
||||
await expect(
|
||||
agentStaking.connect(agent).registerAgent(
|
||||
"Another Agent",
|
||||
"metadata2",
|
||||
["ML"]
|
||||
)
|
||||
).to.be.revertedWith("Agent already registered");
|
||||
});
|
||||
|
||||
it("Should update agent metadata", async function () {
|
||||
await agentStaking.connect(agent).registerAgent(
|
||||
"Test Agent",
|
||||
"metadata",
|
||||
["AI"]
|
||||
);
|
||||
|
||||
await agentStaking.connect(agent).updateAgentMetadata(
|
||||
"Updated Agent",
|
||||
"https://updated.com/metadata",
|
||||
["AI", "ML", "CV"]
|
||||
);
|
||||
|
||||
const agentInfo = await agentStaking.getAgentInfo(agent.address);
|
||||
expect(agentInfo.name).to.equal("Updated Agent");
|
||||
expect(agentInfo.metadataURI).to.equal("https://updated.com/metadata");
|
||||
});
|
||||
});
|
||||
|
||||
describe("Staking Operations", function () {
|
||||
beforeEach(async function () {
|
||||
await agentStaking.connect(agent).registerAgent(
|
||||
"Test Agent",
|
||||
"metadata",
|
||||
["AI"]
|
||||
);
|
||||
});
|
||||
|
||||
it("Should stake tokens successfully", async function () {
|
||||
const stakeAmount = ethers.utils.parseEther("1000");
|
||||
|
||||
await aitbcToken.connect(staker1).approve(agentStaking.address, stakeAmount);
|
||||
|
||||
const tx = await agentStaking.connect(staker1).stake(agent.address, stakeAmount);
|
||||
const receipt = await tx.wait();
|
||||
const event = receipt.events.find(e => e.event === "TokensStaked");
|
||||
|
||||
expect(event.args.staker).to.equal(staker1.address);
|
||||
expect(event.args.agent).to.equal(agent.address);
|
||||
expect(event.args.amount).to.equal(stakeAmount);
|
||||
});
|
||||
|
||||
it("Should track total staked per agent", async function () {
|
||||
const stakeAmount1 = ethers.utils.parseEther("500");
|
||||
const stakeAmount2 = ethers.utils.parseEther("300");
|
||||
|
||||
await aitbcToken.connect(staker1).approve(agentStaking.address, stakeAmount1);
|
||||
await aitbcToken.connect(staker2).approve(agentStaking.address, stakeAmount2);
|
||||
|
||||
await agentStaking.connect(staker1).stake(agent.address, stakeAmount1);
|
||||
await agentStaking.connect(staker2).stake(agent.address, stakeAmount2);
|
||||
|
||||
const agentInfo = await agentStaking.getAgentInfo(agent.address);
|
||||
expect(agentInfo.totalStaked).to.equal(stakeAmount1.add(stakeAmount2));
|
||||
});
|
||||
|
||||
it("Should unstake tokens successfully", async function () {
|
||||
const stakeAmount = ethers.utils.parseEther("1000");
|
||||
|
||||
await aitbcToken.connect(staker1).approve(agentStaking.address, stakeAmount);
|
||||
await agentStaking.connect(staker1).stake(agent.address, stakeAmount);
|
||||
|
||||
// Fast forward past unstaking delay
|
||||
await ethers.provider.send("evm_increaseTime", [86400 * 7]); // 7 days
|
||||
await ethers.provider.send("evm_mine");
|
||||
|
||||
const initialBalance = await aitbcToken.balanceOf(staker1.address);
|
||||
|
||||
await agentStaking.connect(staker1).unstake(agent.address, stakeAmount);
|
||||
|
||||
const finalBalance = await aitbcToken.balanceOf(staker1.address);
|
||||
expect(finalBalance).to.equal(initialBalance.add(stakeAmount));
|
||||
});
|
||||
|
||||
it("Should fail to unstake before delay period", async function () {
|
||||
const stakeAmount = ethers.utils.parseEther("1000");
|
||||
|
||||
await aitbcToken.connect(staker1).approve(agentStaking.address, stakeAmount);
|
||||
await agentStaking.connect(staker1).stake(agent.address, stakeAmount);
|
||||
|
||||
await expect(
|
||||
agentStaking.connect(staker1).unstake(agent.address, stakeAmount)
|
||||
).to.be.revertedWith("Unstaking delay not met");
|
||||
});
|
||||
|
||||
it("Should fail to unstake more than staked", async function () {
|
||||
const stakeAmount = ethers.utils.parseEther("1000");
|
||||
const unstakeAmount = ethers.utils.parseEther("1500");
|
||||
|
||||
await aitbcToken.connect(staker1).approve(agentStaking.address, stakeAmount);
|
||||
await agentStaking.connect(staker1).stake(agent.address, stakeAmount);
|
||||
|
||||
// Fast forward past unstaking delay
|
||||
await ethers.provider.send("evm_increaseTime", [86400 * 7]);
|
||||
await ethers.provider.send("evm_mine");
|
||||
|
||||
await expect(
|
||||
agentStaking.connect(staker1).unstake(agent.address, unstakeAmount)
|
||||
).to.be.revertedWith("Insufficient staked amount");
|
||||
});
|
||||
});
|
||||
|
||||
describe("Reward Distribution", function () {
|
||||
beforeEach(async function () {
|
||||
await agentStaking.connect(agent).registerAgent(
|
||||
"Test Agent",
|
||||
"metadata",
|
||||
["AI"]
|
||||
);
|
||||
|
||||
const stakeAmount = ethers.utils.parseEther("1000");
|
||||
await aitbcToken.connect(staker1).approve(agentStaking.address, stakeAmount);
|
||||
await agentStaking.connect(staker1).stake(agent.address, stakeAmount);
|
||||
});
|
||||
|
||||
it("Should distribute rewards proportionally", async function () {
|
||||
const rewardAmount = ethers.utils.parseEther("100");
|
||||
|
||||
await aitbcToken.transfer(agentStaking.address, rewardAmount);
|
||||
|
||||
const initialBalance = await aitbcToken.balanceOf(staker1.address);
|
||||
|
||||
await agentStaking.distributeRewards(agent.address, rewardAmount);
|
||||
|
||||
const finalBalance = await aitbcToken.balanceOf(staker1.address);
|
||||
expect(finalBalance).to.equal(initialBalance.add(rewardAmount));
|
||||
});
|
||||
|
||||
it("Should handle multiple stakers proportionally", async function () {
|
||||
// Add second staker
|
||||
const stakeAmount2 = ethers.utils.parseEther("500");
|
||||
await aitbcToken.connect(staker2).approve(agentStaking.address, stakeAmount2);
|
||||
await agentStaking.connect(staker2).stake(agent.address, stakeAmount2);
|
||||
|
||||
const rewardAmount = ethers.utils.parseEther("150");
|
||||
await aitbcToken.transfer(agentStaking.address, rewardAmount);
|
||||
|
||||
const initialBalance1 = await aitbcToken.balanceOf(staker1.address);
|
||||
const initialBalance2 = await aitbcToken.balanceOf(staker2.address);
|
||||
|
||||
await agentStaking.distributeRewards(agent.address, rewardAmount);
|
||||
|
||||
const finalBalance1 = await aitbcToken.balanceOf(staker1.address);
|
||||
const finalBalance2 = await aitbcToken.balanceOf(staker2.address);
|
||||
|
||||
// Staker1 had 1000 tokens, Staker2 had 500 tokens (2:1 ratio)
|
||||
// So rewards should be distributed 100:50
|
||||
expect(finalBalance1).to.equal(initialBalance1.add(ethers.utils.parseEther("100")));
|
||||
expect(finalBalance2).to.equal(initialBalance2.add(ethers.utils.parseEther("50")));
|
||||
});
|
||||
});
|
||||
|
||||
describe("Agent Performance Tracking", function () {
|
||||
beforeEach(async function () {
|
||||
await agentStaking.connect(agent).registerAgent(
|
||||
"Test Agent",
|
||||
"metadata",
|
||||
["AI"]
|
||||
);
|
||||
});
|
||||
|
||||
it("Should record successful performance", async function () {
|
||||
await agentStaking.recordPerformance(agent.address, true, 95);
|
||||
|
||||
const agentInfo = await agentStaking.getAgentInfo(agent.address);
|
||||
expect(agentInfo.successfulTasks).to.equal(1);
|
||||
expect(agentInfo.totalTasks).to.equal(1);
|
||||
expect(agentInfo.successRate).to.equal(10000); // 100% in basis points
|
||||
});
|
||||
|
||||
it("Should record failed performance", async function () {
|
||||
await agentStaking.recordPerformance(agent.address, false, 60);
|
||||
|
||||
const agentInfo = await agentStaking.getAgentInfo(agent.address);
|
||||
expect(agentInfo.successfulTasks).to.equal(0);
|
||||
expect(agentInfo.totalTasks).to.equal(1);
|
||||
expect(agentInfo.successRate).to.equal(0);
|
||||
});
|
||||
|
||||
it("Should calculate success rate correctly", async function () {
|
||||
// Record multiple performances
|
||||
await agentStaking.recordPerformance(agent.address, true, 90);
|
||||
await agentStaking.recordPerformance(agent.address, true, 85);
|
||||
await agentStaking.recordPerformance(agent.address, false, 70);
|
||||
await agentStaking.recordPerformance(agent.address, true, 95);
|
||||
|
||||
const agentInfo = await agentStaking.getAgentInfo(agent.address);
|
||||
expect(agentInfo.successfulTasks).to.equal(3);
|
||||
expect(agentInfo.totalTasks).to.equal(4);
|
||||
expect(agentInfo.successRate).to.equal(7500); // 75% in basis points
|
||||
});
|
||||
|
||||
it("Should update average accuracy", async function () {
|
||||
await agentStaking.recordPerformance(agent.address, true, 90);
|
||||
await agentStaking.recordPerformance(agent.address, true, 80);
|
||||
await agentStaking.recordPerformance(agent.address, true, 85);
|
||||
|
||||
const agentInfo = await agentStaking.getAgentInfo(agent.address);
|
||||
expect(agentInfo.averageAccuracy).to.equal(8500); // 85% in basis points
|
||||
});
|
||||
});
|
||||
|
||||
describe("Slashing Mechanism", function () {
|
||||
beforeEach(async function () {
|
||||
await agentStaking.connect(agent).registerAgent(
|
||||
"Test Agent",
|
||||
"metadata",
|
||||
["AI"]
|
||||
);
|
||||
|
||||
const stakeAmount = ethers.utils.parseEther("1000");
|
||||
await aitbcToken.connect(staker1).approve(agentStaking.address, stakeAmount);
|
||||
await agentStaking.connect(staker1).stake(agent.address, stakeAmount);
|
||||
});
|
||||
|
||||
it("Should slash agent stake for misconduct", async function () {
|
||||
const slashAmount = ethers.utils.parseEther("100");
|
||||
|
||||
const initialContractBalance = await aitbcToken.balanceOf(agentStaking.address);
|
||||
|
||||
await agentStaking.slashStake(agent.address, slashAmount, "Test slash reason");
|
||||
|
||||
const finalContractBalance = await aitbcToken.balanceOf(agentStaking.address);
|
||||
expect(finalContractBalance).to.equal(initialContractBalance.sub(slashAmount));
|
||||
});
|
||||
|
||||
it("Should emit slash event", async function () {
|
||||
const slashAmount = ethers.utils.parseEther("100");
|
||||
|
||||
const tx = await agentStaking.slashStake(agent.address, slashAmount, "Test reason");
|
||||
const receipt = await tx.wait();
|
||||
const event = receipt.events.find(e => e.event === "StakeSlashed");
|
||||
|
||||
expect(event.args.agent).to.equal(agent.address);
|
||||
expect(event.args.amount).to.equal(slashAmount);
|
||||
expect(event.args.reason).to.equal("Test reason");
|
||||
});
|
||||
|
||||
it("Should fail to slash more than total staked", async function () {
|
||||
const totalStaked = await agentStaking.getAgentStakedAmount(agent.address);
|
||||
const slashAmount = totalStaked.add(ethers.utils.parseEther("1"));
|
||||
|
||||
await expect(
|
||||
agentStaking.slashStake(agent.address, slashAmount, "Excessive slash")
|
||||
).to.be.revertedWith("Slash amount exceeds total staked");
|
||||
});
|
||||
});
|
||||
|
||||
describe("Access Control", function () {
|
||||
it("Should only allow owner to set performance recorder", async function () {
|
||||
await expect(
|
||||
agentStaking.connect(staker1).setPerformanceRecorder(staker2.address)
|
||||
).to.be.revertedWith("Ownable: caller is not the owner");
|
||||
});
|
||||
|
||||
it("Should allow owner to set performance recorder", async function () {
|
||||
await agentStaking.setPerformanceRecorder(staker2.address);
|
||||
expect(await agentStaking.performanceRecorder()).to.equal(staker2.address);
|
||||
});
|
||||
|
||||
it("Should only allow performance recorder to record performance", async function () {
|
||||
await agentStaking.connect(agent).registerAgent(
|
||||
"Test Agent",
|
||||
"metadata",
|
||||
["AI"]
|
||||
);
|
||||
|
||||
await expect(
|
||||
agentStaking.connect(staker1).recordPerformance(agent.address, true, 90)
|
||||
).to.be.revertedWith("Not authorized to record performance");
|
||||
});
|
||||
});
|
||||
});
|
||||
415
tests/integration/api_integration.test.js
Normal file
415
tests/integration/api_integration.test.js
Normal file
@@ -0,0 +1,415 @@
|
||||
const { expect } = require("chai");
|
||||
const axios = require("axios");
|
||||
const { ethers } = require("hardhat");
|
||||
|
||||
describe("API Integration Tests", function () {
|
||||
let apiBaseUrl, contracts, signers;
|
||||
|
||||
before(async function () {
|
||||
// Setup contracts and API server
|
||||
apiBaseUrl = process.env.API_BASE_URL || "http://localhost:3001";
|
||||
|
||||
// Deploy mock contracts for testing
|
||||
const MockERC20 = await ethers.getContractFactory("MockERC20");
|
||||
const aitbcToken = await MockERC20.deploy("AITBC Token", "AITBC", ethers.utils.parseEther("1000000"));
|
||||
|
||||
const MockAgentBounty = await ethers.getContractFactory("MockAgentBounty");
|
||||
const agentBounty = await MockAgentBounty.deploy(aitbcToken.address);
|
||||
|
||||
const MockAgentStaking = await ethers.getContractFactory("MockAgentStaking");
|
||||
const agentStaking = await MockAgentStaking.deploy(aitbcToken.address);
|
||||
|
||||
contracts = { aitbcToken, agentBounty, agentStaking };
|
||||
signers = await ethers.getSigners();
|
||||
});
|
||||
|
||||
describe("Bounty API Endpoints", function () {
|
||||
it("GET /api/v1/bounties - Should return active bounties", async function () {
|
||||
try {
|
||||
const response = await axios.get(`${apiBaseUrl}/api/v1/bounties`);
|
||||
|
||||
expect(response.status).to.equal(200);
|
||||
expect(response.data).to.have.property('bounties');
|
||||
expect(Array.isArray(response.data.bounties)).to.be.true;
|
||||
|
||||
// Validate bounty structure
|
||||
if (response.data.bounties.length > 0) {
|
||||
const bounty = response.data.bounties[0];
|
||||
expect(bounty).to.have.property('bounty_id');
|
||||
expect(bounty).to.have.property('title');
|
||||
expect(bounty).to.have.property('reward_amount');
|
||||
expect(bounty).to.have.property('status');
|
||||
expect(bounty).to.have.property('deadline');
|
||||
}
|
||||
} catch (error) {
|
||||
// If API server is not running, skip test
|
||||
this.skip();
|
||||
}
|
||||
});
|
||||
|
||||
it("GET /api/v1/bounties/:id - Should return specific bounty", async function () {
|
||||
try {
|
||||
const bountyId = 1;
|
||||
const response = await axios.get(`${apiBaseUrl}/api/v1/bounties/${bountyId}`);
|
||||
|
||||
expect(response.status).to.equal(200);
|
||||
expect(response.data).to.have.property('bounty_id', bountyId);
|
||||
expect(response.data).to.have.property('title');
|
||||
expect(response.data).to.have.property('description');
|
||||
expect(response.data).to.have.property('reward_amount');
|
||||
} catch (error) {
|
||||
if (error.response?.status === 404) {
|
||||
// Expected if bounty doesn't exist
|
||||
expect(error.response.status).to.equal(404);
|
||||
} else {
|
||||
this.skip();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
it("POST /api/v1/bounties - Should create new bounty", async function () {
|
||||
try {
|
||||
const bountyData = {
|
||||
title: "Test Bounty",
|
||||
description: "A test bounty for API testing",
|
||||
reward_amount: "100",
|
||||
deadline: new Date(Date.now() + 86400000).toISOString(),
|
||||
min_accuracy: 90,
|
||||
max_response_time: 3600,
|
||||
max_submissions: 10,
|
||||
requires_zk_proof: false,
|
||||
tags: ["test", "api"],
|
||||
tier: "BRONZE",
|
||||
difficulty: "easy"
|
||||
};
|
||||
|
||||
const response = await axios.post(`${apiBaseUrl}/api/v1/bounties`, bountyData);
|
||||
|
||||
expect(response.status).to.equal(201);
|
||||
expect(response.data).to.have.property('bounty_id');
|
||||
expect(response.data.title).to.equal(bountyData.title);
|
||||
} catch (error) {
|
||||
if (error.response?.status === 401) {
|
||||
// Expected if authentication is required
|
||||
expect(error.response.status).to.equal(401);
|
||||
} else {
|
||||
this.skip();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
it("GET /api/v1/bounties/categories - Should return bounty categories", async function () {
|
||||
try {
|
||||
const response = await axios.get(`${apiBaseUrl}/api/v1/bounties/categories`);
|
||||
|
||||
expect(response.status).to.equal(200);
|
||||
expect(response.data).to.have.property('categories');
|
||||
expect(Array.isArray(response.data.categories)).to.be.true;
|
||||
} catch (error) {
|
||||
this.skip();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe("Staking API Endpoints", function () {
|
||||
it("GET /api/v1/staking/pools - Should return staking pools", async function () {
|
||||
try {
|
||||
const response = await axios.get(`${apiBaseUrl}/api/v1/staking/pools`);
|
||||
|
||||
expect(response.status).to.equal(200);
|
||||
expect(response.data).to.have.property('pools');
|
||||
expect(Array.isArray(response.data.pools)).to.be.true;
|
||||
|
||||
if (response.data.pools.length > 0) {
|
||||
const pool = response.data.pools[0];
|
||||
expect(pool).to.have.property('agent_address');
|
||||
expect(pool).to.have.property('total_staked');
|
||||
expect(pool).to.have.property('apy');
|
||||
expect(pool).to.have.property('staker_count');
|
||||
}
|
||||
} catch (error) {
|
||||
this.skip();
|
||||
}
|
||||
});
|
||||
|
||||
it("GET /api/v1/staking/agents - Should return registered agents", async function () {
|
||||
try {
|
||||
const response = await axios.get(`${apiBaseUrl}/api/v1/staking/agents`);
|
||||
|
||||
expect(response.status).to.equal(200);
|
||||
expect(response.data).to.have.property('agents');
|
||||
expect(Array.isArray(response.data.agents)).to.be.true;
|
||||
|
||||
if (response.data.agents.length > 0) {
|
||||
const agent = response.data.agents[0];
|
||||
expect(agent).to.have.property('address');
|
||||
expect(agent).to.have.property('name');
|
||||
expect(agent).to.have.property('total_staked');
|
||||
expect(agent).to.have.property('success_rate');
|
||||
}
|
||||
} catch (error) {
|
||||
this.skip();
|
||||
}
|
||||
});
|
||||
|
||||
it("POST /api/v1/staking/stake - Should stake tokens", async function () {
|
||||
try {
|
||||
const stakeData = {
|
||||
agent_address: signers[1].address,
|
||||
amount: "1000"
|
||||
};
|
||||
|
||||
const response = await axios.post(`${apiBaseUrl}/api/v1/staking/stake`, stakeData);
|
||||
|
||||
expect(response.status).to.equal(200);
|
||||
expect(response.data).to.have.property('transaction_hash');
|
||||
expect(response.data).to.have.property('stake_amount');
|
||||
} catch (error) {
|
||||
if (error.response?.status === 401) {
|
||||
expect(error.response.status).to.equal(401);
|
||||
} else {
|
||||
this.skip();
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe("Leaderboard API Endpoints", function () {
|
||||
it("GET /api/v1/leaderboard/developers - Should return developer rankings", async function () {
|
||||
try {
|
||||
const response = await axios.get(`${apiBaseUrl}/api/v1/leaderboard/developers`);
|
||||
|
||||
expect(response.status).to.equal(200);
|
||||
expect(response.data).to.have.property('rankings');
|
||||
expect(Array.isArray(response.data.rankings)).to.be.true;
|
||||
|
||||
if (response.data.rankings.length > 0) {
|
||||
const ranking = response.data.rankings[0];
|
||||
expect(ranking).to.have.property('rank');
|
||||
expect(ranking).to.have.property('address');
|
||||
expect(ranking).to.have.property('total_earned');
|
||||
expect(ranking).to.have.property('bounties_completed');
|
||||
expect(ranking).to.have.property('success_rate');
|
||||
}
|
||||
} catch (error) {
|
||||
this.skip();
|
||||
}
|
||||
});
|
||||
|
||||
it("GET /api/v1/leaderboard/top-performers - Should return top performers", async function () {
|
||||
try {
|
||||
const response = await axios.get(`${apiBaseUrl}/api/v1/leaderboard/top-performers`);
|
||||
|
||||
expect(response.status).to.equal(200);
|
||||
expect(response.data).to.have.property('performers');
|
||||
expect(Array.isArray(response.data.performers)).to.be.true;
|
||||
|
||||
if (response.data.performers.length > 0) {
|
||||
const performer = response.data.performers[0];
|
||||
expect(performer).to.have.property('address');
|
||||
expect(performer).to.have.property('name');
|
||||
expect(performer).to.have.property('performance_score');
|
||||
expect(performer).to.have.property('category');
|
||||
}
|
||||
} catch (error) {
|
||||
this.skip();
|
||||
}
|
||||
});
|
||||
|
||||
it("GET /api/v1/leaderboard/category-stats - Should return category statistics", async function () {
|
||||
try {
|
||||
const response = await axios.get(`${apiBaseUrl}/api/v1/leaderboard/category-stats`);
|
||||
|
||||
expect(response.status).to.equal(200);
|
||||
expect(response.data).to.have.property('categories');
|
||||
expect(Array.isArray(response.data.categories)).to.be.true;
|
||||
|
||||
if (response.data.categories.length > 0) {
|
||||
const category = response.data.categories[0];
|
||||
expect(category).to.have.property('category');
|
||||
expect(category).to.have.property('total_earnings');
|
||||
expect(category).to.have.property('participant_count');
|
||||
expect(category).to.have.property('average_performance');
|
||||
}
|
||||
} catch (error) {
|
||||
this.skip();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe("Ecosystem API Endpoints", function () {
|
||||
it("GET /api/v1/ecosystem/overview - Should return ecosystem overview", async function () {
|
||||
try {
|
||||
const response = await axios.get(`${apiBaseUrl}/api/v1/ecosystem/overview`);
|
||||
|
||||
expect(response.status).to.equal(200);
|
||||
expect(response.data).to.have.property('total_developers');
|
||||
expect(response.data).to.have.property('total_agents');
|
||||
expect(response.data).to.have.property('total_stakers');
|
||||
expect(response.data).to.have.property('total_bounties');
|
||||
expect(response.data).to.have.property('ecosystem_health_score');
|
||||
} catch (error) {
|
||||
this.skip();
|
||||
}
|
||||
});
|
||||
|
||||
it("GET /api/v1/ecosystem/developer-earnings - Should return developer earnings", async function () {
|
||||
try {
|
||||
const response = await axios.get(`${apiBaseUrl}/api/v1/ecosystem/developer-earnings?period=weekly&limit=50`);
|
||||
|
||||
expect(response.status).to.equal(200);
|
||||
expect(Array.isArray(response.data)).to.be.true;
|
||||
|
||||
if (response.data.length > 0) {
|
||||
const earnings = response.data[0];
|
||||
expect(earnings).to.have.property('address');
|
||||
expect(earnings).to.have.property('total_earned');
|
||||
expect(earnings).to.have.property('bounties_completed');
|
||||
expect(earnings).to.have.property('success_rate');
|
||||
}
|
||||
} catch (error) {
|
||||
this.skip();
|
||||
}
|
||||
});
|
||||
|
||||
it("GET /api/v1/ecosystem/treasury-allocation - Should return treasury allocation", async function () {
|
||||
try {
|
||||
const response = await axios.get(`${apiBaseUrl}/api/v1/ecosystem/treasury-allocation`);
|
||||
|
||||
expect(response.status).to.equal(200);
|
||||
expect(Array.isArray(response.data)).to.be.true;
|
||||
|
||||
if (response.data.length > 0) {
|
||||
const allocation = response.data[0];
|
||||
expect(allocation).to.have.property('category');
|
||||
expect(allocation).to.have.property('amount');
|
||||
expect(allocation).to.have.property('percentage');
|
||||
expect(allocation).to.have.property('description');
|
||||
}
|
||||
} catch (error) {
|
||||
this.skip();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe("Error Handling", function () {
|
||||
it("Should return 404 for non-existent endpoints", async function () {
|
||||
try {
|
||||
await axios.get(`${apiBaseUrl}/api/v1/nonexistent`);
|
||||
expect.fail("Should have returned 404");
|
||||
} catch (error) {
|
||||
if (error.response) {
|
||||
expect(error.response.status).to.equal(404);
|
||||
} else {
|
||||
this.skip();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
it("Should validate request parameters", async function () {
|
||||
try {
|
||||
const invalidData = {
|
||||
title: "", // Invalid empty title
|
||||
reward_amount: "-100" // Invalid negative amount
|
||||
};
|
||||
|
||||
await axios.post(`${apiBaseUrl}/api/v1/bounties`, invalidData);
|
||||
expect.fail("Should have returned validation error");
|
||||
} catch (error) {
|
||||
if (error.response) {
|
||||
expect(error.response.status).to.be.oneOf([400, 422]);
|
||||
} else {
|
||||
this.skip();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
it("Should handle rate limiting", async function () {
|
||||
try {
|
||||
// Make multiple rapid requests to test rate limiting
|
||||
const requests = Array(20).fill().map(() =>
|
||||
axios.get(`${apiBaseUrl}/api/v1/bounties`)
|
||||
);
|
||||
|
||||
await Promise.all(requests);
|
||||
// If we get here, rate limiting might not be implemented
|
||||
console.log("Rate limiting not detected");
|
||||
} catch (error) {
|
||||
if (error.response?.status === 429) {
|
||||
expect(error.response.status).to.equal(429);
|
||||
} else {
|
||||
this.skip();
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe("Authentication & Authorization", function () {
|
||||
it("Should require authentication for protected endpoints", async function () {
|
||||
try {
|
||||
await axios.post(`${apiBaseUrl}/api/v1/bounties`, {
|
||||
title: "Test",
|
||||
reward_amount: "100"
|
||||
});
|
||||
expect.fail("Should have required authentication");
|
||||
} catch (error) {
|
||||
if (error.response) {
|
||||
expect(error.response.status).to.equal(401);
|
||||
} else {
|
||||
this.skip();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
it("Should validate API tokens", async function () {
|
||||
try {
|
||||
await axios.get(`${apiBaseUrl}/api/v1/bounties`, {
|
||||
headers: {
|
||||
'Authorization': 'Bearer invalid-token'
|
||||
}
|
||||
});
|
||||
expect.fail("Should have rejected invalid token");
|
||||
} catch (error) {
|
||||
if (error.response) {
|
||||
expect(error.response.status).to.be.oneOf([401, 403]);
|
||||
} else {
|
||||
this.skip();
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe("Performance Tests", function () {
|
||||
it("Should handle concurrent requests", async function () {
|
||||
try {
|
||||
const startTime = Date.now();
|
||||
const requests = Array(10).fill().map(() =>
|
||||
axios.get(`${apiBaseUrl}/api/v1/bounties`)
|
||||
);
|
||||
|
||||
await Promise.all(requests);
|
||||
const endTime = Date.now();
|
||||
const duration = endTime - startTime;
|
||||
|
||||
// Should complete within reasonable time (5 seconds)
|
||||
expect(duration).to.be.lessThan(5000);
|
||||
} catch (error) {
|
||||
this.skip();
|
||||
}
|
||||
});
|
||||
|
||||
it("Should return responses within acceptable time limits", async function () {
|
||||
try {
|
||||
const startTime = Date.now();
|
||||
await axios.get(`${apiBaseUrl}/api/v1/bounties`);
|
||||
const endTime = Date.now();
|
||||
const responseTime = endTime - startTime;
|
||||
|
||||
// Should respond within 1 second
|
||||
expect(responseTime).to.be.lessThan(1000);
|
||||
} catch (error) {
|
||||
this.skip();
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
146
tests/run_all_tests.sh
Executable file
146
tests/run_all_tests.sh
Executable file
@@ -0,0 +1,146 @@
|
||||
#!/bin/bash
|
||||
|
||||
# AITBC Developer Ecosystem - Comprehensive Test Runner
|
||||
# This script runs all test suites for the Developer Ecosystem system
|
||||
|
||||
set -e
|
||||
|
||||
echo "🚀 Starting AITBC Developer Ecosystem Test Suite"
|
||||
echo "=================================================="
|
||||
|
||||
# Colors for output
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
BLUE='\033[0;34m'
|
||||
NC='\033[0m' # No Color
|
||||
|
||||
# Function to print colored output
|
||||
print_status() {
|
||||
echo -e "${BLUE}[INFO]${NC} $1"
|
||||
}
|
||||
|
||||
print_success() {
|
||||
echo -e "${GREEN}[SUCCESS]${NC} $1"
|
||||
}
|
||||
|
||||
print_warning() {
|
||||
echo -e "${YELLOW}[WARNING]${NC} $1"
|
||||
}
|
||||
|
||||
print_error() {
|
||||
echo -e "${RED}[ERROR]${NC} $1"
|
||||
}
|
||||
|
||||
# Function to run tests and capture results
|
||||
run_test_suite() {
|
||||
local test_name=$1
|
||||
local test_command=$2
|
||||
local test_dir=$3
|
||||
|
||||
print_status "Running $test_name tests..."
|
||||
|
||||
cd "$test_dir" || {
|
||||
print_error "Failed to navigate to $test_dir"
|
||||
return 1
|
||||
}
|
||||
|
||||
if eval "$test_command"; then
|
||||
print_success "$test_name tests passed!"
|
||||
return 0
|
||||
else
|
||||
print_error "$test_name tests failed!"
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
# Test results tracking
|
||||
TOTAL_TESTS=0
|
||||
PASSED_TESTS=0
|
||||
FAILED_TESTS=0
|
||||
|
||||
# 1. Smart Contract Unit Tests
|
||||
print_status "📋 Phase 1: Smart Contract Unit Tests"
|
||||
echo "----------------------------------------"
|
||||
|
||||
if run_test_suite "Smart Contract" "npx hardhat test tests/contracts/ --reporter spec" "/home/oib/windsurf/aitbc"; then
|
||||
((PASSED_TESTS++))
|
||||
else
|
||||
((FAILED_TESTS++))
|
||||
fi
|
||||
((TOTAL_TESTS++))
|
||||
|
||||
# 2. API Integration Tests
|
||||
print_status "🔌 Phase 2: API Integration Tests"
|
||||
echo "------------------------------------"
|
||||
|
||||
if run_test_suite "API Integration" "npm test tests/integration/" "/home/oib/windsurf/aitbc"; then
|
||||
((PASSED_TESTS++))
|
||||
else
|
||||
((FAILED_TESTS++))
|
||||
fi
|
||||
((TOTAL_TESTS++))
|
||||
|
||||
# 3. Frontend E2E Tests
|
||||
print_status "🌐 Phase 3: Frontend E2E Tests"
|
||||
echo "---------------------------------"
|
||||
|
||||
# Start the frontend dev server in background
|
||||
print_status "Starting frontend development server..."
|
||||
cd /home/oib/windsurf/aitbc/apps/marketplace-web
|
||||
npm run dev &
|
||||
DEV_SERVER_PID=$!
|
||||
|
||||
# Wait for server to start
|
||||
sleep 10
|
||||
|
||||
# Run E2E tests
|
||||
if run_test_suite "Frontend E2E" "npm run test" "/home/oib/windsurf/aitbc/apps/marketplace-web"; then
|
||||
((PASSED_TESTS++))
|
||||
else
|
||||
((FAILED_TESTS++))
|
||||
fi
|
||||
((TOTAL_TESTS++))
|
||||
|
||||
# Stop the dev server
|
||||
kill $DEV_SERVER_PID 2>/dev/null || true
|
||||
|
||||
# 4. Performance Tests
|
||||
print_status "⚡ Phase 4: Performance Tests"
|
||||
echo "---------------------------------"
|
||||
|
||||
if run_test_suite "Performance" "npm run test:performance" "/home/oib/windsurf/aitbc/tests/load"; then
|
||||
((PASSED_TESTS++))
|
||||
else
|
||||
((FAILED_TESTS++))
|
||||
fi
|
||||
((TOTAL_TESTS++))
|
||||
|
||||
# 5. Security Tests
|
||||
print_status "🔒 Phase 5: Security Tests"
|
||||
echo "-------------------------------"
|
||||
|
||||
if run_test_suite "Security" "npm run test:security" "/home/oib/windsurf/aitbc/tests/security"; then
|
||||
((PASSED_TESTS++))
|
||||
else
|
||||
((FAILED_TESTS++))
|
||||
fi
|
||||
((TOTAL_TESTS++))
|
||||
|
||||
# Generate Test Report
|
||||
echo ""
|
||||
echo "=================================================="
|
||||
echo "📊 TEST SUMMARY"
|
||||
echo "=================================================="
|
||||
echo "Total Test Suites: $TOTAL_TESTS"
|
||||
echo -e "Passed: ${GREEN}$PASSED_TESTS${NC}"
|
||||
echo -e "Failed: ${RED}$FAILED_TESTS${NC}"
|
||||
|
||||
if [ $FAILED_TESTS -eq 0 ]; then
|
||||
print_success "🎉 All test suites passed! Ready for deployment."
|
||||
exit 0
|
||||
else
|
||||
print_error "❌ $FAILED_TESTS test suite(s) failed. Please review the logs above."
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
Reference in New Issue
Block a user