1 // Copyright 2015 The Weave Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "src/base_api_handler.h"
6 
7 #include <base/strings/string_number_conversions.h>
8 #include <base/time/default_clock.h>
9 #include <base/values.h>
10 #include <gtest/gtest.h>
11 #include <weave/provider/test/fake_task_runner.h>
12 #include <weave/provider/test/mock_config_store.h>
13 #include <weave/provider/test/mock_http_client.h>
14 #include <weave/test/mock_device.h>
15 #include <weave/test/unittest_utils.h>
16 
17 #include "src/component_manager_impl.h"
18 #include "src/config.h"
19 #include "src/device_registration_info.h"
20 
21 using testing::_;
22 using testing::AnyOf;
23 using testing::Eq;
24 using testing::Invoke;
25 using testing::Return;
26 using testing::ReturnRef;
27 using testing::StrictMock;
28 
29 namespace weave {
30 
31 class BaseApiHandlerTest : public ::testing::Test {
32  protected:
SetUp()33   void SetUp() override {
34     EXPECT_CALL(device_, AddTraitDefinitionsFromJson(_))
35         .WillRepeatedly(Invoke([this](const std::string& json) {
36           EXPECT_TRUE(component_manager_.LoadTraits(json, nullptr));
37         }));
38     EXPECT_CALL(device_, SetStateProperties(_, _, _))
39         .WillRepeatedly(
40             Invoke(&component_manager_, &ComponentManager::SetStateProperties));
41     EXPECT_CALL(device_, SetStateProperty(_, _, _, _))
42         .WillRepeatedly(
43             Invoke(&component_manager_, &ComponentManager::SetStateProperty));
44     EXPECT_CALL(device_, AddComponent(_, _, _))
45         .WillRepeatedly(Invoke([this](const std::string& name,
46                                       const std::vector<std::string>& traits,
47                                       ErrorPtr* error) {
48           return component_manager_.AddComponent("", name, traits, error);
49         }));
50 
51     EXPECT_CALL(device_,
52                 AddCommandHandler(_, AnyOf("base.updateBaseConfiguration",
53                                            "base.updateDeviceInfo"),
54                                   _))
55         .WillRepeatedly(
56             Invoke(&component_manager_, &ComponentManager::AddCommandHandler));
57 
58     dev_reg_.reset(new DeviceRegistrationInfo(&config_, &component_manager_,
59                                               nullptr, &http_client_, nullptr,
60                                               nullptr));
61 
62     EXPECT_CALL(device_, GetSettings())
63         .WillRepeatedly(ReturnRef(dev_reg_->GetSettings()));
64 
65     handler_.reset(new BaseApiHandler{dev_reg_.get(), &device_});
66   }
67 
AddCommand(const std::string & command)68   void AddCommand(const std::string& command) {
69     std::string id;
70     auto command_instance = component_manager_.ParseCommandInstance(
71         *test::CreateDictionaryValue(command.c_str()), Command::Origin::kLocal,
72         UserRole::kOwner, &id, nullptr);
73     ASSERT_NE(nullptr, command_instance.get());
74     component_manager_.AddCommand(std::move(command_instance));
75     EXPECT_EQ(Command::State::kDone,
76               component_manager_.FindCommand(id)->GetState());
77   }
78 
GetBaseState()79   std::unique_ptr<base::DictionaryValue> GetBaseState() {
80     std::unique_ptr<base::DictionaryValue> state;
81     std::string path = component_manager_.FindComponentWithTrait("base");
82     EXPECT_FALSE(path.empty());
83     const auto* component = component_manager_.FindComponent(path, nullptr);
84     CHECK(component);
85     const base::DictionaryValue* base_state = nullptr;
86     if (component->GetDictionary("state.base", &base_state))
87       state.reset(base_state->DeepCopy());
88     else
89       state.reset(new base::DictionaryValue);
90     return state;
91   }
92 
93   provider::test::MockConfigStore config_store_;
94   Config config_{&config_store_};
95   StrictMock<provider::test::MockHttpClient> http_client_;
96   std::unique_ptr<DeviceRegistrationInfo> dev_reg_;
97   StrictMock<provider::test::FakeTaskRunner> task_runner_;
98   ComponentManagerImpl component_manager_{&task_runner_};
99   std::unique_ptr<BaseApiHandler> handler_;
100   StrictMock<test::MockDevice> device_;
101 };
102 
TEST_F(BaseApiHandlerTest,Initialization)103 TEST_F(BaseApiHandlerTest, Initialization) {
104   const base::DictionaryValue* trait = nullptr;
105   ASSERT_TRUE(component_manager_.GetTraits().GetDictionary("base", &trait));
106 
107   auto expected = R"({
108     "commands": {
109       "updateBaseConfiguration": {
110         "minimalRole": "manager",
111         "parameters": {
112           "localAnonymousAccessMaxRole": {
113             "enum": [ "none", "viewer", "user" ],
114             "type": "string"
115           },
116           "localDiscoveryEnabled": {
117             "type": "boolean"
118           },
119           "localPairingEnabled": {
120             "type": "boolean"
121           }
122         }
123       },
124       "updateDeviceInfo": {
125         "minimalRole": "manager",
126         "parameters": {
127           "description": {
128             "type": "string"
129           },
130           "location": {
131             "type": "string"
132           },
133           "name": {
134             "type": "string"
135           }
136         }
137       },
138       "reboot": {
139         "minimalRole": "user",
140         "parameters": {},
141         "errors": ["notEnoughBattery"]
142       },
143       "identify": {
144         "minimalRole": "user",
145         "parameters": {}
146       }
147     },
148     "state": {
149       "firmwareVersion": {
150         "type": "string",
151         "isRequired": true
152       },
153       "localDiscoveryEnabled": {
154         "type": "boolean",
155         "isRequired": true
156       },
157       "localAnonymousAccessMaxRole": {
158         "type": "string",
159         "enum": [ "none", "viewer", "user" ],
160         "isRequired": true
161       },
162       "localPairingEnabled": {
163         "type": "boolean",
164         "isRequired": true
165       },
166       "connectionStatus": {
167         "type": "string"
168       },
169       "network": {
170         "type": "object",
171         "additionalProperties": false,
172         "properties": {
173           "name": { "type": "string" }
174         }
175       }
176     }
177   })";
178   EXPECT_JSON_EQ(expected, *trait);
179 }
180 
TEST_F(BaseApiHandlerTest,UpdateBaseConfiguration)181 TEST_F(BaseApiHandlerTest, UpdateBaseConfiguration) {
182   const Settings& settings = dev_reg_->GetSettings();
183 
184   AddCommand(R"({
185     'name' : 'base.updateBaseConfiguration',
186     'component': 'base',
187     'parameters': {
188       'localDiscoveryEnabled': false,
189       'localAnonymousAccessMaxRole': 'none',
190       'localPairingEnabled': false
191     }
192   })");
193   EXPECT_EQ(AuthScope::kNone, settings.local_anonymous_access_role);
194   EXPECT_FALSE(settings.local_discovery_enabled);
195   EXPECT_FALSE(settings.local_pairing_enabled);
196 
197   auto expected = R"({
198     'firmwareVersion': 'TEST_FIRMWARE',
199     'localAnonymousAccessMaxRole': 'none',
200     'localDiscoveryEnabled': false,
201     'localPairingEnabled': false
202   })";
203   EXPECT_JSON_EQ(expected, *GetBaseState());
204 
205   AddCommand(R"({
206     'name' : 'base.updateBaseConfiguration',
207     'component': 'base',
208     'parameters': {
209       'localDiscoveryEnabled': true,
210       'localAnonymousAccessMaxRole': 'user',
211       'localPairingEnabled': true
212     }
213   })");
214   EXPECT_EQ(AuthScope::kUser, settings.local_anonymous_access_role);
215   EXPECT_TRUE(settings.local_discovery_enabled);
216   EXPECT_TRUE(settings.local_pairing_enabled);
217   expected = R"({
218     'firmwareVersion': 'TEST_FIRMWARE',
219     'localAnonymousAccessMaxRole': 'user',
220     'localDiscoveryEnabled': true,
221     'localPairingEnabled': true
222   })";
223   EXPECT_JSON_EQ(expected, *GetBaseState());
224 
225   {
226     Config::Transaction change{dev_reg_->GetMutableConfig()};
227     change.set_local_anonymous_access_role(AuthScope::kViewer);
228   }
229   expected = R"({
230     'firmwareVersion': 'TEST_FIRMWARE',
231     'localAnonymousAccessMaxRole': 'viewer',
232     'localDiscoveryEnabled': true,
233     'localPairingEnabled': true
234   })";
235   EXPECT_JSON_EQ(expected, *GetBaseState());
236 }
237 
TEST_F(BaseApiHandlerTest,UpdateDeviceInfo)238 TEST_F(BaseApiHandlerTest, UpdateDeviceInfo) {
239   AddCommand(R"({
240     'name' : 'base.updateDeviceInfo',
241     'component': 'base',
242     'parameters': {
243       'name': 'testName',
244       'description': 'testDescription',
245       'location': 'testLocation'
246     }
247   })");
248 
249   const Settings& config = dev_reg_->GetSettings();
250   EXPECT_EQ("testName", config.name);
251   EXPECT_EQ("testDescription", config.description);
252   EXPECT_EQ("testLocation", config.location);
253 
254   AddCommand(R"({
255     'name' : 'base.updateDeviceInfo',
256     'component': 'base',
257     'parameters': {
258       'location': 'newLocation'
259     }
260   })");
261 
262   EXPECT_EQ("testName", config.name);
263   EXPECT_EQ("testDescription", config.description);
264   EXPECT_EQ("newLocation", config.location);
265 }
266 
267 }  // namespace weave
268