ForcePilot/backend/test/unit/channels/test_twitch_irc_parser.py
Kris 3264900bc9 test: 新增多渠道单元测试用例并配置测试环境变量
新增了Twitch、Telegram、Discord、Slack、Mattermost、WeChat、Zalo等多渠道的单元测试用例,覆盖了令牌处理、速率限制、消息去重、会话解析、格式转换、安全策略等模块
同时在测试配置中添加了测试用的OpenAI API密钥环境变量
2026-05-12 00:56:47 +08:00

172 lines
5.3 KiB
Python

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