@@ -1011,19 +1011,102 @@ func test_editor_log_buffer_ring_evicts_and_tracks_dropped() -> void:
10111011 buf .append ("error" , "n %d " % i , "res://x.gd" , i )
10121012 assert_eq (buf .total_count (), cap , "Buffer should cap at MAX_LINES" )
10131013 assert_eq (buf .dropped_count (), 7 , "Should record 7 evictions" )
1014+ assert_eq (buf .appended_total (), cap + 7 , "Cursor should advance for every append" )
10141015 ## Oldest 7 dropped: first remaining entry should be index 7.
10151016 var first := buf .get_range (0 , 1 )
10161017 assert_eq (first [0 ].text , "n 7" )
10171018
10181019
1020+ func test_editor_log_buffer_get_since_returns_entries_after_cursor () -> void :
1021+ var buf := McpEditorLogBuffer .new ()
1022+ buf .append ("error" , "before-a" , "res://a.gd" , 1 )
1023+ buf .append ("error" , "before-b" , "res://b.gd" , 2 )
1024+ var cursor := buf .appended_total ()
1025+ buf .append ("error" , "after-a" , "res://a.gd" , 3 )
1026+ buf .append ("warn" , "after-b" , "res://b.gd" , 4 )
1027+
1028+ var result := buf .get_since (cursor )
1029+ assert_eq (result .cursor , cursor )
1030+ assert_eq (result .entries .size (), 2 )
1031+ assert_eq (result .entries [0 ].text , "after-a" )
1032+ assert_eq (result .entries [1 ].text , "after-b" )
1033+ assert_eq (result .next_cursor , buf .appended_total ())
1034+ assert_false (result .truncated )
1035+ assert_false (result .has_more )
1036+
1037+
1038+ func test_editor_log_buffer_get_since_reports_overflow_truncation () -> void :
1039+ var buf := McpEditorLogBuffer .new ()
1040+ var cap := McpEditorLogBuffer .MAX_LINES
1041+ var cursor := buf .appended_total ()
1042+ for i in range (cap + 3 ):
1043+ buf .append ("error" , "storm %d " % i , "res://storm.gd" , i )
1044+
1045+ var result := buf .get_since (cursor )
1046+ assert_true (result .truncated , "Overflow after the cursor must be visible" )
1047+ assert_eq (result .entries .size (), cap )
1048+ assert_eq (result .entries [0 ].text , "storm 3" )
1049+ assert_eq (result .entries [cap - 1 ].text , "storm %d " % (cap + 2 ))
1050+ assert_eq (result .oldest_cursor , 3 )
1051+ assert_eq (result .next_cursor , buf .appended_total ())
1052+
1053+
1054+ func test_editor_log_buffer_get_since_limit_paginates_without_losing_cursor () -> void :
1055+ var buf := McpEditorLogBuffer .new ()
1056+ for i in range (5 ):
1057+ buf .append ("error" , "page %d " % i )
1058+
1059+ var first := buf .get_since (0 , 2 )
1060+ assert_eq (first .entries .size (), 2 )
1061+ assert_eq (first .entries [0 ].text , "page 0" )
1062+ assert_eq (first .next_cursor , 2 )
1063+ assert_true (first .has_more )
1064+
1065+ var second := buf .get_since (first .next_cursor , 10 )
1066+ assert_eq (second .entries .size (), 3 )
1067+ assert_eq (second .entries [0 ].text , "page 2" )
1068+ assert_eq (second .next_cursor , 5 )
1069+ assert_false (second .has_more )
1070+
1071+
1072+ func test_editor_log_buffer_get_since_future_cursor_clamps_to_tail () -> void :
1073+ var buf := McpEditorLogBuffer .new ()
1074+ for i in range (3 ):
1075+ buf .append ("error" , "future %d " % i )
1076+
1077+ var result := buf .get_since (99 )
1078+ assert_false (result .truncated )
1079+ assert_eq (result .entries .size (), 0 )
1080+ assert_eq (result .next_cursor , buf .appended_total ())
1081+ assert_false (result .has_more )
1082+
1083+
10191084func test_editor_log_buffer_clear_resets_counts () -> void :
10201085 var buf := McpEditorLogBuffer .new ()
10211086 for i in range (5 ):
10221087 buf .append ("error" , "n %d " % i )
1088+ var cursor := buf .appended_total ()
10231089 var cleared := buf .clear ()
10241090 assert_eq (cleared , 5 , "clear() should report cleared count" )
10251091 assert_eq (buf .total_count (), 0 )
10261092 assert_eq (buf .dropped_count (), 0 )
1093+ assert_eq (buf .appended_total (), cursor , "clear() must not reset the cursor" )
1094+
1095+
1096+ func test_editor_log_buffer_get_since_reports_clear_truncation () -> void :
1097+ var buf := McpEditorLogBuffer .new ()
1098+ for i in range (5 ):
1099+ buf .append ("error" , "before-clear %d " % i )
1100+ var stale_cursor := 2
1101+ buf .clear ()
1102+ buf .append ("error" , "after-clear" , "res://after.gd" , 9 )
1103+
1104+ var result := buf .get_since (stale_cursor )
1105+ assert_true (result .truncated , "Clear after the cursor should degrade the window" )
1106+ assert_eq (result .entries .size (), 1 )
1107+ assert_eq (result .entries [0 ].text , "after-clear" )
1108+ assert_eq (result .oldest_cursor , 5 )
1109+ assert_eq (result .next_cursor , 6 )
10271110
10281111
10291112# ----- get_logs source="editor" routing (issue #231) -----
0 commit comments