feat(client): DmService narrate loop + FakeDmTransport (all branches tested)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
80
client/tests/unit/test_dm_service.gd
Normal file
80
client/tests/unit/test_dm_service.gd
Normal file
@@ -0,0 +1,80 @@
|
||||
extends "res://addons/gut/test.gd"
|
||||
|
||||
const DmService = preload("res://scripts/net/dm_service.gd")
|
||||
const DmResponse = preload("res://scripts/net/dm_response.gd")
|
||||
const FallbackLibrary = preload("res://scripts/net/fallback_library.gd")
|
||||
const FakeDmTransport = preload("res://tests/doubles/fake_transport.gd")
|
||||
|
||||
var _fallback
|
||||
|
||||
|
||||
func before_each():
|
||||
# Point the library at a missing file so narrator_line() is deterministically
|
||||
# LAST_RESORT — the degraded assertions don't depend on the authored content.
|
||||
_fallback = FallbackLibrary.new("res://content/fallback/does_not_exist.json")
|
||||
|
||||
|
||||
func _log() -> CanonLog:
|
||||
return CanonLog.new()
|
||||
|
||||
|
||||
func _service_for(resp) -> Dictionary:
|
||||
var t = FakeDmTransport.new()
|
||||
t.set_response(resp)
|
||||
return {"svc": DmService.new(t, _fallback), "transport": t}
|
||||
|
||||
|
||||
func test_success_strips_tags_and_harvests_facts():
|
||||
var body := {"prose": "The docks reek of tar. [FACT: the eastern bridge is out]"}
|
||||
var pair := _service_for(DmResponse.ok(200, body))
|
||||
var r = await pair["svc"].narrate(_log())
|
||||
assert_false(r.degraded)
|
||||
assert_eq(r.display_text, "The docks reek of tar.")
|
||||
assert_eq(r.facts, ["the eastern bridge is out"])
|
||||
|
||||
|
||||
func test_success_posts_canon_log_to_narrate_path():
|
||||
var pair := _service_for(DmResponse.ok(200, {"prose": "x"}))
|
||||
await pair["svc"].narrate(_log())
|
||||
assert_eq(pair["transport"].last_path, "/dm/narrate")
|
||||
assert_true(pair["transport"].last_body.has("canon_log"))
|
||||
|
||||
|
||||
func test_502_degrades_to_fallback_with_no_facts():
|
||||
var pair := _service_for(DmResponse.ok(502, {"detail": {"model_error": "boom"}}))
|
||||
var r = await pair["svc"].narrate(_log())
|
||||
assert_true(r.degraded)
|
||||
assert_eq(r.display_text, FallbackLibrary.LAST_RESORT)
|
||||
assert_eq(r.facts, [])
|
||||
|
||||
|
||||
func test_422_degrades():
|
||||
var pair := _service_for(DmResponse.ok(422, {"detail": {"canon_log_errors": ["x"]}}))
|
||||
var r = await pair["svc"].narrate(_log())
|
||||
assert_true(r.degraded)
|
||||
|
||||
|
||||
func test_transport_failure_degrades():
|
||||
var pair := _service_for(DmResponse.failed("connection refused"))
|
||||
var r = await pair["svc"].narrate(_log())
|
||||
assert_true(r.degraded)
|
||||
|
||||
|
||||
func test_malformed_200_missing_prose_degrades():
|
||||
var pair := _service_for(DmResponse.ok(200, {"nope": 1}))
|
||||
var r = await pair["svc"].narrate(_log())
|
||||
assert_true(r.degraded)
|
||||
|
||||
|
||||
func test_malformed_200_non_dict_body_degrades():
|
||||
var pair := _service_for(DmResponse.ok(200, null))
|
||||
var r = await pair["svc"].narrate(_log())
|
||||
assert_true(r.degraded)
|
||||
|
||||
|
||||
func test_only_facts_harvested_dispositions_and_moves_dropped():
|
||||
var body := {"prose": "Hi [FACT: a] [ADJUST_DISPOSITION: +5] [MOVE: refuse()] bye"}
|
||||
var pair := _service_for(DmResponse.ok(200, body))
|
||||
var r = await pair["svc"].narrate(_log())
|
||||
assert_eq(r.facts, ["a"])
|
||||
assert_eq(r.display_text, "Hi bye")
|
||||
Reference in New Issue
Block a user