- introduce `LyricFlow.Core.Backend` with shared DTOs, rhyme/spellcheck engines, and REST endpoints - wire Python GUI/core to run and call the backend via new bridge/client modules - add backend parity/integration tests and update packaging/ignore settings
102 lines
3.9 KiB
Python
102 lines
3.9 KiB
Python
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()
|