Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Test delegation #189

Open
wants to merge 6 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -66,3 +66,5 @@ build
generated
.DS_Store
test-deploy.sh

tests/.bin
12 changes: 12 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -148,3 +148,15 @@ To build and deploy the subgraphs, run `npm run deploy` for a CLI. You will have
The CLI automatically generates the main subgraph, which is composed of the other subgraph in the `subgraphs` directory. You can also use the CLI to deploy the component subgraphs to the hosted service for faster development and testing.

All of the prompts in the CLI can be provided via options. For more information, run `npm run deploy -- --help`.

## Test setup

Follow the instructions here: https://github.com/LimeChain/matchstick/blob/main/README.md

Tldr. You need to have postgressql installed

## Run tests

`node run-tests.js`

Normally you would run the tests with `npx graph test`, but matchstick is still not very mature and missing some configuration options which means we need to run some workarounds before and after the tests. See scripts/testSetup.js for explanation and implementation
1 change: 1 addition & 0 deletions matchstick.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
manifestPath: subgraphs/main.yaml
8,185 changes: 4,402 additions & 3,783 deletions package-lock.json

Large diffs are not rendered by default.

6 changes: 5 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
"snxData": "bin.js"
},
"scripts": {
"test": "node run-tests.js",
"lint": "eslint src/ scripts/ subgraphs/",
"prepare": "husky install",
"deploy": "node ./scripts/deploy.js"
Expand All @@ -18,7 +19,7 @@
},
"dependencies": {
"@graphprotocol/graph-cli": "0.25.1",
"@graphprotocol/graph-ts": "0.24.1",
"@graphprotocol/graph-ts": "0.27.0",
"@graphql-tools/merge": "^8.2.1",
"chalk": "4.1.2",
"commander": "8.2.0",
Expand All @@ -29,12 +30,15 @@
"devDependencies": {
"@typescript-eslint/eslint-plugin": "4.32.0",
"@typescript-eslint/parser": "4.32.0",
"acorn": "8.8.0",
"eslint": "7.32.0",
"eslint-import-resolver-typescript": "2.5.0",
"eslint-plugin-import": "2.24.2",
"eslint-plugin-prettier": "4.0.0",
"husky": "7.0.2",
"js-yaml": "4.1.0",
"lint-staged": "11.1.2",
"matchstick-as": "0.5.0",
"minify": "7.2.1",
"node-fetch": "^2.6.7",
"prettier": "2.4.1",
Expand Down
15 changes: 15 additions & 0 deletions run-tests.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
const { afterTests, prepareTests } = require('./scripts/testSetup');
const { execSync } = require('child_process');

const run = () => {
prepareTests();
// Providing the -v avoids making github request to check the version, (which leads to rate limit if run frequently enough)
try {
execSync('npx graph test -v 0.5.0', { stdio: 'inherit' });
} catch {
// error will be logged and script will exit, so no need to throw
} finally {
afterTests();
}
};
run();
29 changes: 29 additions & 0 deletions scripts/testSetup.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
const path = require('path');
const fs = require('fs');
const yaml = require('js-yaml');

/**
* Currently matchstick (the testing framework), requires the configuration to be in yaml.
* 1. This function reads the main subgraph configuration file.
* 2. Convert to yaml
* 3. Writes out the yaml file with required "main.yaml" in the root level.
*
* The function below `afterTests` will remove this file.
*
* When matchstick add support for non yaml configs we can remove this workaround
*
*/
const prepareTests = () => {
const main = require('../subgraphs/main.js');
const yamlContent = yaml.dump(main);
fs.writeFileSync(path.join(__dirname, '../subgraphs/main.yaml'), yamlContent);
};

const afterTests = () => {
try {
fs.rmSync(path.join(__dirname, '../subgraphs/main.yaml'));
} catch (error) {
console.log(error);
}
};
module.exports = { prepareTests, afterTests };
51 changes: 51 additions & 0 deletions tests/delegation-test-helpers.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
/* eslint-disable @typescript-eslint/ban-ts-comment,@typescript-eslint/no-array-constructor */
import { newMockEvent } from 'matchstick-as/assembly/index';
import { ethereum, Address } from '@graphprotocol/graph-ts';
import { strToBytes } from '../src/lib/helpers';
import {
Approval as DelegateApprovalEvent,
WithdrawApproval as DelegateWithdrawApprovalEvent,
} from '../generated/subgraphs/delegation/delegation_DelegateApprovals_0/DelegateApprovals';

export function createDelegateApprovalEvent(
authoriser: string,
delegate: string,
action: string,
): DelegateApprovalEvent {
// @ts-ignore
let newDelegateApprovalEvent = changetype<DelegateApprovalEvent>(newMockEvent());
newDelegateApprovalEvent.parameters = new Array();
let authoriserParam = new ethereum.EventParam(
'authoriser',
ethereum.Value.fromAddress(Address.fromString(authoriser)),
);
let delegateParam = new ethereum.EventParam('delegate', ethereum.Value.fromAddress(Address.fromString(delegate)));

let actionParam = new ethereum.EventParam('action', ethereum.Value.fromBytes(strToBytes(action)));

newDelegateApprovalEvent.parameters.push(authoriserParam);
newDelegateApprovalEvent.parameters.push(delegateParam);
newDelegateApprovalEvent.parameters.push(actionParam);
return newDelegateApprovalEvent;
}
export function createDelegateWithdrawApprovalEvent(
authoriser: string,
delegate: string,
action: string,
): DelegateWithdrawApprovalEvent {
// @ts-ignore
let newDelegateWithdrawApprovalEvent = changetype<DelegateWithdrawApprovalEvent>(newMockEvent());
newDelegateWithdrawApprovalEvent.parameters = new Array();
let authoriserParam = new ethereum.EventParam(
'authoriser',
ethereum.Value.fromAddress(Address.fromString(authoriser)),
);
let delegateParam = new ethereum.EventParam('delegate', ethereum.Value.fromAddress(Address.fromString(delegate)));

let actionParam = new ethereum.EventParam('action', ethereum.Value.fromBytes(strToBytes(action)));

newDelegateWithdrawApprovalEvent.parameters.push(authoriserParam);
newDelegateWithdrawApprovalEvent.parameters.push(delegateParam);
newDelegateWithdrawApprovalEvent.parameters.push(actionParam);
return newDelegateWithdrawApprovalEvent;
}
145 changes: 145 additions & 0 deletions tests/delegation.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
/* eslint-disable @typescript-eslint/ban-ts-comment,@typescript-eslint/no-array-constructor */
import { assert, clearStore, test } from 'matchstick-as/assembly/index';
import { Address } from '@graphprotocol/graph-ts';
import { handleDelegateApproval, handleDelegateWithdrawApproval } from '../src/delegation';
import { DelegatedWallet } from '../generated/subgraphs/delegation/schema';
import { createDelegateApprovalEvent, createDelegateWithdrawApprovalEvent } from './delegation-test-helpers';

const defaultAuthoriser = '0xb64ff7a4a33acdf48d97dab0d764afd0f6176882';
const defaultDelegate = '0x0000000000000000000000000000000000000000';
const defaultId = `${defaultAuthoriser}-${defaultDelegate}`;

test('Handle new delegate ApproveAll delegate', () => {
let delegateApprovalEvent = createDelegateApprovalEvent(defaultAuthoriser, defaultDelegate, 'ApproveAll');

handleDelegateApproval(delegateApprovalEvent);
assert.fieldEquals('DelegatedWallet', defaultId, 'id', defaultId);
assert.fieldEquals('DelegatedWallet', defaultId, 'authoriser', defaultAuthoriser);
assert.fieldEquals('DelegatedWallet', defaultId, 'delegate', defaultDelegate);
assert.fieldEquals('DelegatedWallet', defaultId, 'canMint', 'true');
assert.fieldEquals('DelegatedWallet', defaultId, 'canExchange', 'true');
assert.fieldEquals('DelegatedWallet', defaultId, 'canBurn', 'true');
assert.fieldEquals('DelegatedWallet', defaultId, 'canClaim', 'true');

clearStore();
});

test('Update delegate to ApproveAll delegate', () => {
const existing = new DelegatedWallet(defaultId);
existing.authoriser = Address.fromString(defaultAuthoriser);
existing.delegate = Address.fromString(defaultDelegate);
existing.canBurn = true;
existing.canMint = false;
existing.save();
// ensure existing canMint is false
assert.fieldEquals('DelegatedWallet', defaultId, 'canMint', 'false');

let delegateApprovalEvent = createDelegateApprovalEvent(defaultAuthoriser, defaultDelegate, 'ApproveAll');

handleDelegateApproval(delegateApprovalEvent);

assert.fieldEquals('DelegatedWallet', defaultId, 'id', defaultId);
assert.fieldEquals('DelegatedWallet', defaultId, 'authoriser', defaultAuthoriser);
assert.fieldEquals('DelegatedWallet', defaultId, 'delegate', defaultDelegate);
assert.fieldEquals('DelegatedWallet', defaultId, 'canMint', 'true');
assert.fieldEquals('DelegatedWallet', defaultId, 'canExchange', 'true');
assert.fieldEquals('DelegatedWallet', defaultId, 'canBurn', 'true');
assert.fieldEquals('DelegatedWallet', defaultId, 'canClaim', 'true');
clearStore();
});
test('Handle new IssueForAddress delegate', () => {
let delegateApprovalEvent = createDelegateApprovalEvent(defaultAuthoriser, defaultDelegate, 'IssueForAddress');

handleDelegateApproval(delegateApprovalEvent);

assert.fieldEquals('DelegatedWallet', defaultId, 'id', defaultId);
assert.fieldEquals('DelegatedWallet', defaultId, 'authoriser', defaultAuthoriser);
assert.fieldEquals('DelegatedWallet', defaultId, 'delegate', defaultDelegate);
assert.fieldEquals('DelegatedWallet', defaultId, 'canMint', 'true');
// Matchstick doesn't have a way to assert undefined values :(
// assert.fieldEquals('DelegatedWallet', defaultId, 'canClaim', 'false');

clearStore();
});
test('Handle new BurnForAddress delegate', () => {
let delegateApprovalEvent = createDelegateApprovalEvent(defaultAuthoriser, defaultDelegate, 'BurnForAddress');

handleDelegateApproval(delegateApprovalEvent);

assert.fieldEquals('DelegatedWallet', defaultId, 'id', defaultId);
assert.fieldEquals('DelegatedWallet', defaultId, 'authoriser', defaultAuthoriser);
assert.fieldEquals('DelegatedWallet', defaultId, 'delegate', defaultDelegate);
assert.fieldEquals('DelegatedWallet', defaultId, 'canBurn', 'true');
// Matchstick doesn't have a way to assert undefined values :(
// assert.fieldEquals('DelegatedWallet', defaultId, 'canClaim', 'false');

clearStore();
});
test('Handle new ClaimForAddress delegate', () => {
let delegateApprovalEvent = createDelegateApprovalEvent(defaultAuthoriser, defaultDelegate, 'ClaimForAddress');

handleDelegateApproval(delegateApprovalEvent);

assert.fieldEquals('DelegatedWallet', defaultId, 'id', defaultId);
assert.fieldEquals('DelegatedWallet', defaultId, 'authoriser', defaultAuthoriser);
assert.fieldEquals('DelegatedWallet', defaultId, 'delegate', defaultDelegate);
assert.fieldEquals('DelegatedWallet', defaultId, 'canClaim', 'true');
// Matchstick doesn't have a way to assert undefined values :(
// assert.fieldEquals('DelegatedWallet', defaultId, 'canBurn', 'false');

clearStore();
});
test('Handle new ExchangeForAddress delegate', () => {
let delegateApprovalEvent = createDelegateApprovalEvent(defaultAuthoriser, defaultDelegate, 'ExchangeForAddress');

handleDelegateApproval(delegateApprovalEvent);

assert.fieldEquals('DelegatedWallet', defaultId, 'id', defaultId);
assert.fieldEquals('DelegatedWallet', defaultId, 'authoriser', defaultAuthoriser);
assert.fieldEquals('DelegatedWallet', defaultId, 'delegate', defaultDelegate);
assert.fieldEquals('DelegatedWallet', defaultId, 'canExchange', 'true');
// Matchstick doesn't have a way to assert undefined values :(
// assert.fieldEquals('DelegatedWallet', defaultId, 'canBurn', 'false');

clearStore();
});

test('Withdraw mint delegation', () => {
const existing = new DelegatedWallet(defaultId);
existing.authoriser = Address.fromString(defaultAuthoriser);
existing.delegate = Address.fromString(defaultDelegate);
existing.canBurn = true;
existing.canMint = true;
existing.canExchange = true;
existing.canClaim = true;
existing.save();
// ensure existing canMint is true
assert.fieldEquals('DelegatedWallet', defaultId, 'canMint', 'true');

let delegateApprovalEvent = createDelegateWithdrawApprovalEvent(
defaultAuthoriser,
defaultDelegate,
'IssueForAddress',
);

handleDelegateWithdrawApproval(delegateApprovalEvent);
assert.fieldEquals('DelegatedWallet', defaultId, 'id', defaultId);
assert.fieldEquals('DelegatedWallet', defaultId, 'authoriser', defaultAuthoriser);
assert.fieldEquals('DelegatedWallet', defaultId, 'delegate', defaultDelegate);
assert.fieldEquals('DelegatedWallet', defaultId, 'canMint', 'false');

clearStore();
});

test('Withdraw before approval delegation', () => {
let delegateApprovalEvent = createDelegateWithdrawApprovalEvent(
defaultAuthoriser,
defaultDelegate,
'IssueForAddress',
);

handleDelegateWithdrawApproval(delegateApprovalEvent);
assert.notInStore('DelegatedWallet', defaultId);

clearStore();
});