204 lines
7.1 KiB
Python
204 lines
7.1 KiB
Python
import asyncio
|
|
|
|
|
|
def test_run_with_timeout_supports_sync_function(monkeypatch):
|
|
monkeypatch.setenv("ELEPHANT_ALPHA_CACHE_DB", ":memory:")
|
|
from services.elephant_alpha_autonomous_engine import ElephantAlphaAutonomousEngine
|
|
|
|
result = asyncio.run(ElephantAlphaAutonomousEngine._run_with_timeout(lambda value: value + 1, 41))
|
|
|
|
assert result == 42
|
|
|
|
|
|
def test_execute_step_rejects_unknown_action(monkeypatch):
|
|
monkeypatch.setenv("ELEPHANT_ALPHA_CACHE_DB", ":memory:")
|
|
from services.elephant_alpha_autonomous_engine import ElephantAlphaAutonomousEngine
|
|
|
|
engine = ElephantAlphaAutonomousEngine()
|
|
|
|
try:
|
|
asyncio.run(engine._execute_step({"agent": "mystery", "action": "do_anything"}))
|
|
except ValueError as exc:
|
|
assert "Unrecognized step" in str(exc)
|
|
else:
|
|
raise AssertionError("unknown action should fail")
|
|
|
|
|
|
def test_execute_step_routes_code_fix_to_autoheal(monkeypatch):
|
|
monkeypatch.setenv("ELEPHANT_ALPHA_CACHE_DB", ":memory:")
|
|
from services.elephant_alpha_autonomous_engine import ElephantAlphaAutonomousEngine
|
|
|
|
calls = []
|
|
engine = ElephantAlphaAutonomousEngine()
|
|
monkeypatch.setattr(
|
|
engine,
|
|
"_run_auto_heal",
|
|
lambda error_type, context: calls.append((error_type, context)) or {"ok": True},
|
|
)
|
|
|
|
asyncio.run(engine._execute_step({
|
|
"agent": "elephant_alpha",
|
|
"action": "code_fix",
|
|
"parameters": {"target_file": "services/example.py", "error_message": "Traceback"},
|
|
}))
|
|
|
|
assert calls == [("python_exception", {"target_file": "services/example.py", "error_message": "Traceback"})]
|
|
|
|
|
|
def test_execute_step_routes_price_adjustment_to_human_review(monkeypatch):
|
|
monkeypatch.setenv("ELEPHANT_ALPHA_CACHE_DB", ":memory:")
|
|
from services.elephant_alpha_autonomous_engine import ElephantAlphaAutonomousEngine
|
|
|
|
calls = []
|
|
engine = ElephantAlphaAutonomousEngine()
|
|
monkeypatch.setattr(
|
|
engine,
|
|
"_record_price_adjustment_review",
|
|
lambda step: calls.append(step) or {"status": "pending_review", "sku": "SKU-9"},
|
|
)
|
|
|
|
result = asyncio.run(engine._execute_step({
|
|
"agent": "nemotron",
|
|
"action": "execute_price_adjustment",
|
|
"parameters": {"sku": "SKU-9", "recommended_price": 1280},
|
|
}))
|
|
|
|
assert result == {"status": "pending_review", "sku": "SKU-9"}
|
|
assert calls[0]["parameters"]["recommended_price"] == 1280
|
|
|
|
|
|
def test_execute_step_skips_legacy_openclaw_resource_strategy():
|
|
from services.elephant_alpha_autonomous_engine import ElephantAlphaAutonomousEngine
|
|
|
|
engine = ElephantAlphaAutonomousEngine()
|
|
|
|
result = asyncio.run(engine._execute_step({
|
|
"agent": "openclaw",
|
|
"action": "generate_resource_optimization_strategy",
|
|
}))
|
|
|
|
assert result is None
|
|
|
|
|
|
def test_autoheal_derives_python_exception_from_traceback():
|
|
from services.auto_heal_service import AutoHealService
|
|
|
|
svc = AutoHealService()
|
|
|
|
assert svc._derive_error_type({"traceback_str": "Traceback (most recent call last):\nNameError"}) == "python_exception"
|
|
|
|
|
|
def test_action_queue_counts_only_operational_actions(monkeypatch):
|
|
from sqlalchemy import create_engine, text
|
|
import services.elephant_alpha_autonomous_engine as engine_mod
|
|
|
|
monkeypatch.setattr(engine_mod, "CACHE_DB_PATH", ":memory:")
|
|
db = create_engine("sqlite:///:memory:")
|
|
conn = db.connect()
|
|
conn.execute(text("CREATE TABLE action_plans (status TEXT, action_type TEXT)"))
|
|
conn.execute(text("""
|
|
INSERT INTO action_plans (status, action_type) VALUES
|
|
('pending', 'openclaw_recommendation'),
|
|
('pending', 'human_review'),
|
|
('pending', 'auto_heal'),
|
|
('auto_pending', 'code_review_fix'),
|
|
('auto_disabled', 'code_review_fix')
|
|
"""))
|
|
conn.commit()
|
|
|
|
class Session:
|
|
def execute(self, stmt):
|
|
return conn.execute(stmt)
|
|
|
|
def close(self):
|
|
pass
|
|
|
|
monkeypatch.setattr(engine_mod, "get_session", lambda: Session())
|
|
|
|
try:
|
|
engine = engine_mod.ElephantAlphaAutonomousEngine()
|
|
|
|
assert engine._get_action_queue_size() == 2
|
|
finally:
|
|
conn.close()
|
|
db.dispose()
|
|
|
|
|
|
def test_trigger_check_respects_persistent_cooldown(monkeypatch, tmp_path):
|
|
import services.elephant_alpha_autonomous_engine as engine_mod
|
|
|
|
monkeypatch.setattr(engine_mod, "CACHE_DB_PATH", str(tmp_path / "ea_cache.db"))
|
|
engine = engine_mod.ElephantAlphaAutonomousEngine()
|
|
trigger = engine_mod.AutonomousTrigger("resource_optimization", {}, 0.6, True)
|
|
engine._store_escalation("resource_optimization")
|
|
|
|
next_engine = engine_mod.ElephantAlphaAutonomousEngine()
|
|
next_engine.triggers = [trigger]
|
|
called = {"evaluated": False}
|
|
|
|
async def evaluate(_trigger):
|
|
called["evaluated"] = True
|
|
return True
|
|
|
|
async def execute(_trigger):
|
|
raise AssertionError("cooldown should skip execution")
|
|
|
|
monkeypatch.setattr(next_engine, "_evaluate_trigger", evaluate)
|
|
monkeypatch.setattr(next_engine, "_execute_autonomous_decision", execute)
|
|
|
|
asyncio.run(next_engine._check_triggers())
|
|
|
|
assert called["evaluated"] is False
|
|
|
|
|
|
def test_no_evidence_resource_escalation_is_suppressed(monkeypatch, tmp_path):
|
|
import services.elephant_alpha_autonomous_engine as engine_mod
|
|
from services.elephant_alpha_orchestrator import StrategicDecision
|
|
|
|
monkeypatch.setattr(engine_mod, "CACHE_DB_PATH", str(tmp_path / "ea_cache.db"))
|
|
engine = engine_mod.ElephantAlphaAutonomousEngine()
|
|
trigger = engine_mod.AutonomousTrigger("resource_optimization", {}, 0.6, True)
|
|
decision = StrategicDecision(
|
|
priority="medium",
|
|
agents_required=["elephant_alpha"],
|
|
reasoning="Hermes pre-fetch 失敗,沒有可驗證資料",
|
|
expected_outcome="",
|
|
confidence=0.60,
|
|
execution_plan=[],
|
|
resource_requirements={},
|
|
)
|
|
|
|
monkeypatch.setattr(engine, "_get_action_queue_size", lambda: 0)
|
|
monkeypatch.setattr(engine, "_get_system_load_percentage", lambda: 0.0)
|
|
monkeypatch.setattr(
|
|
engine_mod,
|
|
"get_session",
|
|
lambda: (_ for _ in ()).throw(AssertionError("suppressed escalation should not write DB")),
|
|
)
|
|
|
|
async def fail_send(*_args, **_kwargs):
|
|
raise AssertionError("suppressed escalation should not send Telegram")
|
|
|
|
monkeypatch.setattr(engine, "_run_with_timeout", fail_send)
|
|
|
|
asyncio.run(engine._escalate_to_human(decision, trigger))
|
|
|
|
assert engine._load_escalation("no_evidence:resource_optimization") is not None
|
|
assert engine._is_trigger_in_cooldown(trigger) is True
|
|
|
|
|
|
def test_resource_escalation_uses_operational_evidence(monkeypatch, tmp_path):
|
|
import services.elephant_alpha_autonomous_engine as engine_mod
|
|
|
|
monkeypatch.setattr(engine_mod, "CACHE_DB_PATH", str(tmp_path / "ea_cache.db"))
|
|
engine = engine_mod.ElephantAlphaAutonomousEngine()
|
|
|
|
monkeypatch.setattr(engine, "_get_action_queue_size", lambda: 17)
|
|
monkeypatch.setattr(engine, "_get_system_load_percentage", lambda: 42.0)
|
|
|
|
actions = engine._build_resource_escalation_actions()
|
|
|
|
assert actions is not None
|
|
assert "auto action queue 17 筆" in actions[0]
|
|
assert "Hermes" not in "\n".join(actions)
|