Extension syncing: Introduce a NeedsSync pref
[chromium-blink-merge.git] / components / proximity_auth / client_impl_unittest.cc
blobf519e01642afa74db932e5c0d473594d7c85d7db
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/macros.h"
8 #include "base/memory/scoped_ptr.h"
9 #include "components/proximity_auth/client_observer.h"
10 #include "components/proximity_auth/connection.h"
11 #include "components/proximity_auth/remote_device.h"
12 #include "components/proximity_auth/remote_status_update.h"
13 #include "components/proximity_auth/secure_context.h"
14 #include "components/proximity_auth/wire_message.h"
15 #include "testing/gmock/include/gmock/gmock.h"
16 #include "testing/gtest/include/gtest/gtest.h"
18 using testing::_;
19 using testing::AllOf;
20 using testing::EndsWith;
21 using testing::Eq;
22 using testing::Field;
23 using testing::NiceMock;
24 using testing::Pointee;
25 using testing::Return;
26 using testing::StrictMock;
28 namespace proximity_auth {
29 namespace {
31 const char kChallenge[] = "a most difficult challenge";
32 const char kFakeEncodingSuffix[] = ", but encoded";
34 class MockSecureContext : public SecureContext {
35 public:
36 MockSecureContext() {
37 // By default, mock a secure context that uses the 3.1 protocol. Individual
38 // tests override this as needed.
39 ON_CALL(*this, GetProtocolVersion())
40 .WillByDefault(Return(SecureContext::PROTOCOL_VERSION_THREE_ONE));
42 ~MockSecureContext() override {}
44 MOCK_CONST_METHOD0(GetReceivedAuthMessage, std::string());
45 MOCK_CONST_METHOD0(GetProtocolVersion, ProtocolVersion());
47 std::string Encode(const std::string& message) override {
48 return message + kFakeEncodingSuffix;
51 std::string Decode(const std::string& encoded_message) override {
52 EXPECT_THAT(encoded_message, EndsWith(kFakeEncodingSuffix));
53 std::string decoded_message = encoded_message;
54 decoded_message.erase(decoded_message.rfind(kFakeEncodingSuffix));
55 return decoded_message;
58 private:
59 DISALLOW_COPY_AND_ASSIGN(MockSecureContext);
62 class FakeConnection : public Connection {
63 public:
64 FakeConnection() : Connection(RemoteDevice()) { Connect(); }
65 ~FakeConnection() override { Disconnect(); }
67 void Connect() override { SetStatus(CONNECTED); }
69 void Disconnect() override { SetStatus(DISCONNECTED); }
71 void SendMessageImpl(scoped_ptr<WireMessage> message) override {
72 ASSERT_FALSE(current_message_);
73 current_message_ = message.Pass();
76 // Completes the current send operation with success |success|.
77 void FinishSendingMessageWithSuccess(bool success) {
78 ASSERT_TRUE(current_message_);
79 // Capture a copy of the message, as OnDidSendMessage() might reentrantly
80 // call SendMessage().
81 scoped_ptr<WireMessage> sent_message = current_message_.Pass();
82 OnDidSendMessage(*sent_message, success);
85 // Simulates receiving a wire message with the given |payload|.
86 void ReceiveMessageWithPayload(const std::string& payload) {
87 pending_payload_ = payload;
88 OnBytesReceived(std::string());
89 pending_payload_.clear();
92 // Returns a message containing the payload set via
93 // ReceiveMessageWithPayload().
94 scoped_ptr<WireMessage> DeserializeWireMessage(
95 bool* is_incomplete_message) override {
96 *is_incomplete_message = false;
97 return make_scoped_ptr(new WireMessage(std::string(), pending_payload_));
100 WireMessage* current_message() { return current_message_.get(); }
102 private:
103 // The message currently being sent. Only set between a call to
104 // SendMessageImpl() and FinishSendingMessageWithSuccess().
105 scoped_ptr<WireMessage> current_message_;
107 // The payload that should be returned when DeserializeWireMessage() is
108 // called.
109 std::string pending_payload_;
111 DISALLOW_COPY_AND_ASSIGN(FakeConnection);
114 class MockClientObserver : public ClientObserver {
115 public:
116 explicit MockClientObserver(Client* client) : client_(client) {
117 client_->AddObserver(this);
119 virtual ~MockClientObserver() { client_->RemoveObserver(this); }
121 MOCK_METHOD1(OnUnlockEventSent, void(bool success));
122 MOCK_METHOD1(OnRemoteStatusUpdate,
123 void(const RemoteStatusUpdate& status_update));
124 MOCK_METHOD1(OnDecryptResponseProxy,
125 void(const std::string* decrypted_bytes));
126 MOCK_METHOD1(OnUnlockResponse, void(bool success));
127 MOCK_METHOD0(OnDisconnected, void());
129 virtual void OnDecryptResponse(scoped_ptr<std::string> decrypted_bytes) {
130 OnDecryptResponseProxy(decrypted_bytes.get());
133 private:
134 // The client that |this| instance observes.
135 Client* const client_;
137 DISALLOW_COPY_AND_ASSIGN(MockClientObserver);
140 class TestClient : public ClientImpl {
141 public:
142 TestClient()
143 : ClientImpl(make_scoped_ptr(new NiceMock<FakeConnection>()),
144 make_scoped_ptr(new NiceMock<MockSecureContext>())) {}
145 ~TestClient() override {}
147 // Simple getters for the mock objects owned by |this| client.
148 FakeConnection* GetFakeConnection() {
149 return static_cast<FakeConnection*>(connection());
151 MockSecureContext* GetMockSecureContext() {
152 return static_cast<MockSecureContext*>(secure_context());
155 private:
156 DISALLOW_COPY_AND_ASSIGN(TestClient);
159 } // namespace
161 TEST(ProximityAuthClientImplTest, SupportsSignIn_ProtocolVersionThreeZero) {
162 TestClient client;
163 ON_CALL(*client.GetMockSecureContext(), GetProtocolVersion())
164 .WillByDefault(Return(SecureContext::PROTOCOL_VERSION_THREE_ZERO));
165 EXPECT_FALSE(client.SupportsSignIn());
168 TEST(ProximityAuthClientImplTest, SupportsSignIn_ProtocolVersionThreeOne) {
169 TestClient client;
170 ON_CALL(*client.GetMockSecureContext(), GetProtocolVersion())
171 .WillByDefault(Return(SecureContext::PROTOCOL_VERSION_THREE_ONE));
172 EXPECT_TRUE(client.SupportsSignIn());
175 TEST(ProximityAuthClientImplTest,
176 OnConnectionStatusChanged_ConnectionDisconnects) {
177 TestClient client;
178 MockClientObserver observer(&client);
180 EXPECT_CALL(observer, OnDisconnected());
181 client.GetFakeConnection()->Disconnect();
184 TEST(ProximityAuthClientImplTest, DispatchUnlockEvent_SendsExpectedMessage) {
185 TestClient client;
186 client.DispatchUnlockEvent();
188 WireMessage* message = client.GetFakeConnection()->current_message();
189 ASSERT_TRUE(message);
190 EXPECT_EQ(std::string(), message->permit_id());
191 EXPECT_EQ(
193 "\"name\":\"easy_unlock\","
194 "\"type\":\"event\""
195 "}, but encoded",
196 message->payload());
199 TEST(ProximityAuthClientImplTest, DispatchUnlockEvent_SendMessageFails) {
200 TestClient client;
201 MockClientObserver observer(&client);
202 client.DispatchUnlockEvent();
204 EXPECT_CALL(observer, OnUnlockEventSent(false));
205 client.GetFakeConnection()->FinishSendingMessageWithSuccess(false);
208 TEST(ProximityAuthClientImplTest, DispatchUnlockEvent_SendMessageSucceeds) {
209 TestClient client;
210 MockClientObserver observer(&client);
211 client.DispatchUnlockEvent();
213 EXPECT_CALL(observer, OnUnlockEventSent(true));
214 client.GetFakeConnection()->FinishSendingMessageWithSuccess(true);
217 TEST(ProximityAuthClientImplTest,
218 RequestDecryption_SignInUnsupported_DoesntSendMessage) {
219 TestClient client;
220 ON_CALL(*client.GetMockSecureContext(), GetProtocolVersion())
221 .WillByDefault(Return(SecureContext::PROTOCOL_VERSION_THREE_ZERO));
222 client.RequestDecryption(kChallenge);
223 EXPECT_FALSE(client.GetFakeConnection()->current_message());
226 TEST(ProximityAuthClientImplTest, RequestDecryption_SendsExpectedMessage) {
227 TestClient client;
228 client.RequestDecryption(kChallenge);
230 WireMessage* message = client.GetFakeConnection()->current_message();
231 ASSERT_TRUE(message);
232 EXPECT_EQ(std::string(), message->permit_id());
233 EXPECT_EQ(
235 "\"encrypted_data\":\"YSBtb3N0IGRpZmZpY3VsdCBjaGFsbGVuZ2U=\","
236 "\"type\":\"decrypt_request\""
237 "}, but encoded",
238 message->payload());
241 TEST(ProximityAuthClientImplTest,
242 RequestDecryption_SendsExpectedMessage_UsingBase64UrlEncoding) {
243 TestClient client;
244 client.RequestDecryption("\xFF\xE6");
246 WireMessage* message = client.GetFakeConnection()->current_message();
247 ASSERT_TRUE(message);
248 EXPECT_EQ(std::string(), message->permit_id());
249 EXPECT_EQ(
251 "\"encrypted_data\":\"_-Y=\","
252 "\"type\":\"decrypt_request\""
253 "}, but encoded",
254 message->payload());
257 TEST(ProximityAuthClientImplTest, RequestDecryption_SendMessageFails) {
258 TestClient client;
259 MockClientObserver observer(&client);
260 client.RequestDecryption(kChallenge);
262 EXPECT_CALL(observer, OnDecryptResponseProxy(nullptr));
263 client.GetFakeConnection()->FinishSendingMessageWithSuccess(false);
266 TEST(ProximityAuthClientImplTest,
267 RequestDecryption_SendSucceeds_WaitsForReply) {
268 TestClient client;
269 MockClientObserver observer(&client);
270 client.RequestDecryption(kChallenge);
272 EXPECT_CALL(observer, OnDecryptResponseProxy(_)).Times(0);
273 client.GetFakeConnection()->FinishSendingMessageWithSuccess(true);
276 TEST(ProximityAuthClientImplTest,
277 RequestDecryption_SendSucceeds_NotifiesObserversOnReply_NoData) {
278 TestClient client;
279 MockClientObserver observer(&client);
280 client.RequestDecryption(kChallenge);
281 client.GetFakeConnection()->FinishSendingMessageWithSuccess(true);
283 EXPECT_CALL(observer, OnDecryptResponseProxy(nullptr));
284 client.GetFakeConnection()->ReceiveMessageWithPayload(
285 "{\"type\":\"decrypt_response\"}, but encoded");
288 TEST(ProximityAuthClientImplTest,
289 RequestDecryption_SendSucceeds_NotifiesObserversOnReply_InvalidData) {
290 TestClient client;
291 MockClientObserver observer(&client);
292 client.RequestDecryption(kChallenge);
293 client.GetFakeConnection()->FinishSendingMessageWithSuccess(true);
295 EXPECT_CALL(observer, OnDecryptResponseProxy(nullptr));
296 client.GetFakeConnection()->ReceiveMessageWithPayload(
298 "\"type\":\"decrypt_response\","
299 "\"data\":\"not a base64-encoded string\""
300 "}, but encoded");
303 TEST(ProximityAuthClientImplTest,
304 RequestDecryption_SendSucceeds_NotifiesObserversOnReply_ValidData) {
305 TestClient client;
306 MockClientObserver observer(&client);
307 client.RequestDecryption(kChallenge);
308 client.GetFakeConnection()->FinishSendingMessageWithSuccess(true);
310 EXPECT_CALL(observer, OnDecryptResponseProxy(Pointee(Eq("a winner is you"))));
311 client.GetFakeConnection()->ReceiveMessageWithPayload(
313 "\"type\":\"decrypt_response\","
314 "\"data\":\"YSB3aW5uZXIgaXMgeW91\"" // "a winner is you", base64-encoded
315 "}, but encoded");
318 // Verify that the client correctly parses base64url encoded data.
319 TEST(ProximityAuthClientImplTest,
320 RequestDecryption_SendSucceeds_ParsesBase64UrlEncodingInReply) {
321 TestClient client;
322 MockClientObserver observer(&client);
323 client.RequestDecryption(kChallenge);
324 client.GetFakeConnection()->FinishSendingMessageWithSuccess(true);
326 EXPECT_CALL(observer, OnDecryptResponseProxy(Pointee(Eq("\xFF\xE6"))));
327 client.GetFakeConnection()->ReceiveMessageWithPayload(
329 "\"type\":\"decrypt_response\","
330 "\"data\":\"_-Y=\"" // "\0xFF\0xE6", base64url-encoded.
331 "}, but encoded");
334 TEST(ProximityAuthClientImplTest,
335 RequestUnlock_SignInUnsupported_DoesntSendMessage) {
336 TestClient client;
337 ON_CALL(*client.GetMockSecureContext(), GetProtocolVersion())
338 .WillByDefault(Return(SecureContext::PROTOCOL_VERSION_THREE_ZERO));
339 client.RequestUnlock();
340 EXPECT_FALSE(client.GetFakeConnection()->current_message());
343 TEST(ProximityAuthClientImplTest, RequestUnlock_SendsExpectedMessage) {
344 TestClient client;
345 client.RequestUnlock();
347 WireMessage* message = client.GetFakeConnection()->current_message();
348 ASSERT_TRUE(message);
349 EXPECT_EQ(std::string(), message->permit_id());
350 EXPECT_EQ("{\"type\":\"unlock_request\"}, but encoded", message->payload());
353 TEST(ProximityAuthClientImplTest, RequestUnlock_SendMessageFails) {
354 TestClient client;
355 MockClientObserver observer(&client);
356 client.RequestUnlock();
358 EXPECT_CALL(observer, OnUnlockResponse(false));
359 client.GetFakeConnection()->FinishSendingMessageWithSuccess(false);
362 TEST(ProximityAuthClientImplTest, RequestUnlock_SendSucceeds_WaitsForReply) {
363 TestClient client;
364 MockClientObserver observer(&client);
365 client.RequestUnlock();
367 EXPECT_CALL(observer, OnUnlockResponse(_)).Times(0);
368 client.GetFakeConnection()->FinishSendingMessageWithSuccess(true);
371 TEST(ProximityAuthClientImplTest,
372 RequestUnlock_SendSucceeds_NotifiesObserversOnReply) {
373 TestClient client;
374 MockClientObserver observer(&client);
375 client.RequestUnlock();
376 client.GetFakeConnection()->FinishSendingMessageWithSuccess(true);
378 EXPECT_CALL(observer, OnUnlockResponse(true));
379 client.GetFakeConnection()->ReceiveMessageWithPayload(
380 "{\"type\":\"unlock_response\"}, but encoded");
383 TEST(ProximityAuthClientImplTest,
384 OnMessageReceived_RemoteStatusUpdate_Invalid) {
385 TestClient client;
386 MockClientObserver observer(&client);
388 // Receive a status update message that's missing all the data.
389 EXPECT_CALL(observer, OnRemoteStatusUpdate(_)).Times(0);
390 client.GetFakeConnection()->ReceiveMessageWithPayload(
391 "{\"type\":\"status_update\"}, but encoded");
394 TEST(ProximityAuthClientImplTest, OnMessageReceived_RemoteStatusUpdate_Valid) {
395 TestClient client;
396 MockClientObserver observer(&client);
398 EXPECT_CALL(observer,
399 OnRemoteStatusUpdate(
400 AllOf(Field(&RemoteStatusUpdate::user_presence, USER_PRESENT),
401 Field(&RemoteStatusUpdate::secure_screen_lock_state,
402 SECURE_SCREEN_LOCK_ENABLED),
403 Field(&RemoteStatusUpdate::trust_agent_state,
404 TRUST_AGENT_UNSUPPORTED))));
405 client.GetFakeConnection()->ReceiveMessageWithPayload(
407 "\"type\":\"status_update\","
408 "\"user_presence\":\"present\","
409 "\"secure_screen_lock\":\"enabled\","
410 "\"trust_agent\":\"unsupported\""
411 "}, but encoded");
414 TEST(ProximityAuthClientImplTest, OnMessageReceived_InvalidJSON) {
415 TestClient client;
416 StrictMock<MockClientObserver> observer(&client);
417 client.RequestUnlock();
418 client.GetFakeConnection()->FinishSendingMessageWithSuccess(true);
420 // The StrictMock will verify that no observer methods are called.
421 client.GetFakeConnection()->ReceiveMessageWithPayload(
422 "Not JSON, but encoded");
425 TEST(ProximityAuthClientImplTest, OnMessageReceived_MissingTypeField) {
426 TestClient client;
427 StrictMock<MockClientObserver> observer(&client);
428 client.RequestUnlock();
429 client.GetFakeConnection()->FinishSendingMessageWithSuccess(true);
431 // The StrictMock will verify that no observer methods are called.
432 client.GetFakeConnection()->ReceiveMessageWithPayload(
433 "{\"some key that's not 'type'\":\"some value\"}, but encoded");
436 TEST(ProximityAuthClientImplTest, OnMessageReceived_UnexpectedReply) {
437 TestClient client;
438 StrictMock<MockClientObserver> observer(&client);
440 // The StrictMock will verify that no observer methods are called.
441 client.GetFakeConnection()->ReceiveMessageWithPayload(
442 "{\"type\":\"unlock_response\"}, but encoded");
445 TEST(ProximityAuthClientImplTest,
446 OnMessageReceived_MismatchedReply_UnlockInReplyToDecrypt) {
447 TestClient client;
448 StrictMock<MockClientObserver> observer(&client);
450 client.RequestDecryption(kChallenge);
451 client.GetFakeConnection()->FinishSendingMessageWithSuccess(true);
453 // The StrictMock will verify that no observer methods are called.
454 client.GetFakeConnection()->ReceiveMessageWithPayload(
455 "{\"type\":\"unlock_response\"}, but encoded");
458 TEST(ProximityAuthClientImplTest,
459 OnMessageReceived_MismatchedReply_DecryptInReplyToUnlock) {
460 TestClient client;
461 StrictMock<MockClientObserver> observer(&client);
463 client.RequestUnlock();
464 client.GetFakeConnection()->FinishSendingMessageWithSuccess(true);
466 // The StrictMock will verify that no observer methods are called.
467 client.GetFakeConnection()->ReceiveMessageWithPayload(
469 "\"type\":\"decrypt_response\","
470 "\"data\":\"YSB3aW5uZXIgaXMgeW91\""
471 "}, but encoded");
474 TEST(ProximityAuthClientImplTest, BuffersMessages_WhileSending) {
475 TestClient client;
476 MockClientObserver observer(&client);
478 // Initiate a decryption request, and then initiate an unlock request before
479 // the decryption request is even finished sending.
480 client.RequestDecryption(kChallenge);
481 client.RequestUnlock();
483 EXPECT_CALL(observer, OnDecryptResponseProxy(nullptr));
484 client.GetFakeConnection()->FinishSendingMessageWithSuccess(false);
486 EXPECT_CALL(observer, OnUnlockResponse(false));
487 client.GetFakeConnection()->FinishSendingMessageWithSuccess(false);
490 TEST(ProximityAuthClientImplTest, BuffersMessages_WhileAwaitingReply) {
491 TestClient client;
492 MockClientObserver observer(&client);
494 // Initiate a decryption request, and allow the message to be sent.
495 client.RequestDecryption(kChallenge);
496 client.GetFakeConnection()->FinishSendingMessageWithSuccess(true);
498 // At this point, the client is awaiting a reply to the decryption message.
499 // While it's waiting, initiate an unlock request.
500 client.RequestUnlock();
502 // Now simulate a response arriving for the original decryption request.
503 EXPECT_CALL(observer, OnDecryptResponseProxy(Pointee(Eq("a winner is you"))));
504 client.GetFakeConnection()->ReceiveMessageWithPayload(
506 "\"type\":\"decrypt_response\","
507 "\"data\":\"YSB3aW5uZXIgaXMgeW91\""
508 "}, but encoded");
510 // The unlock request should have remained buffered, and should only now be
511 // sent.
512 EXPECT_CALL(observer, OnUnlockResponse(false));
513 client.GetFakeConnection()->FinishSendingMessageWithSuccess(false);
516 } // namespace proximity_auth