Public Access
lib/irc: IrcMessage parse/serialize, base64, ReconnectPolicy (5 s .. 5 min), IrcConfig (validated, persisted) and IrcSession: registration with SASL PLAIN or NickServ, nick fallback, PING, Buffers capped at 50 lines with unread counts, Mentions, CTCP ACTION/VERSION, topics, /names on request only, others' join/part/quit hidden, user commands, and rejoining after a pause. No networking: the session returns lines to send, Log entries and Notifications. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EhqxQ49eCju4CzKYNjZzwT
75 lines
2.9 KiB
C++
75 lines
2.9 KiB
C++
#include <unity.h>
|
|
|
|
#include "base64.h"
|
|
#include "irc_message.h"
|
|
#include "reconnect_policy.h"
|
|
|
|
using namespace roro;
|
|
|
|
void setUp() {}
|
|
void tearDown() {}
|
|
|
|
void test_parse_full_message() {
|
|
auto m = IrcMessage::parse(":alice!a@host PRIVMSG #roro :hello there");
|
|
TEST_ASSERT_EQUAL_STRING("alice!a@host", m.prefix.c_str());
|
|
TEST_ASSERT_EQUAL_STRING("alice", m.nick().c_str());
|
|
TEST_ASSERT_EQUAL_STRING("PRIVMSG", m.command.c_str());
|
|
TEST_ASSERT_EQUAL(2, m.params.size());
|
|
TEST_ASSERT_EQUAL_STRING("#roro", m.params[0].c_str());
|
|
TEST_ASSERT_EQUAL_STRING("hello there", m.params[1].c_str());
|
|
}
|
|
|
|
void test_parse_without_prefix_and_with_tags() {
|
|
auto m = IrcMessage::parse("@time=2026-10-02T12:00:00Z PING :abc");
|
|
TEST_ASSERT_EQUAL_STRING("", m.prefix.c_str());
|
|
TEST_ASSERT_EQUAL_STRING("PING", m.command.c_str());
|
|
TEST_ASSERT_EQUAL_STRING("abc", m.params[0].c_str());
|
|
}
|
|
|
|
void test_parse_strips_line_endings_and_handles_empty_trailing() {
|
|
auto m = IrcMessage::parse(":srv 332 me #roro :\r\n");
|
|
TEST_ASSERT_EQUAL(3, m.params.size());
|
|
TEST_ASSERT_EQUAL_STRING("", m.params[2].c_str());
|
|
}
|
|
|
|
void test_parse_middle_params_only() {
|
|
auto m = IrcMessage::parse(":srv MODE #roro +nt");
|
|
TEST_ASSERT_EQUAL(2, m.params.size());
|
|
TEST_ASSERT_EQUAL_STRING("+nt", m.params[1].c_str());
|
|
}
|
|
|
|
void test_serialize_adds_colon_only_when_needed() {
|
|
TEST_ASSERT_EQUAL_STRING("NICK roro", IrcMessage::serialize("NICK", {"roro"}).c_str());
|
|
TEST_ASSERT_EQUAL_STRING("PRIVMSG #roro :hello there", IrcMessage::serialize("PRIVMSG", {"#roro", "hello there"}).c_str());
|
|
TEST_ASSERT_EQUAL_STRING("PRIVMSG #roro ::)", IrcMessage::serialize("PRIVMSG", {"#roro", ":)"}).c_str());
|
|
TEST_ASSERT_EQUAL_STRING("TOPIC #roro :", IrcMessage::serialize("TOPIC", {"#roro", ""}).c_str());
|
|
}
|
|
|
|
void test_base64() {
|
|
TEST_ASSERT_EQUAL_STRING("", base64Encode("").c_str());
|
|
TEST_ASSERT_EQUAL_STRING("Zg==", base64Encode("f").c_str());
|
|
TEST_ASSERT_EQUAL_STRING("Zm8=", base64Encode("fo").c_str());
|
|
TEST_ASSERT_EQUAL_STRING("Zm9v", base64Encode("foo").c_str());
|
|
TEST_ASSERT_EQUAL_STRING("dXNlcgB1c2VyAHBhc3M=", base64Encode(std::string("user\0user\0pass", 14)).c_str());
|
|
}
|
|
|
|
void test_reconnect_delays_grow_then_cap_and_reset() {
|
|
ReconnectPolicy p;
|
|
uint32_t expected[] = {5000, 10000, 30000, 60000, 120000, 300000, 300000};
|
|
for (uint32_t e : expected) TEST_ASSERT_EQUAL_UINT32(e, p.nextDelayMs());
|
|
p.reset();
|
|
TEST_ASSERT_EQUAL_UINT32(5000, p.nextDelayMs());
|
|
}
|
|
|
|
int main() {
|
|
UNITY_BEGIN();
|
|
RUN_TEST(test_parse_full_message);
|
|
RUN_TEST(test_parse_without_prefix_and_with_tags);
|
|
RUN_TEST(test_parse_strips_line_endings_and_handles_empty_trailing);
|
|
RUN_TEST(test_parse_middle_params_only);
|
|
RUN_TEST(test_serialize_adds_colon_only_when_needed);
|
|
RUN_TEST(test_base64);
|
|
RUN_TEST(test_reconnect_delays_grow_then_cap_and_reset);
|
|
return UNITY_END();
|
|
}
|