Commit 87bac0eee2eccd545a2ad5e9d382c4d222ef2fac

Authored by Jojo-1000
Committed by Jan
1 parent 77890282

Fix Hue::requestUsername exception on API error, update test.

hueplusplus/Hue.cpp
@@ -179,33 +179,26 @@ std::string Hue::requestUsername() @@ -179,33 +179,26 @@ std::string Hue::requestUsername()
179 { 179 {
180 lastCheck = std::chrono::steady_clock::now(); 180 lastCheck = std::chrono::steady_clock::now();
181 answer = http_handler->POSTJson("/api", request, ip, port); 181 answer = http_handler->POSTJson("/api", request, ip, port);
182 -  
183 - try 182 + nlohmann::json jsonUser = utils::safeGetMember(answer, 0, "success", "username");
  183 + if (jsonUser != nullptr)
184 { 184 {
185 - nlohmann::json jsonUser = utils::safeGetMember(answer, 0, "success", "username");  
186 - if (jsonUser != nullptr)  
187 - {  
188 - // [{"success":{"username": "<username>"}}]  
189 - username = jsonUser;  
190 - // Update commands with new username and ip  
191 - commands = HueCommandAPI(ip, port, username, http_handler);  
192 - std::cout << "Success! Link button was pressed!\n";  
193 - std::cout << "Username is \"" << username << "\"\n";  
194 - break;  
195 - } 185 + // [{"success":{"username": "<username>"}}]
  186 + username = jsonUser;
  187 + // Update commands with new username and ip
  188 + commands = HueCommandAPI(ip, port, username, http_handler);
  189 + std::cout << "Success! Link button was pressed!\n";
  190 + std::cout << "Username is \"" << username << "\"\n";
  191 + break;
196 } 192 }
197 - catch (const HueAPIResponseException& e) 193 + else if (answer.size() > 0 && answer[0].count("error"))
198 { 194 {
199 - // 101: Link button not pressed  
200 - if (e.GetErrorNumber() == 101) 195 + // All errors except 101: Link button not pressed
  196 + if (utils::safeGetMember(answer, 0, "error", "type") != 101)
201 { 197 {
202 - std::cout << "Link button not pressed!\n";  
203 - }  
204 - else  
205 - {  
206 - throw; 198 + throw HueAPIResponseException::Create(CURRENT_FILE_INFO, answer[0]);
207 } 199 }
208 } 200 }
  201 +
209 std::this_thread::sleep_until(lastCheck + std::chrono::seconds(1)); 202 std::this_thread::sleep_until(lastCheck + std::chrono::seconds(1));
210 } 203 }
211 } 204 }
hueplusplus/HueCommandAPI.cpp
@@ -112,10 +112,7 @@ nlohmann::json HueCommandAPI::HandleError(FileInfo fileInfo, const nlohmann::jso @@ -112,10 +112,7 @@ nlohmann::json HueCommandAPI::HandleError(FileInfo fileInfo, const nlohmann::jso
112 { 112 {
113 if (response.count("error") != 0) 113 if (response.count("error") != 0)
114 { 114 {
115 - int errorCode = response["type"];  
116 - std::string address = response["address"];  
117 - std::string description = response["description"];  
118 - throw HueAPIResponseException(std::move(fileInfo), errorCode, std::move(address), std::move(description)); 115 + throw HueAPIResponseException::Create(std::move(fileInfo), response);
119 } 116 }
120 return response; 117 return response;
121 } 118 }
hueplusplus/HueException.cpp
@@ -69,6 +69,15 @@ const std::string&amp; HueAPIResponseException::GetDescription() const noexcept @@ -69,6 +69,15 @@ const std::string&amp; HueAPIResponseException::GetDescription() const noexcept
69 return description; 69 return description;
70 } 70 }
71 71
  72 +HueAPIResponseException HueAPIResponseException::Create(FileInfo fileInfo, const nlohmann::json& response)
  73 +{
  74 + const nlohmann::json error = response["error"];
  75 + int errorCode = error["type"];
  76 + std::string address = error["address"];
  77 + std::string description = error["description"];
  78 + return HueAPIResponseException(std::move(fileInfo), errorCode, std::move(address), std::move(description));
  79 +}
  80 +
72 std::string HueAPIResponseException::GetMessage(int error, const std::string& addr, const std::string& description) 81 std::string HueAPIResponseException::GetMessage(int error, const std::string& addr, const std::string& description)
73 { 82 {
74 std::string result = std::to_string(error); 83 std::string result = std::to_string(error);
hueplusplus/include/HueException.h
@@ -26,6 +26,8 @@ @@ -26,6 +26,8 @@
26 #include <exception> 26 #include <exception>
27 #include <string> 27 #include <string>
28 28
  29 +#include "json/json.hpp"
  30 +
29 //! \brief Contains information about error location, use CURRENT_FILE_INFO to create 31 //! \brief Contains information about error location, use CURRENT_FILE_INFO to create
30 struct FileInfo 32 struct FileInfo
31 { 33 {
@@ -96,6 +98,12 @@ public: @@ -96,6 +98,12 @@ public:
96 //! \brief Error description 98 //! \brief Error description
97 const std::string& GetDescription() const noexcept; 99 const std::string& GetDescription() const noexcept;
98 100
  101 + //! \brief Creates exception from API response.
  102 + //! \param fileInfo Location of the cause
  103 + //! \param response Hue API response. Must contain a member "error" with "type", "address" and "description".
  104 + //! \returns HueAPIResponseException with info from the response.
  105 + static HueAPIResponseException Create(FileInfo fileInfo, const nlohmann::json& response);
  106 +
99 private: 107 private:
100 //! \brief Creates exception message containing the given information 108 //! \brief Creates exception message containing the given information
101 static std::string GetMessage(int error, const std::string& addr, const std::string& description); 109 static std::string GetMessage(int error, const std::string& addr, const std::string& description);
hueplusplus/test/test_Hue.cpp
@@ -57,7 +57,7 @@ protected: @@ -57,7 +57,7 @@ protected:
57 .Times(AtLeast(1)) 57 .Times(AtLeast(1))
58 .WillRepeatedly(Return(getBridgeXml())); 58 .WillRepeatedly(Return(getBridgeXml()));
59 } 59 }
60 - ~HueFinderTest() {}; 60 + ~HueFinderTest(){};
61 }; 61 };
62 62
63 TEST_F(HueFinderTest, FindBridges) 63 TEST_F(HueFinderTest, FindBridges)
@@ -86,35 +86,25 @@ TEST_F(HueFinderTest, FindBridges) @@ -86,35 +86,25 @@ TEST_F(HueFinderTest, FindBridges)
86 TEST_F(HueFinderTest, GetBridge) 86 TEST_F(HueFinderTest, GetBridge)
87 { 87 {
88 using namespace ::testing; 88 using namespace ::testing;
89 - nlohmann::json request;  
90 - request["devicetype"] = "HuePlusPlus#User"; 89 + nlohmann::json request{{"devicetype", "HuePlusPlus#User"}};
91 90
92 - nlohmann::json user_ret_uns;  
93 - user_ret_uns = nlohmann::json::array();  
94 - user_ret_uns[0] = nlohmann::json::object();  
95 - user_ret_uns[0]["error"] = nlohmann::json::object();  
96 - user_ret_uns[0]["error"]["type"] = 101;  
97 - user_ret_uns[0]["error"]["address"] = "";  
98 - user_ret_uns[0]["error"]["description"] = "link button not pressed"; 91 + nlohmann::json errorResponse
  92 + = {{{"error", {{"type", 101}, {"address", ""}, {"description", "link button not pressed"}}}}};
99 93
100 EXPECT_CALL(*handler, POSTJson("/api", request, getBridgeIp(), getBridgePort())) 94 EXPECT_CALL(*handler, POSTJson("/api", request, getBridgeIp(), getBridgePort()))
101 .Times(AtLeast(1)) 95 .Times(AtLeast(1))
102 - .WillRepeatedly(Return(user_ret_uns)); 96 + .WillRepeatedly(Return(errorResponse));
103 97
104 HueFinder finder(handler); 98 HueFinder finder(handler);
105 std::vector<HueFinder::HueIdentification> bridges = finder.FindBridges(); 99 std::vector<HueFinder::HueIdentification> bridges = finder.FindBridges();
106 100
107 ASSERT_THROW(finder.GetBridge(bridges[0]), HueException); 101 ASSERT_THROW(finder.GetBridge(bridges[0]), HueException);
108 102
109 - nlohmann::json user_ret_suc;  
110 - user_ret_suc = nlohmann::json::array();  
111 - user_ret_suc[0] = nlohmann::json::object();  
112 - user_ret_suc[0]["success"] = nlohmann::json::object();  
113 - user_ret_suc[0]["success"]["username"] = getBridgeUsername(); 103 + nlohmann::json successResponse = {{{"success", {{"username", getBridgeUsername()}}}}};
114 104
115 EXPECT_CALL(*handler, POSTJson("/api", request, getBridgeIp(), getBridgePort())) 105 EXPECT_CALL(*handler, POSTJson("/api", request, getBridgeIp(), getBridgePort()))
116 .Times(1) 106 .Times(1)
117 - .WillOnce(Return(user_ret_suc)); 107 + .WillOnce(Return(successResponse));
118 108
119 finder = HueFinder(handler); 109 finder = HueFinder(handler);
120 bridges = finder.FindBridges(); 110 bridges = finder.FindBridges();
@@ -126,8 +116,7 @@ TEST_F(HueFinderTest, GetBridge) @@ -126,8 +116,7 @@ TEST_F(HueFinderTest, GetBridge)
126 EXPECT_EQ(test_bridge.getUsername(), getBridgeUsername()) << "Bridge username not matching"; 116 EXPECT_EQ(test_bridge.getUsername(), getBridgeUsername()) << "Bridge username not matching";
127 117
128 // Verify that username is correctly set in api requests 118 // Verify that username is correctly set in api requests
129 - nlohmann::json hue_bridge_state;  
130 - hue_bridge_state["lights"] = {}; 119 + nlohmann::json hue_bridge_state{{"lights", {}}};
131 EXPECT_CALL( 120 EXPECT_CALL(
132 *handler, GETJson("/api/" + getBridgeUsername(), nlohmann::json::object(), getBridgeIp(), getBridgePort())) 121 *handler, GETJson("/api/" + getBridgeUsername(), nlohmann::json::object(), getBridgeIp(), getBridgePort()))
133 .Times(1) 122 .Times(1)
@@ -176,51 +165,59 @@ TEST(Hue, requestUsername) @@ -176,51 +165,59 @@ TEST(Hue, requestUsername)
176 { 165 {
177 using namespace ::testing; 166 using namespace ::testing;
178 std::shared_ptr<MockHttpHandler> handler = std::make_shared<MockHttpHandler>(); 167 std::shared_ptr<MockHttpHandler> handler = std::make_shared<MockHttpHandler>();
179 - nlohmann::json request;  
180 - request["devicetype"] = "HuePlusPlus#User"; 168 + nlohmann::json request{{"devicetype", "HuePlusPlus#User"}};
181 169
182 - nlohmann::json user_ret_uns;  
183 - user_ret_uns = nlohmann::json::array();  
184 - user_ret_uns[0] = nlohmann::json::object();  
185 - user_ret_uns[0]["error"] = nlohmann::json::object();  
186 - user_ret_uns[0]["error"]["type"] = 101;  
187 - user_ret_uns[0]["error"]["address"] = "";  
188 - user_ret_uns[0]["error"]["description"] = "link button not pressed"; 170 + {
  171 + nlohmann::json errorResponse
  172 + = {{{"error", {{"type", 101}, {"address", ""}, {"description", "link button not pressed"}}}}};
189 173
190 - EXPECT_CALL(*handler, POSTJson("/api", request, getBridgeIp(), getBridgePort()))  
191 - .Times(AtLeast(1))  
192 - .WillRepeatedly(Return(user_ret_uns)); 174 + EXPECT_CALL(*handler, POSTJson("/api", request, getBridgeIp(), getBridgePort()))
  175 + .Times(AtLeast(1))
  176 + .WillRepeatedly(Return(errorResponse));
193 177
194 - Hue test_bridge(getBridgeIp(), getBridgePort(), "", handler); 178 + Hue test_bridge(getBridgeIp(), getBridgePort(), "", handler);
195 179
196 - test_bridge.requestUsername();  
197 - EXPECT_EQ(test_bridge.getUsername(), "") << "Bridge username not matching"; 180 + std::string username = test_bridge.requestUsername();
  181 + EXPECT_EQ(username, "") << "Returned username not matching";
  182 + EXPECT_EQ(test_bridge.getUsername(), "") << "Bridge username not matching";
  183 + }
198 184
199 - nlohmann::json user_ret_suc;  
200 - user_ret_suc = nlohmann::json::array();  
201 - user_ret_suc[0] = nlohmann::json::object();  
202 - user_ret_suc[0]["success"] = nlohmann::json::object();  
203 - user_ret_suc[0]["success"]["username"] = getBridgeUsername();  
204 - EXPECT_CALL(*handler, POSTJson("/api", request, getBridgeIp(), getBridgePort()))  
205 - .Times(1)  
206 - .WillRepeatedly(Return(user_ret_suc)); 185 + {
  186 + // Other error code causes exception
  187 + int otherError = 1;
  188 + nlohmann::json exceptionResponse
  189 + = {{{"error", {{"type", otherError}, {"address", ""}, {"description", "some error"}}}}};
  190 + Hue testBridge(getBridgeIp(), getBridgePort(), "", handler);
207 191
208 - test_bridge = Hue(getBridgeIp(), getBridgePort(), "", handler); 192 + EXPECT_CALL(*handler, POSTJson("/api", request, getBridgeIp(), getBridgePort()))
  193 + .WillOnce(Return(exceptionResponse));
209 194
210 - test_bridge.requestUsername(); 195 + EXPECT_THROW(testBridge.requestUsername(), HueAPIResponseException);
  196 + }
211 197
212 - EXPECT_EQ(test_bridge.getBridgeIP(), getBridgeIp()) << "Bridge IP not matching";  
213 - EXPECT_EQ(test_bridge.getUsername(), getBridgeUsername()) << "Bridge username not matching"; 198 + {
  199 + nlohmann::json successResponse = {{{"success", {{"username", getBridgeUsername()}}}}};
  200 + EXPECT_CALL(*handler, POSTJson("/api", request, getBridgeIp(), getBridgePort()))
  201 + .Times(1)
  202 + .WillRepeatedly(Return(successResponse));
214 203
215 - // Verify that username is correctly set in api requests  
216 - nlohmann::json hue_bridge_state;  
217 - hue_bridge_state["lights"] = {};  
218 - EXPECT_CALL(  
219 - *handler, GETJson("/api/" + getBridgeUsername(), nlohmann::json::object(), getBridgeIp(), getBridgePort()))  
220 - .Times(1)  
221 - .WillOnce(Return(hue_bridge_state)); 204 + Hue test_bridge(getBridgeIp(), getBridgePort(), "", handler);
222 205
223 - test_bridge.getAllLights(); 206 + std::string username = test_bridge.requestUsername();
  207 +
  208 + EXPECT_EQ(username, test_bridge.getUsername()) << "Returned username not matching";
  209 + EXPECT_EQ(test_bridge.getBridgeIP(), getBridgeIp()) << "Bridge IP not matching";
  210 + EXPECT_EQ(test_bridge.getUsername(), getBridgeUsername()) << "Bridge username not matching";
  211 +
  212 + // Verify that username is correctly set in api requests
  213 + nlohmann::json hue_bridge_state{{"lights", {}}};
  214 + EXPECT_CALL(
  215 + *handler, GETJson("/api/" + getBridgeUsername(), nlohmann::json::object(), getBridgeIp(), getBridgePort()))
  216 + .Times(1)
  217 + .WillOnce(Return(hue_bridge_state));
  218 +
  219 + test_bridge.getAllLights();
  220 + }
224 } 221 }
225 222
226 TEST(Hue, setIP) 223 TEST(Hue, setIP)