Skip to content

Commit 34856ea

Browse files
authored
added PersistentMNRegTestContainer (#173)
1 parent 8f3903d commit 34856ea

File tree

5 files changed

+131
-18
lines changed

5 files changed

+131
-18
lines changed
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
import { PersistentMNRegTestContainer } from '../../../src'
2+
import waitForExpect from 'wait-for-expect'
3+
4+
beforeEach(async () => {
5+
try {
6+
await new PersistentMNRegTestContainer().stop()
7+
} catch (ignored) {
8+
}
9+
})
10+
11+
afterEach(async () => {
12+
try {
13+
await new PersistentMNRegTestContainer().stop()
14+
} catch (ignored) {
15+
}
16+
})
17+
18+
it('should start and mint coins', async () => {
19+
const container = new PersistentMNRegTestContainer()
20+
await container.start()
21+
await container.waitForReady()
22+
23+
await waitForExpect(async () => {
24+
const info = await container.getMintingInfo()
25+
expect(info.blocks).toBeGreaterThan(3)
26+
})
27+
})
28+
29+
it('should always use the same persistent container', async () => {
30+
let container = new PersistentMNRegTestContainer()
31+
await container.start()
32+
await container.waitForReady()
33+
34+
await waitForExpect(async () => {
35+
const info = await container.getMintingInfo()
36+
expect(info.blocks).toBeGreaterThan(3)
37+
})
38+
39+
container = new PersistentMNRegTestContainer()
40+
await container.start()
41+
await container.waitForReady()
42+
43+
const info = await container.getMintingInfo()
44+
expect(info.blocks).toBeGreaterThan(3)
45+
})

packages/testcontainers/src/chains/container.ts

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -15,19 +15,6 @@ export interface StartOptions {
1515
password?: string
1616
}
1717

18-
const defaultStartOptions = {
19-
user: 'testcontainers-user',
20-
password: 'testcontainers-password'
21-
}
22-
23-
/**
24-
* Generate a name for a new docker container with network type and random number
25-
*/
26-
function generateName (network: Network): string {
27-
const rand = Math.floor(Math.random() * 10000000)
28-
return `${DeFiDContainer.PREFIX}-${network}-${rand}`
29-
}
30-
3118
/**
3219
* Clean up stale nodes are nodes that are running for 1 hour
3320
*/
@@ -94,6 +81,11 @@ export abstract class DeFiDContainer {
9481
public static readonly PREFIX = 'defichain-testcontainers-'
9582
public static readonly image = 'defi/defichain:1.6.3'
9683

84+
public static readonly DefaultStartOptions = {
85+
user: 'testcontainers-user',
86+
password: 'testcontainers-password'
87+
}
88+
9789
protected readonly docker: Dockerode
9890
protected readonly network: Network
9991

@@ -137,9 +129,9 @@ export abstract class DeFiDContainer {
137129
*/
138130
async start (startOptions: StartOptions = {}): Promise<void> {
139131
await pullImage(this.docker)
140-
this.startOptions = Object.assign(defaultStartOptions, startOptions)
132+
this.startOptions = Object.assign(DeFiDContainer.DefaultStartOptions, startOptions)
141133
this.container = await this.docker.createContainer({
142-
name: generateName(this.network),
134+
name: this.generateName(),
143135
Image: DeFiDContainer.image,
144136
Tty: true,
145137
Cmd: this.getCmd(this.startOptions),
@@ -150,6 +142,14 @@ export abstract class DeFiDContainer {
150142
await this.container.start()
151143
}
152144

145+
/**
146+
* Generate a name for a new docker container with network type and random number
147+
*/
148+
generateName (): string {
149+
const rand = Math.floor(Math.random() * 10000000)
150+
return `${DeFiDContainer.PREFIX}-${this.network}-${rand}`
151+
}
152+
153153
/**
154154
* Get host machine port
155155
*

packages/testcontainers/src/chains/reg_test_container/masternode.ts

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -49,8 +49,11 @@ export class MasterNodeRegTestContainer extends RegTestContainer {
4949
/**
5050
* This will automatically import the necessary private key for master to mint tokens
5151
*/
52-
async waitForReady (timeout: number = 15000): Promise<void> {
53-
await super.waitForReady(timeout)
52+
async start (startOptions: StartOptions = {}): Promise<void> {
53+
await super.start(startOptions)
54+
55+
// Wait for ready and setup for auto mint
56+
await super.waitForReady(25000)
5457

5558
// import keys for master node
5659
await this.call('importprivkey', [
@@ -79,7 +82,6 @@ export class MasterNodeRegTestContainer extends RegTestContainer {
7982
// restart and wait for ready
8083
await this.container?.stop()
8184
await this.container?.start()
82-
await super.waitForReady(timeout)
8385
}
8486

8587
/**
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
import { MasterNodeRegTestContainer } from './masternode'
2+
import { DeFiDContainer, StartOptions } from '../container'
3+
import Dockerode, { ContainerInfo } from 'dockerode'
4+
5+
async function getContainerInfoByName (docker: Dockerode, name: string): Promise<ContainerInfo | undefined> {
6+
return await new Promise((resolve, reject) => {
7+
const opts = { limit: 1, filters: `{"name": ["${name}"]}` }
8+
docker.listContainers(opts, function (err, containers) {
9+
if (err === null && containers !== undefined) {
10+
resolve(containers[0])
11+
} else {
12+
reject(err)
13+
}
14+
})
15+
})
16+
}
17+
18+
/**
19+
* PersistentMNRegTestContainer container is a RegTest container with MasterNode minting preconfigured.
20+
* The container configuration is persistent and can be used consistently.
21+
* If you do not stop the container, the same container can be used for all tests.
22+
* However, you must be cognizant of race conditions.
23+
*
24+
* This container should not be used for finished work, it merely a dev tool to speed up test-driven development.
25+
* Once you are done with your dev work, you should swap this out for MasterNodeRegTestContainer.
26+
*/
27+
export class PersistentMNRegTestContainer extends MasterNodeRegTestContainer {
28+
/**
29+
* Init the required container instances for start/stop operation
30+
*/
31+
async init (): Promise<void> {
32+
const info = await getContainerInfoByName(this.docker, this.generateName())
33+
this.container = info?.Id !== undefined ? this.docker.getContainer(info.Id) : undefined
34+
}
35+
36+
/**
37+
* This will only start a persistent container if it's not yet already started.
38+
* @see {generateName()} for the name of container
39+
*/
40+
async start (startOptions: StartOptions = {}): Promise<void> {
41+
this.startOptions = Object.assign(DeFiDContainer.DefaultStartOptions, startOptions)
42+
43+
try {
44+
await this.init()
45+
this.requireContainer()
46+
await this.waitForReady(3000)
47+
} catch (e) {
48+
// Attempt clean up before starting
49+
await super.stop()
50+
await super.start(startOptions)
51+
}
52+
}
53+
54+
/**
55+
* @return {string} name of persistent container that is always consistent.
56+
*/
57+
generateName (): string {
58+
return `${DeFiDContainer.PREFIX}-${this.network}-persistent`
59+
}
60+
61+
async stop (): Promise<void> {
62+
await this.init()
63+
await super.stop()
64+
}
65+
}

packages/testcontainers/src/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,4 @@ export * from './chains/main_net_container'
77
export * from './chains/test_net_container'
88
export * from './chains/reg_test_container/index'
99
export * from './chains/reg_test_container/masternode'
10+
export * from './chains/reg_test_container/persistent'

0 commit comments

Comments
 (0)