| 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 |