using JournalBot.Shared; using Xunit; public class RuntimeReaderTests { private static string MakeRuntime() { var root = Path.Combine(Path.GetTempPath(), "jb_test_" + Guid.NewGuid().ToString("N")); foreach (var sub in new[] { "queue/pending", "queue/done", "queue/failed", "logs" }) Directory.CreateDirectory(Path.Combine(root, sub)); return root; } [Fact] public void ReadHistory_parsesDoneItemsNewestFirst() { var root = MakeRuntime(); var done = Path.Combine(root, "queue", "done"); File.WriteAllText(Path.Combine(done, "1.json"), "{\"update_id\":1,\"received_at\":\"2026-06-14T10:00:00+02:00\",\"type\":\"text\",\"text\":\"a\",\"target_path\":\"05 Daily Notes/2026-06-14.md\",\"processed_at\":\"2026-06-14T11:00:00+02:00\"}"); File.WriteAllText(Path.Combine(done, "2.json"), "{\"update_id\":2,\"received_at\":\"2026-06-14T12:00:00+02:00\",\"type\":\"voice\",\"text\":\"b\",\"processed_at\":\"2026-06-14T13:00:00+02:00\"}"); var reader = new RuntimeReader(root); var history = reader.ReadHistory(); Assert.Equal(2, history.Count); Assert.Equal(2, history[0].UpdateId); // newest processed_at first Assert.Equal("05 Daily Notes/2026-06-14.md", history[1].TargetPath); } [Fact] public void ReadHistory_readsUmlautsAsUtf8() { var root = MakeRuntime(); var done = Path.Combine(root, "queue", "done"); // Python writes done items as UTF-8; read-back must not mangle umlauts. File.WriteAllText(Path.Combine(done, "1.json"), "{\"update_id\":1,\"received_at\":\"2026-06-14T10:00:00+02:00\",\"type\":\"text\",\"text\":\"Geburtstagsfeier für Jörg\",\"processed_at\":\"2026-06-14T11:00:00+02:00\"}", System.Text.Encoding.UTF8); var history = new RuntimeReader(root).ReadHistory(); Assert.Single(history); Assert.Equal("Geburtstagsfeier für Jörg", history[0].Text); } [Fact] public void TailLog_readsUmlautsAsUtf8() { var root = MakeRuntime(); File.WriteAllText(Path.Combine(root, "logs", "service.log"), "Klärung: Eintrag geschrieben für Jörg", System.Text.Encoding.UTF8); var lines = new RuntimeReader(root).TailLog("service.log", 5); Assert.Equal("Klärung: Eintrag geschrieben für Jörg", lines[^1]); } [Fact] public void ReadStatus_countsPendingAndFailed() { var root = MakeRuntime(); File.WriteAllText(Path.Combine(root, "queue", "pending", "1.json"), "{}"); File.WriteAllText(Path.Combine(root, "queue", "pending", "2.json"), "{}"); File.WriteAllText(Path.Combine(root, "queue", "failed", "3.json"), "{}"); var status = new RuntimeReader(root).ReadStatus(); Assert.Equal(2, status.Pending); Assert.Equal(1, status.Failed); } [Fact] public void TailLog_returnsLastLines() { var root = MakeRuntime(); File.WriteAllText(Path.Combine(root, "logs", "service.log"), string.Join("\n", Enumerable.Range(1, 100).Select(i => $"line {i}"))); var lines = new RuntimeReader(root).TailLog("service.log", 5); Assert.Equal(5, lines.Count); Assert.Equal("line 100", lines[^1]); } }