66import warnings
77from pathlib import Path
88from textwrap import dedent
9- from typing import Optional
10- from unittest .mock import ANY , AsyncMock , MagicMock , call
9+ from typing import Any , Optional
10+ from unittest .mock import ANY , AsyncMock , MagicMock , Mock , call
1111
1212import pytest
1313import uv
1414
1515from prefect ._internal .compatibility .deprecated import PrefectDeprecationWarning
1616from prefect .blocks .core import Block
1717from prefect .blocks .system import Secret
18- from prefect .client .orchestration import PrefectClient
18+ from prefect .client .orchestration import PrefectClient , get_client
1919from prefect .deployments .steps import run_step
2020from prefect .deployments .steps .core import StepExecutionError , run_steps
2121from prefect .deployments .steps .pull import agit_clone , set_working_directory
@@ -266,10 +266,12 @@ async def test_requirement_installation_failure(self, monkeypatch, caplog):
266266
267267
268268class TestRunSteps :
269- async def test_run_steps_emits_pull_steps_event (self , monkeypatch ):
270- emitted = []
269+ async def test_run_steps_emits_pull_steps_event (
270+ self , monkeypatch : pytest .MonkeyPatch
271+ ):
272+ emitted : list [dict [str , Any ]] = []
271273
272- def fake_emit_event (** kwargs ) :
274+ def fake_emit_event (** kwargs : Any ) -> object :
273275 emitted .append (kwargs )
274276 return object ()
275277
@@ -281,7 +283,7 @@ def fake_emit_event(**kwargs):
281283 raising = False ,
282284 )
283285
284- def fake_step (* , script : str , ** kwargs ) :
286+ def fake_step (* , script : str , ** kwargs : Any ) -> dict [ str , Any ] :
285287 return {"result" : script , ** kwargs }
286288
287289 monkeypatch .setattr (
@@ -321,11 +323,13 @@ def fake_step(*, script: str, **kwargs):
321323 assert first_step_inputs == {"script" : "first" , "extra" : "value" }
322324 assert payload ["steps" ][0 ]["id" ] == "step-one"
323325
324- async def test_run_steps_skips_event_without_flow_run_id (self , monkeypatch ):
326+ async def test_run_steps_skips_event_without_flow_run_id (
327+ self , monkeypatch : pytest .MonkeyPatch
328+ ):
325329 monkeypatch .delenv ("PREFECT__FLOW_RUN_ID" , raising = False )
326- emitted = []
330+ emitted : list [ dict [ str , Any ]] = []
327331
328- def fake_emit_event (** kwargs ) :
332+ def fake_emit_event (** kwargs : Any ) -> object :
329333 emitted .append (kwargs )
330334 return object ()
331335
@@ -335,7 +339,7 @@ def fake_emit_event(**kwargs):
335339 raising = False ,
336340 )
337341
338- def fake_step (* , script : str , ** kwargs ) :
342+ def fake_step (* , script : str , ** kwargs : Any ) -> dict [ str , Any ] :
339343 return {"result" : script }
340344
341345 monkeypatch .setattr (
@@ -354,10 +358,12 @@ def fake_step(*, script: str, **kwargs):
354358 await run_steps (steps , {})
355359 assert emitted == []
356360
357- async def test_run_steps_emits_event_on_failure (self , monkeypatch ):
358- emitted = []
361+ async def test_run_steps_emits_event_on_failure (
362+ self , monkeypatch : pytest .MonkeyPatch
363+ ):
364+ emitted : list [dict [str , Any ]] = []
359365
360- def fake_emit_event (** kwargs ) :
366+ def fake_emit_event (** kwargs : Any ) -> object :
361367 emitted .append (kwargs )
362368 return object ()
363369
@@ -369,7 +375,7 @@ def fake_emit_event(**kwargs):
369375 raising = False ,
370376 )
371377
372- def fake_step (* , script : str , ** kwargs ) :
378+ def fake_step (* , script : str , ** kwargs : Any ) -> dict [ str , Any ] :
373379 if script == "boom" :
374380 raise RuntimeError ("explode" )
375381 return {"result" : script }
@@ -404,16 +410,12 @@ def fake_step(*, script: str, **kwargs):
404410 assert payload ["steps" ][0 ]["id" ] == "step-one"
405411 assert payload ["steps" ][1 ]["id" ] == "step-two"
406412
407- async def test_run_steps_does_not_expose_secrets_in_event (self , monkeypatch ):
408- """
409- Test that the pull steps event payload contains pre-templated inputs,
410- not resolved secrets from blocks, variables, or environment variables.
411-
412- This is critical for security - we don't want to leak credentials in event payloads.
413- """
414- emitted = []
413+ async def test_run_steps_does_not_expose_secrets_in_event (
414+ self , monkeypatch : pytest .MonkeyPatch
415+ ):
416+ emitted : list [dict [str , Any ]] = []
415417
416- def fake_emit_event (** kwargs ) :
418+ def fake_emit_event (** kwargs : Any ) -> object :
417419 emitted .append (kwargs )
418420 return object ()
419421
@@ -426,19 +428,16 @@ def fake_emit_event(**kwargs):
426428 raising = False ,
427429 )
428430
429- # Save a secret block
430431 await Secret (value = "my-secret-api-key" ).save (name = "api-key" )
431432
432- # Save a variable with sensitive data
433- from prefect .client .orchestration import get_client
434-
435433 async with get_client () as client :
436434 await client ._client .post (
437435 "/variables/" , json = {"name" : "db_password" , "value" : "secret-password" }
438436 )
439437
440- def fake_step (* , script : str , api_key : str , password : str , env_secret : str ):
441- # Verify that the step receives the resolved values
438+ def fake_step (
439+ * , script : str , api_key : str , password : str , env_secret : str
440+ ) -> dict [str , str ]:
442441 assert api_key == "my-secret-api-key"
443442 assert password == "secret-password"
444443 assert env_secret == "super-secret-value"
@@ -463,10 +462,7 @@ def fake_step(*, script: str, api_key: str, password: str, env_secret: str):
463462
464463 output = await run_steps (steps , {})
465464
466- # Step should have executed successfully
467465 assert output ["result" ] == "success"
468-
469- # Verify event was emitted
470466 assert len (emitted ) == 1
471467 event_kwargs = emitted [0 ]
472468 assert event_kwargs ["event" ] == "prefect.flow-run.pull-steps.executed"
@@ -475,25 +471,21 @@ def fake_step(*, script: str, api_key: str, password: str, env_secret: str):
475471 assert payload ["errored" ] is False
476472 assert payload ["count" ] == 1
477473
478- # CRITICAL: The event payload should contain the TEMPLATE STRINGS,
479- # not the resolved secret values
480474 step_inputs = payload ["steps" ][0 ]["inputs" ]
481475 assert step_inputs ["api_key" ] == "{{ prefect.blocks.secret.api-key }}"
482476 assert step_inputs ["password" ] == "{{ prefect.variables.db_password }}"
483477 assert step_inputs ["env_secret" ] == "{{ $SECRET_ENV_VAR }}"
484478
485- # Make sure we're not leaking the actual secret values
486479 assert "my-secret-api-key" not in str (payload )
487480 assert "secret-password" not in str (payload )
488481 assert "super-secret-value" not in str (payload )
489482
490- async def test_run_steps_includes_deployment_as_related_resource (self , monkeypatch ):
491- """
492- Test that the pull steps event includes the deployment as a related resource.
493- """
494- emitted = []
483+ async def test_run_steps_includes_deployment_as_related_resource (
484+ self , monkeypatch : pytest .MonkeyPatch
485+ ):
486+ emitted : list [dict [str , Any ]] = []
495487
496- def fake_emit_event (** kwargs ) :
488+ def fake_emit_event (** kwargs : Any ) -> object :
497489 emitted .append (kwargs )
498490 return object ()
499491
@@ -506,7 +498,7 @@ def fake_emit_event(**kwargs):
506498 raising = False ,
507499 )
508500
509- def fake_step (* , script : str ):
501+ def fake_step (* , script : str ) -> dict [ str , str ] :
510502 return {"result" : "success" }
511503
512504 monkeypatch .setattr (
@@ -523,23 +515,16 @@ def fake_step(*, script: str):
523515 }
524516 ]
525517
526- # Create a mock deployment
527- from unittest .mock import Mock
528-
529518 mock_deployment = Mock ()
530519 mock_deployment .id = deployment_id
531520
532521 output = await run_steps (steps , {}, deployment = mock_deployment )
533522
534- # Step should have executed successfully
535523 assert output ["result" ] == "success"
536-
537- # Verify event was emitted with deployment as related resource
538524 assert len (emitted ) == 1
539525 event_kwargs = emitted [0 ]
540526 assert event_kwargs ["event" ] == "prefect.flow-run.pull-steps.executed"
541527
542- # Check that related resources includes the deployment
543528 related = event_kwargs .get ("related" )
544529 assert related is not None
545530 assert len (related ) == 1
0 commit comments