From 6516883570f9496693810468ff109ac2b85dc42c Mon Sep 17 00:00:00 2001 From: Spencer Schneider Date: Tue, 6 Oct 2026 12:37:13 -0400 Subject: [PATCH] Raise a clear error when there is no default project GET /user/me/default_project now returns null when the user has no default project in the key's organization (rowansci/eyrie#963). default_project() built a Project from that null and failed with a TypeError; it now raises a ValueError saying there is no default project and to set one or pass a folder or project. Folder creation, listing and workflow submission without a folder all go through it. Co-Authored-By: Claude Opus 5.5 --- rowan/project.py | 9 ++++++++- tests/test_project.py | 35 +++++++++++++++++++++++++++++++++++ 2 files changed, 43 insertions(+), 1 deletion(-) create mode 100644 tests/test_project.py diff --git a/rowan/project.py b/rowan/project.py index 30e1589..da5d842 100644 --- a/rowan/project.py +++ b/rowan/project.py @@ -197,8 +197,15 @@ def default_project() -> Project: Raises: httpx.HTTPStatusError: API request fails + ValueError: the user has no default project in the API key's organization """ with api_client() as client: response = client.get("/user/me/default_project") response.raise_for_status() - return Project(**response.json()) + project = response.json() + if project is None: + raise ValueError( + "You have no default project in this organization. " + "Set a default project, or pass a folder or project." + ) + return Project(**project) diff --git a/tests/test_project.py b/tests/test_project.py new file mode 100644 index 0000000..a06f4cf --- /dev/null +++ b/tests/test_project.py @@ -0,0 +1,35 @@ +"""Tests for project retrieval.""" + +from collections.abc import Iterator +from contextlib import contextmanager +from unittest.mock import MagicMock + +import pytest + +from rowan import project as project_module + + +def _mock_client(monkeypatch: pytest.MonkeyPatch, payload: dict | None) -> None: + client = MagicMock() + client.get.return_value.json.return_value = payload + + @contextmanager + def mock_api_client() -> Iterator[MagicMock]: + yield client + + monkeypatch.setattr(project_module, "api_client", mock_api_client) + + +def test_default_project_returns_the_project(monkeypatch: pytest.MonkeyPatch) -> None: + """Return the default project the API reports.""" + _mock_client(monkeypatch, {"uuid": "project-uuid", "name": "Default"}) + + assert project_module.default_project().uuid == "project-uuid" + + +def test_default_project_without_one_raises_a_clear_error(monkeypatch: pytest.MonkeyPatch) -> None: + """Explain a missing default project instead of failing to build one from null.""" + _mock_client(monkeypatch, None) + + with pytest.raises(ValueError, match="no default project"): + project_module.default_project()