QUIC - cleanup changes to sync chromium tree with internal source.
[chromium-blink-merge.git] / components / proximity_auth / client_impl_unittest.cc
blobfc758050cc57898d1d92cf56cde097c2a0a57b4a
1 // Copyright 2014 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.
5 #include "components/proximity_auth/client_impl.h"
7 #include "base/callback.h"
8 #include "base/macros.h"
9 #include "base/memory/scoped_ptr.h"
10 #include "components/proximity_auth/client_observer.h"
11 #include "components/proximity_auth/connection.h"
12 #include "components/proximity_auth/remote_device.h"
13 #include "components/proximity_auth/remote_status_update.h"
14 #include "components/proximity_auth/secure_context.h"
15 #include "components/proximity_auth/wire_message.h"
16 #include "testing/gmock/include/gmock/gmock.h"
17 #include "testing/gtest/include/gtest/gtest.h"
19 using testing::_;
20 using testing::AllOf;
21 using testing::EndsWith;
22 using testing::Eq;
23 using testing::Field;
24 using testing::NiceMock;
25 using testing::Pointee;
26 using testing::Return;
27 using testing::StrictMock;
29 namespace proximity_auth {
30 namespace {
32 const char kChallenge[] = "a most difficult challenge";
33 const char kFakeEncodingSuffix[] = ", but encoded";
35 class MockSecureContext : public SecureContext {
36 public:
37 MockSecureContext() {
38 // By default, mock a secure context that uses the 3.1 protocol. Individual
39 // tests override this as needed.
40 ON_CALL(*this, GetProtocolVersion())
41 .WillByDefault(Return(SecureContext::PROTOCOL_VERSION_THREE_ONE));
43 ~MockSecureContext() override {}
45 MOCK_CONST_METHOD0(GetReceivedAuthMessage, std::string());
46 MOCK_CONST_METHOD0(GetProtocolVersion, ProtocolVersion());
48 void Encode(const std::string& message,
49 const MessageCallback& callback) override {
50 callback.Run(message + kFakeEncodingSuffix);
53 void Decode(const std::string& encoded_message,
54 const MessageCallback& callback) override {
55 EXPECT_THAT(encoded_message, EndsWith(kFakeEncodingSuffix));
56 std::string decoded_message = encoded_message;
57 decoded_message.erase(decoded_message.rfind(kFakeEncodingSuffix));
58 callback.Run(decoded_message);
61 private:
62 DISALLOW_COPY_AND_ASSIGN(MockSecureContext);
65 class FakeConnection : public Connection {
66 public:
67 FakeConnection() : Connection(RemoteDevice()) { Connect(); }
68 ~FakeConnection() override { Disconnect(); }
70 void Connect() override { SetStatus(CONNECTED); }
72 void Disconnect() override { SetStatus(DISCONNECTED); }
74 void SendMessageImpl(scoped_ptr<WireMessage> message) override {
75 ASSERT_FALSE(current_message_);
76 current_message_ = message.Pass();
79 // Completes the current send operation with success |success|.
80 void FinishSendingMessageWithSuccess(bool success) {
81 ASSERT_TRUE(current_message_);
82 // Capture a copy of the message, as OnDidSendMessage() might reentrantly
83 // call SendMessage().
84 scoped_ptr<WireMessage> sent_message = current_message_.Pass();
85 OnDidSendMessage(*sent_message, success);
88 // Simulates receiving a wire message with the given |payload|.
89 void ReceiveMessageWithPayload(const std::string& payload) {
90 pending_payload_ = payload;
91 OnBytesReceived(std::string());
92 pending_payload_.clear();
95 // Returns a message containing the payload set via
96 // ReceiveMessageWithPayload().
97 scoped_ptr<WireMessage> DeserializeWireMessage(
98 bool* is_incomplete_message) override {
99 *is_incomplete_message = false;
100 return make_scoped_ptr(new WireMessage(pending_payload_));
103 WireMessage* current_message() { return current_message_.get(); }
105 private:
106 // The message currently being sent. Only set between a call to
107 // SendMessageImpl() and FinishSendingMessageWithSuccess().
108 scoped_ptr<WireMessage> current_message_;
110 // The payload that should be returned when DeserializeWireMessage() is
111 // called.
112 std::string pending_payload_;
114 DISALLOW_COPY_AND_ASSIGN(FakeConnection);
117 class MockClientObserver : public ClientObserver {
118 public:
119 explicit MockClientObserver(Client* client) : client_(client) {
120 client_->AddObserver(this);
122 virtual ~MockClientObserver() { client_->RemoveObserver(this); }
124 MOCK_METHOD1(OnUnlockEventSent, void(bool success));
125 MOCK_METHOD1(OnRemoteStatusUpdate,
126 void(const RemoteStatusUpdate& status_update));
127 MOCK_METHOD1(OnDecryptResponseProxy,
128 void(const std::string* decrypted_bytes));
129 MOCK_METHOD1(OnUnlockResponse, void(bool success));
130 MOCK_METHOD0(OnDisconnected, void());
132 virtual void OnDecryptResponse(scoped_ptr<std::string> decrypted_bytes) {
133 OnDecryptResponseProxy(decrypted_bytes.get());
136 private:
137 // The client that |this| instance observes.
138 Client* const client_;
140 DISALLOW_COPY_AND_ASSIGN(MockClientObserver);
143 class TestClient : public ClientImpl {
144 public:
145 TestClient()
146 : ClientImpl(make_scoped_ptr(new NiceMock<FakeConnection>()),
147 make_scoped_ptr(new NiceMock<MockSecureContext>())) {}
148 ~TestClient() override {}
150 // Simple getters for the mock objects owned by |this| client.
151 FakeConnection* GetFakeConnection() {
152 return static_cast<FakeConnection*>(connection());
154 MockSecureContext* GetMockSecureContext() {
155 return static_cast<MockSecureContext*>(secure_context());
158 private:
159 DISALLOW_COPY_AND_ASSIGN(TestClient);
162 } // namespace
164 TEST(ProximityAuthClientImplTest, SupportsSignIn_ProtocolVersionThreeZero) {
165 TestClient client;
166 ON_CALL(*client.GetMockSecureContext(), GetProtocolVersion())
167 .WillByDefault(Return(SecureContext::PROTOCOL_VERSION_THREE_ZERO));
168 EXPECT_FALSE(client.SupportsSignIn());
171 TEST(ProximityAuthClientImplTest, SupportsSignIn_ProtocolVersionThreeOne) {
172 TestClient client;
173 ON_CALL(*client.GetMockSecureContext(), GetProtocolVersion())
174 .WillByDefault(Return(SecureContext::PROTOCOL_VERSION_THREE_ONE));
175 EXPECT_TRUE(client.SupportsSignIn());
178 TEST(ProximityAuthClientImplTest,
179 OnConnectionStatusChanged_ConnectionDisconnects) {
180 TestClient client;
181 MockClientObserver observer(&client);
183 EXPECT_CALL(observer, OnDisconnected());
184 client.GetFakeConnection()->Disconnect();
187 TEST(ProximityAuthClientImplTest, DispatchUnlockEvent_SendsExpectedMessage) {
188 TestClient client;
189 client.DispatchUnlockEvent();
191 WireMessage* message = client.GetFakeConnection()->current_message();
192 ASSERT_TRUE(message);
193 EXPECT_EQ(std::string(), message->permit_id());
194 EXPECT_EQ(
196 "\"name\":\"easy_unlock\","
197 "\"type\":\"event\""
198 "}, but encoded",
199 message->payload());
202 TEST(ProximityAuthClientImplTest, DispatchUnlockEvent_SendMessageFails) {
203 TestClient client;
204 MockClientObserver observer(&client);
205 client.DispatchUnlockEvent();
207 EXPECT_CALL(observer, OnUnlockEventSent(false));
208 client.GetFakeConnection()->FinishSendingMessageWithSuccess(false);
211 TEST(ProximityAuthClientImplTest, DispatchUnlockEvent_SendMessageSucceeds) {
212 TestClient client;
213 MockClientObserver observer(&client);
214 client.DispatchUnlockEvent();
216 EXPECT_CALL(observer, OnUnlockEventSent(true));
217 client.GetFakeConnection()->FinishSendingMessageWithSuccess(true);
220 TEST(ProximityAuthClientImplTest,
221 RequestDecryption_SignInUnsupported_DoesntSendMessage) {
222 TestClient client;
223 ON_CALL(*client.GetMockSecureContext(), GetProtocolVersion())
224 .WillByDefault(Return(SecureContext::PROTOCOL_VERSION_THREE_ZERO));
225 client.RequestDecryption(kChallenge);
226 EXPECT_FALSE(client.GetFakeConnection()->current_message());
229 TEST(ProximityAuthClientImplTest, RequestDecryption_SendsExpectedMessage) {
230 TestClient client;
231 client.RequestDecryption(kChallenge);
233 WireMessage* message = client.GetFakeConnection()->current_message();
234 ASSERT_TRUE(message);
235 EXPECT_EQ(std::string(), message->permit_id());
236 EXPECT_EQ(
238 "\"encrypted_data\":\"YSBtb3N0IGRpZmZpY3VsdCBjaGFsbGVuZ2U=\","
239 "\"type\":\"decrypt_request\""
240 "}, but encoded",
241 message->payload());
244 TEST(ProximityAuthClientImplTest,
245 RequestDecryption_SendsExpectedMessage_UsingBase64UrlEncoding) {
246 TestClient client;
247 client.RequestDecryption("\xFF\xE6");
249 WireMessage* message = client.GetFakeConnection()->current_message();
250 ASSERT_TRUE(message);
251 EXPECT_EQ(std::string(), message->permit_id());
252 EXPECT_EQ(
254 "\"encrypted_data\":\"_-Y=\","
255 "\"type\":\"decrypt_request\""
256 "}, but encoded",
257 message->payload());
260 TEST(ProximityAuthClientImplTest, RequestDecryption_SendMessageFails) {
261 TestClient client;
262 MockClientObserver observer(&client);
263 client.RequestDecryption(kChallenge);
265 EXPECT_CALL(observer, OnDecryptResponseProxy(nullptr));
266 client.GetFakeConnection()->FinishSendingMessageWithSuccess(false);
269 TEST(ProximityAuthClientImplTest,
270 RequestDecryption_SendSucceeds_WaitsForReply) {
271 TestClient client;
272 MockClientObserver observer(&client);
273 client.RequestDecryption(kChallenge);
275 EXPECT_CALL(observer, OnDecryptResponseProxy(_)).Times(0);
276 client.GetFakeConnection()->FinishSendingMessageWithSuccess(true);
279 TEST(ProximityAuthClientImplTest,
280 RequestDecryption_SendSucceeds_NotifiesObserversOnReply_NoData) {
281 TestClient client;
282 MockClientObserver observer(&client);
283 client.RequestDecryption(kChallenge);
284 client.GetFakeConnection()->FinishSendingMessageWithSuccess(true);
286 EXPECT_CALL(observer, OnDecryptResponseProxy(nullptr));
287 client.GetFakeConnection()->ReceiveMessageWithPayload(
288 "{\"type\":\"decrypt_response\"}, but encoded");
291 TEST(ProximityAuthClientImplTest,
292 RequestDecryption_SendSucceeds_NotifiesObserversOnReply_InvalidData) {
293 TestClient client;
294 MockClientObserver observer(&client);
295 client.RequestDecryption(kChallenge);
296 client.GetFakeConnection()->FinishSendingMessageWithSuccess(true);
298 EXPECT_CALL(observer, OnDecryptResponseProxy(nullptr));
299 client.GetFakeConnection()->ReceiveMessageWithPayload(
301 "\"type\":\"decrypt_response\","
302 "\"data\":\"not a base64-encoded string\""
303 "}, but encoded");
306 TEST(ProximityAuthClientImplTest,
307 RequestDecryption_SendSucceeds_NotifiesObserversOnReply_ValidData) {
308 TestClient client;
309 MockClientObserver observer(&client);
310 client.RequestDecryption(kChallenge);
311 client.GetFakeConnection()->FinishSendingMessageWithSuccess(true);
313 EXPECT_CALL(observer, OnDecryptResponseProxy(Pointee(Eq("a winner is you"))));
314 client.GetFakeConnection()->ReceiveMessageWithPayload(
316 "\"type\":\"decrypt_response\","
317 "\"data\":\"YSB3aW5uZXIgaXMgeW91\"" // "a winner is you", base64-encoded
318 "}, but encoded");
321 // Verify that the client correctly parses base64url encoded data.
322 TEST(ProximityAuthClientImplTest,
323 RequestDecryption_SendSucceeds_ParsesBase64UrlEncodingInReply) {
324 TestClient client;
325 MockClientObserver observer(&client);
326 client.RequestDecryption(kChallenge);
327 client.GetFakeConnection()->FinishSendingMessageWithSuccess(true);
329 EXPECT_CALL(observer, OnDecryptResponseProxy(Pointee(Eq("\xFF\xE6"))));
330 client.GetFakeConnection()->ReceiveMessageWithPayload(
332 "\"type\":\"decrypt_response\","
333 "\"data\":\"_-Y=\"" // "\0xFF\0xE6", base64url-encoded.
334 "}, but encoded");
337 TEST(ProximityAuthClientImplTest,
338 RequestUnlock_SignInUnsupported_DoesntSendMessage) {
339 TestClient client;
340 ON_CALL(*client.GetMockSecureContext(), GetProtocolVersion())
341 .WillByDefault(Return(SecureContext::PROTOCOL_VERSION_THREE_ZERO));
342 client.RequestUnlock();
343 EXPECT_FALSE(client.GetFakeConnection()->current_message());
346 TEST(ProximityAuthClientImplTest, RequestUnlock_SendsExpectedMessage) {
347 TestClient client;
348 client.RequestUnlock();
350 WireMessage* message = client.GetFakeConnection()->current_message();
351 ASSERT_TRUE(message);
352 EXPECT_EQ(std::string(), message->permit_id());
353 EXPECT_EQ("{\"type\":\"unlock_request\"}, but encoded", message->payload());
356 TEST(ProximityAuthClientImplTest, RequestUnlock_SendMessageFails) {
357 TestClient client;
358 MockClientObserver observer(&client);
359 client.RequestUnlock();
361 EXPECT_CALL(observer, OnUnlockResponse(false));
362 client.GetFakeConnection()->FinishSendingMessageWithSuccess(false);
365 TEST(ProximityAuthClientImplTest, RequestUnlock_SendSucceeds_WaitsForReply) {
366 TestClient client;
367 MockClientObserver observer(&client);
368 client.RequestUnlock();
370 EXPECT_CALL(observer, OnUnlockResponse(_)).Times(0);
371 client.GetFakeConnection()->FinishSendingMessageWithSuccess(true);
374 TEST(ProximityAuthClientImplTest,
375 RequestUnlock_SendSucceeds_NotifiesObserversOnReply) {
376 TestClient client;
377 MockClientObserver observer(&client);
378 client.RequestUnlock();
379 client.GetFakeConnection()->FinishSendingMessageWithSuccess(true);
381 EXPECT_CALL(observer, OnUnlockResponse(true));
382 client.GetFakeConnection()->ReceiveMessageWithPayload(
383 "{\"type\":\"unlock_response\"}, but encoded");
386 TEST(ProximityAuthClientImplTest,
387 OnMessageReceived_RemoteStatusUpdate_Invalid) {
388 TestClient client;
389 MockClientObserver observer(&client);
391 // Receive a status update message that's missing all the data.
392 EXPECT_CALL(observer, OnRemoteStatusUpdate(_)).Times(0);
393 client.GetFakeConnection()->ReceiveMessageWithPayload(
394 "{\"type\":\"status_update\"}, but encoded");
397 TEST(ProximityAuthClientImplTest, OnMessageReceived_RemoteStatusUpdate_Valid) {
398 TestClient client;
399 MockClientObserver observer(&client);
401 EXPECT_CALL(observer,
402 OnRemoteStatusUpdate(
403 AllOf(Field(&RemoteStatusUpdate::user_presence, USER_PRESENT),
404 Field(&RemoteStatusUpdate::secure_screen_lock_state,
405 SECURE_SCREEN_LOCK_ENABLED),
406 Field(&RemoteStatusUpdate::trust_agent_state,
407 TRUST_AGENT_UNSUPPORTED))));
408 client.GetFakeConnection()->ReceiveMessageWithPayload(
410 "\"type\":\"status_update\","
411 "\"user_presence\":\"present\","
412 "\"secure_screen_lock\":\"enabled\","
413 "\"trust_agent\":\"unsupported\""
414 "}, but encoded");
417 TEST(ProximityAuthClientImplTest, OnMessageReceived_InvalidJSON) {
418 TestClient client;
419 StrictMock<MockClientObserver> observer(&client);
420 client.RequestUnlock();
421 client.GetFakeConnection()->FinishSendingMessageWithSuccess(true);
423 // The StrictMock will verify that no observer methods are called.
424 client.GetFakeConnection()->ReceiveMessageWithPayload(
425 "Not JSON, but encoded");
428 TEST(ProximityAuthClientImplTest, OnMessageReceived_MissingTypeField) {
429 TestClient client;
430 StrictMock<MockClientObserver> observer(&client);
431 client.RequestUnlock();
432 client.GetFakeConnection()->FinishSendingMessageWithSuccess(true);
434 // The StrictMock will verify that no observer methods are called.
435 client.GetFakeConnection()->ReceiveMessageWithPayload(
436 "{\"some key that's not 'type'\":\"some value\"}, but encoded");
439 TEST(ProximityAuthClientImplTest, OnMessageReceived_UnexpectedReply) {
440 TestClient client;
441 StrictMock<MockClientObserver> observer(&client);
443 // The StrictMock will verify that no observer methods are called.
444 client.GetFakeConnection()->ReceiveMessageWithPayload(
445 "{\"type\":\"unlock_response\"}, but encoded");
448 TEST(ProximityAuthClientImplTest,
449 OnMessageReceived_MismatchedReply_UnlockInReplyToDecrypt) {
450 TestClient client;
451 StrictMock<MockClientObserver> observer(&client);
453 client.RequestDecryption(kChallenge);
454 client.GetFakeConnection()->FinishSendingMessageWithSuccess(true);
456 // The StrictMock will verify that no observer methods are called.
457 client.GetFakeConnection()->ReceiveMessageWithPayload(
458 "{\"type\":\"unlock_response\"}, but encoded");
461 TEST(ProximityAuthClientImplTest,
462 OnMessageReceived_MismatchedReply_DecryptInReplyToUnlock) {
463 TestClient client;
464 StrictMock<MockClientObserver> observer(&client);
466 client.RequestUnlock();
467 client.GetFakeConnection()->FinishSendingMessageWithSuccess(true);
469 // The StrictMock will verify that no observer methods are called.
470 client.GetFakeConnection()->ReceiveMessageWithPayload(
472 "\"type\":\"decrypt_response\","
473 "\"data\":\"YSB3aW5uZXIgaXMgeW91\""
474 "}, but encoded");
477 TEST(ProximityAuthClientImplTest, BuffersMessages_WhileSending) {
478 TestClient client;
479 MockClientObserver observer(&client);
481 // Initiate a decryption request, and then initiate an unlock request before
482 // the decryption request is even finished sending.
483 client.RequestDecryption(kChallenge);
484 client.RequestUnlock();
486 EXPECT_CALL(observer, OnDecryptResponseProxy(nullptr));
487 client.GetFakeConnection()->FinishSendingMessageWithSuccess(false);
489 EXPECT_CALL(observer, OnUnlockResponse(false));
490 client.GetFakeConnection()->FinishSendingMessageWithSuccess(false);
493 TEST(ProximityAuthClientImplTest, BuffersMessages_WhileAwaitingReply) {
494 TestClient client;
495 MockClientObserver observer(&client);
497 // Initiate a decryption request, and allow the message to be sent.
498 client.RequestDecryption(kChallenge);
499 client.GetFakeConnection()->FinishSendingMessageWithSuccess(true);
501 // At this point, the client is awaiting a reply to the decryption message.
502 // While it's waiting, initiate an unlock request.
503 client.RequestUnlock();
505 // Now simulate a response arriving for the original decryption request.
506 EXPECT_CALL(observer, OnDecryptResponseProxy(Pointee(Eq("a winner is you"))));
507 client.GetFakeConnection()->ReceiveMessageWithPayload(
509 "\"type\":\"decrypt_response\","
510 "\"data\":\"YSB3aW5uZXIgaXMgeW91\""
511 "}, but encoded");
513 // The unlock request should have remained buffered, and should only now be
514 // sent.
515 EXPECT_CALL(observer, OnUnlockResponse(false));
516 client.GetFakeConnection()->FinishSendingMessageWithSuccess(false);
519 } // namespace proximity_auth