mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-07-26 22:56:43 +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:
|
on: [push, pull_request]
|
||||||
pull_request:
|
# pull_request:
|
||||||
types: [closed]
|
# types: [closed]
|
||||||
branches: [master]
|
# branches: [master]
|
||||||
paths: ["**"]
|
# paths: ["**"]
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
deploy:
|
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
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
steps:
|
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 .
|
docker build -t $IMAGE_NAME:with-contract-latest -t $IMAGE_NAME:with-contract-$COMMIT_SHA -f Dockerfile.with-contract .
|
||||||
|
|
||||||
rm Dockerfile.with-contract
|
rm Dockerfile.with-contract
|
||||||
|
rm -rf ./geth_data
|
||||||
|
|
||||||
docker images -a
|
docker images -a
|
||||||
- name: push image
|
- name: push image
|
||||||
run: |
|
run: |
|
||||||
docker push $IMAGE_NAME:with-contract-latest
|
docker push $IMAGE_NAME:with-contract-latest
|
||||||
docker push $IMAGE_NAME:with-contract-$COMMIT_SHA
|
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.
|
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.
|
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
|
## Go Ethereum
|
||||||
|
|
||||||
Golang execution layer implementation of the Ethereum protocol.
|
Golang execution layer implementation of the Ethereum protocol.
|
||||||
|
|
|
||||||
|
|
@ -1,127 +1,103 @@
|
||||||
import {
|
|
||||||
time,
|
|
||||||
loadFixture,
|
|
||||||
} from "@nomicfoundation/hardhat-toolbox/network-helpers";
|
|
||||||
import { anyValue } from "@nomicfoundation/hardhat-chai-matchers/withArgs";
|
import { anyValue } from "@nomicfoundation/hardhat-chai-matchers/withArgs";
|
||||||
import { expect } from "chai";
|
import { expect } from "chai";
|
||||||
import hre from "hardhat";
|
import hre from "hardhat";
|
||||||
|
|
||||||
describe("Lock", function () {
|
describe("Lock", function () {
|
||||||
// We define a fixture to reuse the same setup in every test.
|
const ONE_YEAR_IN_SECS = 365 * 24 * 60 * 60;
|
||||||
// We use loadFixture to run this setup once, snapshot that state,
|
const ONE_GWEI = 1_000_000_000;
|
||||||
// and reset Hardhat Network to that snapshot in every test.
|
const lockedAmount = ONE_GWEI;
|
||||||
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 hre.ethers.getContractFactory("Lock");
|
||||||
const lock = await Lock.deploy(unlockTime, { value: lockedAmount });
|
const lock = await Lock.deploy(unlockTime, { value: lockedAmount });
|
||||||
|
return lock;
|
||||||
return { lock, unlockTime, lockedAmount, owner, otherAccount };
|
|
||||||
}
|
}
|
||||||
|
|
||||||
describe("Deployment", function () {
|
describe("Deployment", function () {
|
||||||
it("Should set the right unlockTime", async 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);
|
expect(await lock.unlockTime()).to.equal(unlockTime);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("Should set the right owner", async function () {
|
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);
|
expect(await lock.owner()).to.equal(owner.address);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("Should receive and store the funds to lock", async function () {
|
it("Should receive and store the funds to lock", async function () {
|
||||||
const { lock, lockedAmount } = await loadFixture(
|
const unlockTime = Math.floor(Date.now() / 1000) + ONE_YEAR_IN_SECS;
|
||||||
deployOneYearLockFixture
|
const lock = await deployLock(unlockTime);
|
||||||
);
|
|
||||||
|
|
||||||
expect(await hre.ethers.provider.getBalance(lock.target)).to.equal(
|
expect(await hre.ethers.provider.getBalance(lock.target)).to.equal(
|
||||||
lockedAmount
|
lockedAmount
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("Should fail if the unlockTime is not in the future", async function () {
|
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 = Math.floor(Date.now() / 1000);
|
||||||
const latestTime = await time.latest();
|
await expect(deployLock(latestTime)).to.be.revertedWith(
|
||||||
const Lock = await hre.ethers.getContractFactory("Lock");
|
|
||||||
await expect(Lock.deploy(latestTime, { value: 1 })).to.be.revertedWith(
|
|
||||||
"Unlock time should be in the future"
|
"Unlock time should be in the future"
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("Withdrawals", function () {
|
describe("Withdrawals", function () {
|
||||||
describe("Validations", function () {
|
it("Should revert with the right error if called too soon", async function () {
|
||||||
it("Should revert with the right error if called too soon", async function () {
|
const unlockTime = Math.floor(Date.now() / 1000) + ONE_YEAR_IN_SECS;
|
||||||
const { lock } = await loadFixture(deployOneYearLockFixture);
|
const lock = await deployLock(unlockTime);
|
||||||
|
await expect(lock.withdraw()).to.be.revertedWith("You can't withdraw yet");
|
||||||
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 revert with the right error if called from another account", async function () {
|
||||||
it("Should emit an event on withdrawals", async function () {
|
const unlockTime = Math.floor(Date.now() / 1000) + 10; // Unlock in 10 seconds
|
||||||
const { lock, unlockTime, lockedAmount } = await loadFixture(
|
const lock = await deployLock(unlockTime);
|
||||||
deployOneYearLockFixture
|
const [, otherAccount] = await hre.ethers.getSigners();
|
||||||
);
|
|
||||||
|
|
||||||
await time.increaseTo(unlockTime);
|
// Wait for unlock time
|
||||||
|
await new Promise((resolve) => setTimeout(resolve, 11000));
|
||||||
|
|
||||||
await expect(lock.withdraw())
|
await expect(lock.connect(otherAccount).withdraw()).to.be.revertedWith(
|
||||||
.to.emit(lock, "Withdrawal")
|
"You aren't the owner"
|
||||||
.withArgs(lockedAmount, anyValue); // We accept any value as `when` arg
|
);
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("Transfers", function () {
|
it("Shouldn't fail if the unlockTime has arrived and the owner calls it", async function () {
|
||||||
it("Should transfer the funds to the owner", async function () {
|
const unlockTime = Math.floor(Date.now() / 1000) + 10; // Unlock in 10 seconds
|
||||||
const { lock, unlockTime, lockedAmount, owner } = await loadFixture(
|
const lock = await deployLock(unlockTime);
|
||||||
deployOneYearLockFixture
|
|
||||||
);
|
|
||||||
|
|
||||||
await time.increaseTo(unlockTime);
|
// Wait for unlock time
|
||||||
|
await new Promise((resolve) => setTimeout(resolve, 11000));
|
||||||
|
|
||||||
await expect(lock.withdraw()).to.changeEtherBalances(
|
await expect(lock.withdraw()).not.to.be.reverted;
|
||||||
[owner, lock],
|
});
|
||||||
[lockedAmount, -lockedAmount]
|
|
||||||
);
|
it("Should emit an event on withdrawals", async function () {
|
||||||
});
|
const unlockTime = Math.floor(Date.now() / 1000) + 10; // Unlock in 10 seconds
|
||||||
|
const lock = await deployLock(unlockTime);
|
||||||
|
const [owner] = await hre.ethers.getSigners();
|
||||||
|
|
||||||
|
// Wait for unlock time
|
||||||
|
await new Promise((resolve) => setTimeout(resolve, 11000));
|
||||||
|
|
||||||
|
await expect(lock.withdraw())
|
||||||
|
.to.emit(lock, "Withdrawal")
|
||||||
|
.withArgs(lockedAmount, anyValue);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Should transfer the funds to the owner", async function () {
|
||||||
|
const unlockTime = Math.floor(Date.now() / 1000) + 10; // Unlock in 10 seconds
|
||||||
|
const lock = await deployLock(unlockTime);
|
||||||
|
const [owner] = await hre.ethers.getSigners();
|
||||||
|
|
||||||
|
// Wait for unlock time
|
||||||
|
await new Promise((resolve) => setTimeout(resolve, 11000));
|
||||||
|
|
||||||
|
await expect(lock.withdraw()).to.changeEtherBalances(
|
||||||
|
[owner, lock],
|
||||||
|
[lockedAmount, -lockedAmount]
|
||||||
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
||||||
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