feat: add snapshot tests for eventsub messages (#5965)
This commit is contained in:
@@ -46,6 +46,7 @@
|
|||||||
- Dev: Updated `googletest` to 1.16.0. (#5942)
|
- Dev: Updated `googletest` to 1.16.0. (#5942)
|
||||||
- Dev: Fixed duplicate CMake configure in clean builds. (#5940)
|
- Dev: Fixed duplicate CMake configure in clean builds. (#5940)
|
||||||
- Dev: BTTV emotes are now loaded as WEBP. (#5957)
|
- Dev: BTTV emotes are now loaded as WEBP. (#5957)
|
||||||
|
- Dev: Added snapshot tests for EventSub. (#5965)
|
||||||
|
|
||||||
## 2.5.2
|
## 2.5.2
|
||||||
|
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ boost::json::result_for<std::chrono::system_clock::time_point,
|
|||||||
|
|
||||||
std::chrono::system_clock::time_point tp;
|
std::chrono::system_clock::time_point tp;
|
||||||
std::istringstream in{*raw};
|
std::istringstream in{*raw};
|
||||||
in >> date::parse("%FT%TZ", tp);
|
in >> date::parse("%FT%H:%M:%12SZ", tp);
|
||||||
|
|
||||||
return tp;
|
return tp;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,6 +9,7 @@
|
|||||||
#include "providers/seventv/SeventvEmotes.hpp"
|
#include "providers/seventv/SeventvEmotes.hpp"
|
||||||
#include "providers/twitch/TwitchChannel.hpp"
|
#include "providers/twitch/TwitchChannel.hpp"
|
||||||
#include "providers/twitch/TwitchIrcServer.hpp"
|
#include "providers/twitch/TwitchIrcServer.hpp"
|
||||||
|
#include "util/Twitch.hpp"
|
||||||
|
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
|
|
||||||
@@ -45,8 +46,19 @@ public:
|
|||||||
|
|
||||||
ChannelPtr getChannelOrEmpty(const QString &dirtyChannelName) override
|
ChannelPtr getChannelOrEmpty(const QString &dirtyChannelName) override
|
||||||
{
|
{
|
||||||
assert(false && "unimplemented getChannelOrEmpty in mock irc server");
|
QString query = cleanChannelName(dirtyChannelName);
|
||||||
return {};
|
|
||||||
|
auto it = this->mockChannels.find(query);
|
||||||
|
if (it == this->mockChannels.end())
|
||||||
|
{
|
||||||
|
return Channel::getEmpty();
|
||||||
|
}
|
||||||
|
auto chan = it->second.lock();
|
||||||
|
if (!chan)
|
||||||
|
{
|
||||||
|
return Channel::getEmpty();
|
||||||
|
}
|
||||||
|
return chan;
|
||||||
}
|
}
|
||||||
|
|
||||||
void addFakeMessage(const QString &data) override
|
void addFakeMessage(const QString &data) override
|
||||||
|
|||||||
@@ -27,6 +27,7 @@
|
|||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
|
|
||||||
class TestIrcMessageHandlerP;
|
class TestIrcMessageHandlerP;
|
||||||
|
class TestEventSubMessagesP;
|
||||||
|
|
||||||
namespace chatterino {
|
namespace chatterino {
|
||||||
|
|
||||||
@@ -514,6 +515,7 @@ private:
|
|||||||
friend class IrcMessageHandler;
|
friend class IrcMessageHandler;
|
||||||
friend class Commands_E2E_Test;
|
friend class Commands_E2E_Test;
|
||||||
friend class ::TestIrcMessageHandlerP;
|
friend class ::TestIrcMessageHandlerP;
|
||||||
|
friend class ::TestEventSubMessagesP;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace chatterino
|
} // namespace chatterino
|
||||||
|
|||||||
@@ -27,6 +27,7 @@
|
|||||||
#include "singletons/WindowManager.hpp"
|
#include "singletons/WindowManager.hpp"
|
||||||
#include "util/PostToThread.hpp"
|
#include "util/PostToThread.hpp"
|
||||||
#include "util/RatelimitBucket.hpp"
|
#include "util/RatelimitBucket.hpp"
|
||||||
|
#include "util/Twitch.hpp"
|
||||||
|
|
||||||
#include <IrcCommand>
|
#include <IrcCommand>
|
||||||
#include <IrcMessage>
|
#include <IrcMessage>
|
||||||
@@ -1201,18 +1202,6 @@ std::shared_ptr<Channel> TwitchIrcServer::getChannelOrEmptyByID(
|
|||||||
return Channel::getEmpty();
|
return Channel::getEmpty();
|
||||||
}
|
}
|
||||||
|
|
||||||
QString TwitchIrcServer::cleanChannelName(const QString &dirtyChannelName)
|
|
||||||
{
|
|
||||||
if (dirtyChannelName.startsWith('#'))
|
|
||||||
{
|
|
||||||
return dirtyChannelName.mid(1).toLower();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return dirtyChannelName.toLower();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool TwitchIrcServer::prepareToSend(
|
bool TwitchIrcServer::prepareToSend(
|
||||||
const std::shared_ptr<TwitchChannel> &channel)
|
const std::shared_ptr<TwitchChannel> &channel)
|
||||||
{
|
{
|
||||||
@@ -1544,7 +1533,7 @@ void TwitchIrcServer::sendRawMessage(const QString &rawMessage)
|
|||||||
|
|
||||||
ChannelPtr TwitchIrcServer::getOrAddChannel(const QString &dirtyChannelName)
|
ChannelPtr TwitchIrcServer::getOrAddChannel(const QString &dirtyChannelName)
|
||||||
{
|
{
|
||||||
auto channelName = this->cleanChannelName(dirtyChannelName);
|
auto channelName = cleanChannelName(dirtyChannelName);
|
||||||
|
|
||||||
// try get channel
|
// try get channel
|
||||||
ChannelPtr chan = this->getChannelOrEmpty(channelName);
|
ChannelPtr chan = this->getChannelOrEmpty(channelName);
|
||||||
@@ -1594,7 +1583,7 @@ ChannelPtr TwitchIrcServer::getOrAddChannel(const QString &dirtyChannelName)
|
|||||||
|
|
||||||
ChannelPtr TwitchIrcServer::getChannelOrEmpty(const QString &dirtyChannelName)
|
ChannelPtr TwitchIrcServer::getChannelOrEmpty(const QString &dirtyChannelName)
|
||||||
{
|
{
|
||||||
auto channelName = this->cleanChannelName(dirtyChannelName);
|
auto channelName = cleanChannelName(dirtyChannelName);
|
||||||
|
|
||||||
std::lock_guard<std::mutex> lock(this->channelMutex);
|
std::lock_guard<std::mutex> lock(this->channelMutex);
|
||||||
|
|
||||||
|
|||||||
@@ -169,8 +169,6 @@ protected:
|
|||||||
|
|
||||||
std::shared_ptr<Channel> getCustomChannel(const QString &channelname);
|
std::shared_ptr<Channel> getCustomChannel(const QString &channelname);
|
||||||
|
|
||||||
QString cleanChannelName(const QString &dirtyChannelName);
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void onMessageSendRequested(const std::shared_ptr<TwitchChannel> &channel,
|
void onMessageSendRequested(const std::shared_ptr<TwitchChannel> &channel,
|
||||||
const QString &message, bool &sent);
|
const QString &message, bool &sent);
|
||||||
|
|||||||
@@ -55,7 +55,10 @@ void Connection::onChannelBan(
|
|||||||
|
|
||||||
BanAction action{};
|
BanAction action{};
|
||||||
|
|
||||||
action.timestamp = std::chrono::steady_clock::now();
|
if (!getApp()->isTest())
|
||||||
|
{
|
||||||
|
action.timestamp = std::chrono::steady_clock::now();
|
||||||
|
}
|
||||||
action.roomID = roomID;
|
action.roomID = roomID;
|
||||||
action.source = ActionUser{
|
action.source = ActionUser{
|
||||||
.id = QString::fromStdString(payload.event.moderatorUserID),
|
.id = QString::fromStdString(payload.event.moderatorUserID),
|
||||||
@@ -85,6 +88,10 @@ void Connection::onChannelBan(
|
|||||||
|
|
||||||
runInGuiThread([action{std::move(action)}, chan{std::move(chan)}] {
|
runInGuiThread([action{std::move(action)}, chan{std::move(chan)}] {
|
||||||
auto time = QDateTime::currentDateTime();
|
auto time = QDateTime::currentDateTime();
|
||||||
|
if (getApp()->isTest())
|
||||||
|
{
|
||||||
|
time = QDateTime::fromSecsSinceEpoch(0).toUTC();
|
||||||
|
}
|
||||||
MessageBuilder msg(action, time);
|
MessageBuilder msg(action, time);
|
||||||
msg->flags.set(MessageFlag::PubSub);
|
msg->flags.set(MessageFlag::PubSub);
|
||||||
chan->addOrReplaceTimeout(msg.release(), QDateTime::currentDateTime());
|
chan->addOrReplaceTimeout(msg.release(), QDateTime::currentDateTime());
|
||||||
@@ -162,7 +169,11 @@ void Connection::onChannelModerate(
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto now = QDateTime::currentDateTime();
|
auto now = QDateTime::currentDateTime();
|
||||||
|
if (getApp()->isTest())
|
||||||
|
{
|
||||||
|
now = QDateTime::fromSecsSinceEpoch(0).toUTC();
|
||||||
|
}
|
||||||
|
|
||||||
std::visit(
|
std::visit(
|
||||||
[&](auto &&action) {
|
[&](auto &&action) {
|
||||||
|
|||||||
@@ -62,6 +62,16 @@ void stripChannelName(QString &channelName)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QString cleanChannelName(const QString &dirtyChannelName)
|
||||||
|
{
|
||||||
|
if (dirtyChannelName.startsWith('#'))
|
||||||
|
{
|
||||||
|
return dirtyChannelName.mid(1).toLower();
|
||||||
|
}
|
||||||
|
|
||||||
|
return dirtyChannelName.toLower();
|
||||||
|
}
|
||||||
|
|
||||||
std::pair<ParsedUserName, ParsedUserID> parseUserNameOrID(const QString &input)
|
std::pair<ParsedUserName, ParsedUserID> parseUserNameOrID(const QString &input)
|
||||||
{
|
{
|
||||||
if (input.startsWith("id:"))
|
if (input.startsWith("id:"))
|
||||||
|
|||||||
@@ -16,6 +16,9 @@ void stripUserName(QString &userName);
|
|||||||
// stripChannelName removes any @ prefix or , suffix to make it more suitable for command use
|
// stripChannelName removes any @ prefix or , suffix to make it more suitable for command use
|
||||||
void stripChannelName(QString &channelName);
|
void stripChannelName(QString &channelName);
|
||||||
|
|
||||||
|
/// Strips a leading `#` and lowercases the name
|
||||||
|
QString cleanChannelName(const QString &dirtyChannelName);
|
||||||
|
|
||||||
using ParsedUserName = QString;
|
using ParsedUserName = QString;
|
||||||
using ParsedUserID = QString;
|
using ParsedUserID = QString;
|
||||||
|
|
||||||
|
|||||||
@@ -53,6 +53,8 @@ set(test_SOURCES
|
|||||||
${CMAKE_CURRENT_LIST_DIR}/src/IgnoreController.cpp
|
${CMAKE_CURRENT_LIST_DIR}/src/IgnoreController.cpp
|
||||||
${CMAKE_CURRENT_LIST_DIR}/src/OnceFlag.cpp
|
${CMAKE_CURRENT_LIST_DIR}/src/OnceFlag.cpp
|
||||||
${CMAKE_CURRENT_LIST_DIR}/src/IncognitoBrowser.cpp
|
${CMAKE_CURRENT_LIST_DIR}/src/IncognitoBrowser.cpp
|
||||||
|
${CMAKE_CURRENT_LIST_DIR}/src/EventSubMessages.cpp
|
||||||
|
|
||||||
${CMAKE_CURRENT_LIST_DIR}/src/lib/Snapshot.cpp
|
${CMAKE_CURRENT_LIST_DIR}/src/lib/Snapshot.cpp
|
||||||
${CMAKE_CURRENT_LIST_DIR}/src/lib/Snapshot.hpp
|
${CMAKE_CURRENT_LIST_DIR}/src/lib/Snapshot.hpp
|
||||||
# Add your new file above this line!
|
# Add your new file above this line!
|
||||||
|
|||||||
@@ -0,0 +1,131 @@
|
|||||||
|
{
|
||||||
|
"input": {
|
||||||
|
"banned_at": "2023-05-20T12:30:55.518375571Z",
|
||||||
|
"broadcaster_user_id": "11148817",
|
||||||
|
"broadcaster_user_login": "pajlada",
|
||||||
|
"broadcaster_user_name": "pajlada",
|
||||||
|
"ends_at": null,
|
||||||
|
"is_permanent": true,
|
||||||
|
"moderator_user_id": "29024944",
|
||||||
|
"moderator_user_login": "CLIModerator",
|
||||||
|
"moderator_user_name": "CLIModerator",
|
||||||
|
"reason": "This is a test event",
|
||||||
|
"user_id": "40389552",
|
||||||
|
"user_login": "testFromUser",
|
||||||
|
"user_name": "testFromUser"
|
||||||
|
},
|
||||||
|
"output": [
|
||||||
|
{
|
||||||
|
"badgeInfos": {
|
||||||
|
},
|
||||||
|
"badges": [
|
||||||
|
],
|
||||||
|
"channelName": "",
|
||||||
|
"count": 1,
|
||||||
|
"displayName": "",
|
||||||
|
"elements": [
|
||||||
|
{
|
||||||
|
"element": {
|
||||||
|
"color": "System",
|
||||||
|
"flags": "Timestamp",
|
||||||
|
"link": {
|
||||||
|
"type": "None",
|
||||||
|
"value": ""
|
||||||
|
},
|
||||||
|
"style": "ChatMedium",
|
||||||
|
"tooltip": "",
|
||||||
|
"trailingSpace": true,
|
||||||
|
"type": "TextElement",
|
||||||
|
"words": [
|
||||||
|
"0:00"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"flags": "Timestamp",
|
||||||
|
"format": "",
|
||||||
|
"link": {
|
||||||
|
"type": "None",
|
||||||
|
"value": ""
|
||||||
|
},
|
||||||
|
"time": "00:00:00",
|
||||||
|
"tooltip": "",
|
||||||
|
"trailingSpace": true,
|
||||||
|
"type": "TimestampElement"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"color": "System",
|
||||||
|
"flags": "Text",
|
||||||
|
"link": {
|
||||||
|
"type": "UserInfo",
|
||||||
|
"value": "CLIModerator"
|
||||||
|
},
|
||||||
|
"style": "ChatMedium",
|
||||||
|
"tooltip": "",
|
||||||
|
"trailingSpace": true,
|
||||||
|
"type": "TextElement",
|
||||||
|
"words": [
|
||||||
|
"CLIModerator"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"color": "System",
|
||||||
|
"flags": "Text",
|
||||||
|
"link": {
|
||||||
|
"type": "None",
|
||||||
|
"value": ""
|
||||||
|
},
|
||||||
|
"style": "ChatMedium",
|
||||||
|
"tooltip": "",
|
||||||
|
"trailingSpace": true,
|
||||||
|
"type": "TextElement",
|
||||||
|
"words": [
|
||||||
|
"banned"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"color": "System",
|
||||||
|
"flags": "Text",
|
||||||
|
"link": {
|
||||||
|
"type": "UserInfo",
|
||||||
|
"value": "testFromUser"
|
||||||
|
},
|
||||||
|
"style": "ChatMedium",
|
||||||
|
"tooltip": "",
|
||||||
|
"trailingSpace": true,
|
||||||
|
"type": "TextElement",
|
||||||
|
"words": [
|
||||||
|
"testFromUser:"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"color": "System",
|
||||||
|
"flags": "Text",
|
||||||
|
"link": {
|
||||||
|
"type": "None",
|
||||||
|
"value": ""
|
||||||
|
},
|
||||||
|
"style": "ChatMedium",
|
||||||
|
"tooltip": "",
|
||||||
|
"trailingSpace": true,
|
||||||
|
"type": "TextElement",
|
||||||
|
"words": [
|
||||||
|
"\"This",
|
||||||
|
"is",
|
||||||
|
"a",
|
||||||
|
"test",
|
||||||
|
"event\"."
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"flags": "System|Timeout|PubSub",
|
||||||
|
"id": "",
|
||||||
|
"localizedName": "",
|
||||||
|
"loginName": "CLIModerator",
|
||||||
|
"messageText": "CLIModerator banned testFromUser: \"This is a test event\". ",
|
||||||
|
"searchText": "CLIModerator banned testFromUser: \"This is a test event\". ",
|
||||||
|
"serverReceivedTime": "1970-01-01T00:00:00Z",
|
||||||
|
"timeoutUser": "testFromUser",
|
||||||
|
"userID": "",
|
||||||
|
"usernameColor": "#ff000000"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -0,0 +1,134 @@
|
|||||||
|
{
|
||||||
|
"input": {
|
||||||
|
"banned_at": "2023-05-20T12:30:55.518375571Z",
|
||||||
|
"broadcaster_user_id": "11148817",
|
||||||
|
"broadcaster_user_login": "pajlada",
|
||||||
|
"broadcaster_user_name": "pajlada",
|
||||||
|
"ends_at": "2023-05-20T12:40:55.518375571Z",
|
||||||
|
"is_permanent": false,
|
||||||
|
"moderator_user_id": "29024944",
|
||||||
|
"moderator_user_login": "CLIModerator",
|
||||||
|
"moderator_user_name": "CLIModerator",
|
||||||
|
"reason": "This is a test event",
|
||||||
|
"user_id": "40389552",
|
||||||
|
"user_login": "testFromUser",
|
||||||
|
"user_name": "testFromUser"
|
||||||
|
},
|
||||||
|
"output": [
|
||||||
|
{
|
||||||
|
"badgeInfos": {
|
||||||
|
},
|
||||||
|
"badges": [
|
||||||
|
],
|
||||||
|
"channelName": "",
|
||||||
|
"count": 1,
|
||||||
|
"displayName": "",
|
||||||
|
"elements": [
|
||||||
|
{
|
||||||
|
"element": {
|
||||||
|
"color": "System",
|
||||||
|
"flags": "Timestamp",
|
||||||
|
"link": {
|
||||||
|
"type": "None",
|
||||||
|
"value": ""
|
||||||
|
},
|
||||||
|
"style": "ChatMedium",
|
||||||
|
"tooltip": "",
|
||||||
|
"trailingSpace": true,
|
||||||
|
"type": "TextElement",
|
||||||
|
"words": [
|
||||||
|
"0:00"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"flags": "Timestamp",
|
||||||
|
"format": "",
|
||||||
|
"link": {
|
||||||
|
"type": "None",
|
||||||
|
"value": ""
|
||||||
|
},
|
||||||
|
"time": "00:00:00",
|
||||||
|
"tooltip": "",
|
||||||
|
"trailingSpace": true,
|
||||||
|
"type": "TimestampElement"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"color": "System",
|
||||||
|
"flags": "Text",
|
||||||
|
"link": {
|
||||||
|
"type": "UserInfo",
|
||||||
|
"value": "CLIModerator"
|
||||||
|
},
|
||||||
|
"style": "ChatMedium",
|
||||||
|
"tooltip": "",
|
||||||
|
"trailingSpace": true,
|
||||||
|
"type": "TextElement",
|
||||||
|
"words": [
|
||||||
|
"CLIModerator"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"color": "System",
|
||||||
|
"flags": "Text",
|
||||||
|
"link": {
|
||||||
|
"type": "None",
|
||||||
|
"value": ""
|
||||||
|
},
|
||||||
|
"style": "ChatMedium",
|
||||||
|
"tooltip": "",
|
||||||
|
"trailingSpace": true,
|
||||||
|
"type": "TextElement",
|
||||||
|
"words": [
|
||||||
|
"timed",
|
||||||
|
"out"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"color": "System",
|
||||||
|
"flags": "Text",
|
||||||
|
"link": {
|
||||||
|
"type": "UserInfo",
|
||||||
|
"value": "testFromUser"
|
||||||
|
},
|
||||||
|
"style": "ChatMedium",
|
||||||
|
"tooltip": "",
|
||||||
|
"trailingSpace": true,
|
||||||
|
"type": "TextElement",
|
||||||
|
"words": [
|
||||||
|
"testFromUser"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"color": "System",
|
||||||
|
"flags": "Text",
|
||||||
|
"link": {
|
||||||
|
"type": "None",
|
||||||
|
"value": ""
|
||||||
|
},
|
||||||
|
"style": "ChatMedium",
|
||||||
|
"tooltip": "",
|
||||||
|
"trailingSpace": true,
|
||||||
|
"type": "TextElement",
|
||||||
|
"words": [
|
||||||
|
"for",
|
||||||
|
"10m:",
|
||||||
|
"\"This",
|
||||||
|
"is",
|
||||||
|
"a",
|
||||||
|
"test",
|
||||||
|
"event\"."
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"flags": "System|Timeout|PubSub",
|
||||||
|
"id": "",
|
||||||
|
"localizedName": "",
|
||||||
|
"loginName": "CLIModerator",
|
||||||
|
"messageText": "CLIModerator timed out testFromUser for 10m: \"This is a test event\". ",
|
||||||
|
"searchText": "CLIModerator timed out testFromUser for 10m: \"This is a test event\". ",
|
||||||
|
"serverReceivedTime": "1970-01-01T00:00:00Z",
|
||||||
|
"timeoutUser": "testFromUser",
|
||||||
|
"userID": "",
|
||||||
|
"usernameColor": "#ff000000"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -0,0 +1,174 @@
|
|||||||
|
{
|
||||||
|
"input": {
|
||||||
|
"action": "warn",
|
||||||
|
"automod_terms": null,
|
||||||
|
"ban": null,
|
||||||
|
"broadcaster_user_id": "11148817",
|
||||||
|
"broadcaster_user_login": "pajlada",
|
||||||
|
"broadcaster_user_name": "pajlada",
|
||||||
|
"delete": null,
|
||||||
|
"followers": null,
|
||||||
|
"moderator_user_id": "424596340",
|
||||||
|
"moderator_user_login": "quotrok",
|
||||||
|
"moderator_user_name": "quotrok",
|
||||||
|
"raid": null,
|
||||||
|
"shared_chat_ban": null,
|
||||||
|
"shared_chat_delete": null,
|
||||||
|
"shared_chat_timeout": null,
|
||||||
|
"shared_chat_unban": null,
|
||||||
|
"shared_chat_untimeout": null,
|
||||||
|
"slow": null,
|
||||||
|
"source_broadcaster_user_id": "41292030",
|
||||||
|
"source_broadcaster_user_login": "adflynn404",
|
||||||
|
"source_broadcaster_user_name": "adflynn404",
|
||||||
|
"timeout": null,
|
||||||
|
"unban": null,
|
||||||
|
"unban_request": null,
|
||||||
|
"unmod": null,
|
||||||
|
"unraid": null,
|
||||||
|
"untimeout": null,
|
||||||
|
"unvip": null,
|
||||||
|
"vip": null,
|
||||||
|
"warn": {
|
||||||
|
"chat_rules_cited": null,
|
||||||
|
"reason": "cut it out",
|
||||||
|
"user_id": "141981764",
|
||||||
|
"user_login": "twitchdev",
|
||||||
|
"user_name": "TwitchDev"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"output": [
|
||||||
|
{
|
||||||
|
"badgeInfos": {
|
||||||
|
},
|
||||||
|
"badges": [
|
||||||
|
],
|
||||||
|
"channelName": "",
|
||||||
|
"count": 1,
|
||||||
|
"displayName": "",
|
||||||
|
"elements": [
|
||||||
|
{
|
||||||
|
"element": {
|
||||||
|
"color": "System",
|
||||||
|
"flags": "Timestamp",
|
||||||
|
"link": {
|
||||||
|
"type": "None",
|
||||||
|
"value": ""
|
||||||
|
},
|
||||||
|
"style": "ChatMedium",
|
||||||
|
"tooltip": "",
|
||||||
|
"trailingSpace": true,
|
||||||
|
"type": "TextElement",
|
||||||
|
"words": [
|
||||||
|
"0:00"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"flags": "Timestamp",
|
||||||
|
"format": "",
|
||||||
|
"link": {
|
||||||
|
"type": "None",
|
||||||
|
"value": ""
|
||||||
|
},
|
||||||
|
"time": "00:00:00",
|
||||||
|
"tooltip": "",
|
||||||
|
"trailingSpace": true,
|
||||||
|
"type": "TimestampElement"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"color": "Text",
|
||||||
|
"fallbackColor": "System",
|
||||||
|
"flags": "Text|Mention",
|
||||||
|
"link": {
|
||||||
|
"type": "None",
|
||||||
|
"value": ""
|
||||||
|
},
|
||||||
|
"style": "ChatMedium",
|
||||||
|
"tooltip": "",
|
||||||
|
"trailingSpace": true,
|
||||||
|
"type": "MentionElement",
|
||||||
|
"userColor": "System",
|
||||||
|
"userLoginName": "quotrok",
|
||||||
|
"words": [
|
||||||
|
"quotrok"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"color": "System",
|
||||||
|
"flags": "Text",
|
||||||
|
"link": {
|
||||||
|
"type": "None",
|
||||||
|
"value": ""
|
||||||
|
},
|
||||||
|
"style": "ChatMedium",
|
||||||
|
"tooltip": "",
|
||||||
|
"trailingSpace": true,
|
||||||
|
"type": "TextElement",
|
||||||
|
"words": [
|
||||||
|
"has",
|
||||||
|
"warned"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"color": "Text",
|
||||||
|
"fallbackColor": "System",
|
||||||
|
"flags": "Text|Mention",
|
||||||
|
"link": {
|
||||||
|
"type": "None",
|
||||||
|
"value": ""
|
||||||
|
},
|
||||||
|
"style": "ChatMedium",
|
||||||
|
"tooltip": "",
|
||||||
|
"trailingSpace": false,
|
||||||
|
"type": "MentionElement",
|
||||||
|
"userColor": "System",
|
||||||
|
"userLoginName": "twitchdev",
|
||||||
|
"words": [
|
||||||
|
"TwitchDev"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"color": "System",
|
||||||
|
"flags": "Text",
|
||||||
|
"link": {
|
||||||
|
"type": "None",
|
||||||
|
"value": ""
|
||||||
|
},
|
||||||
|
"style": "ChatMedium",
|
||||||
|
"tooltip": "",
|
||||||
|
"trailingSpace": true,
|
||||||
|
"type": "TextElement",
|
||||||
|
"words": [
|
||||||
|
":"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"color": "System",
|
||||||
|
"flags": "Text",
|
||||||
|
"link": {
|
||||||
|
"type": "None",
|
||||||
|
"value": ""
|
||||||
|
},
|
||||||
|
"style": "ChatMedium",
|
||||||
|
"tooltip": "",
|
||||||
|
"trailingSpace": true,
|
||||||
|
"type": "TextElement",
|
||||||
|
"words": [
|
||||||
|
"cut",
|
||||||
|
"it",
|
||||||
|
"out"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"flags": "System|Timeout",
|
||||||
|
"id": "",
|
||||||
|
"localizedName": "",
|
||||||
|
"loginName": "quotrok",
|
||||||
|
"messageText": "quotrok has warned twitchdev: cut it out ",
|
||||||
|
"searchText": "quotrok has warned twitchdev: cut it out ",
|
||||||
|
"serverReceivedTime": "1970-01-01T00:00:00Z",
|
||||||
|
"timeoutUser": "",
|
||||||
|
"userID": "",
|
||||||
|
"usernameColor": "#ff000000"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -0,0 +1,174 @@
|
|||||||
|
{
|
||||||
|
"input": {
|
||||||
|
"action": "warn",
|
||||||
|
"automod_terms": null,
|
||||||
|
"ban": null,
|
||||||
|
"broadcaster_user_id": "11148817",
|
||||||
|
"broadcaster_user_login": "pajlada",
|
||||||
|
"broadcaster_user_name": "pajlada",
|
||||||
|
"delete": null,
|
||||||
|
"followers": null,
|
||||||
|
"moderator_user_id": "424596340",
|
||||||
|
"moderator_user_login": "quotrok",
|
||||||
|
"moderator_user_name": "quotrok",
|
||||||
|
"raid": null,
|
||||||
|
"shared_chat_ban": null,
|
||||||
|
"shared_chat_delete": null,
|
||||||
|
"shared_chat_timeout": null,
|
||||||
|
"shared_chat_unban": null,
|
||||||
|
"shared_chat_untimeout": null,
|
||||||
|
"slow": null,
|
||||||
|
"source_broadcaster_user_id": "11148817",
|
||||||
|
"source_broadcaster_user_login": "pajlada",
|
||||||
|
"source_broadcaster_user_name": "pajlada",
|
||||||
|
"timeout": null,
|
||||||
|
"unban": null,
|
||||||
|
"unban_request": null,
|
||||||
|
"unmod": null,
|
||||||
|
"unraid": null,
|
||||||
|
"untimeout": null,
|
||||||
|
"unvip": null,
|
||||||
|
"vip": null,
|
||||||
|
"warn": {
|
||||||
|
"chat_rules_cited": null,
|
||||||
|
"reason": "cut it out",
|
||||||
|
"user_id": "141981764",
|
||||||
|
"user_login": "twitchdev",
|
||||||
|
"user_name": "TwitchDev"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"output": [
|
||||||
|
{
|
||||||
|
"badgeInfos": {
|
||||||
|
},
|
||||||
|
"badges": [
|
||||||
|
],
|
||||||
|
"channelName": "",
|
||||||
|
"count": 1,
|
||||||
|
"displayName": "",
|
||||||
|
"elements": [
|
||||||
|
{
|
||||||
|
"element": {
|
||||||
|
"color": "System",
|
||||||
|
"flags": "Timestamp",
|
||||||
|
"link": {
|
||||||
|
"type": "None",
|
||||||
|
"value": ""
|
||||||
|
},
|
||||||
|
"style": "ChatMedium",
|
||||||
|
"tooltip": "",
|
||||||
|
"trailingSpace": true,
|
||||||
|
"type": "TextElement",
|
||||||
|
"words": [
|
||||||
|
"0:00"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"flags": "Timestamp",
|
||||||
|
"format": "",
|
||||||
|
"link": {
|
||||||
|
"type": "None",
|
||||||
|
"value": ""
|
||||||
|
},
|
||||||
|
"time": "00:00:00",
|
||||||
|
"tooltip": "",
|
||||||
|
"trailingSpace": true,
|
||||||
|
"type": "TimestampElement"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"color": "Text",
|
||||||
|
"fallbackColor": "System",
|
||||||
|
"flags": "Text|Mention",
|
||||||
|
"link": {
|
||||||
|
"type": "None",
|
||||||
|
"value": ""
|
||||||
|
},
|
||||||
|
"style": "ChatMedium",
|
||||||
|
"tooltip": "",
|
||||||
|
"trailingSpace": true,
|
||||||
|
"type": "MentionElement",
|
||||||
|
"userColor": "System",
|
||||||
|
"userLoginName": "quotrok",
|
||||||
|
"words": [
|
||||||
|
"quotrok"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"color": "System",
|
||||||
|
"flags": "Text",
|
||||||
|
"link": {
|
||||||
|
"type": "None",
|
||||||
|
"value": ""
|
||||||
|
},
|
||||||
|
"style": "ChatMedium",
|
||||||
|
"tooltip": "",
|
||||||
|
"trailingSpace": true,
|
||||||
|
"type": "TextElement",
|
||||||
|
"words": [
|
||||||
|
"has",
|
||||||
|
"warned"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"color": "Text",
|
||||||
|
"fallbackColor": "System",
|
||||||
|
"flags": "Text|Mention",
|
||||||
|
"link": {
|
||||||
|
"type": "None",
|
||||||
|
"value": ""
|
||||||
|
},
|
||||||
|
"style": "ChatMedium",
|
||||||
|
"tooltip": "",
|
||||||
|
"trailingSpace": false,
|
||||||
|
"type": "MentionElement",
|
||||||
|
"userColor": "System",
|
||||||
|
"userLoginName": "twitchdev",
|
||||||
|
"words": [
|
||||||
|
"TwitchDev"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"color": "System",
|
||||||
|
"flags": "Text",
|
||||||
|
"link": {
|
||||||
|
"type": "None",
|
||||||
|
"value": ""
|
||||||
|
},
|
||||||
|
"style": "ChatMedium",
|
||||||
|
"tooltip": "",
|
||||||
|
"trailingSpace": true,
|
||||||
|
"type": "TextElement",
|
||||||
|
"words": [
|
||||||
|
":"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"color": "System",
|
||||||
|
"flags": "Text",
|
||||||
|
"link": {
|
||||||
|
"type": "None",
|
||||||
|
"value": ""
|
||||||
|
},
|
||||||
|
"style": "ChatMedium",
|
||||||
|
"tooltip": "",
|
||||||
|
"trailingSpace": true,
|
||||||
|
"type": "TextElement",
|
||||||
|
"words": [
|
||||||
|
"cut",
|
||||||
|
"it",
|
||||||
|
"out"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"flags": "System|Timeout",
|
||||||
|
"id": "",
|
||||||
|
"localizedName": "",
|
||||||
|
"loginName": "quotrok",
|
||||||
|
"messageText": "quotrok has warned twitchdev: cut it out ",
|
||||||
|
"searchText": "quotrok has warned twitchdev: cut it out ",
|
||||||
|
"serverReceivedTime": "1970-01-01T00:00:00Z",
|
||||||
|
"timeoutUser": "",
|
||||||
|
"userID": "",
|
||||||
|
"usernameColor": "#ff000000"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -0,0 +1,197 @@
|
|||||||
|
#include "common/Literals.hpp"
|
||||||
|
#include "controllers/accounts/AccountController.hpp"
|
||||||
|
#include "lib/Snapshot.hpp"
|
||||||
|
#include "messages/Message.hpp"
|
||||||
|
#include "mocks/BaseApplication.hpp"
|
||||||
|
#include "mocks/Logging.hpp"
|
||||||
|
#include "mocks/TwitchIrcServer.hpp"
|
||||||
|
#include "providers/twitch/eventsub/Connection.hpp"
|
||||||
|
#include "Test.hpp"
|
||||||
|
#include "util/QCompareCaseInsensitive.hpp"
|
||||||
|
|
||||||
|
#include <QString>
|
||||||
|
|
||||||
|
using namespace chatterino;
|
||||||
|
using namespace literals;
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
/// Controls whether snapshots will be updated (true) or verified (false)
|
||||||
|
///
|
||||||
|
/// In CI, all snapshots must be verified, thus the integrity tests checks for
|
||||||
|
/// this constant.
|
||||||
|
///
|
||||||
|
/// When adding a test, start with `{ "input": {...} }` and set this to `true`
|
||||||
|
/// to generate an initial snapshot. Make sure to verify the output!
|
||||||
|
constexpr bool UPDATE_SNAPSHOTS = false;
|
||||||
|
|
||||||
|
const QString CATEGORY = u"EventSub"_s;
|
||||||
|
|
||||||
|
/// JSON for the `subscription` object in `payload`
|
||||||
|
const std::map<QString, std::string_view, QCompareCaseInsensitive>
|
||||||
|
SUBSCRIPTIONS{
|
||||||
|
{
|
||||||
|
"channel-moderate",
|
||||||
|
R"({
|
||||||
|
"id": "7297f7eb-3bf5-461f-8ae6-7cd7781ebce3",
|
||||||
|
"status": "enabled",
|
||||||
|
"type": "channel.moderate",
|
||||||
|
"version": "2",
|
||||||
|
"cost": 0,
|
||||||
|
"condition": {
|
||||||
|
"broadcaster_user_id": "11148817",
|
||||||
|
"moderator_user_id": "11148817"
|
||||||
|
},
|
||||||
|
"transport": {
|
||||||
|
"method": "websocket",
|
||||||
|
"session_id": "AgoQUlB8aB2SSsavWVfcs5ljnBIGY2VsbC1j"
|
||||||
|
},
|
||||||
|
"created_at": "2024-02-23T21:12:33.771005262Z"
|
||||||
|
})",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"channel-ban",
|
||||||
|
R"({
|
||||||
|
"id": "4aa632e0-fca3-590b-e981-bbd12abdb3fe",
|
||||||
|
"status": "enabled",
|
||||||
|
"type": "channel.ban",
|
||||||
|
"version": "1",
|
||||||
|
"condition": { "broadcaster_user_id": "11148817" },
|
||||||
|
"transport": { "method": "websocket", "session_id": "38de428e_b11f07be" },
|
||||||
|
"created_at": "2023-05-20T12:30:55.518375571Z",
|
||||||
|
"cost": 0
|
||||||
|
})",
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
class MockApplication : public mock::BaseApplication
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
MockApplication() = default;
|
||||||
|
|
||||||
|
ILogging *getChatLogger() override
|
||||||
|
{
|
||||||
|
return &this->logging;
|
||||||
|
}
|
||||||
|
|
||||||
|
ITwitchIrcServer *getTwitch() override
|
||||||
|
{
|
||||||
|
return &this->twitch;
|
||||||
|
}
|
||||||
|
|
||||||
|
AccountController *getAccounts() override
|
||||||
|
{
|
||||||
|
return &this->accounts;
|
||||||
|
}
|
||||||
|
|
||||||
|
mock::EmptyLogging logging;
|
||||||
|
mock::MockTwitchIrcServer twitch;
|
||||||
|
AccountController accounts;
|
||||||
|
};
|
||||||
|
|
||||||
|
std::shared_ptr<TwitchChannel> makeMockTwitchChannel(const QString &name)
|
||||||
|
{
|
||||||
|
auto chan = std::make_shared<TwitchChannel>(name);
|
||||||
|
return chan;
|
||||||
|
}
|
||||||
|
|
||||||
|
boost::beast::flat_buffer makePayload(std::string_view subJson,
|
||||||
|
const QJsonObject &event)
|
||||||
|
{
|
||||||
|
auto subscription =
|
||||||
|
QJsonDocument::fromJson(
|
||||||
|
QByteArray::fromRawData(subJson.data(),
|
||||||
|
static_cast<qsizetype>(subJson.size())))
|
||||||
|
.object();
|
||||||
|
QJsonObject metadata{
|
||||||
|
{"message_id", "e8edc592-5550-4aa5-bba6-39e31a2be435"},
|
||||||
|
{"message_type", "notification"},
|
||||||
|
{"message_timestamp", "2024-05-14T12:31:47.995298776Z"},
|
||||||
|
{"subscription_type", subscription["type"]},
|
||||||
|
{"subscription_version", subscription["version"]},
|
||||||
|
};
|
||||||
|
QJsonObject payload{
|
||||||
|
{"metadata", metadata},
|
||||||
|
{"payload",
|
||||||
|
QJsonObject{
|
||||||
|
{"subscription", subscription},
|
||||||
|
{"event", event},
|
||||||
|
}},
|
||||||
|
};
|
||||||
|
|
||||||
|
auto bytes = QJsonDocument(payload).toJson();
|
||||||
|
|
||||||
|
boost::beast::flat_buffer buf;
|
||||||
|
auto inner = buf.prepare(bytes.size());
|
||||||
|
std::memcpy(inner.data(), bytes.data(), inner.size());
|
||||||
|
buf.commit(inner.size());
|
||||||
|
return buf;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
class TestEventSubMessagesP : public ::testing::TestWithParam<QString>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
void SetUp() override
|
||||||
|
{
|
||||||
|
auto param = TestEventSubMessagesP::GetParam();
|
||||||
|
this->snapshot = testlib::Snapshot::read(CATEGORY, param);
|
||||||
|
|
||||||
|
this->mockApplication = std::make_unique<MockApplication>();
|
||||||
|
|
||||||
|
this->mainChannel = makeMockTwitchChannel("pajlada");
|
||||||
|
this->mainChannel->setRoomId("11148817");
|
||||||
|
|
||||||
|
this->mockApplication->twitch.mockChannels.emplace("pajlada",
|
||||||
|
this->mainChannel);
|
||||||
|
}
|
||||||
|
|
||||||
|
void TearDown() override
|
||||||
|
{
|
||||||
|
this->mainChannel.reset();
|
||||||
|
this->mockApplication.reset();
|
||||||
|
this->snapshot.reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<TwitchChannel> mainChannel;
|
||||||
|
std::unique_ptr<MockApplication> mockApplication;
|
||||||
|
std::unique_ptr<testlib::Snapshot> snapshot;
|
||||||
|
};
|
||||||
|
|
||||||
|
TEST_P(TestEventSubMessagesP, Run)
|
||||||
|
{
|
||||||
|
QStringView subcategory(snapshot->name());
|
||||||
|
subcategory = subcategory.sliced(0, subcategory.indexOf('/'));
|
||||||
|
auto subscription = SUBSCRIPTIONS.find(subcategory);
|
||||||
|
ASSERT_NE(subscription, SUBSCRIPTIONS.end()) << subcategory;
|
||||||
|
|
||||||
|
auto json = makePayload(subscription->second, snapshot->input().toObject());
|
||||||
|
|
||||||
|
std::unique_ptr<eventsub::lib::Listener> listener =
|
||||||
|
std::make_unique<eventsub::Connection>();
|
||||||
|
auto ec = eventsub::lib::handleMessage(listener, json);
|
||||||
|
ASSERT_FALSE(ec.failed())
|
||||||
|
<< ec.what() << ec.message() << ec.location().to_string();
|
||||||
|
|
||||||
|
auto messages = mainChannel->getMessageSnapshot();
|
||||||
|
QJsonArray output;
|
||||||
|
for (const auto &message : messages)
|
||||||
|
{
|
||||||
|
output.append(message->toJson());
|
||||||
|
}
|
||||||
|
|
||||||
|
ASSERT_TRUE(snapshot->run(output, UPDATE_SNAPSHOTS))
|
||||||
|
<< "Snapshot " << snapshot->name() << " failed. Expected JSON to be\n"
|
||||||
|
<< QJsonDocument(snapshot->output().toArray()).toJson() << "\nbut got\n"
|
||||||
|
<< QJsonDocument(output).toJson() << "\ninstead.";
|
||||||
|
}
|
||||||
|
|
||||||
|
INSTANTIATE_TEST_SUITE_P(
|
||||||
|
EventSubMessages, TestEventSubMessagesP,
|
||||||
|
testing::ValuesIn(testlib::Snapshot::discoverNested(CATEGORY)));
|
||||||
|
|
||||||
|
TEST(TestEventSubMessagesP, Integrity)
|
||||||
|
{
|
||||||
|
ASSERT_FALSE(UPDATE_SNAPSHOTS); // make sure fixtures are actually tested
|
||||||
|
}
|
||||||
@@ -175,6 +175,29 @@ QStringList Snapshot::discover(const QString &category)
|
|||||||
return files;
|
return files;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QStringList Snapshot::discoverNested(const QString &category)
|
||||||
|
{
|
||||||
|
auto directories =
|
||||||
|
baseDir(category).entryList(QDir::NoDotAndDotDot | QDir::Dirs);
|
||||||
|
QStringList all;
|
||||||
|
for (const auto &dir : directories)
|
||||||
|
{
|
||||||
|
auto d = baseDir(category);
|
||||||
|
d.cd(dir);
|
||||||
|
auto files = d.entryList(QDir::NoDotAndDotDot | QDir::Files);
|
||||||
|
for (const auto &file : files)
|
||||||
|
{
|
||||||
|
QStringView view(file);
|
||||||
|
if (view.endsWith(u".json"))
|
||||||
|
{
|
||||||
|
view = view.sliced(0, view.size() - 5);
|
||||||
|
}
|
||||||
|
all.append(dir % u'/' % view);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return all;
|
||||||
|
}
|
||||||
|
|
||||||
bool Snapshot::run(const QJsonValue &got, bool updateSnapshots) const
|
bool Snapshot::run(const QJsonValue &got, bool updateSnapshots) const
|
||||||
{
|
{
|
||||||
if (updateSnapshots)
|
if (updateSnapshots)
|
||||||
|
|||||||
@@ -70,6 +70,9 @@ public:
|
|||||||
/// Finds all tests in @a category
|
/// Finds all tests in @a category
|
||||||
static QStringList discover(const QString &category);
|
static QStringList discover(const QString &category);
|
||||||
|
|
||||||
|
/// Finds all 1-level nested tests in @a category (category/foo/bar)
|
||||||
|
static QStringList discoverNested(const QString &category);
|
||||||
|
|
||||||
/// @brief Runs the snapshot test
|
/// @brief Runs the snapshot test
|
||||||
///
|
///
|
||||||
/// If @a updateSnapshots is `false`, this checks that @a got matches the
|
/// If @a updateSnapshots is `false`, this checks that @a got matches the
|
||||||
|
|||||||
Reference in New Issue
Block a user