bigpowers 2.88.9 → 2.88.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -6,7 +6,9 @@ Run: python3 -m pytest tests/test_srp_engine.py -v
6
6
  """
7
7
  import os
8
8
  import sys
9
+ import tempfile
9
10
  import unittest
11
+ from unittest import mock
10
12
 
11
13
  # Make scripts/lib importable without installation.
12
14
  _REPO_ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
@@ -144,5 +146,153 @@ class TestRewriteLinksForPi(unittest.TestCase):
144
146
  self.assertNotIn("](REFERENCE.md)", result) # real link rewritten
145
147
 
146
148
 
149
+ class TestResolveBash(unittest.TestCase):
150
+ """Regression: dispatch must not make subprocess resolve the bare name
151
+ 'bash'. On Windows the Win32 CreateProcess search order finds the WSL
152
+ stub in System32 before Git Bash on PATH, so setup aborted with
153
+ 'Windows Subsystem for Linux has no installed distributions'."""
154
+
155
+ def test_env_override_wins(self):
156
+ with mock.patch.dict(os.environ, {"BIGPOWERS_BASH": "/custom/bash"}):
157
+ self.assertEqual(_srp.resolve_bash(), "/custom/bash")
158
+
159
+ def test_uses_path_resolved_bash(self):
160
+ env = {k: v for k, v in os.environ.items() if k != "BIGPOWERS_BASH"}
161
+ with mock.patch.dict(os.environ, env, clear=True):
162
+ with mock.patch.object(_srp.shutil, "which", return_value="/usr/bin/bash"):
163
+ self.assertEqual(_srp.resolve_bash(), "/usr/bin/bash")
164
+
165
+ def test_falls_back_to_bare_bash(self):
166
+ env = {k: v for k, v in os.environ.items() if k != "BIGPOWERS_BASH"}
167
+ with mock.patch.dict(os.environ, env, clear=True):
168
+ with mock.patch.object(_srp.shutil, "which", return_value=None):
169
+ self.assertEqual(_srp.resolve_bash(), "bash")
170
+
171
+
172
+ class TestDispatchUsesResolvedBash(unittest.TestCase):
173
+ """The adapter subprocess must receive the PATH-resolved bash path, not
174
+ the bare name that Windows resolves to the WSL stub."""
175
+
176
+ def setUp(self):
177
+ self._tmp = tempfile.TemporaryDirectory()
178
+ self.repo_root = self._tmp.name
179
+ adapter_dir = os.path.join(self.repo_root, "scripts", "adapters")
180
+ os.makedirs(adapter_dir)
181
+ self.adapter = os.path.join(adapter_dir, "fake.sh")
182
+ with open(self.adapter, "w", encoding="utf-8") as f:
183
+ f.write("#!/usr/bin/env bash\n")
184
+
185
+ def tearDown(self):
186
+ self._tmp.cleanup()
187
+
188
+ def test_dispatch_passes_resolved_bash_to_popen(self):
189
+ captured = {}
190
+
191
+ class _FakeProc:
192
+ returncode = 0
193
+
194
+ def communicate(self, input=None):
195
+ return ("", "")
196
+
197
+ def _fake_popen(cmd, **kwargs):
198
+ captured["cmd"] = cmd
199
+ return _FakeProc()
200
+
201
+ sentinel = os.path.join(self.repo_root, "git-bash")
202
+ with mock.patch.object(_srp, "resolve_bash", return_value=sentinel):
203
+ with mock.patch.object(_srp.subprocess, "Popen", side_effect=_fake_popen):
204
+ _srp.dispatch_to_adapter({"name": "x"}, "fake", self.repo_root)
205
+
206
+ self.assertEqual(captured["cmd"][0], sentinel)
207
+ self.assertEqual(captured["cmd"][1], self.adapter)
208
+
209
+
210
+ class TestWriteIrStream(unittest.TestCase):
211
+ """#138: the whole run's IR is serialised once."""
212
+
213
+ def test_writes_nul_delimited_fields_in_order(self):
214
+ records = [{
215
+ 'name': 'demo',
216
+ 'description': 'a "demo"',
217
+ 'model': 'gpt',
218
+ 'body': 'line1\nline2',
219
+ }]
220
+ with mock.patch.object(_srp, 'rewrite_links_for_pi', return_value='PI-BODY'):
221
+ path = _srp._write_ir_stream(records)
222
+ try:
223
+ with open(path, 'rb') as f:
224
+ data = f.read()
225
+ finally:
226
+ os.unlink(path)
227
+ self.assertEqual(data, b'demo\x00a "demo"\x00gpt\x00line1\nline2\x00PI-BODY\x00')
228
+
229
+
230
+ class TestRenderAll(unittest.TestCase):
231
+ """#138: one bash process per adapter, not one per skill×adapter."""
232
+
233
+ @staticmethod
234
+ def _records(n):
235
+ return [{'name': f's{i}', 'description': 'd', 'model': '', 'body': 'b'} for i in range(n)]
236
+
237
+ def test_one_process_per_adapter_not_per_skill(self):
238
+ records = self._records(5)
239
+ with mock.patch.object(_srp, '_write_ir_stream', return_value='/tmp/ir.bin') as write, \
240
+ mock.patch.object(_srp.os, 'unlink') as unlink, \
241
+ mock.patch.object(_srp.subprocess, 'run') as run:
242
+ run.return_value = mock.Mock(returncode=0)
243
+ _srp.render_all(records, ['cursor', 'pi'], _REPO_ROOT)
244
+ self.assertEqual(write.call_count, 1)
245
+ self.assertEqual(run.call_count, 2)
246
+ unlink.assert_called_once_with('/tmp/ir.bin')
247
+
248
+ def test_nonzero_adapter_exits_nonzero(self):
249
+ with mock.patch.object(_srp, '_write_ir_stream', return_value='/tmp/ir.bin'), \
250
+ mock.patch.object(_srp.os, 'unlink'), \
251
+ mock.patch.object(_srp.subprocess, 'run') as run:
252
+ run.return_value = mock.Mock(returncode=3)
253
+ with self.assertRaises(SystemExit):
254
+ _srp.render_all(self._records(1), ['cursor'], _REPO_ROOT)
255
+
256
+ def test_empty_records_does_not_spawn(self):
257
+ with mock.patch.object(_srp.subprocess, 'run') as run:
258
+ _srp.render_all([], ['cursor'], _REPO_ROOT)
259
+ run.assert_not_called()
260
+
261
+ def test_missing_adapter_exits_without_spawning(self):
262
+ with mock.patch.object(_srp, '_write_ir_stream', return_value='/tmp/ir.bin'), \
263
+ mock.patch.object(_srp.os, 'unlink'), \
264
+ mock.patch.object(_srp.subprocess, 'run') as run:
265
+ with self.assertRaises(SystemExit):
266
+ _srp.render_all(self._records(1), ['not-a-real-adapter'], _REPO_ROOT)
267
+ run.assert_not_called()
268
+
269
+
270
+ class TestAdapterRenderAllDriver(unittest.TestCase):
271
+ """#138: the bash driver sources one adapter and renders the stream."""
272
+
273
+ def test_driver_renders_stream_in_a_single_process(self):
274
+ import shutil
275
+ import subprocess
276
+
277
+ driver = os.path.join(_REPO_ROOT, 'scripts', 'lib', 'adapter-render-all.sh')
278
+ adapter = os.path.join(_REPO_ROOT, 'scripts', 'adapters', 'cursor.sh')
279
+ with tempfile.TemporaryDirectory() as tmp:
280
+ stream = os.path.join(tmp, 'ir.bin')
281
+ with open(stream, 'wb') as f:
282
+ for field in ('demo-skill', 'desc', '', 'body text', 'body text'):
283
+ f.write(field.encode())
284
+ f.write(b'\x00')
285
+ env = dict(os.environ)
286
+ env['CURSOR_RULES'] = tmp
287
+ proc = subprocess.run(
288
+ [shutil.which('bash') or 'bash', driver, adapter, stream],
289
+ env=env,
290
+ stdout=subprocess.PIPE,
291
+ stderr=subprocess.PIPE,
292
+ )
293
+ self.assertEqual(proc.returncode, 0, proc.stderr)
294
+ self.assertTrue(os.path.isfile(os.path.join(tmp, 'demo-skill.mdc')))
295
+
296
+
147
297
  if __name__ == "__main__":
148
298
  unittest.main()
@@ -1,23 +0,0 @@
1
- [
2
- {
3
- "name": "P0 Risk",
4
- "matchedStatuses": [
5
- "failed"
6
- ],
7
- "messageRegex": ".*risk.*P0.*"
8
- },
9
- {
10
- "name": "Security Review",
11
- "matchedStatuses": [
12
- "failed"
13
- ],
14
- "messageRegex": ".*security.*(?:medium|high).*"
15
- },
16
- {
17
- "name": "Open Bugs",
18
- "matchedStatuses": [
19
- "failed"
20
- ],
21
- "messageRegex": ".*Bug.*"
22
- }
23
- ]
@@ -1,6 +0,0 @@
1
- {
2
- "name": "bigpowers",
3
- "type": "bigpowers",
4
- "buildName": "2.76.2",
5
- "buildOrder": 328
6
- }