22
33import json
44import os
5+ import random
6+ import subprocess
7+ import time
58from pathlib import Path
69from time import sleep
710
11+ import requests
812from test_base import TestBase
913
10- from warnet .process import stream_command
14+ from warnet .process import run_command
1115
1216
1317class LNBasicTest (TestBase ):
@@ -24,11 +28,18 @@ def __init__(self):
2428 "tank-0005-ln" ,
2529 ]
2630
31+ self .cb_port = 9235
32+ self .cb_node = "tank-0003-ln"
33+ self .port_forward = None
34+
2735 def run_test (self ):
2836 try :
2937 # Wait for all nodes to wake up. ln_init will start automatically
3038 self .setup_network ()
3139
40+ # Test circuit breaker API
41+ self .test_circuit_breaker_api ()
42+
3243 # Send a payment across channels opened automatically by ln_init
3344 self .pay_invoice (sender = "tank-0005-ln" , recipient = "tank-0003-ln" )
3445
@@ -39,11 +50,12 @@ def run_test(self):
3950 self .pay_invoice (sender = "tank-0000-ln" , recipient = "tank-0002-ln" )
4051
4152 finally :
53+ self .cleanup_kubectl_created_services ()
4254 self .cleanup ()
4355
4456 def setup_network (self ):
4557 self .log .info ("Setting up network" )
46- stream_command (f"warnet deploy { self .network_dir } " )
58+ run_command (f"warnet deploy { self .network_dir } " )
4759
4860 def fund_wallets (self ):
4961 outputs = ""
@@ -120,6 +132,98 @@ def scenario_open_channels(self):
120132 self .log .info (f"Running scenario from: { scenario_file } " )
121133 self .warnet (f"run { scenario_file } --source_dir={ self .scen_dir } --debug" )
122134
135+ def test_circuit_breaker_api (self ):
136+ self .log .info ("Testing Circuit Breaker API" )
137+
138+ # Set up port forwarding to the circuit breaker
139+ cb_url = self .setup_api_access (self .cb_node )
140+
141+ self .log .info (f"Testing Circuit Breaker API at { cb_url } " )
142+
143+ # Test /info endpoint
144+ info = self .cb_api_request (cb_url , "get" , "/info" )
145+ assert "version" in info , "Circuit breaker info missing version"
146+
147+ # Test /limits endpoint
148+ limits = self .cb_api_request (cb_url , "get" , "/limits" )
149+ assert isinstance (limits , dict ), "Limits should be a dictionary"
150+
151+ self .log .info ("✅ Circuit Breaker API tests passed" )
152+
153+ def setup_api_access (self , pod_name ):
154+ """Set up Kubernetes Service access to the Circuit Breaker API"""
155+ # Create a properly labeled service using kubectl expose
156+ service_name = f"{ pod_name } -svc"
157+
158+ self .log .info (f"Creating service { service_name } for pod { pod_name } " )
159+
160+ command = f"kubectl expose pod { pod_name } --name { service_name } --port { self .cb_port } --target-port { self .cb_port } "
161+ result = run_command (command )
162+ self .log .info (f"Service creation command output: { result } " )
163+
164+ time .sleep (51 ) # Wait for the service to be created
165+
166+ service_url = f"http://{ service_name } :{ self .cb_port } /api"
167+ self .service_to_cleanup = service_name
168+ self .log .info (f"Service URL: { service_url } " )
169+
170+ self .log .info (f"Successfully created service at { service_url } " )
171+ return service_url
172+
173+ def cb_api_request (self , base_url , method , endpoint , data = None ):
174+ """Universal API request handler with proper path handling"""
175+ try :
176+ # Parse the base URL components
177+ url_parts = base_url .split ("://" )[1 ].split ("/" )
178+ service_name = url_parts [0 ].split (":" )[0 ]
179+ port = url_parts [0 ].split (":" )[1 ] if ":" in url_parts [0 ] else "80"
180+ base_path = "/" + "/" .join (url_parts [1 :]) if len (url_parts ) > 1 else "/"
181+
182+ # Set up port forwarding
183+ local_port = random .randint (10000 , 20000 )
184+ pf = subprocess .Popen (
185+ ["kubectl" , "port-forward" , f"svc/{ service_name } " , f"{ local_port } :{ port } " ],
186+ stdout = subprocess .PIPE ,
187+ stderr = subprocess .PIPE ,
188+ )
189+
190+ try :
191+ # Wait for port-forward to establish
192+ time .sleep (2 )
193+
194+ # Construct the full local URL with proper path handling
195+ full_path = base_path .rstrip ("/" ) + "/" + endpoint .lstrip ("/" )
196+ local_url = f"http://localhost:{ local_port } { full_path } "
197+
198+ self .log .debug (f"Attempting API request to: { local_url } " )
199+
200+ # Make the request
201+ if method .lower () == "get" :
202+ response = requests .get (local_url , timeout = 30 )
203+ else :
204+ response = requests .post (local_url , json = data , timeout = 30 )
205+
206+ response .raise_for_status ()
207+ return response .json ()
208+
209+ finally :
210+ pf .terminate ()
211+ pf .wait ()
212+
213+ except Exception as e :
214+ self .log .error (f"API request to { local_url } failed: { str (e )} " )
215+ raise
216+
217+ def cleanup_kubectl_created_services (self ):
218+ """Clean up any created resources"""
219+ if hasattr (self , "service_to_cleanup" ) and self .service_to_cleanup :
220+ self .log .info (f"Deleting service { self .service_to_cleanup } " )
221+ subprocess .run (
222+ ["kubectl" , "delete" , "svc" , self .service_to_cleanup ],
223+ stdout = subprocess .DEVNULL ,
224+ stderr = subprocess .DEVNULL ,
225+ )
226+
123227
124228if __name__ == "__main__" :
125229 test = LNBasicTest ()
0 commit comments