Test 항목
- Initial Deployment
- Role-Based Access Control
- Proxy Upgrade Functionality
- New Functionality in V2
- Edge Case Testing
- Stress Testing
- Maximum Supply Test
세부 항목
- Initial Deployment
- 배포 후 토큰 세부 정보가 올바르게 설정되어야 함
- Role-Based Access Control
- MINTER_ROLE 권한이 없는 발신자가 민팅을 시도하면 되돌려야 함
- 소유자가 아닌 사람이 일시 중지 및 해제를 시도하면 되돌려야 함
- 일시 중지 상태에서 토큰 전송을 시도하면 되돌려야 함
- 잘못된 매개변수로 전송 함수를 호출하면 되돌려야 함
- Proxy Upgrade Functionality
- 업그레이드는 잔액과 상태에 영향을 미치지 않아야 함
- 업그레이드 후 올바른 구현체를 가리켜야 함
- New Functionality in V2
- 토큰은 정확하게 소각되어야 함
- 높은 가스 비용을 처리할 수 있어야 함
- Edge Case Testing
- 매우 작은 양의 민트를 처리할 수 있어야함
- Stress Testing
- 여러 번의 민트 작업을 처리할 수 있어야함
- Maximum Supply Test
- 최대 토큰 공급량 처리 테스트
1. Initial Deployment
토큰 세부 정보 체크
describe("Initial Deployment", function () {
it("should set the token details after deployment", async function () {
const { erc20ImplInstance } = await loadFixture(deployFixture);
expect(await erc20ImplInstance.name()).to.equal("BittoToken");
expect(await erc20ImplInstance.symbol()).to.equal("BITTO");
expect((await erc20ImplInstance.totalSupply()).toString()).to.equal(
ethers.parseUnits("10000", 18)
);
});
});
2. Role-Based Access Control
MINTER_ROLE 권한이 없는 발신자가 민팅을 시도하면 되돌려야 함
it("should revert minting when sender does not have MINTER_ROLE", async function () {
const { owner, recipient } = await getSigners();
const { erc20ImplInstance } = await loadFixture(deployFixture);
const MINTER_ROLE = await erc20ImplInstance.MINTER_ROLE();
const mintAmount = ethers.parseEther("1000");
const expectedErrorMessage =
`AccessControl: account ${owner.address.toLowerCase()} is missing role ${MINTER_ROLE.toLowerCase()}`.toLowerCase();
await expect(
erc20ImplInstance.connect(owner).mint(recipient.address, mintAmount)
).to.be.revertedWith(new RegExp(expectedErrorMessage, "i"));
});
contractInstance.connect(address).function(); 컨트랙트인스턴스에 함수를 address로 실행
소유자가 아닌 사람이 일시 중지 및 해제를 시도하면 되돌려야 함
it("should revert pausing and unpausing when sender is not the owner", async () => {
const { minter } = await getSigners();
const { erc20ImplInstance } = await loadFixture(deployFixture);
// Try to pause the contract by non-owner
await expect(
erc20ImplInstance.connect(minter).pause()
).to.be.revertedWith("Ownable: caller is not the owner");
// Try to unpause the contract by non-owner
await expect(
erc20ImplInstance.connect(minter).unpause()
).to.be.revertedWith("Ownable: caller is not the owner");
});
일시 중지 상태에서 토큰 전송을 시도하면 되돌려야 함
it("should revert token transfer when paused", async () => {
const { owner, recipient } = await getSigners();
const { erc20ImplInstance } = await loadFixture(deployFixture);
let transferAmount = ethers.parseEther("500");
// Pause Contract
await erc20ImplInstance.pause();
// Attempt Transfer and expect it to be reverted.
await expect(
erc20ImplInstance.transfer(recipient.address, transferAmount)
).to.be.reverted;
});
잘못된 매개변수로 전송 함수를 호출하면 되돌려야 함
it("should revert when transfer is called with invalid parameters", async () => {
const { erc20ImplInstance } = await loadFixture(deployFixture);
// Try to call a function with invalid parameters
await expect(
erc20ImplInstance.transfer(
"0x0000000000000000000000000000000000000000",
ethers.parseEther("10")
)
).to.be.reverted;
});
3. Proxy Upgrade Functionality
업그레이드는 잔액과 상태에 영향을 미치지 않아야 함
it("Upgrade should not affect balance and state", async () => {
const { owner, minter, recipient } = await getSigners();
const {
erc20ImplInstance,
erc20ImplV2Address,
transparentUpgradeableInstance,
erc20ImplV2Instance,
} = await loadFixture(deployFixture);
// Mint tokens to minter
const mintAmount = ethers.parseEther("1000");
await erc20ImplInstance
.connect(minter)
.mint(recipient.address, mintAmount);
// Check minter balance before upgrade
const minterBalanceBeforeUpgrade = await erc20ImplInstance.balanceOf(
recipient.address
);
expect(minterBalanceBeforeUpgrade).to.equal(mintAmount);
// Upgrade the proxy to V2 implementation
await transparentUpgradeableInstance.upgradeTo(erc20ImplV2Address);
const minterBalanceAfterUpgrade = await erc20ImplV2Instance.balanceOf(
recipient.address
);
expect(mintAmount).to.equal(minterBalanceAfterUpgrade);
});
총 발행량과 업그레이드 이전의 밸런스 체크 확인
총 발행량과 업그레이드 이후의 밸런스 체크 확인
업그레이드 후 올바른 구현체를 가리켜야 함
it("should point to correct implementation after upgrade", async () => {
const {
transparentUpgradeableInstance,
erc20ImplV2Address,
erc20Proxy,
} = await loadFixture(deployFixture);
await transparentUpgradeableInstance.upgradeTo(erc20ImplV2Address);
// Check the implementation address after upgrade
const currentImplementation = await erc20Proxy.getImplementation();
expect(currentImplementation).to.equal(erc20ImplV2Address);
});
4. New Functionality in V2
토큰은 정확하게 소각되어야 함
it("should burn tokens correctly", async () => {
const { owner } = await getSigners();
const {
transparentUpgradeableInstance,
erc20ImplV2Instance,
erc20ImplV2Address,
} = await loadFixture(deployFixture);
await transparentUpgradeableInstance.upgradeTo(erc20ImplV2Address);
let initialBalance = await erc20ImplV2Instance.balanceOf(owner.address);
let burnAmount = ethers.parseEther("100");
await erc20ImplV2Instance.burn(burnAmount);
let finalBalance = await erc20ImplV2Instance.balanceOf(owner.address);
// BigInt 타입으로 변환해서 계산
let initialBalanceBigInt = BigInt(initialBalance.toString());
let burnAmountBigInt = BigInt(burnAmount.toString());
let finalBalanceBigInt = BigInt(finalBalance.toString());
let expectedFinalBalanceBigInt =
initialBalanceBigInt - burnAmountBigInt;
expect(finalBalanceBigInt).to.equal(expectedFinalBalanceBigInt);
});
높은 가스 비용을 처리할 수 있어야 함
it("should handle high gas prices", async () => {
const {
transparentUpgradeableInstance,
erc20ImplV2Instance,
erc20ImplV2Address,
} = await loadFixture(deployFixture);
await transparentUpgradeableInstance.upgradeTo(erc20ImplV2Address);
let burnAmount = ethers.parseEther("100");
// High gas price simulation
const overrides = {
gasPrice: ethers.parseUnits("100", "gwei"),
};
// Attempt to burn tokens with high gas price
await expect(erc20ImplV2Instance.burn(burnAmount, overrides)).to.not.be
.reverted;
});
의미가 있나?
5. Edge Case Testing
매우 작은 양의 민트를 처리할 수 있어야함
// Edge Case Testing
it("should handle small mint amounts", async () => {
const { minter, recipient } = await getSigners();
const {
transparentUpgradeableInstance,
erc20ImplV2Instance,
erc20ImplV2Address,
} = await loadFixture(deployFixture);
await transparentUpgradeableInstance.upgradeTo(erc20ImplV2Address);
let mintAmount = ethers.parseUnits("0.0000000001", "ether");
// Attempt to mint a very small amount of tokens
await expect(
erc20ImplV2Instance
.connect(minter)
.mint(recipient.address, mintAmount)
).to.not.be.reverted;
});
6. Stress Testing
여러 번의 민트 작업을 처리할 수 있어야함
// Stress Testing
it("should handle many mint operations", async () => {
const { minter, recipient } = await getSigners();
const {
transparentUpgradeableInstance,
erc20ImplV2Instance,
erc20ImplV2Address,
} = await loadFixture(deployFixture);
await transparentUpgradeableInstance.upgradeTo(erc20ImplV2Address);
let mintAmount = ethers.parseUnits("1", "ether");
// Attempt to mint tokens many times in a loop
for (let i = 0; i < 100; i++) {
await expect(
erc20ImplV2Instance
.connect(minter)
.mint(recipient.address, mintAmount)
).to.not.be.reverted;
}
});
7. Maximum Supply Test
최대 토큰 공급량 처리 테스트
it("should handle maximum token supply", async () => {
const { minter, recipient } = await getSigners();
const {
transparentUpgradeableInstance,
erc20ImplV2Instance,
erc20ImplV2Address,
} = await loadFixture(deployFixture);
await transparentUpgradeableInstance.upgradeTo(erc20ImplV2Address);
//2의 256승에서 1을 뺀 결과. 이 값은 uint256에서 가능한 최대값
let maxSupplyAmount = ethers.parseUnits(
"115792089237316195423570985008687907853269984665640564039457584007913129639935",
"wei"
);
// Attempt to mint the maximum possible amount of tokens
await expect(
erc20ImplV2Instance
.connect(minter)
.mint(recipient.address, maxSupplyAmount)
).to.be.reverted;
});
최대 공급량이 있는데 배포시 초기 공급량 때문에 revert가 떠야함
추가 테스트가 생기면 추가 예정
댓글