diff --git a/CHANGELOG.md b/CHANGELOG.md index 581bf2756..bc949ef74 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,10 @@ Format adapted from [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), al ## [Unreleased] +### Fixed + +- an import you open while it is still working now says it is running. the import history listed it correctly, but opening that same import showed it as failed until it finished — on every file type, and most noticeable on longer imports. the page now also keeps up on its own while the import runs, instead of waiting for a reload. reported and fixed by ha-ye. + ## [1.0.20] - 2026-07-30 ### Added diff --git a/solstone/apps/import/routes.py b/solstone/apps/import/routes.py index cc101f60e..34f16312d 100644 --- a/solstone/apps/import/routes.py +++ b/solstone/apps/import/routes.py @@ -910,16 +910,17 @@ def import_detail_api(timestamp: str) -> Any: journal_root=Path(state.journal_root), timestamp=timestamp, ) + # Resolve status the same way the history list does, so an in-progress + # import (task_id present, no imported.json yet) reads as "running" + # rather than falling through to "failed". Both filesystem reads stay + # inside the guard: an import removed between them is not found, not a 500. + import_data = build_import_info( + journal_root=Path(state.journal_root), + timestamp=timestamp, + ) except FileNotFoundError: return error_response(IMPORT_NOT_FOUND, detail="Import not found") - # Resolve status the same way the history list does, so an in-progress - # import (task_id present, no imported.json yet) reads as "running" - # rather than falling through to "failed". - import_data = build_import_info( - journal_root=Path(state.journal_root), - timestamp=timestamp, - ) resolution = resolve_import_status(import_data) result["status"] = resolution.status result["error"] = resolution.error diff --git a/solstone/apps/import/tests/test_routes.py b/solstone/apps/import/tests/test_routes.py index 011caaf06..58da0a3f9 100644 --- a/solstone/apps/import/tests/test_routes.py +++ b/solstone/apps/import/tests/test_routes.py @@ -189,6 +189,60 @@ def test_import_missing_detail_api_still_returns_not_found(client): assert response.get_json()["reason_code"] == "import_not_found" +def _seed_import_json(journal: Path, timestamp: str, payload: dict) -> None: + import_dir = journal / "imports" / timestamp + import_dir.mkdir(parents=True, exist_ok=True) + (import_dir / "import.json").write_text( + json.dumps(payload) + "\n", + encoding="utf-8", + ) + + +def test_import_detail_api_reports_running_for_an_unfinished_import(client): + import importlib + + import_routes = importlib.import_module("solstone.apps.import.routes") + journal = Path(import_routes.state.journal_root) + _seed_import_json( + journal, + "20260101_120000", + {"original_filename": "in-progress.pdf", "task_id": "123"}, + ) + + response = client.get("/app/import/api/20260101_120000") + + assert response.status_code == 200 + body = response.get_json() + assert body["status"] == "running" + assert body["error"] is None + assert body["error_stage"] is None + + +def test_import_detail_api_reports_failed_with_error_and_stage(client): + import importlib + + import_routes = importlib.import_module("solstone.apps.import.routes") + journal = Path(import_routes.state.journal_root) + _seed_import_json( + journal, + "20260101_130000", + {"original_filename": "broken.pdf", "task_id": "456"}, + ) + _seed_imported_json( + journal, + "20260101_130000", + {"error": "bad ", "error_stage": "writing"}, + ) + + response = client.get("/app/import/api/20260101_130000") + + assert response.status_code == 200 + body = response.get_json() + assert body["status"] == "failed" + assert body["error"] == "bad " + assert body["error_stage"] == "writing" + + def test_import_detail_api_path_resolves(client): adapter = client.application.url_map.bind("localhost")