Files
wehub-resource-sync c56bef871b
Sync docs with Docusaurus / sync (push) Waiting to run
Tests / Check if changed (push) Waiting to run
Tests / format (push) Blocked by required conditions
Tests / check-imports (push) Blocked by required conditions
Tests / Unit / macos-latest (push) Blocked by required conditions
Tests / Unit / ubuntu-latest (push) Blocked by required conditions
Tests / Unit / windows-latest (push) Blocked by required conditions
Tests / mypy (push) Blocked by required conditions
Tests / Integration / ubuntu-latest (push) Blocked by required conditions
Tests / Integration / macos-latest (push) Blocked by required conditions
Tests / Integration / windows-latest (push) Blocked by required conditions
Tests / notify-slack-on-failure (push) Blocked by required conditions
Tests / Mark tests as completed (push) Blocked by required conditions
Docker image release / Build base image (push) Waiting to run
CodeQL / Analyze (python) (push) Has been cancelled
Update Platform Components Table / update (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 13:22:28 +08:00

145 lines
3.8 KiB
Python

# SPDX-FileCopyrightText: 2022-present deepset GmbH <info@deepset.ai>
#
# SPDX-License-Identifier: Apache-2.0
import pytest
from haystack.components.agents.state import State
from haystack.hooks import FunctionHook, hook
from haystack.hooks.utils import (
_deserialize_hooks_dictionary,
_serialize_hooks_dictionary,
_unique_hooks,
close_hooks,
close_hooks_async,
warm_up_hooks,
warm_up_hooks_async,
)
def noop(state: State) -> None:
pass
class LifecycleSpy:
"""Hook recording its lifecycle calls."""
def __init__(self) -> None:
self.warmed = 0
self.warmed_async = 0
self.closed = 0
self.closed_async = 0
def run(self, state: State) -> None:
pass
def warm_up(self) -> None:
self.warmed += 1
async def warm_up_async(self) -> None:
self.warmed_async += 1
def close(self) -> None:
self.closed += 1
async def close_async(self) -> None:
self.closed_async += 1
class WarmOnlyHook:
"""Hook with a sync `warm_up`/`close` but no async variants."""
def __init__(self) -> None:
self.warmed = 0
self.closed = 0
def run(self, state: State) -> None:
pass
def warm_up(self) -> None:
self.warmed += 1
def close(self) -> None:
self.closed += 1
class PlainHook:
"""Hook with no lifecycle methods."""
def run(self, state: State) -> None:
pass
class TestUniqueHooks:
def test_dedupes_by_identity_preserving_order(self):
a, b = PlainHook(), PlainHook()
unique = _unique_hooks({"before_llm": [a, b], "on_exit": [a]})
assert unique == [a, b]
def test_empty(self):
assert _unique_hooks({}) == []
class TestSerializeHooks:
def test_roundtrip_multiple_hook_points(self):
hooks = {"before_llm": [hook(noop)], "on_exit": [hook(noop)]}
restored = _deserialize_hooks_dictionary(_serialize_hooks_dictionary(hooks))
assert set(restored) == {"before_llm", "on_exit"}
assert all(isinstance(h, FunctionHook) for hook_list in restored.values() for h in hook_list)
assert restored["before_llm"][0].function is noop
class TestWarmUpHooks:
def test_calls_warm_up_when_present(self):
spy = LifecycleSpy()
warm_up_hooks({"before_llm": [spy]})
assert spy.warmed == 1
def test_skips_hooks_without_warm_up(self):
warm_up_hooks({"before_llm": [PlainHook()]}) # does not raise
def test_reused_hook_warmed_once(self):
spy = LifecycleSpy()
warm_up_hooks({"before_llm": [spy], "on_exit": [spy]})
assert spy.warmed == 1
class TestCloseHooks:
def test_calls_close_when_present(self):
spy = LifecycleSpy()
close_hooks({"before_llm": [spy]})
assert spy.closed == 1
def test_skips_hooks_without_close(self):
close_hooks({"before_llm": [PlainHook()]}) # does not raise
class TestWarmUpHooksAsync:
@pytest.mark.asyncio
async def test_prefers_async_warm_up(self):
spy = LifecycleSpy()
await warm_up_hooks_async({"before_llm": [spy]})
assert spy.warmed_async == 1
assert spy.warmed == 0
@pytest.mark.asyncio
async def test_falls_back_to_sync_warm_up(self):
hook_obj = WarmOnlyHook()
await warm_up_hooks_async({"before_llm": [hook_obj]})
assert hook_obj.warmed == 1
class TestCloseHooksAsync:
@pytest.mark.asyncio
async def test_prefers_async_close(self):
spy = LifecycleSpy()
await close_hooks_async({"before_llm": [spy]})
assert spy.closed_async == 1
assert spy.closed == 0
@pytest.mark.asyncio
async def test_falls_back_to_sync_close(self):
hook_obj = WarmOnlyHook()
await close_hooks_async({"before_llm": [hook_obj]})
assert hook_obj.closed == 1