From 29971049ae4e98399f74886963fb0c5676c2e232 Mon Sep 17 00:00:00 2001 From: Ray Date: Tue, 14 Jul 2026 17:49:45 +0800 Subject: [PATCH] fix: degrade list_collections when folders are unavailable (403/404) GET /folders/ is gated by the folder plan server-side, so on plans without folder support list_collections raised CloudAPIError while every sibling folder method (create_collection, get_or_create_collection, delete_collection) degrades gracefully. Catch _FOLDER_UNAVAILABLE, emit the one-time upgrade warning, and return []; transient errors still propagate. --- pageindex/backend/cloud.py | 8 +++++++- tests/test_cloud_backend.py | 22 ++++++++++++++++++++++ 2 files changed, 29 insertions(+), 1 deletion(-) diff --git a/pageindex/backend/cloud.py b/pageindex/backend/cloud.py index f7462d4..09f095b 100644 --- a/pageindex/backend/cloud.py +++ b/pageindex/backend/cloud.py @@ -173,7 +173,13 @@ class CloudBackend: return None def list_collections(self) -> list[str]: - data = self._request("GET", "/folders/") + try: + data = self._request("GET", "/folders/") + except CloudAPIError as e: + if e.status_code in self._FOLDER_UNAVAILABLE: + self._warn_folder_upgrade() + return [] + raise return [f["name"] for f in data.get("folders", []) or []] def delete_collection(self, name: str) -> None: diff --git a/tests/test_cloud_backend.py b/tests/test_cloud_backend.py index 16dce98..6154473 100644 --- a/tests/test_cloud_backend.py +++ b/tests/test_cloud_backend.py @@ -127,6 +127,28 @@ def test_folder_transient_error_propagates_and_is_not_cached(monkeypatch): assert "col" not in backend._folder_id_cache +def test_list_collections_degrades_when_folders_unavailable(monkeypatch): + backend = CloudBackend(api_key="pi-test") + + def fake_request(method, path, **kwargs): + raise CloudAPIError("Cloud API error 403: upgrade", status_code=403) + + monkeypatch.setattr(backend, "_request", fake_request) + with pytest.warns(UserWarning, match="not available on this plan"): + assert backend.list_collections() == [] + + +def test_list_collections_propagates_transient_error(monkeypatch): + backend = CloudBackend(api_key="pi-test") + + def fake_request(method, path, **kwargs): + raise CloudAPIError("Cloud API error 503: unavailable", status_code=503) + + monkeypatch.setattr(backend, "_request", fake_request) + with pytest.raises(CloudAPIError): + backend.list_collections() + + # ── doc endpoints: 404 maps to DocumentNotFoundError (local parity) ────────── def test_doc_404_maps_to_document_not_found(monkeypatch):