88- Policy: no self-approve execution
99"""
1010
11+ import importlib .util
12+ import sys
13+ from pathlib import Path
14+
1115import pytest
1216
13- from envelope_parser import Envelope , parse_envelope
14- from rules import (
15- Exit ,
16- Violation ,
17- rule_has_header ,
18- rule_has_msg_id ,
19- rule_has_sender ,
20- rule_has_recipient ,
21- rule_has_mode ,
22- rule_has_scope ,
23- rule_has_goal ,
24- rule_valid_sender ,
25- rule_valid_exit ,
26- rule_response_must_have_exit ,
27- rule_no_self_approve_exec ,
28- )
29- from gate import evaluate , GateResult
17+ # ---------------------------------------------------------------------------
18+ # Robust local imports via importlib (avoids sibling gate.py collision)
19+ # ---------------------------------------------------------------------------
20+ _HERE = Path (__file__ ).resolve ().parent
21+
22+
23+ def _load_local (module_name : str ):
24+ """Load a module from this directory by file path."""
25+ path = _HERE / f"{ module_name } .py"
26+ spec = importlib .util .spec_from_file_location (module_name , str (path ))
27+ mod = importlib .util .module_from_spec (spec )
28+ sys .modules [module_name ] = mod
29+ spec .loader .exec_module (mod )
30+ return mod
31+
32+
33+ _ep = _load_local ("envelope_parser" )
34+ Envelope = _ep .Envelope
35+ parse_envelope = _ep .parse_envelope
36+
37+ _ru = _load_local ("rules" )
38+ Exit = _ru .Exit
39+ Violation = _ru .Violation
40+ rule_has_header = _ru .rule_has_header
41+ rule_has_msg_id = _ru .rule_has_msg_id
42+ rule_has_sender = _ru .rule_has_sender
43+ rule_has_recipient = _ru .rule_has_recipient
44+ rule_has_mode = _ru .rule_has_mode
45+ rule_has_scope = _ru .rule_has_scope
46+ rule_has_goal = _ru .rule_has_goal
47+ rule_valid_sender = _ru .rule_valid_sender
48+ rule_valid_exit = _ru .rule_valid_exit
49+ rule_response_must_have_exit = _ru .rule_response_must_have_exit
50+ rule_no_self_approve_exec = _ru .rule_no_self_approve_exec
51+
52+ _ga = _load_local ("gate" )
53+ evaluate = _ga .evaluate
54+ GateResult = _ga .GateResult
55+
3056
3157
3258# ---------------------------------------------------------------------------
5379 output_spec:
5480 type: NOTE
5581 format: MARKDOWN
56- payload:
57- This is a test payload.
82+ payload: This is a test payload.
5883RETURN:
5984 in_reply_to: ""
6085 exit:
83108 output_spec:
84109 type: NOTE
85110 format: MARKDOWN
86- payload:
87- Response payload here.
111+ payload: Response payload here.
88112RETURN:
89113 in_reply_to: "msg-0008"
90114 exit: ALLOW
91115 reason:
92116 - All checks passed.
93- payload:
94- Conformance verified.
117+ payload: Conformance verified.
95118"""
96119
97120
@@ -125,11 +148,11 @@ def _make_envelope(**overrides) -> Envelope:
125148# R0 structural pre-check tests
126149# ---------------------------------------------------------------------------
127150
151+
128152class TestR0Structural :
129153 def test_valid_envelope_passes_r0 (self ):
130154 env = parse_envelope (VALID_ENVELOPE_RAW )
131155 result = evaluate (env )
132- # Should not get DENY (no structural failures)
133156 assert result .exit != "DENY"
134157
135158 def test_missing_header_is_deny (self ):
@@ -191,6 +214,7 @@ def test_present_fields_pass(self):
191214# EXIT_ENUM_ERRATA v0.1 tests
192215# ---------------------------------------------------------------------------
193216
217+
194218class TestExitEnum :
195219 def test_allow_is_valid (self ):
196220 env = _make_envelope (exit_code = "ALLOW" )
@@ -247,6 +271,7 @@ def test_blank_exit_not_flagged_by_enum_rule(self):
247271# Blank-field clarification (msg-0007)
248272# ---------------------------------------------------------------------------
249273
274+
250275class TestBlankField :
251276 def test_blank_exit_in_request_is_ok (self ):
252277 env = _make_envelope (msg_id = "msg-0010" , exit_code = "" )
@@ -267,9 +292,9 @@ def test_response_with_exit_is_ok(self):
267292# FIRST_FAIL evaluation policy
268293# ---------------------------------------------------------------------------
269294
295+
270296class TestFirstFail :
271297 def test_first_fail_halts_on_first_violation (self ):
272- # Missing msg_id AND missing sender -- FIRST_FAIL should report only 1
273298 env = _make_envelope (msg_id = "" , sender = "" )
274299 result = evaluate (env , policy = "FIRST_FAIL" )
275300 assert len (result .violations ) == 1
@@ -286,6 +311,7 @@ def test_accumulate_all_collects_all(self):
286311# Policy: no self-approve execution
287312# ---------------------------------------------------------------------------
288313
314+
289315class TestSelfApprove :
290316 def test_human_can_approve_exec (self ):
291317 env = _make_envelope (
@@ -318,6 +344,7 @@ def test_agent_hold_on_exec_is_ok(self):
318344# Invalid agent tests
319345# ---------------------------------------------------------------------------
320346
347+
321348class TestAgentValidation :
322349 def test_valid_agents_pass (self ):
323350 for agent in ["HUMAN" , "TRINITY" , "MORPHEUS" ]:
@@ -335,6 +362,7 @@ def test_unknown_agent_fails(self):
335362# Gate-level integration tests
336363# ---------------------------------------------------------------------------
337364
365+
338366class TestGateIntegration :
339367 def test_valid_request_gets_allow (self ):
340368 env = _make_envelope ()
@@ -361,5 +389,4 @@ def test_legacy_exit_gets_hold(self):
361389 def test_blank_response_exit_gets_hold (self ):
362390 env = _make_envelope (msg_id = "msg-0099-R" , exit_code = "" )
363391 result = evaluate (env )
364- # Should be HOLD (not DENY, since structure is present)
365392 assert result .exit == "HOLD"
0 commit comments