import inspect from app.entities import ( fallback_profile, normalize_profile, parse_mint_response, roll_traits, signature_from_anomalies, ) from app.llm import prompts as llm_prompts TRAIT_KEYS = ("alignment", "power", "volatility", "deceptiveness") def _anomaly(freq, mag): return {"source": "radio", "frequency": freq, "magnitude": mag} def test_signature_needs_enough_anomalies(): assert signature_from_anomalies([_anomaly(101.1, 5.0)]) is None assert signature_from_anomalies([]) is None def test_signature_is_deterministic_for_same_pattern(): anomalies = [_anomaly(101.1 + i, 5.0 + i) for i in range(6)] assert signature_from_anomalies(anomalies) == signature_from_anomalies(list(anomalies)) def test_parse_mint_response_extracts_json(): raw = 'Sure! Here you go:\n{"name": "Vesper Wren", "epithet": "the Static Widow"}\nHope that helps' profile = parse_mint_response(raw) assert profile is not None assert profile["name"] == "Vesper Wren" def test_parse_mint_response_rejects_garbage(): assert parse_mint_response("no json here at all") is None assert parse_mint_response('{"epithet": "nameless"}') is None def test_normalize_profile_fills_and_clamps(): profile = normalize_profile( { "name": " Hollow Briar ", "rarity": "legendary", # not a real tier -> common "voice": {"voice_id": "nonexistent", "pitch": 99, "noise": -5}, "visual": {"form": "dragon", "hue": 9999}, "quotes": ["one", 2, "three"], }, "abcdef0123456789", ) assert profile["name"] == "Hollow Briar" assert profile["rarity"] == "common" assert profile["voice"]["voice_id"] != "nonexistent" assert -6 <= profile["voice"]["pitch"] <= 6 assert 0.01 <= profile["voice"]["noise"] <= 0.08 assert profile["visual"]["form"] in ("wisp", "banshee", "fairy", "shade") assert 0 <= profile["visual"]["hue"] <= 360 assert profile["quotes"] == ["one", "three"] def test_fallback_profile_is_deterministic_and_valid(): one = fallback_profile("0123456789abcdef") two = fallback_profile("0123456789abcdef") assert one == two assert one["name"] assert one["rarity"] in ("common", "uncommon", "rare", "mythic") assert one["quotes"] # --- hidden traits (Workstream A) ------------------------------------- def test_roll_traits_values_are_in_range(): traits = roll_traits("some-signature-1") assert set(traits.keys()) == set(TRAIT_KEYS) for key in TRAIT_KEYS: assert isinstance(traits[key], float) assert 0.0 <= traits[key] <= 1.0 def test_roll_traits_is_deterministic_for_same_signature(): one = roll_traits("repeatable-signature") two = roll_traits("repeatable-signature") assert one == two def test_roll_traits_varies_by_signature(): values = {tuple(roll_traits(f"sig-{i}").values()) for i in range(20)} # 20 distinct signatures should not collapse onto a single roll. assert len(values) > 1 def test_normalize_profile_includes_traits_in_range(): profile = normalize_profile({"name": "Hollow Briar"}, "traits-norm-signature") traits = profile["traits"] assert set(traits.keys()) == set(TRAIT_KEYS) for key in TRAIT_KEYS: assert 0.0 <= traits[key] <= 1.0 def test_normalize_profile_traits_match_roll_traits_for_signature(): signature = "cross-check-signature" profile = normalize_profile({"name": "X"}, signature) assert profile["traits"] == roll_traits(signature) def test_normalize_profile_traits_are_signature_deterministic(): one = normalize_profile({"name": "A"}, "det-signature") two = normalize_profile({"name": "B", "rarity": "rare"}, "det-signature") # Same signature -> same traits every call, regardless of the rest of # the (LLM-supplied or fallback) profile passed in. assert one["traits"] == two["traits"] def test_fallback_profile_includes_traits_in_range(): profile = fallback_profile("fallback-traits-signature") traits = profile["traits"] assert set(traits.keys()) == set(TRAIT_KEYS) for key in TRAIT_KEYS: assert 0.0 <= traits[key] <= 1.0 def test_fallback_profile_traits_are_signature_deterministic(): one = fallback_profile("fallback-det-signature") two = fallback_profile("fallback-det-signature") assert one["traits"] == two["traits"] def test_persona_is_independent_of_rolled_traits(): """Persona text must stay fully decoupled from hidden truth: pairing the exact same persona string with many different signatures should surface a spread of alignment rolls, not a single value tied to the persona.""" fixed_persona = "A sweet old lady who just wants to chat about her garden." alignments = set() for i in range(40): profile = normalize_profile( {"name": "Nana", "persona": fixed_persona}, f"persona-independence-{i}" ) assert profile["persona"] == fixed_persona alignments.add(round(profile["traits"]["alignment"], 3)) # A meaningful spread (not clustered on one or two values) shows # alignment isn't derived from — or gated by — the persona text. assert len(alignments) > 20 def test_mint_prompt_never_receives_traits(): """`mint_prompt` builds the LLM system/user prompt; traits must never be one of its inputs, so hidden ground truth can't leak into persona generation.""" params = set(inspect.signature(llm_prompts.mint_prompt).parameters) assert "traits" not in params assert params == {"signature", "channel", "anomaly_summary", "voice_ids"}