@@ -6,25 +6,59 @@ import {
66 Wait ,
77} from 'testcontainers'
88import * as Instance from '../Instance.js'
9+ import { execa } from '../processes/execa.js'
10+
11+ export function command ( parameters : tempo . Parameters ) : string [ ] {
12+ const { faucet, port } = parameters
13+ const dataDir = path . join ( os . tmpdir ( ) , '.prool' , `tempo.${ port } ` )
14+ return [
15+ 'node' ,
16+ `--authrpc.port=${ port ! + 30 } ` ,
17+ `--datadir=${ dataDir } ` ,
18+ '--dev' ,
19+ `--dev.block-time=${ parameters ?. blockTime ?? '50ms' } ` ,
20+ '--engine.disable-precompile-cache' ,
21+ '--engine.legacy-state-root' ,
22+ '--faucet.address' ,
23+ ...( faucet ?. addresses ?? [
24+ '0x20c0000000000000000000000000000000000000' ,
25+ '0x20c0000000000000000000000000000000000001' ,
26+ '0x20c0000000000000000000000000000000000002' ,
27+ '0x20c0000000000000000000000000000000000003' ,
28+ ] ) ,
29+ `--faucet.amount=${ faucet ?. amount ?? '1000000000000' } ` ,
30+ '--faucet.enabled' ,
31+ `--faucet.private-key=${ faucet ?. privateKey ?? '0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80' } ` ,
32+ '--http' ,
33+ '--http.addr=0.0.0.0' ,
34+ '--http.api=all' ,
35+ '--http.corsdomain=*' ,
36+ `--http.port=${ port ! } ` ,
37+ `--port=${ port ! + 10 } ` ,
38+ '--txpool.basefee-max-count=10000000000000' ,
39+ '--txpool.basefee-max-size=10000' ,
40+ '--txpool.max-account-slots=500000' ,
41+ '--txpool.pending-max-count=10000000000000' ,
42+ '--txpool.pending-max-size=10000' ,
43+ '--txpool.queued-max-count=10000000000000' ,
44+ '--txpool.queued-max-size=10000' ,
45+ `--ws.port=${ port ! + 20 } ` ,
46+ ]
47+ }
948
1049/**
1150 * Defines a Tempo instance.
1251 *
1352 * @example
1453 * ```ts
15- * const instance = tempo({ port: 8545 })
54+ * const instance = Instance. tempo({ port: 8545 })
1655 * await instance.start()
1756 * // ...
1857 * await instance.stop()
1958 * ```
2059 */
2160export const tempo = Instance . define ( ( parameters ?: tempo . Parameters ) => {
22- const {
23- containerName = `tempo.${ crypto . randomUUID ( ) } ` ,
24- image = 'ghcr.io/tempoxyz/tempo:latest' ,
25- log : log_ ,
26- ...args
27- } = parameters || { }
61+ const { binary = 'tempo' , log : log_ , ...args } = parameters || { }
2862
2963 const log = ( ( ) => {
3064 try {
@@ -36,115 +70,60 @@ export const tempo = Instance.define((parameters?: tempo.Parameters) => {
3670 const RUST_LOG = log && typeof log !== 'boolean' ? log : ''
3771
3872 const name = 'tempo'
39- let container : StartedTestContainer | undefined
73+ const process = execa ( { name } )
4074
4175 return {
4276 _internal : {
4377 args,
78+ get process ( ) {
79+ return process . _internal . process
80+ } ,
4481 } ,
4582 host : args . host ?? 'localhost' ,
4683 name,
4784 port : args . port ?? 8545 ,
48- async start ( { port = args . port } , { emitter } ) {
49- const promise = Promise . withResolvers < void > ( )
50-
51- const dataDir = path . join ( os . tmpdir ( ) , '.prool' , containerName )
52- const c = new GenericContainer ( image )
53- . withPlatform ( 'linux/x86_64' )
54- . withNetworkMode ( 'host' )
55- . withExtraHosts ( [
56- { host : 'host.docker.internal' , ipAddress : 'host-gateway' } ,
57- { host : 'localhost' , ipAddress : 'host-gateway' } ,
58- ] )
59- . withName ( containerName )
60- . withEnvironment ( { RUST_LOG } )
61- . withCommand ( [
62- 'node' ,
63- `--authrpc.port=${ port ! + 30 } ` ,
64- `--datadir=${ dataDir } ` ,
65- '--dev' ,
66- `--dev.block-time=${ parameters ?. blockTime ?? '50ms' } ` ,
67- '--engine.disable-precompile-cache' ,
68- '--engine.legacy-state-root' ,
69- '--faucet.address' ,
70- ...( parameters ?. faucet ?. addresses ?? [
71- '0x20c0000000000000000000000000000000000000' ,
72- '0x20c0000000000000000000000000000000000001' ,
73- '0x20c0000000000000000000000000000000000002' ,
74- '0x20c0000000000000000000000000000000000003' ,
75- ] ) ,
76- `--faucet.amount=${ parameters ?. faucet ?. amount ?? '1000000000000' } ` ,
77- '--faucet.enabled' ,
78- `--faucet.private-key=${ parameters ?. faucet ?. privateKey ?? '0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80' } ` ,
79- '--http' ,
80- '--http.addr=0.0.0.0' ,
81- '--http.api=all' ,
82- '--http.corsdomain=*' ,
83- `--http.port=${ port ! } ` ,
84- `--port=${ port ! + 10 } ` ,
85- '--txpool.basefee-max-count=10000000000000' ,
86- '--txpool.basefee-max-size=10000' ,
87- '--txpool.max-account-slots=500000' ,
88- '--txpool.pending-max-count=10000000000000' ,
89- '--txpool.pending-max-size=10000' ,
90- '--txpool.queued-max-count=10000000000000' ,
91- '--txpool.queued-max-size=10000' ,
92- `--ws.port=${ port ! + 20 } ` ,
93- ] )
94- . withWaitStrategy ( Wait . forLogMessage ( / R P C H T T P s e r v e r s t a r t e d / ) )
95- . withLogConsumer ( ( stream ) => {
96- stream . on ( 'data' , ( data ) => {
97- const message = data . toString ( )
98- emitter . emit ( 'message' , message )
99- emitter . emit ( 'stdout' , message )
100- if ( log ) console . log ( message )
101- if ( message . includes ( 'shutting down' ) )
102- promise . reject ( new Error ( `Failed to start: ${ message } ` ) )
103- } )
104- stream . on ( 'error' , ( error ) => {
105- if ( log ) console . error ( error . message )
106- emitter . emit ( 'message' , error . message )
107- emitter . emit ( 'stderr' , error . message )
108- promise . reject ( new Error ( `Failed to start: ${ error . message } ` ) )
109- } )
110- } )
111- . withStartupTimeout ( 120_000 )
112-
113- c . start ( )
114- . then ( ( c ) => {
115- container = c
116- promise . resolve ( )
117- } )
118- . catch ( promise . reject )
119-
120- return promise . promise
85+ async start ( { port = args . port } , options ) {
86+ return await process . start (
87+ ( $ ) =>
88+ $ ( {
89+ env : {
90+ RUST_LOG ,
91+ } ,
92+ } ) `${ [ binary , ...command ( { ...parameters , port } ) ] } ` ,
93+ {
94+ ...options ,
95+ // Resolve when the process is listening via "RPC HTTP server started" message.
96+ resolver ( { process, reject, resolve } ) {
97+ process . stdout . on ( 'data' , ( data ) => {
98+ const message = data . toString ( )
99+ if ( log ) console . log ( message )
100+ if ( message . includes ( 'RPC HTTP server started' ) ) resolve ( )
101+ if ( message . includes ( 'shutting down' ) ) reject ( 'shutting down' )
102+ } )
103+ process . stderr . on ( 'data' , ( data ) => {
104+ const message = data . toString ( )
105+ if ( log ) console . error ( message )
106+ } )
107+ } ,
108+ } ,
109+ )
121110 } ,
122111 async stop ( ) {
123- if ( ! container ) return
124- await container . stop ( )
125- container = undefined
112+ await process . stop ( )
126113 } ,
127114 }
128115} )
129116
130117export declare namespace tempo {
131118 export type Parameters = {
132119 /**
133- * Interval between blocks.
134- */
135- blockTime ?: string | undefined
136- /**
137- * Name of the container.
138- */
139- containerName ?: string | undefined
140- /**
141- * Chain this node is running.
120+ * Path or alias to the Tempo binary.
142121 */
143- chain ?: string | undefined
122+ binary ?: string | undefined
144123 /**
145- * Docker image to use .
124+ * Interval between blocks .
146125 */
147- image ?: string | undefined
126+ blockTime ?: string | undefined
148127 /**
149128 * Faucet options.
150129 */
@@ -187,3 +166,113 @@ export declare namespace tempo {
187166 port ?: number | undefined
188167 }
189168}
169+
170+ /**
171+ * Defines a Tempo instance.
172+ *
173+ * @example
174+ * ```ts
175+ * const instance = Instance.tempoDocker({ port: 8545 })
176+ * await instance.start()
177+ * // ...
178+ * await instance.stop()
179+ * ```
180+ */
181+ export const tempoDocker = Instance . define (
182+ ( parameters ?: tempoDocker . Parameters ) => {
183+ const {
184+ containerName = `tempo.${ crypto . randomUUID ( ) } ` ,
185+ image = 'ghcr.io/tempoxyz/tempo:latest' ,
186+ log : log_ ,
187+ ...args
188+ } = parameters || { }
189+
190+ const log = ( ( ) => {
191+ try {
192+ return JSON . parse ( log_ as string )
193+ } catch {
194+ return log_
195+ }
196+ } ) ( )
197+ const RUST_LOG = log && typeof log !== 'boolean' ? log : ''
198+
199+ const name = 'tempo'
200+ let container : StartedTestContainer | undefined
201+
202+ return {
203+ _internal : {
204+ args,
205+ } ,
206+ host : args . host ?? 'localhost' ,
207+ name,
208+ port : args . port ?? 8545 ,
209+ async start ( { port = args . port } , { emitter } ) {
210+ const promise = Promise . withResolvers < void > ( )
211+
212+ const c = new GenericContainer ( image )
213+ . withPlatform ( 'linux/x86_64' )
214+ . withNetworkMode ( 'host' )
215+ . withExtraHosts ( [
216+ { host : 'host.docker.internal' , ipAddress : 'host-gateway' } ,
217+ { host : 'localhost' , ipAddress : 'host-gateway' } ,
218+ ] )
219+ . withName ( containerName )
220+ . withEnvironment ( { RUST_LOG } )
221+ . withCommand ( command ( { ...parameters , port } ) )
222+ . withWaitStrategy ( Wait . forLogMessage ( / R P C H T T P s e r v e r s t a r t e d / ) )
223+ . withLogConsumer ( ( stream ) => {
224+ stream . on ( 'data' , ( data ) => {
225+ const message = data . toString ( )
226+ emitter . emit ( 'message' , message )
227+ emitter . emit ( 'stdout' , message )
228+ if ( log ) console . log ( message )
229+ if ( message . includes ( 'shutting down' ) )
230+ promise . reject ( new Error ( `Failed to start: ${ message } ` ) )
231+ } )
232+ stream . on ( 'error' , ( error ) => {
233+ if ( log ) console . error ( error . message )
234+ emitter . emit ( 'message' , error . message )
235+ emitter . emit ( 'stderr' , error . message )
236+ promise . reject ( new Error ( `Failed to start: ${ error . message } ` ) )
237+ } )
238+ } )
239+ . withStartupTimeout ( 10_000 )
240+
241+ c . start ( )
242+ . then ( ( c ) => {
243+ container = c
244+ promise . resolve ( )
245+ } )
246+ . catch ( promise . reject )
247+
248+ return promise . promise
249+ } ,
250+ async stop ( ) {
251+ if ( ! container ) return
252+ await container . stop ( )
253+ container = undefined
254+ } ,
255+ }
256+ } ,
257+ )
258+
259+ export declare namespace tempoDocker {
260+ export type Parameters = Omit < tempo . Parameters , 'binary' > & {
261+ /**
262+ * Name of the container.
263+ */
264+ containerName ?: string | undefined
265+ /**
266+ * Docker image to use.
267+ */
268+ image ?: string | undefined
269+ /**
270+ * Host the server will listen on.
271+ */
272+ host ?: string | undefined
273+ /**
274+ * Port the server will listen on.
275+ */
276+ port ?: number | undefined
277+ }
278+ }
0 commit comments