diff --git a/libs/gl-sdk-napi/README.md b/libs/gl-sdk-napi/README.md index 7066a631..6a6bf5e6 100644 --- a/libs/gl-sdk-napi/README.md +++ b/libs/gl-sdk-napi/README.md @@ -260,7 +260,7 @@ npm test -- --verbose tests/node.spec.ts The global setup script `./tests/jest.globalSetup.ts` automatically: - Starts `test_setup.py`, spinning up bitcoind, the Greenlight scheduler, and an LSPS2-compatible LSP node - Waits for all services to be ready -- Injects the required environment variables (`GL_SCHEDULER_GRPC_URI`, `GL_CA_CRT`, `GL_NOBODY_CRT`, `GL_NOBODY_KEY`, `LSP_RPC_SOCKET`, `LSP_NODE_ID`, `LSP_PROMISE_SECRET`, `GL_FUND_SERVER`) +- Injects the required environment variables (`GL_SCHEDULER_GRPC_URI`, `GL_CA_CRT`, `GL_NOBODY_CRT`, `GL_NOBODY_KEY`, `LSP_RPC_SOCKET`, `LSP_NODE_ID`, `TEST_SETUP_SERVER_URL`) - Shuts everything down after all tests complete ### Test Helpers diff --git a/libs/gl-sdk-napi/tests/basic.spec.ts b/libs/gl-sdk-napi/tests/basic.spec.ts index e0253046..35783406 100644 --- a/libs/gl-sdk-napi/tests/basic.spec.ts +++ b/libs/gl-sdk-napi/tests/basic.spec.ts @@ -3,21 +3,19 @@ import * as bip39 from 'bip39'; import { Credentials, Scheduler, Signer, Node } from '../index.js'; describe('Greenlight node', () => { - let node: Node; - let credentials: Credentials; - it('can be setup', async () => { + const scheduler = new Scheduler('regtest'); const rand: Buffer = crypto.randomBytes(16); const MNEMONIC: string = bip39.entropyToMnemonic(rand.toString("hex")); - const scheduler = new Scheduler('regtest'); const signer = new Signer(MNEMONIC); + const handle = await signer.start(); const nodeId = signer.nodeId(); - expect(Buffer.isBuffer(nodeId)).toBe(true); expect(nodeId.length).toBeGreaterThan(0); - - credentials = await scheduler.register(signer); - node = new Node(credentials); + const credentials = await scheduler.register(signer); + const node = new Node(credentials); expect(node).toBeTruthy(); + handle.stop(); + await node.stop(); }); }); diff --git a/libs/gl-sdk-napi/tests/eventstream.spec.ts b/libs/gl-sdk-napi/tests/eventstream.spec.ts index 63b7276d..b41fd476 100644 --- a/libs/gl-sdk-napi/tests/eventstream.spec.ts +++ b/libs/gl-sdk-napi/tests/eventstream.spec.ts @@ -1,7 +1,7 @@ import * as crypto from 'crypto'; import * as bip39 from 'bip39'; -import { Credentials, Scheduler, Signer, Node, NodeEventStream, NodeEvent, InvoicePaidEvent } from '../index.js'; -import { startLspServer, stopLspServer, fundNode } from './test.helper.js'; +import { Credentials, Scheduler, Signer, Node, NodeEventStream, NodeEvent, InvoicePaidEvent, Handle } from '../index.js'; +import { fundWallet, getGLNode } from './test.helper.js'; describe('NodeEvent (type contract)', () => { it('NodeEvent and InvoicePaidEvent are assignable from NAPI-generated types', () => { @@ -28,16 +28,12 @@ describe('NodeEvent (type contract)', () => { // ============================================================================ describe('NodeEventStream (integration)', () => { - let credentials: Credentials; + let scheduler: Scheduler = new Scheduler('regtest'); let node: Node; + let handle: Handle; beforeAll(async () => { - const rand: Buffer = crypto.randomBytes(16); - const MNEMONIC: string = bip39.entropyToMnemonic(rand.toString("hex")); - const scheduler = new Scheduler('regtest'); - const signer = new Signer(MNEMONIC); - credentials = await scheduler.register(signer); - node = new Node(credentials); + ({node, handle } = await getGLNode(scheduler, false) as { node: Node; handle: Handle }); try { const probe = await node.streamNodeEvents(); void probe; @@ -49,6 +45,7 @@ describe('NodeEventStream (integration)', () => { afterAll(async () => { if (node) { + handle.stop(); await node.stop(); } }); @@ -94,25 +91,24 @@ describe('NodeEventStream (integration)', () => { }); it('next returns null after the node is stopped', async () => { - const stream: NodeEventStream = await node.streamNodeEvents(); - await node.stop(); - + const mnemonic2 = bip39.entropyToMnemonic(crypto.randomBytes(16).toString('hex')); + const signer2 = new Signer(mnemonic2); + const handle2 = await signer2.start(); + const credentials2 = await scheduler.register(signer2); + let node2 = new Node(credentials2); + const stream: NodeEventStream = await node2.streamNodeEvents(); + await node2.stop(); const result = await stream.next(); expect(result).toBeNull(); - node = new Node(credentials); + node2 = new Node(credentials2); + handle2.stop() + await node2.stop(); }); it.skip('receives real invoice_paid event', async () => { - await startLspServer(); - await fundNode(node, 0.5); + await fundWallet(node, 500_000_000); + const { node: node2, handle: handle2 } = await getGLNode(scheduler, true) as { node: Node; handle: Handle }; const stream: NodeEventStream = await node.streamNodeEvents(); - const rand2: Buffer = crypto.randomBytes(16); - const MNEMONIC2: string = bip39.entropyToMnemonic(rand2.toString("hex")); - const scheduler2 = new Scheduler('regtest'); - const signer2 = new Signer(MNEMONIC2); - const credentials2 = await scheduler2.register(signer2); - const node2 = new Node(credentials2); - const label = `jest-${Date.now()}`; const receiveRes = await node.receive(label, 'jest event stream test', 1_000); const sendResponse = await node2.send(receiveRes.bolt11); @@ -146,7 +142,8 @@ describe('NodeEventStream (integration)', () => { expect(p.label).toBe(label); expect(typeof p.amountMsat).toBe('number'); expect(p.amountMsat).toBeGreaterThan(0); - await stopLspServer(); + handle2.stop(); + await node2.stop(); }, 15_000 // extended timeout for payment round-trip ); diff --git a/libs/gl-sdk-napi/tests/jest.globalSetup.ts b/libs/gl-sdk-napi/tests/jest.globalSetup.ts index 9fb9cf94..600c6e88 100644 --- a/libs/gl-sdk-napi/tests/jest.globalSetup.ts +++ b/libs/gl-sdk-napi/tests/jest.globalSetup.ts @@ -24,8 +24,7 @@ async function waitForEnvFile(): Promise> { 'GL_NOBODY_KEY', 'LSP_RPC_SOCKET', 'LSP_NODE_ID', - 'LSP_PROMISE_SECRET', - 'GL_FUND_SERVER', + 'TEST_SETUP_SERVER_URL' ]; const deadline = Date.now() + SERVER_READY_TIMEOUT_MS; diff --git a/libs/gl-sdk-napi/tests/jest.globalTeardown.ts b/libs/gl-sdk-napi/tests/jest.globalTeardown.ts index 3e210fba..ae221d8c 100644 --- a/libs/gl-sdk-napi/tests/jest.globalTeardown.ts +++ b/libs/gl-sdk-napi/tests/jest.globalTeardown.ts @@ -3,6 +3,7 @@ import * as path from 'path'; const GLTESTS_DIR = '/tmp/gltests'; const PID_FILE = path.join(GLTESTS_DIR, 'gltestserver.pid'); +const ENV_FILE = path.join(GLTESTS_DIR, '.env'); export default async function globalTeardown(): Promise { console.log(`\nšŸ›‘ Stopping test_setup...`); @@ -29,9 +30,23 @@ export default async function globalTeardown(): Promise { if (fs.existsSync(PID_FILE)) { fs.unlinkSync(PID_FILE); } + + // Clean up TMP_DIR — derived from GL_CERT_PATH in the .env file + if (fs.existsSync(ENV_FILE)) { + const envContents = fs.readFileSync(ENV_FILE, 'utf8'); + const certPathMatch = envContents.match(/^GL_CERT_PATH=(.+)$/m); + if (certPathMatch) { + const tmpDir = path.dirname(certPathMatch[1].trim()); + if (tmpDir && tmpDir !== '/' && tmpDir.includes('gl-lsp-setup-')) { + fs.rmSync(tmpDir, { recursive: true, force: true }); + console.log(` Removed TMP_DIR: ${tmpDir}`); + } + } + fs.unlinkSync(ENV_FILE); + } } catch { // Ignore all teardown errors } console.log(`āœ… test_setup stopped.`); -} +} \ No newline at end of file diff --git a/libs/gl-sdk-napi/tests/node.spec.ts b/libs/gl-sdk-napi/tests/node.spec.ts index 9decde1b..d5df58c3 100644 --- a/libs/gl-sdk-napi/tests/node.spec.ts +++ b/libs/gl-sdk-napi/tests/node.spec.ts @@ -1,25 +1,22 @@ -import * as crypto from 'crypto'; -import * as bip39 from 'bip39'; -import { Credentials, Scheduler, Signer, Node } from '../index.js'; -import { fundWallet, lspInfo } from './test.helper'; +import { Handle, Node, Scheduler } from '../index.js'; +import { getGLNode, fundWallet, getLspInvoice } from './test.helper'; describe('Node', () => { + let scheduler: Scheduler = new Scheduler('regtest'); + let glNodes: Array<{ node: Node; handle: Handle }> = []; let node: Node; - let credentials: Credentials; beforeEach(async () => { - const rand: Buffer = crypto.randomBytes(16); - const MNEMONIC: string = bip39.entropyToMnemonic(rand.toString("hex")); - const scheduler = new Scheduler('regtest'); - const signer = new Signer(MNEMONIC); - credentials = await scheduler.register(signer); - node = new Node(credentials); + glNodes.push(await getGLNode(scheduler, false)); + node = glNodes[0].node; }); afterEach(async () => { - if (node) { - await node.stop(); + for (const { node: n, handle: h } of glNodes) { + h.stop(); + await n.stop(); } + glNodes = []; }); it('can be constructed from credentials', async () => { @@ -111,45 +108,33 @@ describe('Node', () => { }); describe('calls onchainSend', () => { - it.skip('can attempt to send specific amount on-chain', async () => { + it('can send specific amount on-chain', async () => { await fundWallet(node, 500_000_000); - const rand2: Buffer = crypto.randomBytes(16); - const MNEMONIC2: string = bip39.entropyToMnemonic(rand2.toString("hex")); - const scheduler2 = new Scheduler('regtest'); - const signer2 = new Signer(MNEMONIC2); - const credentials2 = await scheduler2.register(signer2); - const node2 = new Node(credentials2); - const destAddress = (await node2.onchainReceive()).bech32; + const extraGLNode = await getGLNode(scheduler, true) as { node: Node; handle: Handle }; + glNodes.push(extraGLNode); + const destAddress = (await extraGLNode.node.onchainReceive()).bech32; const response = await node.onchainSend(destAddress, '10000sat'); expect(response).toBeTruthy(); }); - it.skip('can attempt to send all funds on-chain', async () => { + it('can attempt to send all funds on-chain', async () => { await fundWallet(node, 500_000_000); - const rand2: Buffer = crypto.randomBytes(16); - const MNEMONIC2: string = bip39.entropyToMnemonic(rand2.toString("hex")); - const scheduler2 = new Scheduler('regtest'); - const signer2 = new Signer(MNEMONIC2); - const credentials2 = await scheduler2.register(signer2); - const node2 = new Node(credentials2); - const destAddress = (await node2.onchainReceive()).bech32; + const extraGLNode = await getGLNode(scheduler, true) as { node: Node; handle: Handle }; + glNodes.push(extraGLNode); + const destAddress = (await extraGLNode.node.onchainReceive()).bech32; const response = await node.onchainSend(destAddress, 'all'); expect(response).toBeTruthy(); }); }); describe('calls receive', () => { - it.skip('can create invoice with amount', async () => { - const { rpcSocket, nodeId } = lspInfo(); - // Connect to the LSP as a peer - console.log('LSP Node Info:', rpcSocket, nodeId); - // await node.connectPeer(lspNodeInfo.id, lspNodeInfo.bindings[0].address, lspNodeInfo.bindings[0].port); - // await new Promise(resolve => setTimeout(resolve, 2000)); - + it('can create invoice with amount', async () => { + const extraGLNode = await getGLNode(scheduler, true) as { node: Node; handle: Handle }; + glNodes.push(extraGLNode); const label = `test-${Date.now()}`; const description = 'Test payment'; const amountMsat = 100000; - const response = await node.receive(label, description, amountMsat); + const response = await extraGLNode.node.receive(label, description, amountMsat); expect(response).toBeTruthy(); expect(typeof response.bolt11).toBe('string'); expect(response.bolt11.length).toBeGreaterThan(0); @@ -159,44 +144,17 @@ describe('Node', () => { describe('calls send', () => { it.skip('can attempt to send payment to valid invoice', async () => { - const { rpcSocket, nodeId } = lspInfo(); await fundWallet(node, 500_000_000); - const rand2: Buffer = crypto.randomBytes(16); - const MNEMONIC2: string = bip39.entropyToMnemonic(rand2.toString("hex")); - const scheduler2 = new Scheduler('regtest'); - const signer2 = new Signer(MNEMONIC2); - const credentials2 = await scheduler2.register(signer2); - const node2 = new Node(credentials2); - const receiveRes = await node2.receive(`test-${Date.now()}`, 'Test payment', 100000); - const sendResponse = await node.send(receiveRes.bolt11); + const bolt11 = await getLspInvoice(100_000); + const sendResponse = await node.send(bolt11); expect(sendResponse).toBeTruthy(); }); it.skip('can send with explicit amount for zero-amount invoice', async () => { - const { rpcSocket, nodeId } = lspInfo(); await fundWallet(node, 500_000_000); - const rand2: Buffer = crypto.randomBytes(16); - const MNEMONIC2: string = bip39.entropyToMnemonic(rand2.toString("hex")); - const scheduler2 = new Scheduler('regtest'); - const signer2 = new Signer(MNEMONIC2); - const credentials2 = await scheduler2.register(signer2); - const node2 = new Node(credentials2); - const receiveRes = await node2.receive(`test-${Date.now()}`, 'Test payment'); - const sendResponse = await node.send(receiveRes.bolt11); + const bolt11 = await getLspInvoice(0); + const sendResponse = await node.send(bolt11, 200_000); expect(sendResponse).toBeTruthy(); }); }); - - describe('calls stop', () => { - it('can stop the node', async () => { - const rand: Buffer = crypto.randomBytes(16); - const MNEMONIC: string = bip39.entropyToMnemonic(rand.toString("hex")); - const testScheduler = new Scheduler('regtest'); - const testSigner = new Signer(MNEMONIC); - const testCredentials = await testScheduler.register(testSigner); - const testNode = new Node(testCredentials); - - await expect(testNode.stop()).resolves.not.toThrow(); - }); - }); }); diff --git a/libs/gl-sdk-napi/tests/test.helper.ts b/libs/gl-sdk-napi/tests/test.helper.ts index a9e3c66e..0f7f9ebb 100644 --- a/libs/gl-sdk-napi/tests/test.helper.ts +++ b/libs/gl-sdk-napi/tests/test.helper.ts @@ -1,14 +1,19 @@ -import { Node } from '../index.js'; +import * as fs from 'fs'; +import * as crypto from 'crypto'; +import * as bip39 from 'bip39'; +import { Credentials, Scheduler, Signer, Node, Handle } from '../index.js'; export const lspInfo = () => ({ rpcSocket: process.env.LSP_RPC_SOCKET!, nodeId: process.env.LSP_NODE_ID!, - promiseSecret: process.env.LSP_PROMISE_SECRET!, }); export async function fundWallet(node: Node, amountSats = 100_000_000): Promise { + const testSetupServerUrl = process.env.TEST_SETUP_SERVER_URL!; + if (!testSetupServerUrl) throw new Error('TEST_SETUP_SERVER_URL not set'); + const address = (await node.onchainReceive()).bech32; - const res = await fetch(process.env.GL_FUND_SERVER!, { + const res = await fetch(`${testSetupServerUrl}/fund-wallet`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ address, amount: amountSats }), @@ -29,3 +34,39 @@ export async function fundWallet(node: Node, amountSats = 100_000_000): Promise< throw new Error('fundNode timed out waiting for node to detect funds'); } + +export async function getGLNode(scheduler: Scheduler, connectToLSP: boolean = true): Promise<{ node: Node; handle: Handle }> { + const mnemonic = bip39.entropyToMnemonic(crypto.randomBytes(16).toString('hex')); + const secret = bip39.mnemonicToSeedSync(mnemonic); + const signer = new Signer(mnemonic); + let credentials: Credentials; + if (connectToLSP) { + const testSetupServerUrl = process.env.TEST_SETUP_SERVER_URL!; + if (!testSetupServerUrl) throw new Error('TEST_SETUP_SERVER_URL not set'); + + const res = await fetch(`${testSetupServerUrl}/connect-to-lsp`, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ secret: secret.toString('hex') }), + }); + if (!res.ok) throw new Error(`Failed to connect node to LSP: ${await res.text()}`); + const { creds_path } = await res.json(); + credentials = await Credentials.load(fs.readFileSync(creds_path)); + } else { + credentials = await scheduler.register(signer); + } + return { node: new Node(credentials), handle: await signer.start() }; +} + +export async function getLspInvoice(amountMsat: number = 0): Promise { + const testSetupServerUrl = process.env.TEST_SETUP_SERVER_URL!; + if (!testSetupServerUrl) throw new Error('TEST_SETUP_SERVER_URL not set'); + const res = await fetch(`${testSetupServerUrl}/lsp-invoice`, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ amount_msat: amountMsat, label: `test-${Date.now()}`, description: 'Test payment' }), + }); + if (!res.ok) throw new Error(`Failed to get LSP invoice: ${await res.text()}`); + const { bolt11 } = await res.json(); + return bolt11; +} \ No newline at end of file diff --git a/libs/gl-sdk-napi/tests/test_setup.py b/libs/gl-sdk-napi/tests/test_setup.py index 2814ff97..3902a103 100644 --- a/libs/gl-sdk-napi/tests/test_setup.py +++ b/libs/gl-sdk-napi/tests/test_setup.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -import os, signal, sys, tempfile, logging, json, threading +import os, signal, sys, tempfile, logging, json, threading, time from pathlib import Path from http.server import ThreadingHTTPServer, BaseHTTPRequestHandler from concurrent.futures import ThreadPoolExecutor @@ -7,9 +7,11 @@ from gltesting import certs from gltesting.scheduler import Scheduler from gltesting.network import GlNodeFactory +from gltesting.clients import Clients from gltesting import get_plugins_dir -from pyln.testing.utils import BitcoinD +from pyln.testing.utils import BitcoinD, wait_for from pyln.testing.db import SqliteDbProvider +from pyln import grpc as clnpb logging.basicConfig(level=logging.WARNING) @@ -27,8 +29,8 @@ def main(): certs.genca('/services') certs.gencert('/services/scheduler') certs.genca('/users') - nobody_id = certs.gencert('/users/nobody') - scheduler_id = certs.gencert('/services/scheduler') + nobody_id = certs.gencert('/users/nobody') + scheduler_id = certs.gencert('/services/scheduler') os.environ['GL_NOBODY_CRT'] = str(nobody_id.cert_chain_path) os.environ['GL_NOBODY_KEY'] = str(nobody_id.private_key_path) @@ -46,10 +48,10 @@ def main(): os.environ['GL_SCHEDULER_GRPC_URI'] = s.grpc_addr s.start() - # 4. LSP node via GlNodeFactory + # 4. LSP node nodes_dir = TMP_DIR / 'nodes' nodes_dir.mkdir() - executor = ThreadPoolExecutor(max_workers=8) + executor = ThreadPoolExecutor(max_workers=8) db_provider = SqliteDbProvider(str(nodes_dir)) from pyln.testing.utils import LightningNode @@ -75,54 +77,108 @@ def main(): 'important-plugin': str(policy_plugin), }) - # 5. HTTP funding server - class FundHandler(BaseHTTPRequestHandler): + lsp_node_id = lsp.info['id'] + lsp_node_host = '127.0.0.1' + lsp_node_port = lsp.daemon.port + lsp.fundwallet(sats=2 * 10**6) + + # 5. Clients (for /new-lsp-node endpoint) + clients_dir = TMP_DIR / 'clients' + clients_dir.mkdir() + clients = Clients( + directory=clients_dir, + scheduler=s, + nobody_id=nobody_id, + ) + + # 6. HTTP server + class Handler(BaseHTTPRequestHandler): def log_message(self, *args): pass + def _send_json(self, status: int, payload: dict): + body = json.dumps(payload).encode() + self.send_response(status) + self.send_header('Content-Type', 'application/json') + self.send_header('Content-Length', str(len(body))) + self.end_headers() + self.wfile.write(body) + self.wfile.flush() + + def _read_body(self) -> dict: + length = int(self.headers.get('Content-Length', 0)) + return json.loads(self.rfile.read(length)) if length else {} + def do_POST(self): try: - body = json.loads(self.rfile.read(int(self.headers['Content-Length']))) - address = body['address'] - amount = body.get('amount', 100_000_000) - - # Send to address (convert sats to BTC) - nf.bitcoind.rpc.sendtoaddress(address, amount / 100_000_000) - nf.bitcoind.generate_block(6) - - response = b'{"ok":true}' - self.send_response(200) - self.send_header('Content-Type', 'application/json') - self.send_header('Content-Length', str(len(response))) - self.end_headers() - self.wfile.write(response) - self.wfile.flush() + if self.path == '/connect-to-lsp': + self._handle_connect_to_lsp() + elif self.path == '/fund-wallet': + self._handle_fund_wallet() + elif self.path == '/lsp-invoice': + self._handle_lsp_invoice() + elif self.path == '/btc-address': + self._handle_btc_address() + else: + self._send_json(404, {'error': f'Unknown path: {self.path}'}) except Exception as e: - error = json.dumps({'error': str(e)}).encode() - self.send_response(500) - self.send_header('Content-Type', 'application/json') - self.send_header('Content-Length', str(len(error))) - self.end_headers() - self.wfile.write(error) - self.wfile.flush() - - http = ThreadingHTTPServer(('127.0.0.1', 0), FundHandler) - port = http.server_address[1] + self._send_json(500, {'error': str(e)}) + + def _handle_fund_wallet(self): + body = self._read_body() + address = body['address'] + amount = body.get('amount', 100_000_000) # sats + nf.bitcoind.rpc.sendtoaddress(address, amount / 100_000_000) + nf.bitcoind.generate_block(6) + self._send_json(200, {'ok': True}) + + def _handle_connect_to_lsp(self): + body = self._read_body() + secret = bytes.fromhex(body['secret']) + client = clients.new(secret=secret[:32]) + client.register(configure=True) + signer = client.signer() + handle = signer.run_in_thread() + try: + gl_node = client.node() + gl_node.connect_peer(lsp_node_id, f'{lsp_node_host}:{lsp_node_port}') + finally: + handle.shutdown() + creds_path = client.directory / 'greenlight.auth' + self._send_json(200, {'creds_path': str(creds_path)}) + + def _handle_lsp_invoice(self): + body = self._read_body() + import pyln.client + lsp_rpc = pyln.client.LightningRpc(lsp.rpc.socket_path) + invoice = lsp_rpc.invoice( + amount_msat=body.get('amount_msat', 0), + label=body.get('label', f'test-{int(time.time())}'), + description=body.get('description', 'Test payment'), + ) + self._send_json(200, {'bolt11': invoice['bolt11']}) + + def _handle_btc_address(self): + address = nf.bitcoind.rpc.getnewaddress() + self._send_json(200, {'address': address}) + + http = ThreadingHTTPServer(('127.0.0.1', 0), Handler) + http_port = http.server_address[1] threading.Thread(target=http.serve_forever, daemon=True).start() - # 6. Write env for Jest + # 7. Write env for Jest + ENV_FILE.parent.mkdir(parents=True, exist_ok=True) ENV_FILE.write_text('\n'.join([ f"GL_SCHEDULER_GRPC_URI={s.grpc_addr}", f"GL_CA_CRT={cert_dir / 'ca.pem'}", f"GL_NOBODY_CRT={nobody_id.cert_chain_path}", f"GL_NOBODY_KEY={nobody_id.private_key_path}", f"LSP_RPC_SOCKET={lsp.rpc.socket_path}", - f"LSP_NODE_ID={lsp.info['id']}", - f"LSP_PROMISE_SECRET={'A' * 64}", - f"GL_FUND_SERVER=http://127.0.0.1:{port}", + f"LSP_NODE_ID={lsp_node_id}", + f"TEST_SETUP_SERVER_URL=http://127.0.0.1:{http_port}", ])) print('āœ… LSP setup ready', flush=True) - # 7. Block until Jest teardown sends SIGTERM + # 8. Block until teardown def shutdown(*_): nf.killall([]) s.stop()