@@ -89,13 +89,148 @@ def test_landed_fix_writes_one_record(registry, settings, trace, tmp_path):
8989 assert list ((tmp_path / "intake" ).iterdir ()) == [path ]
9090
9191
92+ def _capture_gh (monkeypatch , * , code = 0 , out = None ):
93+ import infermatrix_copilot .engine .steps .pr .debug as debug_module
94+ calls = []
95+
96+ def fake_gh (args , cwd = None ):
97+ calls .append (list (args ))
98+ return code , (json .dumps ({"html_url" : "https://github.com/c/copilot/"
99+ "issues/135#issuecomment-9" })
100+ if out is None else out )
101+
102+ monkeypatch .setattr (debug_module , "_gh" , fake_gh )
103+ return calls
104+
105+
106+ def test_landed_fix_is_posted_to_the_mailbox_issue (
107+ registry , settings , trace , tmp_path , monkeypatch
108+ ):
109+ settings .knowledge_intake_dir = str (tmp_path / "intake" )
110+ settings .knowledge_intake_issue = "c/copilot#135"
111+ settings .allow_post = True
112+ settings .repo_full_names = {"demo" : "owner/demo" }
113+ calls = _capture_gh (monkeypatch )
114+ result = _run (registry , settings , tmp_path = tmp_path , trace = trace ,
115+ state = _state ())
116+ assert result .ok and "via drop file and mailbox issue" in result .summary
117+ assert result .outputs ["intake_comment" ].endswith ("#issuecomment-9" )
118+ assert calls == [["api" , "repos/c/copilot/issues/135/comments" , "-X" , "POST" ,
119+ "-F" , f"body=@{ tmp_path / 'run-x' / 'knowledge-intake-comment.md' } " ]]
120+ body = (tmp_path / "run-x" / "knowledge-intake-comment.md" ).read_text (
121+ encoding = "utf-8"
122+ )
123+ assert body .startswith ("<!-- infermatrix-copilot:bugfix-record:v1 -->\n ```json\n " )
124+ posted = json .loads (body .split ("```json\n " , 1 )[1 ].rsplit ("```" , 1 )[0 ])
125+ # The comment carries exactly the record the drop file carries.
126+ dropped = json .loads ((tmp_path / "intake" / "run-x.json" ).read_text (encoding = "utf-8" ))
127+ assert posted == dropped and posted ["repo" ] == "owner/demo"
128+ assert list (trace .events ("knowledge_intake_published" ))
129+
130+
131+ def test_mailbox_post_failure_is_swallowed (
132+ registry , settings , trace , tmp_path , monkeypatch
133+ ):
134+ settings .knowledge_intake_dir = str (tmp_path / "intake" )
135+ settings .knowledge_intake_issue = "c/copilot#135"
136+ settings .allow_post = True
137+ _capture_gh (monkeypatch , code = 1 , out = "HTTP 403: locked" )
138+ result = _run (registry , settings , tmp_path = tmp_path , trace = trace ,
139+ state = _state ())
140+ assert result .ok # the landed fix and its drop file are not undone
141+ assert (tmp_path / "intake" / "run-x.json" ).is_file ()
142+ assert "via drop file" in result .summary
143+ assert result .outputs ["intake_publish_error" ] == "HTTP 403: locked"
144+ assert "intake_comment" not in result .outputs
145+ assert list (trace .events ("knowledge_intake_publish_failed" ))
146+
147+
148+ def test_mailbox_publisher_exception_is_swallowed (
149+ registry , settings , trace , tmp_path , monkeypatch
150+ ):
151+ import subprocess
152+
153+ import infermatrix_copilot .engine .steps .pr .debug as debug_module
154+
155+ def hanging_gh (args , cwd = None ):
156+ raise subprocess .TimeoutExpired (cmd = ["gh" , * args ], timeout = 120 )
157+
158+ monkeypatch .setattr (debug_module , "_gh" , hanging_gh )
159+ settings .knowledge_intake_dir = str (tmp_path / "intake" )
160+ settings .knowledge_intake_issue = "c/copilot#135"
161+ settings .allow_post = True
162+ result = _run (registry , settings , tmp_path = tmp_path , trace = trace ,
163+ state = _state ())
164+ assert result .ok and "via drop file" in result .summary
165+ assert result .outputs ["intake_publish_error" ].startswith ("TimeoutExpired" )
166+ assert list (trace .events ("knowledge_intake_publish_failed" ))
167+
168+
169+ def test_mailbox_post_is_dry_run_without_allow_post (
170+ registry , settings , trace , tmp_path , monkeypatch
171+ ):
172+ """Invariant 5: an outward write needs the env flag too."""
173+ settings .knowledge_intake_dir = str (tmp_path / "intake" )
174+ settings .knowledge_intake_issue = "c/copilot#135"
175+ settings .allow_post = False
176+ calls = _capture_gh (monkeypatch )
177+ result = _run (registry , settings , tmp_path = tmp_path , trace = trace ,
178+ state = _state ())
179+ assert result .ok and calls == []
180+ assert "via drop file" in result .summary
181+ assert result .outputs ["intake_publish_skipped" ].startswith ("ALLOW_POST=0" )
182+ assert list (trace .events ("knowledge_intake_publish_skipped" ))
183+
184+
185+ def test_mailbox_is_off_without_configuration (
186+ registry , settings , trace , tmp_path , monkeypatch
187+ ):
188+ settings .knowledge_intake_dir = str (tmp_path / "intake" )
189+ calls = _capture_gh (monkeypatch )
190+ result = _run (registry , settings , tmp_path = tmp_path , trace = trace ,
191+ state = _state ())
192+ assert result .ok and calls == []
193+ assert "intake_comment" not in result .outputs
194+
195+
196+ def test_mailbox_only_configuration_posts_without_a_drop_dir (
197+ registry , settings , trace , tmp_path , monkeypatch
198+ ):
199+ """The production shape: no local consumer, only the mailbox."""
200+ settings .knowledge_intake_issue = "c/copilot#135"
201+ settings .allow_post = True
202+ calls = _capture_gh (monkeypatch )
203+ result = _run (registry , settings , tmp_path = tmp_path , trace = trace ,
204+ state = _state ())
205+ assert result .ok and "via mailbox issue" in result .summary
206+ assert len (calls ) == 1 and "intake_comment" in result .outputs
207+ assert "intake_path" not in result .outputs
208+ assert not (tmp_path / "intake" ).exists ()
209+
210+
211+ def test_drop_failure_does_not_suppress_the_mailbox (
212+ registry , settings , trace , tmp_path , monkeypatch
213+ ):
214+ blocker = tmp_path / "intake"
215+ blocker .write_text ("a file where the directory should be" )
216+ settings .knowledge_intake_dir = str (blocker )
217+ settings .knowledge_intake_issue = "c/copilot#135"
218+ settings .allow_post = True
219+ calls = _capture_gh (monkeypatch )
220+ result = _run (registry , settings , tmp_path = tmp_path , trace = trace ,
221+ state = _state ())
222+ assert result .ok and "via mailbox issue" in result .summary
223+ assert "intake_error" in result .outputs and "intake_comment" in result .outputs
224+ assert len (calls ) == 1
225+
226+
92227def test_write_failure_is_swallowed (registry , settings , trace , tmp_path ):
93228 blocker = tmp_path / "intake"
94229 blocker .write_text ("a file where the directory should be" )
95230 settings .knowledge_intake_dir = str (blocker )
96231 result = _run (registry , settings , trace , tmp_path , _state ())
97232 assert result .ok # the landed fix must never be failed by the drop
98- assert "intake drop failed" in result .summary
233+ assert "intake delivery failed" in result .summary
99234 assert "intake_error" in result .outputs
100235
101236
0 commit comments