Browse Source

CancelProposal, VetoProposal, additional status-related events checks

Leszek Wiesner 3 years ago
parent
commit
749b9f3fa8

+ 2 - 0
query-node/mappings/proposals.ts

@@ -450,6 +450,7 @@ export async function proposalsEngine_ProposalExecuted({ store, event }: EventCo
   })
   await store.save<ProposalExecutedEvent>(proposalExecutedEvent)
 
+  newStatus.proposalExecutedEventId = proposalExecutedEvent.id
   proposal.status = newStatus
   proposal.statusSetAtBlock = event.blockNumber
   proposal.statusSetAtTime = eventTime
@@ -499,6 +500,7 @@ export async function proposalsEngine_ProposalCancelled({ store, event }: EventC
   await store.save<ProposalCancelledEvent>(proposalCancelledEvent)
 
   proposal.status = new ProposalStatusCancelled()
+  proposal.status.cancelledInEventId = proposalCancelledEvent.id
   proposal.statusSetAtBlock = event.blockNumber
   proposal.statusSetAtTime = eventTime
   proposal.updatedAt = eventTime

+ 1 - 1
query-node/schemas/proposals.graphql

@@ -58,7 +58,7 @@ type ProposalStatusExpired @variant {
 "The proposal was cancelled by the original proposer."
 type ProposalStatusCancelled @variant {
   "The related ProposalCancelledEvent"
-  canelledInEvent: ProposalCancelledEvent
+  cancelledInEvent: ProposalCancelledEvent
 }
 
 "The proposal was canceled by the runtime (for example, due to runtime upgrade). No cancellation fee was applied."

+ 13 - 0
tests/integration-tests/src/Api.ts

@@ -454,6 +454,19 @@ export class Api {
     )
   }
 
+  public async untilBlock(blockNumber: number, intervalMs = BLOCKTIME, timeoutMs = 180000): Promise<void> {
+    await Utils.until(
+      `blocknumber ${blockNumber}`,
+      async ({ debug }) => {
+        const best = await this.getBestBlock()
+        debug(`Current block: ${best.toNumber()}`)
+        return best.gten(blockNumber)
+      },
+      intervalMs,
+      timeoutMs
+    )
+  }
+
   public async untilProposalsCanBeCreated(
     numberOfProposals = 1,
     intervalMs = BLOCKTIME,

+ 13 - 1
tests/integration-tests/src/QueryNodeApi.ts

@@ -183,6 +183,10 @@ import {
   GetProposalVotedEventsByEventIdsQuery,
   GetProposalVotedEventsByEventIdsQueryVariables,
   GetProposalVotedEventsByEventIds,
+  ProposalCancelledEventFieldsFragment,
+  GetProposalCancelledEventsByEventIdsQuery,
+  GetProposalCancelledEventsByEventIdsQueryVariables,
+  GetProposalCancelledEventsByEventIds,
 } from './graphql/generated/queries'
 import { Maybe } from './graphql/generated/schema'
 import { OperationDefinitionNode } from 'graphql'
@@ -687,7 +691,7 @@ export class QueryNodeApi {
     )
   }
 
-  public async getProposalsByIds(ids: ProposalId[]): Promise<ProposalFieldsFragment[]> {
+  public async getProposalsByIds(ids: (ProposalId | string)[]): Promise<ProposalFieldsFragment[]> {
     return this.multipleEntitiesQuery<GetProposalsByIdsQuery, GetProposalsByIdsQueryVariables>(
       GetProposalsByIds,
       { ids: ids.map((id) => id.toString()) },
@@ -702,4 +706,12 @@ export class QueryNodeApi {
       GetProposalVotedEventsByEventIdsQueryVariables
     >(GetProposalVotedEventsByEventIds, { eventIds }, 'proposalVotedEvents')
   }
+
+  public async getProposalCancelledEvents(events: EventDetails[]): Promise<ProposalCancelledEventFieldsFragment[]> {
+    const eventIds = events.map((e) => this.getQueryNodeEventId(e.blockNumber, e.indexInBlock))
+    return this.multipleEntitiesQuery<
+      GetProposalCancelledEventsByEventIdsQuery,
+      GetProposalCancelledEventsByEventIdsQueryVariables
+    >(GetProposalCancelledEventsByEventIds, { eventIds }, 'proposalCancelledEvents')
+  }
 }

+ 70 - 0
tests/integration-tests/src/fixtures/proposals/CancelProposalsFixture.ts

@@ -0,0 +1,70 @@
+import { Api } from '../../Api'
+import { QueryNodeApi } from '../../QueryNodeApi'
+import { EventDetails } from '../../types'
+import { SubmittableExtrinsic } from '@polkadot/api/types'
+import { Utils } from '../../utils'
+import { ISubmittableResult } from '@polkadot/types/types/'
+import { ProposalCancelledEventFieldsFragment, ProposalFieldsFragment } from '../../graphql/generated/queries'
+import { assert } from 'chai'
+import { Proposal, ProposalId } from '@joystream/types/proposals'
+import { StandardizedFixture } from '../../Fixture'
+
+export class CancelProposalsFixture extends StandardizedFixture {
+  protected proposalIds: ProposalId[]
+  protected proposals: Proposal[] = []
+
+  public constructor(api: Api, query: QueryNodeApi, proposalIds: ProposalId[]) {
+    super(api, query)
+    this.proposalIds = proposalIds
+  }
+
+  protected async getSignerAccountOrAccounts(): Promise<string[]> {
+    return this.api.getMemberSigners(this.proposals.map((p) => ({ asMember: p.proposerId })))
+  }
+
+  protected async getExtrinsics(): Promise<SubmittableExtrinsic<'promise'>[]> {
+    return this.proposalIds.map((proposalId, i) =>
+      this.api.tx.proposalsEngine.cancelProposal(this.proposals[i].proposerId, proposalId)
+    )
+  }
+
+  protected async getEventFromResult(result: ISubmittableResult): Promise<EventDetails> {
+    return this.api.retrieveProposalsEngineEventDetails(result, 'ProposalCancelled')
+  }
+
+  protected assertQueriedProposalsAreValid(
+    qProposals: ProposalFieldsFragment[],
+    qEvents: ProposalCancelledEventFieldsFragment[]
+  ): void {
+    this.events.map((e, i) => {
+      const qEvent = this.findMatchingQueryNodeEvent(e, qEvents)
+      const proposalId = this.proposalIds[i]
+      const qProposal = qProposals.find((p) => p.id === proposalId.toString())
+      Utils.assert(qProposal, 'Query node: Proposal not found')
+      Utils.assert(qProposal.status.__typename === 'ProposalStatusCancelled', 'Invalid proposal status')
+      assert.equal(qProposal.status.cancelledInEvent?.id, qEvent.id)
+    })
+  }
+
+  protected assertQueryNodeEventIsValid(qEvent: ProposalCancelledEventFieldsFragment, i: number): void {
+    const proposalId = this.proposalIds[i]
+    assert.equal(qEvent.proposal.id, proposalId.toString())
+  }
+
+  public async execute(): Promise<void> {
+    this.proposals = await this.api.query.proposalsEngine.proposals.multi<Proposal>(this.proposalIds)
+    await super.execute()
+  }
+
+  async runQueryNodeChecks(): Promise<void> {
+    await super.runQueryNodeChecks()
+    const qEvents = await this.query.tryQueryWithTimeout(
+      () => this.query.getProposalCancelledEvents(this.events),
+      (result) => this.assertQueryNodeEventsAreValid(result)
+    )
+
+    // Query the proposals
+    const qProposals = await this.query.getProposalsByIds(this.proposalIds)
+    this.assertQueriedProposalsAreValid(qProposals, qEvents)
+  }
+}

+ 44 - 2
tests/integration-tests/src/fixtures/proposals/DecideOnProposalStatusFixture.ts

@@ -83,6 +83,15 @@ export class DecideOnProposalStatusFixture extends BaseQueryNodeFixture {
     }
   }
 
+  protected async postExecutionChecks(qProposal: ProposalFieldsFragment): Promise<void> {
+    const { details } = qProposal
+    if (details.__typename === 'VetoProposalDetails') {
+      const [qVetoedProposal] = await this.query.getProposalsByIds([details.proposal!.id])
+      Utils.assert(qVetoedProposal.status.__typename === 'ProposalStatusVetoed', 'Invalid proposal status')
+    }
+    // TODO: Other proposal types
+  }
+
   protected getExpectedProposalStatus(i: number): ResultingProposalStatus {
     const params = this.params[i]
     const proposal = this.proposals[i]
@@ -105,7 +114,27 @@ export class DecideOnProposalStatusFixture extends BaseQueryNodeFixture {
     this.params.forEach((params, i) => {
       const qProposal = qProposals.find((p) => p.id === params.proposalId.toString())
       Utils.assert(qProposal, 'Query node: Proposal not found')
-      assert.equal(qProposal.status.__typename, this.getExpectedProposalStatus(i))
+      Utils.assert(qProposal.status.__typename === this.getExpectedProposalStatus(i))
+      if (
+        qProposal.status.__typename === 'ProposalStatusExecuted' ||
+        qProposal.status.__typename === 'ProposalStatusExecutionFailed'
+      ) {
+        Utils.assert(qProposal.status.proposalExecutedEvent?.id, 'Missing proposalExecutedEvent reference')
+        assert.equal(qProposal.status.proposalExecutedEvent?.executionStatus.__typename, qProposal.status.__typename)
+      } else if (
+        qProposal.status.__typename === 'ProposalStatusDormant' ||
+        qProposal.status.__typename === 'ProposalStatusGracing'
+      ) {
+        Utils.assert(qProposal.status.proposalStatusUpdatedEvent?.id, 'Missing proposalStatusUpdatedEvent reference')
+        assert.equal(qProposal.status.proposalStatusUpdatedEvent?.newStatus.__typename, qProposal.status.__typename)
+        assert.include(
+          qProposal.proposalStatusUpdates.map((u) => u.id),
+          qProposal.status.proposalStatusUpdatedEvent?.id
+        )
+      } else {
+        Utils.assert(qProposal.status.proposalDecisionMadeEvent?.id, 'Missing proposalDecisionMadeEvent reference')
+        assert.equal(qProposal.status.proposalDecisionMadeEvent?.decisionStatus.__typename, qProposal.status.__typename)
+      }
     })
   }
 
@@ -135,10 +164,23 @@ export class DecideOnProposalStatusFixture extends BaseQueryNodeFixture {
     Utils.assert(this.voteOnProposalsRunner)
     await this.voteOnProposalsRunner.runQueryNodeChecks()
 
-    // TODO: ProposalDecisionStatus / ProposalExecutionStatus events
     const qProposals = await this.query.tryQueryWithTimeout(
       () => this.query.getProposalsByIds(this.params.map((p) => p.proposalId)),
       (res) => this.assertProposalStatusesAreValid(res)
     )
+
+    await Promise.all(
+      this.proposals.map(async (proposal, i) => {
+        let qProposal = qProposals[i]
+        if (this.getExpectedProposalStatus(i) === 'ProposalStatusGracing') {
+          await this.api.untilBlock(qProposal.statusSetAtBlock + proposal.parameters.gracePeriod.toNumber())
+          ;[qProposal] = await this.query.tryQueryWithTimeout(
+            () => this.query.getProposalsByIds([this.params[i].proposalId]),
+            ([p]) => p.status.__typename === 'ProposalStatusExecuted'
+          )
+          await this.postExecutionChecks(qProposal)
+        }
+      })
+    )
   }
 }

+ 17 - 0
tests/integration-tests/src/flows/council/elect.ts

@@ -0,0 +1,17 @@
+import { FlowProps } from '../../Flow'
+import Debugger from 'debug'
+import { FixtureRunner } from '../../Fixture'
+import { ElectCouncilFixture } from '../../fixtures/council/ElectCouncilFixture'
+
+// Currently only used by proposals flow
+
+export default async function electCouncil({ api, query }: FlowProps): Promise<void> {
+  const debug = Debugger('flow:elect-council')
+  debug('Started')
+  api.enableDebugTxLogs()
+
+  const electCouncilFixture = new ElectCouncilFixture(api, query)
+  await new FixtureRunner(electCouncilFixture).run()
+
+  debug('Done')
+}

+ 34 - 0
tests/integration-tests/src/flows/proposals/cancellingProposal.ts

@@ -0,0 +1,34 @@
+import { FlowProps } from '../../Flow'
+import Debugger from 'debug'
+import { FixtureRunner } from '../../Fixture'
+import { BuyMembershipHappyCaseFixture } from '../../fixtures/membership'
+import { CreateProposalsFixture } from '../../fixtures/proposals'
+import { CancelProposalsFixture } from '../../fixtures/proposals/CancelProposalsFixture'
+
+export default async function cancellingProposals({ api, query }: FlowProps): Promise<void> {
+  const debug = Debugger('flow:cancelling-proposals')
+  debug('Started')
+  api.enableDebugTxLogs()
+
+  const [account] = (await api.createKeyPairs(1)).map((kp) => kp.address)
+  const buyMembershipFixture = new BuyMembershipHappyCaseFixture(api, query, [account])
+  await new FixtureRunner(buyMembershipFixture).run()
+  const [memberId] = buyMembershipFixture.getCreatedMembers()
+
+  const createProposalFixture = new CreateProposalsFixture(api, query, [
+    {
+      type: 'Signal',
+      details: 'Proposal to cancel',
+      asMember: memberId,
+      title: 'Proposal to cancel',
+      description: 'Proposal to cancel',
+    },
+  ])
+  await new FixtureRunner(createProposalFixture).run()
+  const [proposalId] = createProposalFixture.getCreatedProposalsIds()
+
+  const cancelProposalsFixture = new CancelProposalsFixture(api, query, [proposalId])
+  await new FixtureRunner(cancelProposalsFixture).runWithQueryNodeChecks()
+
+  debug('Done')
+}

+ 0 - 7
tests/integration-tests/src/flows/proposals/index.ts

@@ -10,11 +10,9 @@ import {
 } from '../../fixtures/workingGroups'
 import { OpeningMetadata } from '@joystream/metadata-protobuf'
 import { AllProposalsOutcomesFixture, TestedProposal } from '../../fixtures/proposals/AllProposalsOutcomesFixture'
-import { ElectCouncilFixture } from '../../fixtures/council/ElectCouncilFixture'
 
 //   // TODO:
 //   // RuntimeUpgradeProposal
-//   // VetoProposal
 //   // TODO: Blog-related proposals:
 //   // CreateBlogPost
 //   // EditBlogPostProposal
@@ -26,11 +24,6 @@ export default async function creatingProposals({ api, query }: FlowProps): Prom
   debug('Started')
   api.enableDebugTxLogs()
 
-  debug('Initializing council...')
-  const electCouncilFixture = new ElectCouncilFixture(api, query)
-  await new FixtureRunner(electCouncilFixture).run()
-  debug('Council initialized')
-
   debug('Creating test lead openings and applications...')
   const createLeadOpeningsFixture = new CreateOpeningsFixture(
     api,

+ 47 - 0
tests/integration-tests/src/flows/proposals/vetoProposal.ts

@@ -0,0 +1,47 @@
+import { FlowProps } from '../../Flow'
+import Debugger from 'debug'
+import { FixtureRunner } from '../../Fixture'
+import { BuyMembershipHappyCaseFixture } from '../../fixtures/membership'
+import { CreateProposalsFixture, DecideOnProposalStatusFixture } from '../../fixtures/proposals'
+
+export default async function vetoProposal({ api, query }: FlowProps): Promise<void> {
+  const debug = Debugger('flow:creating-proposals')
+  debug('Started')
+  api.enableDebugTxLogs()
+
+  const [account] = (await api.createKeyPairs(1)).map((kp) => kp.address)
+  const buyMembershipFixture = new BuyMembershipHappyCaseFixture(api, query, [account])
+  await new FixtureRunner(buyMembershipFixture).run()
+  const [memberId] = buyMembershipFixture.getCreatedMembers()
+
+  const createProposalFixture = new CreateProposalsFixture(api, query, [
+    {
+      type: 'Signal',
+      details: 'Proposal to be vetoed',
+      asMember: memberId,
+      title: 'Proposal to veto',
+      description: 'Proposal to be vetoed',
+    },
+  ])
+  await new FixtureRunner(createProposalFixture).run()
+  const [proposalId] = createProposalFixture.getCreatedProposalsIds()
+
+  const createVetoProposalFixture = new CreateProposalsFixture(api, query, [
+    {
+      type: 'VetoProposal',
+      details: proposalId,
+      asMember: memberId,
+      title: 'Veto proposal',
+      description: 'Test veto proposal',
+    },
+  ])
+  await new FixtureRunner(createVetoProposalFixture).run()
+  const [vetoProposalId] = createVetoProposalFixture.getCreatedProposalsIds()
+
+  const decideOnProposalStatusFixture = new DecideOnProposalStatusFixture(api, query, [
+    { proposalId: vetoProposalId, status: 'Approved' },
+  ])
+  await new FixtureRunner(decideOnProposalStatusFixture).runWithQueryNodeChecks()
+
+  debug('Done')
+}

+ 121 - 12
tests/integration-tests/src/graphql/generated/queries.ts

@@ -292,58 +292,137 @@ export type GetInitialInvitationCountUpdatedEventsByEventIdQuery = {
 
 type ProposalStatusFields_ProposalStatusDeciding_Fragment = {
   __typename: 'ProposalStatusDeciding'
-  proposalStatusUpdatedEvent?: Types.Maybe<{ id: string }>
+  proposalStatusUpdatedEvent?: Types.Maybe<{
+    id: string
+    newStatus:
+      | { __typename: 'ProposalStatusDeciding' }
+      | { __typename: 'ProposalStatusGracing' }
+      | { __typename: 'ProposalStatusDormant' }
+  }>
 }
 
 type ProposalStatusFields_ProposalStatusGracing_Fragment = {
   __typename: 'ProposalStatusGracing'
-  proposalStatusUpdatedEvent?: Types.Maybe<{ id: string }>
+  proposalStatusUpdatedEvent?: Types.Maybe<{
+    id: string
+    newStatus:
+      | { __typename: 'ProposalStatusDeciding' }
+      | { __typename: 'ProposalStatusGracing' }
+      | { __typename: 'ProposalStatusDormant' }
+  }>
 }
 
 type ProposalStatusFields_ProposalStatusDormant_Fragment = {
   __typename: 'ProposalStatusDormant'
-  proposalStatusUpdatedEvent?: Types.Maybe<{ id: string }>
+  proposalStatusUpdatedEvent?: Types.Maybe<{
+    id: string
+    newStatus:
+      | { __typename: 'ProposalStatusDeciding' }
+      | { __typename: 'ProposalStatusGracing' }
+      | { __typename: 'ProposalStatusDormant' }
+  }>
 }
 
 type ProposalStatusFields_ProposalStatusVetoed_Fragment = {
   __typename: 'ProposalStatusVetoed'
-  proposalDecisionMadeEvent?: Types.Maybe<{ id: string }>
+  proposalDecisionMadeEvent?: Types.Maybe<{
+    id: string
+    decisionStatus:
+      | { __typename: 'ProposalStatusDormant' }
+      | { __typename: 'ProposalStatusGracing' }
+      | { __typename: 'ProposalStatusVetoed' }
+      | { __typename: 'ProposalStatusSlashed' }
+      | { __typename: 'ProposalStatusRejected' }
+      | { __typename: 'ProposalStatusExpired' }
+      | { __typename: 'ProposalStatusCancelled' }
+      | { __typename: 'ProposalStatusCanceledByRuntime' }
+  }>
 }
 
 type ProposalStatusFields_ProposalStatusExecuted_Fragment = {
   __typename: 'ProposalStatusExecuted'
-  proposalExecutedEvent?: Types.Maybe<{ id: string }>
+  proposalExecutedEvent?: Types.Maybe<{
+    id: string
+    executionStatus: { __typename: 'ProposalStatusExecuted' } | { __typename: 'ProposalStatusExecutionFailed' }
+  }>
 }
 
 type ProposalStatusFields_ProposalStatusExecutionFailed_Fragment = {
   __typename: 'ProposalStatusExecutionFailed'
   errorMessage: string
-  proposalExecutedEvent?: Types.Maybe<{ id: string }>
+  proposalExecutedEvent?: Types.Maybe<{
+    id: string
+    executionStatus: { __typename: 'ProposalStatusExecuted' } | { __typename: 'ProposalStatusExecutionFailed' }
+  }>
 }
 
 type ProposalStatusFields_ProposalStatusSlashed_Fragment = {
   __typename: 'ProposalStatusSlashed'
-  proposalDecisionMadeEvent?: Types.Maybe<{ id: string }>
+  proposalDecisionMadeEvent?: Types.Maybe<{
+    id: string
+    decisionStatus:
+      | { __typename: 'ProposalStatusDormant' }
+      | { __typename: 'ProposalStatusGracing' }
+      | { __typename: 'ProposalStatusVetoed' }
+      | { __typename: 'ProposalStatusSlashed' }
+      | { __typename: 'ProposalStatusRejected' }
+      | { __typename: 'ProposalStatusExpired' }
+      | { __typename: 'ProposalStatusCancelled' }
+      | { __typename: 'ProposalStatusCanceledByRuntime' }
+  }>
 }
 
 type ProposalStatusFields_ProposalStatusRejected_Fragment = {
   __typename: 'ProposalStatusRejected'
-  proposalDecisionMadeEvent?: Types.Maybe<{ id: string }>
+  proposalDecisionMadeEvent?: Types.Maybe<{
+    id: string
+    decisionStatus:
+      | { __typename: 'ProposalStatusDormant' }
+      | { __typename: 'ProposalStatusGracing' }
+      | { __typename: 'ProposalStatusVetoed' }
+      | { __typename: 'ProposalStatusSlashed' }
+      | { __typename: 'ProposalStatusRejected' }
+      | { __typename: 'ProposalStatusExpired' }
+      | { __typename: 'ProposalStatusCancelled' }
+      | { __typename: 'ProposalStatusCanceledByRuntime' }
+  }>
 }
 
 type ProposalStatusFields_ProposalStatusExpired_Fragment = {
   __typename: 'ProposalStatusExpired'
-  proposalDecisionMadeEvent?: Types.Maybe<{ id: string }>
+  proposalDecisionMadeEvent?: Types.Maybe<{
+    id: string
+    decisionStatus:
+      | { __typename: 'ProposalStatusDormant' }
+      | { __typename: 'ProposalStatusGracing' }
+      | { __typename: 'ProposalStatusVetoed' }
+      | { __typename: 'ProposalStatusSlashed' }
+      | { __typename: 'ProposalStatusRejected' }
+      | { __typename: 'ProposalStatusExpired' }
+      | { __typename: 'ProposalStatusCancelled' }
+      | { __typename: 'ProposalStatusCanceledByRuntime' }
+  }>
 }
 
 type ProposalStatusFields_ProposalStatusCancelled_Fragment = {
   __typename: 'ProposalStatusCancelled'
-  canelledInEvent?: Types.Maybe<{ id: string }>
+  cancelledInEvent?: Types.Maybe<{ id: string }>
 }
 
 type ProposalStatusFields_ProposalStatusCanceledByRuntime_Fragment = {
   __typename: 'ProposalStatusCanceledByRuntime'
-  proposalDecisionMadeEvent?: Types.Maybe<{ id: string }>
+  proposalDecisionMadeEvent?: Types.Maybe<{
+    id: string
+    decisionStatus:
+      | { __typename: 'ProposalStatusDormant' }
+      | { __typename: 'ProposalStatusGracing' }
+      | { __typename: 'ProposalStatusVetoed' }
+      | { __typename: 'ProposalStatusSlashed' }
+      | { __typename: 'ProposalStatusRejected' }
+      | { __typename: 'ProposalStatusExpired' }
+      | { __typename: 'ProposalStatusCancelled' }
+      | { __typename: 'ProposalStatusCanceledByRuntime' }
+  }>
 }
 
 export type ProposalStatusFieldsFragment =
@@ -1604,57 +1683,87 @@ export const ProposalStatusFields = gql`
     ... on ProposalStatusDeciding {
       proposalStatusUpdatedEvent {
         id
+        newStatus {
+          __typename
+        }
       }
     }
     ... on ProposalStatusGracing {
       proposalStatusUpdatedEvent {
         id
+        newStatus {
+          __typename
+        }
       }
     }
     ... on ProposalStatusDormant {
       proposalStatusUpdatedEvent {
         id
+        newStatus {
+          __typename
+        }
       }
     }
     ... on ProposalStatusVetoed {
       proposalDecisionMadeEvent {
         id
+        decisionStatus {
+          __typename
+        }
       }
     }
     ... on ProposalStatusExecuted {
       proposalExecutedEvent {
         id
+        executionStatus {
+          __typename
+        }
       }
     }
     ... on ProposalStatusExecutionFailed {
       proposalExecutedEvent {
         id
+        executionStatus {
+          __typename
+        }
       }
       errorMessage
     }
     ... on ProposalStatusSlashed {
       proposalDecisionMadeEvent {
         id
+        decisionStatus {
+          __typename
+        }
       }
     }
     ... on ProposalStatusRejected {
       proposalDecisionMadeEvent {
         id
+        decisionStatus {
+          __typename
+        }
       }
     }
     ... on ProposalStatusExpired {
       proposalDecisionMadeEvent {
         id
+        decisionStatus {
+          __typename
+        }
       }
     }
     ... on ProposalStatusCancelled {
-      canelledInEvent {
+      cancelledInEvent {
         id
       }
     }
     ... on ProposalStatusCanceledByRuntime {
       proposalDecisionMadeEvent {
         id
+        decisionStatus {
+          __typename
+        }
       }
     }
   }

+ 1 - 1
tests/integration-tests/src/graphql/generated/schema.ts

@@ -6716,7 +6716,7 @@ export type ProposalStatusCanceledByRuntime = {
 
 export type ProposalStatusCancelled = {
   /** The related ProposalCancelledEvent */
-  canelledInEvent?: Maybe<ProposalCancelledEvent>
+  cancelledInEvent?: Maybe<ProposalCancelledEvent>
 }
 
 export type ProposalStatusDeciding = {

+ 31 - 1
tests/integration-tests/src/graphql/queries/proposals.graphql

@@ -3,55 +3,82 @@ fragment ProposalStatusFields on ProposalStatus {
   ... on ProposalStatusDeciding {
     proposalStatusUpdatedEvent {
       id
+      newStatus {
+        __typename
+      }
     }
   }
   ... on ProposalStatusGracing {
     proposalStatusUpdatedEvent {
       id
+      newStatus {
+        __typename
+      }
     }
   }
   ... on ProposalStatusDormant {
     proposalStatusUpdatedEvent {
       id
+      newStatus {
+        __typename
+      }
     }
   }
   ... on ProposalStatusVetoed {
     proposalDecisionMadeEvent {
       id
+      decisionStatus {
+        __typename
+      }
     }
   }
   ... on ProposalStatusExecuted {
     proposalExecutedEvent {
       id
+      executionStatus {
+        __typename
+      }
     }
   }
 
   ... on ProposalStatusExecutionFailed {
     proposalExecutedEvent {
       id
+      executionStatus {
+        __typename
+      }
     }
     errorMessage
   }
   ... on ProposalStatusSlashed {
     proposalDecisionMadeEvent {
       id
+      decisionStatus {
+        __typename
+      }
     }
   }
 
   ... on ProposalStatusRejected {
     proposalDecisionMadeEvent {
       id
+      decisionStatus {
+        __typename
+      }
     }
   }
 
   ... on ProposalStatusExpired {
     proposalDecisionMadeEvent {
       id
+      decisionStatus {
+        __typename
+      }
     }
   }
 
   ... on ProposalStatusCancelled {
-    canelledInEvent {
+    cancelledInEvent {
       id
     }
   }
@@ -59,6 +86,9 @@ fragment ProposalStatusFields on ProposalStatus {
   ... on ProposalStatusCanceledByRuntime {
     proposalDecisionMadeEvent {
       id
+      decisionStatus {
+        __typename
+      }
     }
   }
 }

+ 5 - 1
tests/integration-tests/src/scenarios/full.ts

@@ -12,6 +12,9 @@ import groupStatus from '../flows/working-groups/groupStatus'
 import workerActions from '../flows/working-groups/workerActions'
 import groupBudget from '../flows/working-groups/groupBudget'
 import proposals from '../flows/proposals'
+import cancellingProposals from '../flows/proposals/cancellingProposal'
+import vetoProposal from '../flows/proposals/vetoProposal'
+import electCouncil from '../flows/council/elect'
 import { scenario } from '../Scenario'
 
 scenario(async ({ job }) => {
@@ -25,7 +28,8 @@ scenario(async ({ job }) => {
   job('transferring invites', transferringInvites).after(membershipSystemJob)
   job('managing staking accounts', managingStakingAccounts).after(membershipSystemJob)
 
-  const proposalsJob = job('proposals', proposals)
+  const councilJob = job('electing council', electCouncil)
+  const proposalsJob = job('proposals', [proposals, cancellingProposals, vetoProposal]).requires(councilJob)
 
   const sudoHireLead = job('sudo lead opening', leadOpening).after(proposalsJob)
   job('openings and applications', openingsAndApplications).requires(sudoHireLead)

+ 5 - 1
tests/integration-tests/src/scenarios/proposals.ts

@@ -1,6 +1,10 @@
 import proposals from '../flows/proposals'
+import cancellingProposals from '../flows/proposals/cancellingProposal'
+import vetoProposal from '../flows/proposals/vetoProposal'
+import electCouncil from '../flows/council/elect'
 import { scenario } from '../Scenario'
 
 scenario(async ({ job }) => {
-  job('proposals', proposals)
+  const councilJob = job('electing council', electCouncil)
+  job('proposals', [proposals, cancellingProposals, vetoProposal]).requires(councilJob)
 })