diff --git a/bun.lockb b/bun.lockb index 550909f..41a17e8 100755 Binary files a/bun.lockb and b/bun.lockb differ diff --git a/contracts/councilhaus/subgraph/bun.lockb b/contracts/councilhaus/subgraph/bun.lockb index 5444dd5..b296ccf 100755 Binary files a/contracts/councilhaus/subgraph/bun.lockb and b/contracts/councilhaus/subgraph/bun.lockb differ diff --git a/contracts/councilhaus/subgraph/tests/council-factory-utils.ts b/contracts/councilhaus/subgraph/tests/council-factory-utils.ts deleted file mode 100644 index d3b3202..0000000 --- a/contracts/councilhaus/subgraph/tests/council-factory-utils.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { Address, ethereum } from "@graphprotocol/graph-ts"; -import { newMockEvent } from "matchstick-as"; -import { CouncilCreated } from "../generated/CouncilFactory/CouncilFactory"; - -export function createCouncilCreatedEvent( - council: Address, - pool: Address, -): CouncilCreated { - const councilCreatedEvent = changetype(newMockEvent()); - - councilCreatedEvent.parameters = new Array(); - - councilCreatedEvent.parameters.push( - new ethereum.EventParam("council", ethereum.Value.fromAddress(council)), - ); - councilCreatedEvent.parameters.push( - new ethereum.EventParam("pool", ethereum.Value.fromAddress(pool)), - ); - - return councilCreatedEvent; -} diff --git a/contracts/councilhaus/subgraph/tests/council-factory.test.ts b/contracts/councilhaus/subgraph/tests/council-factory.test.ts index a622f70..163b5c5 100644 --- a/contracts/councilhaus/subgraph/tests/council-factory.test.ts +++ b/contracts/councilhaus/subgraph/tests/council-factory.test.ts @@ -9,7 +9,7 @@ import { test, } from "matchstick-as/assembly/index"; import { handleCouncilCreated } from "../src/council-factory"; -import { createCouncilCreatedEvent } from "./council-factory-utils"; +import { createCouncilCreatedEvent } from "./utils"; describe("Describe entity assertions", () => { beforeAll(() => { diff --git a/contracts/councilhaus/subgraph/tests/council.test.ts b/contracts/councilhaus/subgraph/tests/council.test.ts new file mode 100644 index 0000000..0dea440 --- /dev/null +++ b/contracts/councilhaus/subgraph/tests/council.test.ts @@ -0,0 +1,233 @@ +import { Address, BigInt } from "@graphprotocol/graph-ts"; +import { + assert, + beforeEach, + clearStore, + describe, + test, +} from "matchstick-as/assembly/index"; +import { + handleBudgetAllocated, + handleCouncilMemberAdded, + handleCouncilMemberRemoved, + handleGranteeAdded, + handleGranteeRemoved, +} from "../src/council"; +import { + createBudgetAllocatedEvent, + createCouncilMemberAddedEvent, + createCouncilMemberRemovedEvent, + createGranteeAddedEvent, + createGranteeRemovedEvent, +} from "./utils"; + +const COUNCIL_ADDRESS = "0x0000000000000000000000000000000000000001"; + +describe("Council entity assertions", () => { + beforeEach(() => { + clearStore(); + }); + + test("CouncilMember added and stored", () => { + const memberAddress = Address.fromString( + "0x0000000000000000000000000000000000000002", + ); + const votingPower = BigInt.fromI32(100); + const newCouncilMemberAddedEvent = createCouncilMemberAddedEvent( + memberAddress, + votingPower, + Address.fromString(COUNCIL_ADDRESS), + ); + + handleCouncilMemberAdded(newCouncilMemberAddedEvent); + + assert.entityCount("CouncilMember", 1); + assert.fieldEquals( + "CouncilMember", + memberAddress.toHexString(), + "account", + memberAddress.toHexString(), + ); + assert.fieldEquals( + "CouncilMember", + memberAddress.toHexString(), + "votingPower", + votingPower.toString(), + ); + assert.fieldEquals( + "CouncilMember", + memberAddress.toHexString(), + "council", + COUNCIL_ADDRESS, + ); + assert.fieldEquals( + "CouncilMember", + memberAddress.toHexString(), + "enabled", + "true", + ); + }); + + test("CouncilMember removed", () => { + // First, add a council member + const memberAddress = Address.fromString( + "0x0000000000000000000000000000000000000002", + ); + const votingPower = BigInt.fromI32(100); + const addEvent = createCouncilMemberAddedEvent( + memberAddress, + votingPower, + Address.fromString(COUNCIL_ADDRESS), + ); + handleCouncilMemberAdded(addEvent); + + // Now remove the council member + const removeEvent = createCouncilMemberRemovedEvent( + memberAddress, + Address.fromString(COUNCIL_ADDRESS), + ); + handleCouncilMemberRemoved(removeEvent); + + assert.fieldEquals( + "CouncilMember", + memberAddress.toHexString(), + "votingPower", + "0", + ); + assert.fieldEquals( + "CouncilMember", + memberAddress.toHexString(), + "enabled", + "false", + ); + + // We check that running the handler again does not throw an error + handleCouncilMemberRemoved(removeEvent); + }); + + test("Grantee added and stored", () => { + const granteeAddress = Address.fromString( + "0x0000000000000000000000000000000000000003", + ); + const granteeName = "Test Grantee"; + const newGranteeAddedEvent = createGranteeAddedEvent( + granteeAddress, + granteeName, + Address.fromString(COUNCIL_ADDRESS), + ); + + handleGranteeAdded(newGranteeAddedEvent); + + assert.entityCount("Grantee", 1); + assert.fieldEquals( + "Grantee", + granteeAddress.toHexString(), + "name", + granteeName, + ); + assert.fieldEquals( + "Grantee", + granteeAddress.toHexString(), + "account", + granteeAddress.toHexString(), + ); + assert.fieldEquals( + "Grantee", + granteeAddress.toHexString(), + "council", + COUNCIL_ADDRESS, + ); + assert.fieldEquals( + "Grantee", + granteeAddress.toHexString(), + "enabled", + "true", + ); + }); + + test("Grantee removed", () => { + // First, add a grantee + const granteeAddress = Address.fromString( + "0x0000000000000000000000000000000000000003", + ); + const granteeName = "Test Grantee"; + + const addEvent = createGranteeAddedEvent( + granteeAddress, + granteeName, + Address.fromString(COUNCIL_ADDRESS), + ); + handleGranteeAdded(addEvent); + + // // Now remove the grantee + const removeEvent = createGranteeRemovedEvent( + granteeAddress, + Address.fromString(COUNCIL_ADDRESS), + ); + handleGranteeRemoved(removeEvent); + + assert.fieldEquals( + "Grantee", + granteeAddress.toHexString(), + "enabled", + "false", + ); + + // We check that running the handler again does not throw an error + handleGranteeRemoved(removeEvent); + }); + + test("Budget allocated", () => { + // First, add a council member and a grantee + const memberAddress = Address.fromString( + "0x0000000000000000000000000000000000000002", + ); + const granteeAddress = Address.fromString( + "0x0000000000000000000000000000000000000003", + ); + + handleCouncilMemberAdded( + createCouncilMemberAddedEvent( + memberAddress, + BigInt.fromI32(100), + Address.fromString(COUNCIL_ADDRESS), + ), + ); + handleGranteeAdded( + createGranteeAddedEvent( + granteeAddress, + "Test Grantee", + Address.fromString(COUNCIL_ADDRESS), + ), + ); + + // Now allocate budget + const amounts = [BigInt.fromI32(1000)]; + const accounts = [granteeAddress]; + const newBudgetAllocatedEvent = createBudgetAllocatedEvent( + memberAddress, + amounts, + accounts, + Address.fromString(COUNCIL_ADDRESS), + ); + + handleBudgetAllocated(newBudgetAllocatedEvent); + + assert.entityCount("Allocation", 1); + const allocationId = `${newBudgetAllocatedEvent.transaction.hash.toHex()}-${newBudgetAllocatedEvent.logIndex.toString()}`; + assert.fieldEquals("Allocation", allocationId, "council", COUNCIL_ADDRESS); + assert.fieldEquals( + "Allocation", + allocationId, + "councilMember", + memberAddress.toHexString(), + ); + assert.fieldEquals("Allocation", allocationId, "amounts", "[1000]"); + assert.fieldEquals( + "Allocation", + allocationId, + "grantees", + `[${granteeAddress.toHexString()}]`, + ); + }); +}); diff --git a/contracts/councilhaus/subgraph/tests/utils.ts b/contracts/councilhaus/subgraph/tests/utils.ts new file mode 100644 index 0000000..0eb99f3 --- /dev/null +++ b/contracts/councilhaus/subgraph/tests/utils.ts @@ -0,0 +1,116 @@ +import { Address, BigInt, ethereum, log } from "@graphprotocol/graph-ts"; +import { newMockEvent } from "matchstick-as"; +import { CouncilCreated } from "../generated/CouncilFactory/CouncilFactory"; +import { + BudgetAllocated, + CouncilMemberAdded, + CouncilMemberRemoved, + GranteeAdded, + GranteeRemoved, +} from "../generated/templates/Council/Council"; + +export function createCouncilCreatedEvent( + council: Address, + pool: Address, +): CouncilCreated { + const councilCreatedEvent = changetype(newMockEvent()); + + councilCreatedEvent.parameters = new Array(); + + councilCreatedEvent.parameters.push( + new ethereum.EventParam("council", ethereum.Value.fromAddress(council)), + ); + councilCreatedEvent.parameters.push( + new ethereum.EventParam("pool", ethereum.Value.fromAddress(pool)), + ); + + return councilCreatedEvent; +} + +export function createCouncilMemberAddedEvent( + member: Address, + votingPower: BigInt, + council: Address, +): CouncilMemberAdded { + const event = changetype(newMockEvent()); + event.address = council; + event.parameters = new Array(); + event.parameters.push( + new ethereum.EventParam("member", ethereum.Value.fromAddress(member)), + ); + event.parameters.push( + new ethereum.EventParam( + "votingPower", + ethereum.Value.fromUnsignedBigInt(votingPower), + ), + ); + return event; +} + +export function createCouncilMemberRemovedEvent( + member: Address, + council: Address, +): CouncilMemberRemoved { + const event = changetype(newMockEvent()); + event.address = council; + event.parameters = new Array(); + event.parameters.push( + new ethereum.EventParam("member", ethereum.Value.fromAddress(member)), + ); + return event; +} + +export function createGranteeAddedEvent( + grantee: Address, + name: string, + council: Address, +): GranteeAdded { + const event = changetype(newMockEvent()); + event.address = council; + event.parameters = new Array(); + event.parameters.push( + new ethereum.EventParam("name", ethereum.Value.fromString(name)), + ); + event.parameters.push( + new ethereum.EventParam("grantee", ethereum.Value.fromAddress(grantee)), + ); + return event; +} + +export function createGranteeRemovedEvent( + grantee: Address, + council: Address, +): GranteeRemoved { + const event = changetype(newMockEvent()); + event.address = council; + event.parameters = new Array(); + event.parameters.push( + new ethereum.EventParam("grantee", ethereum.Value.fromAddress(grantee)), + ); + return event; +} + +export function createBudgetAllocatedEvent( + member: Address, + amounts: BigInt[], + accounts: Address[], + council: Address, +): BudgetAllocated { + const event = changetype(newMockEvent()); + event.address = council; + event.parameters = new Array(); + event.parameters.push( + new ethereum.EventParam("member", ethereum.Value.fromAddress(member)), + ); + const allocation: Array = [ + ethereum.Value.fromAddressArray(accounts), + ethereum.Value.fromUnsignedBigIntArray(amounts), + ]; + event.parameters.push( + new ethereum.EventParam( + "allocation", + ethereum.Value.fromTuple(changetype(allocation)), + ), + ); + return event; +}