diff --git a/scripts/jyotish_api_server.py b/scripts/jyotish_api_server.py index 0751b077..9e952d5a 100644 --- a/scripts/jyotish_api_server.py +++ b/scripts/jyotish_api_server.py @@ -953,6 +953,13 @@ class JyotishAPIHandler(BaseHTTPRequestHandler): elif path == '/api/vedastro_gateway/enqueue': result = self._compute_vedastro_gateway_enqueue(body) self._json(result) + elif path.startswith('/api/vedastro_gateway/jobs/') and path.endswith('/run'): + job_id = path.split('/')[-2] + result = self._compute_vedastro_gateway_run_job(job_id) + if result is None: + self._error_json('Not found', 404, 'ERR_NOT_FOUND') + else: + self._json(result) elif path == '/api/professional_reading': result = self._compute_professional_reading(body) self._json(result) @@ -1784,6 +1791,11 @@ class JyotishAPIHandler(BaseHTTPRequestHandler): reference_date=str(reference_date), ) + def _compute_vedastro_gateway_run_job(self, job_id): + from scripts.vedastro_gateway import run_gateway_job + + return run_gateway_job(str(job_id)) + def _compute_vedastro_gateway_run(self, body): from scripts.vedastro_gateway import run_gateway_packet diff --git a/scripts/vedastro_gateway.py b/scripts/vedastro_gateway.py index 2b09abc9..c3b964e1 100644 --- a/scripts/vedastro_gateway.py +++ b/scripts/vedastro_gateway.py @@ -143,6 +143,31 @@ def complete_gateway_job(job_id: str, result: dict[str, Any]) -> dict[str, Any]: return _write_job(job) +def run_gateway_job(job_id: str) -> dict[str, Any] | None: + job = get_gateway_job(job_id) + if job is None: + return None + if job.get("status") == "completed": + return job + job["status"] = "running" + job["updated_at"] = _now_iso() + _write_job(job) + request = job.get("request") if isinstance(job.get("request"), dict) else {} + try: + result = run_gateway_packet( + request.get("case") if isinstance(request.get("case"), dict) else {}, + question=str(request.get("question") or ""), + themes=request.get("themes") if isinstance(request.get("themes"), list) else [], + reference_date=str(request.get("reference_date") or ""), + ) + except Exception as exc: + job["status"] = "failed" + job["updated_at"] = _now_iso() + job["error"] = {"type": exc.__class__.__name__, "message": str(exc)} + return _write_job(job) + return complete_gateway_job(job_id, result) + + def gateway_status() -> dict[str, Any]: config = build_gateway_config() return { diff --git a/tests/test_api_server_security.py b/tests/test_api_server_security.py index 9dd96a55..a04c297a 100644 --- a/tests/test_api_server_security.py +++ b/tests/test_api_server_security.py @@ -347,6 +347,32 @@ def test_vedastro_gateway_poll_rejects_invalid_job_id(monkeypatch, tmp_path) -> assert poller.payload()['error_code'] == 'ERR_NOT_FOUND' +def test_vedastro_gateway_job_run_route_executes_worker(monkeypatch, tmp_path) -> None: + from scripts import vedastro_gateway + + monkeypatch.setenv('VEDASTRO_GATEWAY_QUEUE_DIR', str(tmp_path)) + job = vedastro_gateway.enqueue_gateway_job( + {'year': 1955, 'month': 2, 'day': 24, 'hour': 19, 'minute': 15, 'lat': 37.7749, 'lon': -122.4194, 'tz': 8}, + question='事业机会什么时候出现', + themes=['career'], + reference_date='2026-07-02', + ) + + monkeypatch.setattr( + vedastro_gateway, + 'run_gateway_packet', + lambda *args, **kwargs: {'scope': 'vedastro_gateway_run', 'status': 'local_fallback'}, + ) + runner = _PostCaptureHandler(f"/api/vedastro_gateway/jobs/{job['job_id']}/run", {}) + + runner.do_POST() + + assert runner.status_code == 200 + payload = runner.payload() + assert payload['status'] == 'completed' + assert payload['result']['status'] == 'local_fallback' + + def test_professional_reading_composes_high_rigor_and_gateway(monkeypatch) -> None: handler = _handler() diff --git a/tests/test_vedastro_gateway.py b/tests/test_vedastro_gateway.py index 6128ae70..3613901b 100644 --- a/tests/test_vedastro_gateway.py +++ b/tests/test_vedastro_gateway.py @@ -111,3 +111,46 @@ def test_gateway_queue_lifecycle_uses_file_job_store(monkeypatch, tmp_path): polled = vedastro_gateway.get_gateway_job(job["job_id"]) assert polled["result"]["status"] == "local_fallback" assert polled["raw_response_archive"]["status"] == "stored_gateway_packet_not_official_raw" + + +def test_gateway_run_job_executes_queued_request(monkeypatch, tmp_path): + from scripts import vedastro_gateway + + monkeypatch.setenv("VEDASTRO_GATEWAY_QUEUE_DIR", str(tmp_path)) + job = vedastro_gateway.enqueue_gateway_job( + {"year": 1955, "month": 2, "day": 24, "hour": 19, "minute": 15, "lat": 37.7749, "lon": -122.4194, "tz": 8}, + question="事业机会什么时候出现", + themes=["career"], + reference_date="2026-07-02", + ) + seen = {} + + def fake_run(case, question="", themes=None, reference_date=""): + seen.update({"case": case, "question": question, "themes": themes, "reference_date": reference_date}) + return {"scope": "vedastro_gateway_run", "status": "local_fallback"} + + monkeypatch.setattr(vedastro_gateway, "run_gateway_packet", fake_run) + result = vedastro_gateway.run_gateway_job(job["job_id"]) + + assert result["status"] == "completed" + assert result["result"]["status"] == "local_fallback" + assert seen["case"]["year"] == 1955 + assert seen["question"] == "事业机会什么时候出现" + assert seen["themes"] == ["career"] + assert seen["reference_date"] == "2026-07-02" + + +def test_gateway_run_job_records_worker_failure(monkeypatch, tmp_path): + from scripts import vedastro_gateway + + monkeypatch.setenv("VEDASTRO_GATEWAY_QUEUE_DIR", str(tmp_path)) + job = vedastro_gateway.enqueue_gateway_job({"year": 1955}, question="x") + + def broken_run(*_args, **_kwargs): + raise RuntimeError("boom") + + monkeypatch.setattr(vedastro_gateway, "run_gateway_packet", broken_run) + result = vedastro_gateway.run_gateway_job(job["job_id"]) + + assert result["status"] == "failed" + assert result["error"] == {"type": "RuntimeError", "message": "boom"}