Commit bc52722a6d2a0b1ec82ebdfb67e91790c6138ecd
Committed by
Moritz Wirger
1 parent
ec625114
Add documentation and tests for BridgeConfig.
Showing
6 changed files
with
238 additions
and
10 deletions
include/hueplusplus/BridgeConfig.h
| ... | ... | @@ -27,6 +27,7 @@ |
| 27 | 27 | |
| 28 | 28 | namespace hueplusplus |
| 29 | 29 | { |
| 30 | +//! \brief API version consisting of major, minor and patch version | |
| 30 | 31 | struct Version |
| 31 | 32 | { |
| 32 | 33 | int major; |
| ... | ... | @@ -34,31 +35,62 @@ struct Version |
| 34 | 35 | int patch; |
| 35 | 36 | }; |
| 36 | 37 | |
| 38 | +//! \brief User that is whitelisted for Hue API usage | |
| 37 | 39 | struct WhitelistedUser |
| 38 | 40 | { |
| 41 | + //! \brief API username of the user | |
| 39 | 42 | std::string key; |
| 43 | + //! \brief Name provided on user creation | |
| 40 | 44 | std::string name; |
| 45 | + //! \brief Last time the user was used | |
| 41 | 46 | time::AbsoluteTime lastUsed; |
| 47 | + //! \brief Time the user was created | |
| 42 | 48 | time::AbsoluteTime created; |
| 43 | 49 | }; |
| 44 | 50 | |
| 51 | +//! \brief General bridge configuration properties. | |
| 45 | 52 | class BridgeConfig |
| 46 | 53 | { |
| 47 | 54 | public: |
| 55 | + //! \brief Construct BridgeConfig | |
| 48 | 56 | BridgeConfig(std::shared_ptr<APICache> baseCache, std::chrono::steady_clock::duration refreshDuration); |
| 49 | 57 | |
| 58 | + | |
| 59 | + //! \brief Refreshes internal cached state. | |
| 60 | + //! \throws std::system_error when system or socket operations fail | |
| 61 | + //! \throws HueException when response contained no body | |
| 62 | + //! \throws HueAPIResponseException when response contains an error | |
| 63 | + //! \throws nlohmann::json::parse_error when response could not be parsed | |
| 50 | 64 | void refresh(); |
| 51 | 65 | |
| 66 | + //! \brief Get the list of whitelisted users | |
| 67 | + //! \returns All users authorized for API access | |
| 52 | 68 | std::vector<WhitelistedUser> getWhitelistedUsers() const; |
| 69 | + //! \brief Remove user from the whitelist | |
| 70 | + //! \param userKey The API username of the user to remove | |
| 71 | + //! \throws std::system_error when system or socket operations fail | |
| 72 | + //! \throws HueException when response contained no body | |
| 73 | + //! \throws HueAPIResponseException when response contains an error | |
| 74 | + //! \throws nlohmann::json::parse_error when response could not be parsed | |
| 53 | 75 | void removeUser(const std::string& userKey); |
| 54 | 76 | |
| 77 | + //! \brief Get link button state | |
| 78 | + //! \returns true when link button was pressed in the last 30 seconds. | |
| 79 | + //! | |
| 80 | + //! Indicates whether new users can be added currently. | |
| 55 | 81 | bool getLinkButton() const; |
| 82 | + //! \brief Set the link button state to pressed | |
| 56 | 83 | void pressLinkButton(); |
| 57 | 84 | |
| 85 | + //! \brief Add the closest lamp to the network | |
| 58 | 86 | void touchLink(); |
| 59 | 87 | |
| 88 | + //! \brief Get bridge MAC address | |
| 60 | 89 | std::string getMACAddress() const; |
| 90 | + //! \brief Get current (of last refresh) UTC time of the bridge | |
| 61 | 91 | time::AbsoluteTime getUTCTime() const; |
| 92 | + //! \brief Get configured timezone for the bridge | |
| 93 | + //! \note For times not in UTC, the timezone of the program and the bridge are assumed to be identical. | |
| 62 | 94 | std::string getTimezone() const; |
| 63 | 95 | |
| 64 | 96 | protected: | ... | ... |
include/hueplusplus/Hue.h
| ... | ... | @@ -156,6 +156,10 @@ public: |
| 156 | 156 | //! Should only be called rarely, as a full refresh is costly and usually not necessary. |
| 157 | 157 | //! Instead refresh only the parts you are interested in or rely on periodic refreshes |
| 158 | 158 | //! that happen automatically when calling non-const methods. |
| 159 | + //! \throws std::system_error when system or socket operations fail | |
| 160 | + //! \throws HueException when response contained no body | |
| 161 | + //! \throws HueAPIResponseException when response contains an error | |
| 162 | + //! \throws nlohmann::json::parse_error when response could not be parsed | |
| 159 | 163 | void refresh(); |
| 160 | 164 | |
| 161 | 165 | //! \brief Function to get the ip address of the hue bridge | ... | ... |
src/Hue.cpp
| ... | ... | @@ -282,5 +282,6 @@ void Hue::setHttpHandler(std::shared_ptr<const IHttpHandler> handler) |
| 282 | 282 | scheduleList = CreateableResourceList<Schedule, int, CreateSchedule>(stateCache, "schedules", refreshDuration); |
| 283 | 283 | sceneList = CreateableResourceList<Scene, std::string, CreateScene>(stateCache, "scenes", refreshDuration); |
| 284 | 284 | bridgeConfig = BridgeConfig(stateCache, refreshDuration); |
| 285 | + stateCache->refresh(); | |
| 285 | 286 | } |
| 286 | 287 | } // namespace hueplusplus | ... | ... |
test/CMakeLists.txt
| ... | ... | @@ -48,7 +48,7 @@ set(TEST_SOURCES |
| 48 | 48 | test_SimpleColorHueStrategy.cpp |
| 49 | 49 | test_SimpleColorTemperatureStrategy.cpp |
| 50 | 50 | test_StateTransaction.cpp |
| 51 | - test_TimePattern.cpp "test_ColorUnits.cpp") | |
| 51 | + test_TimePattern.cpp "test_ColorUnits.cpp" "test_BridgeConfig.cpp") | |
| 52 | 52 | |
| 53 | 53 | set(HuePlusPlus_INCLUDE_DIR "${PROJECT_SOURCE_DIR}/include") |
| 54 | 54 | ... | ... |
test/test_BridgeConfig.cpp
0 โ 100644
| 1 | +/** | |
| 2 | + \file test_BridgeConfig.cpp | |
| 3 | + Copyright Notice\n | |
| 4 | + Copyright (C) 2020 Jan Rogall - developer\n | |
| 5 | + | |
| 6 | + This file is part of hueplusplus. | |
| 7 | + | |
| 8 | + hueplusplus is free software: you can redistribute it and/or modify | |
| 9 | + it under the terms of the GNU Lesser General Public License as published by | |
| 10 | + the Free Software Foundation, either version 3 of the License, or | |
| 11 | + (at your option) any later version. | |
| 12 | + | |
| 13 | + hueplusplus is distributed in the hope that it will be useful, | |
| 14 | + but WITHOUT ANY WARRANTY; without even the implied warranty of | |
| 15 | + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
| 16 | + GNU Lesser General Public License for more details. | |
| 17 | + | |
| 18 | + You should have received a copy of the GNU Lesser General Public License | |
| 19 | + along with hueplusplus. If not, see <http://www.gnu.org/licenses/>. | |
| 20 | +**/ | |
| 21 | + | |
| 22 | +#include <hueplusplus/BridgeConfig.h> | |
| 23 | + | |
| 24 | +#include <gtest/gtest.h> | |
| 25 | + | |
| 26 | +#include "testhelper.h" | |
| 27 | + | |
| 28 | +#include "mocks/mock_HttpHandler.h" | |
| 29 | + | |
| 30 | +using namespace hueplusplus; | |
| 31 | +using namespace testing; | |
| 32 | + | |
| 33 | +TEST(BridgeConfig, refresh) | |
| 34 | +{ | |
| 35 | + auto handler = std::make_shared<MockHttpHandler>(); | |
| 36 | + HueCommandAPI commands(getBridgeIp(), getBridgePort(), getBridgeUsername(), handler); | |
| 37 | + auto baseCache = std::make_shared<APICache>("", commands, std::chrono::steady_clock::duration::max()); | |
| 38 | + EXPECT_CALL( | |
| 39 | + *handler, GETJson("/api/" + getBridgeUsername(), nlohmann::json::object(), getBridgeIp(), getBridgePort())) | |
| 40 | + .WillOnce(Return(nlohmann::json::object())); | |
| 41 | + baseCache->refresh(); | |
| 42 | + BridgeConfig config(baseCache, std::chrono::steady_clock::duration::max()); | |
| 43 | + | |
| 44 | + EXPECT_CALL(*handler, | |
| 45 | + GETJson("/api/" + getBridgeUsername() + "/config", nlohmann::json::object(), getBridgeIp(), getBridgePort())) | |
| 46 | + .WillOnce(Return(nlohmann::json::object())); | |
| 47 | + config.refresh(); | |
| 48 | +} | |
| 49 | + | |
| 50 | +TEST(BridgeConfig, getWhitelistedUsers) | |
| 51 | +{ | |
| 52 | + const nlohmann::json state {{"config", | |
| 53 | + {{"whitelist", | |
| 54 | + {{"abcd", | |
| 55 | + {{"name", "User A"}, {"last use date", "2020-04-01T10:00:04"}, | |
| 56 | + {"create date", "2020-01-01T12:00:00"}}}, | |
| 57 | + {"cdef", | |
| 58 | + {{"name", "User B"}, {"last use date", "2020-03-05T14:00:00"}, | |
| 59 | + {"create date", "2020-02-01T02:03:40"}}}}}}}}; | |
| 60 | + auto handler = std::make_shared<MockHttpHandler>(); | |
| 61 | + HueCommandAPI commands(getBridgeIp(), getBridgePort(), getBridgeUsername(), handler); | |
| 62 | + auto baseCache = std::make_shared<APICache>("", commands, std::chrono::steady_clock::duration::max()); | |
| 63 | + EXPECT_CALL( | |
| 64 | + *handler, GETJson("/api/" + getBridgeUsername(), nlohmann::json::object(), getBridgeIp(), getBridgePort())) | |
| 65 | + .WillOnce(Return(state)); | |
| 66 | + baseCache->refresh(); | |
| 67 | + BridgeConfig config(baseCache, std::chrono::steady_clock::duration::max()); | |
| 68 | + | |
| 69 | + std::vector<WhitelistedUser> users = config.getWhitelistedUsers(); | |
| 70 | + EXPECT_THAT(users, | |
| 71 | + UnorderedElementsAre(Truly([](const WhitelistedUser& u) { return u.key == "abcd" && u.name == "User A"; }), | |
| 72 | + Truly([](const WhitelistedUser& u) { return u.key == "cdef" && u.name == "User B"; }))); | |
| 73 | +} | |
| 74 | + | |
| 75 | +TEST(BridgeConfig, removeUser) | |
| 76 | +{ | |
| 77 | + auto handler = std::make_shared<MockHttpHandler>(); | |
| 78 | + HueCommandAPI commands(getBridgeIp(), getBridgePort(), getBridgeUsername(), handler); | |
| 79 | + auto baseCache = std::make_shared<APICache>("", commands, std::chrono::steady_clock::duration::max()); | |
| 80 | + EXPECT_CALL( | |
| 81 | + *handler, GETJson("/api/" + getBridgeUsername(), nlohmann::json::object(), getBridgeIp(), getBridgePort())) | |
| 82 | + .WillOnce(Return(nlohmann::json::object())); | |
| 83 | + baseCache->refresh(); | |
| 84 | + BridgeConfig config(baseCache, std::chrono::steady_clock::duration::max()); | |
| 85 | + | |
| 86 | + const std::string userKey = "abcd"; | |
| 87 | + EXPECT_CALL(*handler, | |
| 88 | + DELETEJson("/api/" + getBridgeUsername() + "/config/whitelist/" + userKey, nlohmann::json::object(), | |
| 89 | + getBridgeIp(), getBridgePort())) | |
| 90 | + .WillOnce(Return(nlohmann::json {"/config/whitelist/" + userKey + " deleted"})); | |
| 91 | + config.removeUser(userKey); | |
| 92 | +} | |
| 93 | + | |
| 94 | +TEST(BridgeConfig, getLinkButton) | |
| 95 | +{ | |
| 96 | + const nlohmann::json state {{"config", {{"linkbutton", true}}}}; | |
| 97 | + auto handler = std::make_shared<MockHttpHandler>(); | |
| 98 | + HueCommandAPI commands(getBridgeIp(), getBridgePort(), getBridgeUsername(), handler); | |
| 99 | + auto baseCache = std::make_shared<APICache>("", commands, std::chrono::steady_clock::duration::max()); | |
| 100 | + EXPECT_CALL( | |
| 101 | + *handler, GETJson("/api/" + getBridgeUsername(), nlohmann::json::object(), getBridgeIp(), getBridgePort())) | |
| 102 | + .WillOnce(Return(state)); | |
| 103 | + baseCache->refresh(); | |
| 104 | + BridgeConfig config(baseCache, std::chrono::steady_clock::duration::max()); | |
| 105 | + | |
| 106 | + EXPECT_TRUE(config.getLinkButton()); | |
| 107 | + EXPECT_CALL(*handler, | |
| 108 | + GETJson("/api/" + getBridgeUsername() + "/config", nlohmann::json::object(), getBridgeIp(), getBridgePort())) | |
| 109 | + .WillOnce(Return(nlohmann::json {{"linkbutton", false}})); | |
| 110 | + config.refresh(); | |
| 111 | + EXPECT_FALSE(config.getLinkButton()); | |
| 112 | +} | |
| 113 | + | |
| 114 | +TEST(BridgeConfig, pressLinkButton) | |
| 115 | +{ | |
| 116 | + auto handler = std::make_shared<MockHttpHandler>(); | |
| 117 | + HueCommandAPI commands(getBridgeIp(), getBridgePort(), getBridgeUsername(), handler); | |
| 118 | + auto baseCache = std::make_shared<APICache>("", commands, std::chrono::steady_clock::duration::max()); | |
| 119 | + EXPECT_CALL( | |
| 120 | + *handler, GETJson("/api/" + getBridgeUsername(), nlohmann::json::object(), getBridgeIp(), getBridgePort())) | |
| 121 | + .WillOnce(Return(nlohmann::json::object())); | |
| 122 | + baseCache->refresh(); | |
| 123 | + BridgeConfig config(baseCache, std::chrono::steady_clock::duration::max()); | |
| 124 | + | |
| 125 | + EXPECT_CALL(*handler, | |
| 126 | + PUTJson("/api/" + getBridgeUsername() + "/config", nlohmann::json {{"linkbutton", true}}, getBridgeIp(), | |
| 127 | + getBridgePort())) | |
| 128 | + .WillOnce(Return(nlohmann::json {{{"success", {{"/config/linkbutton", true}}}}})); | |
| 129 | + config.pressLinkButton(); | |
| 130 | +} | |
| 131 | + | |
| 132 | +TEST(BridgeConfig, touchLink) | |
| 133 | +{ | |
| 134 | + auto handler = std::make_shared<MockHttpHandler>(); | |
| 135 | + HueCommandAPI commands(getBridgeIp(), getBridgePort(), getBridgeUsername(), handler); | |
| 136 | + auto baseCache = std::make_shared<APICache>("", commands, std::chrono::steady_clock::duration::max()); | |
| 137 | + EXPECT_CALL( | |
| 138 | + *handler, GETJson("/api/" + getBridgeUsername(), nlohmann::json::object(), getBridgeIp(), getBridgePort())) | |
| 139 | + .WillOnce(Return(nlohmann::json::object())); | |
| 140 | + baseCache->refresh(); | |
| 141 | + BridgeConfig config(baseCache, std::chrono::steady_clock::duration::max()); | |
| 142 | + | |
| 143 | + EXPECT_CALL(*handler, | |
| 144 | + PUTJson("/api/" + getBridgeUsername() + "/config", nlohmann::json {{"touchlink", true}}, getBridgeIp(), | |
| 145 | + getBridgePort())) | |
| 146 | + .WillOnce(Return(nlohmann::json {{{"success", {{"/config/touchlink", true}}}}})); | |
| 147 | + config.touchLink(); | |
| 148 | +} | |
| 149 | + | |
| 150 | +TEST(BridgeConfig, getMACAddress) | |
| 151 | +{ | |
| 152 | + const nlohmann::json state {{"config", {{"mac", getBridgeMac()}}}}; | |
| 153 | + auto handler = std::make_shared<MockHttpHandler>(); | |
| 154 | + HueCommandAPI commands(getBridgeIp(), getBridgePort(), getBridgeUsername(), handler); | |
| 155 | + auto baseCache = std::make_shared<APICache>("", commands, std::chrono::steady_clock::duration::max()); | |
| 156 | + EXPECT_CALL( | |
| 157 | + *handler, GETJson("/api/" + getBridgeUsername(), nlohmann::json::object(), getBridgeIp(), getBridgePort())) | |
| 158 | + .WillOnce(Return(state)); | |
| 159 | + baseCache->refresh(); | |
| 160 | + BridgeConfig config(baseCache, std::chrono::steady_clock::duration::max()); | |
| 161 | + | |
| 162 | + EXPECT_EQ(getBridgeMac(), config.getMACAddress()); | |
| 163 | +} | |
| 164 | + | |
| 165 | +TEST(BridgeConfig, getUTCTime) | |
| 166 | +{ | |
| 167 | + const std::string utc = "2020-06-01T10:00:00"; | |
| 168 | + const nlohmann::json state {{"config", {{"UTC", utc}}}}; | |
| 169 | + auto handler = std::make_shared<MockHttpHandler>(); | |
| 170 | + HueCommandAPI commands(getBridgeIp(), getBridgePort(), getBridgeUsername(), handler); | |
| 171 | + auto baseCache = std::make_shared<APICache>("", commands, std::chrono::steady_clock::duration::max()); | |
| 172 | + EXPECT_CALL( | |
| 173 | + *handler, GETJson("/api/" + getBridgeUsername(), nlohmann::json::object(), getBridgeIp(), getBridgePort())) | |
| 174 | + .WillOnce(Return(state)); | |
| 175 | + baseCache->refresh(); | |
| 176 | + BridgeConfig config(baseCache, std::chrono::steady_clock::duration::max()); | |
| 177 | + | |
| 178 | + EXPECT_EQ(time::AbsoluteTime::parseUTC(utc).getBaseTime(), config.getUTCTime().getBaseTime()); | |
| 179 | +} | |
| 180 | + | |
| 181 | +TEST(BridgeConfig, getTimezone) | |
| 182 | +{ | |
| 183 | + const std::string timezone = "ab"; | |
| 184 | + const nlohmann::json state {{"config", {{"timezone", timezone}}}}; | |
| 185 | + auto handler = std::make_shared<MockHttpHandler>(); | |
| 186 | + HueCommandAPI commands(getBridgeIp(), getBridgePort(), getBridgeUsername(), handler); | |
| 187 | + auto baseCache = std::make_shared<APICache>("", commands, std::chrono::steady_clock::duration::max()); | |
| 188 | + EXPECT_CALL( | |
| 189 | + *handler, GETJson("/api/" + getBridgeUsername(), nlohmann::json::object(), getBridgeIp(), getBridgePort())) | |
| 190 | + .WillOnce(Return(state)); | |
| 191 | + baseCache->refresh(); | |
| 192 | + BridgeConfig config(baseCache, std::chrono::steady_clock::duration::max()); | |
| 193 | + | |
| 194 | + EXPECT_EQ(timezone, config.getTimezone()); | |
| 195 | +} | ... | ... |
test/test_Hue.cpp
| ... | ... | @@ -208,20 +208,16 @@ TEST(Hue, requestUsername) |
| 208 | 208 | |
| 209 | 209 | Hue test_bridge(getBridgeIp(), getBridgePort(), "", handler); |
| 210 | 210 | |
| 211 | - std::string username = test_bridge.requestUsername(); | |
| 212 | - | |
| 213 | - EXPECT_EQ(username, test_bridge.getUsername()) << "Returned username not matching"; | |
| 214 | - EXPECT_EQ(test_bridge.getBridgeIP(), getBridgeIp()) << "Bridge IP not matching"; | |
| 215 | - EXPECT_EQ(test_bridge.getUsername(), getBridgeUsername()) << "Bridge username not matching"; | |
| 216 | - | |
| 217 | - // Verify that username is correctly set in api requests | |
| 218 | - nlohmann::json hue_bridge_state {{"lights", {}}}; | |
| 211 | + nlohmann::json hue_bridge_state{ {"lights", {}} }; | |
| 219 | 212 | EXPECT_CALL( |
| 220 | 213 | *handler, GETJson("/api/" + getBridgeUsername(), nlohmann::json::object(), getBridgeIp(), getBridgePort())) |
| 221 | 214 | .Times(1) |
| 222 | 215 | .WillOnce(Return(hue_bridge_state)); |
| 216 | + std::string username = test_bridge.requestUsername(); | |
| 223 | 217 | |
| 224 | - test_bridge.lights().getAll(); | |
| 218 | + EXPECT_EQ(username, test_bridge.getUsername()) << "Returned username not matching"; | |
| 219 | + EXPECT_EQ(test_bridge.getBridgeIP(), getBridgeIp()) << "Bridge IP not matching"; | |
| 220 | + EXPECT_EQ(test_bridge.getUsername(), getBridgeUsername()) << "Bridge username not matching"; | |
| 225 | 221 | } |
| 226 | 222 | } |
| 227 | 223 | ... | ... |