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

181 lines
5.6 KiB
Solidity

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import "forge-std/Test.sol";
import "../src/CouponFactory.sol";
import "../src/Coupon.sol";
import "../src/Compliance.sol";
import "../src/interfaces/ICoupon.sol";
contract CouponFactoryTest is Test {
CouponFactory factory;
Coupon coupon;
Compliance compliance;
address admin = address(1);
address issuer = address(2);
address minter = address(3);
function setUp() public {
vm.startPrank(admin);
// 部署 Compliance
compliance = new Compliance();
compliance.initialize(admin);
// 部署 Coupon
coupon = new Coupon();
coupon.initialize("Genex Coupon", "GXC", admin);
// 部署 CouponFactory
factory = new CouponFactory();
factory.initialize(address(coupon), address(compliance), admin);
// 授权
coupon.grantRole(keccak256("FACTORY_ROLE"), address(factory));
factory.grantRole(keccak256("MINTER_ROLE"), minter);
// 设置发行方 KYC
compliance.setKycLevel(issuer, 3);
vm.stopPrank();
}
function test_MintBatchUtility() public {
vm.prank(minter);
ICoupon.CouponConfig memory config = _utilityConfig();
uint256[] memory tokenIds = factory.mintBatch(issuer, 100e6, 5, config);
assertEq(tokenIds.length, 5);
for (uint256 i = 0; i < 5; i++) {
assertEq(coupon.ownerOf(tokenIds[i]), issuer);
assertEq(coupon.getFaceValue(tokenIds[i]), 100e6);
}
}
function test_MintBatchSecurity() public {
vm.prank(minter);
ICoupon.CouponConfig memory config = _securityConfig();
uint256[] memory tokenIds = factory.mintBatch(issuer, 1000e6, 3, config);
assertEq(tokenIds.length, 3);
}
function test_UtilityMaxPriceExceedsFaceValue() public {
vm.prank(minter);
ICoupon.CouponConfig memory config = _utilityConfig();
config.maxPrice = 200e6; // 超过面值 100e6
vm.expectRevert("Utility: maxPrice <= faceValue");
factory.mintBatch(issuer, 100e6, 1, config);
}
function test_UtilityMaxExpiry12Months() public {
vm.prank(minter);
ICoupon.CouponConfig memory config = _utilityConfig();
config.expiryDate = block.timestamp + 400 days;
vm.expectRevert("Utility: max 12 months");
factory.mintBatch(issuer, 100e6, 1, config);
}
function test_SecurityRequiresExpiry() public {
vm.prank(minter);
ICoupon.CouponConfig memory config = _securityConfig();
config.expiryDate = 0;
vm.expectRevert("Security: expiry required");
factory.mintBatch(issuer, 1000e6, 1, config);
}
function test_SecurityRequiresKycL3() public {
address lowKycIssuer = address(10);
vm.prank(admin);
compliance.setKycLevel(lowKycIssuer, 1);
vm.prank(minter);
ICoupon.CouponConfig memory config = _securityConfig();
vm.expectRevert("Compliance: insufficient KYC level");
factory.mintBatch(lowKycIssuer, 1000e6, 1, config);
}
function test_OnlyMinterCanMint() public {
vm.prank(issuer);
ICoupon.CouponConfig memory config = _utilityConfig();
vm.expectRevert();
factory.mintBatch(issuer, 100e6, 1, config);
}
function test_ZeroQuantityReverts() public {
vm.prank(minter);
ICoupon.CouponConfig memory config = _utilityConfig();
vm.expectRevert("CouponFactory: invalid quantity");
factory.mintBatch(issuer, 100e6, 0, config);
}
function test_ExcessiveQuantityReverts() public {
vm.prank(minter);
ICoupon.CouponConfig memory config = _utilityConfig();
vm.expectRevert("CouponFactory: invalid quantity");
factory.mintBatch(issuer, 100e6, 10001, config);
}
function test_BatchInfoRecorded() public {
vm.prank(minter);
ICoupon.CouponConfig memory config = _utilityConfig();
factory.mintBatch(issuer, 100e6, 3, config);
CouponFactory.BatchInfo memory batch = factory.getBatchInfo(0);
assertEq(batch.issuer, issuer);
assertEq(batch.faceValue, 100e6);
assertEq(batch.quantity, 3);
assertEq(batch.tokenIds.length, 3);
}
function testFuzz_MintBatchQuantity(uint256 quantity) public {
quantity = bound(quantity, 1, 100);
vm.prank(minter);
ICoupon.CouponConfig memory config = _utilityConfig();
uint256[] memory tokenIds = factory.mintBatch(issuer, 100e6, quantity, config);
assertEq(tokenIds.length, quantity);
}
// --- Helpers ---
function _utilityConfig() internal view returns (ICoupon.CouponConfig memory) {
bytes32[] memory stores = new bytes32[](0);
return ICoupon.CouponConfig({
couponType: ICoupon.CouponType.Utility,
transferable: true,
maxResaleCount: 3,
maxPrice: 100e6,
expiryDate: block.timestamp + 180 days,
minPurchase: 0,
stackable: false,
allowedStores: stores,
issuer: issuer,
faceValue: 100e6
});
}
function _securityConfig() internal view returns (ICoupon.CouponConfig memory) {
bytes32[] memory stores = new bytes32[](0);
return ICoupon.CouponConfig({
couponType: ICoupon.CouponType.Security,
transferable: true,
maxResaleCount: 10,
maxPrice: 0,
expiryDate: block.timestamp + 365 days,
minPurchase: 0,
stackable: false,
allowedStores: stores,
issuer: issuer,
faceValue: 1000e6
});
}
}