Files
codex-app-server/cloud/tests/test_longfunc_remaining.py
T
host 488aedbcad code-to10 v6 A14 batch2: test E402/F401 cleanup plus touched-file residual E,F cleanup
(cherry picked from commit 1db02545f74c96b3b92be4fc32954659cec1f814)

Reconcile-conflict: A14b E402 imports referencing cloud.handler_core (does not exist here; core methods live in server.py) — kept HEAD imports with incoming's noqa: E402 markers, dropped the unused core_mod import (verified unused elsewhere in both files).
2026-11-03 02:49:26 +00:00

208 lines
8.8 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""剩余长函数小提取单测:只钉住新抽出的纯块,不跑真实模型回合、
不联网、不连生产密钥。
覆盖(均取自原函数逐字逻辑):
- webhook:评论解析的 review 回退、fix-review prompt 模板与 2000 字截断;
- followup:请求体校验的成功/错误串、/goal 前置判定的各分支;
- deliver:非 goal 门禁(goal 任务 409、merged 409),以及关键语义——
门禁绝不拦截终态任务(终态走下面的 CAS 复活,不是 409);
- chat_history:秒->毫秒换算、用户条目、链路条目(fu- 回声跳过、
无 ts 贴上一条有 ts 的条目之后且 ts_ms 为 null)。
原路由行为不变由已有的 webhook / followup / thread / chat_history
套件覆盖,本文件不重复测路由,只测抽出子块本身。
"""
import sys
import unittest
from pathlib import Path
try:
from cloud.server import (
_chat_history_trace_entries,
_chat_history_user_entries,
_classify_goal_followup,
_parse_github_comment_with_review_fallback,
_seconds_to_ms,
_thread_non_goal_guard,
_validate_followup_body,
_webhook_fix_review_prompt,
)
except ImportError: # discover -s cloud/tests 时把项目根目录补进 sys.path
sys.path.insert(0, str(Path(__file__).resolve().parents[2]))
from cloud.server import (
_chat_history_trace_entries,
_chat_history_user_entries,
_classify_goal_followup,
_parse_github_comment_with_review_fallback,
_seconds_to_ms,
_thread_non_goal_guard,
_validate_followup_body,
_webhook_fix_review_prompt,
)
def _task(**over):
task = {"status": "running", "goal_mode": False,
"verify_command": None, "plan": None,
"goal": None, "prompt": "p"}
task.update(over)
return task
class WebhookExtractionTestCase(unittest.TestCase):
def test_review_payload_verbatim_semantics(self):
# 原样语义(注意,不是“修”成 7):现行 parser 里 issue 缺失
# 并不使首解返回 None(只使 pr_number 为 None),故回退块
# 在当前 parser 下不可达;抽出原样保留该块,不擅自改行为。
payload = {
"action": "created",
"comment": {"body": "/fix-review 请修", "user": {"login": "a"}},
"pull_request": {"number": 7},
"repository": {"full_name": "o/r"},
}
result = _parse_github_comment_with_review_fallback(payload)
self.assertEqual(result["pr_number"], None)
self.assertEqual(result["comment"], "/fix-review 请修")
self.assertIsNone(_parse_github_comment_with_review_fallback(
{"action": "edited",
"comment": {"body": "/fix-review"},
"pull_request": {"number": 7}}))
def test_issue_comment_direct_and_non_trigger(self):
direct = {
"action": "created",
"comment": {"body": "/fix-review", "user": {"login": "a"}},
"issue": {"number": 3},
"repository": {"full_name": "o/r"},
}
self.assertEqual(
_parse_github_comment_with_review_fallback(direct)["pr_number"],
3)
self.assertIsNone(_parse_github_comment_with_review_fallback(
{"action": "created", "comment": {"body": "hello"}}))
def test_fix_review_prompt_truncates_comment(self):
result = {"repo": "o/r", "pr_number": 9, "author": "a",
"comment": "x" * 2500}
prompt = _webhook_fix_review_prompt(result)
self.assertTrue(prompt.startswith(
"[webhook /fix-review] repo=o/r pr=9 author=a\n"))
self.assertEqual(len(prompt.split("\n", 1)[1]), 2000)
class FollowupBodyTestCase(unittest.TestCase):
def test_ok_strips_and_defaults_queued(self):
text, mode, err = _validate_followup_body({"body": " hi "})
self.assertEqual((text, mode, err), ("hi", "queued", None))
def test_direct_mode_kept(self):
text, mode, err = _validate_followup_body(
{"body": "hi", "mode": "direct"})
self.assertEqual((text, mode, err), ("hi", "direct", None))
def test_body_required_variants(self):
for body in ({}, {"body": ""}, {"body": " "}, {"body": 123}):
_t, _m, err = _validate_followup_body(body)
self.assertEqual(err, (400, {"error": "body required"}))
def test_too_long_and_bad_mode(self):
_t, _m, err = _validate_followup_body({"body": "x" * 20001})
self.assertEqual(err, (400, {"error": "body too long"}))
_t, _m, err = _validate_followup_body(
{"body": "hi", "mode": "weird"})
self.assertEqual(err, (400, {
"error": "mode must be queued or direct"}))
class GoalClassifyTestCase(unittest.TestCase):
def test_goal_empty_and_mainline_and_plan(self):
self.assertEqual(
_classify_goal_followup(_task(), ""),
("error", 400, {"error": "goal 目标不能为空"}))
action, code, payload = _classify_goal_followup(
_task(goal_mode=True, goal="g"), "新目标")
self.assertEqual((action, code), ("error", 409))
self.assertEqual(payload["error"], "主线已有 goal,/goal 不会重复创建")
self.assertEqual(payload["goal"], "g")
self.assertEqual(
_classify_goal_followup(_task(plan="p"), "新目标"),
("error", 400, {"error": "plan 模式与 goal 模式冲突"}))
def test_goal_merged_queued_terminal_running(self):
self.assertEqual(
_classify_goal_followup(_task(status="merged"), "新目标")[0],
"error")
self.assertEqual(
_classify_goal_followup(_task(status="queued"), "新目标")[0],
"convert_queued")
self.assertEqual(
_classify_goal_followup(_task(status="completed"), "新目标")[0],
"revive_terminal")
self.assertEqual(
_classify_goal_followup(_task(status="running"), "新目标")[0],
"continue")
def test_non_goal_only_queued_or_running_continues(self):
self.assertEqual(
_classify_goal_followup(_task(status="queued"), None)[0],
"continue")
action, code, payload = _classify_goal_followup(
_task(status="completed"), None)
self.assertEqual((action, code), ("error", 409))
self.assertEqual(payload["error"], "后续消息仅支持进行中任务")
class ThreadGuardTestCase(unittest.TestCase):
def test_goal_task_and_merged_blocked(self):
self.assertEqual(
_thread_non_goal_guard(_task(verify_command="v")),
(409, {"error": "goal 任务暂不支持后续消息"}))
self.assertEqual(
_thread_non_goal_guard(_task(status="merged"))[0], 409)
def test_terminal_not_blocked_by_guard(self):
# 关键:门禁只拦 goal/merged,终态任务必须放行到 CAS 复活。
self.assertIsNone(_thread_non_goal_guard(_task(status="completed")))
self.assertIsNone(_thread_non_goal_guard(_task(status="running")))
class ChatHistoryExtractionTestCase(unittest.TestCase):
def test_seconds_to_ms(self):
self.assertEqual(_seconds_to_ms(1.5), 1500)
self.assertEqual(_seconds_to_ms(0), 0)
self.assertIsNone(_seconds_to_ms(None))
self.assertIsNone(_seconds_to_ms("bad"))
def test_user_entries(self):
task = {"prompt": "做一件事", "created_at": 10.0}
cont = [{"id": "c1", "task_id": "t", "project_id": "p",
"body": "继续", "created_by": "u", "created_at": 11.0}]
fu = [{"id": "f1", "task_id": "t", "project_id": "p",
"body": "后续", "mode": "queued", "status": "pending",
"created_by": "u", "sender_task_id": None,
"created_at": 12.0, "updated_at": 12.0}]
entries = _chat_history_user_entries(task, cont, fu)
self.assertEqual(
[e[2]["id"] for e in entries],
["task-prompt", "continue-c1", "followup-f1"])
self.assertEqual(entries[0][2]["ts_ms"], 10000)
self.assertEqual(entries[2][2]["label"], "后续消息")
def test_trace_entries_skip_echo_and_inherit_ts(self):
entries = _chat_history_trace_entries([
{"id": "fu-abc", "kind": "message", "text": "回声"},
"not-a-dict",
{"id": "m1", "kind": "message", "text": "你好", "ts": 2000},
{"id": "m2", "kind": "message", "text": "无 ts"},
], start_ms=1000)
self.assertEqual([e[2]["id"] for e in entries], ["m1", "m2"])
self.assertEqual(entries[0][1], 2000)
self.assertEqual(entries[0][2]["ts_ms"], 2000)
self.assertEqual(entries[1][1], 2000) # 贴在上一条有 ts 之后
self.assertIsNone(entries[1][2]["ts_ms"])
if __name__ == "__main__":
unittest.main()