Skip to content

Commit 7df6669

Browse files
authored
feat: add support for scenario testing (#660)
* feat: add support for scenario testing Signed-off-by: Tomás Migone <[email protected]>
1 parent c7e1fa1 commit 7df6669

File tree

9 files changed

+353
-79
lines changed

9 files changed

+353
-79
lines changed

README.md

Lines changed: 3 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -76,72 +76,11 @@ yarn build
7676

7777
# Testing
7878

79-
Testing is done with the following stack:
79+
For testing details see [TESTING.md](./TESTING.md).
8080

81-
- [Waffle](https://getwaffle.io/)
82-
- [Hardhat](https://hardhat.org/)
83-
- [Typescript](https://www.typescriptlang.org/)
84-
- [Ethers](https://docs.ethers.io/v5/)
85-
86-
## Contracts
87-
88-
To test all the smart contracts, use `yarn test`.
89-
To test a single file run: `npx hardhat test test/<FILE_NAME>.ts`
90-
91-
## E2E Testing
92-
93-
End to end tests are also available and can be run against a local network or a live network. These can be useful to validate a protocol deployment is configured and working as expected.
94-
95-
There are several types of e2e tests which can be run separately:
96-
- **deployment/config**
97-
- Test the configuration of deployed contracts (parameters that don't change over time).
98-
- Can be run against any network at any time and the tests should pass.
99-
- Only read only interactions with the blockchain.
100-
- Example: a test validating the curation default reserve ratio matches the value in the graph config file.
101-
- **deployment/init**
102-
- Test the initialization of deployed contracts (parameters that change with protocol usage).
103-
- Can be run against a "fresh" protocol deployment. Running these tests against a protocol with pre-existing state will probably fail.
104-
- Only read only interactions with the blockchain.
105-
- Example: a test validating that the GRT total supply equals 10B, this is only true on a freshly deployed protocol until the first allocation is closed and protocol issuance kicks in.
106-
- **scenarios**
107-
- Test the execution of common protocol actions.
108-
- Can be run against any network at any time and the tests should pass.
109-
- Read and write interactions with the blockchain. _Requires an account with sufficient balance!_
110-
- Example: a test validating that a user can add signal to a subgraph.
111-
112-
### Hardhat local node
113-
114-
To run all e2e tests against a hardhat local node run:
115-
116-
```bash
117-
yarn test:e2e
118-
```
119-
120-
The command will perform the following actions:
121-
122-
- Start a hardhat node (localhost)
123-
- Run `migrate:accounts` hardhat task to create keys for all protocol roles (deployer, governor, arbiter, etc). This currently doesn't support multisig accounts.
124-
- Run `migrate` hardhat task to deploy the protocol
125-
- Run `migrate:ownership` hardhat task to transfer ownership of governed contracts to the governor
126-
- Run `migrate:unpause` to unpause the protocol
127-
- Run `e2e` hardhat task to run all e2e tests
128-
129-
### Other networks
130-
131-
To run tests against a live testnet or even mainnet run:
132-
133-
```bash
134-
# All e2e tests
135-
npx hardhat e2e --network <network> --graph-config config/graph.<network>.yml
136-
137-
# Only deployment config tests
138-
npx hardhat e2e:config --network <network> --graph-config config/graph.<network>.yml
139-
140-
# Only deployment init tests
141-
npx hardhat e2e:init --network <network> --graph-config config/graph.<network>.yml
142-
```
81+
# Deploying Contracts
14382

144-
Note that this command will only run the tests so you need to be sure the protocol is already deployed and the graph config file and address book files are up to date.
83+
In order to run deployments, see [DEPLOYMENT.md](./DEPLOYMENT.md).
14584

14685
# Interacting with the contracts
14786

@@ -187,10 +126,6 @@ Considerations when forking a chain:
187126

188127
- When running on the `localhost` network it will use by default a deterministic seed for testing purposes. If you want to connect to a local node that is forking while retaining the capability to impersonate accounts or use local accounts you need to set the FORK=true environment variable.
189128

190-
# Deploying Contracts
191-
192-
In order to run deployments, see [`./DEPLOYMENT.md`](./DEPLOYMENT.md).
193-
194129
# Contributing
195130

196131
Contributions are welcomed and encouraged! You can do so by:

TESTING.md

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
# Testing
2+
3+
Testing is done with the following stack:
4+
5+
- [Waffle](https://getwaffle.io/)
6+
- [Hardhat](https://hardhat.org/)
7+
- [Typescript](https://www.typescriptlang.org/)
8+
- [Ethers](https://docs.ethers.io/v5/)
9+
10+
## Unit testing
11+
12+
To test all the smart contracts, use `yarn test`.
13+
To test a single file run: `npx hardhat test test/<FILE_NAME>.ts`
14+
15+
## E2E Testing
16+
17+
End to end tests are also available and can be run against a local network or a live network. These can be useful to validate that a protocol deployment is configured and working as expected.
18+
19+
There are several types of e2e tests which can be run separately:
20+
- **deployment/config**
21+
- Test the configuration of deployed contracts (parameters that don't change over time).
22+
- Can be run against any network at any time and the tests should pass.
23+
- Only read only interactions with the blockchain.
24+
- Example: a test validating the curation default reserve ratio matches the value in the graph config file.
25+
- **deployment/init**
26+
- Test the initialization of deployed contracts (parameters that change with protocol usage).
27+
- Can be run against a "fresh" protocol deployment. Running these tests against a protocol with pre-existing state will probably fail.
28+
- Only read only interactions with the blockchain.
29+
- Example: a test validating that the GRT total supply equals 10B, this is only true on a freshly deployed protocol until the first allocation is closed and protocol issuance kicks in.
30+
- **scenarios**
31+
- Test the execution of common protocol actions.
32+
- Can be run against any network at any time and the tests should pass.
33+
- Read and write interactions with the blockchain. _Requires an account with sufficient balance!_
34+
- Example: a test validating that a user can add signal to a subgraph.
35+
36+
### Hardhat local node
37+
38+
To run all e2e tests against a hardhat local node run:
39+
40+
```bash
41+
yarn test:e2e
42+
```
43+
44+
The command will perform the following actions:
45+
46+
- Start a hardhat node (localhost)
47+
- Run `migrate:accounts` hardhat task to create keys for all protocol roles (deployer, governor, arbiter, etc). This currently doesn't support multisig accounts.
48+
- Run `migrate` hardhat task to deploy the protocol
49+
- Run `migrate:ownership` hardhat task to transfer ownership of governed contracts to the governor
50+
- Run `migrate:unpause` to unpause the protocol
51+
- Run `e2e` hardhat task to run all e2e tests, including scenarios
52+
53+
### Other networks
54+
55+
To run tests against a live testnet or even mainnet run:
56+
57+
```bash
58+
# All e2e tests
59+
npx hardhat e2e --network <network> --graph-config config/graph.<network>.yml
60+
61+
# Only deployment config tests
62+
npx hardhat e2e:config --network <network> --graph-config config/graph.<network>.yml
63+
64+
# Only deployment init tests
65+
npx hardhat e2e:init --network <network> --graph-config config/graph.<network>.yml
66+
67+
# Only a specific scenario
68+
npx hardhat e2e:scenario <scenario> --network <network> --graph-config config/graph.<network>.yml
69+
```
70+
71+
Note that this command will only run the tests so you need to be sure the protocol is already deployed and the graph config file and address book files are up to date.
72+
73+
### How to add scenarios
74+
75+
Scenarios are defined by an optional script and a test file:
76+
77+
- Optional ts script
78+
- The objective of this script is to perform actions on the protocol to advance it's state to the desired one.
79+
- Should follow hardhat script convention.
80+
- Should be named e2e/scenarios/{scenario-name}.ts.
81+
- They run before the test file.
82+
- Test file
83+
- Should be named e2e/scenarios/{scenario-name}.test.ts.
84+
- Standard chai/mocha/hardhat/ethers test file.

e2e/scenarios/lib/curation.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import { BigNumberish, Signer } from 'ethers'
2+
import { NetworkContracts } from '../../../cli/contracts'
3+
4+
export const signal = async (
5+
contracts: NetworkContracts,
6+
curator: Signer,
7+
subgraphId: string,
8+
amount: BigNumberish,
9+
): Promise<void> => {
10+
const { GNS } = contracts
11+
12+
// Add signal
13+
const tx = await GNS.connect(curator).mintSignal(subgraphId, amount, 0)
14+
await tx.wait()
15+
}

e2e/scenarios/lib/staking.ts

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,18 @@
1-
import hre from 'hardhat'
2-
import { Staking } from '../../../build/types/Staking'
1+
import { BigNumberish, Signer } from 'ethers'
2+
import { NetworkContracts } from '../../../cli/contracts'
33

4-
export const stake = async (amountWei: string): Promise<void> => {
5-
const graph = hre.graph()
4+
export const stake = async (
5+
contracts: NetworkContracts,
6+
indexer: Signer,
7+
amount: BigNumberish,
8+
): Promise<void> => {
9+
const { GraphToken, Staking } = contracts
610

711
// Approve
8-
const stakeAmountWei = hre.ethers.utils.parseEther(amountWei).toString()
9-
await graph.contracts.GraphToken.approve(graph.contracts.Staking.address, stakeAmountWei)
12+
const txApprove = await GraphToken.connect(indexer).approve(Staking.address, amount)
13+
await txApprove.wait()
1014

1115
// Stake
12-
await graph.contracts.Staking.stake(stakeAmountWei)
16+
const txStake = await Staking.connect(indexer).stake(amount)
17+
await txStake.wait()
1318
}

e2e/scenarios/lib/subgraph.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import { Signer, utils } from 'ethers'
2+
import { NetworkContracts } from '../../../cli/contracts'
3+
4+
const { hexlify, randomBytes } = utils
5+
6+
export const publishNewSubgraph = async (
7+
contracts: NetworkContracts,
8+
publisher: Signer,
9+
deploymentId: string,
10+
): Promise<void> => {
11+
const tx = await contracts.GNS.connect(publisher).publishNewSubgraph(
12+
deploymentId,
13+
hexlify(randomBytes(32)),
14+
hexlify(randomBytes(32)),
15+
)
16+
await tx.wait()
17+
}

e2e/scenarios/lib/token.ts

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import { BigNumberish, ContractReceipt, Signer } from 'ethers'
2+
import { NetworkContracts } from '../../../cli/contracts'
3+
4+
export const airdrop = async (
5+
contracts: NetworkContracts,
6+
sender: Signer,
7+
beneficiaries: string[],
8+
amount: BigNumberish,
9+
): Promise<void> => {
10+
const { GraphToken } = contracts
11+
12+
const txs: Promise<ContractReceipt>[] = []
13+
14+
for (const beneficiary of beneficiaries) {
15+
const tx = await GraphToken.connect(sender).transfer(beneficiary, amount)
16+
txs.push(tx.wait())
17+
}
18+
await Promise.all(txs)
19+
}

e2e/scenarios/scenario1.test.ts

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
import { SignerWithAddress } from '@nomiclabs/hardhat-ethers/signers'
2+
import { expect } from 'chai'
3+
import { solidityKeccak256 } from 'ethers/lib/utils'
4+
import hre from 'hardhat'
5+
import { fixture } from './scenario1'
6+
7+
describe('Scenario 1', () => {
8+
const {
9+
contracts: { GraphToken, Staking, GNS },
10+
getTestAccounts,
11+
} = hre.graph()
12+
13+
let indexer1: SignerWithAddress
14+
let indexer2: SignerWithAddress
15+
let curator1: SignerWithAddress
16+
let curator2: SignerWithAddress
17+
let curator3: SignerWithAddress
18+
let subgraphOwner: SignerWithAddress
19+
20+
let indexers: SignerWithAddress[] = []
21+
let curators: SignerWithAddress[] = []
22+
23+
before(async () => {
24+
;[indexer1, indexer2, subgraphOwner, curator1, curator2, curator3] = await getTestAccounts()
25+
indexers = [indexer1, indexer2]
26+
curators = [curator1, curator2, curator3]
27+
})
28+
29+
describe('GRT balances', () => {
30+
it('indexer1 should match airdropped amount minus staked', async function () {
31+
const balance = await GraphToken.balanceOf(indexer1.address)
32+
expect(balance).eq(fixture.grtAmount.sub(fixture.indexer1.stake))
33+
})
34+
35+
it('indexer2 should match airdropped amount minus staked', async function () {
36+
const balance = await GraphToken.balanceOf(indexer2.address)
37+
expect(balance).eq(fixture.grtAmount.sub(fixture.indexer2.stake))
38+
})
39+
40+
it('curator should match airdropped amount', async function () {
41+
for (const account of curators) {
42+
const balance = await GraphToken.balanceOf(account.address)
43+
expect(balance).eq(fixture.grtAmount)
44+
}
45+
})
46+
})
47+
48+
describe('Staking', () => {
49+
it('indexer1 should have tokens staked', async function () {
50+
const tokensStaked = (await Staking.stakes(indexer1.address)).tokensStaked
51+
expect(tokensStaked).eq(fixture.indexer1.stake)
52+
})
53+
it('indexer2 should have tokens staked', async function () {
54+
const tokensStaked = (await Staking.stakes(indexer2.address)).tokensStaked
55+
expect(tokensStaked).eq(fixture.indexer2.stake)
56+
})
57+
})
58+
59+
// describe('Subgraphs', () => {
60+
// for (const subgraphDeploymentId of fixture.subgraphs) {
61+
// it(`${subgraphDeploymentId} is published`, async function () {
62+
// const seqID = await GNS.nextAccountSeqID(subgraphOwner.address)
63+
// const subgraphId = solidityKeccak256(['address', 'uint256'], [subgraphOwner.address, seqID])
64+
65+
// await GNS.subgraphs(subgraphDeploymentId)
66+
67+
// const isPublished = await GNS.isPublished(subgraphId)
68+
// expect(isPublished).eq(true)
69+
// })
70+
// }
71+
// })
72+
})

0 commit comments

Comments
 (0)