1010from __future__ import annotations
1111
1212import asyncio
13+ import json
1314import os
1415import shutil
1516import sys
@@ -91,6 +92,29 @@ def wait_for_dapr_health(host: str, port: int, timeout: int = 60) -> bool:
9192 return False
9293
9394
95+ def wait_for_dapr_component (host : str , port : int , component_name : str , timeout : int = 60 ) -> bool :
96+ """Wait for a named component to appear in the Dapr metadata endpoint."""
97+ metadata_url = f"http://{ host } :{ port } /v1.0/metadata"
98+ start_time = time .time ()
99+
100+ while time .time () - start_time < timeout :
101+ try :
102+ with urllib .request .urlopen (metadata_url , timeout = 5 ) as response :
103+ if 200 <= response .status < 300 :
104+ payload = json .load (response )
105+ components = payload .get ("components" , [])
106+ if any (component .get ("name" ) == component_name for component in components ):
107+ print (f"✓ Dapr component { component_name } loaded via { metadata_url } " )
108+ return True
109+ except Exception :
110+ pass
111+
112+ time .sleep (1 )
113+
114+ print (f"✗ Dapr component { component_name } did not load after { timeout } s" )
115+ return False
116+
117+
94118@pytest .fixture (scope = "module" )
95119def docker_network ():
96120 """Create a Docker network for container-to-container communication."""
@@ -120,8 +144,10 @@ def dapr_container(redis_container, docker_network):
120144 """Start Dapr sidecar container with Redis state store configuration."""
121145 # Create temporary components directory
122146 temp_dir = tempfile .mkdtemp ()
147+ os .chmod (temp_dir , 0o755 )
123148 components_path = os .path .join (temp_dir , "components" )
124149 os .makedirs (components_path , exist_ok = True )
150+ os .chmod (components_path , 0o755 )
125151
126152 # Write Redis state store component configuration
127153 # KEY: Use 'redis:6379' (network alias), NOT localhost!
@@ -141,8 +167,10 @@ def dapr_container(redis_container, docker_network):
141167 - name: actorStateStore
142168 value: "false"
143169"""
144- with open (os .path .join (components_path , "statestore.yaml" ), "w" ) as f :
170+ state_store_path = os .path .join (components_path , "statestore.yaml" )
171+ with open (state_store_path , "w" ) as f :
145172 f .write (state_store_config )
173+ os .chmod (state_store_path , 0o644 )
146174
147175 # Create Dapr container
148176 container = DockerContainer ("daprio/daprd:latest" )
@@ -157,7 +185,7 @@ def dapr_container(redis_container, docker_network):
157185 "3500" , # HTTP API port for health checks
158186 "-dapr-grpc-port" ,
159187 "50001" ,
160- "-components -path" ,
188+ "-resources -path" ,
161189 "/components" ,
162190 "-log-level" ,
163191 "info" ,
@@ -176,6 +204,11 @@ def dapr_container(redis_container, docker_network):
176204 container .stop ()
177205 pytest .fail ("Dapr container failed to become healthy" )
178206
207+ if not wait_for_dapr_component (http_host , http_port , "statestore" , timeout = 60 ):
208+ logs = container .get_wrapped_container ().logs ().decode ("utf-8" , errors = "replace" )
209+ container .stop ()
210+ pytest .fail (f"Dapr state store component failed to load.\n Container logs:\n { logs } " )
211+
179212 # Set environment variables for Dapr SDK health checks
180213 # The Dapr SDK checks these when creating a client
181214 os .environ ["DAPR_HTTP_PORT" ] = str (http_port )
0 commit comments