"""VoiceSession now accepts text_channel_id and voice_channel_id separately. Locks in the public contract from the voice/text unify plan: the two ids are stored as distinct attributes and both appear in the metrics payload under their own keys (claude_session_key + voice_channel_id). """ from __future__ import annotations import json from pathlib import Path from unittest.mock import MagicMock import pytest from src.voice import pipeline as pipeline_mod from src.voice.pipeline import VoiceSession def _make_session(text_id: int, voice_id: int) -> VoiceSession: return VoiceSession( text_channel_id=text_id, voice_channel_id=voice_id, guild_id=42, voice_client=MagicMock(name="voice_client"), bot=MagicMock(name="bot"), ttsq=MagicMock(name="ttsq"), whitelist=set(), record_enabled=False, mirror_enabled=True, transcripts_jsonl_path=None, loop=None, router_route_message=MagicMock(name="route_message"), ) def test_constructor_stores_separate_channel_ids(): session = _make_session(1001, 2002) assert session.text_channel_id == 1001 assert session.voice_channel_id == 2002 assert session.text_channel_id != session.voice_channel_id def test_constructor_rejects_legacy_channel_id_kwarg(): with pytest.raises(TypeError): VoiceSession( channel_id=1001, # legacy single id no longer accepted voice_channel_id=2002, guild_id=42, voice_client=MagicMock(), bot=MagicMock(), ttsq=MagicMock(), ) def test_metric_payload_contains_both_ids(tmp_path: Path, monkeypatch): metrics_file = tmp_path / "voice_metrics.jsonl" monkeypatch.setattr(pipeline_mod, "LOGS_DIR", tmp_path) monkeypatch.setattr(pipeline_mod, "VOICE_METRICS_PATH", metrics_file) session = _make_session(1001, 2002) session._log_metric({"event": "test_event", "extra": "x"}) lines = metrics_file.read_text(encoding="utf-8").splitlines() assert len(lines) == 1 event = json.loads(lines[0]) assert event["claude_session_key"] == "1001" assert event["voice_channel_id"] == 2002 assert event["event"] == "test_event" assert event["extra"] == "x" assert "channel_id" not in event def test_metric_keys_are_distinct(): # Same numeric id for both must still serialize as two separate keys. session = _make_session(5555, 5555) payload = { "ts": 0.0, "claude_session_key": str(session.text_channel_id), "voice_channel_id": session.voice_channel_id, } assert payload["claude_session_key"] == "5555" assert payload["voice_channel_id"] == 5555 assert isinstance(payload["claude_session_key"], str) assert isinstance(payload["voice_channel_id"], int)