052591281d
- Complete agent system with planning and execution - Bridge API with FastAPI server - WhatsApp-style mobile dashboard - Enhanced web search with query improvement - Security guards and policy checks - Comprehensive documentation
120 lines
3.6 KiB
Python
120 lines
3.6 KiB
Python
"""Regression tests for Hardening Sprint 2 — agent planner layer."""
|
|
from __future__ import annotations
|
|
|
|
import os
|
|
import sys
|
|
import importlib
|
|
from pathlib import Path
|
|
|
|
import pytest
|
|
|
|
sys.path.insert(0, str(Path(__file__).resolve().parents[1]))
|
|
|
|
|
|
def _reload_planner(test_mode: str | None) -> object:
|
|
"""Reload planner.planner with a specific AGENT_TEST_MODE value."""
|
|
env = os.environ.copy()
|
|
if test_mode is None:
|
|
env.pop("AGENT_TEST_MODE", None)
|
|
else:
|
|
env["AGENT_TEST_MODE"] = test_mode
|
|
|
|
old = os.environ.copy()
|
|
os.environ.clear()
|
|
os.environ.update(env)
|
|
try:
|
|
if "planner.planner" in sys.modules:
|
|
del sys.modules["planner.planner"]
|
|
import planner.planner as p
|
|
importlib.reload(p)
|
|
return p
|
|
finally:
|
|
os.environ.clear()
|
|
os.environ.update(old)
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# 3. Planner — test branches disabled when AGENT_TEST_MODE != 1
|
|
# ---------------------------------------------------------------------------
|
|
|
|
TEST_GOALS = [
|
|
"test deny bash",
|
|
"test deny rm",
|
|
"test deny curl",
|
|
"test forbidden command",
|
|
"test traversal",
|
|
"test absolute path",
|
|
"test retry execution",
|
|
"test flow pwd to file",
|
|
"test flow read to copy",
|
|
"test flow ls to file",
|
|
"test flow bad template",
|
|
]
|
|
|
|
NORMAL_GOALS = [
|
|
"inspect project",
|
|
"analyze codebase",
|
|
"create file hello.txt with text hi",
|
|
]
|
|
|
|
|
|
@pytest.mark.parametrize("goal", TEST_GOALS)
|
|
def test_test_goal_fails_without_test_mode(goal):
|
|
"""AGENT_TEST_MODE unset → all test goals raise PlannerError."""
|
|
p = _reload_planner(None)
|
|
assert p.TEST_MODE is False
|
|
with pytest.raises(p.PlannerError):
|
|
p.build_plan(goal)
|
|
|
|
|
|
@pytest.mark.parametrize("goal", TEST_GOALS)
|
|
def test_test_goal_fails_with_test_mode_zero(goal):
|
|
"""AGENT_TEST_MODE=0 → still raises PlannerError."""
|
|
p = _reload_planner("0")
|
|
assert p.TEST_MODE is False
|
|
with pytest.raises(p.PlannerError):
|
|
p.build_plan(goal)
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# 4. Planner — test branches enabled when AGENT_TEST_MODE == 1
|
|
# ---------------------------------------------------------------------------
|
|
|
|
@pytest.mark.parametrize("goal", TEST_GOALS)
|
|
def test_test_goal_succeeds_with_test_mode_one(goal):
|
|
"""AGENT_TEST_MODE=1 → test goals build a plan (no PlannerError)."""
|
|
p = _reload_planner("1")
|
|
assert p.TEST_MODE is True
|
|
plan = p.build_plan(goal)
|
|
assert len(plan.steps) >= 1
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# 5. Safety test — normal goals unaffected by TEST_MODE gating
|
|
# ---------------------------------------------------------------------------
|
|
|
|
@pytest.mark.parametrize("goal", NORMAL_GOALS)
|
|
def test_normal_goal_works_without_test_mode(goal):
|
|
"""Normal goals always produce a plan regardless of AGENT_TEST_MODE."""
|
|
p = _reload_planner(None)
|
|
plan = p.build_plan(goal)
|
|
assert len(plan.steps) >= 1
|
|
assert plan.steps[-1].kind == "finish"
|
|
|
|
|
|
@pytest.mark.parametrize("goal", NORMAL_GOALS)
|
|
def test_normal_goal_works_with_test_mode(goal):
|
|
"""Normal goals still work when AGENT_TEST_MODE=1."""
|
|
p = _reload_planner("1")
|
|
plan = p.build_plan(goal)
|
|
assert len(plan.steps) >= 1
|
|
assert plan.steps[-1].kind == "finish"
|
|
|
|
|
|
def test_unknown_goal_raises_planner_error():
|
|
"""Unknown goals raise PlannerError in both modes."""
|
|
for mode in (None, "0", "1"):
|
|
p = _reload_planner(mode)
|
|
with pytest.raises(p.PlannerError):
|
|
p.build_plan("run the test suite please")
|