import os import tempfile import unittest from src.gui.backend_client import AppCorePreferences, DesktopBackendClient, ProjectState, SessionTabSnapshot from src.gui.backend_runner import BackendRunner class TestBackendContract(unittest.TestCase): @classmethod def setUpClass(cls): cls._state_root = tempfile.TemporaryDirectory() cls._old_session_path = os.environ.get("State__SessionPath") cls._old_prefs_path = os.environ.get("State__CorePreferencesPath") os.environ["State__SessionPath"] = os.path.join(cls._state_root.name, "session_snapshots.json") os.environ["State__CorePreferencesPath"] = os.path.join(cls._state_root.name, "core_preferences.json") cls.runner = BackendRunner() cls.runner.start() cls.client = DesktopBackendClient() @classmethod def tearDownClass(cls): cls.runner.stop() if cls._old_session_path is None: os.environ.pop("State__SessionPath", None) else: os.environ["State__SessionPath"] = cls._old_session_path if cls._old_prefs_path is None: os.environ.pop("State__CorePreferencesPath", None) else: os.environ["State__CorePreferencesPath"] = cls._old_prefs_path cls._state_root.cleanup() def test_project_round_trip(self): with tempfile.TemporaryDirectory() as tmp: project_file = os.path.join(tmp, ".lyricproject") state = ProjectState( version=2, name="demo", open_files=[os.path.join(tmp, "one.lmd")], active_file=os.path.join(tmp, "one.lmd"), cursor_positions={os.path.join(tmp, "one.lmd"): 12}, scratchpad_open=True, ) self.client.write_project(project_file, state) loaded = self.client.read_project(project_file) self.assertEqual(state.to_payload(), loaded.to_payload()) def test_session_round_trip(self): snapshot = SessionTabSnapshot( tab_id="untitled::0", file_path=None, display_name="Untitled", content="hook line", cursor_position=4, is_dirty=True, is_untitled=True, snapshot_mtime=None, workspace_root=None, updated_at="2026-03-13T00:00:00+00:00", ) self.client.save_session(None, [snapshot]) loaded = self.client.load_session(None) self.assertEqual(1, len(loaded)) self.assertEqual(snapshot.display_name, loaded[0].display_name) self.client.clear_session(None) def test_core_preferences_round_trip(self): prefs = AppCorePreferences( reopen_last_project=False, restore_unsaved_tabs=False, last_project_file="C:/demo/.lyricproject", ) self.client.save_core_preferences(prefs) loaded = self.client.load_core_preferences() self.assertEqual(prefs, loaded) def test_file_operations_round_trip(self): with tempfile.TemporaryDirectory() as tmp: file_path = os.path.join(tmp, "draft.lmd") renamed_path = os.path.join(tmp, "renamed.lmd") created = self.client.create_entry(file_path) self.assertTrue(created.success, created.message) written = self.client.write_file(file_path, "verse") self.assertTrue(written.success, written.message) loaded = self.client.read_file(file_path) self.assertTrue(loaded.success, loaded.message) self.assertEqual("verse", loaded.content) renamed = self.client.rename_entry(file_path, renamed_path, tmp) self.assertTrue(renamed.success, renamed.message) deleted = self.client.delete_entry(renamed_path, tmp) self.assertTrue(deleted.success, deleted.message) if __name__ == "__main__": unittest.main()