chore: remove outdated documentation and reference files
Some checks failed
AITBC CI/CD Pipeline / lint-and-test (3.11) (push) Has been cancelled
AITBC CI/CD Pipeline / lint-and-test (3.12) (push) Has been cancelled
AITBC CI/CD Pipeline / lint-and-test (3.13) (push) Has been cancelled
AITBC CI/CD Pipeline / test-cli (push) Has been cancelled
AITBC CI/CD Pipeline / test-services (push) Has been cancelled
AITBC CI/CD Pipeline / test-production-services (push) Has been cancelled
AITBC CI/CD Pipeline / security-scan (push) Has been cancelled
AITBC CI/CD Pipeline / build (push) Has been cancelled
AITBC CI/CD Pipeline / deploy-staging (push) Has been cancelled
AITBC CI/CD Pipeline / deploy-production (push) Has been cancelled
AITBC CI/CD Pipeline / performance-test (push) Has been cancelled
AITBC CI/CD Pipeline / docs (push) Has been cancelled
AITBC CI/CD Pipeline / release (push) Has been cancelled
AITBC CI/CD Pipeline / notify (push) Has been cancelled
Security Scanning / Bandit Security Scan (apps/coordinator-api/src) (push) Has been cancelled
Security Scanning / Bandit Security Scan (cli/aitbc_cli) (push) Has been cancelled
Security Scanning / Bandit Security Scan (packages/py/aitbc-core/src) (push) Has been cancelled
Security Scanning / Bandit Security Scan (packages/py/aitbc-crypto/src) (push) Has been cancelled
Security Scanning / Bandit Security Scan (packages/py/aitbc-sdk/src) (push) Has been cancelled
Security Scanning / Bandit Security Scan (tests) (push) Has been cancelled
Security Scanning / CodeQL Security Analysis (javascript) (push) Has been cancelled
Security Scanning / CodeQL Security Analysis (python) (push) Has been cancelled
Security Scanning / Dependency Security Scan (push) Has been cancelled
Security Scanning / Container Security Scan (push) Has been cancelled
Security Scanning / OSSF Scorecard (push) Has been cancelled
Security Scanning / Security Summary Report (push) Has been cancelled
AITBC CLI Level 1 Commands Test / test-cli-level1 (3.11) (push) Has been cancelled
AITBC CLI Level 1 Commands Test / test-cli-level1 (3.12) (push) Has been cancelled
AITBC CLI Level 1 Commands Test / test-cli-level1 (3.13) (push) Has been cancelled
AITBC CLI Level 1 Commands Test / test-summary (push) Has been cancelled

- Remove debugging service documentation (DEBUgging_SERVICES.md)
- Remove development logs policy and quick reference guides
- Remove E2E test creation summary
- Remove gift certificate example file
- Remove GitHub pull summary documentation
This commit is contained in:
2026-03-25 12:56:07 +01:00
parent 26f7dd5ad0
commit bfe6f94b75
229 changed files with 537 additions and 381 deletions

View 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");
});
});
});

View 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");
});
});
});

View File

@@ -0,0 +1,461 @@
const { expect } = require("chai");
const { ethers } = require("hardhat");
describe("AITBC Smart Contract Integration", function () {
let aitbcToken, zkVerifier, groth16Verifier;
let aiPowerRental, paymentProcessor, performanceVerifier;
let disputeResolution, escrowService, dynamicPricing;
let owner, provider, consumer, arbitrator, oracle;
beforeEach(async function () {
// Get signers
[owner, provider, consumer, arbitrator, oracle] = await ethers.getSigners();
// Deploy mock contracts for testing
const MockERC20 = await ethers.getContractFactory("MockERC20");
aitbcToken = await MockERC20.deploy("AITBC Token", "AITBC", ethers.utils.parseEther("1000000"));
await aitbcToken.deployed();
const MockZKVerifier = await ethers.getContractFactory("MockZKVerifier");
zkVerifier = await MockZKVerifier.deploy();
await zkVerifier.deployed();
const MockGroth16Verifier = await ethers.getContractFactory("MockGroth16Verifier");
groth16Verifier = await MockGroth16Verifier.deploy();
await groth16Verifier.deployed();
// Deploy main contracts
const AIPowerRental = await ethers.getContractFactory("AIPowerRental");
aiPowerRental = await AIPowerRental.deploy(
aitbcToken.address,
zkVerifier.address,
groth16Verifier.address
);
await aiPowerRental.deployed();
const AITBCPaymentProcessor = await ethers.getContractFactory("AITBCPaymentProcessor");
paymentProcessor = await AITBCPaymentProcessor.deploy(
aitbcToken.address,
aiPowerRental.address
);
await paymentProcessor.deployed();
const PerformanceVerifier = await ethers.getContractFactory("PerformanceVerifier");
performanceVerifier = await PerformanceVerifier.deploy(
zkVerifier.address,
groth16Verifier.address,
aiPowerRental.address
);
await performanceVerifier.deployed();
const DisputeResolution = await ethers.getContractFactory("DisputeResolution");
disputeResolution = await DisputeResolution.deploy(
aiPowerRental.address,
paymentProcessor.address,
performanceVerifier.address
);
await disputeResolution.deployed();
const EscrowService = await ethers.getContractFactory("EscrowService");
escrowService = await EscrowService.deploy(
aitbcToken.address,
aiPowerRental.address,
paymentProcessor.address
);
await escrowService.deployed();
const DynamicPricing = await ethers.getContractFactory("DynamicPricing");
dynamicPricing = await DynamicPricing.deploy(
aiPowerRental.address,
performanceVerifier.address,
aitbcToken.address
);
await dynamicPricing.deployed();
// Setup authorizations
await aiPowerRental.authorizeProvider(provider.address);
await aiPowerRental.authorizeConsumer(consumer.address);
await paymentProcessor.authorizePayee(provider.address);
await paymentProcessor.authorizePayer(consumer.address);
await performanceVerifier.authorizeOracle(oracle.address);
await disputeResolution.authorizeArbitrator(arbitrator.address);
await escrowService.authorizeArbiter(arbitrator.address);
await dynamicPricing.authorizePriceOracle(oracle.address);
// Transfer tokens to consumer for testing
await aitbcToken.transfer(consumer.address, ethers.utils.parseEther("1000"));
});
describe("Contract Deployment", function () {
it("Should deploy all contracts successfully", async function () {
expect(await aiPowerRental.deployed()).to.be.true;
expect(await paymentProcessor.deployed()).to.be.true;
expect(await performanceVerifier.deployed()).to.be.true;
expect(await disputeResolution.deployed()).to.be.true;
expect(await escrowService.deployed()).to.be.true;
expect(await dynamicPricing.deployed()).to.be.true;
});
it("Should have correct contract addresses", async function () {
expect(await aiPowerRental.aitbcToken()).to.equal(aitbcToken.address);
expect(await aiPowerRental.zkVerifier()).to.equal(zkVerifier.address);
expect(await aiPowerRental.groth16Verifier()).to.equal(groth16Verifier.address);
});
});
describe("AI Power Rental Integration", function () {
it("Should create and manage rental agreements", async function () {
const duration = 3600; // 1 hour
const price = ethers.utils.parseEther("0.01");
const gpuModel = "RTX 4090";
const computeUnits = 100;
const tx = await aiPowerRental.connect(consumer).createRental(
provider.address,
duration,
price,
gpuModel,
computeUnits
);
const receipt = await tx.wait();
const event = receipt.events.find(e => e.event === "AgreementCreated");
expect(event).to.not.be.undefined;
expect(event.args.provider).to.equal(provider.address);
expect(event.args.consumer).to.equal(consumer.address);
expect(event.args.price).to.equal(price);
});
it("Should start rental and lock payment", async function () {
// Create rental first
const duration = 3600;
const price = ethers.utils.parseEther("0.01");
const platformFee = price.mul(250).div(10000); // 2.5%
const totalAmount = price.add(platformFee);
const createTx = await aiPowerRental.connect(consumer).createRental(
provider.address,
duration,
price,
"RTX 4090",
100
);
const createReceipt = await createTx.wait();
const agreementId = createReceipt.events.find(e => e.event === "AgreementCreated").args.agreementId;
// Approve tokens
await aitbcToken.connect(consumer).approve(aiPowerRental.address, totalAmount);
// Start rental
const startTx = await aiPowerRental.connect(consumer).startRental(agreementId);
const startReceipt = await startTx.wait();
const startEvent = startReceipt.events.find(e => e.event === "AgreementStarted");
expect(startEvent).to.not.be.undefined;
// Check agreement status
const agreement = await aiPowerRental.getRentalAgreement(agreementId);
expect(agreement.status).to.equal(1); // Active
});
});
describe("Payment Processing Integration", function () {
it("Should create and confirm payments", async function () {
const amount = ethers.utils.parseEther("0.01");
const agreementId = ethers.utils.formatBytes32String("test-agreement");
// Approve tokens
await aitbcToken.connect(consumer).approve(paymentProcessor.address, amount);
// Create payment
const tx = await paymentProcessor.connect(consumer).createPayment(
provider.address,
amount,
agreementId,
"Test payment",
0 // Immediate release
);
const receipt = await tx.wait();
const event = receipt.events.find(e => e.event === "PaymentCreated");
expect(event).to.not.be.undefined;
expect(event.args.from).to.equal(consumer.address);
expect(event.args.to).to.equal(provider.address);
expect(event.args.amount).to.equal(amount);
});
it("Should handle escrow payments", async function () {
const amount = ethers.utils.parseEther("0.01");
const releaseTime = Math.floor(Date.now() / 1000) + 3600; // 1 hour from now
// Approve tokens
await aitbcToken.connect(consumer).approve(escrowService.address, amount);
// Create escrow
const tx = await escrowService.connect(consumer).createEscrow(
provider.address,
arbitrator.address,
amount,
0, // Standard escrow
0, // Manual release
releaseTime,
"Test escrow"
);
const receipt = await tx.wait();
const event = receipt.events.find(e => e.event === "EscrowCreated");
expect(event).to.not.be.undefined;
expect(event.args.depositor).to.equal(consumer.address);
expect(event.args.beneficiary).to.equal(provider.address);
});
});
describe("Performance Verification Integration", function () {
it("Should submit and verify performance metrics", async function () {
const agreementId = 1;
const responseTime = 1000; // 1 second
const accuracy = 95;
const availability = 99;
const computePower = 1000;
const throughput = 100;
const memoryUsage = 512;
const energyEfficiency = 85;
// Create mock ZK proof
const mockZKProof = "0x" + "0".repeat(64);
const mockGroth16Proof = "0x" + "0".repeat(64);
// Submit performance
const tx = await performanceVerifier.connect(provider).submitPerformance(
agreementId,
responseTime,
accuracy,
availability,
computePower,
throughput,
memoryUsage,
energyEfficiency,
mockZKProof,
mockGroth16Proof
);
const receipt = await tx.wait();
const event = receipt.events.find(e => e.event === "PerformanceSubmitted");
expect(event).to.not.be.undefined;
expect(event.args.responseTime).to.equal(responseTime);
expect(event.args.accuracy).to.equal(accuracy);
});
});
describe("Dispute Resolution Integration", function () {
it("Should file and manage disputes", async function () {
const agreementId = 1;
const reason = "Service quality issues";
// File dispute
const tx = await disputeResolution.connect(consumer).fileDispute(
agreementId,
provider.address,
0, // Performance dispute
reason,
ethers.utils.formatBytes32String("evidence")
);
const receipt = await tx.wait();
const event = receipt.events.find(e => e.event === "DisputeFiled");
expect(event).to.not.be.undefined;
expect(event.args.initiator).to.equal(consumer.address);
expect(event.args.respondent).to.equal(provider.address);
});
});
describe("Dynamic Pricing Integration", function () {
it("Should update market data and calculate prices", async function () {
const totalSupply = 10000;
const totalDemand = 8000;
const activeProviders = 50;
const activeConsumers = 100;
const totalVolume = ethers.utils.parseEther("100");
const transactionCount = 1000;
const averageResponseTime = 2000;
const averageAccuracy = 96;
const marketSentiment = 75;
// Update market data
const tx = await dynamicPricing.connect(oracle).updateMarketData(
totalSupply,
totalDemand,
activeProviders,
activeConsumers,
totalVolume,
transactionCount,
averageResponseTime,
averageAccuracy,
marketSentiment
);
const receipt = await tx.wait();
const event = receipt.events.find(e => e.event === "MarketDataUpdated");
expect(event).to.not.be.undefined;
expect(event.args.totalSupply).to.equal(totalSupply);
expect(event.args.totalDemand).to.equal(totalDemand);
// Get market price
const marketPrice = await dynamicPricing.getMarketPrice(address(0), "");
expect(marketPrice).to.be.gt(0);
});
});
describe("Cross-Contract Integration", function () {
it("Should handle complete rental lifecycle", async function () {
// 1. Create rental agreement
const duration = 3600;
const price = ethers.utils.parseEther("0.01");
const platformFee = price.mul(250).div(10000);
const totalAmount = price.add(platformFee);
const createTx = await aiPowerRental.connect(consumer).createRental(
provider.address,
duration,
price,
"RTX 4090",
100
);
const createReceipt = await createTx.wait();
const agreementId = createReceipt.events.find(e => e.event === "AgreementCreated").args.agreementId;
// 2. Approve and start rental
await aitbcToken.connect(consumer).approve(aiPowerRental.address, totalAmount);
await aiPowerRental.connect(consumer).startRental(agreementId);
// 3. Submit performance metrics
const mockZKProof = "0x" + "0".repeat(64);
const mockGroth16Proof = "0x" + "0".repeat(64);
await performanceVerifier.connect(provider).submitPerformance(
agreementId,
1000, // responseTime
95, // accuracy
99, // availability
1000, // computePower
100, // throughput
512, // memoryUsage
85, // energyEfficiency
mockZKProof,
mockGroth16Proof
);
// 4. Complete rental
await aiPowerRental.connect(provider).completeRental(agreementId);
// 5. Verify final state
const agreement = await aiPowerRental.getRentalAgreement(agreementId);
expect(agreement.status).to.equal(2); // Completed
});
});
describe("Security Tests", function () {
it("Should prevent unauthorized access", async function () {
// Try to create rental without authorization
await expect(
aiPowerRental.connect(arbitrator).createRental(
provider.address,
3600,
ethers.utils.parseEther("0.01"),
"RTX 4090",
100
)
).to.be.revertedWith("Not authorized consumer");
});
it("Should handle emergency pause", async function () {
// Pause contracts
await aiPowerRental.pause();
await paymentProcessor.pause();
await performanceVerifier.pause();
await disputeResolution.pause();
await escrowService.pause();
await dynamicPricing.pause();
// Try to perform operations while paused
await expect(
aiPowerRental.connect(consumer).createRental(
provider.address,
3600,
ethers.utils.parseEther("0.01"),
"RTX 4090",
100
)
).to.be.revertedWith("Pausable: paused");
// Unpause
await aiPowerRental.unpause();
await paymentProcessor.unpause();
await performanceVerifier.unpause();
await disputeResolution.unpause();
await escrowService.unpause();
await dynamicPricing.unpause();
});
});
describe("Gas Optimization Tests", function () {
it("Should track gas usage for major operations", async function () {
// Create rental
const tx = await aiPowerRental.connect(consumer).createRental(
provider.address,
3600,
ethers.utils.parseEther("0.01"),
"RTX 4090",
100
);
const receipt = await tx.wait();
console.log(`Gas used for createRental: ${receipt.gasUsed.toString()}`);
// Should be reasonable gas usage
expect(receipt.gasUsed).to.be.lt(500000); // Less than 500k gas
});
});
});
// Mock contracts for testing
const MockERC20Source = `
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
contract MockERC20 is ERC20 {
constructor(string memory name, string memory symbol, uint256 initialSupply) ERC20(name, symbol) {
_mint(msg.sender, initialSupply);
}
}
`;
const MockZKVerifierSource = `
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;
contract MockZKVerifier {
function verifyPerformanceProof(
uint256,
uint256,
uint256,
uint256,
uint256,
bytes memory
) external pure returns (bool) {
return true;
}
}
`;
const MockGroth16VerifierSource = `
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;
contract MockGroth16Verifier {
function verifyProof(bytes memory) external pure returns (bool) {
return true;
}
}
`;

10
tests/unit/MockERC20.sol Normal file
View File

@@ -0,0 +1,10 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
contract MockERC20 is ERC20 {
constructor(string memory name, string memory symbol, uint256 initialSupply) ERC20(name, symbol) {
_mint(msg.sender, initialSupply);
}
}

View File

@@ -0,0 +1,8 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;
contract MockGroth16Verifier {
function verifyProof(bytes memory) external pure returns (bool) {
return true;
}
}

View File

@@ -0,0 +1,15 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;
contract MockZKVerifier {
function verifyPerformanceProof(
uint256,
uint256,
uint256,
uint256,
uint256,
bytes memory
) external pure returns (bool) {
return true;
}
}

666
tests/unit/locustfile.py Executable file
View File

@@ -0,0 +1,666 @@
"""
Load tests for AITBC Marketplace using Locust
"""
from locust import HttpUser, task, between, events
from locust.env import Environment
from locust.stats import stats_printer, stats_history
import json
import random
import time
from datetime import datetime, timedelta
import gevent
from gevent.pool import Pool
class MarketplaceUser(HttpUser):
"""Simulated marketplace user behavior"""
wait_time = between(1, 3)
weight = 10
def on_start(self):
"""Called when a user starts"""
# Initialize user session
self.user_id = f"user_{random.randint(1000, 9999)}"
self.tenant_id = f"tenant_{random.randint(100, 999)}"
self.auth_headers = {
"X-Tenant-ID": self.tenant_id,
"Authorization": f"Bearer token_{self.user_id}",
}
# Create user wallet
self.create_wallet()
# Track user state
self.offers_created = []
self.bids_placed = []
self.balance = 10000.0 # Starting balance in USDC
def create_wallet(self):
"""Create a wallet for the user"""
wallet_data = {
"name": f"Wallet_{self.user_id}",
"password": f"pass_{self.user_id}",
}
response = self.client.post(
"/v1/wallets",
json=wallet_data,
headers=self.auth_headers
)
if response.status_code == 201:
self.wallet_id = response.json()["id"]
else:
self.wallet_id = f"wallet_{self.user_id}"
@task(3)
def browse_offers(self):
"""Browse marketplace offers"""
params = {
"limit": 20,
"offset": random.randint(0, 100),
"service_type": random.choice([
"ai_inference",
"image_generation",
"video_processing",
"data_analytics",
]),
}
with self.client.get(
"/v1/marketplace/offers",
params=params,
headers=self.auth_headers,
catch_response=True,
) as response:
if response.status_code == 200:
data = response.json()
offers = data.get("items", [])
# Simulate user viewing offers
if offers:
self.view_offer_details(random.choice(offers)["id"])
response.success()
else:
response.failure(f"Failed to browse offers: {response.status_code}")
def view_offer_details(self, offer_id):
"""View detailed offer information"""
with self.client.get(
f"/v1/marketplace/offers/{offer_id}",
headers=self.auth_headers,
catch_response=True,
) as response:
if response.status_code == 200:
response.success()
else:
response.failure(f"Failed to view offer: {response.status_code}")
@task(2)
def create_offer(self):
"""Create a new marketplace offer"""
if self.balance < 100:
return # Insufficient balance
offer_data = {
"service_type": random.choice([
"ai_inference",
"image_generation",
"video_processing",
"data_analytics",
"scientific_computing",
]),
"pricing": {
"per_hour": round(random.uniform(0.1, 5.0), 2),
"per_unit": round(random.uniform(0.001, 0.1), 4),
},
"capacity": random.randint(10, 1000),
"requirements": {
"gpu_memory": random.choice(["8GB", "16GB", "32GB", "64GB"]),
"cpu_cores": random.randint(4, 32),
"ram": random.choice(["16GB", "32GB", "64GB", "128GB"]),
},
"availability": {
"start_time": (datetime.utcnow() + timedelta(hours=1)).isoformat(),
"end_time": (datetime.utcnow() + timedelta(days=30)).isoformat(),
},
}
with self.client.post(
"/v1/marketplace/offers",
json=offer_data,
headers=self.auth_headers,
catch_response=True,
) as response:
if response.status_code == 201:
offer = response.json()
self.offers_created.append(offer["id"])
response.success()
else:
response.failure(f"Failed to create offer: {response.status_code}")
@task(3)
def place_bid(self):
"""Place a bid on an existing offer"""
# First get available offers
with self.client.get(
"/v1/marketplace/offers",
params={"limit": 10, "status": "active"},
headers=self.auth_headers,
) as response:
if response.status_code != 200:
return
offers = response.json().get("items", [])
if not offers:
return
# Select random offer
offer = random.choice(offers)
# Calculate bid amount
max_price = offer["pricing"]["per_hour"]
bid_price = round(max_price * random.uniform(0.8, 0.95), 2)
if self.balance < bid_price:
return
bid_data = {
"offer_id": offer["id"],
"quantity": random.randint(1, min(10, offer["capacity"])),
"max_price": bid_price,
"duration_hours": random.randint(1, 24),
}
with self.client.post(
"/v1/marketplace/bids",
json=bid_data,
headers=self.auth_headers,
catch_response=True,
) as response:
if response.status_code == 201:
bid = response.json()
self.bids_placed.append(bid["id"])
self.balance -= bid_price * bid_data["quantity"]
response.success()
else:
response.failure(f"Failed to place bid: {response.status_code}")
@task(2)
def check_bids(self):
"""Check status of placed bids"""
if not self.bids_placed:
return
bid_id = random.choice(self.bids_placed)
with self.client.get(
f"/v1/marketplace/bids/{bid_id}",
headers=self.auth_headers,
catch_response=True,
) as response:
if response.status_code == 200:
bid = response.json()
# If bid is accepted, create transaction
if bid["status"] == "accepted":
self.create_transaction(bid)
response.success()
else:
response.failure(f"Failed to check bid: {response.status_code}")
def create_transaction(self, bid):
"""Create transaction for accepted bid"""
tx_data = {
"bid_id": bid["id"],
"payment_method": "wallet",
"confirmations": True,
}
with self.client.post(
"/v1/marketplace/transactions",
json=tx_data,
headers=self.auth_headers,
catch_response=True,
) as response:
if response.status_code == 201:
response.success()
else:
response.failure(f"Failed to create transaction: {response.status_code}")
@task(1)
def get_marketplace_stats(self):
"""Get marketplace statistics"""
with self.client.get(
"/v1/marketplace/stats",
headers=self.auth_headers,
catch_response=True,
) as response:
if response.status_code == 200:
response.success()
else:
response.failure(f"Failed to get stats: {response.status_code}")
@task(1)
def search_services(self):
"""Search for specific services"""
query = random.choice([
"AI inference",
"image generation",
"video rendering",
"data processing",
"machine learning",
])
params = {
"q": query,
"limit": 20,
"min_price": random.uniform(0.1, 1.0),
"max_price": random.uniform(5.0, 10.0),
}
with self.client.get(
"/v1/marketplace/search",
params=params,
headers=self.auth_headers,
catch_response=True,
) as response:
if response.status_code == 200:
response.success()
else:
response.failure(f"Failed to search: {response.status_code}")
class MarketplaceProvider(HttpUser):
"""Simulated service provider behavior"""
wait_time = between(5, 15)
weight = 3
def on_start(self):
"""Initialize provider"""
self.provider_id = f"provider_{random.randint(100, 999)}"
self.tenant_id = f"tenant_{random.randint(100, 999)}"
self.auth_headers = {
"X-Tenant-ID": self.tenant_id,
"Authorization": f"Bearer provider_token_{self.provider_id}",
}
# Register as provider
self.register_provider()
# Provider services
self.services = []
def register_provider(self):
"""Register as a service provider"""
provider_data = {
"name": f"Provider_{self.provider_id}",
"description": "AI/ML computing services provider",
"endpoint": f"https://provider-{self.provider_id}.aitbc.io",
"capabilities": [
"ai_inference",
"image_generation",
"video_processing",
],
"infrastructure": {
"gpu_count": random.randint(10, 100),
"cpu_cores": random.randint(100, 1000),
"memory_gb": random.randint(500, 5000),
},
}
self.client.post(
"/v1/marketplace/providers/register",
json=provider_data,
headers=self.auth_headers
)
@task(4)
def update_service_status(self):
"""Update status of provider services"""
if not self.services:
return
service = random.choice(self.services)
status_data = {
"service_id": service["id"],
"status": random.choice(["available", "busy", "maintenance"]),
"utilization": random.uniform(0.1, 0.9),
"queue_length": random.randint(0, 20),
}
with self.client.patch(
f"/v1/marketplace/services/{service['id']}/status",
json=status_data,
headers=self.auth_headers,
catch_response=True,
) as response:
if response.status_code == 200:
response.success()
else:
response.failure(f"Failed to update status: {response.status_code}")
@task(3)
def create_bulk_offers(self):
"""Create multiple offers at once"""
offers = []
for _ in range(random.randint(5, 15)):
offer_data = {
"service_type": random.choice([
"ai_inference",
"image_generation",
"video_processing",
]),
"pricing": {
"per_hour": round(random.uniform(0.5, 3.0), 2),
},
"capacity": random.randint(50, 500),
"requirements": {
"gpu_memory": "16GB",
"cpu_cores": 16,
},
}
offers.append(offer_data)
bulk_data = {"offers": offers}
with self.client.post(
"/v1/marketplace/offers/bulk",
json=bulk_data,
headers=self.auth_headers,
catch_response=True,
) as response:
if response.status_code == 201:
created = response.json().get("created", [])
self.services.extend(created)
response.success()
else:
response.failure(f"Failed to create bulk offers: {response.status_code}")
@task(2)
def respond_to_bids(self):
"""Respond to incoming bids"""
with self.client.get(
"/v1/marketplace/bids",
params={"provider_id": self.provider_id, "status": "pending"},
headers=self.auth_headers,
) as response:
if response.status_code != 200:
return
bids = response.json().get("items", [])
if not bids:
return
# Respond to random bid
bid = random.choice(bids)
action = random.choice(["accept", "reject", "counter"])
response_data = {
"bid_id": bid["id"],
"action": action,
}
if action == "counter":
response_data["counter_price"] = round(
bid["max_price"] * random.uniform(1.05, 1.15), 2
)
with self.client.post(
"/v1/marketplace/bids/respond",
json=response_data,
headers=self.auth_headers,
catch_response=True,
) as response:
if response.status_code == 200:
response.success()
else:
response.failure(f"Failed to respond to bid: {response.status_code}")
@task(1)
def get_provider_analytics(self):
"""Get provider analytics"""
with self.client.get(
f"/v1/marketplace/providers/{self.provider_id}/analytics",
headers=self.auth_headers,
catch_response=True,
) as response:
if response.status_code == 200:
response.success()
else:
response.failure(f"Failed to get analytics: {response.status_code}")
class MarketplaceAdmin(HttpUser):
"""Simulated admin user behavior"""
wait_time = between(10, 30)
weight = 1
def on_start(self):
"""Initialize admin"""
self.auth_headers = {
"Authorization": "Bearer admin_token_123",
"X-Admin-Access": "true",
}
@task(3)
def monitor_marketplace_health(self):
"""Monitor marketplace health metrics"""
endpoints = [
"/v1/marketplace/health",
"/v1/marketplace/metrics",
"/v1/marketplace/stats",
]
endpoint = random.choice(endpoints)
with self.client.get(
endpoint,
headers=self.auth_headers,
catch_response=True,
) as response:
if response.status_code == 200:
response.success()
else:
response.failure(f"Health check failed: {response.status_code}")
@task(2)
def review_suspicious_activity(self):
"""Review suspicious marketplace activity"""
with self.client.get(
"/v1/admin/marketplace/activity",
params={
"suspicious_only": True,
"limit": 50,
},
headers=self.auth_headers,
catch_response=True,
) as response:
if response.status_code == 200:
activities = response.json().get("items", [])
# Take action on suspicious activities
for activity in activities[:5]: # Limit to 5 actions
self.take_action(activity["id"])
response.success()
else:
response.failure(f"Failed to review activity: {response.status_code}")
def take_action(self, activity_id):
"""Take action on suspicious activity"""
action = random.choice(["warn", "suspend", "investigate"])
with self.client.post(
f"/v1/admin/marketplace/activity/{activity_id}/action",
json={"action": action},
headers=self.auth_headers,
catch_response=True,
) as response:
if response.status_code in [200, 404]:
response.success()
else:
response.failure(f"Failed to take action: {response.status_code}")
@task(1)
def generate_reports(self):
"""Generate marketplace reports"""
report_types = [
"daily_summary",
"weekly_analytics",
"provider_performance",
"user_activity",
]
report_type = random.choice(report_types)
with self.client.post(
"/v1/admin/marketplace/reports",
json={
"type": report_type,
"format": "json",
"email": f"admin@aitbc.io",
},
headers=self.auth_headers,
catch_response=True,
) as response:
if response.status_code == 202:
response.success()
else:
response.failure(f"Failed to generate report: {response.status_code}")
# Custom event handlers for monitoring
@events.request.add_listener
def on_request(request_type, name, response_time, response_length, exception, **kwargs):
"""Custom request handler for additional metrics"""
if exception:
print(f"Request failed: {name} - {exception}")
elif response_time > 5000: # Log slow requests
print(f"Slow request: {name} - {response_time}ms")
@events.test_start.add_listener
def on_test_start(environment, **kwargs):
"""Called when test starts"""
print("Starting marketplace load test")
print(f"Target: {environment.host}")
@events.test_stop.add_listener
def on_test_stop(environment, **kwargs):
"""Called when test stops"""
print("\nLoad test completed")
# Print summary statistics
stats = environment.stats
print(f"\nTotal requests: {stats.total.num_requests}")
print(f"Failures: {stats.total.num_failures}")
print(f"Average response time: {stats.total.avg_response_time:.2f}ms")
print(f"95th percentile: {stats.total.get_response_time_percentile(0.95):.2f}ms")
print(f"Requests per second: {stats.total.current_rps:.2f}")
# Custom load shapes
class GradualLoadShape:
"""Gradually increase load over time"""
def __init__(self, max_users=100, spawn_rate=10):
self.max_users = max_users
self.spawn_rate = spawn_rate
def tick(self):
run_time = time.time() - self.start_time
if run_time < 60: # First minute: ramp up
return int(self.spawn_rate * run_time / 60)
elif run_time < 300: # Next 4 minutes: maintain
return self.max_users
else: # Last minute: ramp down
remaining = 360 - run_time
return int(self.max_users * remaining / 60)
class BurstLoadShape:
"""Burst traffic pattern"""
def __init__(self, burst_size=50, normal_size=10):
self.burst_size = burst_size
self.normal_size = normal_size
def tick(self):
run_time = time.time() - self.start_time
# Burst every 30 seconds for 10 seconds
if int(run_time) % 30 < 10:
return self.burst_size
else:
return self.normal_size
# Performance monitoring
class PerformanceMonitor:
"""Monitor performance during load test"""
def __init__(self):
self.metrics = {
"response_times": [],
"error_rates": [],
"throughput": [],
}
def record_request(self, response_time, success):
"""Record request metrics"""
self.metrics["response_times"].append(response_time)
self.metrics["error_rates"].append(0 if success else 1)
def get_summary(self):
"""Get performance summary"""
if not self.metrics["response_times"]:
return {}
return {
"avg_response_time": sum(self.metrics["response_times"]) / len(self.metrics["response_times"]),
"max_response_time": max(self.metrics["response_times"]),
"error_rate": sum(self.metrics["error_rates"]) / len(self.metrics["error_rates"]),
"total_requests": len(self.metrics["response_times"]),
}
# Test configuration
if __name__ == "__main__":
# Setup environment
env = Environment(user_classes=[MarketplaceUser, MarketplaceProvider, MarketplaceAdmin])
# Create performance monitor
monitor = PerformanceMonitor()
# Setup host
env.host = "http://localhost:8001"
# Setup load shape
env.create_local_runner()
# Start web UI for monitoring
env.create_web_ui("127.0.0.1", 8089)
# Start the load test
print("Starting marketplace load test...")
print("Web UI available at: http://127.0.0.1:8089")
# Run for 6 minutes
env.runner.start(100, spawn_rate=10)
gevent.spawn_later(360, env.runner.stop)
# Print stats
gevent.spawn(stats_printer(env.stats))
# Wait for test to complete
env.runner.greenlet.join()