Harden VedAstro adapter and index PyJHora artifacts
This commit is contained in:
@@ -0,0 +1,131 @@
|
||||
# Antigravity AI Sidecar Work Order - Round 39 - 2026-06-27
|
||||
|
||||
## Mission
|
||||
|
||||
本轮继续只服务印度占星 `skill` 主线,不做网页/app 外观工作,不改核心源码。目标是替主线程进一步压缩“全球第一 skill”还缺的硬情报,尤其围绕:
|
||||
|
||||
1. PyJHora black-box 资产继续扩充
|
||||
2. VedAstro service adapter 对接前的字段映射与公开契约核查
|
||||
3. Dasha / Shadbala / Tajika 三大战线的真实 external oracle 首包之后的第二批优先级
|
||||
4. 文章级解释模板的权威降噪
|
||||
5. 长期 benchmark 护城河的公开字段设计
|
||||
|
||||
## Hard Constraints
|
||||
|
||||
- 不修改主仓核心实现代码。
|
||||
- 只做研究、检索、黑盒比对、任务拆解、资料整理。
|
||||
- 必须优先复用现有 `docs/research/`、`references/oracle/`、`references/open_source_sources/` 资料。
|
||||
- AGPL / GPL 只允许 black-box、stdout、字段映射、行为观察,不允许建议复制代码实现。
|
||||
- 每份报告必须能直接减少 Codex 下一轮的上下文浪费。
|
||||
|
||||
## Work Packages
|
||||
|
||||
### A. PyJHora black-box asset expansion board
|
||||
|
||||
目标:
|
||||
|
||||
- 基于现有 `references/oracle/artifacts/` 和 `pending_packets/`,列出下一批最值得补抓的 12 个 PyJHora black-box 资产。
|
||||
- 优先级偏向:
|
||||
- Chara Dasha
|
||||
- Kalachakra Dasha
|
||||
- Ashtakavarga / SAV / BAV
|
||||
- Bhava Bala
|
||||
- Special Lagnas
|
||||
- Vargottama / Pushkara / Avastha 边界例子
|
||||
|
||||
输出:
|
||||
- `antigravity_round39_pyjhora_blackbox_asset_expansion_board_2026_06_27.md`
|
||||
|
||||
### B. VedAstro request/response mapping audit
|
||||
|
||||
目标:
|
||||
|
||||
- 只研究 VedAstro 作为 service adapter candidate 时,哪些字段和我们本地 contract 已对齐,哪些还缺中间映射层。
|
||||
- 明确:
|
||||
- ayanamsa policy
|
||||
- node policy
|
||||
- body naming
|
||||
- divisional chart identifiers
|
||||
- dasha naming
|
||||
- timezone / DST expectations
|
||||
|
||||
输出:
|
||||
- `antigravity_round39_vedastro_mapping_audit_2026_06_27.md`
|
||||
|
||||
### C. Three-front second-wave packet rerank
|
||||
|
||||
目标:
|
||||
|
||||
- 在 Dasha / Shadbala / Tajika 三大战线里,不再讨论 first packet,而是排出 second-wave 的前 15 个 packet。
|
||||
- 每个 packet 说明:
|
||||
- 为什么它是 second-wave 高 ROI
|
||||
- 需要人类录入还是可以先靠公开书例
|
||||
- 对 skill 精度和“全球第一”目标的实际贡献
|
||||
|
||||
输出:
|
||||
- `antigravity_round39_three_front_second_wave_packets_2026_06_27.md`
|
||||
|
||||
### D. Authority-first article template truth cleanup
|
||||
|
||||
目标:
|
||||
|
||||
- 把近几轮用户补充的文章、截图线索里,容易误导的说法筛一遍。
|
||||
- 特别检查:
|
||||
- 财富点 / Lakshmi / Dhana
|
||||
- DK / UL / spouse elevation
|
||||
- 上升度数 / 紧密合相
|
||||
- Nakshatra 天赋标签
|
||||
- 工作时间预测
|
||||
|
||||
输出:
|
||||
- `antigravity_round39_article_truth_cleanup_2026_06_27.md`
|
||||
|
||||
### E. Public benchmark schema v2 board
|
||||
|
||||
目标:
|
||||
|
||||
- 设计一个别人难以短期追上的公开 benchmark 字段体系。
|
||||
- 不是产品方案,而是字段和数据制度方案:
|
||||
- case metadata
|
||||
- external evidence provenance
|
||||
- tolerance policy
|
||||
- ayanamsa / node / timezone normalization
|
||||
- explanation-template scoring
|
||||
|
||||
输出:
|
||||
- `antigravity_round39_public_benchmark_schema_v2_2026_06_27.md`
|
||||
|
||||
### F. Codex direct top 80
|
||||
|
||||
目标:
|
||||
|
||||
- 基于以上 5 包,为主线程输出只服务 skill 的 Top 80 下一步动作。
|
||||
- 每条动作必须标记:
|
||||
- `doc_freeze`
|
||||
- `tdd_now`
|
||||
- `needs_human_oracle`
|
||||
- `blocked_by_license`
|
||||
|
||||
输出:
|
||||
- `antigravity_round39_codex_round40_skill_top80_2026_06_27.md`
|
||||
|
||||
## Deliverables
|
||||
|
||||
至少生成以下 6 个文件到 `docs/research/`:
|
||||
|
||||
1. `antigravity_round39_pyjhora_blackbox_asset_expansion_board_2026_06_27.md`
|
||||
2. `antigravity_round39_vedastro_mapping_audit_2026_06_27.md`
|
||||
3. `antigravity_round39_three_front_second_wave_packets_2026_06_27.md`
|
||||
4. `antigravity_round39_article_truth_cleanup_2026_06_27.md`
|
||||
5. `antigravity_round39_public_benchmark_schema_v2_2026_06_27.md`
|
||||
6. `antigravity_round39_codex_round40_skill_top80_2026_06_27.md`
|
||||
|
||||
## Success Standard
|
||||
|
||||
- 不重复“还有很多 gap”这种空话。
|
||||
- 每份报告都能直接节省 Codex 下一轮时间。
|
||||
- 明确区分:
|
||||
- 现在可直接编码
|
||||
- 现在只能文档冻结
|
||||
- 必须等待人类 external oracle
|
||||
- 受许可证隔离,只能 black-box
|
||||
@@ -0,0 +1,76 @@
|
||||
{
|
||||
"scope": "pyjhora_oracle_artifact_manifest",
|
||||
"generated_at": "2026-06-27T06:25:10.958318+00:00",
|
||||
"artifact_count": 9,
|
||||
"packet_count": 8,
|
||||
"artifacts": [
|
||||
"pyjhora_extreme_latitude_kp_shadbala_stdout_20260627.txt",
|
||||
"pyjhora_redacted_place_shadbala_raman_stdout_20260627.txt",
|
||||
"pyjhora_historical_epoch_dasha_stdout_20260627.txt",
|
||||
"pyjhora_oracle_artifact_manifest.json",
|
||||
"pyjhora_steve_jobs_dasha_stdout_20260627.txt",
|
||||
"pyjhora_steve_jobs_shadbala_lahiri_stdout_20260627.txt",
|
||||
"pyjhora_steve_jobs_varshaphala_1984_lahiri_stdout_20260627.txt",
|
||||
"pyjhora_user_REDACTED_YEAR_dasha_stdout_20260627.txt",
|
||||
"pyjhora_user_REDACTED_YEAR_shadbala_lahiri_stdout_20260627.txt"
|
||||
],
|
||||
"pending_packets": [
|
||||
"external_template_extreme_latitude_kp_pyjhora_20260627.json",
|
||||
"external_template_redacted_place_shadbala_raman_pyjhora_20260627.json",
|
||||
"external_template_historical_epoch_lahiri_pyjhora_20260627.json",
|
||||
"external_template_steve_jobs_dasha_lahiri_pyjhora_20260627.json",
|
||||
"external_template_steve_jobs_shadbala_lahiri_pyjhora_20260627.json",
|
||||
"external_template_steve_jobs_varshaphala_1984_lahiri_pyjhora_20260627.json",
|
||||
"external_template_user_REDACTED_YEAR_moon_longitude_lahiri_pyjhora_20260627.json",
|
||||
"external_template_user_REDACTED_YEAR_shadbala_lahiri_pyjhora_20260627.json"
|
||||
],
|
||||
"fronts": {
|
||||
"dasha": {
|
||||
"artifact_count": 4,
|
||||
"packet_count": 3,
|
||||
"artifacts": [
|
||||
"pyjhora_historical_epoch_dasha_stdout_20260627.txt",
|
||||
"pyjhora_oracle_artifact_manifest.json",
|
||||
"pyjhora_steve_jobs_dasha_stdout_20260627.txt",
|
||||
"pyjhora_user_REDACTED_YEAR_dasha_stdout_20260627.txt"
|
||||
],
|
||||
"pending_packets": [
|
||||
"external_template_extreme_latitude_kp_pyjhora_20260627.json",
|
||||
"external_template_historical_epoch_lahiri_pyjhora_20260627.json",
|
||||
"external_template_steve_jobs_dasha_lahiri_pyjhora_20260627.json"
|
||||
]
|
||||
},
|
||||
"shadbala": {
|
||||
"artifact_count": 4,
|
||||
"packet_count": 4,
|
||||
"artifacts": [
|
||||
"pyjhora_extreme_latitude_kp_shadbala_stdout_20260627.txt",
|
||||
"pyjhora_redacted_place_shadbala_raman_stdout_20260627.txt",
|
||||
"pyjhora_steve_jobs_shadbala_lahiri_stdout_20260627.txt",
|
||||
"pyjhora_user_REDACTED_YEAR_shadbala_lahiri_stdout_20260627.txt"
|
||||
],
|
||||
"pending_packets": [
|
||||
"external_template_redacted_place_shadbala_raman_pyjhora_20260627.json",
|
||||
"external_template_steve_jobs_shadbala_lahiri_pyjhora_20260627.json",
|
||||
"external_template_user_REDACTED_YEAR_moon_longitude_lahiri_pyjhora_20260627.json",
|
||||
"external_template_user_REDACTED_YEAR_shadbala_lahiri_pyjhora_20260627.json"
|
||||
]
|
||||
},
|
||||
"tajika_sahams": {
|
||||
"artifact_count": 1,
|
||||
"packet_count": 1,
|
||||
"artifacts": [
|
||||
"pyjhora_steve_jobs_varshaphala_1984_lahiri_stdout_20260627.txt"
|
||||
],
|
||||
"pending_packets": [
|
||||
"external_template_steve_jobs_varshaphala_1984_lahiri_pyjhora_20260627.json"
|
||||
]
|
||||
}
|
||||
},
|
||||
"files": {
|
||||
"manifest": "references/oracle/artifacts/pyjhora_oracle_artifact_manifest.json",
|
||||
"artifacts_dir": "references/oracle/artifacts",
|
||||
"pending_packets_dir": "references/oracle/artifacts/pending_packets"
|
||||
},
|
||||
"boundary": "These files are black-box external evidence only. They document PyJHora outputs and pending oracle packets without importing AGPL code into the local skill implementation."
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Generate a tracked manifest for PyJHora black-box oracle artifacts."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
from collections import defaultdict
|
||||
from datetime import datetime, timezone
|
||||
from pathlib import Path
|
||||
from typing import Any
|
||||
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[1]
|
||||
ARTIFACTS_DIR = ROOT / "references" / "oracle" / "artifacts"
|
||||
PENDING_DIR = ARTIFACTS_DIR / "pending_packets"
|
||||
OUTPUT_PATH = ARTIFACTS_DIR / "pyjhora_oracle_artifact_manifest.json"
|
||||
|
||||
|
||||
def _front_for_name(name: str) -> str:
|
||||
lowered = name.lower()
|
||||
if "varshaphala" in lowered or "sahams" in lowered or "tajika" in lowered:
|
||||
return "tajika_sahams"
|
||||
if "shadbala" in lowered or "moon_longitude" in lowered:
|
||||
return "shadbala"
|
||||
return "dasha"
|
||||
|
||||
|
||||
def _relative(path: Path) -> str:
|
||||
return str(path.relative_to(ROOT))
|
||||
|
||||
|
||||
def build_manifest() -> dict[str, Any]:
|
||||
artifacts = sorted(
|
||||
path for path in ARTIFACTS_DIR.glob("pyjhora_*")
|
||||
if path.is_file()
|
||||
)
|
||||
pending_packets = sorted(
|
||||
path for path in PENDING_DIR.glob("*pyjhora_20260627.json")
|
||||
if path.is_file()
|
||||
)
|
||||
|
||||
fronts: dict[str, dict[str, Any]] = defaultdict(lambda: {"artifact_count": 0, "packet_count": 0, "artifacts": [], "pending_packets": []})
|
||||
for artifact in artifacts:
|
||||
front = _front_for_name(artifact.name)
|
||||
fronts[front]["artifact_count"] += 1
|
||||
fronts[front]["artifacts"].append(artifact.name)
|
||||
for packet in pending_packets:
|
||||
front = _front_for_name(packet.name)
|
||||
fronts[front]["packet_count"] += 1
|
||||
fronts[front]["pending_packets"].append(packet.name)
|
||||
|
||||
report = {
|
||||
"scope": "pyjhora_oracle_artifact_manifest",
|
||||
"generated_at": datetime.now(timezone.utc).isoformat(),
|
||||
"artifact_count": len(artifacts),
|
||||
"packet_count": len(pending_packets),
|
||||
"artifacts": [path.name for path in artifacts],
|
||||
"pending_packets": [path.name for path in pending_packets],
|
||||
"fronts": {
|
||||
front: {
|
||||
"artifact_count": payload["artifact_count"],
|
||||
"packet_count": payload["packet_count"],
|
||||
"artifacts": payload["artifacts"],
|
||||
"pending_packets": payload["pending_packets"],
|
||||
}
|
||||
for front, payload in sorted(fronts.items())
|
||||
},
|
||||
"files": {
|
||||
"manifest": _relative(OUTPUT_PATH),
|
||||
"artifacts_dir": _relative(ARTIFACTS_DIR),
|
||||
"pending_packets_dir": _relative(PENDING_DIR),
|
||||
},
|
||||
"boundary": (
|
||||
"These files are black-box external evidence only. They document PyJHora outputs and "
|
||||
"pending oracle packets without importing AGPL code into the local skill implementation."
|
||||
),
|
||||
}
|
||||
OUTPUT_PATH.write_text(json.dumps(report, ensure_ascii=False, indent=2) + "\n", encoding="utf-8")
|
||||
return report
|
||||
|
||||
|
||||
def main() -> int:
|
||||
print(json.dumps(build_manifest(), ensure_ascii=False, indent=2))
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
@@ -11,6 +11,7 @@ from __future__ import annotations
|
||||
import argparse
|
||||
import json
|
||||
import os
|
||||
import socket
|
||||
from pathlib import Path
|
||||
from typing import Any
|
||||
from urllib import request, error
|
||||
@@ -59,6 +60,7 @@ PARITY_CASES = {
|
||||
}
|
||||
|
||||
DEFAULT_TIMEOUT_SECONDS = 8
|
||||
TIMEOUT_ENV = "VEDASTRO_TIMEOUT_SECONDS"
|
||||
RETRY_POLICY = {
|
||||
"max_attempts": 2,
|
||||
"backoff_seconds": 1,
|
||||
@@ -67,6 +69,16 @@ RETRY_POLICY = {
|
||||
ALLOW_NETWORK_ENV = "VEDASTRO_ENABLE_NETWORK"
|
||||
|
||||
|
||||
def _timeout_seconds() -> float:
|
||||
raw = os.environ.get(TIMEOUT_ENV, "").strip()
|
||||
if not raw:
|
||||
return DEFAULT_TIMEOUT_SECONDS
|
||||
try:
|
||||
return float(raw)
|
||||
except ValueError:
|
||||
return DEFAULT_TIMEOUT_SECONDS
|
||||
|
||||
|
||||
def schema() -> dict[str, Any]:
|
||||
request_example = {
|
||||
**PARITY_CASES["beijing_first_use_demo"],
|
||||
@@ -130,7 +142,7 @@ def _unconfigured(reason: str) -> dict[str, Any]:
|
||||
"endpoint_env": "VEDASTRO_API_ENDPOINT",
|
||||
"api_key_env": "VEDASTRO_API_KEY",
|
||||
"provenance_mode": "external_service_candidate",
|
||||
"timeout_seconds": DEFAULT_TIMEOUT_SECONDS,
|
||||
"timeout_seconds": _timeout_seconds(),
|
||||
"retry_policy": RETRY_POLICY,
|
||||
},
|
||||
}
|
||||
@@ -156,7 +168,7 @@ def _normalize_success(payload: dict[str, Any], endpoint: str) -> dict[str, Any]
|
||||
"transport": "http_json_service_boundary",
|
||||
"endpoint": endpoint,
|
||||
"provenance_mode": "external_service_candidate",
|
||||
"timeout_seconds": DEFAULT_TIMEOUT_SECONDS,
|
||||
"timeout_seconds": _timeout_seconds(),
|
||||
"retry_policy": RETRY_POLICY,
|
||||
**(payload.get("source_metadata") or {}),
|
||||
},
|
||||
@@ -189,7 +201,7 @@ def run_case(case_id: str) -> dict[str, Any]:
|
||||
"transport": "http_json_service_boundary",
|
||||
"endpoint": endpoint,
|
||||
"provenance_mode": "external_service_candidate",
|
||||
"timeout_seconds": DEFAULT_TIMEOUT_SECONDS,
|
||||
"timeout_seconds": _timeout_seconds(),
|
||||
"retry_policy": RETRY_POLICY,
|
||||
"network_execution_env": ALLOW_NETWORK_ENV,
|
||||
},
|
||||
@@ -201,7 +213,7 @@ def run_case(case_id: str) -> dict[str, Any]:
|
||||
headers={"Content-Type": "application/json"},
|
||||
method="POST",
|
||||
)
|
||||
with request.urlopen(req, timeout=DEFAULT_TIMEOUT_SECONDS) as resp:
|
||||
with request.urlopen(req, timeout=_timeout_seconds()) as resp:
|
||||
raw = resp.read().decode("utf-8")
|
||||
payload = json.loads(raw)
|
||||
except error.HTTPError as exc:
|
||||
@@ -215,7 +227,7 @@ def run_case(case_id: str) -> dict[str, Any]:
|
||||
"transport": "http_json_service_boundary",
|
||||
"endpoint": endpoint,
|
||||
"provenance_mode": "external_service_candidate",
|
||||
"timeout_seconds": DEFAULT_TIMEOUT_SECONDS,
|
||||
"timeout_seconds": _timeout_seconds(),
|
||||
"retry_policy": RETRY_POLICY,
|
||||
"network_execution_env": ALLOW_NETWORK_ENV,
|
||||
},
|
||||
@@ -231,12 +243,12 @@ def run_case(case_id: str) -> dict[str, Any]:
|
||||
"transport": "http_json_service_boundary",
|
||||
"endpoint": endpoint,
|
||||
"provenance_mode": "external_service_candidate",
|
||||
"timeout_seconds": DEFAULT_TIMEOUT_SECONDS,
|
||||
"timeout_seconds": _timeout_seconds(),
|
||||
"retry_policy": RETRY_POLICY,
|
||||
"network_execution_env": ALLOW_NETWORK_ENV,
|
||||
},
|
||||
}
|
||||
except TimeoutError:
|
||||
except (TimeoutError, socket.timeout):
|
||||
return {
|
||||
"backend": "vedastro_service_adapter_candidate",
|
||||
"available": False,
|
||||
@@ -247,7 +259,7 @@ def run_case(case_id: str) -> dict[str, Any]:
|
||||
"transport": "http_json_service_boundary",
|
||||
"endpoint": endpoint,
|
||||
"provenance_mode": "external_service_candidate",
|
||||
"timeout_seconds": DEFAULT_TIMEOUT_SECONDS,
|
||||
"timeout_seconds": _timeout_seconds(),
|
||||
"retry_policy": RETRY_POLICY,
|
||||
"network_execution_env": ALLOW_NETWORK_ENV,
|
||||
},
|
||||
@@ -263,7 +275,7 @@ def run_case(case_id: str) -> dict[str, Any]:
|
||||
"transport": "http_json_service_boundary",
|
||||
"endpoint": endpoint,
|
||||
"provenance_mode": "external_service_candidate",
|
||||
"timeout_seconds": DEFAULT_TIMEOUT_SECONDS,
|
||||
"timeout_seconds": _timeout_seconds(),
|
||||
"retry_policy": RETRY_POLICY,
|
||||
"network_execution_env": ALLOW_NETWORK_ENV,
|
||||
},
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
import subprocess
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[1]
|
||||
|
||||
|
||||
def test_pyjhora_oracle_artifact_manifest_reports_current_blackbox_assets() -> None:
|
||||
completed = subprocess.run(
|
||||
[sys.executable, "scripts/generate_pyjhora_oracle_artifact_manifest.py"],
|
||||
cwd=ROOT,
|
||||
text=True,
|
||||
capture_output=True,
|
||||
timeout=120,
|
||||
check=False,
|
||||
)
|
||||
|
||||
assert completed.returncode == 0, completed.stderr or completed.stdout
|
||||
report = json.loads(completed.stdout)
|
||||
assert report["scope"] == "pyjhora_oracle_artifact_manifest"
|
||||
assert report["artifact_count"] >= 5
|
||||
assert report["packet_count"] >= 4
|
||||
assert report["fronts"]["dasha"]["artifact_count"] >= 2
|
||||
assert report["fronts"]["shadbala"]["artifact_count"] >= 2
|
||||
assert report["fronts"]["tajika_sahams"]["artifact_count"] >= 1
|
||||
assert report["files"]["manifest"].endswith("references/oracle/artifacts/pyjhora_oracle_artifact_manifest.json")
|
||||
|
||||
manifest = ROOT / "references" / "oracle" / "artifacts" / "pyjhora_oracle_artifact_manifest.json"
|
||||
assert manifest.exists()
|
||||
manifest_data = json.loads(manifest.read_text(encoding="utf-8"))
|
||||
assert manifest_data["scope"] == "pyjhora_oracle_artifact_manifest"
|
||||
assert "pyjhora_steve_jobs_dasha_stdout_20260627.txt" in manifest_data["artifacts"]
|
||||
assert "external_template_steve_jobs_dasha_lahiri_pyjhora_20260627.json" in manifest_data["pending_packets"]
|
||||
@@ -198,3 +198,83 @@ def test_vedastro_service_adapter_classifies_http_error() -> None:
|
||||
report = json.loads(completed.stdout)
|
||||
assert report["status"] == "http_error"
|
||||
assert "503" in report["reason"]
|
||||
|
||||
|
||||
def test_vedastro_service_adapter_classifies_invalid_json_response() -> None:
|
||||
class Handler(BaseHTTPRequestHandler):
|
||||
def do_POST(self) -> None: # noqa: N802
|
||||
body = b"not-json"
|
||||
self.send_response(200)
|
||||
self.send_header("Content-Type", "application/json")
|
||||
self.send_header("Content-Length", str(len(body)))
|
||||
self.end_headers()
|
||||
self.wfile.write(body)
|
||||
|
||||
def log_message(self, format: str, *args) -> None: # noqa: A003
|
||||
return
|
||||
|
||||
server = HTTPServer(("127.0.0.1", 0), Handler)
|
||||
thread = threading.Thread(target=server.serve_forever, daemon=True)
|
||||
thread.start()
|
||||
try:
|
||||
env = os.environ.copy()
|
||||
env["VEDASTRO_API_ENDPOINT"] = f"http://127.0.0.1:{server.server_port}/vedastro"
|
||||
env["VEDASTRO_ENABLE_NETWORK"] = "1"
|
||||
completed = subprocess.run(
|
||||
[sys.executable, "scripts/vedastro_service_adapter.py", "--case", "beijing_first_use_demo"],
|
||||
cwd=ROOT,
|
||||
text=True,
|
||||
capture_output=True,
|
||||
timeout=120,
|
||||
check=False,
|
||||
env=env,
|
||||
)
|
||||
finally:
|
||||
server.shutdown()
|
||||
thread.join(timeout=5)
|
||||
|
||||
assert completed.returncode == 0, completed.stderr or completed.stdout
|
||||
report = json.loads(completed.stdout)
|
||||
assert report["status"] == "invalid_json"
|
||||
|
||||
|
||||
def test_vedastro_service_adapter_classifies_timeout() -> None:
|
||||
class Handler(BaseHTTPRequestHandler):
|
||||
def do_POST(self) -> None: # noqa: N802
|
||||
import time
|
||||
|
||||
time.sleep(0.2)
|
||||
body = b"{}"
|
||||
self.send_response(200)
|
||||
self.send_header("Content-Type", "application/json")
|
||||
self.send_header("Content-Length", str(len(body)))
|
||||
self.end_headers()
|
||||
self.wfile.write(body)
|
||||
|
||||
def log_message(self, format: str, *args) -> None: # noqa: A003
|
||||
return
|
||||
|
||||
server = HTTPServer(("127.0.0.1", 0), Handler)
|
||||
thread = threading.Thread(target=server.serve_forever, daemon=True)
|
||||
thread.start()
|
||||
try:
|
||||
env = os.environ.copy()
|
||||
env["VEDASTRO_API_ENDPOINT"] = f"http://127.0.0.1:{server.server_port}/vedastro"
|
||||
env["VEDASTRO_ENABLE_NETWORK"] = "1"
|
||||
env["VEDASTRO_TIMEOUT_SECONDS"] = "0.05"
|
||||
completed = subprocess.run(
|
||||
[sys.executable, "scripts/vedastro_service_adapter.py", "--case", "beijing_first_use_demo"],
|
||||
cwd=ROOT,
|
||||
text=True,
|
||||
capture_output=True,
|
||||
timeout=120,
|
||||
check=False,
|
||||
env=env,
|
||||
)
|
||||
finally:
|
||||
server.shutdown()
|
||||
thread.join(timeout=5)
|
||||
|
||||
assert completed.returncode == 0, completed.stderr or completed.stdout
|
||||
report = json.loads(completed.stdout)
|
||||
assert report["status"] == "timeout"
|
||||
|
||||
Reference in New Issue
Block a user