"""Thorough pytest suite for space/arc.py. Run with: pytest space/tests/test_arc.py Imports arc via a sys.path insert of the parent (space/) directory so the test works regardless of the current working directory. """ import os import sys # Make `space/` importable so `import arc` resolves to space/arc.py. _HERE = os.path.dirname(os.path.abspath(__file__)) _SPACE_DIR = os.path.dirname(_HERE) if _SPACE_DIR not in sys.path: sys.path.insert(0, _SPACE_DIR) import arc # noqa: E402 # --------------------------------------------------------------------------- # meter_to_stage boundaries # --------------------------------------------------------------------------- def test_meter_to_stage_boundaries(): assert arc.meter_to_stage(0) == "oblivious" assert arc.meter_to_stage(19) == "oblivious" assert arc.meter_to_stage(20) == "uneasy" assert arc.meter_to_stage(39) == "uneasy" assert arc.meter_to_stage(40) == "questioning" assert arc.meter_to_stage(59) == "questioning" assert arc.meter_to_stage(60) == "dawning" assert arc.meter_to_stage(79) == "dawning" assert arc.meter_to_stage(80) == "acceptance" assert arc.meter_to_stage(100) == "acceptance" def test_meter_to_stage_full_sweep_is_monotonic(): order = ["oblivious", "uneasy", "questioning", "dawning", "acceptance"] last_idx = 0 for m in range(0, 101): stage = arc.meter_to_stage(m) idx = order.index(stage) assert idx >= last_idx, "stage went backwards at meter=%d" % m last_idx = idx # --------------------------------------------------------------------------- # STAGES table integrity # --------------------------------------------------------------------------- def test_stages_table_shape(): assert [s["key"] for s in arc.STAGES] == [ "oblivious", "uneasy", "questioning", "dawning", "acceptance", ] for s in arc.STAGES: assert set(s.keys()) >= { "key", "lo", "hi", "descriptor", "default_mood", "sample_lines", } assert s["default_mood"] in arc.MOODS assert isinstance(s["sample_lines"], list) and s["sample_lines"] assert isinstance(s["descriptor"], str) and len(s["descriptor"]) > 40 def test_stage_default_mood(): assert arc.stage_default_mood("oblivious") == "warm" assert arc.stage_default_mood("uneasy") == "uneasy" assert arc.stage_default_mood("questioning") == "searching" assert arc.stage_default_mood("dawning") == "hollow" assert arc.stage_default_mood("acceptance") == "tender" # Unknown stage falls back to a valid mood. assert arc.stage_default_mood("bogus") in arc.MOODS def test_constants(): assert arc.PERSONA_NAME == "NIGHTWAVE" assert arc.VOICE == "am_michael" assert arc.TIME_DRIP_PER_MIN == 6 def test_dj_name_in_persona(): assert arc.DJ_NAME == "Sam Dusk" assert arc.DJ_NAME in arc.HOST_PERSONA assert arc.PERSONA_NAME in arc.HOST_PERSONA # station name still present # --------------------------------------------------------------------------- # build_system_prompt # --------------------------------------------------------------------------- def _descriptor_for(stage): return next(s["descriptor"] for s in arc.STAGES if s["key"] == stage) def test_build_system_prompt_broadcast_contains_descriptor_and_contract(): for stage in arc.STAGE_KEYS: p = arc.build_system_prompt(stage, "broadcast") # The stage descriptor must be injected verbatim. assert _descriptor_for(stage) in p # Persona stated. assert "NIGHTWAVE" in p # JSON instruction present, naming all three keys. assert '"text"' in p assert '"mood"' in p assert '"arc_cue"' in p # Forbids markdown / lists / emoji. low = p.lower() assert "markdown" in low assert "no emoji" in low or "emoji" in low assert "list" in low def test_build_system_prompt_caller_contains_descriptor_and_contract(): for stage in arc.STAGE_KEYS: p = arc.build_system_prompt( stage, "caller", caller_text="are you real?" ) assert _descriptor_for(stage) in p assert '"arc_cue"' in p assert "markdown" in p.lower() # The caller's words are injected. assert "are you real?" in p # It must instruct returning to the broadcast. assert "broadcast" in p.lower() def test_build_host_prompt_all_kinds_have_contract(): for kind in ("thought", "song_intro", "caller", "local_weather"): p = arc.build_host_prompt(kind) assert arc.HOST_PERSONA in p assert '"text"' in p and '"mood"' in p and '"arc_cue"' in p def test_build_host_prompt_local_weather_is_distinct(): p = arc.build_host_prompt("local_weather") assert "forecast" in p.lower() # instructs NOT to read like a forecast assert p != arc.build_host_prompt("thought") def test_build_fragment_prompt(): p = arc.build_fragment_prompt() assert '"text"' in p and '"mood"' in p and '"arc_cue"' in p # output contract assert arc.HOST_PERSONA not in p # NOT the Sam Dusk host persona assert "another station" in p.lower() or "other station" in p.lower() def test_build_host_prompt_dedication(): p = arc.build_host_prompt("dedication") assert arc.HOST_PERSONA in p assert '"text"' in p and '"mood"' in p and '"arc_cue"' in p assert "dedication" in p.lower() def test_build_system_prompt_broadcast_uses_topic(): p = arc.build_system_prompt("oblivious", "broadcast", topic="a lighthouse") assert "a lighthouse" in p def test_build_system_prompt_broadcast_no_topic_invents(): p = arc.build_system_prompt("oblivious", "broadcast") low = p.lower() assert "town that does not exist" in low or "dedication" in low def test_build_system_prompt_caller_empty_text_is_graceful(): p = arc.build_system_prompt("oblivious", "caller", caller_text="") assert "NIGHTWAVE" in p assert "crackly" in p.lower() or "connection" in p.lower() def test_build_system_prompt_unknown_stage_defaults_to_oblivious(): p = arc.build_system_prompt("bogus", "broadcast") assert _descriptor_for("oblivious") in p # --------------------------------------------------------------------------- # detect_triggers # --------------------------------------------------------------------------- def test_detect_triggers_reality(): assert arc.detect_triggers("are you real?") >= 14 assert arc.detect_triggers("Are you an AI?") >= 14 assert arc.detect_triggers("are you a robot") >= 14 assert arc.detect_triggers("are you alive?") >= 14 assert arc.detect_triggers("are you conscious") >= 14 def test_detect_triggers_identity(): assert arc.detect_triggers("what's your name") >= 14 assert arc.detect_triggers("who are you?") >= 14 assert arc.detect_triggers("do you have a name") >= 14 def test_detect_triggers_station(): assert arc.detect_triggers("where are you broadcasting from?") >= 14 assert arc.detect_triggers("what's the address of the station?") >= 14 assert arc.detect_triggers("where is the studio") >= 14 def test_detect_triggers_generic_question(): assert arc.detect_triggers("what's the weather") == 5 assert arc.detect_triggers("can you play something slow?") == 5 def test_detect_triggers_non_question(): assert arc.detect_triggers("that was a lovely song") == 0 assert arc.detect_triggers("") == 0 assert arc.detect_triggers(None) == 0 def test_detect_triggers_clamped(): # No single utterance should ever exceed 30 or go below 0. for txt in [ "are you real and what's your name and where is the station?", "who are you, are you an ai, where is the studio, are you alive?", "what's the weather", "", "hello", ]: d = arc.detect_triggers(txt) assert 0 <= d <= 30 if __name__ == "__main__": import pytest raise SystemExit(pytest.main([__file__, "-v"]))