from __future__ import annotations import pytest from yuxi.channels.adapters.twitch.irc_parser import ( ParsedIRCMessage, build_identity_tags, extract_nick, make_irc_message_id, parse_badges, parse_emotes, parse_irc_line, parse_prefix, ) PRIVMSG = ( "@badge-info=subscriber/1;badges=subscriber/0;color=#FF4500;" "display-name=TestUser;emotes=;flags=;id=abc-123;mod=0;" "room-id=123456;subscriber=1;tmi-sent-ts=1600000000000;" "turbo=0;user-id=789;user-type= " ":testuser!testuser@testuser.tmi.twitch.tv PRIVMSG #channel :Hello world" ) PRIVMSG_WITH_EMOTES = ( "@badges=;emotes=425618:0-7;user-id=789 " ":testuser!testuser@testuser.tmi.twitch.tv PRIVMSG #channel :Kreygasm hello" ) PRIVMSG_WITH_BITS = ( "@badges=;bits=100;user-id=789 " ":testuser!testuser@testuser.tmi.twitch.tv PRIVMSG #channel :Cheer100 hello" ) PING = "PING :tmi.twitch.tv" NOTICE = ( "@msg-id=slow_off :tmi.twitch.tv NOTICE #channel :This room is no longer in slow mode." ) USERNOTICE = ( "@badges=subscriber/6;color=#FF4500;display-name=SubUser;" "emotes=;flags=;id=sub-msg-1;login=subuser;mod=0;" "msg-id=sub;msg-param-cumulative-months=6;msg-param-months=0;" "msg-param-should-share-streak=1;msg-param-sub-plan-name=Channel\\sSubscription;" "msg-param-sub-plan=1000;room-id=123456;subscriber=1;" "system-msg=SubUser\\ssubscribed\\sat\\sTier\\s1.;" "tmi-sent-ts=1600000000000;user-id=789;user-type= " ":tmi.twitch.tv USERNOTICE #channel" ) class TestParseIRCLine: def test_parse_privmsg(self): parsed = parse_irc_line(PRIVMSG) assert parsed.command == "PRIVMSG" assert parsed.prefix == "testuser!testuser@testuser.tmi.twitch.tv" assert parsed.params == ["#channel"] assert parsed.trailing == "Hello world" assert parsed.tags["display-name"] == "TestUser" assert parsed.tags["user-id"] == "789" assert parsed.tags["mod"] == "0" def test_parse_ping(self): parsed = parse_irc_line(PING) assert parsed.command == "PING" assert "tmi.twitch.tv" in (parsed.trailing or " ".join(parsed.params)) def test_parse_notice(self): parsed = parse_irc_line(NOTICE) assert parsed.command == "NOTICE" assert parsed.params == ["#channel"] assert "slow_off" in parsed.tags.get("msg-id", "") def test_parse_usernotice(self): parsed = parse_irc_line(USERNOTICE) assert parsed.command == "USERNOTICE" assert parsed.tags["msg-id"] == "sub" assert parsed.tags["msg-param-sub-plan"] == "1000" def test_parse_empty_line(self): parsed = parse_irc_line("") assert parsed.command == "" def test_parse_invalid_line(self): parsed = parse_irc_line("garbage data without structure") assert parsed.command == "GARBAGE" def test_parse_cmd_only(self): parsed = parse_irc_line("PING") assert parsed.command == "PING" class TestParsePrefix: def test_full_prefix(self): result = parse_prefix("user!user@host") assert result["nick"] == "user" assert result["user"] == "user" assert result["host"] == "host" def test_nick_only(self): result = parse_prefix("justnick") assert result["nick"] == "justnick" def test_none_prefix(self): result = parse_prefix(None) assert result["nick"] is None class TestExtractNick: def test_extract_from_prefix(self): assert extract_nick("testuser!testuser@host") == "testuser" def test_extract_no_bang(self): assert extract_nick("simplenick") == "simplenick" def test_extract_none(self): assert extract_nick(None) == "unknown" class TestParseBadges: def test_parse_badges(self): badges = parse_badges("subscriber/6,moderator/1") assert badges == {"subscriber": "6", "moderator": "1"} def test_parse_empty(self): assert parse_badges("") == {} def test_parse_no_version(self): badges = parse_badges("vip") assert badges == {"vip": "1"} class TestParseEmotes: def test_parse_single_emote(self): emotes = parse_emotes("425618:0-7", "Kreygasm hello") assert len(emotes) == 1 assert emotes[0]["id"] == "425618" assert emotes[0]["text"] == "Kreygasm" assert emotes[0]["start"] == 0 assert emotes[0]["end"] == 7 def test_parse_no_emotes(self): assert parse_emotes("", "hello") == [] def test_parse_multiple_positions(self): emotes = parse_emotes("1:0-2,4-6", "abcabc") assert len(emotes) == 2 def test_parse_emote_url(self): emotes = parse_emotes("425618:0-7", "Kreygasm") assert "static-cdn.jtvnw.net" in emotes[0]["url"] class TestMakeIRCMessageId: def test_with_ts(self): msg_id = make_irc_message_id("789", "1600000000000") assert msg_id == "789:1600000000000" def test_without_ts(self): msg_id = make_irc_message_id("789") assert msg_id.startswith("789:") class TestBuildIdentityTags: def test_build_tags(self): tags = build_identity_tags(user_id="789", color="#FF4500") assert "user-id=789" in tags or "user_id=789" in tags assert "color=#FF4500" in tags def test_none_values_skipped(self): tags = build_identity_tags(user_id="789", color=None) assert "color" not in tags