OLD | NEW |
| (Empty) |
1 // Copyright 2015 The Chromium 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 "components/copresence/rpc/rpc_handler.h" | |
6 | |
7 #include <map> | |
8 #include <string> | |
9 #include <utility> | |
10 #include <vector> | |
11 | |
12 #include "base/bind.h" | |
13 #include "base/bind_helpers.h" | |
14 #include "base/memory/ptr_util.h" | |
15 #include "base/memory/scoped_vector.h" | |
16 #include "base/message_loop/message_loop.h" | |
17 #include "components/audio_modem/public/modem.h" | |
18 #include "components/audio_modem/test/stub_whispernet_client.h" | |
19 #include "components/copresence/copresence_state_impl.h" | |
20 #include "components/copresence/handlers/directive_handler.h" | |
21 #include "components/copresence/proto/data.pb.h" | |
22 #include "components/copresence/proto/enums.pb.h" | |
23 #include "components/copresence/proto/rpcs.pb.h" | |
24 #include "components/copresence/test/fake_directive_handler.h" | |
25 #include "net/http/http_status_code.h" | |
26 #include "testing/gmock/include/gmock/gmock.h" | |
27 | |
28 using google::protobuf::MessageLite; | |
29 using google::protobuf::RepeatedPtrField; | |
30 | |
31 using testing::ElementsAre; | |
32 using testing::Property; | |
33 using testing::SizeIs; | |
34 | |
35 using audio_modem::AudioToken; | |
36 using audio_modem::WhispernetClient; | |
37 | |
38 namespace copresence { | |
39 | |
40 namespace { | |
41 | |
42 const char kChromeVersion[] = "Chrome Version String"; | |
43 | |
44 void IgnoreMessages( | |
45 const RepeatedPtrField<SubscribedMessage>& /* messages */) {} | |
46 | |
47 } // namespace | |
48 | |
49 class RpcHandlerTest : public testing::Test, public CopresenceDelegate { | |
50 public: | |
51 RpcHandlerTest() | |
52 : whispernet_client_(new audio_modem::StubWhispernetClient), | |
53 // TODO(ckehoe): Use a FakeCopresenceState here | |
54 // and test that it gets called correctly. | |
55 rpc_handler_(this, | |
56 &directive_handler_, | |
57 nullptr, | |
58 nullptr, | |
59 base::Bind(&IgnoreMessages), | |
60 base::Bind(&RpcHandlerTest::CaptureHttpPost, | |
61 base::Unretained(this))), | |
62 status_(SUCCESS) {} | |
63 | |
64 // CopresenceDelegate implementation | |
65 | |
66 void HandleMessages(const std::string& /* app_id */, | |
67 const std::string& subscription_id, | |
68 const std::vector<Message>& messages) override { | |
69 NOTREACHED(); | |
70 } | |
71 | |
72 void HandleStatusUpdate(CopresenceStatus /* status */) override { | |
73 NOTREACHED(); | |
74 } | |
75 | |
76 net::URLRequestContextGetter* GetRequestContext() const override { | |
77 return nullptr; | |
78 } | |
79 | |
80 std::string GetPlatformVersionString() const override { | |
81 return kChromeVersion; | |
82 } | |
83 | |
84 std::string GetAPIKey(const std::string& app_id) const override { | |
85 return app_id + " API Key"; | |
86 } | |
87 | |
88 WhispernetClient* GetWhispernetClient() override { | |
89 return whispernet_client_.get(); | |
90 } | |
91 | |
92 // TODO(ckehoe): Add GCM tests. | |
93 gcm::GCMDriver* GetGCMDriver() override { | |
94 return nullptr; | |
95 } | |
96 | |
97 std::string GetDeviceId(bool authenticated) override { | |
98 return device_id_by_auth_state_[authenticated]; | |
99 } | |
100 | |
101 void SaveDeviceId(bool authenticated, const std::string& device_id) override { | |
102 device_id_by_auth_state_[authenticated] = device_id; | |
103 } | |
104 | |
105 protected: | |
106 | |
107 // Send test input to RpcHandler | |
108 | |
109 void RegisterDevice(bool authenticated) { | |
110 rpc_handler_.RegisterDevice(authenticated); | |
111 } | |
112 | |
113 void SendRegisterResponse(bool authenticated, | |
114 const std::string& device_id) { | |
115 RegisterDeviceResponse response; | |
116 response.set_registered_device_id(device_id); | |
117 response.mutable_header()->mutable_status()->set_code(OK); | |
118 | |
119 std::string serialized_response; | |
120 response.SerializeToString(&serialized_response); | |
121 rpc_handler_.RegisterResponseHandler( | |
122 authenticated, false, nullptr, net::HTTP_OK, serialized_response); | |
123 } | |
124 | |
125 void SendReport(std::unique_ptr<ReportRequest> request, | |
126 const std::string& app_id, | |
127 const std::string& auth_token) { | |
128 rpc_handler_.SendReportRequest(std::move(request), app_id, auth_token, | |
129 StatusCallback()); | |
130 } | |
131 | |
132 void SendReportResponse(int status_code, | |
133 std::unique_ptr<ReportResponse> response) { | |
134 response->mutable_header()->mutable_status()->set_code(OK); | |
135 | |
136 std::string serialized_response; | |
137 response->SerializeToString(&serialized_response); | |
138 rpc_handler_.ReportResponseHandler( | |
139 base::Bind(&RpcHandlerTest::CaptureStatus, base::Unretained(this)), | |
140 nullptr, | |
141 status_code, | |
142 serialized_response); | |
143 } | |
144 | |
145 // Read and modify RpcHandler state | |
146 | |
147 void SetAuthToken(const std::string& auth_token) { | |
148 rpc_handler_.auth_token_ = auth_token; | |
149 } | |
150 | |
151 const ScopedVector<RpcHandler::PendingRequest>& request_queue() const { | |
152 return rpc_handler_.pending_requests_queue_; | |
153 } | |
154 | |
155 void AddInvalidToken(const std::string& token) { | |
156 rpc_handler_.invalid_audio_token_cache_.Add(token, true); | |
157 } | |
158 | |
159 bool TokenIsInvalid(const std::string& token) { | |
160 return rpc_handler_.invalid_audio_token_cache_.HasKey(token); | |
161 } | |
162 | |
163 // For rpc_handler_.invalid_audio_token_cache_ | |
164 base::MessageLoop message_loop_; | |
165 | |
166 std::unique_ptr<WhispernetClient> whispernet_client_; | |
167 FakeDirectiveHandler directive_handler_; | |
168 RpcHandler rpc_handler_; | |
169 | |
170 std::map<bool, std::string> device_id_by_auth_state_; | |
171 | |
172 CopresenceStatus status_; | |
173 std::string rpc_name_; | |
174 std::string api_key_; | |
175 std::string auth_token_; | |
176 ScopedVector<MessageLite> request_protos_; | |
177 | |
178 private: | |
179 void CaptureHttpPost( | |
180 net::URLRequestContextGetter* url_context_getter, | |
181 const std::string& rpc_name, | |
182 const std::string& api_key, | |
183 const std::string& auth_token, | |
184 std::unique_ptr<MessageLite> request_proto, | |
185 const RpcHandler::PostCleanupCallback& response_callback) { | |
186 rpc_name_ = rpc_name; | |
187 api_key_ = api_key; | |
188 auth_token_ = auth_token; | |
189 request_protos_.push_back(request_proto.release()); | |
190 } | |
191 | |
192 void CaptureStatus(CopresenceStatus status) { | |
193 status_ = status; | |
194 } | |
195 }; | |
196 | |
197 TEST_F(RpcHandlerTest, RegisterDevice) { | |
198 RegisterDevice(false); | |
199 EXPECT_THAT(request_protos_, SizeIs(1)); | |
200 const RegisterDeviceRequest* registration = | |
201 static_cast<RegisterDeviceRequest*>(request_protos_[0]); | |
202 EXPECT_EQ(CHROME, registration->device_identifiers().registrant().type()); | |
203 | |
204 SetAuthToken("Register auth"); | |
205 RegisterDevice(true); | |
206 EXPECT_THAT(request_protos_, SizeIs(2)); | |
207 registration = static_cast<RegisterDeviceRequest*>(request_protos_[1]); | |
208 EXPECT_FALSE(registration->has_device_identifiers()); | |
209 } | |
210 | |
211 TEST_F(RpcHandlerTest, RequestQueuing) { | |
212 // Send a report. | |
213 ReportRequest* report = new ReportRequest; | |
214 report->mutable_manage_messages_request()->add_id_to_unpublish("unpublish"); | |
215 SendReport(base::WrapUnique(report), "Q App ID", "Q Auth Token"); | |
216 EXPECT_THAT(request_queue(), SizeIs(1)); | |
217 EXPECT_TRUE(request_queue()[0]->authenticated); | |
218 | |
219 // Check for registration request. | |
220 EXPECT_THAT(request_protos_, SizeIs(1)); | |
221 const RegisterDeviceRequest* registration = | |
222 static_cast<RegisterDeviceRequest*>(request_protos_[0]); | |
223 EXPECT_FALSE(registration->device_identifiers().has_registrant()); | |
224 EXPECT_EQ("Q Auth Token", auth_token_); | |
225 | |
226 // Send a second report. | |
227 report = new ReportRequest; | |
228 report->mutable_manage_subscriptions_request()->add_id_to_unsubscribe( | |
229 "unsubscribe"); | |
230 SendReport(base::WrapUnique(report), "Q App ID", "Q Auth Token"); | |
231 EXPECT_THAT(request_protos_, SizeIs(1)); | |
232 EXPECT_THAT(request_queue(), SizeIs(2)); | |
233 EXPECT_TRUE(request_queue()[1]->authenticated); | |
234 | |
235 // Send an anonymous report. | |
236 report = new ReportRequest; | |
237 report->mutable_update_signals_request()->add_token_observation() | |
238 ->set_token_id("Q Audio Token"); | |
239 SendReport(base::WrapUnique(report), "Q App ID", ""); | |
240 EXPECT_THAT(request_queue(), SizeIs(3)); | |
241 EXPECT_FALSE(request_queue()[2]->authenticated); | |
242 | |
243 // Check for another registration request. | |
244 EXPECT_THAT(request_protos_, SizeIs(2)); | |
245 registration = static_cast<RegisterDeviceRequest*>(request_protos_[1]); | |
246 EXPECT_TRUE(registration->device_identifiers().has_registrant()); | |
247 EXPECT_EQ("", auth_token_); | |
248 | |
249 // Respond to the first registration. | |
250 SendRegisterResponse(true, "Q Auth Device ID"); | |
251 EXPECT_EQ("Q Auth Device ID", device_id_by_auth_state_[true]); | |
252 | |
253 // Check that queued reports are sent. | |
254 EXPECT_THAT(request_protos_, SizeIs(4)); | |
255 EXPECT_THAT(request_queue(), SizeIs(1)); | |
256 EXPECT_THAT(directive_handler_.removed_directives(), | |
257 ElementsAre("unpublish", "unsubscribe")); | |
258 report = static_cast<ReportRequest*>(request_protos_[2]); | |
259 EXPECT_EQ("unpublish", report->manage_messages_request().id_to_unpublish(0)); | |
260 report = static_cast<ReportRequest*>(request_protos_[3]); | |
261 EXPECT_EQ("unsubscribe", | |
262 report->manage_subscriptions_request().id_to_unsubscribe(0)); | |
263 | |
264 // Respond to the second registration. | |
265 SendRegisterResponse(false, "Q Anonymous Device ID"); | |
266 EXPECT_EQ("Q Anonymous Device ID", device_id_by_auth_state_[false]); | |
267 | |
268 // Check for last report. | |
269 EXPECT_THAT(request_protos_, SizeIs(5)); | |
270 EXPECT_TRUE(request_queue().empty()); | |
271 report = static_cast<ReportRequest*>(request_protos_[4]); | |
272 EXPECT_EQ("Q Audio Token", | |
273 report->update_signals_request().token_observation(0).token_id()); | |
274 } | |
275 | |
276 TEST_F(RpcHandlerTest, CreateRequestHeader) { | |
277 device_id_by_auth_state_[true] = "CreateRequestHeader Device ID"; | |
278 SendReport(base::WrapUnique(new ReportRequest), "CreateRequestHeader App", | |
279 "CreateRequestHeader Auth Token"); | |
280 | |
281 EXPECT_EQ(RpcHandler::kReportRequestRpcName, rpc_name_); | |
282 EXPECT_EQ("CreateRequestHeader App API Key", api_key_); | |
283 EXPECT_EQ("CreateRequestHeader Auth Token", auth_token_); | |
284 const ReportRequest* report = static_cast<ReportRequest*>(request_protos_[0]); | |
285 EXPECT_EQ(kChromeVersion, | |
286 report->header().framework_version().version_name()); | |
287 EXPECT_EQ("CreateRequestHeader App", | |
288 report->header().client_version().client()); | |
289 EXPECT_EQ("CreateRequestHeader Device ID", | |
290 report->header().registered_device_id()); | |
291 EXPECT_EQ(CHROME_PLATFORM_TYPE, | |
292 report->header().device_fingerprint().type()); | |
293 } | |
294 | |
295 TEST_F(RpcHandlerTest, ReportTokens) { | |
296 std::vector<AudioToken> test_tokens; | |
297 test_tokens.push_back(AudioToken("token 1", false)); | |
298 test_tokens.push_back(AudioToken("token 2", false)); | |
299 test_tokens.push_back(AudioToken("token 3", true)); | |
300 AddInvalidToken("token 2"); | |
301 | |
302 device_id_by_auth_state_[false] = "ReportTokens Anonymous Device"; | |
303 device_id_by_auth_state_[true] = "ReportTokens Auth Device"; | |
304 SetAuthToken("ReportTokens Auth"); | |
305 | |
306 rpc_handler_.ReportTokens(test_tokens); | |
307 EXPECT_EQ(RpcHandler::kReportRequestRpcName, rpc_name_); | |
308 EXPECT_EQ(" API Key", api_key_); | |
309 EXPECT_THAT(request_protos_, SizeIs(2)); | |
310 const ReportRequest* report = static_cast<ReportRequest*>(request_protos_[0]); | |
311 RepeatedPtrField<TokenObservation> tokens_sent = | |
312 report->update_signals_request().token_observation(); | |
313 EXPECT_THAT(tokens_sent, ElementsAre( | |
314 Property(&TokenObservation::token_id, "token 1"), | |
315 Property(&TokenObservation::token_id, "token 3"), | |
316 Property(&TokenObservation::token_id, "current audible"), | |
317 Property(&TokenObservation::token_id, "current inaudible"))); | |
318 } | |
319 | |
320 TEST_F(RpcHandlerTest, ReportResponseHandler) { | |
321 // Fail on HTTP status != 200. | |
322 std::unique_ptr<ReportResponse> response(new ReportResponse); | |
323 status_ = SUCCESS; | |
324 SendReportResponse(net::HTTP_BAD_REQUEST, std::move(response)); | |
325 EXPECT_EQ(FAIL, status_); | |
326 | |
327 // Construct a test ReportResponse. | |
328 response.reset(new ReportResponse); | |
329 response->mutable_header()->mutable_status()->set_code(OK); | |
330 UpdateSignalsResponse* update_response = | |
331 response->mutable_update_signals_response(); | |
332 update_response->set_status(util::error::OK); | |
333 Token* invalid_token = update_response->add_token(); | |
334 invalid_token->set_id("bad token"); | |
335 invalid_token->set_status(INVALID); | |
336 update_response->add_directive()->set_subscription_id("Subscription 1"); | |
337 update_response->add_directive()->set_subscription_id("Subscription 2"); | |
338 | |
339 // Check processing. | |
340 status_ = FAIL; | |
341 SendReportResponse(net::HTTP_OK, std::move(response)); | |
342 EXPECT_EQ(SUCCESS, status_); | |
343 EXPECT_TRUE(TokenIsInvalid("bad token")); | |
344 EXPECT_THAT(directive_handler_.added_directives(), | |
345 ElementsAre("Subscription 1", "Subscription 2")); | |
346 } | |
347 | |
348 } // namespace copresence | |
OLD | NEW |