1- import pytest
21import os
32import socket
43import subprocess
54import uuid
6-
75from logging import getLogger
86from os import environ , getenv
9- from time import time , sleep
7+ from time import sleep , time
108from typing import Optional
119
1210from pytest import fixture , mark
@@ -48,14 +46,21 @@ def pytest_addoption(parser):
4846 "--run-kind" , action = "store_true" , help = "Run integration tests against kind"
4947 )
5048 parser .addoption (
51- "--run-compose" , action = "store_true" , help = "Run integration tests against docker-compose"
49+ "--run-compose" ,
50+ action = "store_true" ,
51+ help = "Run integration tests against docker-compose" ,
5252 )
5353
5454
5555def pytest_configure (config ):
5656 config .addinivalue_line ("markers" , "slow: mark test as slow to run" )
57- config .addinivalue_line ("markers" , "kind_only: tests that require a Kubernetes/Kind environment" )
58- config .addinivalue_line ("markers" , "dedicated_helm_install: Marker for tests that need a dedicated helm release installation" )
57+ config .addinivalue_line (
58+ "markers" , "kind_only: tests that require a Kubernetes/Kind environment"
59+ )
60+ config .addinivalue_line (
61+ "markers" ,
62+ "dedicated_helm_install: Marker for tests that need a dedicated helm release installation" ,
63+ )
5964
6065
6166def pytest_collection_modifyitems (config , items ):
@@ -233,7 +238,7 @@ def pytest_generate_tests(metafunc):
233238def get_free_port ():
234239 """Ask the OS for a free ephemeral port."""
235240 with socket .socket (socket .AF_INET , socket .SOCK_STREAM ) as s :
236- s .bind (('' , 0 ))
241+ s .bind (("" , 0 ))
237242 s .setsockopt (socket .SOL_SOCKET , socket .SO_REUSEADDR , 1 )
238243 return s .getsockname ()[1 ]
239244
@@ -256,30 +261,46 @@ def kind_cluster(request):
256261 clusters = result .stdout .splitlines ()
257262
258263 if KIND_CLUSTER_NAME in clusters :
259- print (f"\n [Kind] Cluster '{ KIND_CLUSTER_NAME } ' already exists. Skipping creation." )
264+ print (
265+ f"\n [Kind] Cluster '{ KIND_CLUSTER_NAME } ' already exists. Skipping creation."
266+ )
260267 else :
261268 print (f"\n [Kind] Creating cluster '{ KIND_CLUSTER_NAME } '..." )
262- subprocess .run ([
263- "kind" , "create" , "cluster" ,
264- "--name" , KIND_CLUSTER_NAME ,
265- "--config" , "kind.yaml" ,
266- "--wait" , "5m"
267- ], check = True )
269+ subprocess .run (
270+ [
271+ "kind" ,
272+ "create" ,
273+ "cluster" ,
274+ "--name" ,
275+ KIND_CLUSTER_NAME ,
276+ "--config" ,
277+ "kind.yaml" ,
278+ "--wait" ,
279+ "5m" ,
280+ ],
281+ check = True ,
282+ )
268283
269284 context = f"kind-{ KIND_CLUSTER_NAME } "
270285 yield context
271286
272287 # Optional: Only delete when running in CI
273288 if os .getenv ("GITHUB_ACTIONS" ) == "true" :
274289 print (f"\n [Kind] CI detected: Deleting cluster '{ KIND_CLUSTER_NAME } '..." )
275- subprocess .run (["kind" , "delete" , "cluster" , "--name" , KIND_CLUSTER_NAME ], check = True )
290+ subprocess .run (
291+ ["kind" , "delete" , "cluster" , "--name" , KIND_CLUSTER_NAME ], check = True
292+ )
276293 else :
277- print (f"\n [Kind] Local: Cluster '{ KIND_CLUSTER_NAME } ' will be kept for next session." )
294+ print (
295+ f"\n [Kind] Local: Cluster '{ KIND_CLUSTER_NAME } ' will be kept for next session."
296+ )
278297
279298
280299def deploy_app (kind_cluster , helm_values = None ):
281300 """Common logic for both session and function-scoped setups."""
282- test_id = f"{ os .environ .get ('PYTEST_XDIST_WORKER' , 'python-sdk' )} -{ uuid .uuid4 ().hex [:4 ]} "
301+ test_id = (
302+ f"{ os .environ .get ('PYTEST_XDIST_WORKER' , 'python-sdk' )} -{ uuid .uuid4 ().hex [:4 ]} "
303+ )
283304 release , ns = f"core-{ test_id } " , f"ns-{ test_id } "
284305 local_port = get_free_port ()
285306
@@ -297,26 +318,62 @@ def deploy_app(kind_cluster, helm_values=None):
297318 set_args .extend (["--set" , f"{ key } ={ value } " ])
298319
299320 print (f"[Kind] Installing Helm release { release } into namespace { ns } ..." )
300- subprocess .run ([
301- "helm" , "install" , release ,
302- "oci://ghcr.io/firebolt-db/helm-charts/firebolt-core" ,
303- "--version" , getenv (CORE_HELM_CHART_VERSION_ENV , CORE_DEFAULT_HELM_CHART_VERSION ),
304- "-n" , ns ,
305- "--create-namespace" , "--wait" , "--kube-context" , kind_cluster
306- ]+ set_args , check = True )
321+ subprocess .run (
322+ [
323+ "helm" ,
324+ "install" ,
325+ release ,
326+ "oci://ghcr.io/firebolt-db/helm-charts/firebolt-core" ,
327+ "--version" ,
328+ getenv (CORE_HELM_CHART_VERSION_ENV , CORE_DEFAULT_HELM_CHART_VERSION ),
329+ "-n" ,
330+ ns ,
331+ "--create-namespace" ,
332+ "--wait" ,
333+ "--kube-context" ,
334+ kind_cluster ,
335+ ]
336+ + set_args ,
337+ check = True ,
338+ )
307339
308340 print (f"[Kind] Waiting for pods in { ns } to be ready..." )
309- subprocess .run ([
310- "kubectl" , "wait" , "--for=condition=ready" , "pod" ,
311- "-l" , "app.kubernetes.io/instance=" + release , # Adjust label to match your chart
312- "--namespace" , ns , "--timeout=120s" , "--context" , kind_cluster
313- ], check = True )
341+ subprocess .run (
342+ [
343+ "kubectl" ,
344+ "wait" ,
345+ "--for=condition=ready" ,
346+ "pod" ,
347+ "-l" ,
348+ "app.kubernetes.io/instance=" + release , # Adjust label to match your chart
349+ "--namespace" ,
350+ ns ,
351+ "--timeout=120s" ,
352+ "--context" ,
353+ kind_cluster ,
354+ ],
355+ check = True ,
356+ )
314357
315358 # Get pod names
316- pod_names_result = subprocess .run ([
317- "kubectl" , "get" , "pods" , "-l" , "app.kubernetes.io/instance=" + release ,
318- "-n" , ns , "-o" , "jsonpath={.items[*].metadata.name}" , "--context" , kind_cluster
319- ], capture_output = True , text = True , check = True )
359+ pod_names_result = subprocess .run (
360+ [
361+ "kubectl" ,
362+ "get" ,
363+ "pods" ,
364+ "-l" ,
365+ "app.kubernetes.io/instance=" + release ,
366+ "-n" ,
367+ ns ,
368+ "-o" ,
369+ "jsonpath={.items[*].metadata.name}" ,
370+ "--context" ,
371+ kind_cluster ,
372+ ],
373+ capture_output = True ,
374+ text = True ,
375+ check = True ,
376+ )
320377 pod_names = pod_names_result .stdout .split ()
321378
322379 pf_procs = []
@@ -325,12 +382,23 @@ def deploy_app(kind_cluster, helm_values=None):
325382 ip = "127.0.0.1"
326383 port = local_port + i
327384 ips_with_ports .append (f"{ ip } :{ port } " )
328- print (f"[Kind] Port-forward to pod { pod_name } on { ip } :{ port } ->{ FIREBOLT_CORE_PORT } ..." )
329- pf_proc = subprocess .Popen ([
330- "kubectl" , "port-forward" , "--address" , ip ,
331- f"pod/{ pod_name } " ,
332- f"{ port } :{ FIREBOLT_CORE_PORT } " , "-n" , ns , "--context" , kind_cluster
333- ])
385+ print (
386+ f"[Kind] Port-forward to pod { pod_name } on { ip } :{ port } ->{ FIREBOLT_CORE_PORT } ..."
387+ )
388+ pf_proc = subprocess .Popen (
389+ [
390+ "kubectl" ,
391+ "port-forward" ,
392+ "--address" ,
393+ ip ,
394+ f"pod/{ pod_name } " ,
395+ f"{ port } :{ FIREBOLT_CORE_PORT } " ,
396+ "-n" ,
397+ ns ,
398+ "--context" ,
399+ kind_cluster ,
400+ ]
401+ )
334402 pf_procs .append (pf_proc )
335403
336404 sleep (1 )
@@ -362,16 +430,26 @@ def deploy_app(kind_cluster, helm_values=None):
362430 "processes" : pf_procs ,
363431 "release" : release ,
364432 "ns" : ns ,
365- "ips" : ips_with_ports
433+ "ips" : ips_with_ports ,
366434 }
367435
368436
369437def cleanup_app (setup_data , kind_cluster ):
370438 """Common teardown logic."""
371439 for proc in setup_data ["processes" ]:
372440 proc .terminate ()
373- subprocess .run (["helm" , "uninstall" , setup_data ["release" ], "-n" ,
374- setup_data ["ns" ], "--kube-context" , kind_cluster ], check = True )
441+ subprocess .run (
442+ [
443+ "helm" ,
444+ "uninstall" ,
445+ setup_data ["release" ],
446+ "-n" ,
447+ setup_data ["ns" ],
448+ "--kube-context" ,
449+ kind_cluster ,
450+ ],
451+ check = True ,
452+ )
375453 subprocess .run (["kubectl" , "delete" , "ns" , setup_data ["ns" ]], check = True )
376454
377455
@@ -414,7 +492,11 @@ def kind_app_setup(request, session_helm_install, dedicated_helm_install):
414492 Dynamically injects the required environment variables from whichever kind setup is active.
415493 """
416494 # Use dedicated if it exists (not None), otherwise fall back to session
417- active_setup = dedicated_helm_install if dedicated_helm_install is not None else session_helm_install
495+ active_setup = (
496+ dedicated_helm_install
497+ if dedicated_helm_install is not None
498+ else session_helm_install
499+ )
418500
419501 if active_setup :
420502 if active_setup == dedicated_helm_install :
@@ -436,7 +518,7 @@ def kind_app_setup(request, session_helm_install, dedicated_helm_install):
436518 "SERVICE_ID" : "" ,
437519 "SERVICE_SECRET" : "" ,
438520 "ENGINE_URL" : "" ,
439- "CORE_IPS" : "," .join (ips )
521+ "CORE_IPS" : "," .join (ips ),
440522 }
441523 old_env = {k : os .environ .get (k ) for k in env_vars }
442524 os .environ .update (env_vars )
@@ -445,7 +527,9 @@ def kind_app_setup(request, session_helm_install, dedicated_helm_install):
445527
446528 # Restore env
447529 for k , v in old_env .items ():
448- if v is None : os .environ .pop (k , None )
449- else : os .environ [k ] = v
530+ if v is None :
531+ os .environ .pop (k , None )
532+ else :
533+ os .environ [k ] = v
450534 else :
451535 yield "docker-compose"
0 commit comments