feat(#2): Tier-2 — transient characters + persona-state write; audit converges
v1 coverage-audit: the last in-scope client I/O points. The audit now CONVERGES — REST 17/40 covered with zero in-scope gaps (23 excluded-by- design), SSE 11/11, Bifrost planes 8/8. - sessions.py: list_character_models / create_character / get_character_state / delete_character (#161, character.read/write) + set_persona_state (POST /sessions/{id}/persona_state — freeform body, unpinned in the frozen surface). 200/201 -> dict (or None on 204), off-status -> SessionApiFailed. - cli.py: two one-shot probes (mirror --whoami): --characters (CRUD lifecycle report) + --set-persona-pad "p,a,d" (requires --session). New ParsedArgs.characters/set_persona_pad + probe mutual-exclusion. - Contract #2 amended (5 FNs) + validated. TDD: 7 wrapper + 5 cli tests. Suite 573 green; touched code ruff-clean. - Char read side live-proven (GET /models/available-for-characters -> 200). Coverage-map: convergence frontier CLOSED — scope-A "done" (every frozen I/O point classified) is met; ratatoskr cuts v1 when Worldtree tags 1.0.
This commit is contained in:
@@ -1633,3 +1633,63 @@ class TestWhoami:
|
||||
rc = main(["--whoami", "--api-key", "k", "--server", "https://w.example"])
|
||||
assert rc == 20
|
||||
assert "[session_api_failed]" in capsys.readouterr().err
|
||||
|
||||
|
||||
class TestTier2Probes:
|
||||
"""--characters + --set-persona-pad one-shot probes (Tier-2: #161 + persona_state-write)."""
|
||||
|
||||
def test_characters_standalone_accepted(self) -> None:
|
||||
"""characters_standalone: --characters alone → valid."""
|
||||
args = _parse_args(["--characters", "--api-key", "k"])
|
||||
assert args.characters is True
|
||||
assert args.session_id is None
|
||||
|
||||
def test_set_persona_requires_session(self) -> None:
|
||||
"""set_persona_requires_session [adversarial]: --set-persona-pad needs --session."""
|
||||
with pytest.raises(UsageError, match="requires --session"):
|
||||
_parse_args(["--set-persona-pad", "0.4,0.1,-0.2", "--api-key", "k"])
|
||||
|
||||
def test_probes_mutually_exclusive(self) -> None:
|
||||
"""probes_mutually_exclusive [adversarial]: --whoami + --characters → UsageError."""
|
||||
with pytest.raises(UsageError, match="mutually exclusive"):
|
||||
_parse_args(["--whoami", "--characters", "--api-key", "k"])
|
||||
|
||||
@respx.mock
|
||||
def test_characters_probe_lifecycle(self, capsys: pytest.CaptureFixture[str]) -> None:
|
||||
"""characters_probe [happy,tracer]: models → create → state → delete; report to stdout."""
|
||||
respx.get("https://w.example/models/available-for-characters").mock(
|
||||
return_value=httpx.Response(200, json={"items": [{"name": "fast"}]})
|
||||
)
|
||||
respx.post("https://w.example/characters").mock(
|
||||
return_value=httpx.Response(201, json={"character_id": "char_z", "ttl_expires_at": "t"})
|
||||
)
|
||||
respx.get("https://w.example/characters/char_z/state").mock(
|
||||
return_value=httpx.Response(200, json={"schema_version": "1", "pad": [0.1, 0.2, 0.3]})
|
||||
)
|
||||
del_route = respx.delete("https://w.example/characters/char_z").mock(
|
||||
return_value=httpx.Response(204)
|
||||
)
|
||||
rc = main(["--characters", "--api-key", "k", "--server", "https://w.example"])
|
||||
assert rc == 0
|
||||
out = capsys.readouterr().out
|
||||
assert "character models: fast" in out
|
||||
assert "created: char_z" in out
|
||||
assert "pad=[0.1, 0.2, 0.3]" in out
|
||||
assert "deleted: char_z" in out
|
||||
assert del_route.call_count == 1 # lifecycle cleaned up
|
||||
|
||||
@respx.mock
|
||||
def test_set_persona_probe(self, capsys: pytest.CaptureFixture[str]) -> None:
|
||||
"""set_persona_probe [happy,tracer]: POST pad to /sessions/{id}/persona_state; 204."""
|
||||
import json as _json
|
||||
|
||||
route = respx.post("https://w.example/sessions/s1/persona_state").mock(
|
||||
return_value=httpx.Response(204)
|
||||
)
|
||||
rc = main(
|
||||
["--set-persona-pad", "0.4,0.1,-0.2", "--session", "s1",
|
||||
"--api-key", "k", "--server", "https://w.example"]
|
||||
)
|
||||
assert rc == 0
|
||||
assert "persona_state set" in capsys.readouterr().out
|
||||
assert _json.loads(route.calls[0].request.content) == {"pad": [0.4, 0.1, -0.2]}
|
||||
|
||||
Reference in New Issue
Block a user