From 4268d67c772639a1ab7f4ba8f2b5f39f18ed1aa9 Mon Sep 17 00:00:00 2001 From: Nerixyz Date: Sun, 25 Jun 2023 13:01:33 +0200 Subject: [PATCH 1/5] refactor: Improve network error messages --- src/common/NetworkPrivate.cpp | 11 +- src/common/NetworkResult.cpp | 28 +- src/common/NetworkResult.hpp | 30 ++- src/providers/IvrApi.cpp | 4 +- src/providers/RecentMessagesApi.cpp | 8 +- src/providers/bttv/BttvEmotes.cpp | 20 +- src/providers/ffz/FfzEmotes.cpp | 21 +- src/providers/seventv/SeventvEmotes.cpp | 29 +-- src/providers/twitch/api/Helix.cpp | 325 +++++++++++++++++------- src/singletons/Updates.cpp | 8 +- src/util/NuulsUploader.cpp | 2 +- tests/CMakeLists.txt | 1 + tests/src/NetworkRequest.cpp | 6 +- tests/src/NetworkResult.cpp | 48 ++++ 14 files changed, 372 insertions(+), 169 deletions(-) create mode 100644 tests/src/NetworkResult.cpp diff --git a/src/common/NetworkPrivate.cpp b/src/common/NetworkPrivate.cpp index 9af3018837b..8cda1bc62ca 100644 --- a/src/common/NetworkPrivate.cpp +++ b/src/common/NetworkPrivate.cpp @@ -155,7 +155,7 @@ void loadUncached(std::shared_ptr &&data) { postToThread([data] { data->onError_(NetworkResult( - {}, NetworkResult::timedoutStatus)); + NetworkResult::Error::TimeoutError, {}, {})); }); } @@ -218,8 +218,9 @@ void loadUncached(std::shared_ptr &&data) QString(data->payload_)); } // TODO: Should this always be run on the GUI thread? - postToThread([data, code = status.toInt(), reply] { - data->onError_(NetworkResult(reply->readAll(), code)); + postToThread([data, status, reply] { + data->onError_(NetworkResult(reply->error(), status, + reply->readAll())); }); } @@ -238,7 +239,7 @@ void loadUncached(std::shared_ptr &&data) auto status = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute); - NetworkResult result(bytes, status.toInt()); + NetworkResult result(reply->error(), status, bytes); DebugCount::increase("http request success"); // log("starting {}", data->request_.url().toString()); @@ -337,7 +338,7 @@ void loadCached(std::shared_ptr &&data) // XXX: check if bytes is empty? QByteArray bytes = cachedFile.readAll(); - NetworkResult result(bytes, 200); + NetworkResult result(NetworkResult::Error::NoError, QVariant(200), bytes); qCDebug(chatterinoHTTP) << QString("%1 [CACHED] 200 %2") diff --git a/src/common/NetworkResult.cpp b/src/common/NetworkResult.cpp index 0a5295fdbdd..dda9105df97 100644 --- a/src/common/NetworkResult.cpp +++ b/src/common/NetworkResult.cpp @@ -3,15 +3,21 @@ #include "common/QLogging.hpp" #include +#include #include #include namespace chatterino { -NetworkResult::NetworkResult(const QByteArray &data, int status) - : data_(data) - , status_(status) +NetworkResult::NetworkResult(Error error, const QVariant &httpStatusCode, + QByteArray data) + : data_(std::move(data)) + , error_(error) { + if (httpStatusCode.isValid()) + { + this->status_ = static_cast(httpStatusCode.toInt()); + } } QJsonObject NetworkResult::parseJson() const @@ -59,9 +65,21 @@ const QByteArray &NetworkResult::getData() const return this->data_; } -int NetworkResult::status() const +QString NetworkResult::formatError() const { - return this->status_; + if (this->status_) + { + return QString::number(*this->status_); + } + + const auto *name = + QMetaEnum::fromType().valueToKey( + this->error_); + if (name == nullptr) + { + return QStringLiteral("unknown error (%1)").arg(this->error_); + } + return name; } } // namespace chatterino diff --git a/src/common/NetworkResult.hpp b/src/common/NetworkResult.hpp index 64baed5ea8d..c0f59add33c 100644 --- a/src/common/NetworkResult.hpp +++ b/src/common/NetworkResult.hpp @@ -2,14 +2,19 @@ #include #include +#include #include +#include + namespace chatterino { class NetworkResult { public: - NetworkResult(const QByteArray &data, int status); + using Error = QNetworkReply::NetworkError; + + NetworkResult(Error error, const QVariant &httpStatusCode, QByteArray data); /// Parses the result as json and returns the root as an object. /// Returns empty object if parsing failed. @@ -20,13 +25,30 @@ class NetworkResult /// Parses the result as json and returns the document. rapidjson::Document parseRapidJson() const; const QByteArray &getData() const; - int status() const; - static constexpr int timedoutStatus = -2; + /// The error code of the reply. + /// In case of a successful reply, this will be NoError (0) + Error error() const + { + return this->error_; + } + + /// The HTTP status code if a request was made. + std::optional status() const + { + return this->status_; + } + + /// Formats the error: + /// without HTTP status: [Name] + /// with HTTP status: [HTTP status] + QString formatError() const; private: QByteArray data_; - int status_; + + Error error_; + std::optional status_; }; } // namespace chatterino diff --git a/src/providers/IvrApi.cpp b/src/providers/IvrApi.cpp index 26b4088e5eb..868a9ff082b 100644 --- a/src/providers/IvrApi.cpp +++ b/src/providers/IvrApi.cpp @@ -27,7 +27,7 @@ void IvrApi::getSubage(QString userName, QString channelName, }) .onError([failureCallback](auto result) { qCWarning(chatterinoIvr) - << "Failed IVR API Call!" << result.status() + << "Failed IVR API Call!" << result.formatError() << QString(result.getData()); failureCallback(); }) @@ -51,7 +51,7 @@ void IvrApi::getBulkEmoteSets(QString emoteSetList, }) .onError([failureCallback](auto result) { qCWarning(chatterinoIvr) - << "Failed IVR API Call!" << result.status() + << "Failed IVR API Call!" << result.formatError() << QString(result.getData()); failureCallback(); }) diff --git a/src/providers/RecentMessagesApi.cpp b/src/providers/RecentMessagesApi.cpp index 9fd524f684d..4245e10c0ec 100644 --- a/src/providers/RecentMessagesApi.cpp +++ b/src/providers/RecentMessagesApi.cpp @@ -217,17 +217,19 @@ void RecentMessagesApi::loadRecentMessages(const QString &channelName, return Success; }) - .onError([channelPtr, onError](NetworkResult result) { + .onError([channelPtr, onError](const NetworkResult &result) { auto shared = channelPtr.lock(); if (!shared) + { return; + } qCDebug(chatterinoRecentMessages) << "Failed to load recent messages for" << shared->getName(); shared->addMessage(makeSystemMessage( - QString("Message history service unavailable (Error %1)") - .arg(result.status()))); + QStringLiteral("Message history service unavailable (%1)") + .arg(result.formatError()))); onError(); }) diff --git a/src/providers/bttv/BttvEmotes.cpp b/src/providers/bttv/BttvEmotes.cpp index b1b6fb2e772..7d114b89058 100644 --- a/src/providers/bttv/BttvEmotes.cpp +++ b/src/providers/bttv/BttvEmotes.cpp @@ -259,23 +259,17 @@ void BttvEmotes::loadChannel(std::weak_ptr channel, shared->addMessage( makeSystemMessage(CHANNEL_HAS_NO_EMOTES)); } - else if (result.status() == NetworkResult::timedoutStatus) - { - // TODO: Auto retry in case of a timeout, with a delay - qCWarning(chatterinoBttv) - << "Fetching BTTV emotes for channel" << channelId - << "failed due to timeout"; - shared->addMessage(makeSystemMessage( - "Failed to fetch BetterTTV channel emotes. (timed out)")); - } else { + // TODO: Auto retry in case of a timeout, with a delay + auto errorString = result.formatError(); qCWarning(chatterinoBttv) << "Error fetching BTTV emotes for channel" << channelId - << ", error" << result.status(); - shared->addMessage( - makeSystemMessage("Failed to fetch BetterTTV channel " - "emotes. (unknown error)")); + << ", error" << errorString; + shared->addMessage(makeSystemMessage( + QStringLiteral("Failed to fetch BetterTTV channel " + "emotes. (%1)") + .arg(errorString))); } }) .execute(); diff --git a/src/providers/ffz/FfzEmotes.cpp b/src/providers/ffz/FfzEmotes.cpp index 73be0b4dda4..cd91351f04e 100644 --- a/src/providers/ffz/FfzEmotes.cpp +++ b/src/providers/ffz/FfzEmotes.cpp @@ -273,24 +273,17 @@ void FfzEmotes::loadChannel( makeSystemMessage(CHANNEL_HAS_NO_EMOTES)); } } - else if (result.status() == NetworkResult::timedoutStatus) - { - // TODO: Auto retry in case of a timeout, with a delay - qCWarning(chatterinoFfzemotes) - << "Fetching FFZ emotes for channel" << channelID - << "failed due to timeout"; - shared->addMessage( - makeSystemMessage("Failed to fetch FrankerFaceZ channel " - "emotes. (timed out)")); - } else { + // TODO: Auto retry in case of a timeout, with a delay + auto errorString = result.formatError(); qCWarning(chatterinoFfzemotes) << "Error fetching FFZ emotes for channel" << channelID - << ", error" << result.status(); - shared->addMessage( - makeSystemMessage("Failed to fetch FrankerFaceZ channel " - "emotes. (unknown error)")); + << ", error" << errorString; + shared->addMessage(makeSystemMessage( + QStringLiteral("Failed to fetch FrankerFaceZ channel " + "emotes. (%1)") + .arg(errorString))); } }) .execute(); diff --git a/src/providers/seventv/SeventvEmotes.cpp b/src/providers/seventv/SeventvEmotes.cpp index 321e43a6519..6dcee463d86 100644 --- a/src/providers/seventv/SeventvEmotes.cpp +++ b/src/providers/seventv/SeventvEmotes.cpp @@ -386,23 +386,17 @@ void SeventvEmotes::loadChannelEmotes( makeSystemMessage(CHANNEL_HAS_NO_EMOTES)); } } - else if (result.status() == NetworkResult::timedoutStatus) - { - // TODO: Auto retry in case of a timeout, with a delay - qCWarning(chatterinoSeventv) - << "Fetching 7TV emotes for channel" << channelId - << "failed due to timeout"; - shared->addMessage(makeSystemMessage( - "Failed to fetch 7TV channel emotes. (timed out)")); - } else { + // TODO: Auto retry in case of a timeout, with a delay + auto errorString = result.formatError(); qCWarning(chatterinoSeventv) << "Error fetching 7TV emotes for channel" << channelId - << ", error" << result.status(); - shared->addMessage( - makeSystemMessage("Failed to fetch 7TV channel " - "emotes. (unknown error)")); + << ", error" << errorString; + shared->addMessage(makeSystemMessage( + QStringLiteral("Failed to fetch 7TV channel " + "emotes. (%1)") + .arg(errorString))); } }) .execute(); @@ -502,14 +496,7 @@ void SeventvEmotes::getEmoteSet( }) .onError([emoteSetId, callback = std::move(errorCallback)]( const NetworkResult &result) { - if (result.status() == NetworkResult::timedoutStatus) - { - callback("timed out"); - } - else - { - callback(QString("status: %1").arg(result.status())); - } + callback(result.formatError()); }) .execute(); } diff --git a/src/providers/twitch/api/Helix.cpp b/src/providers/twitch/api/Helix.cpp index 59329ae599b..bd016aab04e 100644 --- a/src/providers/twitch/api/Helix.cpp +++ b/src/providers/twitch/api/Helix.cpp @@ -390,7 +390,7 @@ void Helix::createClip(QString channelId, return Success; }) .onError([failureCallback](auto result) { - switch (result.status()) + switch (result.status().value_or(0)) { case 503: { // Channel has disabled clip-creation, or channel has made cliops only creatable by followers and the user is not a follower (or subscriber) @@ -406,7 +406,7 @@ void Helix::createClip(QString channelId, default: { qCDebug(chatterinoTwitch) - << "Failed to create a clip: " << result.status() + << "Failed to create a clip: " << result.formatError() << result.getData(); failureCallback(HelixClipError::Unknown); } @@ -477,7 +477,7 @@ void Helix::createStreamMarker( return Success; }) .onError([failureCallback](NetworkResult result) { - switch (result.status()) + switch (result.status().value_or(0)) { case 403: { // User isn't a Channel Editor, so he can't create markers @@ -495,7 +495,7 @@ void Helix::createStreamMarker( default: { qCDebug(chatterinoTwitch) << "Failed to create a stream marker: " - << result.status() << result.getData(); + << result.formatError() << result.getData(); failureCallback(HelixStreamMarkerError::Unknown); } break; @@ -636,7 +636,7 @@ void Helix::manageAutoModMessages( return Success; }) .onError([failureCallback, msgID, action](NetworkResult result) { - switch (result.status()) + switch (result.status().value_or(0)) { case 400: { // Message was already processed @@ -668,7 +668,7 @@ void Helix::manageAutoModMessages( default: { qCDebug(chatterinoTwitch) << "Failed to manage automod message: " << action - << msgID << result.status() << result.getData(); + << msgID << result.formatError() << result.getData(); failureCallback(HelixAutoModMessageError::Unknown); } break; @@ -710,7 +710,7 @@ void Helix::getCheermotes( .onError([broadcasterId, failureCallback](NetworkResult result) { qCDebug(chatterinoTwitch) << "Failed to get cheermotes(broadcaster_id=" << broadcasterId - << "): " << result.status() << result.getData(); + << "): " << result.formatError() << result.getData(); failureCallback(); }) .execute(); @@ -804,17 +804,24 @@ void Helix::updateUserChatColor( { qCWarning(chatterinoTwitch) << "Success result for updating chat color was" - << result.status() << "but we only expected it to be 204"; + << result.formatError() + << "but we only expected it to be 204"; } successCallback(); return Success; }) - .onError([failureCallback](auto result) { + .onError([failureCallback](const auto &result) -> void { + if (!result.status()) + { + failureCallback(Error::Unknown, result.formatError()); + return; + } + auto obj = result.parseJson(); auto message = obj.value("message").toString(); - switch (result.status()) + switch (*result.status()) { case 400: { if (message.startsWith("invalid color", @@ -847,7 +854,7 @@ void Helix::updateUserChatColor( default: { qCDebug(chatterinoTwitch) << "Unhandled error changing user color:" - << result.status() << result.getData() << obj; + << result.formatError() << result.getData() << obj; failureCallback(Error::Unknown, message); } break; @@ -880,17 +887,24 @@ void Helix::deleteChatMessages( { qCWarning(chatterinoTwitch) << "Success result for deleting chat messages was" - << result.status() << "but we only expected it to be 204"; + << result.formatError() + << "but we only expected it to be 204"; } successCallback(); return Success; }) - .onError([failureCallback](auto result) { + .onError([failureCallback](const auto &result) -> void { + if (!result.status()) + { + failureCallback(Error::Unknown, result.formatError()); + return; + } + auto obj = result.parseJson(); auto message = obj.value("message").toString(); - switch (result.status()) + switch (*result.status()) { case 404: { // A 404 on this endpoint means message id is invalid or unable to be deleted. @@ -932,7 +946,7 @@ void Helix::deleteChatMessages( default: { qCDebug(chatterinoTwitch) << "Unhandled error deleting chat messages:" - << result.status() << result.getData() << obj; + << result.formatError() << result.getData() << obj; failureCallback(Error::Unknown, message); } break; @@ -958,17 +972,24 @@ void Helix::addChannelModerator( { qCWarning(chatterinoTwitch) << "Success result for adding a moderator was" - << result.status() << "but we only expected it to be 204"; + << result.formatError() + << "but we only expected it to be 204"; } successCallback(); return Success; }) - .onError([failureCallback](auto result) { + .onError([failureCallback](const auto &result) -> void { + if (!result.status()) + { + failureCallback(Error::Unknown, result.formatError()); + return; + } + auto obj = result.parseJson(); auto message = obj.value("message").toString(); - switch (result.status()) + switch (*result.status()) { case 401: { if (message.startsWith("Missing scope", @@ -1020,7 +1041,7 @@ void Helix::addChannelModerator( default: { qCDebug(chatterinoTwitch) << "Unhandled error adding channel moderator:" - << result.status() << result.getData() << obj; + << result.formatError() << result.getData() << obj; failureCallback(Error::Unknown, message); } break; @@ -1046,17 +1067,24 @@ void Helix::removeChannelModerator( { qCWarning(chatterinoTwitch) << "Success result for unmodding user was" - << result.status() << "but we only expected it to be 204"; + << result.formatError() + << "but we only expected it to be 204"; } successCallback(); return Success; }) - .onError([failureCallback](auto result) { + .onError([failureCallback](const auto &result) -> void { + if (!result.status()) + { + failureCallback(Error::Unknown, result.formatError()); + return; + } + auto obj = result.parseJson(); auto message = obj.value("message").toString(); - switch (result.status()) + switch (*result.status()) { case 400: { if (message.compare("user is not a mod", @@ -1098,8 +1126,8 @@ void Helix::removeChannelModerator( default: { qCDebug(chatterinoTwitch) - << "Unhandled error unmodding user:" << result.status() - << result.getData() << obj; + << "Unhandled error unmodding user:" + << result.formatError() << result.getData() << obj; failureCallback(Error::Unknown, message); } break; @@ -1133,17 +1161,24 @@ void Helix::sendChatAnnouncement( { qCWarning(chatterinoTwitch) << "Success result for sending an announcement was" - << result.status() << "but we only expected it to be 204"; + << result.formatError() + << "but we only expected it to be 204"; } successCallback(); return Success; }) - .onError([failureCallback](auto result) { + .onError([failureCallback](const auto &result) -> void { + if (!result.status()) + { + failureCallback(Error::Unknown, result.formatError()); + return; + } + auto obj = result.parseJson(); auto message = obj.value("message").toString(); - switch (result.status()) + switch (*result.status()) { case 400: { // These errors are generally well formatted, so we just forward them. @@ -1176,7 +1211,7 @@ void Helix::sendChatAnnouncement( default: { qCDebug(chatterinoTwitch) << "Unhandled error sending an announcement:" - << result.status() << result.getData() << obj; + << result.formatError() << result.getData() << obj; failureCallback(Error::Unknown, message); } break; @@ -1202,17 +1237,24 @@ void Helix::addChannelVIP( { qCWarning(chatterinoTwitch) << "Success result for adding channel VIP was" - << result.status() << "but we only expected it to be 204"; + << result.formatError() + << "but we only expected it to be 204"; } successCallback(); return Success; }) - .onError([failureCallback](auto result) { + .onError([failureCallback](const auto &result) -> void { + if (!result.status()) + { + failureCallback(Error::Unknown, result.formatError()); + return; + } + auto obj = result.parseJson(); auto message = obj.value("message").toString(); - switch (result.status()) + switch (*result.status()) { case 400: case 409: @@ -1254,7 +1296,7 @@ void Helix::addChannelVIP( default: { qCDebug(chatterinoTwitch) << "Unhandled error adding channel VIP:" - << result.status() << result.getData() << obj; + << result.formatError() << result.getData() << obj; failureCallback(Error::Unknown, message); } break; @@ -1280,17 +1322,24 @@ void Helix::removeChannelVIP( { qCWarning(chatterinoTwitch) << "Success result for removing channel VIP was" - << result.status() << "but we only expected it to be 204"; + << result.formatError() + << "but we only expected it to be 204"; } successCallback(); return Success; }) - .onError([failureCallback](auto result) { + .onError([failureCallback](const auto &result) -> void { + if (!result.status()) + { + failureCallback(Error::Unknown, result.formatError()); + return; + } + auto obj = result.parseJson(); auto message = obj.value("message").toString(); - switch (result.status()) + switch (*result.status()) { case 400: case 409: @@ -1331,7 +1380,7 @@ void Helix::removeChannelVIP( default: { qCDebug(chatterinoTwitch) << "Unhandled error removing channel VIP:" - << result.status() << result.getData() << obj; + << result.formatError() << result.getData() << obj; failureCallback(Error::Unknown, message); } break; @@ -1369,17 +1418,24 @@ void Helix::unbanUser( { qCWarning(chatterinoTwitch) << "Success result for unbanning user was" - << result.status() << "but we only expected it to be 204"; + << result.formatError() + << "but we only expected it to be 204"; } successCallback(); return Success; }) - .onError([failureCallback](auto result) { + .onError([failureCallback](const auto &result) -> void { + if (!result.status()) + { + failureCallback(Error::Unknown, result.formatError()); + return; + } + auto obj = result.parseJson(); auto message = obj.value("message").toString(); - switch (result.status()) + switch (*result.status()) { case 400: { if (message.startsWith("The user in the user_id query " @@ -1435,8 +1491,8 @@ void Helix::unbanUser( default: { qCDebug(chatterinoTwitch) - << "Unhandled error unbanning user:" << result.status() - << result.getData() << obj; + << "Unhandled error unbanning user:" + << result.formatError() << result.getData() << obj; failureCallback(Error::Unknown, message); } break; @@ -1474,11 +1530,17 @@ void Helix::startRaid( successCallback(); return Success; }) - .onError([failureCallback](auto result) { + .onError([failureCallback](const auto &result) -> void { + if (!result.status()) + { + failureCallback(Error::Unknown, result.formatError()); + return; + } + auto obj = result.parseJson(); auto message = obj.value("message").toString(); - switch (result.status()) + switch (*result.status()) { case 400: { if (message.compare("The IDs in from_broadcaster_id and " @@ -1529,7 +1591,7 @@ void Helix::startRaid( default: { qCDebug(chatterinoTwitch) << "Unhandled error while starting a raid:" - << result.status() << result.getData() << obj; + << result.formatError() << result.getData() << obj; failureCallback(Error::Unknown, message); } break; @@ -1554,17 +1616,24 @@ void Helix::cancelRaid( { qCWarning(chatterinoTwitch) << "Success result for canceling the raid was" - << result.status() << "but we only expected it to be 204"; + << result.formatError() + << "but we only expected it to be 204"; } successCallback(); return Success; }) - .onError([failureCallback](auto result) { + .onError([failureCallback](const auto &result) -> void { + if (!result.status()) + { + failureCallback(Error::Unknown, result.formatError()); + return; + } + auto obj = result.parseJson(); auto message = obj.value("message").toString(); - switch (result.status()) + switch (*result.status()) { case 401: { if (message.startsWith("Missing scope", @@ -1601,7 +1670,7 @@ void Helix::cancelRaid( default: { qCDebug(chatterinoTwitch) << "Unhandled error while canceling the raid:" - << result.status() << result.getData() << obj; + << result.formatError() << result.getData() << obj; failureCallback(Error::Unknown, message); } break; @@ -1715,18 +1784,24 @@ void Helix::updateChatSettings( { qCWarning(chatterinoTwitch) << "Success result for updating chat settings was" - << result.status() << "but we expected it to be 200"; + << result.formatError() << "but we expected it to be 200"; } auto response = result.parseJson(); successCallback(HelixChatSettings( response.value("data").toArray().first().toObject())); return Success; }) - .onError([failureCallback](auto result) { + .onError([failureCallback](const auto &result) -> void { + if (!result.status()) + { + failureCallback(Error::Unknown, result.formatError()); + return; + } + auto obj = result.parseJson(); auto message = obj.value("message").toString(); - switch (result.status()) + switch (*result.status()) { case 400: { if (message.contains("must be in the range")) @@ -1773,7 +1848,7 @@ void Helix::updateChatSettings( default: { qCDebug(chatterinoTwitch) << "Unhandled error updating chat settings:" - << result.status() << result.getData() << obj; + << result.formatError() << result.getData() << obj; failureCallback(Error::Unknown, message); } break; @@ -1838,18 +1913,24 @@ void Helix::fetchChatters( { qCWarning(chatterinoTwitch) << "Success result for getting chatters was " - << result.status() << "but we expected it to be 200"; + << result.formatError() << "but we expected it to be 200"; } auto response = result.parseJson(); successCallback(HelixChatters(response)); return Success; }) - .onError([failureCallback](auto result) { + .onError([failureCallback](const auto &result) -> void { + if (!result.status()) + { + failureCallback(Error::Unknown, result.formatError()); + return; + } + auto obj = result.parseJson(); auto message = obj.value("message").toString(); - switch (result.status()) + switch (*result.status()) { case 400: { failureCallback(Error::Forwarded, message); @@ -1880,7 +1961,7 @@ void Helix::fetchChatters( default: { qCDebug(chatterinoTwitch) - << "Unhandled error data:" << result.status() + << "Unhandled error data:" << result.formatError() << result.getData() << obj; failureCallback(Error::Unknown, message); } @@ -1947,18 +2028,24 @@ void Helix::fetchModerators( { qCWarning(chatterinoTwitch) << "Success result for getting moderators was " - << result.status() << "but we expected it to be 200"; + << result.formatError() << "but we expected it to be 200"; } auto response = result.parseJson(); successCallback(HelixModerators(response)); return Success; }) - .onError([failureCallback](auto result) { + .onError([failureCallback](const auto &result) -> void { + if (!result.status()) + { + failureCallback(Error::Unknown, result.formatError()); + return; + } + auto obj = result.parseJson(); auto message = obj.value("message").toString(); - switch (result.status()) + switch (*result.status()) { case 400: { failureCallback(Error::Forwarded, message); @@ -1989,7 +2076,7 @@ void Helix::fetchModerators( default: { qCDebug(chatterinoTwitch) - << "Unhandled error data:" << result.status() + << "Unhandled error data:" << result.formatError() << result.getData() << obj; failureCallback(Error::Unknown, message); } @@ -2033,17 +2120,23 @@ void Helix::banUser(QString broadcasterID, QString moderatorID, QString userID, { qCWarning(chatterinoTwitch) << "Success result for banning a user was" - << result.status() << "but we expected it to be 200"; + << result.formatError() << "but we expected it to be 200"; } // we don't care about the response successCallback(); return Success; }) - .onError([failureCallback](auto result) { + .onError([failureCallback](const auto &result) -> void { + if (!result.status()) + { + failureCallback(Error::Unknown, result.formatError()); + return; + } + auto obj = result.parseJson(); auto message = obj.value("message").toString(); - switch (result.status()) + switch (*result.status()) { case 400: { if (message.startsWith("The user specified in the user_id " @@ -2097,8 +2190,8 @@ void Helix::banUser(QString broadcasterID, QString moderatorID, QString userID, default: { qCDebug(chatterinoTwitch) - << "Unhandled error banning user:" << result.status() - << result.getData() << obj; + << "Unhandled error banning user:" + << result.formatError() << result.getData() << obj; failureCallback(Error::Unknown, message); } break; @@ -2130,17 +2223,23 @@ void Helix::sendWhisper( { qCWarning(chatterinoTwitch) << "Success result for sending a whisper was" - << result.status() << "but we expected it to be 204"; + << result.formatError() << "but we expected it to be 204"; } // we don't care about the response successCallback(); return Success; }) - .onError([failureCallback](auto result) { + .onError([failureCallback](const auto &result) -> void { + if (!result.status()) + { + failureCallback(Error::Unknown, result.formatError()); + return; + } + auto obj = result.parseJson(); auto message = obj.value("message").toString(); - switch (result.status()) + switch (*result.status()) { case 400: { if (message.startsWith("A user cannot whisper themself", @@ -2201,8 +2300,8 @@ void Helix::sendWhisper( default: { qCDebug(chatterinoTwitch) - << "Unhandled error banning user:" << result.status() - << result.getData() << obj; + << "Unhandled error banning user:" + << result.formatError() << result.getData() << obj; failureCallback(Error::Unknown, message); } break; @@ -2272,8 +2371,8 @@ void Helix::getChannelVIPs( if (result.status() != 200) { qCWarning(chatterinoTwitch) - << "Success result for getting VIPs was" << result.status() - << "but we expected it to be 200"; + << "Success result for getting VIPs was" + << result.formatError() << "but we expected it to be 200"; } auto response = result.parseJson(); @@ -2287,11 +2386,17 @@ void Helix::getChannelVIPs( successCallback(channelVips); return Success; }) - .onError([failureCallback](auto result) { + .onError([failureCallback](const auto &result) -> void { + if (!result.status()) + { + failureCallback(Error::Unknown, result.formatError()); + return; + } + auto obj = result.parseJson(); auto message = obj.value("message").toString(); - switch (result.status()) + switch (*result.status()) { case 400: { failureCallback(Error::Forwarded, message); @@ -2331,8 +2436,8 @@ void Helix::getChannelVIPs( default: { qCDebug(chatterinoTwitch) - << "Unhandled error listing VIPs:" << result.status() - << result.getData() << obj; + << "Unhandled error listing VIPs:" + << result.formatError() << result.getData() << obj; failureCallback(Error::Unknown, message); } break; @@ -2368,11 +2473,17 @@ void Helix::startCommercial( successCallback(HelixStartCommercialResponse(obj)); return Success; }) - .onError([failureCallback](auto result) { + .onError([failureCallback](const auto &result) -> void { + if (!result.status()) + { + failureCallback(Error::Unknown, result.formatError()); + return; + } + auto obj = result.parseJson(); auto message = obj.value("message").toString(); - switch (result.status()) + switch (*result.status()) { case 400: { if (message.startsWith("Missing scope", @@ -2427,7 +2538,7 @@ void Helix::startCommercial( default: { qCDebug(chatterinoTwitch) << "Unhandled error starting commercial:" - << result.status() << result.getData() << obj; + << result.formatError() << result.getData() << obj; failureCallback(Error::Unknown, message); } break; @@ -2450,18 +2561,24 @@ void Helix::getGlobalBadges( { qCWarning(chatterinoTwitch) << "Success result for getting global badges was " - << result.status() << "but we expected it to be 200"; + << result.formatError() << "but we expected it to be 200"; } auto response = result.parseJson(); successCallback(HelixGlobalBadges(response)); return Success; }) - .onError([failureCallback](auto result) { + .onError([failureCallback](const auto &result) -> void { + if (!result.status()) + { + failureCallback(Error::Unknown, result.formatError()); + return; + } + auto obj = result.parseJson(); auto message = obj.value("message").toString(); - switch (result.status()) + switch (*result.status()) { case 401: { failureCallback(Error::Forwarded, message); @@ -2471,7 +2588,7 @@ void Helix::getGlobalBadges( default: { qCWarning(chatterinoTwitch) << "Helix global badges, unhandled error data:" - << result.status() << result.getData() << obj; + << result.formatError() << result.getData() << obj; failureCallback(Error::Unknown, message); } break; @@ -2497,18 +2614,24 @@ void Helix::getChannelBadges( { qCWarning(chatterinoTwitch) << "Success result for getting badges was " - << result.status() << "but we expected it to be 200"; + << result.formatError() << "but we expected it to be 200"; } auto response = result.parseJson(); successCallback(HelixChannelBadges(response)); return Success; }) - .onError([failureCallback](auto result) { + .onError([failureCallback](const auto &result) -> void { + if (!result.status()) + { + failureCallback(Error::Unknown, result.formatError()); + return; + } + auto obj = result.parseJson(); auto message = obj.value("message").toString(); - switch (result.status()) + switch (*result.status()) { case 400: case 401: { @@ -2519,7 +2642,7 @@ void Helix::getChannelBadges( default: { qCWarning(chatterinoTwitch) << "Helix channel badges, unhandled error data:" - << result.status() << result.getData() << obj; + << result.formatError() << result.getData() << obj; failureCallback(Error::Unknown, message); } break; @@ -2550,7 +2673,7 @@ void Helix::updateShieldMode( { qCWarning(chatterinoTwitch) << "Success result for updating shield mode was " - << result.status() << "but we expected it to be 200"; + << result.formatError() << "but we expected it to be 200"; } const auto response = result.parseJson(); @@ -2558,11 +2681,17 @@ void Helix::updateShieldMode( HelixShieldModeStatus(response["data"][0].toObject())); return Success; }) - .onError([failureCallback](auto result) { + .onError([failureCallback](const auto &result) -> void { + if (!result.status()) + { + failureCallback(Error::Unknown, result.formatError()); + return; + } + const auto obj = result.parseJson(); auto message = obj["message"].toString(); - switch (result.status()) + switch (*result.status()) { case 400: { if (message.startsWith("Missing scope", @@ -2588,7 +2717,7 @@ void Helix::updateShieldMode( default: { qCWarning(chatterinoTwitch) << "Helix shield mode, unhandled error data:" - << result.status() << result.getData() << obj; + << result.formatError() << result.getData() << obj; failureCallback(Error::Unknown, message); } break; @@ -2617,17 +2746,23 @@ void Helix::sendShoutout( { qCWarning(chatterinoTwitch) << "Success result for sending shoutout was " - << result.status() << "but we expected it to be 204"; + << result.formatError() << "but we expected it to be 204"; } successCallback(); return Success; }) - .onError([failureCallback](NetworkResult result) -> void { + .onError([failureCallback](const NetworkResult &result) -> void { + if (!result.status()) + { + failureCallback(Error::Unknown, result.formatError()); + return; + } + const auto obj = result.parseJson(); auto message = obj["message"].toString(); - switch (result.status()) + switch (*result.status()) { case 400: { if (message.startsWith("The broadcaster may not give " @@ -2690,7 +2825,7 @@ void Helix::sendShoutout( default: { qCWarning(chatterinoTwitch) << "Helix send shoutout, unhandled error data:" - << result.status() << result.getData() << obj; + << result.formatError() << result.getData() << obj; failureCallback(Error::Unknown, message); } } diff --git a/src/singletons/Updates.cpp b/src/singletons/Updates.cpp index c54d63e7b7d..b2f8b3a5d77 100644 --- a/src/singletons/Updates.cpp +++ b/src/singletons/Updates.cpp @@ -128,8 +128,8 @@ void Updates::installUpdates() auto *box = new QMessageBox( QMessageBox::Information, "Chatterino Update", QStringLiteral("The update couldn't be downloaded " - "(HTTP status %1).") - .arg(result.status())); + "(%1).") + .arg(result.formatError())); box->setAttribute(Qt::WA_DeleteOnClose); box->exec(); return Failure; @@ -189,8 +189,8 @@ void Updates::installUpdates() auto *box = new QMessageBox( QMessageBox::Information, "Chatterino Update", QStringLiteral("The update couldn't be downloaded " - "(HTTP status %1).") - .arg(result.status())); + "(%1).") + .arg(result.formatError())); box->setAttribute(Qt::WA_DeleteOnClose); box->exec(); return Failure; diff --git a/src/util/NuulsUploader.cpp b/src/util/NuulsUploader.cpp index 79f7b3794e7..8627ef8fddd 100644 --- a/src/util/NuulsUploader.cpp +++ b/src/util/NuulsUploader.cpp @@ -208,7 +208,7 @@ void uploadImageToNuuls(RawImageData imageData, ChannelPtr channel, .onError([channel](NetworkResult result) -> bool { auto errorMessage = QString("An error happened while uploading your image: %1") - .arg(result.status()); + .arg(result.formatError()); // Try to read more information from the result body auto obj = result.parseJson(); diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 09a9828bc2b..06c254ba9ac 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -8,6 +8,7 @@ set(test_SOURCES ${CMAKE_CURRENT_LIST_DIR}/src/AccessGuard.cpp ${CMAKE_CURRENT_LIST_DIR}/src/NetworkCommon.cpp ${CMAKE_CURRENT_LIST_DIR}/src/NetworkRequest.cpp + ${CMAKE_CURRENT_LIST_DIR}/src/NetworkResult.cpp ${CMAKE_CURRENT_LIST_DIR}/src/ChatterSet.cpp ${CMAKE_CURRENT_LIST_DIR}/src/HighlightPhrase.cpp ${CMAKE_CURRENT_LIST_DIR}/src/Emojis.cpp diff --git a/tests/src/NetworkRequest.cpp b/tests/src/NetworkRequest.cpp index 2bcb5da9a8b..144d546cfab 100644 --- a/tests/src/NetworkRequest.cpp +++ b/tests/src/NetworkRequest.cpp @@ -210,7 +210,8 @@ TEST(NetworkRequest, TimeoutTimingOut) .onError([&waiter, url](const NetworkResult &result) { qDebug() << QTime::currentTime().toString() << "timeout request finish error"; - EXPECT_EQ(result.status(), NetworkResult::timedoutStatus); + EXPECT_EQ(result.error(), NetworkResult::Error::TimeoutError); + EXPECT_EQ(result.status(), std::nullopt); waiter.requestDone(); }) @@ -267,7 +268,8 @@ TEST(NetworkRequest, FinallyCallbackOnTimeout) }) .onError([&](const NetworkResult &result) { onErrorCalled = true; - EXPECT_EQ(result.status(), NetworkResult::timedoutStatus); + EXPECT_EQ(result.error(), NetworkResult::Error::TimeoutError); + EXPECT_EQ(result.status(), std::nullopt); }) .finally([&] { finallyCalled = true; diff --git a/tests/src/NetworkResult.cpp b/tests/src/NetworkResult.cpp new file mode 100644 index 00000000000..995d89be3c7 --- /dev/null +++ b/tests/src/NetworkResult.cpp @@ -0,0 +1,48 @@ +#include "common/NetworkResult.hpp" + +#include + +using namespace chatterino; + +using Error = NetworkResult::Error; + +namespace { + +void checkResult(const NetworkResult &res, Error error, + std::optional status, const QString &formatted) +{ + ASSERT_EQ(res.error(), error); + ASSERT_EQ(res.status(), status); + ASSERT_EQ(res.formatError(), formatted); +} + +} // namespace + +TEST(NetworkResult, NoError) +{ + checkResult({Error::NoError, 200, {}}, Error::NoError, 200, "200"); + checkResult({Error::NoError, 202, {}}, Error::NoError, 202, "202"); + + // no status code + checkResult({Error::NoError, {}, {}}, Error::NoError, std::nullopt, + "NoError"); +} + +TEST(NetworkResult, Errors) +{ + checkResult({Error::TimeoutError, {}, {}}, Error::TimeoutError, + std::nullopt, "TimeoutError"); + checkResult({Error::RemoteHostClosedError, {}, {}}, + Error::RemoteHostClosedError, std::nullopt, + "RemoteHostClosedError"); + + // status code takes precedence + checkResult({Error::TimeoutError, 400, {}}, Error::TimeoutError, 400, + "400"); +} + +TEST(NetworkResult, InvalidError) +{ + checkResult({static_cast(-1), {}, {}}, static_cast(-1), + std::nullopt, "unknown error (-1)"); +} From 2b0e22eb41c7a8585a68bd8b0708cad79a93c88d Mon Sep 17 00:00:00 2001 From: Nerixyz Date: Sun, 25 Jun 2023 13:11:34 +0200 Subject: [PATCH 2/5] chore: add changelog entry --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 87c3da49264..cbdcefe7182 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,7 @@ - Bugfix: Fix visual glitches with smooth scrolling. (#4501) - Bugfix: Fixed pings firing for the "Your username" highlight when not signed in. (#4698) - Bugfix: Fixed partially broken filters on Qt 6 builds. (#4702) +- Bugfix: Fixed some network errors having `0` as their HTTP status. (#4704) - Dev: Added command to set Qt's logging filter/rules at runtime (`/c2-set-logging-rules`). (#4637) - Dev: Added the ability to see & load custom themes from the Themes directory. No stable promises are made of this feature, changes might be made that breaks custom themes without notice. (#4570) - Dev: Added test cases for emote and tab completion. (#4644) From bb3ae8b2b60b90339dd39bb4b519d0da766c9097 Mon Sep 17 00:00:00 2001 From: Nerixyz Date: Sun, 25 Jun 2023 15:17:40 +0200 Subject: [PATCH 3/5] fix: add error prefix --- src/providers/RecentMessagesApi.cpp | 3 ++- src/providers/bttv/BttvEmotes.cpp | 2 +- src/providers/ffz/FfzEmotes.cpp | 2 +- src/providers/seventv/SeventvEmotes.cpp | 2 +- src/singletons/Updates.cpp | 4 ++-- 5 files changed, 7 insertions(+), 6 deletions(-) diff --git a/src/providers/RecentMessagesApi.cpp b/src/providers/RecentMessagesApi.cpp index 4245e10c0ec..05ee6e8c8c5 100644 --- a/src/providers/RecentMessagesApi.cpp +++ b/src/providers/RecentMessagesApi.cpp @@ -228,7 +228,8 @@ void RecentMessagesApi::loadRecentMessages(const QString &channelName, << "Failed to load recent messages for" << shared->getName(); shared->addMessage(makeSystemMessage( - QStringLiteral("Message history service unavailable (%1)") + QStringLiteral( + "Message history service unavailable (Error: %1)") .arg(result.formatError()))); onError(); diff --git a/src/providers/bttv/BttvEmotes.cpp b/src/providers/bttv/BttvEmotes.cpp index 7d114b89058..42bf51cba0c 100644 --- a/src/providers/bttv/BttvEmotes.cpp +++ b/src/providers/bttv/BttvEmotes.cpp @@ -268,7 +268,7 @@ void BttvEmotes::loadChannel(std::weak_ptr channel, << ", error" << errorString; shared->addMessage(makeSystemMessage( QStringLiteral("Failed to fetch BetterTTV channel " - "emotes. (%1)") + "emotes. (Error: %1)") .arg(errorString))); } }) diff --git a/src/providers/ffz/FfzEmotes.cpp b/src/providers/ffz/FfzEmotes.cpp index cd91351f04e..0a28cc51b6e 100644 --- a/src/providers/ffz/FfzEmotes.cpp +++ b/src/providers/ffz/FfzEmotes.cpp @@ -282,7 +282,7 @@ void FfzEmotes::loadChannel( << ", error" << errorString; shared->addMessage(makeSystemMessage( QStringLiteral("Failed to fetch FrankerFaceZ channel " - "emotes. (%1)") + "emotes. (Error: %1)") .arg(errorString))); } }) diff --git a/src/providers/seventv/SeventvEmotes.cpp b/src/providers/seventv/SeventvEmotes.cpp index 6dcee463d86..a384189fac3 100644 --- a/src/providers/seventv/SeventvEmotes.cpp +++ b/src/providers/seventv/SeventvEmotes.cpp @@ -395,7 +395,7 @@ void SeventvEmotes::loadChannelEmotes( << ", error" << errorString; shared->addMessage(makeSystemMessage( QStringLiteral("Failed to fetch 7TV channel " - "emotes. (%1)") + "emotes. (Error: %1)") .arg(errorString))); } }) diff --git a/src/singletons/Updates.cpp b/src/singletons/Updates.cpp index b2f8b3a5d77..b25e52819bc 100644 --- a/src/singletons/Updates.cpp +++ b/src/singletons/Updates.cpp @@ -128,7 +128,7 @@ void Updates::installUpdates() auto *box = new QMessageBox( QMessageBox::Information, "Chatterino Update", QStringLiteral("The update couldn't be downloaded " - "(%1).") + "(Error: %1).") .arg(result.formatError())); box->setAttribute(Qt::WA_DeleteOnClose); box->exec(); @@ -189,7 +189,7 @@ void Updates::installUpdates() auto *box = new QMessageBox( QMessageBox::Information, "Chatterino Update", QStringLiteral("The update couldn't be downloaded " - "(%1).") + "(Error: %1).") .arg(result.formatError())); box->setAttribute(Qt::WA_DeleteOnClose); box->exec(); From 9442e963d405ad097b63e73fac0bd13c2f5dc852 Mon Sep 17 00:00:00 2001 From: Nerixyz Date: Sun, 25 Jun 2023 15:20:44 +0200 Subject: [PATCH 4/5] fix: use int --- src/common/NetworkResult.cpp | 2 +- src/common/NetworkResult.hpp | 4 ++-- tests/src/NetworkResult.cpp | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/common/NetworkResult.cpp b/src/common/NetworkResult.cpp index dda9105df97..dd4dc9bd2c1 100644 --- a/src/common/NetworkResult.cpp +++ b/src/common/NetworkResult.cpp @@ -16,7 +16,7 @@ NetworkResult::NetworkResult(Error error, const QVariant &httpStatusCode, { if (httpStatusCode.isValid()) { - this->status_ = static_cast(httpStatusCode.toInt()); + this->status_ = httpStatusCode.toInt(); } } diff --git a/src/common/NetworkResult.hpp b/src/common/NetworkResult.hpp index c0f59add33c..f93d84e6385 100644 --- a/src/common/NetworkResult.hpp +++ b/src/common/NetworkResult.hpp @@ -34,7 +34,7 @@ class NetworkResult } /// The HTTP status code if a request was made. - std::optional status() const + std::optional status() const { return this->status_; } @@ -48,7 +48,7 @@ class NetworkResult QByteArray data_; Error error_; - std::optional status_; + std::optional status_; }; } // namespace chatterino diff --git a/tests/src/NetworkResult.cpp b/tests/src/NetworkResult.cpp index 995d89be3c7..23e49d1e5f0 100644 --- a/tests/src/NetworkResult.cpp +++ b/tests/src/NetworkResult.cpp @@ -9,7 +9,7 @@ using Error = NetworkResult::Error; namespace { void checkResult(const NetworkResult &res, Error error, - std::optional status, const QString &formatted) + std::optional status, const QString &formatted) { ASSERT_EQ(res.error(), error); ASSERT_EQ(res.status(), status); From ea89f9c1cb4d72a1be68c44cd597a386c36f5c16 Mon Sep 17 00:00:00 2001 From: Nerixyz Date: Sun, 25 Jun 2023 15:21:08 +0200 Subject: [PATCH 5/5] fix: rename to NetworkError --- src/common/NetworkPrivate.cpp | 6 ++++-- src/common/NetworkResult.cpp | 2 +- src/common/NetworkResult.hpp | 16 ++++++++-------- tests/src/NetworkRequest.cpp | 6 ++++-- tests/src/NetworkResult.cpp | 2 +- 5 files changed, 18 insertions(+), 14 deletions(-) diff --git a/src/common/NetworkPrivate.cpp b/src/common/NetworkPrivate.cpp index 8cda1bc62ca..661b2eccf47 100644 --- a/src/common/NetworkPrivate.cpp +++ b/src/common/NetworkPrivate.cpp @@ -155,7 +155,8 @@ void loadUncached(std::shared_ptr &&data) { postToThread([data] { data->onError_(NetworkResult( - NetworkResult::Error::TimeoutError, {}, {})); + NetworkResult::NetworkError::TimeoutError, {}, + {})); }); } @@ -338,7 +339,8 @@ void loadCached(std::shared_ptr &&data) // XXX: check if bytes is empty? QByteArray bytes = cachedFile.readAll(); - NetworkResult result(NetworkResult::Error::NoError, QVariant(200), bytes); + NetworkResult result(NetworkResult::NetworkError::NoError, QVariant(200), + bytes); qCDebug(chatterinoHTTP) << QString("%1 [CACHED] 200 %2") diff --git a/src/common/NetworkResult.cpp b/src/common/NetworkResult.cpp index dd4dc9bd2c1..ae614bac0fe 100644 --- a/src/common/NetworkResult.cpp +++ b/src/common/NetworkResult.cpp @@ -9,7 +9,7 @@ namespace chatterino { -NetworkResult::NetworkResult(Error error, const QVariant &httpStatusCode, +NetworkResult::NetworkResult(NetworkError error, const QVariant &httpStatusCode, QByteArray data) : data_(std::move(data)) , error_(error) diff --git a/src/common/NetworkResult.hpp b/src/common/NetworkResult.hpp index f93d84e6385..9f0ada784cf 100644 --- a/src/common/NetworkResult.hpp +++ b/src/common/NetworkResult.hpp @@ -12,9 +12,10 @@ namespace chatterino { class NetworkResult { public: - using Error = QNetworkReply::NetworkError; + using NetworkError = QNetworkReply::NetworkError; - NetworkResult(Error error, const QVariant &httpStatusCode, QByteArray data); + NetworkResult(NetworkError error, const QVariant &httpStatusCode, + QByteArray data); /// Parses the result as json and returns the root as an object. /// Returns empty object if parsing failed. @@ -28,26 +29,25 @@ class NetworkResult /// The error code of the reply. /// In case of a successful reply, this will be NoError (0) - Error error() const + NetworkError error() const { return this->error_; } - /// The HTTP status code if a request was made. + /// The HTTP status code if a response was received. std::optional status() const { return this->status_; } - /// Formats the error: - /// without HTTP status: [Name] - /// with HTTP status: [HTTP status] + /// Formats the error. + /// If a reply is received, returns the HTTP status otherwise, the network error. QString formatError() const; private: QByteArray data_; - Error error_; + NetworkError error_; std::optional status_; }; diff --git a/tests/src/NetworkRequest.cpp b/tests/src/NetworkRequest.cpp index 144d546cfab..048ed1c91f7 100644 --- a/tests/src/NetworkRequest.cpp +++ b/tests/src/NetworkRequest.cpp @@ -210,7 +210,8 @@ TEST(NetworkRequest, TimeoutTimingOut) .onError([&waiter, url](const NetworkResult &result) { qDebug() << QTime::currentTime().toString() << "timeout request finish error"; - EXPECT_EQ(result.error(), NetworkResult::Error::TimeoutError); + EXPECT_EQ(result.error(), + NetworkResult::NetworkError::TimeoutError); EXPECT_EQ(result.status(), std::nullopt); waiter.requestDone(); @@ -268,7 +269,8 @@ TEST(NetworkRequest, FinallyCallbackOnTimeout) }) .onError([&](const NetworkResult &result) { onErrorCalled = true; - EXPECT_EQ(result.error(), NetworkResult::Error::TimeoutError); + EXPECT_EQ(result.error(), + NetworkResult::NetworkError::TimeoutError); EXPECT_EQ(result.status(), std::nullopt); }) .finally([&] { diff --git a/tests/src/NetworkResult.cpp b/tests/src/NetworkResult.cpp index 23e49d1e5f0..6bafc3a83bb 100644 --- a/tests/src/NetworkResult.cpp +++ b/tests/src/NetworkResult.cpp @@ -4,7 +4,7 @@ using namespace chatterino; -using Error = NetworkResult::Error; +using Error = NetworkResult::NetworkError; namespace {