import datetime from minecraft_stack import _server_performance def test_tick_lag_and_movement_are_distinct(): now = datetime.datetime(2026, 9, 27, 16, 30, 0) lines = [ "[2026-09-27 16:24:00] minecraft> [Server thread/WARN]: Can't keep up! Is the server overloaded? Running 9000ms or 180 ticks behind", "[2026-09-27 16:27:00] minecraft> [Server thread/WARN]: IffyBinkie6134 moved too quickly!", "[2026-09-27 16:29:00] minecraft> [Server thread/WARN]: Can't keep up! Is the server overloaded? Running 321ms or 6 ticks behind", "[2026-09-27 16:29:10] minecraft> [Server thread/WARN]: IffyBinkie6134 moved wrongly!", ] result = _server_performance(lines, now) assert result == { "window_seconds": 300, "tick_lag_events": 1, "max_reported_tick_delay_ms": 321, "movement_warnings": 2, "movement_warning_samples": [ "[2026-09-27 16:27:00] minecraft> [Server thread/WARN]: IffyBinkie6134 moved too quickly!", "[2026-09-27 16:29:10] minecraft> [Server thread/WARN]: IffyBinkie6134 moved wrongly!", ], "last_tick_lag_at": "2026-09-27 16:29:00", } assert _server_performance(lines[:1], now)["max_reported_tick_delay_ms"] is None def test_movement_samples_name_the_entity_behind_the_burst(): # Measured: the counter went 0 -> 107 in under a minute and then stopped. A # count cannot say whether one entity joining caused it or the whole server is # degrading, so the entity has to be in the payload. now = datetime.datetime(2026, 9, 29, 10, 30, 0) lines = [ f"[2026-09-29 10:29:{second:02d}] minecraft> [Server thread/WARN]: Naldo_Bot moved too quickly! 12.5,0.0,-8.25" for second in range(0, 40) ] result = _server_performance(lines, now) assert result["movement_warnings"] == 40 # The burst carries a distinct timestamp per entry, so the samples must # collapse on the message: one entity, one problem, one sample. assert result["movement_warning_samples"] == [ "[2026-09-29 10:29:00] minecraft> [Server thread/WARN]: Naldo_Bot moved too quickly! 12.5,0.0,-8.25" ], "a burst from one entity must not fill the payload with copies" assert "Naldo_Bot" in result["movement_warning_samples"][0] def test_movement_samples_are_bounded(): now = datetime.datetime(2026, 9, 29, 10, 30, 0) lines = [ f"[2026-09-29 10:29:{second:02d}] minecraft> [Server thread/WARN]: Bot{second} moved wrongly!" for second in range(0, 20) ] result = _server_performance(lines, now) assert result["movement_warnings"] == 20 assert len(result["movement_warning_samples"]) == 5, "the payload must stay small" assert all(len(sample) <= 200 for sample in result["movement_warning_samples"]) def test_samples_are_empty_when_nothing_moved_wrongly(): now = datetime.datetime(2026, 9, 29, 10, 30, 0) result = _server_performance( ["[2026-09-29 10:29:00] minecraft> [Server thread/INFO]: Done (1.2s)!"], now ) assert result["movement_warnings"] == 0 assert result["movement_warning_samples"] == [] if __name__ == "__main__": test_tick_lag_and_movement_are_distinct() test_movement_samples_name_the_entity_behind_the_burst() test_movement_samples_are_bounded() test_samples_are_empty_when_nothing_moved_wrongly() print("PASS bounded vanilla tick-lag and movement diagnostics")