mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-07-27 07:06:42 +00:00
Add tests (#6)
* Rework tests to remove dependency on hardhat network * Add Docs for testing * update readme * temporary allow workflow to run on push and pr * Add tests * update container name for tests run * Update workflow * Cleanup * rename workflow
This commit is contained in:
parent
7df5aa3c92
commit
c6cb671406
4 changed files with 231 additions and 91 deletions
|
|
@ -1,14 +1,14 @@
|
|||
name: deploy
|
||||
name: deploy contract and run test
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
types: [closed]
|
||||
branches: [master]
|
||||
paths: ["**"]
|
||||
on: [push, pull_request]
|
||||
# pull_request:
|
||||
# types: [closed]
|
||||
# branches: [master]
|
||||
# paths: ["**"]
|
||||
|
||||
jobs:
|
||||
deploy:
|
||||
if: github.event.pull_request.merged == true && contains(github.event.pull_request.labels.*.name, 'CI:Deploy')
|
||||
# if: github.event.pull_request.merged == true && contains(github.event.pull_request.labels.*.name, 'CI:Deploy')
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
|
|
@ -72,9 +72,27 @@ jobs:
|
|||
docker build -t $IMAGE_NAME:with-contract-latest -t $IMAGE_NAME:with-contract-$COMMIT_SHA -f Dockerfile.with-contract .
|
||||
|
||||
rm Dockerfile.with-contract
|
||||
rm -rf ./geth_data
|
||||
|
||||
docker images -a
|
||||
- name: push image
|
||||
run: |
|
||||
docker push $IMAGE_NAME:with-contract-latest
|
||||
docker push $IMAGE_NAME:with-contract-$COMMIT_SHA
|
||||
|
||||
- name: start geth with contract
|
||||
run: |
|
||||
docker run -d --name geth-service-with-contract -p 8545:8545 -p 30303:30303 yyx00xzz/geth-dev:with-contract-latest --dev --http --http.addr 0.0.0.0 --http.port 8545 --http.api admin,debug,web3,eth,txpool,miner,net,dev --miner.gasprice "1" --http.corsdomain "*"
|
||||
echo "Waiting for Geth to be ready..."
|
||||
# Poll the Geth RPC endpoint until it's responsive
|
||||
while ! curl -s -X POST -H "Content-Type: application/json" -d '{"jsonrpc":"2.0","method":"web3_clientVersion","params":[],"id":1}' http://localhost:8545 > /dev/null; do
|
||||
echo "Geth not ready yet, waiting..."
|
||||
docker logs geth-service
|
||||
sleep 5
|
||||
done
|
||||
echo "Geth is ready."
|
||||
|
||||
- name: run tests
|
||||
run: |
|
||||
cd ./hardhat
|
||||
npx hardhat test --network geth
|
||||
19
README.md
19
README.md
|
|
@ -27,6 +27,25 @@ Using mounted dir for the data can be tricky because of permission errs tho.
|
|||
Currently, the new image (with contract) is built from the latest base image.
|
||||
This should be extended in the future to support building from a given tag.
|
||||
|
||||
---
|
||||
|
||||
### Tests
|
||||
The default tests that Hardhat Sample Project provides depends on using the Hardhat network.
|
||||
This is an example of an error they throw when ran against the Geth:
|
||||
```
|
||||
4) Lock
|
||||
Deployment
|
||||
Should fail if the unlockTime is not in the future:
|
||||
OnlyHardhatNetworkError: This helper can only be used with Hardhat Network. You are connected to 'geth'.
|
||||
at checkIfDevelopmentNetwork (<obfustcated>/hardhat/node_modules/@nomicfoundation/hardhat-network-helpers/src/utils.ts:30:11)
|
||||
at getHardhatProvider (<obfuscated>/hardhat/node_modules/@nomicfoundation/hardhat-network-helpers/src/utils.ts:41:9)
|
||||
at async Object.latest (<obfuscated>/hardhat/node_modules/@nomicfoundation/hardhat-network-helpers/src/helpers/time/latest.ts:7:20)
|
||||
at async Context.<anonymous> (<obfuscated>/hardhat/test/Lock.ts:54:26)
|
||||
```
|
||||
I've provided the same tests reworked to be compatible with Geth.
|
||||
|
||||
When running the docker container with Geth, I do not use bind mount and I am making sure that the geth_data dir from previous steps is cleaned so I can be sure that I am working with the contract previously deployed in the container.
|
||||
|
||||
## Go Ethereum
|
||||
|
||||
Golang execution layer implementation of the Ethereum protocol.
|
||||
|
|
|
|||
|
|
@ -1,121 +1,98 @@
|
|||
import {
|
||||
time,
|
||||
loadFixture,
|
||||
} from "@nomicfoundation/hardhat-toolbox/network-helpers";
|
||||
import { anyValue } from "@nomicfoundation/hardhat-chai-matchers/withArgs";
|
||||
import { expect } from "chai";
|
||||
import hre from "hardhat";
|
||||
|
||||
describe("Lock", function () {
|
||||
// We define a fixture to reuse the same setup in every test.
|
||||
// We use loadFixture to run this setup once, snapshot that state,
|
||||
// and reset Hardhat Network to that snapshot in every test.
|
||||
async function deployOneYearLockFixture() {
|
||||
const ONE_YEAR_IN_SECS = 365 * 24 * 60 * 60;
|
||||
const ONE_GWEI = 1_000_000_000;
|
||||
|
||||
const lockedAmount = ONE_GWEI;
|
||||
const unlockTime = (await time.latest()) + ONE_YEAR_IN_SECS;
|
||||
|
||||
// Contracts are deployed using the first signer/account by default
|
||||
const [owner, otherAccount] = await hre.ethers.getSigners();
|
||||
|
||||
async function deployLock(unlockTime: number) {
|
||||
const Lock = await hre.ethers.getContractFactory("Lock");
|
||||
const lock = await Lock.deploy(unlockTime, { value: lockedAmount });
|
||||
|
||||
return { lock, unlockTime, lockedAmount, owner, otherAccount };
|
||||
return lock;
|
||||
}
|
||||
|
||||
describe("Deployment", function () {
|
||||
it("Should set the right unlockTime", async function () {
|
||||
const { lock, unlockTime } = await loadFixture(deployOneYearLockFixture);
|
||||
|
||||
const unlockTime = Math.floor(Date.now() / 1000) + ONE_YEAR_IN_SECS;
|
||||
const lock = await deployLock(unlockTime);
|
||||
expect(await lock.unlockTime()).to.equal(unlockTime);
|
||||
});
|
||||
|
||||
it("Should set the right owner", async function () {
|
||||
const { lock, owner } = await loadFixture(deployOneYearLockFixture);
|
||||
|
||||
const unlockTime = Math.floor(Date.now() / 1000) + ONE_YEAR_IN_SECS;
|
||||
const [owner] = await hre.ethers.getSigners();
|
||||
const lock = await deployLock(unlockTime);
|
||||
expect(await lock.owner()).to.equal(owner.address);
|
||||
});
|
||||
|
||||
it("Should receive and store the funds to lock", async function () {
|
||||
const { lock, lockedAmount } = await loadFixture(
|
||||
deployOneYearLockFixture
|
||||
);
|
||||
|
||||
const unlockTime = Math.floor(Date.now() / 1000) + ONE_YEAR_IN_SECS;
|
||||
const lock = await deployLock(unlockTime);
|
||||
expect(await hre.ethers.provider.getBalance(lock.target)).to.equal(
|
||||
lockedAmount
|
||||
);
|
||||
});
|
||||
|
||||
it("Should fail if the unlockTime is not in the future", async function () {
|
||||
// We don't use the fixture here because we want a different deployment
|
||||
const latestTime = await time.latest();
|
||||
const Lock = await hre.ethers.getContractFactory("Lock");
|
||||
await expect(Lock.deploy(latestTime, { value: 1 })).to.be.revertedWith(
|
||||
const latestTime = Math.floor(Date.now() / 1000);
|
||||
await expect(deployLock(latestTime)).to.be.revertedWith(
|
||||
"Unlock time should be in the future"
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe("Withdrawals", function () {
|
||||
describe("Validations", function () {
|
||||
it("Should revert with the right error if called too soon", async function () {
|
||||
const { lock } = await loadFixture(deployOneYearLockFixture);
|
||||
|
||||
await expect(lock.withdraw()).to.be.revertedWith(
|
||||
"You can't withdraw yet"
|
||||
);
|
||||
const unlockTime = Math.floor(Date.now() / 1000) + ONE_YEAR_IN_SECS;
|
||||
const lock = await deployLock(unlockTime);
|
||||
await expect(lock.withdraw()).to.be.revertedWith("You can't withdraw yet");
|
||||
});
|
||||
|
||||
it("Should revert with the right error if called from another account", async function () {
|
||||
const { lock, unlockTime, otherAccount } = await loadFixture(
|
||||
deployOneYearLockFixture
|
||||
);
|
||||
const unlockTime = Math.floor(Date.now() / 1000) + 10; // Unlock in 10 seconds
|
||||
const lock = await deployLock(unlockTime);
|
||||
const [, otherAccount] = await hre.ethers.getSigners();
|
||||
|
||||
// We can increase the time in Hardhat Network
|
||||
await time.increaseTo(unlockTime);
|
||||
// Wait for unlock time
|
||||
await new Promise((resolve) => setTimeout(resolve, 11000));
|
||||
|
||||
// We use lock.connect() to send a transaction from another account
|
||||
await expect(lock.connect(otherAccount).withdraw()).to.be.revertedWith(
|
||||
"You aren't the owner"
|
||||
);
|
||||
});
|
||||
|
||||
it("Shouldn't fail if the unlockTime has arrived and the owner calls it", async function () {
|
||||
const { lock, unlockTime } = await loadFixture(
|
||||
deployOneYearLockFixture
|
||||
);
|
||||
const unlockTime = Math.floor(Date.now() / 1000) + 10; // Unlock in 10 seconds
|
||||
const lock = await deployLock(unlockTime);
|
||||
|
||||
// Transactions are sent using the first signer by default
|
||||
await time.increaseTo(unlockTime);
|
||||
// Wait for unlock time
|
||||
await new Promise((resolve) => setTimeout(resolve, 11000));
|
||||
|
||||
await expect(lock.withdraw()).not.to.be.reverted;
|
||||
});
|
||||
});
|
||||
|
||||
describe("Events", function () {
|
||||
it("Should emit an event on withdrawals", async function () {
|
||||
const { lock, unlockTime, lockedAmount } = await loadFixture(
|
||||
deployOneYearLockFixture
|
||||
);
|
||||
const unlockTime = Math.floor(Date.now() / 1000) + 10; // Unlock in 10 seconds
|
||||
const lock = await deployLock(unlockTime);
|
||||
const [owner] = await hre.ethers.getSigners();
|
||||
|
||||
await time.increaseTo(unlockTime);
|
||||
// Wait for unlock time
|
||||
await new Promise((resolve) => setTimeout(resolve, 11000));
|
||||
|
||||
await expect(lock.withdraw())
|
||||
.to.emit(lock, "Withdrawal")
|
||||
.withArgs(lockedAmount, anyValue); // We accept any value as `when` arg
|
||||
});
|
||||
.withArgs(lockedAmount, anyValue);
|
||||
});
|
||||
|
||||
describe("Transfers", function () {
|
||||
it("Should transfer the funds to the owner", async function () {
|
||||
const { lock, unlockTime, lockedAmount, owner } = await loadFixture(
|
||||
deployOneYearLockFixture
|
||||
);
|
||||
const unlockTime = Math.floor(Date.now() / 1000) + 10; // Unlock in 10 seconds
|
||||
const lock = await deployLock(unlockTime);
|
||||
const [owner] = await hre.ethers.getSigners();
|
||||
|
||||
await time.increaseTo(unlockTime);
|
||||
// Wait for unlock time
|
||||
await new Promise((resolve) => setTimeout(resolve, 11000));
|
||||
|
||||
await expect(lock.withdraw()).to.changeEtherBalances(
|
||||
[owner, lock],
|
||||
|
|
@ -124,4 +101,3 @@ describe("Lock", function () {
|
|||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
|||
127
hardhat/test/Lock.ts.backup
Normal file
127
hardhat/test/Lock.ts.backup
Normal file
|
|
@ -0,0 +1,127 @@
|
|||
import {
|
||||
time,
|
||||
loadFixture,
|
||||
} from "@nomicfoundation/hardhat-toolbox/network-helpers";
|
||||
import { anyValue } from "@nomicfoundation/hardhat-chai-matchers/withArgs";
|
||||
import { expect } from "chai";
|
||||
import hre from "hardhat";
|
||||
|
||||
describe("Lock", function () {
|
||||
// We define a fixture to reuse the same setup in every test.
|
||||
// We use loadFixture to run this setup once, snapshot that state,
|
||||
// and reset Hardhat Network to that snapshot in every test.
|
||||
async function deployOneYearLockFixture() {
|
||||
const ONE_YEAR_IN_SECS = 365 * 24 * 60 * 60;
|
||||
const ONE_GWEI = 1_000_000_000;
|
||||
|
||||
const lockedAmount = ONE_GWEI;
|
||||
const unlockTime = (await time.latest()) + ONE_YEAR_IN_SECS;
|
||||
|
||||
// Contracts are deployed using the first signer/account by default
|
||||
const [owner, otherAccount] = await hre.ethers.getSigners();
|
||||
|
||||
const Lock = await hre.ethers.getContractFactory("Lock");
|
||||
const lock = await Lock.deploy(unlockTime, { value: lockedAmount });
|
||||
|
||||
return { lock, unlockTime, lockedAmount, owner, otherAccount };
|
||||
}
|
||||
|
||||
describe("Deployment", function () {
|
||||
it("Should set the right unlockTime", async function () {
|
||||
const { lock, unlockTime } = await loadFixture(deployOneYearLockFixture);
|
||||
|
||||
expect(await lock.unlockTime()).to.equal(unlockTime);
|
||||
});
|
||||
|
||||
it("Should set the right owner", async function () {
|
||||
const { lock, owner } = await loadFixture(deployOneYearLockFixture);
|
||||
|
||||
expect(await lock.owner()).to.equal(owner.address);
|
||||
});
|
||||
|
||||
it("Should receive and store the funds to lock", async function () {
|
||||
const { lock, lockedAmount } = await loadFixture(
|
||||
deployOneYearLockFixture
|
||||
);
|
||||
|
||||
expect(await hre.ethers.provider.getBalance(lock.target)).to.equal(
|
||||
lockedAmount
|
||||
);
|
||||
});
|
||||
|
||||
it("Should fail if the unlockTime is not in the future", async function () {
|
||||
// We don't use the fixture here because we want a different deployment
|
||||
const latestTime = await time.latest();
|
||||
const Lock = await hre.ethers.getContractFactory("Lock");
|
||||
await expect(Lock.deploy(latestTime, { value: 1 })).to.be.revertedWith(
|
||||
"Unlock time should be in the future"
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe("Withdrawals", function () {
|
||||
describe("Validations", function () {
|
||||
it("Should revert with the right error if called too soon", async function () {
|
||||
const { lock } = await loadFixture(deployOneYearLockFixture);
|
||||
|
||||
await expect(lock.withdraw()).to.be.revertedWith(
|
||||
"You can't withdraw yet"
|
||||
);
|
||||
});
|
||||
|
||||
it("Should revert with the right error if called from another account", async function () {
|
||||
const { lock, unlockTime, otherAccount } = await loadFixture(
|
||||
deployOneYearLockFixture
|
||||
);
|
||||
|
||||
// We can increase the time in Hardhat Network
|
||||
await time.increaseTo(unlockTime);
|
||||
|
||||
// We use lock.connect() to send a transaction from another account
|
||||
await expect(lock.connect(otherAccount).withdraw()).to.be.revertedWith(
|
||||
"You aren't the owner"
|
||||
);
|
||||
});
|
||||
|
||||
it("Shouldn't fail if the unlockTime has arrived and the owner calls it", async function () {
|
||||
const { lock, unlockTime } = await loadFixture(
|
||||
deployOneYearLockFixture
|
||||
);
|
||||
|
||||
// Transactions are sent using the first signer by default
|
||||
await time.increaseTo(unlockTime);
|
||||
|
||||
await expect(lock.withdraw()).not.to.be.reverted;
|
||||
});
|
||||
});
|
||||
|
||||
describe("Events", function () {
|
||||
it("Should emit an event on withdrawals", async function () {
|
||||
const { lock, unlockTime, lockedAmount } = await loadFixture(
|
||||
deployOneYearLockFixture
|
||||
);
|
||||
|
||||
await time.increaseTo(unlockTime);
|
||||
|
||||
await expect(lock.withdraw())
|
||||
.to.emit(lock, "Withdrawal")
|
||||
.withArgs(lockedAmount, anyValue); // We accept any value as `when` arg
|
||||
});
|
||||
});
|
||||
|
||||
describe("Transfers", function () {
|
||||
it("Should transfer the funds to the owner", async function () {
|
||||
const { lock, unlockTime, lockedAmount, owner } = await loadFixture(
|
||||
deployOneYearLockFixture
|
||||
);
|
||||
|
||||
await time.increaseTo(unlockTime);
|
||||
|
||||
await expect(lock.withdraw()).to.changeEtherBalances(
|
||||
[owner, lock],
|
||||
[lockedAmount, -lockedAmount]
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
Loading…
Reference in a new issue