Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 27 additions & 2 deletions limacharlie/sdk/cases.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,14 +24,39 @@ class Cases:

def __init__(self, org: Organization, api_root: str | None = None) -> None:
self._org = org
self._api_root = api_root or os.environ.get(
"LC_CASES_API_ROOT", _DEFAULT_API_ROOT
self._api_root_override = api_root or os.environ.get(
"LC_CASES_API_ROOT", None
)
self._resolved_root: str | None = None

@property
def oid(self) -> str:
return self._org.oid

@property
def _api_root(self) -> str:
"""Lazily resolve the cases API root URL.

Priority: explicit api_root / LC_CASES_API_ROOT env var >
org URLs endpoint ("cases" key) > _DEFAULT_API_ROOT fallback.
"""
if self._api_root_override is not None:
return self._api_root_override
if self._resolved_root is not None:
return self._resolved_root
try:
urls = self._org.get_urls()
cases_url = urls.get("cases", "")
if cases_url:
if not cases_url.startswith("http://") and not cases_url.startswith("https://"):
cases_url = "https://" + cases_url
self._resolved_root = cases_url
else:
self._resolved_root = _DEFAULT_API_ROOT
except Exception:
self._resolved_root = _DEFAULT_API_ROOT
return self._resolved_root

_EXTENSION_NAME = "ext-cases"

def create_case(
Expand Down
32 changes: 32 additions & 0 deletions tests/unit/test_sdk_cases.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ def mock_org():
org.oid = "test-oid"
org.client = MagicMock()
org.client._jwt = "fake-jwt-token"
org.get_urls.return_value = {}
return org


Expand Down Expand Up @@ -40,22 +41,53 @@ def _extract_body(mock_org):

class TestCasesInit:
def test_default_api_root(self, mock_org):
"""When no override and get_urls has no 'cases' key, falls back to default."""
mock_org.get_urls.return_value = {}
t = Cases(mock_org)
assert t._api_root == "https://cases.limacharlie.io"

def test_org_url_resolved(self, mock_org):
"""When get_urls returns a 'cases' key, use it."""
mock_org.get_urls.return_value = {"cases": "cases.staging.limacharlie.io"}
t = Cases(mock_org)
assert t._api_root == "https://cases.staging.limacharlie.io"

def test_org_url_with_scheme(self, mock_org):
"""When get_urls returns a URL with scheme, don't double-prefix."""
mock_org.get_urls.return_value = {"cases": "https://cases.staging.limacharlie.io"}
t = Cases(mock_org)
assert t._api_root == "https://cases.staging.limacharlie.io"

def test_org_url_cached(self, mock_org):
"""get_urls should only be called once."""
mock_org.get_urls.return_value = {"cases": "cases.staging.limacharlie.io"}
t = Cases(mock_org)
_ = t._api_root
_ = t._api_root
mock_org.get_urls.assert_called_once()

def test_org_url_error_fallback(self, mock_org):
"""When get_urls raises, fall back to default."""
mock_org.get_urls.side_effect = Exception("network error")
t = Cases(mock_org)
assert t._api_root == "https://cases.limacharlie.io"

def test_custom_api_root(self, mock_org):
t = Cases(mock_org, api_root="https://custom.example.com")
assert t._api_root == "https://custom.example.com"
mock_org.get_urls.assert_not_called()

def test_env_override(self, mock_org, monkeypatch):
monkeypatch.setenv("LC_CASES_API_ROOT", "https://env.example.com")
t = Cases(mock_org)
assert t._api_root == "https://env.example.com"
mock_org.get_urls.assert_not_called()

def test_explicit_api_root_overrides_env(self, mock_org, monkeypatch):
monkeypatch.setenv("LC_CASES_API_ROOT", "https://env.example.com")
t = Cases(mock_org, api_root="https://explicit.example.com")
assert t._api_root == "https://explicit.example.com"
mock_org.get_urls.assert_not_called()

def test_oid_property(self, cases):
assert cases.oid == "test-oid"
Expand Down