"""自动代码评审服务端单测:POST /tasks//reviews、 GET /tasks//reviews、POST /tasks//reviews//findings。 不跑真实模型、不联网:本地临时端口 + 临时 SQLite,与 test_peer_messages_server.py 同款骨架。覆盖鉴权、权限、 目标校验、队列查看、findings 写回与跨任务隔离。 """ import http.client import json import os import sys import tempfile import threading import unittest from pathlib import Path try: from cloud.auth import AuthStore from cloud.server import create_server except ImportError: # discover -s cloud/tests 时把项目根目录补进 sys.path sys.path.insert(0, str(Path(__file__).resolve().parents[2])) from cloud.auth import AuthStore from cloud.server import create_server class ReviewServerTestCase(unittest.TestCase): def setUp(self): self.tmp = tempfile.TemporaryDirectory() self.db_path = os.path.join(self.tmp.name, "control.db") self.auth = AuthStore(self.db_path) self.owner_id, self.owner_key = self.auth.create_user("owner") self.outsider_id, self.outsider_key = self.auth.create_user( "outsider") self.server = create_server(self.db_path, "127.0.0.1", 0) self.port = self.server.server_address[1] self.thread = threading.Thread( target=self.server.serve_forever, daemon=True) self.thread.start() self.trace_dir = os.path.join(self.tmp.name, "host-traces") os.makedirs(self.trace_dir, exist_ok=True) self.server.trace_dir = self.trace_dir status, self.project = self._req( "POST", "/projects", {"name": "demo"}, self.owner_key) self.assertEqual(status, 201) status, self.project2 = self._req( "POST", "/projects", {"name": "other"}, self.owner_key) self.assertEqual(status, 201) def tearDown(self): self.server.shutdown() self.server.server_close() self.thread.join(timeout=5) try: self.auth.conn.close() except Exception: pass self.tmp.cleanup() def _req(self, method, path, body=None, key=None): conn = http.client.HTTPConnection( "127.0.0.1", self.port, timeout=10) hdrs = {} payload = None if body is not None: payload = json.dumps(body).encode("utf-8") hdrs["Content-Type"] = "application/json" if key is not None: hdrs["X-API-Key"] = key try: conn.request(method, path, body=payload, headers=hdrs) resp = conn.getresponse() data = resp.read() finally: conn.close() try: parsed = json.loads(data.decode("utf-8")) if data else None except ValueError: parsed = None return resp.status, parsed def _create_task(self, project_id): status, task = self._req( "POST", "/tasks", {"project_id": project_id, "prompt": "做事"}, self.owner_key) self.assertEqual(status, 201, task) return task def test_create_default_and_list(self): task = self._create_task(self.project["id"]) status, created = self._req( "POST", "/tasks/%s/reviews" % task["id"], {}, self.owner_key) self.assertEqual(status, 201, created) self.assertEqual(created["status"], "requested") self.assertIsNone(created["findings"]) self.assertEqual(created["target_type"], "uncommittedChanges") status, body = self._req( "GET", "/tasks/%s/reviews" % task["id"], None, self.owner_key) self.assertEqual(status, 200, body) self.assertEqual(len(body["reviews"]), 1) self.assertEqual(body["reviews"][0]["id"], created["id"]) def test_create_all_targets(self): task = self._create_task(self.project["id"]) cases = [ ({"target_type": "baseBranch", "branch": "main"}, "main"), ({"target_type": "commit", "sha": "abc123"}, "abc123"), ({"target_type": "custom", "instructions": "look"}, None), ] for payload, _expected in cases: status, body = self._req( "POST", "/tasks/%s/reviews" % task["id"], payload, self.owner_key) self.assertEqual(status, 201, body) status, body = self._req( "GET", "/tasks/%s/reviews" % task["id"], None, self.owner_key) self.assertEqual(len(body["reviews"]), 3) def test_create_target_validation(self): task = self._create_task(self.project["id"]) bad = [ {"target_type": "baseBranch"}, {"target_type": "commit"}, {"target_type": "custom"}, {"target_type": "nope"}, {"target_type": "baseBranch", "branch": 123}, ] for payload in bad: status, body = self._req( "POST", "/tasks/%s/reviews" % task["id"], payload, self.owner_key) self.assertEqual(status, 400, (payload, body)) status, body = self._req( "GET", "/tasks/%s/reviews" % task["id"], None, self.owner_key) self.assertEqual(body["reviews"], []) def test_findings_writeback(self): task = self._create_task(self.project["id"]) _status, review = self._req( "POST", "/tasks/%s/reviews" % task["id"], {}, self.owner_key) status, body = self._req( "POST", "/tasks/%s/reviews/%s/findings" % (task["id"], review["id"]), {"findings": "two issues"}, self.owner_key) self.assertEqual(status, 200, body) self.assertEqual(body["status"], "completed") self.assertEqual(body["findings"], "two issues") status, body = self._req( "GET", "/tasks/%s/reviews" % task["id"], None, self.owner_key) self.assertEqual(body["reviews"][0]["findings"], "two issues") def test_findings_explicit_failed(self): task = self._create_task(self.project["id"]) _status, review = self._req( "POST", "/tasks/%s/reviews" % task["id"], {}, self.owner_key) status, body = self._req( "POST", "/tasks/%s/reviews/%s/findings" % (task["id"], review["id"]), {"status": "failed", "findings": "reviewer error"}, self.owner_key) self.assertEqual(status, 200, body) self.assertEqual(body["status"], "failed") def test_findings_validation_and_isolation(self): task = self._create_task(self.project["id"]) other = self._create_task(self.project["id"]) _status, review = self._req( "POST", "/tasks/%s/reviews" % task["id"], {}, self.owner_key) url = "/tasks/%s/reviews/%s/findings" % ( task["id"], review["id"]) status, _body = self._req("POST", url, {}, self.owner_key) self.assertEqual(status, 400) status, _body = self._req( "POST", url, {"status": "bogus"}, self.owner_key) self.assertEqual(status, 400) status, _body = self._req( "POST", url, {"findings": 123}, self.owner_key) self.assertEqual(status, 400) # 记录属于 task,挂在 other 任务下写回时 404,不泄露。 status, _body = self._req( "POST", "/tasks/%s/reviews/%s/findings" % ( other["id"], review["id"]), {"findings": "x"}, self.owner_key) self.assertEqual(status, 404) status, _body = self._req( "POST", "/tasks/%s/reviews/%s/findings" % ( task["id"], "no-such-review"), {"findings": "x"}, self.owner_key) self.assertEqual(status, 404) def test_auth_and_permissions(self): task = self._create_task(self.project["id"]) status, _body = self._req( "POST", "/tasks/%s/reviews" % task["id"], {}) self.assertEqual(status, 401) status, _body = self._req( "GET", "/tasks/%s/reviews" % task["id"], None) self.assertEqual(status, 401) status, _body = self._req( "POST", "/tasks/%s/reviews" % task["id"], {}, self.outsider_key) self.assertEqual(status, 403) status, _body = self._req( "GET", "/tasks/%s/reviews" % task["id"], None, self.outsider_key) self.assertEqual(status, 403) _status, review = self._req( "POST", "/tasks/%s/reviews" % task["id"], {}, self.owner_key) status, _body = self._req( "POST", "/tasks/%s/reviews/%s/findings" % ( task["id"], review["id"]), {"findings": "x"}, self.outsider_key) self.assertEqual(status, 403) def test_task_not_found(self): status, _body = self._req( "POST", "/tasks/no-such-task/reviews", {}, self.owner_key) self.assertEqual(status, 404) status, _body = self._req( "GET", "/tasks/no-such-task/reviews", None, self.owner_key) self.assertEqual(status, 404) if __name__ == "__main__": unittest.main()