Update graph-node tests to use dummy indexer (#65)

* Update test to call handlers to use dummy indexer

* Update graph-node tests to use dummy indexer
This commit is contained in:
prathamesh0 2021-11-29 17:51:16 +05:30 committed by nabarun
parent 94e9182dd3
commit 970092ece2
9 changed files with 301 additions and 74 deletions

View File

@ -7,11 +7,12 @@ import chai, { assert, expect } from 'chai';
import spies from 'chai-spies';
import { utils } from 'ethers';
import { getDummyEventData, getTestDatabase } from '../test/utils';
import { getDummyEventData, getDummyGraphData, getTestDatabase, getTestIndexer } from '../test/utils';
import abi from '../test/subgraph/example1/build/Example1/abis/Example1.json';
import { instantiate } from './loader';
import { createEvent, createBlock, Block } from './utils';
import { Database } from './database';
import { Indexer } from '../test/utils/indexer';
chai.use(spies);
@ -20,12 +21,21 @@ const sandbox = chai.spy.sandbox();
describe('call handler in mapping code', () => {
let exports: any;
let db: Database;
let indexer: Indexer;
// Create dummy event data.
// Create dummy test data.
const dummyEventData = getDummyEventData();
const dummyGraphData = getDummyGraphData();
before(async () => {
db = getTestDatabase();
indexer = getTestIndexer();
sandbox.on(indexer, 'createDiffStaged', (contractAddress: string, blockHash: string, data: any) => {
assert(contractAddress);
assert(blockHash);
assert(data);
});
sandbox.on(db, 'getEntity', (blockHash: string, entityString: string, idString: string) => {
assert(blockHash);
@ -34,20 +44,12 @@ describe('call handler in mapping code', () => {
});
sandbox.on(db, 'fromGraphEntity', async (instanceExports: any, block: Block, entity: string, entityInstance: any) => {
const entityFields = [
{ type: 'varchar', propertyName: 'id' },
{ type: 'bigint', propertyName: 'count' },
{ type: 'varchar', propertyName: 'paramString' },
{ type: 'integer', propertyName: 'paramInt' },
{ type: 'bigint', propertyName: 'paramBigInt' },
{ type: 'boolean', propertyName: 'paramBoolean' },
{ type: 'varchar', propertyName: 'paramBytes' },
{ type: 'numeric', propertyName: 'paramBigDecimal' },
{ type: 'varchar', propertyName: 'related' },
{ type: 'varchar', propertyName: 'manyRelated' }
];
assert(instanceExports);
assert(block);
assert(entity);
assert(entityInstance);
return db.getEntityValues(instanceExports, block, entityInstance, entityFields);
return {};
});
sandbox.on(db, 'saveEntity', (entity: string, data: any) => {
@ -58,19 +60,23 @@ describe('call handler in mapping code', () => {
it('should load the subgraph example wasm', async () => {
const filePath = path.resolve(__dirname, '../test/subgraph/example1/build/Example1/Example1.wasm');
const instance = await instantiate(db, { event: { block: dummyEventData.block } }, filePath);
const instance = await instantiate(
db,
indexer,
{ event: { block: dummyEventData.block } },
filePath,
dummyGraphData
);
exports = instance.exports;
});
it('should execute the event handler function', async () => {
const {
_start,
handleTest
} = exports;
const { _start } = exports;
// Important to call _start for built subgraphs on instantiation!
// TODO: Check api version https://github.com/graphprotocol/graph-node/blob/6098daa8955bdfac597cec87080af5449807e874/runtime/wasm/src/module/mod.rs#L533
_start();
});
it('should execute the event handler function', async () => {
const { handleTest } = exports;
// Create event params data.
const contractInterface = new utils.Interface(abi);
@ -94,16 +100,13 @@ describe('call handler in mapping code', () => {
expect(db.getEntity).to.have.been.called();
expect(db.fromGraphEntity).to.have.been.called();
expect(db.saveEntity).to.have.been.called();
expect(indexer.createDiffStaged).to.have.been.called();
});
it('should execute the block handler function', async () => {
const { _start, handleBlock } = exports;
const { handleBlock } = exports;
const blockData = dummyEventData.block;
// Important to call _start for built subgraphs on instantiation!
// TODO: Check api version https://github.com/graphprotocol/graph-node/blob/6098daa8955bdfac597cec87080af5449807e874/runtime/wasm/src/module/mod.rs#L533
_start();
// Create an ethereum block to be passed to the handler.
const block = await createBlock(exports, blockData);

View File

@ -13,8 +13,9 @@ import { createEvent, Block, createBlock } from './utils';
import edenNetworkAbi from '../test/subgraph/eden/EdenNetwork/abis/EdenNetwork.json';
import merkleDistributorAbi from '../test/subgraph/eden/EdenNetworkDistribution/abis/MerkleDistributor.json';
import distributorGovernanceAbi from '../test/subgraph/eden/EdenNetworkGovernance/abis/DistributorGovernance.json';
import { getDummyEventData, getTestDatabase } from '../test/utils';
import { getDummyEventData, getTestDatabase, getTestIndexer } from '../test/utils';
import { Database } from './database';
import { Indexer } from '../test/utils/indexer';
const ZERO_ADDRESS = '0x0000000000000000000000000000000000000000';
@ -24,12 +25,20 @@ const sandbox = chai.spy.sandbox();
describe('eden wasm loader tests', async () => {
let db: Database;
let indexer: Indexer;
// Create dummy event data.
const dummyEventData = getDummyEventData();
before(async () => {
db = getTestDatabase();
indexer = getTestIndexer();
sandbox.on(indexer, 'createDiffStaged', (contractAddress: string, blockHash: string, data: any) => {
assert(contractAddress);
assert(blockHash);
assert(data);
});
sandbox.on(db, 'getEntity', (blockHash: string, entityString: string, idString: string) => {
assert(blockHash);
@ -69,7 +78,13 @@ describe('eden wasm loader tests', async () => {
it('should load the subgraph network wasm', async () => {
const filePath = path.resolve(__dirname, '../test/subgraph/eden/EdenNetwork/EdenNetwork.wasm');
({ exports } = await instantiate(db, { event: { block: dummyEventData.block } }, filePath, data));
({ exports } = await instantiate(
db,
indexer,
{ event: { block: dummyEventData.block } },
filePath,
data
));
const { _start } = exports;
_start();
});
@ -80,7 +95,7 @@ describe('eden wasm loader tests', async () => {
} = exports;
// Create dummy SlotClaimedEvent params.
const eventFragment = contractInterface.getEvent('SlotClaimed(indexed uint8,indexed address,indexed address,uint128,uint128,uint16,uint16)');
const eventFragment = contractInterface.getEvent('SlotClaimed(uint8,address,address,uint128,uint128,uint16,uint16)');
dummyEventData.inputs = eventFragment.inputs;
dummyEventData.event = {
slot: 0,
@ -104,7 +119,7 @@ describe('eden wasm loader tests', async () => {
} = exports;
// Create dummy SlotDelegateUpdatedEvent params.
const eventFragment = contractInterface.getEvent('SlotDelegateUpdated(indexed uint8,indexed address,indexed address,address)');
const eventFragment = contractInterface.getEvent('SlotDelegateUpdated(uint8,address,address,address)');
dummyEventData.inputs = eventFragment.inputs;
dummyEventData.event = {
slot: 0,
@ -125,7 +140,7 @@ describe('eden wasm loader tests', async () => {
} = exports;
// Create dummy StakeEvent params.
const eventFragment = contractInterface.getEvent('Stake(indexed address,uint256)');
const eventFragment = contractInterface.getEvent('Stake(address,uint256)');
dummyEventData.inputs = eventFragment.inputs;
dummyEventData.event = {
staker: '0xDC7d7A8920C8Eecc098da5B7522a5F31509b5Bfc',
@ -144,7 +159,7 @@ describe('eden wasm loader tests', async () => {
} = exports;
// Create dummy UnstakeEvent params.
const eventFragment = contractInterface.getEvent('Unstake(indexed address,uint256)');
const eventFragment = contractInterface.getEvent('Unstake(address,uint256)');
dummyEventData.inputs = eventFragment.inputs;
dummyEventData.event = {
staker: ZERO_ADDRESS,
@ -178,7 +193,12 @@ describe('eden wasm loader tests', async () => {
it('should load the subgraph network distribution wasm', async () => {
const filePath = path.resolve(__dirname, '../test/subgraph/eden/EdenNetworkDistribution/EdenNetworkDistribution.wasm');
({ exports } = await instantiate(db, { event: { block: dummyEventData.block } }, filePath, data));
({ exports } = await instantiate(db,
indexer,
{ event: { block: dummyEventData.block } },
filePath,
data
));
const { _start } = exports;
_start();
});
@ -189,7 +209,7 @@ describe('eden wasm loader tests', async () => {
} = exports;
// Create dummy ClaimedEvent params.
const eventFragment = contractInterface.getEvent('Claimed(uint256,uint256,indexed address,uint256)');
const eventFragment = contractInterface.getEvent('Claimed(uint256,uint256,address,uint256)');
dummyEventData.inputs = eventFragment.inputs;
dummyEventData.event = {
index: BigInt(1),
@ -210,7 +230,7 @@ describe('eden wasm loader tests', async () => {
} = exports;
// Create dummy SlashedEvent params.
const eventFragment = contractInterface.getEvent('Slashed(indexed address,uint256)');
const eventFragment = contractInterface.getEvent('Slashed(address,uint256)');
dummyEventData.inputs = eventFragment.inputs;
dummyEventData.event = {
account: ZERO_ADDRESS,
@ -249,7 +269,7 @@ describe('eden wasm loader tests', async () => {
} = exports;
// Create dummy AccountUpdatedEvent params.
const eventFragment = contractInterface.getEvent('AccountUpdated(indexed address,uint256,uint256)');
const eventFragment = contractInterface.getEvent('AccountUpdated(address,uint256,uint256)');
dummyEventData.inputs = eventFragment.inputs;
dummyEventData.event = {
account: ZERO_ADDRESS,
@ -284,7 +304,13 @@ describe('eden wasm loader tests', async () => {
it('should load the subgraph network governance wasm', async () => {
const filePath = path.resolve(__dirname, '../test/subgraph/eden/EdenNetworkGovernance/EdenNetworkGovernance.wasm');
({ exports } = await instantiate(db, { event: { block: dummyEventData.block } }, filePath, data));
({ exports } = await instantiate(
db,
indexer,
{ event: { block: dummyEventData.block } },
filePath,
data
));
const { _start } = exports;
_start();
});
@ -295,9 +321,9 @@ describe('eden wasm loader tests', async () => {
} = exports;
// Create dummy BlockProducerAddedEvent params.
const eventFragment = contractInterface.getEvent('BlockProducerAdded(indexed address)');
const eventFragment = contractInterface.getEvent('BlockProducerAdded(address)');
dummyEventData.inputs = eventFragment.inputs;
dummyEventData.event = { produces: ZERO_ADDRESS };
dummyEventData.event = { producer: ZERO_ADDRESS };
// Create an ethereum event BlockProducerAddedEvent to be passed to handler.
const blockProducerAddedEvent = await createEvent(exports, contractAddress, dummyEventData);
@ -311,7 +337,7 @@ describe('eden wasm loader tests', async () => {
} = exports;
// Create dummy BlockProducerRemovedEvent params.
const eventFragment = contractInterface.getEvent('BlockProducerRemoved(indexed address)');
const eventFragment = contractInterface.getEvent('BlockProducerRemoved(address)');
dummyEventData.inputs = eventFragment.inputs;
dummyEventData.event = { producer: ZERO_ADDRESS };
@ -327,7 +353,7 @@ describe('eden wasm loader tests', async () => {
} = exports;
// Create dummy BlockProducerRewardCollectorChangedEvent params.
const eventFragment = contractInterface.getEvent('BlockProducerRewardCollectorChanged(indexed address,indexed address)');
const eventFragment = contractInterface.getEvent('BlockProducerRewardCollectorChanged(address,address)');
dummyEventData.inputs = eventFragment.inputs;
dummyEventData.event = {
producer: ZERO_ADDRESS,

View File

@ -7,12 +7,14 @@ import path from 'path';
import { instantiate } from './loader';
import exampleAbi from '../test/subgraph/example1/build/Example1/abis/Example1.json';
import { getTestDatabase } from '../test/utils';
import { getTestDatabase, getTestIndexer } from '../test/utils';
import { Database } from './database';
import { Indexer } from '../test/utils/indexer';
describe('eth-call wasm tests', () => {
let exports: any;
let db: Database;
let indexer: Indexer;
const contractAddress = process.env.EXAMPLE_CONTRACT_ADDRESS;
assert(contractAddress);
@ -28,21 +30,41 @@ describe('eth-call wasm tests', () => {
before(async () => {
db = getTestDatabase();
indexer = getTestIndexer();
});
it('should load the subgraph example wasm', async () => {
const filePath = path.resolve(__dirname, '../test/subgraph/example1/build/Example1/Example1.wasm');
const instance = await instantiate(db, { event: {} }, filePath, data);
const instance = await instantiate(
db,
indexer,
{ event: {} },
filePath,
data
);
exports = instance.exports;
});
it('should execute exported function', async () => {
const { _start, testEthCall } = exports;
const { _start } = exports;
// Important to call _start for built subgraphs on instantiation!
// TODO: Check api version https://github.com/graphprotocol/graph-node/blob/6098daa8955bdfac597cec87080af5449807e874/runtime/wasm/src/module/mod.rs#L533
_start();
});
await testEthCall();
it('should execute exported getMethod function', async () => {
const { testGetEthCall } = exports;
await testGetEthCall();
});
it('should execute exported addMethod function', async () => {
const { testAddEthCall } = exports;
await testAddEthCall();
});
it('should execute exported structMethod function', async () => {
const { testStructEthCall } = exports;
await testStructEthCall();
});
});

View File

@ -6,20 +6,28 @@ import path from 'path';
import { expect } from 'chai';
import { instantiate } from './loader';
import { getTestDatabase } from '../test/utils';
import { getTestDatabase, getTestIndexer } from '../test/utils';
import { Database } from './database';
import { Indexer } from '../test/utils/indexer';
const WASM_FILE_PATH = '../build/debug.wasm';
describe('wasm loader tests', () => {
let exports: any;
let db: Database;
let indexer: Indexer;
before(async () => {
db = getTestDatabase();
indexer = getTestIndexer();
const filePath = path.resolve(__dirname, WASM_FILE_PATH);
const instance = await instantiate(db, { event: {} }, filePath);
const instance = await instantiate(
db,
indexer,
{ event: {} },
filePath
);
exports = instance.exports;
});

View File

@ -6,20 +6,28 @@ import path from 'path';
import { expect } from 'chai';
import { instantiate } from './loader';
import { getTestDatabase } from '../test/utils';
import { getTestDatabase, getTestIndexer } from '../test/utils';
import { Database } from './database';
import { Indexer } from '../test/utils/indexer';
const EXAMPLE_WASM_FILE_PATH = '../test/subgraph/example1/build/Example1/Example1.wasm';
describe('numbers wasm tests', () => {
let exports: any;
let db: Database;
let indexer: Indexer;
before(async () => {
db = getTestDatabase();
indexer = getTestIndexer();
const filePath = path.resolve(__dirname, EXAMPLE_WASM_FILE_PATH);
const instance = await instantiate(db, { event: {} }, filePath);
const instance = await instantiate(
db,
indexer,
{ event: {} },
filePath
);
exports = instance.exports;
const { _start } = exports;

View File

@ -6,20 +6,28 @@ import path from 'path';
import { expect } from 'chai';
import { instantiate } from './loader';
import { getTestDatabase } from '../test/utils';
import { getTestDatabase, getTestIndexer } from '../test/utils';
import { Database } from './database';
import { Indexer } from '../test/utils/indexer';
const EXAMPLE_WASM_FILE_PATH = '../test/subgraph/example1/build/Example1/Example1.wasm';
describe('typeConversion wasm tests', () => {
let exports: any;
let db: Database;
let indexer: Indexer;
before(async () => {
db = getTestDatabase();
indexer = getTestIndexer();
const filePath = path.resolve(__dirname, EXAMPLE_WASM_FILE_PATH);
const instance = await instantiate(db, { event: {} }, filePath);
const instance = await instantiate(
db,
indexer,
{ event: { } },
filePath
);
exports = instance.exports;
const { _start } = exports;

View File

@ -66,22 +66,6 @@ export function handleTest (event: Test): void {
blog.save();
const contractAddress = dataSource.address();
const contract = Example1.bind(contractAddress);
// Access functions by calling them.
const res1 = contract.try_addMethod(BigInt.fromString('10'), BigInt.fromString('20'));
if (res1.reverted) {
log.debug('Contract eth call reverted', []);
} else {
log.debug('Contract eth call result: {}', [res1.value.toString()]);
}
// Access functions by calling them.
const res = contract.structMethod(BigInt.fromString('1000'), BigInt.fromString('500'));
log.debug('Contract eth call result: {}', [res.bidAmount1.toString()]);
// Note: If a handler doesn't require existing field values, it is faster
// _not_ to load the entity from the store. Instead, create it fresh with
// `new Entity(...)`, set the fields that should be updated and save the
@ -124,8 +108,8 @@ export function handleBlock (block: ethereum.Block): void {
}
}
export function testEthCall (): void {
log.debug('In test eth call', []);
export function testGetEthCall (): void {
log.debug('In test get eth call', []);
// Bind the contract to the address that emitted the event.
// TODO: Address.fromString throws error in WASM module instantiation.
@ -141,6 +125,38 @@ export function testEthCall (): void {
}
}
export function testAddEthCall (): void {
log.debug('In test add eth call', []);
const contractAddress = dataSource.address();
const contract = Example1.bind(contractAddress);
// Access functions by calling them.
const res = contract.try_addMethod(BigInt.fromString('10'), BigInt.fromString('20'));
if (res.reverted) {
log.debug('Contract eth call reverted', []);
} else {
log.debug('Contract eth call result: {}', [res.value.toString()]);
}
}
export function testStructEthCall (): void {
log.debug('In test struct eth call', []);
// Bind the contract to the address that emitted the event.
// TODO: Address.fromString throws error in WASM module instantiation.
const contractAddress = dataSource.address();
const contract = Example1.bind(contractAddress);
// Access functions by calling them.
const res = contract.try_structMethod(BigInt.fromString('1000'), BigInt.fromString('500'));
if (res.reverted) {
log.debug('Contract eth call reverted', []);
} else {
log.debug('Contract eth call result: {}, {}', [res.value.bidAmount1.toString(), res.value.bidAmount2.toString()]);
}
}
export function testBytesToHex (): string {
log.debug('In test bytesToHex', []);

View File

@ -4,6 +4,7 @@
import { EventData } from '../../src/utils';
import { Database } from '../../src/database';
import { Indexer } from './indexer';
export const ZERO_ADDRESS = '0x0000000000000000000000000000000000000000';
export const ZERO_HASH = '0x0000000000000000000000000000000000000000000000000000000000000000';
@ -40,6 +41,18 @@ export const getDummyEventData = (): EventData => {
};
};
export const getDummyGraphData = (): any => {
return {
dataSource: {
address: ZERO_ADDRESS
}
};
};
export const getTestDatabase = (): Database => {
return new Database({ type: 'postgres' }, '');
};
export const getTestIndexer = (): Indexer => {
return new Indexer();
};

View File

@ -0,0 +1,123 @@
import assert from 'assert';
import { DeepPartial } from 'typeorm';
import {
IndexerInterface,
BlockProgressInterface,
EventInterface,
SyncStatusInterface
} from '@vulcanize/util';
export class Indexer implements IndexerInterface {
async getBlockProgress (blockHash: string): Promise<BlockProgressInterface | undefined> {
assert(blockHash);
return undefined;
}
async getEvent (id: string): Promise<EventInterface | undefined> {
assert(id);
return undefined;
}
async getSyncStatus (): Promise<SyncStatusInterface | undefined> {
return undefined;
}
async getBlock (blockHash: string): Promise<any> {
assert(blockHash);
return undefined;
}
async getBlocksAtHeight (height: number, isPruned: boolean): Promise<BlockProgressInterface[]> {
assert(height);
assert(isPruned);
return [];
}
async getBlockEvents (blockHash: string): Promise<Array<EventInterface>> {
assert(blockHash);
return [];
}
async getAncestorAtDepth (blockHash: string, depth: number): Promise<string> {
assert(blockHash);
assert(depth);
return '';
}
async getOrFetchBlockEvents (block: DeepPartial<BlockProgressInterface>): Promise<Array<EventInterface>> {
assert(block);
return [];
}
async removeUnknownEvents (block: BlockProgressInterface): Promise<void> {
assert(block);
}
async updateBlockProgress (blockHash: string, lastProcessedEventIndex: number): Promise<void> {
assert(blockHash);
assert(lastProcessedEventIndex);
}
async updateSyncStatusChainHead (blockHash: string, blockNumber: number): Promise<SyncStatusInterface> {
assert(blockHash);
assert(blockNumber);
return new SyncStatus();
}
async updateSyncStatusIndexedBlock (blockHash: string, blockNumber: number, force?: boolean): Promise<SyncStatusInterface> {
assert(blockNumber);
assert(blockHash);
assert(force);
return new SyncStatus();
}
async updateSyncStatusCanonicalBlock (blockHash: string, blockNumber: number, force?: boolean): Promise<SyncStatusInterface> {
assert(blockNumber);
assert(blockHash);
assert(force);
return new SyncStatus();
}
async markBlocksAsPruned (blocks: BlockProgressInterface[]): Promise<void> {
assert(blocks);
return undefined;
}
async createDiffStaged (contractAddress: string, blockHash: string, data: any): Promise<void> {
assert(contractAddress);
assert(blockHash);
assert(data);
}
}
class SyncStatus implements SyncStatusInterface {
id: number;
chainHeadBlockHash: string;
chainHeadBlockNumber: number;
latestIndexedBlockHash: string;
latestIndexedBlockNumber: number;
latestCanonicalBlockHash: string;
latestCanonicalBlockNumber: number;
constructor () {
this.id = 0;
this.chainHeadBlockHash = '0';
this.chainHeadBlockNumber = 0;
this.latestIndexedBlockHash = '0';
this.latestIndexedBlockNumber = 0;
this.latestCanonicalBlockHash = '0';
this.latestCanonicalBlockNumber = 0;
}
}