Files
FreeCADWasmDev/tools/test_scheduler_round_report.py

102 lines
6.1 KiB
Python

import contextlib
import io
import json
from pathlib import Path
import tempfile
import unittest
import scheduler_round_report as reports
class RoundReportTests(unittest.TestCase):
def setUp(self):
directory = tempfile.TemporaryDirectory()
self.addCleanup(directory.cleanup)
self.root = Path(directory.name)
self.current = self.root / "docs/current"
self.current.mkdir(parents=True)
self.batch = {"status": "ready", "batchId": "next", "selected": {"module": "Part"},
"records": [["Part:desktop:Next", "下一项建模任务"]]}
reports.write(self.current / "module-batch-manifest.json", self.batch)
reports.write(self.current / "business-acceptance.json", {"records": {"finished": {
"evidencePath": "docs/current/business-evidence/example.json"}}})
def event(self, **extra):
return {"cycle": 10, "module": "Web", "outcome": "source_edits_pending_outcome", "seconds": 12,
"firstBusinessPassIds": [], "fileChanges": 2, "reason": "已接线,仍缺保存恢复", "at": 10, **extra}
def update(self, event, status):
reports.write(self.current / "scheduler-progress.json", {"history": [event]})
stream = io.StringIO()
with contextlib.redirect_stdout(stream):
result = reports.refresh(status, root=self.root, force=True)
return result, stream.getvalue()
def test_partial_result_is_not_reported_as_pass_and_no_next_task_is_invented(self):
result, output = self.update(self.event(), {"cycle": 10, "status": "cycle_complete"})
self.assertEqual(result["lastCompleted"]["result"], "已修改,整项验收未完成")
self.assertIsNone(result["nextTask"])
self.assertIn("待调度确认", output)
self.assertEqual(reports.read(self.root / "web/public/execution-status.json")["lastCompleted"]["cycle"], 10)
def test_confirmed_next_task_updates_without_repeating_completion_notice(self):
event = self.event()
self.update(event, {"cycle": 10})
status = {"cycle": 11, "status": "codex_turn_running", "moduleBatchId": "next"}
result, output = self.update(event, status)
self.assertEqual(result["nextTask"]["tasks"], ["下一项建模任务"])
self.assertIn("接续任务已确认", output)
self.assertNotIn("轮结束", output)
self.assertEqual(self.update(event, status)[1], "")
def test_wrong_batch_and_preparation_are_not_confirmed_tasks(self):
for status in [{"cycle": 11, "status": "codex_turn_running", "moduleBatchId": "stale"},
{"cycle": 11, "status": "preparing", "moduleBatchId": "next"}]:
self.assertIsNone(self.update(self.event(), status)[0]["nextTask"])
def test_first_pass_links_evidence_and_never_changes_acceptance_ledger(self):
ledger = self.current / "business-acceptance.json"
before = ledger.read_bytes()
result, _ = self.update(self.event(outcome="progress", firstBusinessPassIds=["finished"]), {"cycle": 10})
self.assertEqual(result["lastCompleted"]["result"], "新增整项通过 1 项")
self.assertEqual(result["lastCompleted"]["evidence"][0]["id"], "finished")
self.assertEqual(ledger.read_bytes(), before)
def test_provider_wait_and_evidence_refresh_do_not_claim_delivery(self):
self.assertIn("等待", reports.completed_row(self.event(outcome="provider_wait"), {})["result"])
self.assertEqual(reports.completed_row(self.event(outcome="progress", evidenceRefreshOnly=True), {})["result"],
"证据刷新,无新增整项交付")
def test_maintenance_round_joins_timeline_with_its_real_outcome(self):
path = "docs/current/periodic-optimization/check-result.json"
reports.write(self.root / path, {"runId": "check", "providerFailed": True, "turnCompleted": False,
"summary": "等待服务恢复", "elapsedSeconds": 5})
reports.write(self.current / "scheduler-maintenance.json", {"lastLog": "docs/current/continuation-runs/cycle-000011.log",
"lastResultPath": path})
result, _ = self.update(self.event(), {"cycle": 12, "status": "codex_turn_running", "moduleBatchId": "next"})
self.assertEqual(result["lastCompleted"]["cycle"], 11)
self.assertEqual(result["lastCompleted"]["outcome"], "provider_wait")
self.assertEqual(result["nextTask"]["cycle"], 12)
def test_budget_bounded_maintenance_with_a_real_repair_is_not_reported_as_empty(self):
path = "docs/current/periodic-optimization/repair-result.json"
reports.write(self.root / path, {"runId": "repair", "providerFailed": False, "turnCompleted": False,
"summary": "修复并验证表达式回滚", "fileChanges": 2, "elapsedSeconds": 5})
reports.write(self.current / "scheduler-maintenance.json", {"lastLog": "docs/current/continuation-runs/cycle-000011.log",
"lastResultPath": path})
result, _ = self.update(self.event(), {"cycle": 12, "status": "codex_turn_running", "moduleBatchId": "next"})
self.assertEqual(result["lastCompleted"]["result"], "巡检有修改,完成情况见说明")
def test_historical_agent_result_is_kept_separate_from_pipeline_blocker(self):
log = self.current / "continuation-runs/cycle-000010.log"
log.parent.mkdir()
log.write_text(json.dumps({"type": "item.completed", "item": {"type": "agent_message", "text": "已补任务取消,保存恢复尚未完成"}}) + "\n")
result, _ = self.update(self.event(reason=json.dumps({"status": "acceptance_required", "coveredIds": ["unrelated"]})), {"cycle": 11})
self.assertEqual(result["lastCompleted"]["detail"], "已补任务取消,保存恢复尚未完成")
self.assertIn("尚未齐备", result["lastCompleted"]["remaining"])
self.assertNotIn("unrelated", result["lastCompleted"]["remaining"])
if __name__ == "__main__":
unittest.main()