"""Round 38: one merge window, and play counts that can't conflict. Two bugs stacked fifteen "Synced changes merged" windows on the desktop: the dialog was never reused, and playing a song rewrote all 15 MB of library.json — so Syncthing produced a conflict file roughly once per track. The dialog is now a session-long log, and play counts live in per-machine journal files that only their owner ever writes. """ from datetime import datetime import pytest from lintunes.storage.conflict_resolver import ConflictSummary def _summary(file="library.json", lines=("2 track(s) reconciled.",), backup="/b/1"): return ConflictSummary(file, "library", list(lines), backup) @pytest.fixture def dialog_cls(qapp): from lintunes.gui.conflict_dialog import ConflictSummaryDialog return ConflictSummaryDialog class TestOneMergeWindow: def test_second_merge_appends_instead_of_replacing(self, dialog_cls): dlg = dialog_cls([_summary(lines=["first merge"])], manager=None) dlg.add_event([_summary(lines=["second merge"])]) text = dlg._body.toPlainText() assert "first merge" in text and "second merge" in text # Newest first. assert text.index("second merge") < text.index("first merge") def test_events_are_timestamped_once_there_is_more_than_one(self, dialog_cls): dlg = dialog_cls([_summary()], manager=None) assert "──" not in dlg._body.toPlainText() # a lone merge needs no header dlg.add_event([_summary()], when=datetime(2026, 8, 20, 18, 42)) text = dlg._body.toPlainText() assert "6:42 PM" in text assert "2 merges this session." in dlg._intro.text() def test_restore_targets_the_newest_backup_and_names_it(self, dialog_cls): dlg = dialog_cls([_summary(backup="/b/old")], manager=None) assert dlg._backup_dir == "/b/old" dlg.add_event([_summary(backup="/b/new")], when=datetime(2026, 8, 20, 18, 42)) assert dlg._backup_dir == "/b/new" assert dlg._restore_btn.text() == "Restore backup from 6:42 PM" def test_empty_event_leaves_the_previous_backup_reachable(self, dialog_cls): dlg = dialog_cls([_summary(backup="/b/old")], manager=None) dlg.add_event([]) assert dlg._backup_dir == "/b/old" assert dlg._restore_btn.isEnabled() def test_no_backup_disables_the_backup_buttons(self, dialog_cls): dlg = dialog_cls([_summary(backup="")], manager=None) assert not dlg._open_btn.isEnabled() assert not dlg._restore_btn.isEnabled() class TestWindowReuse: """MainWindow isn't constructible headless, so exercise the two real methods on a minimal QWidget host — that's where the stacking bug lived.""" @pytest.fixture def host(self, qapp): from PyQt6.QtWidgets import QWidget from lintunes.gui.main_window import MainWindow class Host(QWidget): _manager = None _conflict_dialog = None show_conflict_summary = MainWindow.show_conflict_summary _on_conflict_dialog_closed = MainWindow._on_conflict_dialog_closed return Host() def test_repeated_merges_make_exactly_one_dialog(self, host): from lintunes.gui.conflict_dialog import ConflictSummaryDialog for i in range(15): host.show_conflict_summary([_summary(lines=[f"merge {i}"])]) dialogs = host.findChildren(ConflictSummaryDialog) assert len(dialogs) == 1 text = dialogs[0]._body.toPlainText() assert "merge 0" in text and "merge 14" in text def test_closing_lets_the_next_merge_open_a_fresh_window(self, host): host.show_conflict_summary([_summary()]) first = host._conflict_dialog first.accept() # "Keep merged" assert host._conflict_dialog is None host.show_conflict_summary([_summary()]) assert host._conflict_dialog is not None assert host._conflict_dialog is not first