diff --git a/plugins/platforms/photon/auth.py b/plugins/platforms/photon/auth.py index e4e2421538ba..227b90d79dd0 100644 --- a/plugins/platforms/photon/auth.py +++ b/plugins/platforms/photon/auth.py @@ -681,11 +681,19 @@ def create_project( resp = httpx.post(url, json=body, headers=_bearer(token), timeout=30.0) resp.raise_for_status() data = resp.json() or {} + if not isinstance(data, dict): + raise RuntimeError("Photon create-project returned an unexpected response") if data.get("error"): raise RuntimeError(f"Photon create-project failed: {data['error']}") - if not data.get("id"): + if data.get("succeed") is False: + raise RuntimeError( + f"Photon create-project failed: {data.get('message') or data}" + ) + project_candidate = data.get("data") + project: Dict[str, Any] = project_candidate if isinstance(project_candidate, dict) else data + if not project.get("id"): raise RuntimeError("Photon create-project did not return a project id") - return data + return project def regenerate_project_secret(token: str, project_id: str) -> str: diff --git a/tests/plugins/platforms/photon/test_auth.py b/tests/plugins/platforms/photon/test_auth.py index b3635fddd511..3e6208de2517 100644 --- a/tests/plugins/platforms/photon/test_auth.py +++ b/tests/plugins/platforms/photon/test_auth.py @@ -325,6 +325,47 @@ def test_regenerate_project_secret(monkeypatch: pytest.MonkeyPatch) -> None: assert photon_auth.regenerate_project_secret("tok", "p") == "rotated" + +def test_create_project_unwraps_success_data(monkeypatch: pytest.MonkeyPatch) -> None: + """Photon may wrap project credentials under a top-level data object.""" + captured: Dict[str, Any] = {} + + def fake_post(url: str, **kwargs: Any) -> _FakeResponse: + captured["url"] = url + captured["body"] = kwargs.get("json") + captured["headers"] = kwargs.get("headers") + return _FakeResponse(json_body={ + "succeed": True, + "data": { + "id": "dashboard-project-id", + "spectrumProjectId": "spectrum-project-id", + "projectSecret": "project-secret", + }, + }) + + monkeypatch.setattr(photon_auth.httpx, "post", fake_post) + + data = photon_auth.create_project("dashboard-token", name="Hermes Agent") + + assert data["spectrumProjectId"] == "spectrum-project-id" + assert data["projectSecret"] == "project-secret" + assert data["id"] == "dashboard-project-id" + assert "spectrum" not in captured["body"] + assert captured["body"]["name"] == "Hermes Agent" + assert captured["headers"]["Authorization"] == "Bearer dashboard-token" + assert captured["url"].endswith("/api/projects") + + +def test_create_project_raises_on_succeed_false(monkeypatch: pytest.MonkeyPatch) -> None: + def fake_post(url: str, **kwargs: Any) -> _FakeResponse: + return _FakeResponse(json_body={"succeed": False, "message": "quota exceeded"}) + + monkeypatch.setattr(photon_auth.httpx, "post", fake_post) + + with pytest.raises(RuntimeError, match="quota exceeded"): + photon_auth.create_project("tok") + + # --------------------------------------------------------------------------- # Users