"""Tests for the OutputBuffer.""" import pytest from core.parsers.buffer import OutputBuffer from core.parsers.dead_souls import DeadSoulsParser @pytest.fixture def parser(): return DeadSoulsParser() @pytest.fixture def buffer(parser): return OutputBuffer(parser) class TestOutputBuffer: def test_no_prompt_buffers_text(self, buffer): """Text without a prompt should be buffered, not returned.""" blocks = buffer.feed("You are in a dark room.\nA torch flickers.") assert blocks == [] assert buffer.pending_lines == 2 def test_prompt_flushes_buffer(self, buffer): """When a prompt arrives, the buffered text is flushed.""" buffer.feed("Town Square\n\nYou are in the town square.\n") blocks = buffer.feed("hp: 350/350 mp: 440/440 sp: 431/431 >") # Should get 2 blocks: the accumulated room text + the prompt line assert len(blocks) == 2 assert "Town Square" in blocks[0] assert "hp:" in blocks[1] assert buffer.pending_lines == 0 def test_multiple_prompts(self, buffer): """Two prompts in one feed should flush twice.""" text = ( "Room A\nhp: 100/100 mp: 50/50 sp: 75/75 >\n" "Room B\nhp: 100/100 mp: 50/50 sp: 75/75 >\n" ) blocks = buffer.feed(text) # Expect: "Room A", prompt, "Room B", prompt assert len(blocks) == 4 assert "Room A" in blocks[0] assert "hp:" in blocks[1] assert "Room B" in blocks[2] assert "hp:" in blocks[3] def test_flush_remaining(self, buffer): """flush() should return whatever is in the buffer.""" buffer.feed("Some text without prompt\nAnother line") remaining = buffer.flush() assert remaining is not None assert "Some text" in remaining assert buffer.pending_lines == 0 def test_flush_empty(self, buffer): """flush() on empty buffer returns None.""" assert buffer.flush() is None def test_max_lines_safety(self, parser): """Buffer should auto-flush when exceeding max_lines.""" small_buffer = OutputBuffer(parser, max_lines=5) # Feed 10 lines without a prompt text = "\n".join(f"line {i}" for i in range(10)) blocks = small_buffer.feed(text) # Should have flushed at least once due to exceeding 5 lines assert len(blocks) >= 1 assert small_buffer.pending_lines < 5 def test_prompt_line_is_separate_block(self, buffer): """The prompt itself should be its own block, not mixed with room text.""" buffer.feed("Dark Forest\nTrees surround you.\n") blocks = buffer.feed("hp: 200/300 mp: 100/200 sp: 150/200 >") # blocks[0] = room text, blocks[1] = prompt assert len(blocks) == 2 assert "hp:" not in blocks[0] assert "hp:" in blocks[1] class TestBufferWithRealGameOutput: """Integration-style tests using text patterns from real Dead Souls sessions.""" def test_login_then_room(self, buffer): """Simulate the login sequence followed by the first room.""" # Login messages (no prompt) buffer.feed("Allowing login.\n") # Then the game sends the prompt blocks = buffer.feed("hp: 350/350 mp: 440/440 sp: 431/431 >") assert len(blocks) == 2 assert "Allowing login." in blocks[0] assert "hp: 350/350" in blocks[1] def test_room_description_then_prompt(self, buffer): """Simulate receiving a room description followed by prompt.""" text = ( "Town Square\n" "\n" "You are standing in the town square.\n" "There is one obvious exit: north.\n" "A guard is standing here.\n" "hp: 350/350 mp: 440/440 sp: 431/431 >\n" ) blocks = buffer.feed(text) # First block: room text, second: prompt assert len(blocks) == 2 room_block = blocks[0] assert "Town Square" in room_block assert "guard" in room_block assert "hp:" in blocks[1]