From 2818c6bfdffba2df1be22f82bd2efae45fe331a6 Mon Sep 17 00:00:00 2001 From: Jamiras Date: Sun, 2 Aug 2026 14:41:07 -0600 Subject: [PATCH 1/3] eliminate FetchBadgeIds API helper --- src/RA_Integration.vcxproj | 1 - src/RA_Integration.vcxproj.filters | 3 -- src/api/ApiCall.cpp | 1 - src/api/FetchBadgeIds.hh | 37 -------------------- src/api/IServer.hh | 2 -- src/api/impl/ConnectedServer.cpp | 40 ---------------------- src/api/impl/ConnectedServer.hh | 1 - src/api/impl/ServerBase.hh | 5 --- src/devkit/context/impl/RcClient.cpp | 3 +- src/ui/win32/AssetEditorDialog.cpp | 50 +++++++++++++++++++--------- tests/mocks/MockServer.hh | 5 --- 11 files changed, 36 insertions(+), 112 deletions(-) delete mode 100644 src/api/FetchBadgeIds.hh diff --git a/src/RA_Integration.vcxproj b/src/RA_Integration.vcxproj index d5498e04..debef5ef 100644 --- a/src/RA_Integration.vcxproj +++ b/src/RA_Integration.vcxproj @@ -191,7 +191,6 @@ - diff --git a/src/RA_Integration.vcxproj.filters b/src/RA_Integration.vcxproj.filters index f36a6510..8ff32993 100644 --- a/src/RA_Integration.vcxproj.filters +++ b/src/RA_Integration.vcxproj.filters @@ -647,9 +647,6 @@ UI\Win32\Bindings - - API - API diff --git a/src/api/ApiCall.cpp b/src/api/ApiCall.cpp index b27bd61a..efa616bb 100644 --- a/src/api/ApiCall.cpp +++ b/src/api/ApiCall.cpp @@ -19,7 +19,6 @@ UpdateLeaderboard::Response UpdateLeaderboard::Request::Call() const { return Se FetchLeaderboardInfo::Response FetchLeaderboardInfo::Request::Call() const { return Server().FetchLeaderboardInfo(*this); } UpdateRichPresence::Response UpdateRichPresence::Request::Call() const { return Server().UpdateRichPresence(*this); } LatestClient::Response LatestClient::Request::Call() const { return Server().LatestClient(*this); } -FetchBadgeIds::Response FetchBadgeIds::Request::Call() const { return Server().FetchBadgeIds(*this); } UploadBadge::Response UploadBadge::Request::Call() const { return Server().UploadBadge(*this); } } // namespace api diff --git a/src/api/FetchBadgeIds.hh b/src/api/FetchBadgeIds.hh deleted file mode 100644 index 97673615..00000000 --- a/src/api/FetchBadgeIds.hh +++ /dev/null @@ -1,37 +0,0 @@ -#ifndef RA_API_FETCH_BADGE_IDS_HH -#define RA_API_FETCH_BADGE_IDS_HH -#pragma once - -#include "ApiCall.hh" - -namespace ra { -namespace api { - -class FetchBadgeIds -{ -public: - static constexpr const char* const Name() noexcept { return "FetchBadgeIds"; } - - struct Response : ApiResponseBase - { - unsigned int FirstID{}; - unsigned int NextID{}; - }; - - struct Request : ApiRequestBase - { - using Callback = std::function; - - Response Call() const; - - void CallAsync(Callback&& callback) const - { - ApiRequestBase::CallAsync(*this, std::move(callback)); - } - }; -}; - -} // namespace api -} // namespace ra - -#endif // !RA_API_FETCH_BADGE_IDS_HH diff --git a/src/api/IServer.hh b/src/api/IServer.hh index 7611ba3c..88d20fda 100644 --- a/src/api/IServer.hh +++ b/src/api/IServer.hh @@ -3,7 +3,6 @@ #pragma once #include "api/FetchAchievementInfo.hh" -#include "api/FetchBadgeIds.hh" #include "api/FetchLeaderboardInfo.hh" #include "api/LatestClient.hh" #include "api/ResolveHash.hh" @@ -30,7 +29,6 @@ public: // === other functions === virtual LatestClient::Response LatestClient(const LatestClient::Request& request) = 0; - virtual FetchBadgeIds::Response FetchBadgeIds(const FetchBadgeIds::Request& request) = 0; virtual UploadBadge::Response UploadBadge(const UploadBadge::Request& request) = 0; protected: diff --git a/src/api/impl/ConnectedServer.cpp b/src/api/impl/ConnectedServer.cpp index 47e3b48b..8523cec3 100644 --- a/src/api/impl/ConnectedServer.cpp +++ b/src/api/impl/ConnectedServer.cpp @@ -779,46 +779,6 @@ LatestClient::Response ConnectedServer::LatestClient(const LatestClient::Request return response; } -FetchBadgeIds::Response ConnectedServer::FetchBadgeIds(const FetchBadgeIds::Request&) -{ - FetchBadgeIds::Response response; - - rc_api_fetch_badge_range_request_t api_params; - memset(&api_params, 0, sizeof(api_params)); - - rc_api_request_t api_request; - const int result = rc_api_init_fetch_badge_range_request(&api_request, &api_params); - if (result == RC_OK) - { - ra::services::Http::Response httpResponse; - if (DoRequest(api_request, FetchBadgeIds::Name(), httpResponse, response)) - { - rc_api_fetch_badge_range_response_t api_response; - rc_api_server_response_t server_response; - HttpResponseToServerResponse(httpResponse, &server_response); - - const auto nResult = rc_api_process_fetch_badge_range_server_response(&api_response, &server_response); - - if (ValidateResponse(nResult, api_response.response, FetchBadgeIds::Name(), httpResponse.StatusCode(), response)) - { - response.Result = ApiResult::Success; - response.FirstID = api_response.first_badge_id; - response.NextID = api_response.next_badge_id; - } - - rc_api_destroy_fetch_badge_range_response(&api_response); - } - } - else - { - response.Result = ApiResult::Failed; - response.ErrorMessage = rc_error_str(result); - } - - rc_api_destroy_request(&api_request); - return response; -} - UploadBadge::Response ConnectedServer::UploadBadge(const UploadBadge::Request& request) { UploadBadge::Response response; diff --git a/src/api/impl/ConnectedServer.hh b/src/api/impl/ConnectedServer.hh index 0fe05cc6..32964493 100644 --- a/src/api/impl/ConnectedServer.hh +++ b/src/api/impl/ConnectedServer.hh @@ -22,7 +22,6 @@ public: FetchLeaderboardInfo::Response FetchLeaderboardInfo(const FetchLeaderboardInfo::Request& request) override; UpdateRichPresence::Response UpdateRichPresence(const UpdateRichPresence::Request& request) override; LatestClient::Response LatestClient(const LatestClient::Request& request) override; - FetchBadgeIds::Response FetchBadgeIds(const FetchBadgeIds::Request& request) override; UploadBadge::Response UploadBadge(const UploadBadge::Request& request) override; private: diff --git a/src/api/impl/ServerBase.hh b/src/api/impl/ServerBase.hh index ea93e4e9..c1d5d218 100644 --- a/src/api/impl/ServerBase.hh +++ b/src/api/impl/ServerBase.hh @@ -54,11 +54,6 @@ public: return UnsupportedApi(LatestClient::Name()); } - FetchBadgeIds::Response FetchBadgeIds(const FetchBadgeIds::Request&) override - { - return UnsupportedApi(FetchBadgeIds::Name()); - } - UploadBadge::Response UploadBadge(const UploadBadge::Request&) override { return UnsupportedApi(UploadBadge::Name()); diff --git a/src/devkit/context/impl/RcClient.cpp b/src/devkit/context/impl/RcClient.cpp index 92933d0b..630c3768 100644 --- a/src/devkit/context/impl/RcClient.cpp +++ b/src/devkit/context/impl/RcClient.cpp @@ -224,7 +224,8 @@ void RcClient::CallApi(const std::string& sApi, const ra::services::Http::Reques void* pCallbackData) const { std::wstring sParameter; - if (sApi == "codenotes2") { + if (sApi == "codenotes2") + { const auto svGameId = FindParameter(pRequest.GetPostData(), "g="); if (!svGameId.empty()) sParameter = ra::util::String::Widen(svGameId); diff --git a/src/ui/win32/AssetEditorDialog.cpp b/src/ui/win32/AssetEditorDialog.cpp index bec6fdb5..74edc598 100644 --- a/src/ui/win32/AssetEditorDialog.cpp +++ b/src/ui/win32/AssetEditorDialog.cpp @@ -3,7 +3,7 @@ #include "RA_Defs.h" #include "RA_Resource.h" -#include "api\FetchBadgeIds.hh" +#include "context\IRcClient.hh" #include "data\context\EmulatorContext.hh" @@ -20,6 +20,8 @@ #include "util\EnumOps.hh" #include "util\Log.hh" +#include + using ra::data::models::AssetModelBase; using ra::data::models::AchievementModel; using ra::ui::viewmodels::AssetEditorViewModel; @@ -513,6 +515,36 @@ void AssetEditorDialog::BadgeNameBinding::UpdateSourceFromText(const std::wstrin void AssetEditorDialog::BadgeNameBinding::UpdateTextFromSource(const std::wstring& sText) { + if (m_nMinimum == 0 && !ra::util::String::StartsWith(sText, L"local\\")) + { + const auto& pRcClient = ra::services::ServiceLocator::Get(); + + rc_api_fetch_badge_range_request_t api_params; + memset(&api_params, 0, sizeof(api_params)); + + rc_api_request_t api_request; + const int nResult = rc_api_init_fetch_badge_range_request_hosted(&api_request, &api_params, pRcClient.GetHost()); + if (nResult == RC_OK) + { + pRcClient.DispatchRequest(api_request, [this](const rc_api_server_response_t& server_response, void*) + { + rc_api_fetch_badge_range_response_t api_response; + const auto nResult = rc_api_process_fetch_badge_range_server_response(&api_response, &server_response); + if (nResult == RC_OK) + { + SetRange(ra::to_signed(api_response.first_badge_id), + ra::to_signed(api_response.next_badge_id) - 1); + + InvokeOnUIThread([hWnd = m_hWndSpinner]() noexcept { ::EnableWindow(hWnd, true); }); + } + + rc_api_destroy_fetch_badge_range_response(&api_response); + }, nullptr); + + rc_api_destroy_request(&api_request); + } + } + InvokeOnUIThread([this, sTextCopy = sText]() { if (ra::util::String::StartsWith(sTextCopy, L"local\\")) @@ -523,21 +555,7 @@ void AssetEditorDialog::BadgeNameBinding::UpdateTextFromSource(const std::wstrin else { SetWindowTextW(m_hWnd, sTextCopy.c_str()); - - if (m_nMinimum == 0) - { - ::EnableWindow(m_hWndSpinner, false); - - ra::api::FetchBadgeIds::Request request; - request.CallAsync([this](const ra::api::FetchBadgeIds::Response& response) { - SetRange(ra::to_signed(response.FirstID), ra::to_signed(response.NextID) - 1); - InvokeOnUIThread([hWnd = m_hWndSpinner]() noexcept { ::EnableWindow(hWnd, true); }); - }); - } - else - { - ::EnableWindow(m_hWndSpinner, true); - } + ::EnableWindow(m_hWndSpinner, (m_nMinimum != 0)); } }); } diff --git a/tests/mocks/MockServer.hh b/tests/mocks/MockServer.hh index d3344663..c278d6fa 100644 --- a/tests/mocks/MockServer.hh +++ b/tests/mocks/MockServer.hh @@ -95,11 +95,6 @@ public: return HandleRequest(request); } - FetchBadgeIds::Response FetchBadgeIds(const FetchBadgeIds::Request& request) override - { - return HandleRequest(request); - } - UploadBadge::Response UploadBadge(const UploadBadge::Request& request) override { return HandleRequest(request); From d14137c5c1dd081511dec5798741880415efa144 Mon Sep 17 00:00:00 2001 From: Jamiras Date: Sun, 2 Aug 2026 16:11:54 -0600 Subject: [PATCH 2/3] eliminate LatestClient API helper --- src/Exports.cpp | 2 - src/RA_Integration.vcxproj | 4 - src/RA_Integration.vcxproj.filters | 12 - src/api/ApiCall.cpp | 1 - src/api/IServer.hh | 2 - src/api/LatestClient.hh | 39 ---- src/api/impl/ConnectedServer.cpp | 27 --- src/api/impl/ConnectedServer.hh | 1 - src/api/impl/DisconnectedServer.cpp | 21 -- src/api/impl/DisconnectedServer.hh | 6 +- src/api/impl/OfflineServer.cpp | 27 --- src/api/impl/OfflineServer.hh | 19 -- src/api/impl/ServerBase.hh | 5 - src/data/context/EmulatorContext.cpp | 118 ++++++---- src/data/context/EmulatorContext.hh | 1 + src/devkit/context/impl/RcClient.cpp | 15 +- src/devkit/context/impl/RcClient.hh | 4 + src/services/impl/OfflineRcClient.cpp | 15 +- src/services/impl/OfflineRcClient.hh | 5 + tests/RA_Integration.Tests.vcxproj | 3 - tests/RA_Integration.Tests.vcxproj.filters | 9 - tests/api/ConnectedServer_Tests.cpp | 227 ------------------- tests/data/context/EmulatorContext_Tests.cpp | 71 ++---- tests/mocks/MockServer.hh | 5 - 24 files changed, 125 insertions(+), 514 deletions(-) delete mode 100644 src/api/LatestClient.hh delete mode 100644 src/api/impl/DisconnectedServer.cpp delete mode 100644 src/api/impl/OfflineServer.cpp delete mode 100644 src/api/impl/OfflineServer.hh delete mode 100644 tests/api/ConnectedServer_Tests.cpp diff --git a/src/Exports.cpp b/src/Exports.cpp index cda724bb..7633516c 100644 --- a/src/Exports.cpp +++ b/src/Exports.cpp @@ -6,7 +6,6 @@ #include "RA_Resource.h" #include "api\IServer.hh" -#include "api\impl\OfflineServer.hh" #include "context\IConsoleContext.hh" #include "context\IRcClient.hh" @@ -103,7 +102,6 @@ static void InitializeOfflineMode() auto& pConfiguration = ra::services::ServiceLocator::GetMutable(); pConfiguration.SetFeatureEnabled(ra::services::Feature::Offline, true); - ra::services::ServiceLocator::Provide(std::make_unique()); ra::services::ServiceLocator::Provide(std::make_unique()); // reattach hooks to new rc_client_t diff --git a/src/RA_Integration.vcxproj b/src/RA_Integration.vcxproj index debef5ef..99863849 100644 --- a/src/RA_Integration.vcxproj +++ b/src/RA_Integration.vcxproj @@ -74,8 +74,6 @@ - - @@ -194,10 +192,8 @@ - - diff --git a/src/RA_Integration.vcxproj.filters b/src/RA_Integration.vcxproj.filters index 8ff32993..ad69bf40 100644 --- a/src/RA_Integration.vcxproj.filters +++ b/src/RA_Integration.vcxproj.filters @@ -132,12 +132,6 @@ API\impl - - API\impl - - - API\impl - UI\Drawing\GDI @@ -527,9 +521,6 @@ API\impl - - API\impl - UI\Drawing\GDI @@ -584,9 +575,6 @@ UI\Win32\Bindings - - API - UI diff --git a/src/api/ApiCall.cpp b/src/api/ApiCall.cpp index efa616bb..c4b402e1 100644 --- a/src/api/ApiCall.cpp +++ b/src/api/ApiCall.cpp @@ -18,7 +18,6 @@ FetchAchievementInfo::Response FetchAchievementInfo::Request::Call() const { ret UpdateLeaderboard::Response UpdateLeaderboard::Request::Call() const { return Server().UpdateLeaderboard(*this); } FetchLeaderboardInfo::Response FetchLeaderboardInfo::Request::Call() const { return Server().FetchLeaderboardInfo(*this); } UpdateRichPresence::Response UpdateRichPresence::Request::Call() const { return Server().UpdateRichPresence(*this); } -LatestClient::Response LatestClient::Request::Call() const { return Server().LatestClient(*this); } UploadBadge::Response UploadBadge::Request::Call() const { return Server().UploadBadge(*this); } } // namespace api diff --git a/src/api/IServer.hh b/src/api/IServer.hh index 88d20fda..e3162118 100644 --- a/src/api/IServer.hh +++ b/src/api/IServer.hh @@ -4,7 +4,6 @@ #include "api/FetchAchievementInfo.hh" #include "api/FetchLeaderboardInfo.hh" -#include "api/LatestClient.hh" #include "api/ResolveHash.hh" #include "api/UpdateAchievement.hh" #include "api/UpdateLeaderboard.hh" @@ -28,7 +27,6 @@ public: virtual UpdateRichPresence::Response UpdateRichPresence(const UpdateRichPresence::Request& request) = 0; // === other functions === - virtual LatestClient::Response LatestClient(const LatestClient::Request& request) = 0; virtual UploadBadge::Response UploadBadge(const UploadBadge::Request& request) = 0; protected: diff --git a/src/api/LatestClient.hh b/src/api/LatestClient.hh deleted file mode 100644 index 5f976bfb..00000000 --- a/src/api/LatestClient.hh +++ /dev/null @@ -1,39 +0,0 @@ -#ifndef RA_API_LATESTCLIENT_HH -#define RA_API_LATESTCLIENT_HH -#pragma once - -#include "ApiCall.hh" - -namespace ra { -namespace api { - -class LatestClient -{ -public: - static constexpr const char* const Name() noexcept { return "LatestClient"; } - - struct Response : ApiResponseBase - { - std::string LatestVersion; - std::string MinimumVersion; - }; - - struct Request : ApiRequestBase - { - unsigned int EmulatorId; - - using Callback = std::function; - - Response Call() const; - - void CallAsync(Callback&& callback) const - { - ApiRequestBase::CallAsync(*this, std::move(callback)); - } - }; -}; - -} // namespace api -} // namespace ra - -#endif // !RA_API_LATESTCLIENT_HH diff --git a/src/api/impl/ConnectedServer.cpp b/src/api/impl/ConnectedServer.cpp index 8523cec3..1b29dd8c 100644 --- a/src/api/impl/ConnectedServer.cpp +++ b/src/api/impl/ConnectedServer.cpp @@ -752,33 +752,6 @@ UpdateRichPresence::Response ConnectedServer::UpdateRichPresence(const UpdateRic return response; } -LatestClient::Response ConnectedServer::LatestClient(const LatestClient::Request& request) -{ - LatestClient::Response response; - rapidjson::Document document; - std::string sPostData; - - // LatestClient doesn't require User/Password, so the next few lines are a subset of DoRequest - AppendUrlParam(sPostData, "r", "latestclient"); - AppendUrlParam(sPostData, "e", std::to_string(request.EmulatorId)); - RA_LOG_INFO("%s Request: %s", LatestClient::Name(), sPostData.c_str()); - - ra::services::Http::Request httpRequest(ra::util::String::Printf("%s/dorequest.php", m_sHost)); - httpRequest.SetPostData(sPostData); - - const auto httpResponse = httpRequest.Call(); - if (GetJson(LatestClient::Name(), httpResponse, response, document)) - { - response.Result = ApiResult::Success; - GetRequiredJsonField(response.LatestVersion, document, "LatestVersion", response); - GetOptionalJsonField(response.MinimumVersion, document, "MinimumVersion"); - if (response.MinimumVersion.empty()) - response.MinimumVersion = response.LatestVersion; - } - - return response; -} - UploadBadge::Response ConnectedServer::UploadBadge(const UploadBadge::Request& request) { UploadBadge::Response response; diff --git a/src/api/impl/ConnectedServer.hh b/src/api/impl/ConnectedServer.hh index 32964493..52acd609 100644 --- a/src/api/impl/ConnectedServer.hh +++ b/src/api/impl/ConnectedServer.hh @@ -21,7 +21,6 @@ public: UpdateLeaderboard::Response UpdateLeaderboard(const UpdateLeaderboard::Request& request) override; FetchLeaderboardInfo::Response FetchLeaderboardInfo(const FetchLeaderboardInfo::Request& request) override; UpdateRichPresence::Response UpdateRichPresence(const UpdateRichPresence::Request& request) override; - LatestClient::Response LatestClient(const LatestClient::Request& request) override; UploadBadge::Response UploadBadge(const UploadBadge::Request& request) override; private: diff --git a/src/api/impl/DisconnectedServer.cpp b/src/api/impl/DisconnectedServer.cpp deleted file mode 100644 index ec31fcf5..00000000 --- a/src/api/impl/DisconnectedServer.cpp +++ /dev/null @@ -1,21 +0,0 @@ -#include "DisconnectedServer.hh" - -#include "api\impl\ConnectedServer.hh" - -#include "services\ServiceLocator.hh" - -namespace ra { -namespace api { -namespace impl { - -LatestClient::Response DisconnectedServer::LatestClient(const LatestClient::Request& request) -{ - // LatestClient call doesn't require being logged in. Dispatch to the ConnectedServer::LatestClient method. - ConnectedServer serverApi(m_sHost); - return serverApi.LatestClient(request); -} - -} // namespace impl -} // namespace api -} // namespace ra - diff --git a/src/api/impl/DisconnectedServer.hh b/src/api/impl/DisconnectedServer.hh index b07f9811..94ae8811 100644 --- a/src/api/impl/DisconnectedServer.hh +++ b/src/api/impl/DisconnectedServer.hh @@ -1,12 +1,12 @@ #pragma once -#include "OfflineServer.hh" +#include "ServerBase.hh" namespace ra { namespace api { namespace impl { -class DisconnectedServer : public OfflineServer +class DisconnectedServer : public ServerBase { public: explicit DisconnectedServer(const std::string& sHost) : m_sHost(sHost) {} @@ -15,8 +15,6 @@ public: const std::string& Host() const noexcept { return m_sHost; } - LatestClient::Response LatestClient(const LatestClient::Request& request) override; - private: const std::string m_sHost; }; diff --git a/src/api/impl/OfflineServer.cpp b/src/api/impl/OfflineServer.cpp deleted file mode 100644 index 0a4e2095..00000000 --- a/src/api/impl/OfflineServer.cpp +++ /dev/null @@ -1,27 +0,0 @@ -#include "OfflineServer.hh" - -#include "RA_Json.h" - -#include "api\impl\ConnectedServer.hh" - -#include "services\ILocalStorage.hh" -#include "services\ServiceLocator.hh" - -#include - -namespace ra { -namespace api { -namespace impl { - -LatestClient::Response OfflineServer::LatestClient(const LatestClient::Request&) -{ - // all versions are newer than 0.0.0.0, and are therefore valid/allowed - LatestClient::Response response; - response.LatestVersion = response.MinimumVersion = "0.0.0.0"; - response.Result = ApiResult::Success; - return response; -} - -} // namespace impl -} // namespace api -} // namespace ra diff --git a/src/api/impl/OfflineServer.hh b/src/api/impl/OfflineServer.hh deleted file mode 100644 index 5776faf3..00000000 --- a/src/api/impl/OfflineServer.hh +++ /dev/null @@ -1,19 +0,0 @@ -#pragma once - -#include "ServerBase.hh" - -namespace ra { -namespace api { -namespace impl { - -class OfflineServer : public ServerBase -{ -public: - const char* Name() const noexcept override { return "offline client"; } - - LatestClient::Response LatestClient(const LatestClient::Request& request) override; -}; - -} // namespace impl -} // namespace api -} // namespace ra diff --git a/src/api/impl/ServerBase.hh b/src/api/impl/ServerBase.hh index c1d5d218..bb92592a 100644 --- a/src/api/impl/ServerBase.hh +++ b/src/api/impl/ServerBase.hh @@ -49,11 +49,6 @@ public: // === other functions === - LatestClient::Response LatestClient(const LatestClient::Request&) override - { - return UnsupportedApi(LatestClient::Name()); - } - UploadBadge::Response UploadBadge(const UploadBadge::Request&) override { return UnsupportedApi(UploadBadge::Name()); diff --git a/src/data/context/EmulatorContext.cpp b/src/data/context/EmulatorContext.cpp index e8dd1712..b0efa92d 100644 --- a/src/data/context/EmulatorContext.cpp +++ b/src/data/context/EmulatorContext.cpp @@ -6,8 +6,6 @@ #include "util\Log.hh" #include "util\Strings.hh" -#include "api\LatestClient.hh" - #include "context\IRcClient.hh" #include "context\UserContext.hh" @@ -30,6 +28,7 @@ #include "RAInterface\RA_Emulators.h" #include +#include namespace ra { namespace data { @@ -228,6 +227,83 @@ bool EmulatorContext::ValidateClientVersion() return true; } +bool EmulatorContext::FetchLatestVersion() +{ + const auto& pRcClient = ra::services::ServiceLocator::Get(); + + // latestclient does not have an rapi wrapper + rc_api_request_t request; + memset(&request, 0, sizeof(request)); + + rc_api_url_builder_t builder; + rc_api_url_build_dorequest_url(&request, pRcClient.GetHost()); + rc_url_builder_init(&builder, &request.buffer, 48); + rc_url_builder_append_str_param(&builder, "r", "latestclient"); + rc_url_builder_append_str_param(&builder, "e", std::to_string(ra::etoi(m_nEmulatorId)).c_str()); + request.post_data = rc_url_builder_finalize(&builder); + request.content_type = RC_CONTENT_TYPE_URLENCODED; + + // this function is expected to return a result synchronously + rc_api_server_response_t server_response; + std::string sResponseBuffer; + pRcClient.SendRequest(request, server_response, sResponseBuffer); + + rc_api_response_t api_response; + rc_buffer_init(&api_response.buffer); + + rc_json_field_t fields[] = { + RC_JSON_NEW_FIELD("Success"), + RC_JSON_NEW_FIELD("Error"), + RC_JSON_NEW_FIELD("Code"), + RC_JSON_NEW_FIELD("LatestVersion"), + RC_JSON_NEW_FIELD("MinimumVersion") + }; + + bool bResult = false; + const auto nResult = rc_json_parse_server_response(&api_response, &server_response, fields, sizeof(fields) / sizeof(fields[0])); + if (nResult != RC_OK || !api_response.succeeded) + { + m_sLatestVersionError = api_response.error_message ? api_response.error_message : rc_error_str(nResult); + + if (nResult == RC_NOT_FOUND) + { + // if m_nEmulatorID is not recognized by the server, let it through regardless of version. + // assume it's a new emulator that hasn't been released yet. + m_sLatestVersion = "0.0.0.0"; + bResult = true; + + ra::ui::viewmodels::MessageBoxViewModel::ShowWarningMessage(L"Could not retrieve latest client version.", ra::util::String::Widen(m_sLatestVersionError)); + } + else + { + m_sLatestVersion = "Unknown"; + } + } + else + { + const char* sLatestVersion; + if (rc_json_get_required_string(&sLatestVersion, &api_response, &fields[3], "LatestVersion")) + { + m_sLatestVersion = sLatestVersion; + + const char* sMinimumVersion; + rc_json_get_optional_string(&sMinimumVersion, &api_response, &fields[4], "MinimumVersion", sLatestVersion); + m_sMinimumVersion = sMinimumVersion; + + const unsigned long long nServerVersion = ParseVersion(m_sLatestVersion.c_str()); + const unsigned long long nLocalVersion = ParseVersion(m_sVersion.c_str()); + RA_LOG_INFO("Client %s date: server %s, current %s", + (nLocalVersion >= nServerVersion) ? "up to" : "out of", + m_sLatestVersion, + m_sVersion); + + bResult = true; + } + } + + return bResult; +} + /// /// Returns true if the player is allowed to play using the current client. /// @@ -241,43 +317,7 @@ bool EmulatorContext::ValidateClientVersion(bool& bHardcore) // fetch the latest version if (m_sLatestVersion.empty()) - { - ra::api::LatestClient::Request request; - request.EmulatorId = ra::etoi(m_nEmulatorId); - auto response = request.Call(); - if (!response.Succeeded()) - { - if (ra::util::String::StartsWith(response.ErrorMessage, "Unknown client")) - { - // if m_nEmulatorID is not recognized by the server, let it through regardless of version. - // assume it's a new emulator that hasn't been released yet. - m_sLatestVersion = "0.0.0.0"; - ra::ui::viewmodels::MessageBoxViewModel::ShowWarningMessage(L"Could not retrieve latest client version.", ra::util::String::Widen(response.ErrorMessage)); - } - else - { - m_sLatestVersion = "Unknown"; - m_sLatestVersionError = response.ErrorMessage; - } - } - else - { - m_sLatestVersion = response.LatestVersion; - m_sMinimumVersion = response.MinimumVersion; - -#ifndef RA_UTEST - const unsigned long long nServerVersion = ParseVersion(m_sLatestVersion.c_str()); - const unsigned long long nLocalVersion = ParseVersion(m_sVersion.c_str()); - RA_LOG_INFO("Client %s date: server %s, current %s", - (nLocalVersion >= nServerVersion) ? "up to" : "out of", - m_sLatestVersion, - m_sVersion); -#endif - } - - if (m_nEmulatorId == EmulatorID::RA_Gens) - ra::ui::viewmodels::MessageBoxViewModel::ShowWarningMessage(L"RAGens is being retired", L"With the next major release of the toolkit, you will no longer be able to play games using RAGens. Please switch over to RALibretro or RetroArch."); - } + FetchLatestVersion(); // if we failed to fetch the latest version, abort if (m_sLatestVersion == "Unknown") diff --git a/src/data/context/EmulatorContext.hh b/src/data/context/EmulatorContext.hh index 31803183..9f49edcd 100644 --- a/src/data/context/EmulatorContext.hh +++ b/src/data/context/EmulatorContext.hh @@ -187,6 +187,7 @@ public: protected: void UpdateUserAgent(); virtual bool ValidateClientVersion(bool& bHardcore); + bool FetchLatestVersion(); EmulatorID m_nEmulatorId = EmulatorID::UnknownEmulator; std::string m_sVersion; diff --git a/src/devkit/context/impl/RcClient.cpp b/src/devkit/context/impl/RcClient.cpp index 630c3768..d2605895 100644 --- a/src/devkit/context/impl/RcClient.cpp +++ b/src/devkit/context/impl/RcClient.cpp @@ -146,7 +146,7 @@ static std::string LogRequest(std::string sParams) return sApi; } -static void LogResponse(const std::string& sApi, const ra::services::Http::Response& httpResponse) +void RcClient::LogResponse(const std::string& sApi, const ra::services::Http::Response& httpResponse) const { if (ra::services::ServiceLocator::Exists()) { @@ -162,11 +162,11 @@ static void LogResponse(const std::string& sApi, const ra::services::Http::Respo if (nIndex2 != std::string::npos) sResponse.replace(nIndex, nIndex2 - nIndex, "[redacted]"); } - RA_LOG_INFO("<< %s response (%d): %s", sApi.c_str(), ra::etoi(httpResponse.StatusCode()), sResponse.c_str()); + RA_LOG_INFO("<< %s %s (%d): %s", sApi.c_str(), GetResponseLogQualifier(), ra::etoi(httpResponse.StatusCode()), sResponse.c_str()); } else { - RA_LOG_INFO("<< %s response (%d): %s", sApi.c_str(), ra::etoi(httpResponse.StatusCode()), httpResponse.Content().c_str()); + RA_LOG_INFO("<< %s %s (%d): %s", sApi.c_str(), GetResponseLogQualifier(), ra::etoi(httpResponse.StatusCode()), httpResponse.Content().c_str()); } } } @@ -178,7 +178,7 @@ void RcClient::SendRequest(const rc_api_request_t& pRequest, rc_api_server_respo httpRequest.SetContentType(pRequest.content_type); std::string sApi = LogRequest(httpRequest.GetPostData()); - const auto httpResponse = httpRequest.Call(); + const auto httpResponse = CallApiSync(sApi, httpRequest); LogResponse(sApi, httpResponse); @@ -191,6 +191,11 @@ void RcClient::SendRequest(const rc_api_request_t& pRequest, rc_api_server_respo } } +ra::services::Http::Response RcClient::CallApiSync(const std::string&, const ra::services::Http::Request& pRequest) const +{ + return pRequest.Call(); +} + void RcClient::DispatchRequest(const rc_api_request_t& pRequest, std::function fCallback, void* pCallbackData) const @@ -231,7 +236,7 @@ void RcClient::CallApi(const std::string& sApi, const ra::services::Http::Reques sParameter = ra::util::String::Widen(svGameId); } - pRequest.CallAsync([fCallback, pCallbackData, sApi=sApi, sParameter](const ra::services::Http::Response& httpResponse) + pRequest.CallAsync([this, fCallback, pCallbackData, sApi=sApi, sParameter](const ra::services::Http::Response& httpResponse) { rc_api_server_response_t pResponse; std::string sErrorBuffer; diff --git a/src/devkit/context/impl/RcClient.hh b/src/devkit/context/impl/RcClient.hh index c3a2915b..eda8cdb1 100644 --- a/src/devkit/context/impl/RcClient.hh +++ b/src/devkit/context/impl/RcClient.hh @@ -31,6 +31,10 @@ protected: virtual void CallApi(const std::string& sApi, const ra::services::Http::Request& pRequest, std::function fCallback, void* pCallbackData) const; + virtual ra::services::Http::Response CallApiSync(const std::string& sApi, + const ra::services::Http::Request& pRequest) const; + void LogResponse(const std::string& sApi, const ra::services::Http::Response& httpResponse) const; + virtual const char* GetResponseLogQualifier() const noexcept { return "response"; } private: static void LogMessage(const char* sMessage, const rc_client_t* pClient); diff --git a/src/services/impl/OfflineRcClient.cpp b/src/services/impl/OfflineRcClient.cpp index 48448b55..9c8111f1 100644 --- a/src/services/impl/OfflineRcClient.cpp +++ b/src/services/impl/OfflineRcClient.cpp @@ -121,6 +121,9 @@ static ra::services::Http::Response HandleOfflineRequest(const ra::services::Htt return ra::services::Http::Response(ra::services::Http::StatusCode::OK, sContents); } + if (sApi == "latestclient") + return ra::services::Http::Response(ra::services::Http::StatusCode::OK, "{\"Success\":true,\"LatestVersion\":\"0.0.0.0\"}"); + return ra::services::Http::Response(ra::services::Http::StatusCode::NotImplemented, ra::util::String::Printf("{\"Success\":false,\"Error\":\"No offline implementation for %s\"}", sApi)); } @@ -130,14 +133,11 @@ void OfflineRcClient::CallApi(const std::string& sApi, const ra::services::Http: void* pCallbackData) const { ra::services::ServiceLocator::GetMutable().RunAsync( - [httpRequest = pRequest, fCallback, pCallbackData, sApi]() + [this, httpRequest = pRequest, fCallback, pCallbackData, sApi]() { ra::services::Http::Response httpResponse = HandleOfflineRequest(httpRequest, sApi); - if (ra::services::ServiceLocator::Exists()) - { - RA_LOG_INFO("<< %s response (offline) (%d): %s", sApi.c_str(), ra::etoi(httpResponse.StatusCode()), httpResponse.Content().c_str()); - } + LogResponse(sApi, httpResponse); rc_api_server_response_t pResponse; memset(&pResponse, 0, sizeof(pResponse)); @@ -149,6 +149,11 @@ void OfflineRcClient::CallApi(const std::string& sApi, const ra::services::Http: }); } +ra::services::Http::Response OfflineRcClient::CallApiSync(const std::string& sApi, const ra::services::Http::Request& pRequest) const +{ + return HandleOfflineRequest(pRequest, sApi); +} + } // namespace impl } // namespace services } // namespace ra diff --git a/src/services/impl/OfflineRcClient.hh b/src/services/impl/OfflineRcClient.hh index bd26d027..a8da9446 100644 --- a/src/services/impl/OfflineRcClient.hh +++ b/src/services/impl/OfflineRcClient.hh @@ -14,6 +14,11 @@ protected: void CallApi(const std::string& sApi, const ra::services::Http::Request& pRequest, std::function fCallback, void* pCallbackData) const override; + + ra::services::Http::Response CallApiSync(const std::string& sApi, + const ra::services::Http::Request& pRequest) const override; + + const char* GetResponseLogQualifier() const noexcept override { return "response [offline]"; } }; } // namespace impl diff --git a/tests/RA_Integration.Tests.vcxproj b/tests/RA_Integration.Tests.vcxproj index fbfda826..02f1f0ec 100644 --- a/tests/RA_Integration.Tests.vcxproj +++ b/tests/RA_Integration.Tests.vcxproj @@ -76,8 +76,6 @@ - - @@ -140,7 +138,6 @@ - diff --git a/tests/RA_Integration.Tests.vcxproj.filters b/tests/RA_Integration.Tests.vcxproj.filters index 2b1e921d..6a44d291 100644 --- a/tests/RA_Integration.Tests.vcxproj.filters +++ b/tests/RA_Integration.Tests.vcxproj.filters @@ -102,21 +102,12 @@ Code - - Tests\API - Code - - Code - Code - - Code - Code diff --git a/tests/api/ConnectedServer_Tests.cpp b/tests/api/ConnectedServer_Tests.cpp deleted file mode 100644 index 3b111c88..00000000 --- a/tests/api/ConnectedServer_Tests.cpp +++ /dev/null @@ -1,227 +0,0 @@ -#include "CppUnitTest.h" - -#include "api\impl\ConnectedServer.hh" - -#include "api\impl\DisconnectedServer.hh" - -#include "tests\RA_UnitTestHelpers.h" -#include "tests\api\ApiAsserts.hh" -#include "tests\devkit\context\mocks\MockUserContext.hh" -#include "tests\devkit\services\mocks\MockHttpRequester.hh" -#include "tests\devkit\services\mocks\MockLocalStorage.hh" -#include "tests\devkit\services\mocks\MockThreadPool.hh" -#include "tests\mocks\MockServer.hh" - - -using namespace Microsoft::VisualStudio::CppUnitTestFramework; - -using ra::api::impl::ConnectedServer; -using ra::api::mocks::MockServer; -using ra::context::mocks::MockUserContext; -using ra::services::mocks::MockHttpRequester; -using ra::services::mocks::MockLocalStorage; -using ra::services::mocks::MockThreadPool; -using ra::services::Http; - -namespace ra { -namespace api { -namespace tests { - -TEST_CLASS(ConnectedServer_Tests) -{ -public: - // ConnectedServer manages converting the Http error codes to Incomplete responses, this just tests the handling - // of Incomplete responses by the ApiRequestBase. - - // ==================================================== - // These tests validate the generic handling of errors - they just happen to be written using the LatestClient API - - TEST_METHOD(TestLatestClientInvalid) - { - MockHttpRequester mockHttp([]([[maybe_unused]] const Http::Request& /*request*/) - { - return Http::Response(Http::StatusCode::OK, "{\"Success\":false,\"Error\":\"Unknown client\"}"); - }); - - ConnectedServer server("host.com"); - - LatestClient::Request request; - request.EmulatorId = 1; - - auto response = server.LatestClient(request); - - Assert::AreEqual(ApiResult::Error, response.Result); - Assert::AreEqual(std::string("Unknown client"), response.ErrorMessage); - Assert::AreEqual(std::string(""), response.LatestVersion); - Assert::AreEqual(std::string(""), response.MinimumVersion); - } - - TEST_METHOD(TestLatestClientFailed) - { - MockHttpRequester mockHttp([]([[maybe_unused]] const Http::Request& /*request*/) - { - return Http::Response(Http::StatusCode::OK, "{\"Success\":false}"); - }); - - ConnectedServer server("host.com"); - - LatestClient::Request request; - request.EmulatorId = 1; - - auto response = server.LatestClient(request); - - // "Success:false" without error message results in Failed call, not Error call - Assert::AreEqual(ApiResult::Failed, response.Result); - Assert::AreEqual(std::string(""), response.ErrorMessage); - Assert::AreEqual(std::string(""), response.LatestVersion); - Assert::AreEqual(std::string(""), response.MinimumVersion); - } - - TEST_METHOD(TestLatestClientFailed401) - { - MockHttpRequester mockHttp([]([[maybe_unused]] const Http::Request& /*request*/) - { - return Http::Response(Http::StatusCode::Unauthorized, ""); - }); - - ConnectedServer server("host.com"); - - LatestClient::Request request; - request.EmulatorId = 1; - - auto response = server.LatestClient(request); - - Assert::AreEqual(ApiResult::Error, response.Result); - Assert::AreEqual(std::string("HTTP error code: 401 (err401)"), response.ErrorMessage); - Assert::AreEqual(std::string(""), response.LatestVersion); - Assert::AreEqual(std::string(""), response.MinimumVersion); - } - - TEST_METHOD(TestLatestClientFailed401WithMessage) - { - MockHttpRequester mockHttp([]([[maybe_unused]] const Http::Request& /*request*/) - { - return Http::Response(Http::StatusCode::Unauthorized, "{\"Success\":false,\"Error\":\"Unknown client\"}"); - }); - - ConnectedServer server("host.com"); - - LatestClient::Request request; - request.EmulatorId = 1; - - auto response = server.LatestClient(request); - - Assert::AreEqual(ApiResult::Error, response.Result); - Assert::AreEqual(std::string("Unknown client"), response.ErrorMessage); - Assert::AreEqual(std::string(""), response.LatestVersion); - Assert::AreEqual(std::string(""), response.MinimumVersion); - } - - TEST_METHOD(TestLatestClientUnknownServer) - { - MockHttpRequester mockHttp([]([[maybe_unused]] const Http::Request& /*request*/) - { - return Http::Response(Http::StatusCode::NotFound, ""); - }); - - ConnectedServer server("host.com"); - - LatestClient::Request request; - request.EmulatorId = 1; - - auto response = server.LatestClient(request); - - Assert::AreEqual(ApiResult::Error, response.Result); - Assert::AreEqual(std::string("HTTP error code: 404 (err404)"), response.ErrorMessage); - Assert::AreEqual(std::string(""), response.LatestVersion); - Assert::AreEqual(std::string(""), response.MinimumVersion); - } - - TEST_METHOD(TestLatestClientEmptyResponse) - { - MockHttpRequester mockHttp([]([[maybe_unused]] const Http::Request& /*request*/) - { - return Http::Response(Http::StatusCode::OK, ""); - }); - - ConnectedServer server("host.com"); - - LatestClient::Request request; - request.EmulatorId = 1; - - auto response = server.LatestClient(request); - - Assert::AreEqual(ApiResult::Failed, response.Result); - Assert::AreEqual(std::string("Empty JSON response"), response.ErrorMessage); - Assert::AreEqual(std::string(""), response.LatestVersion); - Assert::AreEqual(std::string(""), response.MinimumVersion); - } - - TEST_METHOD(TestLatestClientInvalidJson) - { - MockHttpRequester mockHttp([]([[maybe_unused]] const Http::Request& /*request*/) - { - return Http::Response(Http::StatusCode::OK, "You do not have access to that resource"); - }); - - ConnectedServer server("host.com"); - - LatestClient::Request request; - request.EmulatorId = 1; - - auto response = server.LatestClient(request); - - Assert::AreEqual(ApiResult::Error, response.Result); - Assert::AreEqual(std::string("You do not have access to that resource"), response.ErrorMessage); - Assert::AreEqual(std::string(""), response.LatestVersion); - Assert::AreEqual(std::string(""), response.MinimumVersion); - } - - TEST_METHOD(TestLatestClientHtmlResponse) - { - MockHttpRequester mockHttp([]([[maybe_unused]] const Http::Request& /*request*/) - { - return Http::Response(Http::StatusCode::OK, "You do not have access to that resource"); - }); - - ConnectedServer server("host.com"); - - LatestClient::Request request; - request.EmulatorId = 1; - - auto response = server.LatestClient(request); - - Assert::AreEqual(ApiResult::Error, response.Result); - Assert::AreEqual(std::string("You do not have access to that resource"), response.ErrorMessage); - Assert::AreEqual(std::string(""), response.LatestVersion); - Assert::AreEqual(std::string(""), response.MinimumVersion); - } - - TEST_METHOD(TestLatestClientNoRequiredFields) - { - MockHttpRequester mockHttp([]([[maybe_unused]] const Http::Request& /*request*/) - { - return Http::Response(Http::StatusCode::OK, "{\"Success\":true}"); - }); - - ConnectedServer server("host.com"); - - LatestClient::Request request; - request.EmulatorId = 1; - - auto response = server.LatestClient(request); - - // only the first missing required field is reported - Assert::AreEqual(ApiResult::Error, response.Result); - Assert::AreEqual(std::string("LatestVersion not found in response"), response.ErrorMessage); - Assert::AreEqual(std::string(""), response.LatestVersion); - Assert::AreEqual(std::string(""), response.MinimumVersion); - } - - // End of generic validation tests - // ==================================================== -}; - -} // namespace tests -} // namespace api -} // namespace ra diff --git a/tests/data/context/EmulatorContext_Tests.cpp b/tests/data/context/EmulatorContext_Tests.cpp index 095051b4..79359fda 100644 --- a/tests/data/context/EmulatorContext_Tests.cpp +++ b/tests/data/context/EmulatorContext_Tests.cpp @@ -20,7 +20,6 @@ #include "tests\mocks\MockGameContext.hh" #include "tests\mocks\MockLoginService.hh" #include "tests\mocks\MockOverlayManager.hh" -#include "tests\mocks\MockServer.hh" #include "tests\mocks\MockWindowManager.hh" using namespace Microsoft::VisualStudio::CppUnitTestFramework; @@ -36,7 +35,6 @@ TEST_CLASS(EmulatorContext_Tests) class EmulatorContextHarness : public EmulatorContext { public: - ra::api::mocks::MockServer mockServer; ra::context::mocks::MockEmulatorMemoryContext mockEmulatorMemoryContext; ra::context::mocks::MockRcClient mockRcClient; ra::context::mocks::MockUserContext mockUserContext; @@ -62,13 +60,9 @@ TEST_CLASS(EmulatorContext_Tests) void MockVersions(const std::string& sClientVersion, const std::string& sServerVersion, const std::string& sMinimumVersion) { SetClientVersion(sClientVersion); - mockServer.HandleRequest([sServerVersion, sMinimumVersion](const ra::api::LatestClient::Request&, ra::api::LatestClient::Response& response) - { - response.LatestVersion = sServerVersion; - response.MinimumVersion = sMinimumVersion; - response.Result = ra::api::ApiResult::Success; - return true; - }); + + mockRcClient.MockResponse("r=latestclient&e=" + std::to_string(ra::etoi(m_nEmulatorId)), + ra::util::String::Printf("{\"Success\":true,\"MinimumVersion\":\"%s\",\"LatestVersion\":\"%s\"}", sMinimumVersion, sServerVersion)); } void MockVersions(const std::string& sClientVersion, const std::string& sServerVersion) @@ -143,14 +137,7 @@ TEST_CLASS(EmulatorContext_Tests) { EmulatorContextHarness emulator; emulator.Initialize(EmulatorID::RA_Snes9x, nullptr); - emulator.SetClientVersion("0.56"); - emulator.mockServer.HandleRequest([](const ra::api::LatestClient::Request& request, ra::api::LatestClient::Response& response) - { - Assert::AreEqual(static_cast(EmulatorID::RA_Snes9x), request.EmulatorId); - response.LatestVersion = "0.56"; - response.Result = ra::api::ApiResult::Success; - return true; - }); + emulator.MockVersions("0.56", "0.56"); Assert::IsTrue(emulator.ValidateClientVersion()); Assert::IsFalse(emulator.mockDesktop.WasDialogShown()); @@ -171,12 +158,7 @@ TEST_CLASS(EmulatorContext_Tests) EmulatorContextHarness emulator; emulator.Initialize(EmulatorID::RA_Snes9x, nullptr); emulator.SetClientVersion("0.57"); - emulator.mockServer.HandleRequest([](const ra::api::LatestClient::Request&, ra::api::LatestClient::Response& response) - { - response.ErrorMessage = "Could not communicate with server."; - response.Result = ra::api::ApiResult::Error; - return true; - }); + emulator.mockRcClient.MockResponse("r=latestclient&e=2", "Could not communicate with server.", 0); emulator.mockDesktop.ExpectWindow([](ra::ui::viewmodels::MessageBoxViewModel& vmMessageBox) { Assert::AreEqual(std::wstring(L"Could not retrieve latest client version."), vmMessageBox.GetHeader()); @@ -194,12 +176,7 @@ TEST_CLASS(EmulatorContext_Tests) emulator.Initialize(EmulatorID::RA_Snes9x, nullptr); emulator.SetClientVersion("0.57"); emulator.mockLoginService.Login("User", "Token"); - emulator.mockServer.HandleRequest([](const ra::api::LatestClient::Request&, ra::api::LatestClient::Response& response) - { - response.ErrorMessage = "Could not communicate with server."; - response.Result = ra::api::ApiResult::Error; - return true; - }); + emulator.mockRcClient.MockResponse("r=latestclient&e=2", "Could not communicate with server.", 0); emulator.mockDesktop.ExpectWindow([](ra::ui::viewmodels::MessageBoxViewModel& vmMessageBox) { Assert::AreEqual(std::wstring(L"Could not retrieve latest client version."), vmMessageBox.GetHeader()); @@ -219,16 +196,11 @@ TEST_CLASS(EmulatorContext_Tests) emulator.SetClientVersion("0.57"); emulator.mockConfiguration.SetFeatureEnabled(ra::services::Feature::Hardcore, true); emulator.mockConfiguration.SetApiToken("Token"); - emulator.mockServer.HandleRequest([](const ra::api::LatestClient::Request&, ra::api::LatestClient::Response& response) - { - response.ErrorMessage = "Could not communicate with server."; - response.Result = ra::api::ApiResult::Error; - return true; - }); + emulator.mockRcClient.MockResponse("r=latestclient&e=2", "{\"Success\":false,\"Error\":\"Something bad happened.\"}", 500); emulator.mockDesktop.ExpectWindow([](ra::ui::viewmodels::MessageBoxViewModel& vmMessageBox) { Assert::AreEqual(std::wstring(L"Could not retrieve latest client version."), vmMessageBox.GetHeader()); - Assert::AreEqual(std::wstring(L"The latest client is required for hardcore mode. Login canceled.\nCould not communicate with server."), vmMessageBox.GetMessage()); + Assert::AreEqual(std::wstring(L"The latest client is required for hardcore mode. Login canceled.\nSomething bad happened."), vmMessageBox.GetMessage()); return ra::ui::DialogResult::OK; }); @@ -243,16 +215,11 @@ TEST_CLASS(EmulatorContext_Tests) emulator.SetClientVersion("0.57"); emulator.mockConfiguration.SetFeatureEnabled(ra::services::Feature::Hardcore, true); emulator.mockLoginService.DisableLogin(); - emulator.mockServer.HandleRequest([](const ra::api::LatestClient::Request&, ra::api::LatestClient::Response& response) - { - response.ErrorMessage = "Could not communicate with server."; - response.Result = ra::api::ApiResult::Error; - return true; - }); + emulator.mockRcClient.MockResponse("r=latestclient&e=2", "{\"Success\":false,\"Error\":\"Something bad happened.\"}", 500); emulator.mockDesktop.ExpectWindow([](ra::ui::viewmodels::MessageBoxViewModel& vmMessageBox) { Assert::AreEqual(std::wstring(L"Could not retrieve latest client version."), vmMessageBox.GetHeader()); - Assert::AreEqual(std::wstring(L"The latest client is required for hardcore mode.\nCould not communicate with server."), vmMessageBox.GetMessage()); + Assert::AreEqual(std::wstring(L"The latest client is required for hardcore mode.\nSomething bad happened."), vmMessageBox.GetMessage()); return ra::ui::DialogResult::OK; }); @@ -267,16 +234,11 @@ TEST_CLASS(EmulatorContext_Tests) emulator.SetClientVersion("0.57"); emulator.mockConfiguration.SetFeatureEnabled(ra::services::Feature::Hardcore, true); emulator.mockLoginService.Login("User", "Token"); - emulator.mockServer.HandleRequest([](const ra::api::LatestClient::Request&, ra::api::LatestClient::Response& response) - { - response.ErrorMessage = "Could not communicate with server."; - response.Result = ra::api::ApiResult::Error; - return true; - }); + emulator.mockRcClient.MockResponse("r=latestclient&e=2", "{\"Success\":false,\"Error\":\"Something bad happened.\"}", 500); emulator.mockDesktop.ExpectWindow([](ra::ui::viewmodels::MessageBoxViewModel& vmMessageBox) { Assert::AreEqual(std::wstring(L"Could not retrieve latest client version."), vmMessageBox.GetHeader()); - Assert::AreEqual(std::wstring(L"The latest client is required for hardcore mode. You will be logged out.\nCould not communicate with server."), vmMessageBox.GetMessage()); + Assert::AreEqual(std::wstring(L"The latest client is required for hardcore mode. You will be logged out.\nSomething bad happened."), vmMessageBox.GetMessage()); return ra::ui::DialogResult::OK; }); @@ -553,16 +515,11 @@ TEST_CLASS(EmulatorContext_Tests) EmulatorContextHarness emulator; emulator.Initialize(EmulatorID::RA_Snes9x, nullptr); emulator.SetClientVersion("1.0"); - emulator.mockServer.HandleRequest([](const ra::api::LatestClient::Request&, ra::api::LatestClient::Response& response) - { - response.ErrorMessage = "Unknown client"; - response.Result = ra::api::ApiResult::Error; - return true; - }); + emulator.mockRcClient.MockResponse("r=latestclient&e=2", "{\"Success\":false,\"Status\":404,\"Code\":\"not_found\",\"Error\":\"Unknown client.\"}", 404); emulator.mockDesktop.ExpectWindow([](ra::ui::viewmodels::MessageBoxViewModel& vmMessageBox) { Assert::AreEqual(std::wstring(L"Could not retrieve latest client version."), vmMessageBox.GetHeader()); - Assert::AreEqual(std::wstring(L"Unknown client"), vmMessageBox.GetMessage()); + Assert::AreEqual(std::wstring(L"Unknown client."), vmMessageBox.GetMessage()); return ra::ui::DialogResult::OK; }); diff --git a/tests/mocks/MockServer.hh b/tests/mocks/MockServer.hh index c278d6fa..a2cdce64 100644 --- a/tests/mocks/MockServer.hh +++ b/tests/mocks/MockServer.hh @@ -90,11 +90,6 @@ public: // === other functions === - LatestClient::Response LatestClient(const LatestClient::Request& request) override - { - return HandleRequest(request); - } - UploadBadge::Response UploadBadge(const UploadBadge::Request& request) override { return HandleRequest(request); From 87051ea13188b8499407169ee3d708c7ef78d368 Mon Sep 17 00:00:00 2001 From: Jamiras Date: Sun, 2 Aug 2026 16:23:45 -0600 Subject: [PATCH 3/3] remove unused code --- src/api/impl/ConnectedServer.cpp | 27 ------------------- src/data/context/EmulatorContext.cpp | 3 +++ .../OverlayRecentGamesPageViewModel.cpp | 2 -- 3 files changed, 3 insertions(+), 29 deletions(-) diff --git a/src/api/impl/ConnectedServer.cpp b/src/api/impl/ConnectedServer.cpp index 1b29dd8c..96b1c309 100644 --- a/src/api/impl/ConnectedServer.cpp +++ b/src/api/impl/ConnectedServer.cpp @@ -183,33 +183,6 @@ static void GetRequiredJsonField(_Out_ std::string& sValue, _In_ const rapidjson } } -static void GetOptionalJsonField(_Out_ std::string& sValue, _In_ const rapidjson::Value& pDocument, - _In_ const char* const sField, _In_ const char* const sDefaultValue = "") -{ - if (pDocument.HasMember(sField)) - { - auto& pField = pDocument[sField]; - if (pField.IsString()) - sValue = pField.GetString(); - else - sValue = sDefaultValue; - } - else - { - sValue = sDefaultValue; - } -} - -static void AppendUrlParam(_Inout_ std::string& sParams, _In_ const char* const sParam, _In_ const std::string& sValue) -{ - if (!sParams.empty() && sParams.back() != '?') - sParams.push_back('&'); - - sParams.append(sParam); - sParams.push_back('='); - ra::services::Http::UrlEncodeAppend(sParams, sValue); -} - static bool DoRequestWithoutLog(const rc_api_request_t& api_request, _UNUSED const char* sApiName, ra::services::Http::Response& pHttpResponse, ApiResponseBase& pResponse) { ra::services::Http::Request httpRequest(api_request.url); diff --git a/src/data/context/EmulatorContext.cpp b/src/data/context/EmulatorContext.cpp index b0efa92d..ac8183c7 100644 --- a/src/data/context/EmulatorContext.cpp +++ b/src/data/context/EmulatorContext.cpp @@ -290,12 +290,15 @@ bool EmulatorContext::FetchLatestVersion() rc_json_get_optional_string(&sMinimumVersion, &api_response, &fields[4], "MinimumVersion", sLatestVersion); m_sMinimumVersion = sMinimumVersion; +#ifndef RA_UTEST + // RA_LOG_INFO becomes a no-op in the unit tests so nServerVersion and nLocalVersion become unreferenced. const unsigned long long nServerVersion = ParseVersion(m_sLatestVersion.c_str()); const unsigned long long nLocalVersion = ParseVersion(m_sVersion.c_str()); RA_LOG_INFO("Client %s date: server %s, current %s", (nLocalVersion >= nServerVersion) ? "up to" : "out of", m_sLatestVersion, m_sVersion); +#endif bResult = true; } diff --git a/src/ui/viewmodels/OverlayRecentGamesPageViewModel.cpp b/src/ui/viewmodels/OverlayRecentGamesPageViewModel.cpp index 15fd8fd9..166a1fef 100644 --- a/src/ui/viewmodels/OverlayRecentGamesPageViewModel.cpp +++ b/src/ui/viewmodels/OverlayRecentGamesPageViewModel.cpp @@ -2,8 +2,6 @@ #include "util\Strings.hh" -#include "api\impl\OfflineServer.hh" - #include "context\IRcClient.hh" #include "context\UserContext.hh"