Skip to content

Commit cfd35aa

Browse files
authored
fix: use importlib for local imports in test_envelope_gate.py
1 parent df48dbf commit cfd35aa

1 file changed

Lines changed: 53 additions & 26 deletions

File tree

primitives/envelope-gate/test_envelope_gate.py

Lines changed: 53 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -8,25 +8,51 @@
88
- Policy: no self-approve execution
99
"""
1010

11+
import importlib.util
12+
import sys
13+
from pathlib import Path
14+
1115
import 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
# ---------------------------------------------------------------------------
@@ -53,8 +79,7 @@
5379
output_spec:
5480
type: NOTE
5581
format: MARKDOWN
56-
payload:
57-
This is a test payload.
82+
payload: This is a test payload.
5883
RETURN:
5984
in_reply_to: ""
6085
exit:
@@ -83,15 +108,13 @@
83108
output_spec:
84109
type: NOTE
85110
format: MARKDOWN
86-
payload:
87-
Response payload here.
111+
payload: Response payload here.
88112
RETURN:
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+
128152
class 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+
194218
class 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+
250275
class 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+
270296
class 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+
289315
class 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+
321348
class 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+
338366
class 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

Comments
 (0)