fix(cli): warn on welcome-fetch failures; expand test coverage
This commit is contained in:
parent
c604dad9cf
commit
517e63d217
2 changed files with 60 additions and 6 deletions
|
|
@ -314,8 +314,17 @@ def _generate_claude_md(workspace: Path, server_url: str, token: str) -> None:
|
||||||
resp = httpx.get(url, headers=headers, timeout=15.0)
|
resp = httpx.get(url, headers=headers, timeout=15.0)
|
||||||
if resp.status_code == 200:
|
if resp.status_code == 200:
|
||||||
rendered = resp.json().get("content")
|
rendered = resp.json().get("content")
|
||||||
except Exception:
|
elif resp.status_code != 404:
|
||||||
pass
|
typer.echo(
|
||||||
|
f" Warning: server returned {resp.status_code} for /api/welcome; "
|
||||||
|
"using minimal fallback. Tell your admin if this persists.",
|
||||||
|
err=True,
|
||||||
|
)
|
||||||
|
except Exception as e:
|
||||||
|
typer.echo(
|
||||||
|
f" Warning: couldn't fetch welcome prompt ({e}); using minimal fallback.",
|
||||||
|
err=True,
|
||||||
|
)
|
||||||
|
|
||||||
if rendered is None:
|
if rendered is None:
|
||||||
# Fallback for older servers — keeps the CLI usable, just less rich.
|
# Fallback for older servers — keeps the CLI usable, just less rich.
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
"""Integration tests for da analyst setup → /api/welcome wiring."""
|
"""Integration tests for da analyst setup → /api/welcome wiring."""
|
||||||
|
|
||||||
|
import json
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
import httpx
|
import httpx
|
||||||
|
|
@ -19,9 +20,14 @@ class _MockClient:
|
||||||
return httpx.Response(status_code=status, json=body, request=httpx.Request("GET", url))
|
return httpx.Response(status_code=status, json=body, request=httpx.Request("GET", url))
|
||||||
|
|
||||||
|
|
||||||
def test_generate_claude_md_uses_server_render(tmp_path, monkeypatch):
|
def _ws(tmp_path: Path) -> Path:
|
||||||
workspace = tmp_path / "ws"
|
workspace = tmp_path / "ws"
|
||||||
(workspace / ".claude").mkdir(parents=True)
|
(workspace / ".claude").mkdir(parents=True)
|
||||||
|
return workspace
|
||||||
|
|
||||||
|
|
||||||
|
def test_generate_claude_md_uses_server_render(tmp_path, monkeypatch):
|
||||||
|
workspace = _ws(tmp_path)
|
||||||
rendered = "# CUSTOM\n\nFrom server.\n"
|
rendered = "# CUSTOM\n\nFrom server.\n"
|
||||||
mock = _MockClient({
|
mock = _MockClient({
|
||||||
"https://example.com/api/welcome?server_url=https%3A%2F%2Fexample.com": (
|
"https://example.com/api/welcome?server_url=https%3A%2F%2Fexample.com": (
|
||||||
|
|
@ -30,15 +36,54 @@ def test_generate_claude_md_uses_server_render(tmp_path, monkeypatch):
|
||||||
})
|
})
|
||||||
monkeypatch.setattr("cli.commands.analyst.httpx", type("_M", (), {"get": mock.get}))
|
monkeypatch.setattr("cli.commands.analyst.httpx", type("_M", (), {"get": mock.get}))
|
||||||
_generate_claude_md(workspace, server_url="https://example.com", token="t")
|
_generate_claude_md(workspace, server_url="https://example.com", token="t")
|
||||||
|
|
||||||
assert (workspace / "CLAUDE.md").read_text(encoding="utf-8") == rendered
|
assert (workspace / "CLAUDE.md").read_text(encoding="utf-8") == rendered
|
||||||
|
# Workspace side-effects are created on the success path too.
|
||||||
|
assert (workspace / ".claude" / "CLAUDE.local.md").exists()
|
||||||
|
settings = json.loads((workspace / ".claude" / "settings.json").read_text(encoding="utf-8"))
|
||||||
|
assert settings["model"] == "sonnet"
|
||||||
|
|
||||||
|
|
||||||
def test_generate_claude_md_falls_back_on_404(tmp_path, monkeypatch):
|
def test_generate_claude_md_falls_back_on_404(tmp_path, monkeypatch):
|
||||||
workspace = tmp_path / "ws"
|
workspace = _ws(tmp_path)
|
||||||
(workspace / ".claude").mkdir(parents=True)
|
|
||||||
mock = _MockClient({}) # everything 404s
|
mock = _MockClient({}) # everything 404s
|
||||||
monkeypatch.setattr("cli.commands.analyst.httpx", type("_M", (), {"get": mock.get}))
|
monkeypatch.setattr("cli.commands.analyst.httpx", type("_M", (), {"get": mock.get}))
|
||||||
_generate_claude_md(workspace, server_url="https://example.com", token="t")
|
_generate_claude_md(workspace, server_url="https://example.com", token="t")
|
||||||
body = (workspace / "CLAUDE.md").read_text(encoding="utf-8")
|
body = (workspace / "CLAUDE.md").read_text(encoding="utf-8")
|
||||||
assert "AI Data Analyst" in body # embedded fallback contains this string
|
assert "AI Data Analyst" in body
|
||||||
assert "https://example.com" in body
|
assert "https://example.com" in body
|
||||||
|
|
||||||
|
|
||||||
|
def test_generate_claude_md_falls_back_on_null_content(tmp_path, monkeypatch):
|
||||||
|
"""Server returns 200 but malformed body (`content: null`). CLI must use fallback."""
|
||||||
|
workspace = _ws(tmp_path)
|
||||||
|
mock = _MockClient({
|
||||||
|
"https://example.com/api/welcome?server_url=https%3A%2F%2Fexample.com": (
|
||||||
|
{"content": None}, 200
|
||||||
|
),
|
||||||
|
})
|
||||||
|
monkeypatch.setattr("cli.commands.analyst.httpx", type("_M", (), {"get": mock.get}))
|
||||||
|
_generate_claude_md(workspace, server_url="https://example.com", token="t")
|
||||||
|
body = (workspace / "CLAUDE.md").read_text(encoding="utf-8")
|
||||||
|
# Embedded fallback contains these literals
|
||||||
|
assert "AI Data Analyst" in body
|
||||||
|
assert "https://example.com" in body
|
||||||
|
|
||||||
|
|
||||||
|
def test_generate_claude_md_warns_on_5xx(tmp_path, monkeypatch, capsys):
|
||||||
|
"""500 from server → embedded fallback, with a stderr warning so operators can diagnose."""
|
||||||
|
workspace = _ws(tmp_path)
|
||||||
|
mock = _MockClient({
|
||||||
|
"https://example.com/api/welcome?server_url=https%3A%2F%2Fexample.com": (
|
||||||
|
{"detail": "boom"}, 500
|
||||||
|
),
|
||||||
|
})
|
||||||
|
monkeypatch.setattr("cli.commands.analyst.httpx", type("_M", (), {"get": mock.get}))
|
||||||
|
_generate_claude_md(workspace, server_url="https://example.com", token="t")
|
||||||
|
|
||||||
|
body = (workspace / "CLAUDE.md").read_text(encoding="utf-8")
|
||||||
|
assert "AI Data Analyst" in body # fallback used
|
||||||
|
|
||||||
|
captured = capsys.readouterr()
|
||||||
|
assert "500" in captured.err
|
||||||
|
assert "fallback" in captured.err.lower()
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue