mirror of
https://github.com/NCBM/plyngent.git
synced 2026-11-07 23:47:54 +08:00
test: cover declined, timed out, cancelled and absent prompts
The four kinds and the words each produces, at the registry level and through the four policy gates: a timeout names its deadline, a decline carries the comment, and neither is reported as the other. The existing denial assertions now expect the specific clause.
This commit is contained in:
@@ -123,7 +123,24 @@ async def test_deny_none(
|
||||
outside = tmp_path_factory.mktemp("timeout").resolve()
|
||||
set_directory_access_confirm_hook(lambda *_a: None)
|
||||
out = await call_async(request_directory_access, str(outside))
|
||||
assert "denied (declined or timed out" in out
|
||||
assert "denied (user declined)" in out
|
||||
assert session.access_grants == {}
|
||||
|
||||
|
||||
async def test_a_prompt_timeout_is_not_reported_as_a_decision(
|
||||
workspace: object,
|
||||
session: SessionState,
|
||||
tmp_path_factory: pytest.TempPathFactory,
|
||||
) -> None:
|
||||
"""The deadline the prompt waited out is what the model reads."""
|
||||
from plyngent.agent import Denial, DenialKind
|
||||
|
||||
del workspace
|
||||
outside = tmp_path_factory.mktemp("silence").resolve()
|
||||
set_directory_access_confirm_hook(lambda *_a: Denial(DenialKind.TIMEOUT, seconds=12.0))
|
||||
out = await call_async(request_directory_access, str(outside))
|
||||
assert "denied (no answer within 12s — denied by default)" in out
|
||||
assert "user" not in out
|
||||
assert session.access_grants == {}
|
||||
|
||||
|
||||
|
||||
@@ -204,6 +204,21 @@ async def test_a_private_host_answer_is_recorded_as_a_user_process_approval(work
|
||||
assert recorded == [Approval("user", "process")]
|
||||
|
||||
|
||||
async def test_a_private_host_timeout_names_the_deadline(workspace: Path) -> None:
|
||||
"""A prompt that nobody answered says the clock, not a human's no."""
|
||||
del workspace
|
||||
from plyngent.agent import Denial, DenialKind
|
||||
from plyngent.tools.net.grants import ensure_host_allowed, set_fetch_policy_confirm_hook
|
||||
from plyngent.tools.net.policy import FetchPolicyError
|
||||
|
||||
set_fetch_policy_confirm_hook(lambda *_args: Denial(DenialKind.TIMEOUT, seconds=7.0))
|
||||
try:
|
||||
with pytest.raises(FetchPolicyError, match="no answer within 7s"):
|
||||
await ensure_host_allowed("http://127.0.0.1:9/")
|
||||
finally:
|
||||
set_fetch_policy_confirm_hook(None)
|
||||
|
||||
|
||||
async def test_a_private_host_grant_is_recorded_as_a_process_grant(workspace: Path) -> None:
|
||||
"""A second fetch to the granted host asks nobody, and says so."""
|
||||
del workspace
|
||||
|
||||
@@ -309,10 +309,26 @@ async def test_skill_write_prompt_denial_is_reported(tmp_path: Path) -> None:
|
||||
with _bound(tmp_path, root, policy=SkillWritePolicy(confirm=_deny, timeout=3.0)):
|
||||
out = await skill_edit.handler("acme", old_string="Before.", new_string="After.")
|
||||
assert out.startswith("error: skill write to ")
|
||||
assert out.endswith("denied (declined or timed out after 3s)")
|
||||
assert out.endswith("denied (user declined)")
|
||||
assert "After." not in (root / "acme" / SKILL_FILE).read_text(encoding="utf-8")
|
||||
|
||||
|
||||
async def test_a_skill_write_timeout_names_the_deadline(tmp_path: Path) -> None:
|
||||
"""Nobody answering is not the same message as a human saying no."""
|
||||
from plyngent.agent import Denial, DenialKind
|
||||
|
||||
root = _write_root(tmp_path)
|
||||
_ = _write_skill(root, "acme", body="Before.")
|
||||
|
||||
def silent(*_args: object) -> SkillWriteDecision:
|
||||
return SkillWriteDecision(allow=False, denial=Denial(DenialKind.TIMEOUT, seconds=3.0))
|
||||
|
||||
with _bound(tmp_path, root, policy=SkillWritePolicy(confirm=silent, timeout=3.0)):
|
||||
out = await skill_edit.handler("acme", old_string="Before.", new_string="After.")
|
||||
assert out.endswith("denied (no answer within 3s — denied by default)")
|
||||
assert "user" not in out
|
||||
|
||||
|
||||
async def test_skill_write_confirm_sees_the_action_and_the_skill(tmp_path: Path) -> None:
|
||||
root = _write_root(tmp_path)
|
||||
seen: list[tuple[Path, str, str, float]] = []
|
||||
|
||||
@@ -280,13 +280,30 @@ def test_command_denylist_policy_confirm_deny(workspace: object) -> None:
|
||||
set_policy_confirm_hook(lambda *_a: False)
|
||||
clear_policy_allowed_commands()
|
||||
try:
|
||||
with pytest.raises(WorkspaceError, match="declined or timed out"):
|
||||
with pytest.raises(WorkspaceError, match="user declined"):
|
||||
check_command_allowed(["sudo", "echo", "x"])
|
||||
finally:
|
||||
set_policy_confirm_hook(None)
|
||||
clear_policy_allowed_commands()
|
||||
|
||||
|
||||
def test_command_denylist_policy_timeout_names_the_deadline(workspace: object) -> None:
|
||||
"""Silence is reported as the clock, never as the human's decision."""
|
||||
del workspace
|
||||
from plyngent.agent import Denial, DenialKind
|
||||
from plyngent.tools.workspace import clear_policy_allowed_commands, set_policy_confirm_hook
|
||||
|
||||
set_policy_confirm_hook(lambda *_a: Denial(DenialKind.TIMEOUT, seconds=3.0))
|
||||
clear_policy_allowed_commands()
|
||||
try:
|
||||
with pytest.raises(WorkspaceError, match="no answer within 3s") as refusal:
|
||||
check_command_allowed(["sudo", "echo", "x"])
|
||||
finally:
|
||||
set_policy_confirm_hook(None)
|
||||
clear_policy_allowed_commands()
|
||||
assert "declined" not in str(refusal.value)
|
||||
|
||||
|
||||
def test_command_denylist_policy_confirm_timeout_value(workspace: object) -> None:
|
||||
del workspace
|
||||
from plyngent.tools.workspace import (
|
||||
|
||||
Reference in New Issue
Block a user