from __future__ import annotations import json import logging import os import tempfile import unittest from app.utils.call_timeline import CallTimeline class CallTimelineTests(unittest.TestCase): def test_emit_writes_jsonl_with_relative_time(self) -> None: logger = logging.getLogger("test.call_timeline") logger.handlers = [] logger.addHandler(logging.NullHandler()) with tempfile.TemporaryDirectory() as tmpdir: old_dir = os.environ.get("CALL_TIMELINE_DIR") old_console = os.environ.get("CALL_TIMELINE_CONSOLE") old_enabled = os.environ.get("CALL_TIMELINE_ENABLED") os.environ["CALL_TIMELINE_DIR"] = tmpdir os.environ["CALL_TIMELINE_CONSOLE"] = "0" os.environ["CALL_TIMELINE_ENABLED"] = "1" try: timeline = CallTimeline( logger=logger, component="bridge", timeline_id="room-dev-123", protocol="PRT-1", room="room-dev-123", session_id="PRT-1", phone_number="31999999999", origin_unix_ms=1000, ) timeline.emit("call_start", ok=True, nested={"a": 1}) timeline.emit("ready_sent") self.assertTrue(timeline.flush(timeout=5.0)) with timeline.path.open("r", encoding="utf-8") as handle: lines = [json.loads(line) for line in handle if line.strip()] finally: if old_dir is None: os.environ.pop("CALL_TIMELINE_DIR", None) else: os.environ["CALL_TIMELINE_DIR"] = old_dir if old_console is None: os.environ.pop("CALL_TIMELINE_CONSOLE", None) else: os.environ["CALL_TIMELINE_CONSOLE"] = old_console if old_enabled is None: os.environ.pop("CALL_TIMELINE_ENABLED", None) else: os.environ["CALL_TIMELINE_ENABLED"] = old_enabled self.assertEqual(2, len(lines)) self.assertEqual("bridge", lines[0]["component"]) self.assertEqual("call_start", lines[0]["event"]) self.assertEqual("room-dev-123", lines[0]["timeline_id"]) self.assertEqual("PRT-1", lines[0]["protocol"]) self.assertEqual("31999999999", lines[0]["phone_number"]) self.assertTrue(lines[0]["ok"]) self.assertEqual({"a": 1}, lines[0]["nested"]) self.assertGreaterEqual(lines[0]["t_rel_ms"], 0) self.assertEqual("ready_sent", lines[1]["event"]) def test_background_writer_preserves_order_for_many_events(self) -> None: logger = logging.getLogger("test.call_timeline.order") logger.handlers = [logging.NullHandler()] with tempfile.TemporaryDirectory() as tmpdir: old_dir = os.environ.get("CALL_TIMELINE_DIR") old_enabled = os.environ.get("CALL_TIMELINE_ENABLED") os.environ["CALL_TIMELINE_DIR"] = tmpdir os.environ["CALL_TIMELINE_ENABLED"] = "1" try: timeline = CallTimeline( logger=logger, component="bridge", timeline_id="room-order", origin_unix_ms=1000, ) for index in range(200): timeline.emit("tick", index=index) self.assertTrue(timeline.flush(timeout=5.0)) with timeline.path.open("r", encoding="utf-8") as handle: lines = [json.loads(line) for line in handle if line.strip()] finally: if old_dir is None: os.environ.pop("CALL_TIMELINE_DIR", None) else: os.environ["CALL_TIMELINE_DIR"] = old_dir if old_enabled is None: os.environ.pop("CALL_TIMELINE_ENABLED", None) else: os.environ["CALL_TIMELINE_ENABLED"] = old_enabled self.assertEqual(list(range(200)), [line["index"] for line in lines]) if __name__ == "__main__": unittest.main()