Skip to content

Commit

Permalink
feat(packages/testcontainers): new WhaleApiContainer for whale-api …
Browse files Browse the repository at this point in the history
…testing (#2023)

#### What this PR does / why we need it:

Add `WhaleApiContainer` for `whale-api` testing. This is only possible
after #1790 was merged.

```ts
const network = await new Network().start()

const defid = await new NativeChainContainer()
  .withNetwork(network)
  .withPreconfiguredRegtestMasternode()
  .start()

const whale = await new WhaleApiContainer()
  .withNetwork(network)
  .withNativeChain(defid, network)
  .start()

// Get WhaleApiClient options and inject into WhaleApiClient
const api = new WhaleApiClient(whale.getWhaleApiClientOptions())

// Query the API as you would on Ocean.
const blocks = await api.blocks.list(1)
```

#### Which issue(s) does this PR fixes?:

Fixes #2006 

#### Additional comments?:

`PlaygroundApiContainer` will be implemented in a separate PR. Currently
it's blocked by #2022.
  • Loading branch information
fuxingloh authored Feb 3, 2023
1 parent d08f4f4 commit 8b7e028
Show file tree
Hide file tree
Showing 3 changed files with 117 additions and 6 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import { NativeChainContainer, StartedNativeChainContainer } from '@defichain/testcontainers'
import { Network } from 'testcontainers'
import {
StartedWhaleApiContainer,
WhaleApiContainer
} from '@defichain/testcontainers/dist/containers/AppContainer/WhaleApiContainer'
import { WhaleApiClient } from '@defichain/whale-api-client'

let defid: StartedNativeChainContainer
let whale: StartedWhaleApiContainer

beforeAll(async () => {
const network = await new Network().start()

defid = await new NativeChainContainer()
.withNetwork(network)
.withPreconfiguredRegtestMasternode()
.start()

whale = await new WhaleApiContainer()
.withNetwork(network)
.withNativeChain(defid, network)
.start()
})

afterAll(async () => {
await whale.stop()
await defid.stop()
})

it('should waitForIndexedBlockHeight(100)', async () => {
await defid.waitFor.walletCoinbaseMaturity()

const api = new WhaleApiClient(whale.getWhaleApiClientOptions())
await whale.waitForIndexedBlockHeight(100)

const blocks = await api.blocks.list(1)
expect(blocks[0].height).toBeGreaterThanOrEqual(100)
})
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
import { GenericContainer, StartedNetwork } from 'testcontainers'
import { AbstractStartedContainer } from 'testcontainers/dist/modules/abstract-started-container'
import { waitForCondition } from '../../utils'
import { StartedNativeChainContainer } from '../NativeChainContainer'
import fetch from 'cross-fetch'

// eslint-disable-next-line
// @ts-ignore because `package.json` will always be available in the root of pnpm package
import packageJson from '../../../package.json'

const WHALE_API_PORT = 3000

/**
* For local environment, `:latest` tag will be used as there isn't pipeline to automatically rebuild image locally.
*/
const WHALE_VERSION = packageJson.version === '0.0.0' ? 'latest' : packageJson.version

export class WhaleApiContainer extends GenericContainer {
constructor (image: string = `ghcr.io/jellyfishsdk/whale-api:${WHALE_VERSION}`) {
super(image)
this.withExposedPorts(WHALE_API_PORT).withStartupTimeout(120_000)
}

public withNativeChain (
container: StartedNativeChainContainer,
network: StartedNetwork
): this {
const ipAddress = container.getIpAddress(network.getName())
this.withEnvironment({
WHALE_DEFID_URL: `http://${container.rpcUser}:${container.rpcPassword}@${ipAddress}:19554/`,
WHALE_DATABASE_PROVIDER: 'level',
WHALE_DATABASE_LEVEL_LOCATION: '.level/index',
WHALE_NETWORK: 'regtest',
WHALE_VERSION: 'v0'
})
return this
}

public async start (): Promise<StartedWhaleApiContainer> {
return new StartedWhaleApiContainer(await super.start())
}
}

export class StartedWhaleApiContainer extends AbstractStartedContainer {
public getContainerPort (): number {
return WHALE_API_PORT
}

public getPort (): number {
return this.getMappedPort(this.getContainerPort())
}

getEndpoint (): string {
return `http://localhost:${this.getPort()}`
}

getWhaleApiClientOptions (): { url: string, version: 'v0', network: 'regtest' } {
return {
url: this.getEndpoint(),
version: 'v0',
network: 'regtest'
}
}

async waitForIndexedBlockHeight (height: number, timeout: number = 590000): Promise<void> {
const url = `${this.getEndpoint()}/v0/regtest/blocks?size=1`

return await waitForCondition(async () => {
const response = await fetch(url, {
method: 'GET'
})
const { data } = await response.json()
return data[0].height > height
}, timeout, 200, 'waitForIndexedBlockHeight')
}
}
8 changes: 2 additions & 6 deletions packages/testcontainers/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { MasterNodeKey as MNK, RegTestFoundationKeys } from '@defichain/jellyfish-network'
import { RegTestFoundationKeys } from '@defichain/jellyfish-network'

export { DockerOptions } from 'dockerode'
export { waitForCondition } from './utils'
Expand All @@ -8,11 +8,6 @@ export { waitForCondition } from './utils'
* @deprecated use `import { RegTestFoundationKeys } from '@defichain/jellyfish-network'`
*/
export const GenesisKeys = RegTestFoundationKeys
/**
* Moved to @defichain/jellyfish-network
* @deprecated use `import { MasterNodeKey } from '@defichain/jellyfish-network'`
*/
export type MasterNodeKey = MNK

export * from './containers/DeFiDContainer'
export * from './containers/MainNetContainer'
Expand All @@ -26,6 +21,7 @@ export * from './utils'
export * from './containers/RegTestContainer/LoanContainer'

export * from './containers/AppContainer/WhaleSanityContainer'
export * from './containers/AppContainer/WhaleApiContainer'

export * from './containers/NativeChainContainer'
export * from './containers/NativeChainRpc'
Expand Down

0 comments on commit 8b7e028

Please sign in to comment.