From 552017342ed077ec9a516b02ea4b6855a7c17007 Mon Sep 17 00:00:00 2001 From: Aotrix <9aotrix@protonmail.com> Date: Mon, 13 Jul 2026 00:23:36 +0200 Subject: Added test file for Token --- battleNetApi/CMakeLists.txt | 13 -- battleNetApi/include/hearthstoneTokenBuilder.hpp | 32 +++-- battleNetApi/include/token.hpp | 8 +- battleNetApi/src/hearthstoneTokenBuilder.cpp | 86 +++++-------- battleNetApi/src/token.cpp | 16 ++- battleNetApi/tests/CMakeLists.txt | 43 +++++++ battleNetApi/tests/src/tests.cpp | 144 ++++++++++++++++++++++ battleNetApi/tests/src/tokenTests.cpp | 20 +++ battleNetApi/tests/tests.cpp | 149 ----------------------- gameView/main.cpp | 6 +- 10 files changed, 284 insertions(+), 233 deletions(-) create mode 100644 battleNetApi/tests/CMakeLists.txt create mode 100644 battleNetApi/tests/src/tests.cpp create mode 100644 battleNetApi/tests/src/tokenTests.cpp delete mode 100644 battleNetApi/tests/tests.cpp diff --git a/battleNetApi/CMakeLists.txt b/battleNetApi/CMakeLists.txt index e662d13..6999d57 100644 --- a/battleNetApi/CMakeLists.txt +++ b/battleNetApi/CMakeLists.txt @@ -22,21 +22,8 @@ set(SRC ${PROJECT_SOURCE_DIR}/src/apiUrl.cpp ${PROJECT_SOURCE_DIR}/src/main.cpp ) -set(SRC_TESTS - ${PROJECT_SOURCE_DIR}/src/hearthstoneApi.cpp - ${PROJECT_SOURCE_DIR}/src/token.cpp - ${PROJECT_SOURCE_DIR}/src/tokenBuilder.cpp - ${PROJECT_SOURCE_DIR}/src/hearthstoneTokenBuilder.cpp - ${PROJECT_SOURCE_DIR}/src/apiUrl.cpp - ${PROJECT_SOURCE_DIR}/tests/tests.cpp -) - find_package(Catch2 3 REQUIRED) find_package(nlohmann_json 3.12.0 REQUIRED) -add_executable(tests ${SRC_TESTS}) -target_include_directories(tests PRIVATE ${INCLUDE_DIR}) -target_link_libraries(tests PRIVATE Catch2::Catch2WithMain curl) - add_executable(downloadCards ${SRC}) target_include_directories(downloadCards PRIVATE ${INCLUDE_DIR}) target_link_libraries(downloadCards PRIVATE curl) diff --git a/battleNetApi/include/hearthstoneTokenBuilder.hpp b/battleNetApi/include/hearthstoneTokenBuilder.hpp index 488aff4..e946fe8 100644 --- a/battleNetApi/include/hearthstoneTokenBuilder.hpp +++ b/battleNetApi/include/hearthstoneTokenBuilder.hpp @@ -14,30 +14,40 @@ class HearthstoneTokenBuilder : TokenBuilder { private: std::shared_ptr mCurrentToken; bool mUpdateSourceFile{false}; - std::vector mJsonPaths; + std::vector _credentialJsonPaths; + [[nodiscard]] bool isUpdateSourceFileAllowed() const; - const bool hasExpired(json iToken) const; - static size_t curlCallbackStringOutput(void *iPtr, size_t iSize, - size_t iNmemb, + [[nodiscard]] + const bool hasExpired(json iTokenJson) const; + static size_t curlCallbackStringOutput(void *iPtr, size_t iSize, size_t iNmemb, std::string *iUserData); + [[nodiscard]] std::chrono::seconds getCurrentTime() const; - std::pair getTokenWithApi() const; + [[nodiscard]] + std::pair + getTokenWithApi(const std::string &iAgentJsonFilePath) const; + [[nodiscard]] std::string getTokenFromLocalFile() const; - void writeTokenToLocalFile( - const std::pair &iToken) const; + void writeTokenToLocalFile(const std::pair &iToken) const; public: HearthstoneTokenBuilder(const std::span &iJsonPaths); - HearthstoneTokenBuilder(const std::string &iJsonPath); - HearthstoneTokenBuilder &addJsonPath(const std::string &iJsonPath); - HearthstoneTokenBuilder &addJsonPaths(const std::string &iJsonPath); + template + HearthstoneTokenBuilder(CredentialJsonPaths... iCredentialJsonPaths) { + (_credentialJsonPaths.push_back(iCredentialJsonPaths), ...); + } + template + HearthstoneTokenBuilder &addCredentialJsonFiles(CredentialJsonPaths... iCredentialJsonPaths) { + (_credentialJsonPaths.push_back(iCredentialJsonPaths), ...); + } HearthstoneTokenBuilder &allowUpdateSourceFile(); /** * @return Create an access token to perform API requests. - * First, the cache files are checked. + * First, the cache files are checked. (Which ones?) * A new token is computed if the token lifetimes expired, * or if there is no token at all. */ HearthstoneTokenBuilder &buildToken() override; + [[nodiscard]] std::shared_ptr getToken() override; }; diff --git a/battleNetApi/include/token.hpp b/battleNetApi/include/token.hpp index cc909a8..8b9f95e 100644 --- a/battleNetApi/include/token.hpp +++ b/battleNetApi/include/token.hpp @@ -1,13 +1,17 @@ #pragma once +#include #include class Token { public: - explicit Token(const std::string &iTokenText); + explicit Token(const std::string &iTokenText, const std::chrono::seconds &iExpirationTime); std::string text() const; void setText(const std::string &iTokenText); + void setExpirationTime(const std::chrono::seconds &iExpirationTime); + bool isExpired(); private: - std::string mTokenText; //!< Token value + std::string _tokenText; + std::chrono::seconds _expirationTime; }; diff --git a/battleNetApi/src/hearthstoneTokenBuilder.cpp b/battleNetApi/src/hearthstoneTokenBuilder.cpp index 8403f46..9509aa6 100644 --- a/battleNetApi/src/hearthstoneTokenBuilder.cpp +++ b/battleNetApi/src/hearthstoneTokenBuilder.cpp @@ -1,29 +1,20 @@ +#include #include +#include #include #include #include -HearthstoneTokenBuilder::HearthstoneTokenBuilder(const std::string &iJsonPath) { - mJsonPaths.push_back(iJsonPath); -} -HearthstoneTokenBuilder::HearthstoneTokenBuilder( - const std::span &iJsonPaths) { - mJsonPaths.append_range(iJsonPaths); -} -HearthstoneTokenBuilder & -HearthstoneTokenBuilder::addJsonPath(const std::string &iJsonPath) { - mJsonPaths.push_back(iJsonPath); - return *this; -} -HearthstoneTokenBuilder & -HearthstoneTokenBuilder::addJsonPaths(const std::string &iJsonPath) { - mJsonPaths.push_back(iJsonPath); - return *this; +HearthstoneTokenBuilder::HearthstoneTokenBuilder(const std::span &iJsonPaths) { + _credentialJsonPaths.append_range(iJsonPaths); } HearthstoneTokenBuilder &HearthstoneTokenBuilder::buildToken() { + std::ranges::find_if(_credentialJsonPaths, + [](const std::string &iCredentialJsonPath) { return true; }); if (hasExpired()) { - const std::pair kTokenContent{ - getTokenWithApi()}; + if (isUpdateSourceFileAllowed()) { + } + const std::pair kTokenContent{getTokenWithApi()}; writeTokenToLocalFile(kTokenContent); } // return getTokenFromLocalFile(); @@ -35,38 +26,31 @@ HearthstoneTokenBuilder &HearthstoneTokenBuilder::buildToken() { // mCurrentToken = std::make_unique(...); return *this; } -std::shared_ptr HearthstoneTokenBuilder::getToken() { - return mCurrentToken; -} +std::shared_ptr HearthstoneTokenBuilder::getToken() { return mCurrentToken; } HearthstoneTokenBuilder &HearthstoneTokenBuilder::allowUpdateSourceFile() { mUpdateSourceFile = true; return *this; } -bool HearthstoneTokenBuilder::isUpdateSourceFileAllowed() const { - return mUpdateSourceFile; -} +bool HearthstoneTokenBuilder::isUpdateSourceFileAllowed() const { return mUpdateSourceFile; } -const bool HearthstoneTokenBuilder::hasExpired(json iCredentials) const { - if (not iCredentials.contains("Token") or - not iCredentials["Token"].contains("ExpirationTime") or - not iCredentials["Token"].contains("Content")) { +const bool HearthstoneTokenBuilder::hasExpired(json iTokenJson) const { + if (not iTokenJson.contains("Token") or not iTokenJson["Token"].contains("ExpirationTime") or + not iTokenJson["Token"].contains("Content")) { return true; } - if (iCredentials["Token"]["Content"].dump().length() < - kTOKEN_CONTENT_MIN_LENGTH) { + if (iTokenJson["Token"]["Content"].dump().length() < kTOKEN_CONTENT_MIN_LENGTH) { return true; } - const std::uint64_t kExpirationTime{ - iCredentials["Token"]["ExpirationTime"]}; - const std::uint64_t kCurrentTime{static_cast( - std::chrono::duration_cast( - std::chrono::high_resolution_clock::now().time_since_epoch()) - .count())}; + const std::uint64_t kExpirationTime{iTokenJson["Token"]["ExpirationTime"]}; + const std::uint64_t kCurrentTime{ + static_cast(std::chrono::duration_cast( + std::chrono::high_resolution_clock::now().time_since_epoch()) + .count())}; return kCurrentTime >= kExpirationTime; } -size_t HearthstoneTokenBuilder::curlCallbackStringOutput( - void *iPtr, size_t iSize, size_t iNmemb, std::string *iUserData) { +size_t HearthstoneTokenBuilder::curlCallbackStringOutput(void *iPtr, size_t iSize, size_t iNmemb, + std::string *iUserData) { size_t newLength{iSize * iNmemb}; try { iUserData->append(static_cast(iPtr), newLength); @@ -83,11 +67,10 @@ std::chrono::seconds HearthstoneTokenBuilder::getCurrentTime() const { } std::pair -HearthstoneTokenBuilder::getTokenWithApi() const { - std::ifstream aCredentialsFile{mFilePath}; - json aCredentials = json::parse(aCredentialsFile); - if (not aCredentials.contains("Agent") or - not aCredentials["Agent"].contains("Username") or +HearthstoneTokenBuilder::getTokenWithApi(const std::string &iAgentJsonFilePath) const { + std::ifstream aAgentJsonFile{iAgentJsonFilePath}; + json aCredentials = json::parse(aAgentJsonFile); + if (not aCredentials.contains("Agent") or not aCredentials["Agent"].contains("Username") or not aCredentials["Agent"].contains("Password")) { std::cerr << "Please define Agent/Username and Agent/Password in the " "JSON credentials file\n"; @@ -101,18 +84,15 @@ HearthstoneTokenBuilder::getTokenWithApi() const { curl_easy_setopt(aCurl, CURLOPT_URL, "https://oauth.battle.net/token"); curl_easy_setopt(aCurl, CURLOPT_SSL_VERIFYPEER, 0L); curl_easy_setopt(aCurl, CURLOPT_SSL_VERIFYHOST, 0L); - curl_easy_setopt(aCurl, CURLOPT_WRITEFUNCTION, - curlCallbackStringOutput); + curl_easy_setopt(aCurl, CURLOPT_WRITEFUNCTION, curlCallbackStringOutput); curl_easy_setopt(aCurl, CURLOPT_WRITEDATA, &aTokenInfo); - curl_easy_setopt( - aCurl, CURLOPT_USERPWD, - std::format("{}:{}", kAgentUsername, kAgentPassword).data()); - curl_easy_setopt(aCurl, CURLOPT_POSTFIELDS, - "grant_type=client_credentials"); + curl_easy_setopt(aCurl, CURLOPT_USERPWD, + std::format("{}:{}", kAgentUsername, kAgentPassword).data()); + curl_easy_setopt(aCurl, CURLOPT_POSTFIELDS, "grant_type=client_credentials"); CURLcode result_code{curl_easy_perform(aCurl)}; curl_easy_cleanup(aCurl); if (result_code != CURLE_OK) { - std::cerr << "ERROR: token request failed" << std::endl; + std::cerr << "ERROR: token request failed\n"; std::exit(EXIT_FAILURE); } } @@ -131,8 +111,8 @@ void HearthstoneTokenBuilder::writeTokenToLocalFile( json aCredentialsFile = json::parse(aTokenFileInput); aCredentialsFile["Token"]["Content"] = iToken.first; aCredentialsFile["Token"]["ExpirationTime"] = - std::chrono::duration_cast( - getCurrentTime() + std::chrono::seconds(iToken.second)) + std::chrono::duration_cast(getCurrentTime() + + std::chrono::seconds(iToken.second)) .count(); std::ofstream aTokenFileOutput{mFilePath}; diff --git a/battleNetApi/src/token.cpp b/battleNetApi/src/token.cpp index b312cc1..6fcef1a 100644 --- a/battleNetApi/src/token.cpp +++ b/battleNetApi/src/token.cpp @@ -1,7 +1,17 @@ #include "token.hpp" -Token::Token(const std::string &iTokenText) : mTokenText{iTokenText} {} +Token::Token(const std::string &iTokenText, const std::chrono::seconds &iExpirationTime) + : _tokenText{iTokenText}, _expirationTime{iExpirationTime} {} -std::string Token::text() const { return mTokenText; } +std::string Token::text() const { return _tokenText; } -void Token::setText(const std::string &iTokenText) { mTokenText = iTokenText; } +void Token::setText(const std::string &iTokenText) { _tokenText = iTokenText; } + +void Token::setExpirationTime(const std::chrono::seconds &iExpirationTime) { + _expirationTime = iExpirationTime; +} + +bool Token::isExpired() { + return _expirationTime <= std::chrono::duration_cast( + std::chrono::steady_clock::now().time_since_epoch()); +} diff --git a/battleNetApi/tests/CMakeLists.txt b/battleNetApi/tests/CMakeLists.txt new file mode 100644 index 0000000..6918fb5 --- /dev/null +++ b/battleNetApi/tests/CMakeLists.txt @@ -0,0 +1,43 @@ +cmake_minimum_required(VERSION 3.15) + +get_filename_component(FNAME ${CMAKE_SOURCE_DIR} NAME) +set(ProjectName ${FNAME}) + +project(${ProjectName} LANGUAGES CXX) + +set(CMAKE_COMPILE_COMMANDS ON) +set(CMAKE_CXX_STANDARD 23) +set(CXX_STANDARD_REQUIRED ON) +set(CMAKE_BUILD_TYPE Debug) +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall") +set(CMAKE_EXPORT_COMPILE_COMMANDS ON) +add_definitions(-DSOURCE_DIR_PATH="${CMAKE_SOURCE_DIR}") +find_package(Catch2 3 REQUIRED) +find_package(nlohmann_json 3.12.0 REQUIRED) +execute_process( + COMMAND "git" "rev-parse" "--show-toplevel" + OUTPUT_VARIABLE GIT_REPOSITORY_FOLDER_PATH + OUTPUT_STRIP_TRAILING_WHITESPACE +) +set(BATTLE_NET_API_DIR ${GIT_REPOSITORY_FOLDER_PATH}/battleNetApi) + +#set(SRC_TESTS +# ${PROJECT_SOURCE_DIR}/src/hearthstoneApi.cpp +# ${PROJECT_SOURCE_DIR}/src/token.cpp +# ${PROJECT_SOURCE_DIR}/src/tokenBuilder.cpp +# ${PROJECT_SOURCE_DIR}/src/hearthstoneTokenBuilder.cpp +# ${PROJECT_SOURCE_DIR}/src/apiUrl.cpp +# ${PROJECT_SOURCE_DIR}/tests/src/tests.cpp +#) +#add_executable(tests ${SRC_TESTS}) +#target_include_directories(tests PRIVATE ${INCLUDE_DIR}) +#target_link_libraries(tests PRIVATE Catch2::Catch2WithMain curl) + +set(INCLUDE_TOKEN_DIR ${BATTLE_NET_API_DIR}/include) +set(SRC_TOKEN_TESTS + ${BATTLE_NET_API_DIR}/src/token.cpp + ${BATTLE_NET_API_DIR}/tests/src/tokenTests.cpp +) +add_executable(tokenTests ${SRC_TOKEN_TESTS}) +target_include_directories(tokenTests PRIVATE ${INCLUDE_TOKEN_DIR}) +target_link_libraries(tokenTests PRIVATE Catch2::Catch2WithMain curl) diff --git a/battleNetApi/tests/src/tests.cpp b/battleNetApi/tests/src/tests.cpp new file mode 100644 index 0000000..d3c09c7 --- /dev/null +++ b/battleNetApi/tests/src/tests.cpp @@ -0,0 +1,144 @@ +#include "apiUrl.hpp" +#include "hearthstoneApi.hpp" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +TEST_CASE("Retrieve API Token", "[generateApiToken]") { + const std::string aCredentialsFile{"tests/resources/credentials.json"}; + Token aToken(aCredentialsFile); + const std::string aTokenContent{aToken.text()}; + const std::string::size_type aTokenSize{aTokenContent.size()}; + REQUIRE(aTokenSize >= 30); + REQUIRE(aTokenSize <= 40); +} + +TEST_CASE("Retrieve number of cards", "[countCards]") { + const std::string aCredentialsFile{}; + const std::string aContentType{"cards"}; + const std::string aRegion{"eu"}; + ApiUrl aApiUrl(aContentType, aRegion); + aApiUrl.setLocale("fr_FR"); + aApiUrl.setGameMode("constructed"); + aApiUrl.setPageSize("1"); + TokenPool::feedTokenSource("tests/resources/credentials.json"); + const json aResponse = + HearthstoneApi::makeApiRequest(aApiUrl, TokenPool::build().value().text()); + REQUIRE(aResponse.size() > 0); + REQUIRE(aResponse.contains("pageCount")); + std::uint64_t aNumberOfCards{aResponse["pageCount"]}; + std::uint64_t aExpectedNumberOfCards{6178}; + REQUIRE(aNumberOfCards == aExpectedNumberOfCards); +} + +TEST_CASE("Retrieve page once", "[retrievePageOnce]") { + const std::string aCredentialsFile{"tests/resources/credentials.json"}; + Token aToken(kCredentialsFile); + const std::string aContentType{"cards"}; + const std::string aRegion{"eu"}; + ApiUrl aApiUrl(aContentType, aRegion); + aApiUrl.setLocale("fr_FR"); + aApiUrl.setGameMode("constructed"); + aApiUrl.setPageSize("16"); + TokenPool::feedTokenSource("tests/resources/credentials.json"); + const json aApiResponse = + HearthstoneApi::makeApiRequest(aApiUrl, TokenPool::build().value().text()); + REQUIRE(aApiResponse["cards"].size() == 16); +} + +TEST_CASE("Retrieve pages 10 times without parallelization", "[retrievePagesNoParallel]") { + const std::string aCredentialsFile{"tests/resources/credentials.json"}; + Token aToken(aCredentialsFile); + const std::string aContentType{"cards"}; + const std::string aRegion{"eu"}; + ApiUrl aApiUrl(aContentType, aRegion); + aApiUrl.setLocale("fr_FR"); + aApiUrl.setGameMode("constructed"); + aApiUrl.setPageSize("16"); + aApiUrl.setPage("1"); + TokenPool::feedTokenSource("tests/resources/credentials.json"); + std::string aTokenText{TokenPool::build().value().text()}; + json aApiResponse = HearthstoneApi::makeApiRequest(aApiUrl, aTokenText); + + // Retrieve number of pages + const int aNumberOfRetrievals{10}; + const int aNumberOfPages{aApiResponse["pageCount"]}; + REQUIRE(aNumberOfPages >= aNumberOfRetrievals); + for (int i{1}; i < aNumberOfRetrievals; ++i) { + REQUIRE(aApiResponse["cards"].size() == 16); + aApiUrl.setPage(std::to_string(i + 1)); + aApiResponse = HearthstoneApi::makeApiRequest(aApiUrl, aTokenText); + } + REQUIRE(aApiResponse["cards"].size() >= 1); + REQUIRE(aApiResponse["cards"].size() <= 16); +} + +TEST_CASE("Retrieve pages 10 times with parallelization", "[retrievePagesParallel]") { + const std::string aCredentialsFile{"tests/resources/credentials.json"}; + Token aToken(kCredentialsFile); + const std::string aContentType{"cards"}; + const std::string aRegion{"eu"}; + ApiUrl aApiUrl(aContentType, aRegion); + aApiUrl.setLocale("fr_FR"); + aApiUrl.setGameMode("constructed"); + aApiUrl.setPageSize("16"); + aApiUrl.setPage("1"); + std::vector aRequests; + TokenPool::feedTokenSource("tests/resources/credentials.json"); + std::string aTokenText{TokenPool::build().value().text()}; + json aFirstPageJson = HearthstoneApi::makeApiRequest(aApiUrl, aTokenText); + + int kNumberOfPages{aFirstPageJson["pageCount"]}; + const int aNumberOfRetrievals{10}; + REQUIRE(aNumberOfPages >= aNumberOfRetrievals); + for (int i{1}; i < aNumberOfRetrievals; ++i) { + aApiUrl.setPage(std::to_string(i + 1)); + aRequests.push_back(aApiUrl.text()); + } + std::vector aApiResponses = HearthstoneApi::makeApiRequest(aRequests, aTokenText); + aApiResponses.emplace(aApiResponses.begin(), aFirstPageJson); + REQUIRE(aApiResponses.size() == aNumberOfRetrievals); + for (const json &aApiResponse : aApiResponses) { + REQUIRE(aApiResponse["cards"].size() == 16); + } +} + +TEST_CASE("Merge two card lists from two page requests", "[Merge pages]") { + std::ifstream aFirstPageFile{"tests/resources/page_merge_00.json"}; + json aFirstPageJson = json::parse(aFirstPageFile); + std::ifstream aSecondPageFile{"tests/resources/page_merge_01.json"}; + const json aSecondPageJson = json::parse(aSecondPageFile); + + json::reference aMergedCards = aFirstPageJson["cards"]; + for (auto &card : aSecondPageJson["cards"]) { + aMergedCards.push_back(std::move(card)); + } + REQUIRE(aMergedCards.size() == 32); + std::ofstream aOutputJson("build/output_pages_merge.json"); + aOutputJson << aMergedCards.dump(4); +} + +TEST_CASE("Retrieve a single page with HearthstoneApi::downloadCards", + "[retrievePageOnceHearthstoneApi]") { + const std::string aContentType{"cards"}; + const std::string aRegion{"eu"}; + ApiUrl aApiUrl(aContentType, aRegion); + aApiUrl.setPage(3); + TokenPool::feedTokenSource("tests/resources/credentials.json"); + json aApiResponse = HearthstoneApi::makeApiRequest(aApiUrl, TokenPool::build().value().text()); + const json::size_type aExpectedNumberOfCards{aApiResponse["cards"].size()}; + TokenPool::feedTokenSource("tests/resources/credentials.json"); + aApiResponse = HearthstoneApi::downloadCards(aApiUrl, TokenPool::build().value()); + const json::size_type aCurrentNumberOfCards{aApiResponse["cards"].size()}; + REQUIRE(aCurrentNumberOfCards == aExpectedNumberOfCards); +} diff --git a/battleNetApi/tests/src/tokenTests.cpp b/battleNetApi/tests/src/tokenTests.cpp new file mode 100644 index 0000000..89e4039 --- /dev/null +++ b/battleNetApi/tests/src/tokenTests.cpp @@ -0,0 +1,20 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +TEST_CASE("Retrieve API Token", "[generateApiToken]") { + const std::string aCredentialsFile{"../tests/resources/credentials.json"}; + Token aToken(aCredentialsFile, std::chrono::seconds(0)); + const std::string aTokenContent{aToken.text()}; + const std::string::size_type aTokenSize{aTokenContent.size()}; + REQUIRE(aTokenSize >= 30); + REQUIRE(aTokenSize <= 40); +} diff --git a/battleNetApi/tests/tests.cpp b/battleNetApi/tests/tests.cpp deleted file mode 100644 index 10c6f23..0000000 --- a/battleNetApi/tests/tests.cpp +++ /dev/null @@ -1,149 +0,0 @@ -#include "apiUrl.hpp" -#include "hearthstoneApi.hpp" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -TEST_CASE("Retrieve API Token", "[generateApiToken]") { - const std::string kCredentialsFile{"tests/resources/credentials.json"}; - Token aToken(kCredentialsFile); - const std::string kTokenContent{aToken.text()}; - const std::string::size_type kTokenSize{kTokenContent.size()}; - REQUIRE(kTokenSize >= 30); - REQUIRE(kTokenSize <= 40); -} - -TEST_CASE("Retrieve number of cards", "[countCards]") { - const std::string kCredentialsFile{}; - const std::string kContentType{"cards"}; - const std::string kRegion{"eu"}; - ApiUrl aApiUrl(kContentType, kRegion); - aApiUrl.setLocale("fr_FR"); - aApiUrl.setGameMode("constructed"); - aApiUrl.setPageSize("1"); - TokenPool::feedTokenSource("tests/resources/credentials.json"); - const json kResponse = HearthstoneApi::makeApiRequest( - aApiUrl, TokenPool::build().value().text()); - REQUIRE(kResponse.size() > 0); - REQUIRE(kResponse.contains("pageCount")); - std::uint64_t kNumberOfCards{kResponse["pageCount"]}; - std::uint64_t kExpectedNumberOfCards{6178}; - REQUIRE(kNumberOfCards == kExpectedNumberOfCards); -} - -TEST_CASE("Retrieve page once", "[retrievePageOnce]") { - const std::string kCredentialsFile{"tests/resources/credentials.json"}; - Token aToken(kCredentialsFile); - const std::string kContentType{"cards"}; - const std::string kRegion{"eu"}; - ApiUrl aApiUrl(kContentType, kRegion); - aApiUrl.setLocale("fr_FR"); - aApiUrl.setGameMode("constructed"); - aApiUrl.setPageSize("16"); - TokenPool::feedTokenSource("tests/resources/credentials.json"); - const json kApiResponse = HearthstoneApi::makeApiRequest( - aApiUrl, TokenPool::build().value().text()); - REQUIRE(kApiResponse["cards"].size() == 16); -} - -TEST_CASE("Retrieve pages 10 times without parallelization", - "[retrievePagesNoParallel]") { - const std::string kCredentialsFile{"tests/resources/credentials.json"}; - Token aToken(kCredentialsFile); - const std::string kContentType{"cards"}; - const std::string kRegion{"eu"}; - ApiUrl aApiUrl(kContentType, kRegion); - aApiUrl.setLocale("fr_FR"); - aApiUrl.setGameMode("constructed"); - aApiUrl.setPageSize("16"); - aApiUrl.setPage("1"); - TokenPool::feedTokenSource("tests/resources/credentials.json"); - std::string kTokenText{TokenPool::build().value().text()}; - json kApiResponse = HearthstoneApi::makeApiRequest(aApiUrl, kTokenText); - - // Retrieve number of pages - const int kNumberOfRetrievals{10}; - const int kNumberOfPages{kApiResponse["pageCount"]}; - REQUIRE(kNumberOfPages >= kNumberOfRetrievals); - for (int i{1}; i < kNumberOfRetrievals; ++i) { - REQUIRE(kApiResponse["cards"].size() == 16); - aApiUrl.setPage(std::to_string(i + 1)); - kApiResponse = HearthstoneApi::makeApiRequest(aApiUrl, kTokenText); - } - REQUIRE(kApiResponse["cards"].size() >= 1); - REQUIRE(kApiResponse["cards"].size() <= 16); -} - -TEST_CASE("Retrieve pages 10 times with parallelization", - "[retrievePagesParallel]") { - const std::string kCredentialsFile{"tests/resources/credentials.json"}; - Token aToken(kCredentialsFile); - const std::string kContentType{"cards"}; - const std::string kRegion{"eu"}; - ApiUrl aApiUrl(kContentType, kRegion); - aApiUrl.setLocale("fr_FR"); - aApiUrl.setGameMode("constructed"); - aApiUrl.setPageSize("16"); - aApiUrl.setPage("1"); - std::vector aRequests; - TokenPool::feedTokenSource("tests/resources/credentials.json"); - std::string kTokenText{TokenPool::build().value().text()}; - json kFirstPageJson = HearthstoneApi::makeApiRequest(aApiUrl, kTokenText); - - int kNumberOfPages{kFirstPageJson["pageCount"]}; - const int kNumberOfRetrievals{10}; - REQUIRE(kNumberOfPages >= kNumberOfRetrievals); - for (int i{1}; i < kNumberOfRetrievals; ++i) { - aApiUrl.setPage(std::to_string(i + 1)); - aRequests.push_back(aApiUrl.text()); - } - std::vector kApiResponses = - HearthstoneApi::makeApiRequest(aRequests, kTokenText); - kApiResponses.emplace(kApiResponses.begin(), kFirstPageJson); - REQUIRE(kApiResponses.size() == kNumberOfRetrievals); - for (const json &kApiResponse : kApiResponses) { - REQUIRE(kApiResponse["cards"].size() == 16); - } -} - -TEST_CASE("Merge two card lists from two page requests", "[Merge pages]") { - std::ifstream aFirstPage{"tests/resources/page_merge_00.json"}; - json kFirstPage = json::parse(aFirstPage); - std::ifstream aSecondPage{"tests/resources/page_merge_01.json"}; - const json kSecondPage = json::parse(aSecondPage); - - json::reference aMergedCards = kFirstPage["cards"]; - for (auto &card : kSecondPage["cards"]) { - aMergedCards.push_back(std::move(card)); - } - REQUIRE(aMergedCards.size() == 32); - std::ofstream aOutputJson("build/output_pages_merge.json"); - aOutputJson << aMergedCards.dump(4); -} - -TEST_CASE("Retrieve a single page with HearthstoneApi::downloadCards", - "[retrievePageOnceHearthstoneApi]") { - const std::string kContentType{"cards"}; - const std::string kRegion{"eu"}; - ApiUrl aApiUrl(kContentType, kRegion); - aApiUrl.setPage(3); - TokenPool::feedTokenSource("tests/resources/credentials.json"); - json kApiResponse = HearthstoneApi::makeApiRequest( - aApiUrl, TokenPool::build().value().text()); - const json::size_type kExpectedNumberOfCards{kApiResponse["cards"].size()}; - TokenPool::feedTokenSource("tests/resources/credentials.json"); - kApiResponse = - HearthstoneApi::downloadCards(aApiUrl, TokenPool::build().value()); - const json::size_type kCurrentNumberOfCards{kApiResponse["cards"].size()}; - REQUIRE(kCurrentNumberOfCards == kExpectedNumberOfCards); -} diff --git a/gameView/main.cpp b/gameView/main.cpp index 5b3a1ae..193fbed 100644 --- a/gameView/main.cpp +++ b/gameView/main.cpp @@ -10,7 +10,7 @@ #include #include -void configureMainMenuBar(QMenuBar &iMenuBar) { +void configureMainMenuBar(QMenuBar &iMenuBar, QWidget &iWindow) { QMenu *aOpenMenu = iMenuBar.addMenu("File"); aOpenMenu->addAction("Open JSON file", [] { QFileDialog aFileDialog; @@ -26,6 +26,7 @@ void configureMainMenuBar(QMenuBar &iMenuBar) { std::cout << kFilePaths[0].toStdString(); } }); + aOpenMenu->addAction("Quit", [&iWindow] { iWindow.close(); }); } int main(int argc, char *argv[]) { @@ -37,11 +38,12 @@ int main(int argc, char *argv[]) { QGridLayout aLayout(&aWindow); QMenuBar aMenuBar; - configureMainMenuBar(aMenuBar); + configureMainMenuBar(aMenuBar, aWindow); QListWidget aListOfCards; aListOfCards.insertItem(0, "first card"); aListOfCards.insertItem(1, "second card"); + aListOfCards.insertItem(2, "third card"); aLayout.addWidget(&aMenuBar, 0, 0); aLayout.addWidget(&aListOfCards, 1, 0); -- cgit v1.2.3