from __future__ import annotations from yuxi.channel.capabilities import CapabilityMatrix from yuxi.channel.capabilities.levels import ( EditSupport, InteractiveSupport, MarkdownSupport, MediaSupport, StreamingSupport, ThreadSupport, ) from yuxi.channel.capabilities.negotiation import ( CapabilityRequirement, get_required_ports, negotiate, supports, ) from yuxi.channel.plugins.protocol import ChannelCapability, ChannelMeta from yuxi.channel.ports import AuthPort, InboundPort, OutboundPort, SessionPort class TestCapabilityMatrixDefaults: def test_defaults(self): matrix = CapabilityMatrix() assert matrix.text is True assert matrix.markdown == MarkdownSupport.NONE assert matrix.media == {MediaSupport.NONE} assert matrix.interactive == InteractiveSupport.NONE assert matrix.reactions is False assert matrix.threads == ThreadSupport.NONE assert matrix.edits == EditSupport.NONE assert matrix.streaming == StreamingSupport.NONE assert matrix.batch_send is False assert matrix.qr_login is False assert matrix.scan_pairing is False assert matrix.pin is False assert matrix.directory is False class TestCapabilityMatrixFromChannelCapability: def test_text_only(self): matrix = CapabilityMatrix.from_channel_capability(ChannelCapability.TEXT) assert matrix.text is True assert matrix.markdown == MarkdownSupport.NONE assert matrix.media == {MediaSupport.NONE} def test_markdown(self): matrix = CapabilityMatrix.from_channel_capability(ChannelCapability.MARKDOWN) assert matrix.markdown == MarkdownSupport.BASIC def test_image_and_file(self): matrix = CapabilityMatrix.from_channel_capability(ChannelCapability.IMAGE | ChannelCapability.FILE) assert matrix.media == {MediaSupport.IMAGE, MediaSupport.FILE} def test_interactive(self): matrix = CapabilityMatrix.from_channel_capability(ChannelCapability.INTERACTIVE) assert matrix.interactive == InteractiveSupport.STATIC def test_reaction(self): matrix = CapabilityMatrix.from_channel_capability(ChannelCapability.REACTION) assert matrix.reactions is True def test_thread(self): matrix = CapabilityMatrix.from_channel_capability(ChannelCapability.THREAD) assert matrix.threads == ThreadSupport.REPLY def test_edit(self): matrix = CapabilityMatrix.from_channel_capability(ChannelCapability.EDIT) assert matrix.edits == EditSupport.WITHIN_MINUTE def test_streaming(self): matrix = CapabilityMatrix.from_channel_capability(ChannelCapability.STREAMING) assert matrix.streaming == StreamingSupport.TYPING def test_combined_flags(self): caps = ( ChannelCapability.TEXT | ChannelCapability.MARKDOWN | ChannelCapability.IMAGE | ChannelCapability.INTERACTIVE | ChannelCapability.THREAD ) matrix = CapabilityMatrix.from_channel_capability(caps) assert matrix.text is True assert matrix.markdown == MarkdownSupport.BASIC assert MediaSupport.IMAGE in matrix.media assert matrix.interactive == InteractiveSupport.STATIC assert matrix.threads == ThreadSupport.REPLY class TestCapabilityMatrixSupports: def test_text_supported_by_default(self): matrix = CapabilityMatrix() assert matrix.supports("text") is True def test_markdown_when_none(self): matrix = CapabilityMatrix() assert matrix.supports("markdown") is False def test_markdown_when_basic(self): matrix = CapabilityMatrix(markdown=MarkdownSupport.BASIC) assert matrix.supports("markdown") is True def test_media_when_none(self): matrix = CapabilityMatrix() assert matrix.supports("media") is False def test_media_when_image(self): matrix = CapabilityMatrix(media={MediaSupport.IMAGE}) assert matrix.supports("media") is True def test_interactive_when_static(self): matrix = CapabilityMatrix(interactive=InteractiveSupport.STATIC) assert matrix.supports("interactive") is True def test_reactions(self): matrix = CapabilityMatrix(reactions=True) assert matrix.supports("reactions") is True def test_threads(self): matrix = CapabilityMatrix(threads=ThreadSupport.REPLY) assert matrix.supports("threads") is True def test_edits(self): matrix = CapabilityMatrix(edits=EditSupport.FOREVER) assert matrix.supports("edits") is True def test_streaming(self): matrix = CapabilityMatrix(streaming=StreamingSupport.FULL) assert matrix.supports("streaming") is True def test_batch_send(self): matrix = CapabilityMatrix(batch_send=True) assert matrix.supports("batch_send") is True def test_qr_login(self): matrix = CapabilityMatrix(qr_login=True) assert matrix.supports("qr_login") is True def test_scan_pairing(self): matrix = CapabilityMatrix(scan_pairing=True) assert matrix.supports("scan_pairing") is True def test_unknown_capability(self): matrix = CapabilityMatrix() assert matrix.supports("unknown") is False class TestCapabilityMatrixOverrides: def test_override_disabled(self): matrix = CapabilityMatrix(text=True, markdown=MarkdownSupport.BASIC) config = {"capability_overrides": {"text": "disabled", "markdown": "disabled"}} assert matrix.supports("text", config) is False assert matrix.supports("markdown", config) is False def test_override_enabled(self): matrix = CapabilityMatrix() config = {"capability_overrides": {"text": "enabled", "markdown": "enabled"}} assert matrix.supports("text", config) is True assert matrix.supports("markdown", config) is True def test_override_level(self): matrix = CapabilityMatrix(markdown=MarkdownSupport.BASIC) config = {"capability_overrides": {"markdown": "extended"}} assert matrix.supports("markdown", config) is True def test_override_invalid_level_returns_false(self): matrix = CapabilityMatrix(markdown=MarkdownSupport.BASIC) config = {"capability_overrides": {"markdown": "not-a-level"}} assert matrix.supports("markdown", config) is False def test_override_media(self): matrix = CapabilityMatrix() config = {"capability_overrides": {"media": "image"}} assert matrix.supports("media", config) is True def test_override_ignores_unrelated_keys(self): matrix = CapabilityMatrix(text=True) config = {"capability_overrides": {"unknown": "enabled"}} assert matrix.supports("text", config) is True class TestSupportsFunction: def test_supports_when_matrix_present(self): class FakeChannel: def get_meta(self) -> ChannelMeta: return ChannelMeta( channel_type="test", display_name="Test", capability_matrix=CapabilityMatrix(text=True), ) assert supports(FakeChannel(), "text") is True def test_supports_false_when_not_supported(self): class FakeChannel: def get_meta(self) -> ChannelMeta: return ChannelMeta( channel_type="test", display_name="Test", capability_matrix=CapabilityMatrix(), ) assert supports(FakeChannel(), "markdown") is False def test_supports_with_config_override(self): class FakeChannel: def get_meta(self) -> ChannelMeta: return ChannelMeta( channel_type="test", display_name="Test", capability_matrix=CapabilityMatrix(markdown=MarkdownSupport.BASIC), ) config = {"capability_overrides": {"markdown": "disabled"}} assert supports(FakeChannel(), "markdown", config) is False def test_supports_returns_false_without_meta(self): class FakeChannel: pass assert supports(FakeChannel(), "text") is False # type: ignore[arg-type] def test_supports_returns_false_without_matrix(self): class FakeChannel: def get_meta(self) -> ChannelMeta: meta = ChannelMeta(channel_type="test", display_name="Test") meta.capability_matrix = None # type: ignore[assignment] return meta assert supports(FakeChannel(), "text") is False class TestNegotiateFunction: def test_negotiate_supported(self): class FakeChannel: def get_meta(self) -> ChannelMeta: return ChannelMeta( channel_type="test", display_name="Test", capability_matrix=CapabilityMatrix(text=True), ) req = CapabilityRequirement(capability="text") result = negotiate(FakeChannel(), req) assert result.supported is True assert result.fallback == "ignore" def test_negotiate_unsupported(self): class FakeChannel: def get_meta(self) -> ChannelMeta: return ChannelMeta( channel_type="test", display_name="Test", capability_matrix=CapabilityMatrix(), ) req = CapabilityRequirement(capability="markdown", fallback="text") result = negotiate(FakeChannel(), req) assert result.supported is False assert result.fallback == "text" def test_negotiate_level_met(self): class FakeChannel: def get_meta(self) -> ChannelMeta: return ChannelMeta( channel_type="test", display_name="Test", capability_matrix=CapabilityMatrix(markdown=MarkdownSupport.EXTENDED), ) req = CapabilityRequirement(capability="markdown", level="basic") result = negotiate(FakeChannel(), req) assert result.supported is True def test_negotiate_level_not_met(self): class FakeChannel: def get_meta(self) -> ChannelMeta: return ChannelMeta( channel_type="test", display_name="Test", capability_matrix=CapabilityMatrix(markdown=MarkdownSupport.BASIC), ) req = CapabilityRequirement(capability="markdown", level="extended") result = negotiate(FakeChannel(), req) assert result.supported is False class TestGetRequiredPorts: def test_text_requires_outbound(self): assert get_required_ports("text") == {OutboundPort} def test_media_requires_outbound(self): assert get_required_ports("media") == {OutboundPort} def test_threads_requires_outbound_and_session(self): assert get_required_ports("threads") == {OutboundPort, SessionPort} def test_qr_login_requires_auth(self): assert get_required_ports("qr_login") == {AuthPort} def test_scan_pairing_requires_inbound(self): assert get_required_ports("scan_pairing") == {InboundPort} def test_unknown_capability_returns_empty(self): assert get_required_ports("unknown") == set()