gcx/blockchain/genex-contracts/test/CouponBatch.t.sol

257 lines
7.9 KiB
Solidity

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import "forge-std/Test.sol";
import "../src/CouponBatch.sol";
import "../src/interfaces/ICouponBatch.sol";
contract CouponBatchTest is Test {
CouponBatch batch;
address admin = address(1);
address factory = address(2);
address burner = address(3);
address issuer = address(4);
address user1 = address(5);
address user2 = address(6);
bytes32 storeA = keccak256("STORE_A");
bytes32 storeB = keccak256("STORE_B");
function setUp() public {
vm.startPrank(admin);
batch = new CouponBatch();
batch.initialize("https://genex.io/api/coupon-batch/{id}.json", admin);
batch.grantRole(keccak256("FACTORY_ROLE"), factory);
batch.grantRole(keccak256("BURNER_ROLE"), burner);
vm.stopPrank();
}
// ==========================================
// 铸造测试
// ==========================================
function test_MintCreatesNewType() public {
ICouponBatch.BatchCouponConfig memory config = _defaultConfig();
vm.prank(factory);
uint256 typeId = batch.mint(issuer, 100e6, 1000, config);
assertEq(typeId, 1);
assertEq(batch.balanceOf(issuer, typeId), 1000);
assertEq(batch.totalSupply(typeId), 1000);
assertEq(batch.getMintedQuantity(typeId), 1000);
}
function test_MintMultipleTypes() public {
ICouponBatch.BatchCouponConfig memory config = _defaultConfig();
vm.startPrank(factory);
uint256 typeId1 = batch.mint(issuer, 100e6, 500, config);
uint256 typeId2 = batch.mint(issuer, 50e6, 2000, config);
vm.stopPrank();
assertEq(typeId1, 1);
assertEq(typeId2, 2);
assertEq(batch.balanceOf(issuer, typeId1), 500);
assertEq(batch.balanceOf(issuer, typeId2), 2000);
}
function test_MintStoresConfig() public {
bytes32[] memory stores = new bytes32[](2);
stores[0] = storeA;
stores[1] = storeB;
ICouponBatch.BatchCouponConfig memory config = ICouponBatch.BatchCouponConfig({
transferable: false,
maxPrice: 50e6,
expiryDate: block.timestamp + 90 days,
minPurchase: 10,
stackable: true,
allowedStores: stores,
issuer: issuer,
faceValue: 50e6
});
vm.prank(factory);
uint256 typeId = batch.mint(issuer, 50e6, 100, config);
ICouponBatch.BatchCouponConfig memory stored = batch.getConfig(typeId);
assertEq(stored.transferable, false);
assertEq(stored.maxPrice, 50e6);
assertEq(stored.minPurchase, 10);
assertEq(stored.stackable, true);
assertEq(stored.allowedStores.length, 2);
assertEq(stored.issuer, issuer);
assertEq(stored.faceValue, 50e6);
}
function test_MintZeroAddressReverts() public {
ICouponBatch.BatchCouponConfig memory config = _defaultConfig();
vm.prank(factory);
vm.expectRevert("CouponBatch: zero address");
batch.mint(address(0), 100e6, 1000, config);
}
function test_MintZeroFaceValueReverts() public {
ICouponBatch.BatchCouponConfig memory config = _defaultConfig();
vm.prank(factory);
vm.expectRevert("CouponBatch: zero face value");
batch.mint(issuer, 0, 1000, config);
}
function test_MintZeroQuantityReverts() public {
ICouponBatch.BatchCouponConfig memory config = _defaultConfig();
vm.prank(factory);
vm.expectRevert("CouponBatch: zero quantity");
batch.mint(issuer, 100e6, 0, config);
}
function test_MintOnlyFactoryRole() public {
ICouponBatch.BatchCouponConfig memory config = _defaultConfig();
vm.prank(user1);
vm.expectRevert();
batch.mint(issuer, 100e6, 1000, config);
}
function test_GetFaceValue() public {
ICouponBatch.BatchCouponConfig memory config = _defaultConfig();
vm.prank(factory);
uint256 typeId = batch.mint(issuer, 250e6, 100, config);
assertEq(batch.getFaceValue(typeId), 250e6);
}
function test_GetConfigNonexistentReverts() public {
vm.expectRevert("CouponBatch: nonexistent type");
batch.getConfig(999);
}
// ==========================================
// 销毁测试
// ==========================================
function test_BurnByOwner() public {
ICouponBatch.BatchCouponConfig memory config = _defaultConfig();
vm.prank(factory);
uint256 typeId = batch.mint(issuer, 100e6, 1000, config);
vm.prank(issuer);
batch.burn(issuer, typeId, 300);
assertEq(batch.balanceOf(issuer, typeId), 700);
assertEq(batch.totalSupply(typeId), 700);
}
function test_BurnByBurnerRole() public {
ICouponBatch.BatchCouponConfig memory config = _defaultConfig();
vm.prank(factory);
uint256 typeId = batch.mint(issuer, 100e6, 1000, config);
vm.prank(burner);
batch.burn(issuer, typeId, 500);
assertEq(batch.balanceOf(issuer, typeId), 500);
}
function test_BurnByApproved() public {
ICouponBatch.BatchCouponConfig memory config = _defaultConfig();
vm.prank(factory);
uint256 typeId = batch.mint(issuer, 100e6, 1000, config);
vm.prank(issuer);
batch.setApprovalForAll(user1, true);
vm.prank(user1);
batch.burn(issuer, typeId, 200);
assertEq(batch.balanceOf(issuer, typeId), 800);
}
function test_BurnUnauthorizedReverts() public {
ICouponBatch.BatchCouponConfig memory config = _defaultConfig();
vm.prank(factory);
uint256 typeId = batch.mint(issuer, 100e6, 1000, config);
vm.prank(user1);
vm.expectRevert("CouponBatch: not authorized to burn");
batch.burn(issuer, typeId, 100);
}
// ==========================================
// 转让限制测试
// ==========================================
function test_TransferableTokenCanTransfer() public {
ICouponBatch.BatchCouponConfig memory config = _defaultConfig();
config.transferable = true;
vm.prank(factory);
uint256 typeId = batch.mint(issuer, 100e6, 100, config);
vm.prank(issuer);
batch.safeTransferFrom(issuer, user1, typeId, 50, "");
assertEq(batch.balanceOf(issuer, typeId), 50);
assertEq(batch.balanceOf(user1, typeId), 50);
}
function test_NonTransferableTokenReverts() public {
ICouponBatch.BatchCouponConfig memory config = _defaultConfig();
config.transferable = false;
vm.prank(factory);
uint256 typeId = batch.mint(issuer, 100e6, 100, config);
vm.prank(issuer);
vm.expectRevert("CouponBatch: non-transferable");
batch.safeTransferFrom(issuer, user1, typeId, 10, "");
}
// ==========================================
// Fuzz 测试
// ==========================================
function testFuzz_MintQuantity(uint256 quantity) public {
quantity = bound(quantity, 1, 100_000);
ICouponBatch.BatchCouponConfig memory config = _defaultConfig();
vm.prank(factory);
uint256 typeId = batch.mint(issuer, 100e6, quantity, config);
assertEq(batch.balanceOf(issuer, typeId), quantity);
assertEq(batch.totalSupply(typeId), quantity);
assertEq(batch.getMintedQuantity(typeId), quantity);
}
// ==========================================
// Helpers
// ==========================================
function _defaultConfig() internal view returns (ICouponBatch.BatchCouponConfig memory) {
bytes32[] memory stores = new bytes32[](0);
return ICouponBatch.BatchCouponConfig({
transferable: true,
maxPrice: 100e6,
expiryDate: block.timestamp + 180 days,
minPurchase: 0,
stackable: false,
allowedStores: stores,
issuer: issuer,
faceValue: 100e6
});
}
}