2
2
3
3
import json
4
4
import os
5
+ import random
6
+ import subprocess
7
+ import time
5
8
from pathlib import Path
6
9
from time import sleep
7
10
11
+ import requests
8
12
from test_base import TestBase
9
13
10
- from warnet .process import stream_command
14
+ from warnet .process import run_command
11
15
12
16
13
17
class LNBasicTest (TestBase ):
@@ -24,11 +28,18 @@ def __init__(self):
24
28
"tank-0005-ln" ,
25
29
]
26
30
31
+ self .cb_port = 9235
32
+ self .cb_node = "tank-0003-ln"
33
+ self .port_forward = None
34
+
27
35
def run_test (self ):
28
36
try :
29
37
# Wait for all nodes to wake up. ln_init will start automatically
30
38
self .setup_network ()
31
39
40
+ # Test circuit breaker API
41
+ self .test_circuit_breaker_api ()
42
+
32
43
# Send a payment across channels opened automatically by ln_init
33
44
self .pay_invoice (sender = "tank-0005-ln" , recipient = "tank-0003-ln" )
34
45
@@ -39,11 +50,12 @@ def run_test(self):
39
50
self .pay_invoice (sender = "tank-0000-ln" , recipient = "tank-0002-ln" )
40
51
41
52
finally :
53
+ self .cleanup_kubectl_created_services ()
42
54
self .cleanup ()
43
55
44
56
def setup_network (self ):
45
57
self .log .info ("Setting up network" )
46
- stream_command (f"warnet deploy { self .network_dir } " )
58
+ run_command (f"warnet deploy { self .network_dir } " )
47
59
48
60
def fund_wallets (self ):
49
61
outputs = ""
@@ -120,6 +132,98 @@ def scenario_open_channels(self):
120
132
self .log .info (f"Running scenario from: { scenario_file } " )
121
133
self .warnet (f"run { scenario_file } --source_dir={ self .scen_dir } --debug" )
122
134
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
+
123
227
124
228
if __name__ == "__main__" :
125
229
test = LNBasicTest ()
0 commit comments