UNPKG

@graphprotocol/graph-cli

Version:

CLI for building for and deploying to The Graph

197 lines (165 loc) • 6.9 kB
const prettier = require('prettier') const { strings } = require('gluegun') const { ascTypeForEthereum, ethereumFromAsc } = require("../codegen/types") const VARIABLES_VALUES = { "i32": 123, "BigInt": 234, "Bytes": 1234567890, "Address": "0x0000000000000000000000000000000000000001", "string": "Example string value", "bool": true, } const generateTestsFiles = (contract, events, indexEvents) => { const eventsTypes = events .flatMap(event => event .inputs .map(input => { // If the asc type is Array<T> we need to check if T is a native type or a custom graph-ts type // If we don't do that we may miss a type that should be imported from graph-ts const ascType = ascTypeForEthereum(input.type) const inner = fetchArrayInnerType(ascType) return inner ? inner[1] : ascType }) ).filter(type => !type.startsWith("ethereum.") && !isNativeType(type)) const importTypes = [...new Set(eventsTypes)].join(', ') return { [`${strings.kebabCase(contract)}.test.ts`]: prettier.format(generateExampleTest(contract, events[0], indexEvents, importTypes), { parser: 'typescript', semi: false }), [`${strings.kebabCase(contract)}-utils.ts`]: prettier.format(generateTestHelper(contract, events, importTypes), { parser: 'typescript', semi: false }), } } /* Generates the arguments that will be passed to the mock event function from the event inputs. Example: let id = BigInt.fromI32(234) let owner = Address.fromString("0x0000000000000000000000000000000000000001") let displayName = "Example string value" let imageUrl = "Example string value" */ const generateArguments = (eventInputs) => { return eventInputs.map((input, index) => { let ascType = ascTypeForEthereum(input.type) return `let ${input.name || `param${index}`} = ${assignValue(ascType, input.name)}` }).join('\n') } // Generates the value that will be assigned to a variable in generateArguments() const assignValue = (type) => { switch (type) { case "string": return `"${VARIABLES_VALUES[type]}"` case "BigInt": return `BigInt.fromI32(${VARIABLES_VALUES[type]})` case "Address": return `Address.fromString("${VARIABLES_VALUES[type]}")` case "Bytes": return `Bytes.fromI32(${VARIABLES_VALUES[type]})` case fetchArrayInnerType(type)?.input: innerType = fetchArrayInnerType(type)[1] return `[${assignValue(innerType)}]` default: let value = VARIABLES_VALUES[type] return value ? value : `"${type} Not implemented"` } } /* Generates the assert.fieldEquals() for a given entity and event inputs. Example: assert.fieldEquals( "ExampleEntity", "0xa16081f360e3847006db660bae1c6d1b2e17ec2a", "owner", "0x0000000000000000000000000000000000000001" ) */ const generateFieldsAssertions = (entity, eventInputs, indexEvents) => eventInputs.filter(input => input.name != "id").map((input, index) => `assert.fieldEquals( "${entity}", "0xa16081f360e3847006db660bae1c6d1b2e17ec2a${indexEvents ? "-1" : ""}", "${input.name || `param${index}`}", "${expectedValue(ascTypeForEthereum(input.type))}" )` ).join('\n') // Returns the expected value for a given type in generateFieldsAssertions() const expectedValue = type => { switch (type) { case fetchArrayInnerType(type)?.input: innerType = fetchArrayInnerType(type)[1] return `[${expectedValue(innerType)}]` default: let value = VARIABLES_VALUES[type] return value ? value : `${type} Not implemented` } } // Checks if the type is a native AS type or should be imported from graph-ts const isNativeType = type => { let natives = [ /i32/, /string/, /boolean/ ] return natives.some(rx => rx.test(type)); } const fetchArrayInnerType = type => type.match(/Array<(.*?)>/) // Generates the example test.ts file const generateExampleTest = (contract, event, indexEvents, importTypes) => { const entity = indexEvents ? `${event._alias}` : 'ExampleEntity' const eventInputs = event.inputs const eventName = event._alias return ` import { assert, describe, test, clearStore, beforeAll, afterAll } from "matchstick-as/assembly/index" import { ${importTypes} } from "@graphprotocol/graph-ts" import { ${entity} } from "../generated/schema" import { ${indexEvents ? `${eventName} as ${eventName}Event` : eventName} } from "../generated/${contract}/${contract}" import { handle${eventName} } from "../src/${strings.kebabCase(contract)}" import { create${eventName}Event } from "./${strings.kebabCase(contract)}-utils" // Tests structure (matchstick-as >=0.5.0) // https://thegraph.com/docs/en/developer/matchstick/#tests-structure-0-5-0 describe("Describe entity assertions", () => { beforeAll(() => { ${generateArguments(eventInputs)} let new${eventName}Event = create${eventName}Event(${eventInputs.map((input, index) => input.name || `param${index}`).join(', ')}); handle${eventName}(new${eventName}Event) }) afterAll(() => { clearStore() }) // For more test scenarios, see: // https://thegraph.com/docs/en/developer/matchstick/#write-a-unit-test test("${entity} created and stored", () => { assert.entityCount('${entity}', 1) // 0xa16081f360e3847006db660bae1c6d1b2e17ec2a is the default address used in newMockEvent() function ${generateFieldsAssertions(entity, eventInputs, indexEvents)} // More assert options: // https://thegraph.com/docs/en/developer/matchstick/#asserts }) }) ` } // Generates the utils helper file const generateTestHelper = (contract, events, importTypes) => { const eventsNames = events.map(event => event._alias) return ` import { newMockEvent } from 'matchstick-as'; import { ethereum, ${importTypes} } from '@graphprotocol/graph-ts'; import { ${eventsNames.join(", ")} } from '../generated/${contract}/${contract}'; ${generateMockedEvents(events).join("\n")}` } const generateMockedEvents = events => events.reduce( (acc, event) => acc.concat(generateMockedEvent(event)), [], ) const generateMockedEvent = event => { const varName = `${strings.camelCase(event._alias)}Event` const fnArgs = event.inputs.map((input, index) => `${input.name || `param${index}`}: ${ascTypeForEthereum(input.type)}`); const ascToEth = event.inputs.map((input, index) => `${varName}.parameters.push(new ethereum.EventParam("${input.name || `param${index}`}", ${ethereumFromAsc(input.name || `param${index}`, input.type)}))`); return ` export function create${event._alias}Event(${fnArgs.join(', ')}): ${event._alias} { let ${varName} = changetype<${event._alias}>(newMockEvent()); ${varName}.parameters = new Array(); ${ascToEth.join('\n')} return ${varName}; } ` } module.exports = { generateTestsFiles, }