Files
znc/test/IRCSockTest.cpp
T
RealKindOne a0d58c8fed Add IsParting() flag to track PART commands waiting IRCd response.
Fixes potential desync where ZNC marks channel as parted before server
confirms the PART, which could cause issues if the server never replies.

Add unittests and integration tests.
2026-07-21 03:24:38 -04:00

601 lines
21 KiB
C++

/*
* Copyright (C) 2004-2026 ZNC, see the NOTICE file for details.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <gtest/gtest.h>
#include <gmock/gmock.h>
#include "IRCTest.h"
#include <algorithm>
using testing::ElementsAre;
using testing::ContainerEq;
using testing::InSequence;
using testing::Invoke;
using testing::IsEmpty;
using testing::Mock;
using testing::ResultOf;
using testing::_;
class IRCSockTest : public IRCTest {
protected:
};
TEST_F(IRCSockTest, OnAccountMessage) {
CMessage msg(":nick!user@host ACCOUNT accountname");
m_pTestSock->ReadLine(msg.ToString());
EXPECT_THAT(m_pTestModule->vsHooks,
IsEmpty()); // no OnAccountMessage() hook (yet?)
EXPECT_THAT(m_pTestClient->vsLines, IsEmpty()); // account-notify disabled
}
TEST_F(IRCSockTest, OnActionMessage) {
// 2 callbacks are called in row: OnCTCP, OnAction.
// If OnCTCP returns HALT, OnAction isn't called.
struct ActionModule : TestModule {
ActionModule() { Reset(); }
void Reset() {
Mock::VerifyAndClear(this);
EXPECT_CALL(*this, OnPrivCTCPMessage(_)).Times(0);
EXPECT_CALL(*this, OnChanCTCPMessage(_)).Times(0);
EXPECT_CALL(*this, OnPrivActionMessage(_)).Times(0);
EXPECT_CALL(*this, OnChanActionMessage(_)).Times(0);
TestModule::Reset();
}
MOCK_METHOD1(OnPrivCTCPMessage, EModRet(CCTCPMessage&));
MOCK_METHOD1(OnChanCTCPMessage, EModRet(CCTCPMessage&));
MOCK_METHOD1(OnPrivActionMessage, EModRet(CActionMessage&));
MOCK_METHOD1(OnChanActionMessage, EModRet(CActionMessage&));
};
ActionModule testModule;
CZNC::Get().GetModules().push_back(&testModule);
CChan* pExpectedChan = m_pTestChan;
CMessage msg(":nick PRIVMSG #chan :\001ACTION hello\001");
auto CON = Invoke([&](CMessage& m) {
EXPECT_EQ(m.ToString(), msg.ToString());
EXPECT_EQ(m.GetNetwork(), m_pTestNetwork);
EXPECT_EQ(m.GetChan(), pExpectedChan);
return CModule::CONTINUE;
});
auto HAL = Invoke([&](CMessage& m) {
EXPECT_EQ(m.ToString(), msg.ToString());
EXPECT_EQ(m.GetNetwork(), m_pTestNetwork);
EXPECT_EQ(m.GetChan(), pExpectedChan);
return CModule::HALT;
});
auto Reset = [&]() {
testModule.Reset();
m_pTestClient->Reset();
};
Reset();
{
InSequence seq;
EXPECT_CALL(testModule, OnChanCTCPMessage(_)).WillOnce(CON);
EXPECT_CALL(testModule, OnChanActionMessage(_)).WillOnce(CON);
}
m_pTestSock->ReadLine(msg.ToString());
EXPECT_THAT(m_pTestClient->vsLines, ElementsAre(msg.ToString()));
Reset();
{
InSequence seq;
EXPECT_CALL(testModule, OnChanCTCPMessage(_)).WillOnce(CON);
EXPECT_CALL(testModule, OnChanActionMessage(_)).WillOnce(HAL);
}
m_pTestSock->ReadLine(msg.ToString());
EXPECT_THAT(m_pTestClient->vsLines, IsEmpty());
Reset();
EXPECT_CALL(testModule, OnChanCTCPMessage(_)).WillOnce(HAL);
m_pTestSock->ReadLine(msg.ToString());
EXPECT_THAT(m_pTestClient->vsLines, IsEmpty());
msg.Parse(":nick PRIVMSG me :\001ACTION hello\001");
pExpectedChan = nullptr;
Reset();
{
InSequence seq;
EXPECT_CALL(testModule, OnPrivCTCPMessage(_)).WillOnce(CON);
EXPECT_CALL(testModule, OnPrivActionMessage(_)).WillOnce(CON);
}
m_pTestSock->ReadLine(msg.ToString());
EXPECT_THAT(m_pTestClient->vsLines, ElementsAre(msg.ToString()));
Reset();
{
InSequence seq;
EXPECT_CALL(testModule, OnPrivCTCPMessage(_)).WillOnce(CON);
EXPECT_CALL(testModule, OnPrivActionMessage(_)).WillOnce(HAL);
}
m_pTestSock->ReadLine(msg.ToString());
EXPECT_THAT(m_pTestClient->vsLines, IsEmpty());
Reset();
EXPECT_CALL(testModule, OnPrivCTCPMessage(_)).WillOnce(HAL);
m_pTestSock->ReadLine(msg.ToString());
EXPECT_THAT(m_pTestClient->vsLines, IsEmpty());
}
TEST_F(IRCSockTest, OnAwayMessage) {
CMessage msg(":nick!user@host AWAY :reason");
m_pTestSock->ReadLine(msg.ToString());
EXPECT_THAT(m_pTestModule->vsHooks,
IsEmpty()); // no OnAwayMessage() hook (yet?)
EXPECT_THAT(m_pTestClient->vsLines, IsEmpty()); // away-notify disabled
}
TEST_F(IRCSockTest, OnCapabilityMessage) {
CMessage msg(":server CAP * LS :multi-prefix sasl");
m_pTestSock->ReadLine(msg.ToString());
EXPECT_THAT(m_pTestModule->vsHooks,
IsEmpty()); // no OnCapabilityMessage() hook (yet?)
EXPECT_THAT(m_pTestClient->vsLines, IsEmpty());
}
TEST_F(IRCSockTest, OnCTCPMessage) {
CMessage msg(":nick PRIVMSG #chan :\001VERSION\001");
m_pTestModule->eAction = CModule::HALT;
m_pTestSock->ReadLine(msg.ToString());
EXPECT_THAT(m_pTestModule->vsHooks, ElementsAre("OnChanCTCPMessage"));
EXPECT_THAT(m_pTestModule->vsMessages, ElementsAre(msg.ToString()));
EXPECT_THAT(m_pTestModule->vNetworks, ElementsAre(m_pTestNetwork));
EXPECT_THAT(m_pTestModule->vChannels, ElementsAre(m_pTestChan));
EXPECT_THAT(m_pTestClient->vsLines, IsEmpty()); // halt
m_pTestModule->eAction = CModule::CONTINUE;
m_pTestSock->ReadLine(msg.ToString());
EXPECT_THAT(m_pTestClient->vsLines, ElementsAre(msg.ToString()));
m_pTestClient->Reset();
m_pTestModule->Reset();
msg.Parse(":nick PRIVMSG me :\001VERSION\001");
m_pTestModule->eAction = CModule::HALT;
m_pTestSock->ReadLine(msg.ToString());
EXPECT_THAT(m_pTestModule->vsHooks, ElementsAre("OnPrivCTCPMessage"));
EXPECT_THAT(m_pTestModule->vsMessages, ElementsAre(msg.ToString()));
EXPECT_THAT(m_pTestModule->vNetworks, ElementsAre(m_pTestNetwork));
EXPECT_THAT(m_pTestModule->vChannels, ElementsAre(nullptr));
EXPECT_THAT(m_pTestClient->vsLines, IsEmpty()); // halt
m_pTestModule->eAction = CModule::CONTINUE;
m_pTestSock->ReadLine(msg.ToString());
EXPECT_THAT(m_pTestClient->vsLines, ElementsAre(msg.ToString()));
m_pTestClient->Reset();
m_pTestModule->Reset();
msg.Parse(":nick NOTICE me :\001VERSION ZNC 0.0\001");
m_pTestModule->eAction = CModule::HALT;
m_pTestSock->ReadLine(msg.ToString());
EXPECT_THAT(m_pTestModule->vsHooks, ElementsAre("OnCTCPReplyMessage"));
EXPECT_THAT(m_pTestModule->vsMessages, ElementsAre(msg.ToString()));
EXPECT_THAT(m_pTestModule->vNetworks, ElementsAre(m_pTestNetwork));
EXPECT_THAT(m_pTestModule->vChannels, ElementsAre(nullptr));
EXPECT_THAT(m_pTestClient->vsLines, IsEmpty()); // halt
m_pTestModule->eAction = CModule::CONTINUE;
m_pTestSock->ReadLine(msg.ToString());
EXPECT_THAT(m_pTestClient->vsLines, ElementsAre(msg.ToString()));
}
TEST_F(IRCSockTest, OnErrorMessage) {
CMessage msg(":server ERROR :foo bar");
m_pTestSock->ReadLine(msg.ToString());
EXPECT_THAT(m_pTestModule->vsHooks,
IsEmpty()); // no OnErrorMessage() hook (yet?)
EXPECT_THAT(
m_pTestClient->vsLines,
ElementsAre(
":*status!status@znc.in PRIVMSG me :Error from server: foo bar"));
}
TEST_F(IRCSockTest, OnInviteMessage) {
CMessage msg(":nick INVITE me #znc");
m_pTestSock->ReadLine(msg.ToString());
EXPECT_THAT(m_pTestModule->vsHooks,
IsEmpty()); // no OnInviteMessage() hook (yet?)
EXPECT_THAT(m_pTestClient->vsLines, ElementsAre(msg.ToString()));
}
TEST_F(IRCSockTest, OnJoinMessage) {
CMessage msg(":somebody JOIN #chan");
m_pTestSock->ReadLine(msg.ToString());
EXPECT_THAT(m_pTestModule->vsHooks, ElementsAre("OnJoinMessage"));
EXPECT_THAT(m_pTestModule->vsMessages, ElementsAre(msg.ToString()));
EXPECT_THAT(m_pTestModule->vNetworks, ElementsAre(m_pTestNetwork));
EXPECT_THAT(m_pTestModule->vChannels, ElementsAre(m_pTestChan));
}
TEST_F(IRCSockTest, OnKickMessage) {
CMessage msg(":nick KICK #chan person :reason");
m_pTestSock->ReadLine(msg.ToString());
EXPECT_THAT(m_pTestModule->vsHooks, ElementsAre("OnKickMessage"));
EXPECT_THAT(m_pTestModule->vsMessages, ElementsAre(msg.ToString()));
EXPECT_THAT(m_pTestModule->vNetworks, ElementsAre(m_pTestNetwork));
EXPECT_THAT(m_pTestModule->vChannels, ElementsAre(m_pTestChan));
}
TEST_F(IRCSockTest, OnModeMessage) {
CMessage msg(":nick MODE #chan +k key");
m_pTestSock->ReadLine(msg.ToString());
EXPECT_THAT(m_pTestModule->vsHooks,
IsEmpty()); // no OnModeMessage() hook (yet?)
EXPECT_THAT(m_pTestClient->vsLines, ElementsAre(msg.ToString()));
}
TEST_F(IRCSockTest, OnNickMessage) {
CMessage msg(":nobody NICK nick");
m_pTestSock->ReadLine(msg.ToString());
EXPECT_THAT(m_pTestModule->vsHooks, ElementsAre("OnNickMessage"));
EXPECT_THAT(m_pTestModule->vsMessages, ElementsAre(msg.ToString()));
EXPECT_THAT(m_pTestModule->vNetworks, ElementsAre(m_pTestNetwork));
EXPECT_THAT(m_pTestModule->vChannels, ElementsAre(nullptr));
EXPECT_THAT(m_pTestClient->vsLines, IsEmpty());
msg.Parse(":nick NICK somebody");
m_pTestSock->ReadLine(msg.ToString());
EXPECT_THAT(m_pTestClient->vsLines, ElementsAre(msg.ToString()));
}
TEST_F(IRCSockTest, OnNoticeMessage) {
CMessage msg(":nick NOTICE #chan :hello");
m_pTestModule->eAction = CModule::HALT;
m_pTestSock->ReadLine(msg.ToString());
EXPECT_THAT(m_pTestModule->vsHooks, ElementsAre("OnChanNoticeMessage"));
EXPECT_THAT(m_pTestModule->vsMessages, ElementsAre(msg.ToString()));
EXPECT_THAT(m_pTestModule->vNetworks, ElementsAre(m_pTestNetwork));
EXPECT_THAT(m_pTestModule->vChannels, ElementsAre(m_pTestChan));
EXPECT_THAT(m_pTestClient->vsLines, IsEmpty()); // halt
m_pTestModule->eAction = CModule::CONTINUE;
m_pTestSock->ReadLine(msg.ToString());
EXPECT_THAT(m_pTestClient->vsLines, ElementsAre(msg.ToString()));
m_pTestClient->Reset();
m_pTestModule->Reset();
msg.Parse(":nick NOTICE me :hello");
m_pTestModule->eAction = CModule::HALT;
m_pTestSock->ReadLine(msg.ToString());
EXPECT_THAT(m_pTestModule->vsHooks, ElementsAre("OnPrivNoticeMessage"));
EXPECT_THAT(m_pTestModule->vsMessages, ElementsAre(msg.ToString()));
EXPECT_THAT(m_pTestModule->vNetworks, ElementsAre(m_pTestNetwork));
EXPECT_THAT(m_pTestModule->vChannels, ElementsAre(nullptr));
EXPECT_THAT(m_pTestClient->vsLines, IsEmpty()); // halt
m_pTestModule->eAction = CModule::CONTINUE;
m_pTestSock->ReadLine(msg.ToString());
EXPECT_THAT(m_pTestClient->vsLines, ElementsAre(msg.ToString()));
}
TEST_F(IRCSockTest, OnNumericMessage) {
CMessage msg(":irc.server.com 372 nick :motd");
m_pTestModule->eAction = CModule::HALT;
m_pTestSock->ReadLine(msg.ToString());
EXPECT_THAT(m_pTestModule->vsHooks, ElementsAre("OnNumericMessage"));
EXPECT_THAT(m_pTestModule->vsMessages, ElementsAre(msg.ToString()));
EXPECT_THAT(m_pTestModule->vNetworks, ElementsAre(m_pTestNetwork));
EXPECT_THAT(m_pTestModule->vChannels, ElementsAre(nullptr));
EXPECT_THAT(m_pTestClient->vsLines, IsEmpty()); // halt
m_pTestModule->eAction = CModule::CONTINUE;
m_pTestSock->ReadLine(msg.ToString());
EXPECT_THAT(m_pTestClient->vsLines, ElementsAre(msg.ToString()));
}
TEST_F(IRCSockTest, OnPartMessage) {
CMessage msg(":nick PART #chan :reason");
m_pTestSock->ReadLine(msg.ToString());
EXPECT_THAT(m_pTestModule->vsHooks, ElementsAre("OnPartMessage"));
EXPECT_THAT(m_pTestModule->vsMessages, ElementsAre(msg.ToString()));
EXPECT_THAT(m_pTestModule->vNetworks, ElementsAre(m_pTestNetwork));
EXPECT_THAT(m_pTestModule->vChannels, ElementsAre(m_pTestChan));
}
TEST_F(IRCSockTest, StatusModes) {
m_pTestSock->ReadLine(
":server 005 user PREFIX=(Yohv)!@%+ :are supported by this server");
EXPECT_TRUE(m_pTestSock->IsPermMode('Y'));
EXPECT_TRUE(m_pTestSock->IsPermMode('o'));
EXPECT_TRUE(m_pTestSock->IsPermMode('h'));
EXPECT_TRUE(m_pTestSock->IsPermMode('v'));
m_pTestChan->SetModes("+sp");
m_pTestChan->ModeChange("+Y :nick");
EXPECT_EQ(m_pTestChan->GetModeString(), "+ps");
const CNick& pNick = m_pTestChan->GetNicks().at("nick");
EXPECT_TRUE(pNick.HasPerm('!'));
EXPECT_FALSE(pNick.HasPerm('@'));
}
TEST_F(IRCSockTest, OnPingMessage) {
CMessage msg(":server PING :arg");
m_pTestSock->ReadLine(msg.ToString());
EXPECT_THAT(m_pTestModule->vsHooks, IsEmpty());
EXPECT_THAT(m_pTestClient->vsLines, IsEmpty());
}
TEST_F(IRCSockTest, OnPongMessage) {
CMessage msg(":server PONG :arg");
m_pTestSock->ReadLine(msg.ToString());
EXPECT_THAT(m_pTestModule->vsHooks, IsEmpty());
EXPECT_THAT(m_pTestClient->vsLines, IsEmpty());
}
TEST_F(IRCSockTest, OnQuitMessage) {
CMessage msg(":nobody QUIT :bye");
m_pTestSock->ReadLine(msg.ToString());
EXPECT_THAT(m_pTestModule->vsHooks, ElementsAre("OnQuitMessage"));
EXPECT_THAT(m_pTestModule->vsMessages, ElementsAre(msg.ToString()));
EXPECT_THAT(m_pTestModule->vNetworks, ElementsAre(m_pTestNetwork));
EXPECT_THAT(m_pTestModule->vChannels, ElementsAre(nullptr));
msg.Parse(":nick QUIT :bye");
m_pTestSock->ReadLine(msg.ToString());
EXPECT_THAT(m_pTestClient->vsLines, ElementsAre(msg.ToString()));
}
TEST_F(IRCSockTest, OnSendToIRCMessage) {
CMessage msg(":nick PRIVMSG #chan :hello");
m_pTestModule->eAction = CModule::HALT;
m_pTestModule->bSendHooks = true;
m_pTestSock->PutIRC(msg.ToString());
EXPECT_THAT(m_pTestModule->vsHooks, ElementsAre("OnSendToIRCMessage"));
EXPECT_THAT(m_pTestModule->vsMessages, ElementsAre(msg.ToString()));
EXPECT_THAT(m_pTestModule->vNetworks, ElementsAre(m_pTestNetwork));
EXPECT_THAT(m_pTestModule->vChannels, ElementsAre(nullptr));
EXPECT_THAT(m_pTestClient->vsLines, IsEmpty()); // halt
m_pTestModule->eAction = CModule::CONTINUE;
m_pTestSock->ReadLine(msg.ToString());
EXPECT_THAT(m_pTestClient->vsLines, ElementsAre(msg.ToString()));
m_pTestModule->bSendHooks = false;
}
TEST_F(IRCSockTest, OnTextMessage) {
CMessage msg(":nick PRIVMSG #chan :hello");
m_pTestModule->eAction = CModule::HALT;
m_pTestSock->ReadLine(msg.ToString());
EXPECT_THAT(m_pTestModule->vsHooks, ElementsAre("OnChanTextMessage"));
EXPECT_THAT(m_pTestModule->vsMessages, ElementsAre(msg.ToString()));
EXPECT_THAT(m_pTestModule->vNetworks, ElementsAre(m_pTestNetwork));
EXPECT_THAT(m_pTestModule->vChannels, ElementsAre(m_pTestChan));
EXPECT_THAT(m_pTestClient->vsLines, IsEmpty()); // halt
m_pTestModule->eAction = CModule::CONTINUE;
m_pTestSock->ReadLine(msg.ToString());
EXPECT_THAT(m_pTestClient->vsLines, ElementsAre(msg.ToString()));
m_pTestClient->Reset();
m_pTestModule->Reset();
msg.Parse(":nick PRIVMSG me :hello");
m_pTestModule->eAction = CModule::HALT;
m_pTestSock->ReadLine(msg.ToString());
EXPECT_THAT(m_pTestModule->vsHooks, ElementsAre("OnPrivTextMessage"));
EXPECT_THAT(m_pTestModule->vsMessages, ElementsAre(msg.ToString()));
EXPECT_THAT(m_pTestModule->vNetworks, ElementsAre(m_pTestNetwork));
EXPECT_THAT(m_pTestModule->vChannels, ElementsAre(nullptr));
EXPECT_THAT(m_pTestClient->vsLines, IsEmpty()); // halt
m_pTestModule->eAction = CModule::CONTINUE;
m_pTestSock->ReadLine(msg.ToString());
EXPECT_THAT(m_pTestClient->vsLines, ElementsAre(msg.ToString()));
}
TEST_F(IRCSockTest, OnTopicMessage) {
CMessage msg(":nick TOPIC #chan :topic");
m_pTestModule->eAction = CModule::HALT;
m_pTestSock->ReadLine(msg.ToString());
EXPECT_THAT(m_pTestModule->vsHooks, ElementsAre("OnTopicMessage"));
EXPECT_THAT(m_pTestModule->vsMessages, ElementsAre(msg.ToString()));
EXPECT_THAT(m_pTestModule->vNetworks, ElementsAre(m_pTestNetwork));
EXPECT_THAT(m_pTestModule->vChannels, ElementsAre(m_pTestChan));
EXPECT_THAT(m_pTestClient->vsLines, IsEmpty()); // halt
m_pTestModule->eAction = CModule::CONTINUE;
m_pTestSock->ReadLine(msg.ToString());
EXPECT_THAT(m_pTestClient->vsLines, ElementsAre(msg.ToString()));
}
TEST_F(IRCSockTest, OnWallopsMessage) {
CMessage msg(":blub!dummy@rox-8DBEFE92 WALLOPS :this is a test");
m_pTestSock->ReadLine(msg.ToString());
EXPECT_THAT(m_pTestModule->vsHooks,
IsEmpty()); // no OnWallopsMessage() hook (yet?)
EXPECT_THAT(m_pTestClient->vsLines, ElementsAre(msg.ToString()));
}
TEST_F(IRCSockTest, ISupport) {
MCString m1 = {
{"CHANTYPES", "#"}, {"EXCEPTS", ""},
{"INVEX", ""}, {"CHANMODES", "eIbq,k,flj,CFLMPQScgimnprstz"},
{"CHANLIMIT", "#:120"}, {"PREFIX", "(ov)@+"},
{"MAXLIST", "bqeI:100"}, {"MODES", "4"},
{"NETWORK", "znc"}, {"KNOCK", ""},
{"STATUSMSG", "@+"}, {"CALLERID", "g"},
};
m_pTestSock->ReadLine(
":irc.znc.in 005 user CHANTYPES=# EXCEPTS INVEX "
"CHANMODES=eIbq,k,flj,CFLMPQScgimnprstz CHANLIMIT=#:120 PREFIX=(ov)@+ "
"MAXLIST=bqeI:100 MODES=4 NETWORK=znc KNOCK STATUSMSG=@+ CALLERID=g "
":are supported by this server");
EXPECT_THAT(m_pTestSock->GetISupport(), ContainerEq(m1));
for (const auto& it : m1) {
EXPECT_EQ(m_pTestSock->GetISupport(it.first), it.second);
}
MCString m2 = {
{"CASEMAPPING", "rfc1459"},
{"CHARSET", "ascii"},
{"NICKLEN", "16"},
{"CHANNELLEN", "50"},
{"TOPICLEN", "390"},
{"ETRACE", ""},
{"CPRIVMSG", ""},
{"CNOTICE", ""},
{"DEAF", "D"},
{"MONITOR", "100"},
{"FNC", ""},
{"TARGMAX",
"NAMES:1,LIST:1,KICK:1,WHOIS:1,PRIVMSG:4,NOTICE:4,ACCEPT:,MONITOR:"},
};
MCString m12;
std::merge(m1.begin(), m1.end(), m2.begin(), m2.end(),
std::inserter(m12, m12.begin()));
m_pTestSock->ReadLine(
":server 005 user CASEMAPPING=rfc1459 CHARSET=ascii NICKLEN=16 "
"CHANNELLEN=50 TOPICLEN=390 ETRACE CPRIVMSG CNOTICE DEAF=D MONITOR=100 "
"FNC "
"TARGMAX=NAMES:1,LIST:1,KICK:1,WHOIS:1,PRIVMSG:4,NOTICE:4,ACCEPT:,"
"MONITOR: :are supported by this server");
EXPECT_THAT(m_pTestSock->GetISupport(), ContainerEq(m12));
for (const auto& it : m2) {
EXPECT_EQ(m_pTestSock->GetISupport(it.first), it.second);
}
MCString m3 = {
{"EXTBAN", "$,ajrxz"}, {"WHOX", ""}, {"CLIENTVER", "3.0"},
{"SAFELIST", ""}, {"ELIST", "CTU"},
};
MCString m123;
std::merge(m12.begin(), m12.end(), m3.begin(), m3.end(),
std::inserter(m123, m123.begin()));
m_pTestSock->ReadLine(
":server 005 zzzzzz EXTBAN=$,ajrxz WHOX CLIENTVER=3.0 SAFELIST "
"ELIST=CTU :are supported by this server");
EXPECT_THAT(m_pTestSock->GetISupport(), ContainerEq(m123));
for (const auto& it : m3) {
EXPECT_EQ(m_pTestSock->GetISupport(it.first), it.second);
}
EXPECT_EQ(m_pTestSock->GetISupport("FOOBAR", "default"), "default");
EXPECT_EQ(m_pTestSock->GetISupport("CLIENTVER", "default"), "3.0");
EXPECT_EQ(m_pTestSock->GetISupport("SAFELIST", "default"), "");
}
TEST_F(IRCSockTest, StatusMsg) {
m_pTestSock->ReadLine(
":irc.znc.in 001 me :Welcome to the Internet Relay Network me");
m_pTestSock->ReadLine(
":irc.znc.in 005 me CHANTYPES=# PREFIX=(ov)@+ STATUSMSG=@+ :are "
"supported by this server");
m_pTestUser->SetAutoClearChanBuffer(false);
m_pTestSock->ReadLine(":someone PRIVMSG @#chan :hello ops");
EXPECT_EQ(m_pTestChan->GetBuffer().Size(), 1u);
m_pTestUser->SetTimestampPrepend(false);
EXPECT_EQ(m_pTestChan->GetBuffer().GetLine(0, *m_pTestClient),
":someone PRIVMSG @#chan :hello ops");
}
TEST_F(IRCSockTest, ChanMode) {
// https://github.com/znc/znc/issues/1684
m_pTestSock->ReadLine(
":irc.znc.in 001 me :Welcome to the Internet Relay Network me");
m_pTestSock->ReadLine(
":irc.znc.in 005 me CHANMODES=be,f,lj,nti "
":are supported by this server");
m_pTestSock->ReadLine(":irc.znc.in 324 me #chan +ntf ");
}
TEST_F(IRCSockTest, PartingFlag) {
// Set channel as joined
m_pTestChan->SetIsOn(true);
CMessage clientPart("PART #chan");
m_pTestClient->ReadLine(clientPart.ToString());
EXPECT_THAT(m_pTestSock->vsLines, ElementsAre("PART #chan"));
EXPECT_TRUE(m_pTestChan->IsParting());
m_pTestSock->Reset();
CMessage serverPart(":me PART #chan");
m_pTestSock->ReadLine(serverPart.ToString());
// IsParting() should be free
EXPECT_FALSE(m_pTestChan->IsParting());
// Verify channel was deleted
EXPECT_EQ(m_pTestNetwork->FindChan("#chan"), nullptr);
}
TEST_F(IRCSockTest, PartingFlagOnError) {
// Set channel as joined
m_pTestChan->SetIsOn(true);
CMessage clientPart("PART #chan");
m_pTestClient->ReadLine(clientPart.ToString());
m_pTestSock->Reset();
CMessage errorMsg(":server 442 me #chan :You're not on that channel");
m_pTestSock->ReadLine(errorMsg.ToString());
// IsParting() should be free
EXPECT_FALSE(m_pTestChan->IsParting());
// Verify channel was deleted
EXPECT_NE(m_pTestNetwork->FindChan("#chan"), nullptr);
}