Files
wehub-resource-sync 0418dc5cf9
CI / Python tests (3.10) (push) Has been cancelled
CI / Python tests (3.11) (push) Has been cancelled
CI / Python quality (push) Has been cancelled
CI / Frontend typecheck (push) Has been cancelled
Autopilot Pages / deploy (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 12:45:00 +08:00

100 lines
3.7 KiB
Python

"""Tests for hook priority ordering."""
from __future__ import annotations
from pathlib import Path
import pytest
from openharness.api.client import ApiMessageCompleteEvent
from openharness.api.usage import UsageSnapshot
from openharness.engine.messages import ConversationMessage, TextBlock
from openharness.hooks import HookEvent, HookExecutionContext, HookExecutor
from openharness.hooks.loader import HookRegistry
from openharness.hooks.schemas import CommandHookDefinition, HttpHookDefinition
class FakeApiClient:
"""Minimal fake streaming client."""
def __init__(self, text: str) -> None:
self._text = text
async def stream_message(self, request):
del request
yield ApiMessageCompleteEvent(
message=ConversationMessage(role="assistant", content=[TextBlock(text=self._text)]),
usage=UsageSnapshot(input_tokens=1, output_tokens=1),
stop_reason=None,
)
def test_priority_defaults_to_zero():
assert CommandHookDefinition(command="true").priority == 0
assert HttpHookDefinition(url="https://example.invalid").priority == 0
def test_registry_orders_by_priority_descending():
registry = HookRegistry()
registry.register(HookEvent.PRE_TOOL_USE, CommandHookDefinition(command="low", priority=1))
registry.register(HookEvent.PRE_TOOL_USE, CommandHookDefinition(command="high", priority=10))
registry.register(HookEvent.PRE_TOOL_USE, CommandHookDefinition(command="mid", priority=5))
commands = [hook.command for hook in registry.get(HookEvent.PRE_TOOL_USE)]
assert commands == ["high", "mid", "low"]
def test_registry_ties_keep_registration_order():
"""sorted() is stable, so equal priorities preserve insertion order."""
registry = HookRegistry()
registry.register(HookEvent.PRE_TOOL_USE, CommandHookDefinition(command="first", priority=5))
registry.register(HookEvent.PRE_TOOL_USE, CommandHookDefinition(command="second", priority=5))
registry.register(HookEvent.PRE_TOOL_USE, CommandHookDefinition(command="third", priority=5))
commands = [hook.command for hook in registry.get(HookEvent.PRE_TOOL_USE)]
assert commands == ["first", "second", "third"]
def test_negative_priority_runs_last():
registry = HookRegistry()
registry.register(HookEvent.SESSION_START, CommandHookDefinition(command="cleanup", priority=-10))
registry.register(HookEvent.SESSION_START, CommandHookDefinition(command="default"))
commands = [hook.command for hook in registry.get(HookEvent.SESSION_START)]
assert commands == ["default", "cleanup"]
def test_summary_includes_non_default_priority():
registry = HookRegistry()
registry.register(HookEvent.PRE_TOOL_USE, CommandHookDefinition(command="guard", priority=10))
registry.register(HookEvent.PRE_TOOL_USE, CommandHookDefinition(command="logger"))
summary = registry.summary()
assert "priority=10" in summary
# A default (zero) priority is not noisily printed.
assert "priority=0" not in summary
@pytest.mark.asyncio
async def test_executor_runs_hooks_in_priority_order(tmp_path: Path):
"""End-to-end: execute() honours the priority-sorted registry order."""
registry = HookRegistry()
registry.register(HookEvent.PRE_TOOL_USE, CommandHookDefinition(command="printf 'low'", priority=1))
registry.register(HookEvent.PRE_TOOL_USE, CommandHookDefinition(command="printf 'high'", priority=10))
executor = HookExecutor(
registry,
HookExecutionContext(
cwd=tmp_path,
api_client=FakeApiClient('{"ok": true}'),
default_model="claude-test",
),
)
result = await executor.execute(HookEvent.PRE_TOOL_USE, {"tool_name": "bash"})
assert [hook.output for hook in result.results] == ["high", "low"]