using ClaudeDo.Ui.Helpers; namespace ClaudeDo.Ui.Tests.Helpers; public class StreamLineFormatterTests { private readonly StreamLineFormatter _formatter = new(); // --- Text deltas --- [Fact] public void FormatLine_TextDelta_ReturnsTextContent() { var line = """{"type":"stream_event","event":{"type":"content_block_delta","index":0,"delta":{"type":"text_delta","text":"Hello world"}}}"""; Assert.Equal("Hello world", _formatter.FormatLine(line)); } [Fact] public void FormatLine_ConsecutiveTextDeltas_ReturnEachDelta() { var line1 = """{"type":"stream_event","event":{"type":"content_block_delta","index":0,"delta":{"type":"text_delta","text":"Hello "}}}"""; var line2 = """{"type":"stream_event","event":{"type":"content_block_delta","index":0,"delta":{"type":"text_delta","text":"world"}}}"""; Assert.Equal("Hello ", _formatter.FormatLine(line1)); Assert.Equal("world", _formatter.FormatLine(line2)); } [Fact] public void FormatLine_ContentBlockStop_ReturnsNewline() { var line = """{"type":"stream_event","event":{"type":"content_block_stop","index":0}}"""; Assert.Equal("\n", _formatter.FormatLine(line)); } // --- Tool use, result, system, fallback --- [Fact] public void FormatLine_ToolUseStart_ReturnsToolNameLine() { var line = """{"type":"stream_event","event":{"type":"content_block_start","index":1,"content_block":{"type":"tool_use","id":"x","name":"bash"}}}"""; Assert.Equal("\n[Tool: bash]\n", _formatter.FormatLine(line)); } [Fact] public void FormatLine_InputJsonDelta_ReturnsNull() { var line = """{"type":"stream_event","event":{"type":"content_block_delta","index":1,"delta":{"type":"input_json_delta","partial_json":"{\"cmd\":"}}}"""; Assert.Null(_formatter.FormatLine(line)); } [Fact] public void FormatLine_Result_ReturnsFormattedResult() { var line = """{"type":"result","result":"Done."}"""; Assert.Equal("\n--- Result ---\nDone.\n", _formatter.FormatLine(line)); } [Fact] public void FormatLine_ApiRetry_ReturnsRetryNotice() { var line = """{"type":"system","subtype":"api_retry"}"""; Assert.Equal("\n[Retrying API call...]\n", _formatter.FormatLine(line)); } [Fact] public void FormatLine_SystemNonRetry_ReturnsNull() { var line = """{"type":"system","subtype":"init"}"""; Assert.Null(_formatter.FormatLine(line)); } [Fact] public void FormatLine_AssistantType_ReturnsNull() { var line = """{"type":"assistant","message":{}}"""; Assert.Null(_formatter.FormatLine(line)); } [Fact] public void FormatLine_MalformedJson_ReturnsRawLine() { var line = "not json at all"; Assert.Equal("not json at all", _formatter.FormatLine(line)); } [Fact] public void FormatLine_MessageStartAndDelta_ReturnsNull() { var start = """{"type":"stream_event","event":{"type":"message_start","message":{}}}"""; var delta = """{"type":"stream_event","event":{"type":"message_delta","delta":{}}}"""; Assert.Null(_formatter.FormatLine(start)); Assert.Null(_formatter.FormatLine(delta)); } // --- FormatFile and Trim --- [Fact] public void FormatFile_ParsesAllLinesAndReturnsFormattedText() { var lines = new[] { """{"type":"stream_event","event":{"type":"content_block_delta","index":0,"delta":{"type":"text_delta","text":"Hello"}}}""", """{"type":"stream_event","event":{"type":"content_block_start","index":1,"content_block":{"type":"tool_use","id":"x","name":"bash"}}}""", """{"type":"result","result":"Done."}""", }; var file = Path.GetTempFileName(); try { File.WriteAllLines(file, lines); var result = _formatter.FormatFile(file); Assert.Contains("Hello", result); Assert.Contains("[Tool: bash]", result); Assert.Contains("Done.", result); } finally { File.Delete(file); } } [Fact] public void FormatFile_TrimsLargeContent() { var chunk = new string('x', 1000); var line = "{\"type\":\"stream_event\",\"event\":{\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"" + chunk + "\"}}}"; var lines = Enumerable.Repeat(line, 65).ToArray(); var file = Path.GetTempFileName(); try { File.WriteAllLines(file, lines); var result = _formatter.FormatFile(file); Assert.True(result.Length <= 50_200, $"Expected <= 50200 but got {result.Length}"); } finally { File.Delete(file); } } }