test_api.py 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154
  1. from copy import deepcopy
  2. from fastapi.testclient import TestClient
  3. from app import main
  4. client = TestClient(main.app)
  5. def test_v8_execution_round_activity_and_artifact_endpoints(monkeypatch, current_bundle):
  6. bundle = deepcopy(current_bundle)
  7. monkeypatch.setattr(main.provider, "load_bundle", lambda _build_id: deepcopy(bundle))
  8. monkeypatch.setattr(main.provider, "load_current_artifact", lambda _build_id: deepcopy(bundle["currentArtifact"]))
  9. monkeypatch.setattr(main.provider, "list_builds", lambda **_kwargs: [{"id": "88001", "status": "running"}])
  10. execution = client.get("/api/script-builds/88001/execution-view")
  11. assert execution.status_code == 200
  12. assert execution.json()["schemaVersion"] == "8"
  13. round_detail = client.get("/api/script-builds/88001/rounds/1")
  14. assert round_detail.status_code == 200
  15. assert round_detail.json()["businessSections"]
  16. data_detail = client.get("/api/script-builds/88001/activities/data-decision:11")
  17. assert data_detail.status_code == 200
  18. assert data_detail.json()["detailKind"] == "agent-decision"
  19. assert data_detail.json()["decisionType"] == "tradeoff"
  20. assert "明确取舍" in [block["title"] for block in data_detail.json()["blocks"]]
  21. artifact = client.get("/api/script-builds/88001/artifacts/base:current")
  22. assert artifact.status_code == 200
  23. assert artifact.json()["changes"]["exactness"] == "current-only"
  24. assert artifact.json()["scriptTable"]["counts"] == {"paragraphs": 3, "elements": 2, "links": 1}
  25. round_artifact = client.get("/api/script-builds/88001/artifacts/artifact:round:1")
  26. assert round_artifact.status_code == 200
  27. assert round_artifact.json()["artifactContext"] == {
  28. "type": "round",
  29. "roundIndex": 1,
  30. "versionKind": "current-base",
  31. "note": "当前数据库未保存第 1 轮结束时的独立快照;这里展示当前保存的主脚本。",
  32. "exactness": "current-only",
  33. }
  34. candidate = client.get("/api/script-builds/88001/artifacts/artifact:branch:1")
  35. assert candidate.status_code == 200
  36. assert candidate.json()["artifactContext"] == {
  37. "type": "candidate",
  38. "roundIndex": 1,
  39. "branchId": 1,
  40. "branchStatus": "merged",
  41. "pathType": "内容",
  42. "versionKind": "saved-at-disposition",
  43. "historicalAccuracy": "exact",
  44. "currentProjectionAccuracy": "not-applicable",
  45. "note": "测试候选版本",
  46. "exactness": "historical-snapshot",
  47. }
  48. domain = client.get("/api/script-builds/88001/artifacts/artifact:branch:2")
  49. assert domain.status_code == 404
  50. assert "领域信息方案" in domain.json()["detail"]
  51. def test_terminal_branch_without_snapshot_does_not_fake_a_candidate(monkeypatch, current_bundle):
  52. bundle = deepcopy(current_bundle)
  53. branch = bundle["branches"][0]
  54. branch["candidate_snapshot"] = {
  55. "snapshot": None,
  56. "snapshotKind": "missing-disposition-snapshot",
  57. "historicalAccuracy": "missing",
  58. "currentProjectionAccuracy": "not-applicable",
  59. }
  60. monkeypatch.setattr(main.provider, "load_bundle", lambda _build_id: deepcopy(bundle))
  61. response = client.get("/api/script-builds/88001/artifacts/artifact:branch:1")
  62. assert response.status_code == 404
  63. assert "没有可用的候选脚本快照" in response.json()["detail"]
  64. def test_event_body_is_loaded_only_from_activity_endpoint(monkeypatch, current_bundle):
  65. full_report = "评审原文" * 8_000
  66. monkeypatch.setattr(main.provider, "load_bundle", lambda _build_id: deepcopy(current_bundle))
  67. monkeypatch.setattr(main.provider, "load_event_detail", lambda _build_id, _event_id: {
  68. "id": 41,
  69. "event_name": "script_evaluator",
  70. "title": "整体评审",
  71. "input": {"content": "评审第1轮"},
  72. "output": {"content": f"整体结论:部分通过\n{full_report}"},
  73. })
  74. execution = client.get("/api/script-builds/88001/execution-view").json()
  75. assert "评审第1轮" not in str(execution)
  76. detail = client.get("/api/script-builds/88001/activities/event:41")
  77. assert detail.status_code == 200
  78. payload = detail.json()
  79. assert payload["detailKind"] == "agent-decision"
  80. assert payload["decisionType"] == "evaluation"
  81. assert any(
  82. block.get("title") == "总结论" and block.get("value") == "部分通过"
  83. for block in payload["blocks"]
  84. )
  85. assert "script_evaluator" not in str(payload["blocks"])
  86. assert full_report in payload["technical"]["output"]["content"]
  87. assert "[truncated" not in payload["technical"]["output"]["content"]
  88. def test_removed_fixture_and_raw_log_routes_are_not_exposed():
  89. assert client.get("/api/fixtures/current-schema/execution-view").status_code == 404
  90. assert client.get("/api/script-builds/1/raw-log").status_code == 404
  91. def test_capabilities_only_advertise_new_db_contract():
  92. response = client.get("/api/capabilities")
  93. assert response.status_code == 200
  94. payload = response.json()
  95. assert payload["schemaVersion"] == "8"
  96. assert payload["readOnly"] is True
  97. assert "script_build_multipath_decision" in payload["businessSources"]
  98. assert payload["runtimeSupplement"] == ["script_build_event", "script_build_event_body"]
  99. def test_prompt_endpoint_marks_current_rules_as_not_a_run_snapshot(monkeypatch):
  100. monkeypatch.setattr(
  101. main.provider,
  102. "load_prompt_context",
  103. lambda _build_id, _prompt_ref: {
  104. "promptRef": "prompt:event:50",
  105. "actor": {"role": "implementer", "label": "实现 Agent"},
  106. "task": "补齐烹饪步骤",
  107. "promptContent": "当前规则\nAuthorization: Bearer secret-token",
  108. "promptSource": "current-db",
  109. "promptVersion": 8,
  110. },
  111. )
  112. response = client.get(
  113. "/api/script-builds/88001/decision-prompts/prompt:event:50"
  114. )
  115. assert response.status_code == 200
  116. payload = response.json()
  117. assert payload["exactTask"] == {
  118. "content": "补齐烹饪步骤",
  119. "accuracy": "run-exact",
  120. }
  121. assert payload["systemPrompt"]["source"] == "current-db"
  122. assert payload["systemPrompt"]["accuracy"] == "current-not-run-snapshot"
  123. assert "secret-token" not in str(payload)
  124. assert any(
  125. item["code"] == "PROMPT_NOT_SNAPSHOTTED"
  126. for item in payload["notices"]
  127. )