TerminateWorkersFixture.ts 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144
  1. import BN from 'bn.js'
  2. import { assert } from 'chai'
  3. import { Api } from '../../Api'
  4. import { QueryNodeApi } from '../../QueryNodeApi'
  5. import { EventDetails, WorkingGroupModuleName } from '../../types'
  6. import { BaseWorkingGroupFixture } from './BaseWorkingGroupFixture'
  7. import { Worker, WorkerId } from '@joystream/types/working-group'
  8. import { SubmittableExtrinsic } from '@polkadot/api/types'
  9. import { ISubmittableResult } from '@polkadot/types/types/'
  10. import { Utils } from '../../utils'
  11. import {
  12. LeaderUnsetEventFieldsFragment,
  13. TerminatedLeaderEventFieldsFragment,
  14. TerminatedWorkerEventFieldsFragment,
  15. WorkerFieldsFragment,
  16. } from '../../graphql/generated/queries'
  17. export class TerminateWorkersFixture extends BaseWorkingGroupFixture {
  18. protected asSudo: boolean
  19. protected workerIds: WorkerId[]
  20. protected penalties: BN[]
  21. protected workers: Worker[] = []
  22. protected workerStakes: BN[] = []
  23. public constructor(
  24. api: Api,
  25. query: QueryNodeApi,
  26. group: WorkingGroupModuleName,
  27. workerIds: WorkerId[],
  28. penalties: BN[],
  29. asSudo = false
  30. ) {
  31. super(api, query, group)
  32. this.workerIds = workerIds
  33. this.penalties = penalties
  34. this.asSudo = asSudo
  35. }
  36. protected async loadWorkersData(): Promise<void> {
  37. this.workers = await this.api.query[this.group].workerById.multi<Worker>(this.workerIds)
  38. this.workerStakes = await Promise.all(
  39. this.workers.map((w) => this.api.getStakedBalance(w.staking_account_id, this.api.lockIdByGroup(this.group)))
  40. )
  41. }
  42. protected async getSignerAccountOrAccounts(): Promise<string> {
  43. return this.asSudo ? (await this.api.query.sudo.key()).toString() : await this.api.getLeadRoleKey(this.group)
  44. }
  45. protected async getExtrinsics(): Promise<SubmittableExtrinsic<'promise'>[]> {
  46. const extrinsics = this.workerIds.map((workerId, i) =>
  47. this.api.tx[this.group].terminateRole(workerId, this.penalties[i], this.getRationale(workerId))
  48. )
  49. return this.asSudo ? extrinsics.map((tx) => this.api.tx.sudo.sudo(tx)) : extrinsics
  50. }
  51. protected getEventFromResult(result: ISubmittableResult): Promise<EventDetails> {
  52. return this.api.retrieveWorkingGroupsEventDetails(
  53. result,
  54. this.group,
  55. this.asSudo ? 'TerminatedLeader' : 'TerminatedWorker'
  56. )
  57. }
  58. public async execute(): Promise<void> {
  59. await this.loadWorkersData()
  60. await super.execute()
  61. }
  62. protected getRationale(workerId: WorkerId): string {
  63. return `Worker ${workerId.toString()} termination rationale`
  64. }
  65. protected assertQueryNodeEventIsValid(
  66. qEvent: TerminatedWorkerEventFieldsFragment | TerminatedLeaderEventFieldsFragment,
  67. i: number
  68. ): void {
  69. assert.equal(qEvent.worker.runtimeId, this.workerIds[i].toNumber())
  70. assert.equal(qEvent.group.name, this.group)
  71. assert.equal(qEvent.rationale, this.getRationale(this.workerIds[i]))
  72. assert.equal(qEvent.penalty, this.penalties[i].toString())
  73. }
  74. protected assertQueriedWorkersAreValid(
  75. qEvents: (TerminatedWorkerEventFieldsFragment | TerminatedLeaderEventFieldsFragment)[],
  76. qWorkers: WorkerFieldsFragment[]
  77. ): void {
  78. this.events.map((e, i) => {
  79. const qEvent = this.findMatchingQueryNodeEvent(e, qEvents)
  80. const workerId = this.workerIds[i]
  81. const worker = qWorkers.find((w) => w.runtimeId === workerId.toNumber())
  82. Utils.assert(worker, 'Query node: Worker not found!')
  83. assert.equal(worker.stake, '0')
  84. assert.equal(worker.rewardPerBlock, '0')
  85. Utils.assert(
  86. worker.status.__typename === 'WorkerStatusTerminated',
  87. `Invalid worker status: ${worker.status.__typename}`
  88. )
  89. Utils.assert(worker.status.terminatedWorkerEvent, 'Query node: Missing terminatedWorkerEvent relation')
  90. assert.equal(worker.status.terminatedWorkerEvent.id, qEvent.id)
  91. })
  92. }
  93. protected assertQueriedLeaderUnsetEventIsValid(
  94. eventDetails: EventDetails,
  95. qEvent: LeaderUnsetEventFieldsFragment | null
  96. ): void {
  97. Utils.assert(qEvent, 'Query node: LeaderUnsetEvent not found!')
  98. assert.equal(qEvent.inExtrinsic, this.extrinsics[0].hash.toString())
  99. assert.equal(qEvent.inBlock, eventDetails.blockNumber)
  100. assert.equal(new Date(qEvent.createdAt).getTime(), eventDetails.blockTimestamp)
  101. assert.equal(qEvent.group.name, this.group)
  102. assert.equal(qEvent.leader.runtimeId, this.workerIds[0].toNumber())
  103. }
  104. async runQueryNodeChecks(): Promise<void> {
  105. await super.runQueryNodeChecks()
  106. // Query the event and check event + hiredWorkers
  107. const qEvents = await this.query.tryQueryWithTimeout(
  108. () =>
  109. this.asSudo
  110. ? this.query.getTerminatedLeaderEvents(this.events)
  111. : this.query.getTerminatedWorkerEvents(this.events),
  112. (qEvents) => this.assertQueryNodeEventsAreValid(qEvents)
  113. )
  114. // Check workers
  115. const qWorkers = await this.query.getWorkersByIds(this.workerIds, this.group)
  116. this.assertQueriedWorkersAreValid(qEvents, qWorkers)
  117. // If lead - check LeaderUnset event
  118. if (this.asSudo) {
  119. const leaderUnsetEvent = await this.api.retrieveWorkingGroupsEventDetails(
  120. this.results[0],
  121. this.group,
  122. 'LeaderUnset'
  123. )
  124. const qEvent = await this.query.getLeaderUnsetEvent(leaderUnsetEvent)
  125. this.assertQueriedLeaderUnsetEventIsValid(leaderUnsetEvent, qEvent)
  126. }
  127. }
  128. }