CreateOpeningsFixture.ts 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154
  1. import { Api } from '../../Api'
  2. import { BaseCreateOpeningFixture } from './BaseCreateOpeningFixture'
  3. import { QueryNodeApi } from '../../QueryNodeApi'
  4. import { OpeningAddedEventDetails, WorkingGroupModuleName } from '../../types'
  5. import { OpeningId } from '@joystream/types/working-group'
  6. import { SubmittableExtrinsic } from '@polkadot/api/types'
  7. import { Utils } from '../../utils'
  8. import { ISubmittableResult } from '@polkadot/types/types/'
  9. import { OpeningAddedEventFieldsFragment, OpeningFieldsFragment } from '../../graphql/generated/queries'
  10. import { WorkingGroupOpeningType } from '../../graphql/generated/schema'
  11. import { assert } from 'chai'
  12. import { MIN_APPLICATION_STAKE, MIN_UNSTANKING_PERIOD } from '../../consts'
  13. import moment from 'moment'
  14. import BN from 'bn.js'
  15. import { IOpeningMetadata, OpeningMetadata } from '@joystream/metadata-protobuf'
  16. import { createType } from '@joystream/types'
  17. import { Bytes } from '@polkadot/types'
  18. export type OpeningParams = {
  19. stake: BN
  20. unstakingPeriod: number
  21. reward: BN
  22. metadata: IOpeningMetadata | string
  23. expectMetadataFailure?: boolean
  24. }
  25. export const DEFAULT_OPENING_PARAMS: Omit<OpeningParams, 'metadata'> & { metadata: IOpeningMetadata } = {
  26. stake: MIN_APPLICATION_STAKE,
  27. unstakingPeriod: MIN_UNSTANKING_PERIOD,
  28. reward: new BN(10),
  29. metadata: {
  30. shortDescription: 'Test opening',
  31. description: '# Test opening',
  32. expectedEndingTimestamp: moment().unix() + 60,
  33. hiringLimit: 1,
  34. applicationDetails: '- This is automatically created opening, do not apply!',
  35. applicationFormQuestions: [
  36. { question: 'Question 1?', type: OpeningMetadata.ApplicationFormQuestion.InputType.TEXT },
  37. { question: 'Question 2?', type: OpeningMetadata.ApplicationFormQuestion.InputType.TEXTAREA },
  38. ],
  39. },
  40. }
  41. export class CreateOpeningsFixture extends BaseCreateOpeningFixture {
  42. protected asSudo: boolean
  43. protected events: OpeningAddedEventDetails[] = []
  44. protected openingsParams: OpeningParams[]
  45. public constructor(
  46. api: Api,
  47. query: QueryNodeApi,
  48. group: WorkingGroupModuleName,
  49. openingsParams?: OpeningParams[],
  50. asSudo = false
  51. ) {
  52. super(api, query, group)
  53. this.openingsParams = openingsParams || [DEFAULT_OPENING_PARAMS]
  54. this.asSudo = asSudo
  55. }
  56. public getCreatedOpeningIds(): OpeningId[] {
  57. if (!this.events.length) {
  58. throw new Error('Trying to get created opening ids before they were created!')
  59. }
  60. return this.events.map((e) => e.openingId)
  61. }
  62. protected async getSignerAccountOrAccounts(): Promise<string> {
  63. return this.asSudo ? (await this.api.query.sudo.key()).toString() : await this.api.getLeadRoleKey(this.group)
  64. }
  65. protected getOpeningMetadata(params: OpeningParams): IOpeningMetadata | null {
  66. if (typeof params.metadata === 'string') {
  67. try {
  68. return Utils.metadataFromBytes(OpeningMetadata, createType('Bytes', params.metadata))
  69. } catch (e) {
  70. if (!params.expectMetadataFailure) {
  71. throw e
  72. }
  73. return null
  74. }
  75. }
  76. return params.metadata
  77. }
  78. protected getOpeningMetadataBytes(params: { metadata: IOpeningMetadata | string }): Bytes {
  79. const { metadata } = params
  80. return typeof metadata === 'string'
  81. ? createType('Bytes', metadata)
  82. : Utils.metadataToBytes(OpeningMetadata, metadata)
  83. }
  84. protected async getExtrinsics(): Promise<SubmittableExtrinsic<'promise'>[]> {
  85. const extrinsics = this.openingsParams.map((params) =>
  86. this.api.tx[this.group].addOpening(
  87. this.getOpeningMetadataBytes(params),
  88. this.asSudo ? 'Leader' : 'Regular',
  89. { stake_amount: params.stake, leaving_unstaking_period: params.unstakingPeriod },
  90. params.reward
  91. )
  92. )
  93. return this.asSudo ? extrinsics.map((tx) => this.api.tx.sudo.sudo(tx)) : extrinsics
  94. }
  95. protected async getEventFromResult(result: ISubmittableResult): Promise<OpeningAddedEventDetails> {
  96. return this.api.retrieveOpeningAddedEventDetails(result, this.group)
  97. }
  98. protected assertQueriedOpeningsAreValid(
  99. qOpenings: OpeningFieldsFragment[],
  100. qEvents: OpeningAddedEventFieldsFragment[]
  101. ): void {
  102. this.events.map((e, i) => {
  103. const qOpening = qOpenings.find((o) => o.runtimeId === e.openingId.toNumber())
  104. const openingParams = this.openingsParams[i]
  105. const qEvent = this.findMatchingQueryNodeEvent(e, qEvents)
  106. Utils.assert(qOpening, 'Query node: Opening not found')
  107. assert.equal(qOpening.runtimeId, e.openingId.toNumber())
  108. assert.equal(qOpening.createdInEvent.id, qEvent.id)
  109. assert.equal(qOpening.group.name, this.group)
  110. assert.equal(qOpening.rewardPerBlock, openingParams.reward.toString())
  111. assert.equal(qOpening.type, this.asSudo ? WorkingGroupOpeningType.Leader : WorkingGroupOpeningType.Regular)
  112. assert.equal(qOpening.status.__typename, 'OpeningStatusOpen')
  113. assert.equal(qOpening.stakeAmount, openingParams.stake.toString())
  114. assert.equal(qOpening.unstakingPeriod, openingParams.unstakingPeriod)
  115. // Metadata
  116. this.assertQueriedOpeningMetadataIsValid(qOpening.metadata, this.getOpeningMetadata(openingParams))
  117. })
  118. }
  119. protected assertQueryNodeEventIsValid(qEvent: OpeningAddedEventFieldsFragment, i: number): void {
  120. assert.equal(qEvent.group.name, this.group)
  121. assert.equal(qEvent.opening.runtimeId, this.events[i].openingId.toNumber())
  122. }
  123. async runQueryNodeChecks(): Promise<void> {
  124. await super.runQueryNodeChecks()
  125. // Query the events
  126. const qEvents = await this.query.tryQueryWithTimeout(
  127. () => this.query.getOpeningAddedEvents(this.events),
  128. (qEvents) => this.assertQueryNodeEventsAreValid(qEvents)
  129. )
  130. // Query the openings
  131. const qOpenings = await this.query.getOpeningsByIds(
  132. this.events.map((e) => e.openingId),
  133. this.group
  134. )
  135. this.assertQueriedOpeningsAreValid(qOpenings, qEvents)
  136. }
  137. }