-
Notifications
You must be signed in to change notification settings - Fork 20
Bounties e2e test #399
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
Merged
Merged
Bounties e2e test #399
Changes from 4 commits
Commits
Show all changes
68 commits
Select commit
Hold shift + click to select a range
d6ec0f7
bounties module added
dhirajs0 b2aeffb
Creating a bounty test added
dhirajs0 289da9e
bounty approval flow test added
dhirajs0 6583de2
curator assignment and acceptance test added
dhirajs0 c1279db
rafac: setupTestAccounts function using a map
dhirajs0 aacbce1
rafac: bountyValue is changed to exential deposit multiple
dhirajs0 ac3066a
fix: bountyValue type is set to BigInt
dhirajs0 46a8d53
Bountry approval flow with Curator test added
dhirajs0 0e4e845
fix: added event verification in bountyApprovalWithCuratorTest
dhirajs0 b68fbe8
bountyFundingTest added
dhirajs0 31dd6ac
Bounty funding for ApprovedWithCurator Bounties test added
dhirajs0 39c758e
Curator assignment and acceptance test added
dhirajs0 cd0d353
improve: setStorage replaced with extrinsics call
dhirajs0 b6baac5
helper function getBountyEvents added
dhirajs0 64d8ce6
setStorage replaces with Extrinsic call in fundind with curator test
dhirajs0 bca934b
replaced the setStorage with Extrinsic calls in curator assignment an…
dhirajs0 33019a2
bounty extension test added
dhirajs0 7e20cdb
bounty awading and claiming tests added
dhirajs0 1f4ac58
bounty closure on proposed test added
dhirajs0 8c5ae5e
bounty closure in funded state test added
dhirajs0 e5403b6
helper function to log all events added
dhirajs0 3bff36d
update: blance slash check added in bounty closure of proposed bounty…
dhirajs0 de4380d
bounty closure in active state test added
dhirajs0 df503aa
unassign curator when bounty state is ApprovedWithCurator test is added
dhirajs0 2297f8d
unassign curator of bounty in curator proposed state test added
dhirajs0 51e55e7
unassign curator by curator when bounty status is Active test added
dhirajs0 0224166
unassign curator by treasury when bounty is active test is added
dhirajs0 1de2dfb
unassign curator by treasury when bounty status is PendingPayout test…
dhirajs0 4dff492
refac: unassign curates tests are grouped
dhirajs0 4284aa9
refac: bounty clousure tests are grouped together
dhirajs0 8f25c68
debug logs removed
dhirajs0 cbb4291
removed unused functions
dhirajs0 60e0cec
polkadot snaps added
dhirajs0 290a044
kusama snapshot added
dhirajs0 78b145d
failure test: close bounty in approved status emits UnexpectedStatus …
dhirajs0 34c295a
removed get all bounty events
dhirajs0 c9dbc24
failure test: added bountyClosure in PendingPayoutTest emits PendingP…
dhirajs0 d10c4cd
failure test: added unassignCuratorActiveStateByPublicPrematureTest e…
dhirajs0 c396856
failure test: reasonTooBigTest added
dhirajs0 f32a1ab
failure test: invalidValueTest
dhirajs0 92873dd
failure test: invalidIndexApprovalTest added
dhirajs0 166deaa
failure test: unexpectedStatusProposeCuratorTest proposing curator b…
dhirajs0 7615e6f
failure test: requireCuratorAcceptTest i.e Non-curator trying to acce…
dhirajs0 f23a752
fix: typecasting removed from getBountyIndexFromEvent
dhirajs0 bace3c8
refac
dhirajs0 c90ef96
refac: grouped bounty success tests
dhirajs0 fc819c7
refac: grouped approval tests and funding tests
dhirajs0 5b23af8
failure test: hasActiveChildBountyTest added, Bounty cannot be awarde…
dhirajs0 f645277
refac
dhirajs0 cef1c55
improv: explicit expect are used
dhirajs0 35bc702
refac: added helper fun, removed magic nums
dhirajs0 1bf7448
refac: defined type of params
dhirajs0 0f24e0d
snanpshots updated for polkadot and kusama
dhirajs0 37cfb5a
fix
dhirajs0 e212286
Merge branch 'master' into bounties-e2e
dhirajs0 c37a27e
refac: parameterize the sendTransation and scheduleInlineCallWithOrig…
dhirajs0 ed92bea
readme: added bounties tests convered
dhirajs0 379c601
devAccounts replaced with testAccounts
dhirajs0 67da6c0
set the lastSpendPeriod such that the next spend is few block ahead
dhirajs0 6a0f9fc
snapshot of bounties updated
dhirajs0 656ddd4
added setLastSpendPeriodBlockNumber() fn for repeated logic
dhirajs0 7fb4b4f
added extractExtrinsicFailedEvent() for repeated logic
dhirajs0 871110a
minor improvements
dhirajs0 e98793a
Merge branch 'master' into bounties-e2e
dhirajs0 1598eb0
linter warning fixed
dhirajs0 5a11ba6
empty events fixed and docs updated
dhirajs0 33d2688
snapshot updated
dhirajs0 c1e3a92
openng docs updated
dhirajs0 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Some comments aren't visible on the classic Files Changed page.
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,4 @@ | ||
| import { kusama } from '@e2e-test/networks/chains' | ||
| import { baseBountiesE2ETests, registerTestTree } from '@e2e-test/shared' | ||
|
|
||
| registerTestTree(baseBountiesE2ETests(kusama, { testSuiteName: 'Kusama Bounties', addressEncoding: 2 })) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,4 @@ | ||
| import { polkadot } from '@e2e-test/networks/chains' | ||
| import { baseBountiesE2ETests, registerTestTree } from '@e2e-test/shared' | ||
|
|
||
| registerTestTree(baseBountiesE2ETests(polkadot, { testSuiteName: 'Polkadot Bounties', addressEncoding: 0 })) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,292 @@ | ||
| import { sendTransaction } from '@acala-network/chopsticks-testing' | ||
|
|
||
| import { type Chain, defaultAccountsSr25519 as devAccounts } from '@e2e-test/networks' | ||
| import { setupNetworks } from '@e2e-test/shared' | ||
|
|
||
| import { expect } from 'vitest' | ||
|
|
||
| import { checkEvents, checkSystemEvents, scheduleInlineCallWithOrigin } from './helpers/index.js' | ||
| import type { RootTestTree } from './types.js' | ||
|
|
||
| /// ------- | ||
| /// Helpers | ||
| /// ------- | ||
|
|
||
| /** | ||
| * Get the current bounty count | ||
| */ | ||
| async function getBountyCount(client: any): Promise<number> { | ||
| return (await client.api.query.bounties.bountyCount()).toNumber() | ||
| } | ||
|
|
||
| /** | ||
| * Get a bounty by index | ||
| */ | ||
| async function getBounty(client: any, bountyIndex: number): Promise<any | null> { | ||
| const bounty = await client.api.query.bounties.bounties(bountyIndex) | ||
| return bounty.isSome ? bounty.unwrap() : null | ||
| } | ||
|
|
||
| /** | ||
| * Get bounty description by index | ||
| */ | ||
| async function getBountyDescription(client: any, bountyIndex: number): Promise<string | null> { | ||
| const description = await client.api.query.bounties.bountyDescriptions(bountyIndex) | ||
| return description.isSome ? description.unwrap().toUtf8() : null | ||
| } | ||
|
|
||
| /** | ||
| * Get approved bounties queue | ||
| */ | ||
| async function getBountyApprovals(client: any): Promise<number[]> { | ||
| const approvals = await client.api.query.bounties.bountyApprovals() | ||
| return approvals.map((index: any) => index.toNumber()) | ||
| } | ||
|
|
||
| /** | ||
| * Setup accounts with funds for testing | ||
| */ | ||
| async function setupTestAccounts(client: any, accounts: string[] = ['alice', 'bob']) { | ||
| const accountData: any[] = [] | ||
|
|
||
| if (accounts.includes('alice')) { | ||
| accountData.push([[devAccounts.alice.address], { providers: 1, data: { free: 100000000000000n } }]) | ||
| } | ||
| if (accounts.includes('bob')) { | ||
| accountData.push([[devAccounts.bob.address], { providers: 1, data: { free: 100000000000000n } }]) | ||
| } | ||
| if (accounts.includes('charlie')) { | ||
| accountData.push([[devAccounts.charlie.address], { providers: 1, data: { free: 100000000000000n } }]) | ||
| } | ||
|
|
||
| await client.dev.setStorage({ | ||
| System: { | ||
| account: accountData, | ||
| }, | ||
| }) | ||
| } | ||
|
|
||
| /** | ||
| * Get bounty index from BountyProposed event | ||
| */ | ||
| async function getBountyIndexFromEvent(client: any): Promise<number> { | ||
| const [bountyProposedEvent] = (await client.api.query.system.events()).filter( | ||
| ({ event }: any) => event.section === 'bounties' && event.method === 'BountyProposed', | ||
| ) | ||
| expect(bountyProposedEvent).toBeDefined() | ||
| return (bountyProposedEvent.event.data as any).index.toNumber() | ||
|
dhirajs0 marked this conversation as resolved.
Outdated
|
||
| } | ||
|
|
||
| /// ------- | ||
| /// Tests | ||
| /// ------- | ||
|
|
||
| /** | ||
| * Test 1: Creating a bounty | ||
| * Propose a bounty | ||
| * Verifies: | ||
| * - Bounty proposal is successful | ||
| * - Correct events are emitted | ||
| * - Bounty data is stored correctly | ||
| * - Bounty count increases | ||
| */ | ||
| export async function bountyCreationTest< | ||
| TCustom extends Record<string, unknown> | undefined, | ||
| TInitStorages extends Record<string, Record<string, any>> | undefined, | ||
| >(chain: Chain<TCustom, TInitStorages>) { | ||
| const [client] = await setupNetworks(chain) | ||
|
|
||
| // Setup test accounts | ||
| await setupTestAccounts(client, ['alice']) | ||
|
|
||
| const initialBountyCount = await getBountyCount(client) | ||
| const bountyValue = 10000000000000n // 1000 tokens | ||
|
dhirajs0 marked this conversation as resolved.
Outdated
|
||
| const description = 'Test bounty for development work' | ||
|
|
||
| // Propose a bounty | ||
| const bountyProposalEvents = await sendTransaction( | ||
| client.api.tx.bounties.proposeBounty(bountyValue, description).signAsync(devAccounts.alice), | ||
| ) | ||
|
|
||
| await client.dev.newBlock() | ||
|
|
||
| // Verify events | ||
| await checkEvents(bountyProposalEvents, { section: 'bounties', method: 'BountyProposed' }) | ||
| .redact({ redactKeys: /index/ }) | ||
| .toMatchSnapshot('bounty proposal events') | ||
|
|
||
| // Verify bounty count increased | ||
| const newBountyCount = await getBountyCount(client) | ||
| expect(newBountyCount).toBe(initialBountyCount + 1) | ||
|
|
||
| // Get bounty index and verify bounty data | ||
| const bountyIndex = await getBountyIndexFromEvent(client) | ||
| const bounty = await getBounty(client, bountyIndex) | ||
| expect(bounty).toBeDefined() | ||
|
dhirajs0 marked this conversation as resolved.
Outdated
|
||
| expect(bounty.value.toBigInt()).toBe(bountyValue) | ||
| expect(bounty.status.isProposed).toBe(true) | ||
|
|
||
| // Verify description was stored | ||
| const storedDescription = await getBountyDescription(client, bountyIndex) | ||
| expect(storedDescription).toBeTruthy() | ||
| expect(storedDescription).toBe(description) | ||
|
|
||
| await client.teardown() | ||
| } | ||
|
|
||
| /** | ||
| * Test 2: Bounty approval flow | ||
| * | ||
| * Verifies: | ||
| * - Bounty can be approved by treasurer | ||
| * - Status changes from Proposed to Approved | ||
| * - Bounty is added to approvals queue | ||
| * - Correct events are emitted | ||
| */ | ||
| export async function bountyApprovalTest< | ||
| TCustom extends Record<string, unknown> | undefined, | ||
| TInitStorages extends Record<string, Record<string, any>> | undefined, | ||
| >(chain: Chain<TCustom, TInitStorages>) { | ||
| const [client] = await setupNetworks(chain) | ||
|
|
||
| await setupTestAccounts(client, ['alice']) | ||
|
|
||
| const bountyValue = 10000000000000n // 1000 | ||
| const description = 'Test bounty for approval' | ||
|
|
||
| // Propose a bounty | ||
| await sendTransaction(client.api.tx.bounties.proposeBounty(bountyValue, description).signAsync(devAccounts.alice)) | ||
|
|
||
| await client.dev.newBlock() | ||
| const bountyIndex = await getBountyIndexFromEvent(client) | ||
|
|
||
| // Verify initial state | ||
| const proposedBounty = await getBounty(client, bountyIndex) | ||
| expect(proposedBounty.status.isProposed).toBe(true) | ||
|
|
||
| // Approve the bounty | ||
| await scheduleInlineCallWithOrigin(client, client.api.tx.bounties.approveBounty(bountyIndex).method.toHex(), { | ||
| Origins: 'Treasurer', | ||
| }) | ||
|
|
||
| await client.dev.newBlock() | ||
|
|
||
| // Verify approval events | ||
| await checkSystemEvents(client, { section: 'bounties', method: 'BountyApproved' }) | ||
| .redact({ redactKeys: /index/ }) | ||
| .toMatchSnapshot('bounty approval events') | ||
|
|
||
| // Verify status changed | ||
| const approvedBounty = await getBounty(client, bountyIndex) | ||
| expect(approvedBounty.status.isApproved).toBe(true) | ||
|
|
||
| // Verify bounty is in approvals queue | ||
| const approvals = await getBountyApprovals(client) | ||
| expect(approvals).toContain(bountyIndex) | ||
|
|
||
| await client.teardown() | ||
| } | ||
|
|
||
| /** | ||
| * Test 3: Curator assignment and acceptance | ||
| * | ||
| * Verifies: | ||
| * - Curator can be proposed for a funded bounty | ||
| * - Curator can accept the role | ||
| * - Status transitions correctly | ||
| * - Curator deposit is reserved | ||
| * - Correct events are emitted | ||
| */ | ||
| export async function curatorAssignmentTest< | ||
| TCustom extends Record<string, unknown> | undefined, | ||
| TInitStorages extends Record<string, Record<string, any>> | undefined, | ||
| >(chain: Chain<TCustom, TInitStorages>) { | ||
| const [client] = await setupNetworks(chain) | ||
|
|
||
| await setupTestAccounts(client, ['alice', 'bob']) | ||
|
|
||
| const bountyValue = 10000000000000n // 1000 tokens | ||
| const curatorFee = 1000000000000n // 100 tokens (10% fee) | ||
| const description = 'Test bounty with curator' | ||
|
|
||
| // Propose and approve bounty | ||
| await sendTransaction(client.api.tx.bounties.proposeBounty(bountyValue, description).signAsync(devAccounts.alice)) | ||
|
|
||
| await client.dev.newBlock() | ||
| const bountyIndex = await getBountyIndexFromEvent(client) | ||
|
|
||
| await scheduleInlineCallWithOrigin(client, client.api.tx.bounties.approveBounty(bountyIndex).method.toHex(), { | ||
| Origins: 'Treasurer', | ||
| }) | ||
|
|
||
| await client.dev.newBlock() | ||
|
|
||
| // Propose a curator | ||
| await scheduleInlineCallWithOrigin( | ||
| client, | ||
| client.api.tx.bounties.proposeCurator(bountyIndex, devAccounts.bob.address, curatorFee).method.toHex(), | ||
| { Origins: 'Treasurer' }, | ||
| ) | ||
|
|
||
| await client.dev.newBlock() | ||
|
|
||
| // Verify curator proposed events | ||
| await checkSystemEvents(client, { section: 'bounties', method: 'CuratorProposed' }) | ||
| .redact({ redactKeys: /bountyId/ }) | ||
| .toMatchSnapshot('curator proposed events') | ||
|
|
||
| // Verify bounty status | ||
| const bounty = await getBounty(client, bountyIndex) | ||
| expect(bounty.status.isCuratorProposed).toBeTruthy() | ||
| expect(bounty.fee.toBigInt()).toBe(curatorFee) | ||
|
|
||
| // Curator accepts the role | ||
| const acceptCuratorEvents = await sendTransaction( | ||
| client.api.tx.bounties.acceptCurator(bountyIndex).signAsync(devAccounts.bob), | ||
| ) | ||
|
|
||
| await client.dev.newBlock() | ||
|
|
||
| // Verify curator accepted events | ||
| await checkEvents(acceptCuratorEvents, { section: 'bounties', method: 'CuratorAccepted' }) | ||
| .redact({ redactKeys: /bountyId/ }) | ||
| .toMatchSnapshot('curator accepted events') | ||
|
|
||
| // Verify bounty is now active | ||
| const activeBounty = await getBounty(client, bountyIndex) | ||
| console.log('Active bounty status:', activeBounty.status.toHuman()) | ||
| expect(activeBounty.status.isActive).toBeTruthy() | ||
|
|
||
| await client.teardown() | ||
| } | ||
|
|
||
| /// ------- | ||
| /// Test Suite | ||
| /// ------- | ||
|
|
||
| export function baseBountiesE2ETests< | ||
| TCustom extends Record<string, unknown> | undefined, | ||
| TInitStorages extends Record<string, Record<string, any>> | undefined, | ||
| >(chain: Chain<TCustom, TInitStorages>, testConfig: { testSuiteName: string; addressEncoding: number }): RootTestTree { | ||
| return { | ||
| kind: 'describe', | ||
| label: testConfig.testSuiteName, | ||
| children: [ | ||
| { | ||
| kind: 'test', | ||
| label: 'Creating a bounty', | ||
| testFn: async () => await bountyCreationTest(chain), | ||
| }, | ||
| { | ||
| kind: 'test', | ||
| label: 'Bounty approval flow', | ||
| testFn: async () => await bountyApprovalTest(chain), | ||
| }, | ||
| { | ||
| kind: 'test', | ||
| label: 'Curator assignment and acceptance', | ||
| testFn: async () => await curatorAssignmentTest(chain), | ||
| }, | ||
| ], | ||
| } | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.