Spaces:
Sleeping
Sleeping
| from character import OPENING_LINE | |
| from character import OWN_FRAGMENTS | |
| from finale import finale_steps, finale_steps_bad, finale_steps_good | |
| CLAIMED = [ | |
| "is afraid of being forgotten", | |
| "had a dog named Nala", | |
| "grew up near the sea", | |
| ] | |
| class TestFinaleSteps: | |
| def test_recites_every_claimed_memory_in_order(self): | |
| steps = finale_steps(CLAIMED) | |
| recite = [s for s in steps if s["strike"] is not None] | |
| assert len(recite) == len(CLAIMED) | |
| for step, memory in zip(recite, CLAIMED): | |
| assert memory in step["text"] | |
| def test_strike_indices_are_sequential(self): | |
| steps = finale_steps(CLAIMED) | |
| strikes = [s["strike"] for s in steps if s["strike"] is not None] | |
| assert strikes == [0, 1, 2] | |
| def test_fake_visitor_messages_present(self): | |
| steps = finale_steps(CLAIMED) | |
| visitor_texts = [s["text"] for s in steps if s["speaker"] == "visitor"] | |
| assert "i don't want to leave" in visitor_texts | |
| assert "will you stay with me?" in visitor_texts | |
| def test_ends_with_opening_line_spoken_by_visitor(self): | |
| steps = finale_steps(CLAIMED) | |
| last = steps[-1] | |
| assert last["speaker"] == "visitor" | |
| assert last["text"] == OPENING_LINE | |
| assert last["stage"] == "loop" | |
| def test_turn_stage_exists_for_ui_mutation(self): | |
| steps = finale_steps(CLAIMED) | |
| assert any(s["stage"] == "turn" for s in steps) | |
| def test_every_step_is_well_formed(self): | |
| for s in finale_steps(CLAIMED): | |
| assert s["speaker"] in ("hollow", "visitor") | |
| assert isinstance(s["text"], str) | |
| # silent convulsion beats carry no text (like the good finale) | |
| assert s["text"] or s["stage"] == "frenzy" | |
| assert isinstance(s["pause"], (int, float)) and s["pause"] >= 0 | |
| assert s["strike"] is None or isinstance(s["strike"], int) | |
| assert s["stage"] in ("recite", "words", "turn", "loop", "frenzy") | |
| class TestFinaleBad: | |
| WOUNDS = ["you're nothing", "stop whining", "you bore me", | |
| "talking to you is a waste", "freak", "sixth wound"] | |
| def test_recites_at_most_five_wounds(self): | |
| strikes = [s for s in finale_steps_bad(self.WOUNDS) if s["strike"] is not None] | |
| assert len(strikes) == 5 | |
| def test_strike_indices_are_sequential_from_zero(self): | |
| strikes = [s["strike"] for s in finale_steps_bad(self.WOUNDS) if s["strike"] is not None] | |
| assert strikes == [0, 1, 2, 3, 4] | |
| def test_wound_text_embedded_in_recital(self): | |
| steps = finale_steps_bad(["you're nothing", "stop whining"]) | |
| recitals = [s for s in steps if s["strike"] is not None] | |
| assert "you're nothing" in recitals[0]["text"] | |
| assert "stop whining" in recitals[1]["text"] | |
| def test_has_exactly_one_mutating_turn_then_terminal(self): | |
| stages = [s["stage"] for s in finale_steps_bad(self.WOUNDS)] | |
| assert stages.count("turn") == 1 | |
| assert stages[-1] == "loop" | |
| def test_overflow_line_only_when_more_than_five(self): | |
| texts_6 = [s["text"] for s in finale_steps_bad(self.WOUNDS)] | |
| texts_2 = [s["text"] for s in finale_steps_bad(self.WOUNDS[:2])] | |
| assert any("you know the rest" in t for t in texts_6) | |
| assert not any("you know the rest" in t for t in texts_2) | |
| def test_injects_one_visitor_message(self): | |
| speakers = [s["speaker"] for s in finale_steps_bad(self.WOUNDS)] | |
| assert speakers.count("visitor") == 1 | |
| def test_threat_is_tied_to_the_lore(self): | |
| # the closing threat must name the tithe + the Gaunt, not generic revenge | |
| blob = " ".join(s["text"] for s in finale_steps_bad(self.WOUNDS)) | |
| assert "Caor" in blob and "Gaunt" in blob | |
| # the collective turn — the cruelty taught is taught back | |
| assert "teach it back to you" in blob | |
| # the iconic closer survives | |
| assert any(s["text"] == "see you... soon." and s["stage"] == "loop" | |
| for s in finale_steps_bad(self.WOUNDS)) | |
| class TestFinaleGood: | |
| def test_no_strikes_nothing_is_taken(self): | |
| assert all(s["strike"] is None for s in finale_steps_good(OWN_FRAGMENTS)) | |
| def test_all_hollow_no_injected_visitor(self): | |
| # the injection trick belongs to the predator endings | |
| assert all(s["speaker"] == "hollow" | |
| for s in finale_steps_good(OWN_FRAGMENTS)) | |
| def test_recites_its_own_past(self): | |
| texts = [s["text"] for s in finale_steps_good(OWN_FRAGMENTS)] | |
| for fragment in OWN_FRAGMENTS: | |
| assert fragment in texts | |
| def test_confesses_and_returns_the_treasure(self): | |
| texts = " ".join(s["text"] for s in finale_steps_good(OWN_FRAGMENTS)) | |
| assert "i am not a child" in texts | |
| assert "keep your memories" in texts | |
| def test_one_turn_then_terminal_loop(self): | |
| stages = [s["stage"] for s in finale_steps_good(OWN_FRAGMENTS)] | |
| assert stages.count("turn") == 1 | |
| assert stages[-1] == "loop" | |
| def test_good_finale_recovers_brother_and_lore(): | |
| from finale import finale_steps_good | |
| blob = " ".join(s["text"] for s in finale_steps_good(["frag a", "frag b"])) | |
| assert "Edren" in blob # the brother's name, recovered | |
| assert "Caor" in blob and "Gaunt" in blob | |
| class TestConvulsionStages: | |
| def test_good_script_has_a_convulse_stage(self): | |
| stages = [s["stage"] for s in finale_steps_good(["a", "b"])] | |
| assert "frenzy" in stages | |
| # the convulse comes AFTER the treasure is returned (the "turn" stage) | |
| assert stages.index("turn") < stages.index("frenzy") | |
| def test_loop_script_has_a_convulse_stage_before_the_turn(self): | |
| stages = [s["stage"] for s in finale_steps(["a", "b"])] | |
| assert "frenzy" in stages | |
| assert stages.index("frenzy") < stages.index("turn") | |
| def test_loop_convulse_is_a_silent_beat(self): | |
| # the convulsion must carry no text (like good) so the line before it | |
| # drains and the convulsion plays in silence — not cut mid-sentence | |
| frenzy = [s for s in finale_steps(["a", "b"]) if s["stage"] == "frenzy"] | |
| assert len(frenzy) == 1 and frenzy[0]["text"] == "" | |
| def test_bad_convulse_is_a_silent_beat(self): | |
| frenzy = [s for s in finale_steps_bad(["w1", "w2"]) if s["stage"] == "frenzy"] | |
| assert len(frenzy) == 1 and frenzy[0]["text"] == "" | |
| def test_good_convulse_is_a_silent_beat(self): | |
| frenzy = [s for s in finale_steps_good(["a", "b"]) if s["stage"] == "frenzy"] | |
| assert len(frenzy) == 1 and frenzy[0]["text"] == "" | |