From 87bac0eee2eccd545a2ad5e9d382c4d222ef2fac Mon Sep 17 00:00:00 2001 From: Jojo-1000 <33495614+Jojo-1000@users.noreply.github.com> Date: Sun, 5 Apr 2020 21:21:31 +0200 Subject: [PATCH] Fix Hue::requestUsername exception on API error, update test. --- hueplusplus/Hue.cpp | 35 ++++++++++++++--------------------- hueplusplus/HueCommandAPI.cpp | 5 +---- hueplusplus/HueException.cpp | 9 +++++++++ hueplusplus/include/HueException.h | 8 ++++++++ hueplusplus/test/test_Hue.cpp | 105 +++++++++++++++++++++++++++++++++++++++++++++++++++------------------------------------------------------ 5 files changed, 83 insertions(+), 79 deletions(-) diff --git a/hueplusplus/Hue.cpp b/hueplusplus/Hue.cpp index f9462c9..6895644 100644 --- a/hueplusplus/Hue.cpp +++ b/hueplusplus/Hue.cpp @@ -179,33 +179,26 @@ std::string Hue::requestUsername() { lastCheck = std::chrono::steady_clock::now(); answer = http_handler->POSTJson("/api", request, ip, port); - - try + nlohmann::json jsonUser = utils::safeGetMember(answer, 0, "success", "username"); + if (jsonUser != nullptr) { - nlohmann::json jsonUser = utils::safeGetMember(answer, 0, "success", "username"); - if (jsonUser != nullptr) - { - // [{"success":{"username": ""}}] - username = jsonUser; - // Update commands with new username and ip - commands = HueCommandAPI(ip, port, username, http_handler); - std::cout << "Success! Link button was pressed!\n"; - std::cout << "Username is \"" << username << "\"\n"; - break; - } + // [{"success":{"username": ""}}] + username = jsonUser; + // Update commands with new username and ip + commands = HueCommandAPI(ip, port, username, http_handler); + std::cout << "Success! Link button was pressed!\n"; + std::cout << "Username is \"" << username << "\"\n"; + break; } - catch (const HueAPIResponseException& e) + else if (answer.size() > 0 && answer[0].count("error")) { - // 101: Link button not pressed - if (e.GetErrorNumber() == 101) + // All errors except 101: Link button not pressed + if (utils::safeGetMember(answer, 0, "error", "type") != 101) { - std::cout << "Link button not pressed!\n"; - } - else - { - throw; + throw HueAPIResponseException::Create(CURRENT_FILE_INFO, answer[0]); } } + std::this_thread::sleep_until(lastCheck + std::chrono::seconds(1)); } } diff --git a/hueplusplus/HueCommandAPI.cpp b/hueplusplus/HueCommandAPI.cpp index d2dbd87..da45561 100644 --- a/hueplusplus/HueCommandAPI.cpp +++ b/hueplusplus/HueCommandAPI.cpp @@ -112,10 +112,7 @@ nlohmann::json HueCommandAPI::HandleError(FileInfo fileInfo, const nlohmann::jso { if (response.count("error") != 0) { - int errorCode = response["type"]; - std::string address = response["address"]; - std::string description = response["description"]; - throw HueAPIResponseException(std::move(fileInfo), errorCode, std::move(address), std::move(description)); + throw HueAPIResponseException::Create(std::move(fileInfo), response); } return response; } diff --git a/hueplusplus/HueException.cpp b/hueplusplus/HueException.cpp index 15f7be4..c23bf33 100644 --- a/hueplusplus/HueException.cpp +++ b/hueplusplus/HueException.cpp @@ -69,6 +69,15 @@ const std::string& HueAPIResponseException::GetDescription() const noexcept return description; } +HueAPIResponseException HueAPIResponseException::Create(FileInfo fileInfo, const nlohmann::json& response) +{ + const nlohmann::json error = response["error"]; + int errorCode = error["type"]; + std::string address = error["address"]; + std::string description = error["description"]; + return HueAPIResponseException(std::move(fileInfo), errorCode, std::move(address), std::move(description)); +} + std::string HueAPIResponseException::GetMessage(int error, const std::string& addr, const std::string& description) { std::string result = std::to_string(error); diff --git a/hueplusplus/include/HueException.h b/hueplusplus/include/HueException.h index 0d2cf6b..11437e1 100644 --- a/hueplusplus/include/HueException.h +++ b/hueplusplus/include/HueException.h @@ -26,6 +26,8 @@ #include #include +#include "json/json.hpp" + //! \brief Contains information about error location, use CURRENT_FILE_INFO to create struct FileInfo { @@ -96,6 +98,12 @@ public: //! \brief Error description const std::string& GetDescription() const noexcept; + //! \brief Creates exception from API response. + //! \param fileInfo Location of the cause + //! \param response Hue API response. Must contain a member "error" with "type", "address" and "description". + //! \returns HueAPIResponseException with info from the response. + static HueAPIResponseException Create(FileInfo fileInfo, const nlohmann::json& response); + private: //! \brief Creates exception message containing the given information static std::string GetMessage(int error, const std::string& addr, const std::string& description); diff --git a/hueplusplus/test/test_Hue.cpp b/hueplusplus/test/test_Hue.cpp index a053515..45b4c42 100644 --- a/hueplusplus/test/test_Hue.cpp +++ b/hueplusplus/test/test_Hue.cpp @@ -57,7 +57,7 @@ protected: .Times(AtLeast(1)) .WillRepeatedly(Return(getBridgeXml())); } - ~HueFinderTest() {}; + ~HueFinderTest(){}; }; TEST_F(HueFinderTest, FindBridges) @@ -86,35 +86,25 @@ TEST_F(HueFinderTest, FindBridges) TEST_F(HueFinderTest, GetBridge) { using namespace ::testing; - nlohmann::json request; - request["devicetype"] = "HuePlusPlus#User"; + nlohmann::json request{{"devicetype", "HuePlusPlus#User"}}; - nlohmann::json user_ret_uns; - user_ret_uns = nlohmann::json::array(); - user_ret_uns[0] = nlohmann::json::object(); - user_ret_uns[0]["error"] = nlohmann::json::object(); - user_ret_uns[0]["error"]["type"] = 101; - user_ret_uns[0]["error"]["address"] = ""; - user_ret_uns[0]["error"]["description"] = "link button not pressed"; + nlohmann::json errorResponse + = {{{"error", {{"type", 101}, {"address", ""}, {"description", "link button not pressed"}}}}}; EXPECT_CALL(*handler, POSTJson("/api", request, getBridgeIp(), getBridgePort())) .Times(AtLeast(1)) - .WillRepeatedly(Return(user_ret_uns)); + .WillRepeatedly(Return(errorResponse)); HueFinder finder(handler); std::vector bridges = finder.FindBridges(); ASSERT_THROW(finder.GetBridge(bridges[0]), HueException); - nlohmann::json user_ret_suc; - user_ret_suc = nlohmann::json::array(); - user_ret_suc[0] = nlohmann::json::object(); - user_ret_suc[0]["success"] = nlohmann::json::object(); - user_ret_suc[0]["success"]["username"] = getBridgeUsername(); + nlohmann::json successResponse = {{{"success", {{"username", getBridgeUsername()}}}}}; EXPECT_CALL(*handler, POSTJson("/api", request, getBridgeIp(), getBridgePort())) .Times(1) - .WillOnce(Return(user_ret_suc)); + .WillOnce(Return(successResponse)); finder = HueFinder(handler); bridges = finder.FindBridges(); @@ -126,8 +116,7 @@ TEST_F(HueFinderTest, GetBridge) EXPECT_EQ(test_bridge.getUsername(), getBridgeUsername()) << "Bridge username not matching"; // Verify that username is correctly set in api requests - nlohmann::json hue_bridge_state; - hue_bridge_state["lights"] = {}; + nlohmann::json hue_bridge_state{{"lights", {}}}; EXPECT_CALL( *handler, GETJson("/api/" + getBridgeUsername(), nlohmann::json::object(), getBridgeIp(), getBridgePort())) .Times(1) @@ -176,51 +165,59 @@ TEST(Hue, requestUsername) { using namespace ::testing; std::shared_ptr handler = std::make_shared(); - nlohmann::json request; - request["devicetype"] = "HuePlusPlus#User"; + nlohmann::json request{{"devicetype", "HuePlusPlus#User"}}; - nlohmann::json user_ret_uns; - user_ret_uns = nlohmann::json::array(); - user_ret_uns[0] = nlohmann::json::object(); - user_ret_uns[0]["error"] = nlohmann::json::object(); - user_ret_uns[0]["error"]["type"] = 101; - user_ret_uns[0]["error"]["address"] = ""; - user_ret_uns[0]["error"]["description"] = "link button not pressed"; + { + nlohmann::json errorResponse + = {{{"error", {{"type", 101}, {"address", ""}, {"description", "link button not pressed"}}}}}; - EXPECT_CALL(*handler, POSTJson("/api", request, getBridgeIp(), getBridgePort())) - .Times(AtLeast(1)) - .WillRepeatedly(Return(user_ret_uns)); + EXPECT_CALL(*handler, POSTJson("/api", request, getBridgeIp(), getBridgePort())) + .Times(AtLeast(1)) + .WillRepeatedly(Return(errorResponse)); - Hue test_bridge(getBridgeIp(), getBridgePort(), "", handler); + Hue test_bridge(getBridgeIp(), getBridgePort(), "", handler); - test_bridge.requestUsername(); - EXPECT_EQ(test_bridge.getUsername(), "") << "Bridge username not matching"; + std::string username = test_bridge.requestUsername(); + EXPECT_EQ(username, "") << "Returned username not matching"; + EXPECT_EQ(test_bridge.getUsername(), "") << "Bridge username not matching"; + } - nlohmann::json user_ret_suc; - user_ret_suc = nlohmann::json::array(); - user_ret_suc[0] = nlohmann::json::object(); - user_ret_suc[0]["success"] = nlohmann::json::object(); - user_ret_suc[0]["success"]["username"] = getBridgeUsername(); - EXPECT_CALL(*handler, POSTJson("/api", request, getBridgeIp(), getBridgePort())) - .Times(1) - .WillRepeatedly(Return(user_ret_suc)); + { + // Other error code causes exception + int otherError = 1; + nlohmann::json exceptionResponse + = {{{"error", {{"type", otherError}, {"address", ""}, {"description", "some error"}}}}}; + Hue testBridge(getBridgeIp(), getBridgePort(), "", handler); - test_bridge = Hue(getBridgeIp(), getBridgePort(), "", handler); + EXPECT_CALL(*handler, POSTJson("/api", request, getBridgeIp(), getBridgePort())) + .WillOnce(Return(exceptionResponse)); - test_bridge.requestUsername(); + EXPECT_THROW(testBridge.requestUsername(), HueAPIResponseException); + } - EXPECT_EQ(test_bridge.getBridgeIP(), getBridgeIp()) << "Bridge IP not matching"; - EXPECT_EQ(test_bridge.getUsername(), getBridgeUsername()) << "Bridge username not matching"; + { + nlohmann::json successResponse = {{{"success", {{"username", getBridgeUsername()}}}}}; + EXPECT_CALL(*handler, POSTJson("/api", request, getBridgeIp(), getBridgePort())) + .Times(1) + .WillRepeatedly(Return(successResponse)); - // Verify that username is correctly set in api requests - nlohmann::json hue_bridge_state; - hue_bridge_state["lights"] = {}; - EXPECT_CALL( - *handler, GETJson("/api/" + getBridgeUsername(), nlohmann::json::object(), getBridgeIp(), getBridgePort())) - .Times(1) - .WillOnce(Return(hue_bridge_state)); + Hue test_bridge(getBridgeIp(), getBridgePort(), "", handler); - test_bridge.getAllLights(); + std::string username = test_bridge.requestUsername(); + + EXPECT_EQ(username, test_bridge.getUsername()) << "Returned username not matching"; + EXPECT_EQ(test_bridge.getBridgeIP(), getBridgeIp()) << "Bridge IP not matching"; + EXPECT_EQ(test_bridge.getUsername(), getBridgeUsername()) << "Bridge username not matching"; + + // Verify that username is correctly set in api requests + nlohmann::json hue_bridge_state{{"lights", {}}}; + EXPECT_CALL( + *handler, GETJson("/api/" + getBridgeUsername(), nlohmann::json::object(), getBridgeIp(), getBridgePort())) + .Times(1) + .WillOnce(Return(hue_bridge_state)); + + test_bridge.getAllLights(); + } } TEST(Hue, setIP) -- libgit2 0.21.4