Ver código fonte

tests: create channel and video fixtures

Mokhtar Naamani 3 anos atrás
pai
commit
b6ccd54350

+ 4 - 1
tests/network-tests/.env

@@ -57,4 +57,7 @@ MINT_CAPACITY_INCREMENT = 1000
 # Storage node address to download content from
 STORAGE_NODE_URL = http://localhost:3001/asset/v0
 # Mini-secret or mnemonic used in SURI for deterministic key derivation
-SURI_MINI_SECRET = ""
+SURI_MINI_SECRET = ""
+# The starting key id to use when running a scenario. This will allow scenario
+# to be able to use all accounts generated in a prior scenario run against the same chain
+START_KEY_ID = 0

+ 71 - 3
tests/network-tests/src/Api.ts

@@ -29,10 +29,11 @@ import {
   OpeningId,
 } from '@joystream/types/hiring'
 import { FillOpeningParameters, ProposalId } from '@joystream/types/proposals'
-import { v4 as uuid } from 'uuid'
 import { ContentId, DataObject } from '@joystream/types/storage'
 import { extendDebug } from './Debugger'
 import { InvertedPromise } from './InvertedPromise'
+import { VideoId } from '@joystream/types/content'
+import { ChannelId } from '@joystream/types/common'
 
 export enum WorkingGroups {
   StorageWorkingGroup = 'storageWorkingGroup',
@@ -109,6 +110,15 @@ export class ApiFactory {
     }
     return keys
   }
+
+  public keyGenInfo(): string {
+    const start = 0
+    const final = this.keyId
+    return JSON.stringify({
+      start,
+      final,
+    })
+  }
 }
 
 export class Api {
@@ -137,8 +147,8 @@ export class Api {
     return this.factory.createKeyPairs(n)
   }
 
-  public getAccountToKeyIdMappings() {
-    return this.factory.addressesToKeyId
+  public keyGenInfo(): string {
+    return this.factory.keyGenInfo()
   }
 
   // Well known WorkingGroup enum defined in runtime
@@ -1723,4 +1733,62 @@ export class Api {
     const dataObject = await this.api.query.dataDirectory.dataByContentId<Option<DataObject>>(contentId)
     return dataObject.unwrapOr(null)
   }
+
+  async getMemberControllerAccount(memberId: number): Promise<string | undefined> {
+    return (await this.api.query.members.membershipById(memberId))?.controller_account.toString()
+  }
+
+  async createMockChannel(memberId: number, memberControllerAccount?: string): Promise<ChannelId | null> {
+    memberControllerAccount = memberControllerAccount || (await this.getMemberControllerAccount(memberId))
+
+    if (!memberControllerAccount) {
+      throw new Error('invalid member id')
+    }
+
+    // Create a channel without any assets
+    const tx = this.api.tx.content.createChannel(
+      { Member: memberId },
+      {
+        assets: [],
+        meta: null,
+        reward_account: null,
+      }
+    )
+
+    const result = await this.sender.signAndSend(tx, memberControllerAccount)
+
+    const record = this.findEventRecord(result.events, 'content', 'ChannelCreated')
+    if (record) {
+      return record.event.data[1] as ChannelId
+    }
+
+    return null
+  }
+
+  async createMockVideo(
+    memberId: number,
+    channelId: number,
+    memberControllerAccount?: string
+  ): Promise<VideoId | null> {
+    memberControllerAccount = memberControllerAccount || (await this.getMemberControllerAccount(memberId))
+
+    if (!memberControllerAccount) {
+      throw new Error('invalid member id')
+    }
+
+    // Create a video without any assets
+    const tx = this.api.tx.content.createVideo({ Member: memberId }, channelId, {
+      assets: [],
+      meta: null,
+    })
+
+    const result = await this.sender.signAndSend(tx, memberControllerAccount)
+
+    const record = this.findEventRecord(result.events, 'content', 'VideoCreated')
+    if (record) {
+      return record.event.data[2] as VideoId
+    }
+
+    return null
+  }
 }

+ 15 - 3
tests/network-tests/src/Scenario.ts

@@ -24,7 +24,7 @@ export async function scenario(scene: (props: ScenarioProps) => Promise<void>):
   // Connect api to the chain
   const nodeUrl: string = env.NODE_URL || 'ws://127.0.0.1:9944'
   const provider = new WsProvider(nodeUrl)
-  const miniSecret = process.env.SURI_MINI_SECRET || ''
+  const miniSecret = env.SURI_MINI_SECRET || ''
   const apiFactory = await ApiFactory.create(
     provider,
     env.TREASURY_ACCOUNT_URI || '//Alice',
@@ -32,6 +32,14 @@ export async function scenario(scene: (props: ScenarioProps) => Promise<void>):
     miniSecret
   )
 
+  const api = apiFactory.getApi('Key Generation')
+
+  // Generate all key ids before START_KEY_ID
+  const startKeyId = parseInt(env.START_KEY_ID || '0')
+  if (startKeyId) {
+    api.createKeyPairs(startKeyId)
+  }
+
   const queryNodeUrl: string = env.QUERY_NODE_URL || 'http://127.0.0.1:8081/graphql'
 
   const queryNodeProvider = new ApolloClient({
@@ -50,18 +58,22 @@ export async function scenario(scene: (props: ScenarioProps) => Promise<void>):
 
   const resources = new ResourceManager()
 
+  let exitCode = 0
+
   try {
     await jobs.run(resources)
   } catch (err) {
     console.error(err)
-    process.exit(-1)
+    exitCode = -1
   }
 
+  console.log(api.keyGenInfo())
+
   // Note: disconnecting and then reconnecting to the chain in the same process
   // doesn't seem to work!
   // Disconnecting is causing error to be thrown:
   // RPC-CORE: getStorage(key: StorageKey, at?: BlockHash): StorageData:: disconnected from ws://127.0.0.1:9944: 1000:: Normal connection closure
   // Are there subsciptions somewhere?
   // apiFactory.close()
-  process.exit()
+  process.exit(exitCode)
 }

+ 2 - 18
tests/network-tests/src/scenarios/setup-new-chain.ts

@@ -9,22 +9,6 @@ scenario(async ({ job }) => {
 
   const leads = job('Setup WorkingGroup Leads', [leaderSetup.storage, leaderSetup.content])
 
-  // After content lead is created create some mock content in content directory
-  // Without uploading actual media
-  const mockContent = job('Create Mock Content', mockContentFlow).after(leads)
-
-  // Dump the account key ids that where generated in scenario so they can be re-derived at a later time
-  job('Dump accounts', async ({ api }) => {
-    const mappings = api.getAccountToKeyIdMappings()
-    console.log(mappings)
-
-    // TODO: get each account we are interested in knowing the keyid for..
-    // const api = apiFactory.getApi('get interesting accounts')
-    // Member accounts of council, lead, workers, and worker role accounts.
-    // let accounts = api.getInterestingAccounts()
-    // console.log(Api.addressesToKeyId.get(account))
-  })
-    .after(leads)
-    .after(council)
-    .after(mockContent)
+  // Create some mock content in content directory - without assets or any real metadata
+  const mockContent = job('Create Mock Content', mockContentFlow)
 })

+ 1 - 1
tests/network-tests/src/sender.ts

@@ -17,7 +17,7 @@ export enum LogLevel {
 
 export class Sender {
   private readonly api: ApiPromise
-  private static readonly asyncLock: AsyncLock = new AsyncLock()
+  private static readonly asyncLock: AsyncLock = new AsyncLock({ maxPending: 2048 })
   private readonly keyring: Keyring
   private readonly debug: Debugger.Debugger
   private logs: LogLevel = LogLevel.None

+ 36 - 0
tests/network-tests/src/sumer/createChannelsAsMemberFixture.ts

@@ -0,0 +1,36 @@
+import { BaseFixture } from '../Fixture'
+import { Api } from '../Api'
+// import { MemberId } from '@joystream/types/members'
+import { ChannelId } from '@joystream/types/common'
+import { assert } from 'chai'
+
+export class CreateChannelsAsMemberFixture extends BaseFixture {
+  // Member that will be channel owner
+  private memberId: number
+  private numChannels: number
+  private createdChannels: (ChannelId | null)[] = []
+
+  constructor(api: Api, memberId: number, numChannels: number) {
+    super(api)
+    this.memberId = memberId
+    this.numChannels = numChannels
+  }
+
+  public getCreatedChannels(): (ChannelId | null)[] {
+    return this.createdChannels.slice()
+  }
+
+  public async execute(): Promise<void> {
+    const account = await this.api.getMemberControllerAccount(this.memberId)
+
+    const channels = []
+    for (let i = 0; i < this.numChannels; i++) {
+      channels.push(this.api.createMockChannel(this.memberId, account))
+    }
+
+    const channelIds = await Promise.all(channels)
+    this.createdChannels = channelIds.filter((id) => id !== null)
+
+    assert.equal(this.createdChannels.length, this.numChannels)
+  }
+}

+ 32 - 0
tests/network-tests/src/sumer/createVideosAsMemberFixture.ts

@@ -0,0 +1,32 @@
+import { BaseFixture } from '../Fixture'
+import { Api } from '../Api'
+// import { MemberId } from '@joystream/types/members'
+// import { ChannelId } from '@joystream/types/common'
+import { assert } from 'chai'
+
+export class CreateVideosAsMemberFixture extends BaseFixture {
+  // Member that will be channel owner
+  private memberId: number
+  private numVideos: number
+  private channelId: number
+
+  constructor(api: Api, memberId: number, channelId: number, numVideos: number) {
+    super(api)
+    this.memberId = memberId
+    this.numVideos = numVideos
+    this.channelId = channelId
+  }
+
+  public async execute(): Promise<void> {
+    const account = await this.api.getMemberControllerAccount(this.memberId)
+
+    const videos = []
+    for (let i = 0; i < this.numVideos; i++) {
+      videos.push(this.api.createMockVideo(this.memberId, this.channelId, account))
+    }
+
+    const videoIds = await Promise.all(videos)
+    const created = videoIds.filter((id) => id !== null).length
+    assert.equal(created, this.numVideos)
+  }
+}

+ 44 - 7
tests/network-tests/src/sumer/mockContentFlow.ts

@@ -1,18 +1,55 @@
 // import { assert } from 'chai'
-// import { ContentId } from '@joystream/types/storage'
 // import { registry } from '@joystream/types'
+import { CreateChannelsAsMemberFixture } from './createChannelsAsMemberFixture'
+import { CreateVideosAsMemberFixture } from './createVideosAsMemberFixture'
+import { BuyMembershipHappyCaseFixture } from '../fixtures/membershipModule'
 
 import { FlowProps } from '../Flow'
-// import { Utils } from '../utils'
+import { FixtureRunner } from '../Fixture'
 import { extendDebug } from '../Debugger'
+import BN from 'bn.js'
 
 export default async function mockContent({ api }: FlowProps): Promise<void> {
   const debug = extendDebug('flow:createMockContent')
   debug('Started')
-  // TODO: implement and use new fixtures:
-  // create categories with lead
-  // pick N member accounts
-  // create one channel per member
-  // upload V videos per channel (same contentid)
+
+  // TODO: create categories with lead
+
+  const memberAccount = api.createKeyPairs(1)[0].key.address
+  const createMember: BuyMembershipHappyCaseFixture = new BuyMembershipHappyCaseFixture(
+    api,
+    [memberAccount],
+    api.createPaidTermId(new BN(0))
+  )
+  await createMember.runner()
+  const memberId = createMember.getCreatedMembers()[0].toNumber()
+
+  // If we are too "aggressive" seeing
+  // 'ExtrinsicStatus:: 1010: Invalid Transaction: Transaction is outdated' errors
+  const numberOfChannelsPerRound = 100
+  const numberOfRoundsChannel = 5
+  const numberOfVideosPerRound = 100
+  const numberOfRoundsVideo = 100
+
+  const channelIds: number[] = []
+
+  // create mock channels
+  debug('Creating Channels')
+  for (let n = 0; n < numberOfRoundsChannel; n++) {
+    const createChannels = new CreateChannelsAsMemberFixture(api, memberId, numberOfChannelsPerRound)
+    await new FixtureRunner(createChannels).run()
+    createChannels.getCreatedChannels().forEach((id) => channelIds.push(id!.toNumber()))
+  }
+
+  // Create all videos in same channel
+  const channelId = channelIds[0]
+
+  // create mock videos
+  for (let n = 0; n < numberOfRoundsVideo; n++) {
+    debug('Creating Videos round', n)
+    const createVideos = new CreateVideosAsMemberFixture(api, memberId, channelId, numberOfVideosPerRound)
+    await new FixtureRunner(createVideos).run()
+  }
+
   debug('Done')
 }

+ 1 - 1
tests/network-tests/test-setup-new-chain.sh

@@ -63,7 +63,7 @@ fi
 echo "starting joystream-node container"
 # Start a chain with generated chain spec
 # Add "-l ws=trace,ws::handler=info" to get websocket trace logs
-CONTAINER_ID=`docker run -d -v ${DATA_PATH}:/data -p 9944:9944 ${NETWORK_ARG} joystream/node:${RUNTIME} \
+CONTAINER_ID=`docker run -d -v ${DATA_PATH}:/data -p 9944:9944 ${NETWORK_ARG} --name joystream-node joystream/node:${RUNTIME} \
   --validator --alice --unsafe-ws-external --rpc-cors=all -l runtime \
   --chain /data/chain-spec-raw.json`