pyLyricFlow/tests/test_backend_contract.py
stan44 e0f298ba36 Add LyricFlow .NET backend API and Python bridge integration
- 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
2026-03-15 01:44:56 -05:00

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()