• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2024 The Chromium Authors
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 "net/device_bound_sessions/session_service_impl.h"
6 
7 #include "base/test/test_future.h"
8 #include "crypto/scoped_mock_unexportable_key_provider.h"
9 #include "net/device_bound_sessions/mock_session_store.h"
10 #include "net/device_bound_sessions/session_store.h"
11 #include "net/device_bound_sessions/unexportable_key_service_factory.h"
12 #include "net/test/test_with_task_environment.h"
13 #include "net/url_request/url_request_context_builder.h"
14 #include "net/url_request/url_request_test_util.h"
15 #include "testing/gtest/include/gtest/gtest.h"
16 #include "url/gurl.h"
17 
18 using ::testing::InSequence;
19 using ::testing::Invoke;
20 using ::testing::StrictMock;
21 using ::testing::UnorderedElementsAre;
22 
23 namespace net::device_bound_sessions {
24 
25 namespace {
26 
27 constexpr net::NetworkTrafficAnnotationTag kDummyAnnotation =
28     net::DefineNetworkTrafficAnnotation("dbsc_registration", "");
29 
30 // Constant variables
31 constexpr char kUrlString[] = "https://example.com";
32 const GURL kTestUrl(kUrlString);
33 const std::string kSessionId = "SessionId";
34 const std::string kChallenge = "challenge";
35 
36 // Matcher for SessionKeys
ExpectId(std::string_view id)37 auto ExpectId(std::string_view id) {
38   return testing::Field(&SessionKey::id, Session::Id(std::string(id)));
39 }
40 
TestFetcher(std::string session_id,std::optional<std::string> referral_session_identifier)41 std::optional<RegistrationFetcher::RegistrationCompleteParams> TestFetcher(
42     std::string session_id,
43     std::optional<std::string> referral_session_identifier) {
44   std::vector<SessionParams::Credential> cookie_credentials;
45   cookie_credentials.push_back(
46       SessionParams::Credential{"test_cookie", "secure"});
47   SessionParams::Scope scope;
48   scope.include_site = true;
49   SessionParams session_params(std::move(session_id), kUrlString,
50                                std::move(scope), std::move(cookie_credentials));
51   unexportable_keys::UnexportableKeyId key_id;
52   return std::make_optional<RegistrationFetcher::RegistrationCompleteParams>(
53       std::move(session_params), std::move(key_id), kTestUrl,
54       std::move(referral_session_identifier));
55 }
56 
NullFetcher()57 std::optional<RegistrationFetcher::RegistrationCompleteParams> NullFetcher() {
58   return std::nullopt;
59 }
60 
TestFetcherFactory(std::string session_id,std::optional<std::string> referral_session_identifier)61 RegistrationFetcher::FetcherType TestFetcherFactory(
62     std::string session_id,
63     std::optional<std::string> referral_session_identifier) {
64   static std::string g_session_id;
65   static std::optional<std::string> g_referral_session_id;
66   g_session_id = std::move(session_id);
67   g_referral_session_id = std::move(referral_session_identifier);
68 
69   return []() { return TestFetcher(g_session_id, g_referral_session_id); };
70 }
71 
72 class ScopedTestFetcher {
73  public:
ScopedTestFetcher(std::string session_id,std::optional<std::string> referral_session_identifier=std::nullopt)74   explicit ScopedTestFetcher(
75       std::string session_id,
76       std::optional<std::string> referral_session_identifier = std::nullopt) {
77     RegistrationFetcher::SetFetcherForTesting(TestFetcherFactory(
78         std::move(session_id), std::move(referral_session_identifier)));
79   }
80 
~ScopedTestFetcher()81   ~ScopedTestFetcher() { RegistrationFetcher::SetFetcherForTesting(nullptr); }
82 };
83 
84 class ScopedNullFetcher {
85  public:
ScopedNullFetcher()86   ScopedNullFetcher() {
87     RegistrationFetcher::SetFetcherForTesting(NullFetcher);
88   }
~ScopedNullFetcher()89   ~ScopedNullFetcher() { RegistrationFetcher::SetFetcherForTesting(nullptr); }
90 };
91 
92 class SessionServiceImplTest : public TestWithTaskEnvironment {
93  public:
SessionServiceImplTest()94   SessionServiceImplTest()
95       : context_(CreateTestURLRequestContextBuilder()->Build()),
96         service_(*UnexportableKeyServiceFactory::GetInstance()->GetShared(),
97                  context_.get(),
98                  /*store=*/nullptr) {}
99 
context()100   URLRequestContext* context() { return context_.get(); }
service()101   SessionServiceImpl& service() { return service_; }
102 
103  private:
104   crypto::ScopedMockUnexportableKeyProvider scoped_mock_key_provider_;
105   std::unique_ptr<URLRequestContext> context_;
106   SessionServiceImpl service_;
107 };
108 
109 // Not implemented so test just makes sure it can run
TEST_F(SessionServiceImplTest,TestDefer)110 TEST_F(SessionServiceImplTest, TestDefer) {
111   SessionService::RefreshCompleteCallback cb1 = base::DoNothing();
112   SessionService::RefreshCompleteCallback cb2 = base::DoNothing();
113   net::TestDelegate delegate;
114   std::unique_ptr<URLRequest> request =
115       context()->CreateRequest(kTestUrl, IDLE, &delegate, kDummyAnnotation);
116   service().DeferRequestForRefresh(request.get(), Session::Id("test"),
117                                    std::move(cb1), std::move(cb2));
118 }
119 
TEST_F(SessionServiceImplTest,RegisterSuccess)120 TEST_F(SessionServiceImplTest, RegisterSuccess) {
121   ScopedTestFetcher scoped_test_fetcher(kSessionId);
122   auto fetch_param = RegistrationFetcherParam::CreateInstanceForTesting(
123       kTestUrl, {crypto::SignatureVerifier::SignatureAlgorithm::ECDSA_SHA256},
124       kChallenge,
125       /*authorization=*/std::nullopt);
126   service().RegisterBoundSession(base::DoNothing(), std::move(fetch_param),
127                                  IsolationInfo::CreateTransient());
128 
129   net::TestDelegate delegate;
130   std::unique_ptr<URLRequest> request =
131       context()->CreateRequest(kTestUrl, IDLE, &delegate, kDummyAnnotation);
132 
133   // The request needs to be samesite for it to be considered
134   // candidate for deferral.
135   request->set_site_for_cookies(SiteForCookies::FromUrl(kTestUrl));
136 
137   std::optional<Session::Id> maybe_id =
138       service().GetAnySessionRequiringDeferral(request.get());
139   ASSERT_TRUE(maybe_id);
140   EXPECT_EQ(**maybe_id, kSessionId);
141 }
142 
TEST_F(SessionServiceImplTest,RegisterNoId)143 TEST_F(SessionServiceImplTest, RegisterNoId) {
144   ScopedTestFetcher scoped_test_fetcher(/*session_id=*/"");
145   auto fetch_param = RegistrationFetcherParam::CreateInstanceForTesting(
146       kTestUrl, {crypto::SignatureVerifier::SignatureAlgorithm::ECDSA_SHA256},
147       kChallenge,
148       /*authorization=*/std::nullopt);
149   service().RegisterBoundSession(base::DoNothing(), std::move(fetch_param),
150                                  IsolationInfo::CreateTransient());
151 
152   net::TestDelegate delegate;
153   std::unique_ptr<URLRequest> request =
154       context()->CreateRequest(kTestUrl, IDLE, &delegate, kDummyAnnotation);
155 
156   request->set_site_for_cookies(SiteForCookies::FromUrl(kTestUrl));
157 
158   std::optional<Session::Id> maybe_id =
159       service().GetAnySessionRequiringDeferral(request.get());
160   // session_id is empty, so should not be valid
161   EXPECT_FALSE(maybe_id);
162 }
163 
TEST_F(SessionServiceImplTest,RegisterNullFetcher)164 TEST_F(SessionServiceImplTest, RegisterNullFetcher) {
165   ScopedNullFetcher scopedNullFetcher;
166   auto fetch_param = RegistrationFetcherParam::CreateInstanceForTesting(
167       kTestUrl, {crypto::SignatureVerifier::SignatureAlgorithm::ECDSA_SHA256},
168       kChallenge,
169       /*authorization=*/std::nullopt);
170   service().RegisterBoundSession(base::DoNothing(), std::move(fetch_param),
171                                  IsolationInfo::CreateTransient());
172 
173   net::TestDelegate delegate;
174   std::unique_ptr<URLRequest> request =
175       context()->CreateRequest(kTestUrl, IDLE, &delegate, kDummyAnnotation);
176 
177   request->set_site_for_cookies(SiteForCookies::FromUrl(kTestUrl));
178 
179   std::optional<Session::Id> maybe_id =
180       service().GetAnySessionRequiringDeferral(request.get());
181   // NullFetcher, so should not be valid
182   EXPECT_FALSE(maybe_id);
183 }
184 
TEST_F(SessionServiceImplTest,SetChallengeForBoundSession)185 TEST_F(SessionServiceImplTest, SetChallengeForBoundSession) {
186   ScopedTestFetcher scoped_test_fetcher(kSessionId);
187   auto fetch_param = RegistrationFetcherParam::CreateInstanceForTesting(
188       kTestUrl, {crypto::SignatureVerifier::SignatureAlgorithm::ECDSA_SHA256},
189       kChallenge,
190       /*authorization=*/std::nullopt);
191   service().RegisterBoundSession(base::DoNothing(), std::move(fetch_param),
192                                  IsolationInfo::CreateTransient());
193 
194   scoped_refptr<net::HttpResponseHeaders> headers =
195       HttpResponseHeaders::Builder({1, 1}, "200 OK").Build();
196   headers->AddHeader(
197       "Sec-Session-Challenge",
198       R"("challenge";id="SessionId", "challenge1";id="NonExisted")");
199   headers->AddHeader("Sec-Session-Challenge", R"("challenge2")");
200 
201   std::vector<SessionChallengeParam> params =
202       SessionChallengeParam::CreateIfValid(kTestUrl, headers.get());
203 
204   EXPECT_EQ(params.size(), 3U);
205 
206   for (const auto& param : params) {
207     service().SetChallengeForBoundSession(base::DoNothing(), kTestUrl, param);
208   }
209 
210   const Session* session =
211       service().GetSessionForTesting(SchemefulSite(kTestUrl), kSessionId);
212   ASSERT_TRUE(session);
213   EXPECT_EQ(session->cached_challenge(), "challenge");
214 
215   session =
216       service().GetSessionForTesting(SchemefulSite(kTestUrl), "NonExisted");
217   ASSERT_FALSE(session);
218 }
219 
TEST_F(SessionServiceImplTest,ExpiryExtendedOnUser)220 TEST_F(SessionServiceImplTest, ExpiryExtendedOnUser) {
221   ScopedTestFetcher scoped_test_fetcher(kSessionId);
222   auto fetch_param = RegistrationFetcherParam::CreateInstanceForTesting(
223       kTestUrl, {crypto::SignatureVerifier::SignatureAlgorithm::ECDSA_SHA256},
224       kChallenge,
225       /*authorization=*/std::nullopt);
226   service().RegisterBoundSession(base::DoNothing(), std::move(fetch_param),
227                                  IsolationInfo::CreateTransient());
228 
229   Session* session =
230       service().GetSessionForTesting(SchemefulSite(kTestUrl), kSessionId);
231   ASSERT_TRUE(session);
232   session->set_expiry_date(base::Time::Now() + base::Days(1));
233 
234   net::TestDelegate delegate;
235   std::unique_ptr<URLRequest> request =
236       context()->CreateRequest(kTestUrl, IDLE, &delegate, kDummyAnnotation);
237   // The request needs to be samesite for it to be considered
238   // candidate for deferral.
239   request->set_site_for_cookies(SiteForCookies::FromUrl(kTestUrl));
240 
241   service().GetAnySessionRequiringDeferral(request.get());
242 
243   EXPECT_GT(session->expiry_date(), base::Time::Now() + base::Days(399));
244 }
245 
TEST_F(SessionServiceImplTest,NullAccessObserver)246 TEST_F(SessionServiceImplTest, NullAccessObserver) {
247   ScopedTestFetcher scoped_test_fetcher(kSessionId);
248 
249   auto fetch_param = RegistrationFetcherParam::CreateInstanceForTesting(
250       kTestUrl, {crypto::SignatureVerifier::SignatureAlgorithm::ECDSA_SHA256},
251       "challenge", /*authorization=*/std::nullopt);
252   service().RegisterBoundSession(SessionService::OnAccessCallback(),
253                                  std::move(fetch_param),
254                                  IsolationInfo::CreateTransient());
255 
256   // The access observer was null, so no call is expected
257 }
258 
TEST_F(SessionServiceImplTest,AccessObserverCalledOnRegistration)259 TEST_F(SessionServiceImplTest, AccessObserverCalledOnRegistration) {
260   ScopedTestFetcher scoped_test_fetcher(kSessionId);
261 
262   auto fetch_param = RegistrationFetcherParam::CreateInstanceForTesting(
263       kTestUrl, {crypto::SignatureVerifier::SignatureAlgorithm::ECDSA_SHA256},
264       "challenge", /*authorization=*/std::nullopt);
265   base::test::TestFuture<SessionKey> future;
266   service().RegisterBoundSession(
267       future.GetRepeatingCallback<const SessionKey&>(), std::move(fetch_param),
268       IsolationInfo::CreateTransient());
269 
270   SessionKey session_key = future.Take();
271   EXPECT_EQ(session_key.site, SchemefulSite(kTestUrl));
272   EXPECT_EQ(session_key.id.value(), kSessionId);
273 }
274 
TEST_F(SessionServiceImplTest,AccessObserverCalledOnDeferral)275 TEST_F(SessionServiceImplTest, AccessObserverCalledOnDeferral) {
276   ScopedTestFetcher scoped_test_fetcher(kSessionId);
277   auto fetch_param = RegistrationFetcherParam::CreateInstanceForTesting(
278       kTestUrl, {crypto::SignatureVerifier::SignatureAlgorithm::ECDSA_SHA256},
279       "challenge", /*authorization=*/std::nullopt);
280   net::TestDelegate delegate;
281   std::unique_ptr<URLRequest> request =
282       context()->CreateRequest(kTestUrl, IDLE, &delegate, kDummyAnnotation);
283   service().RegisterBoundSession(base::DoNothing(), std::move(fetch_param),
284                                  IsolationInfo::CreateTransient());
285 
286   // The request needs to be samesite for it to be considered
287   // candidate for deferral.
288   request->set_site_for_cookies(SiteForCookies::FromUrl(kTestUrl));
289 
290   base::test::TestFuture<SessionKey> future;
291   request->SetDeviceBoundSessionAccessCallback(
292       future.GetRepeatingCallback<const SessionKey&>());
293   service().GetAnySessionRequiringDeferral(request.get());
294 
295   SessionKey session_key = future.Take();
296   EXPECT_EQ(session_key.site, SchemefulSite(kTestUrl));
297   EXPECT_EQ(session_key.id.value(), kSessionId);
298 }
299 
TEST_F(SessionServiceImplTest,AccessObserverCalledOnSetChallenge)300 TEST_F(SessionServiceImplTest, AccessObserverCalledOnSetChallenge) {
301   ScopedTestFetcher scoped_test_fetcher(kSessionId);
302 
303   auto fetch_param = RegistrationFetcherParam::CreateInstanceForTesting(
304       kTestUrl, {crypto::SignatureVerifier::SignatureAlgorithm::ECDSA_SHA256},
305       "challenge", /*authorization=*/std::nullopt);
306   service().RegisterBoundSession(base::DoNothing(), std::move(fetch_param),
307                                  IsolationInfo::CreateTransient());
308 
309   scoped_refptr<net::HttpResponseHeaders> headers =
310       HttpResponseHeaders::Builder({1, 1}, "200 OK").Build();
311   headers->AddHeader("Sec-Session-Challenge", "\"challenge\";id=\"SessionId\"");
312 
313   std::vector<SessionChallengeParam> params =
314       SessionChallengeParam::CreateIfValid(kTestUrl, headers.get());
315   ASSERT_EQ(params.size(), 1U);
316 
317   base::test::TestFuture<SessionKey> future;
318   service().SetChallengeForBoundSession(
319       future.GetRepeatingCallback<const SessionKey&>(), kTestUrl, params[0]);
320 
321   SessionKey session_key = future.Take();
322   EXPECT_EQ(session_key.site, SchemefulSite(kTestUrl));
323   EXPECT_EQ(session_key.id.value(), kSessionId);
324 }
325 
TEST_F(SessionServiceImplTest,ReferralSessionIdentifier)326 TEST_F(SessionServiceImplTest, ReferralSessionIdentifier) {
327   // Register a session with Id `kSessionId`.
328   {
329     ScopedTestFetcher scoped_test_fetcher(kSessionId);
330 
331     auto fetch_param = RegistrationFetcherParam::CreateInstanceForTesting(
332         kTestUrl, {crypto::SignatureVerifier::SignatureAlgorithm::ECDSA_SHA256},
333         "challenge", /*authorization=*/std::nullopt);
334     service().RegisterBoundSession(base::DoNothing(), std::move(fetch_param),
335                                    IsolationInfo::CreateTransient());
336   }
337 
338   auto site = SchemefulSite(kTestUrl);
339   ASSERT_TRUE(service().GetSessionForTesting(site, kSessionId));
340 
341   // Register a session with new Id to replace the former session.
342   std::string session_id("NewSessionId");
343   {
344     ScopedTestFetcher scoped_test_fetcher(session_id, kSessionId);
345 
346     auto fetch_param = RegistrationFetcherParam::CreateInstanceForTesting(
347         kTestUrl, {crypto::SignatureVerifier::SignatureAlgorithm::ECDSA_SHA256},
348         "challenge", /*authorization=*/std::nullopt);
349     service().RegisterBoundSession(base::DoNothing(), std::move(fetch_param),
350                                    IsolationInfo::CreateTransient());
351   }
352 
353   ASSERT_TRUE(service().GetSessionForTesting(site, session_id));
354   ASSERT_FALSE(service().GetSessionForTesting(site, kSessionId));
355 }
356 
TEST_F(SessionServiceImplTest,GetAllSessions)357 TEST_F(SessionServiceImplTest, GetAllSessions) {
358   const std::string session_id_1("SessionId");
359   {
360     ScopedTestFetcher scoped_test_fetcher(session_id_1);
361 
362     auto fetch_param = RegistrationFetcherParam::CreateInstanceForTesting(
363         kTestUrl, {crypto::SignatureVerifier::SignatureAlgorithm::ECDSA_SHA256},
364         "challenge", /*authorization=*/std::nullopt);
365     service().RegisterBoundSession(base::DoNothing(), std::move(fetch_param),
366                                    IsolationInfo::CreateTransient());
367   }
368 
369   const std::string session_id_2("SessionId2");
370   {
371     ScopedTestFetcher scoped_test_fetcher(session_id_2);
372 
373     auto fetch_param = RegistrationFetcherParam::CreateInstanceForTesting(
374         kTestUrl, {crypto::SignatureVerifier::SignatureAlgorithm::ECDSA_SHA256},
375         "challenge", /*authorization=*/std::nullopt);
376     service().RegisterBoundSession(base::DoNothing(), std::move(fetch_param),
377                                    IsolationInfo::CreateTransient());
378   }
379 
380   base::test::TestFuture<std::vector<SessionKey>> future;
381   service().GetAllSessionsAsync(
382       future.GetCallback<const std::vector<SessionKey>&>());
383   EXPECT_THAT(future.Take(), UnorderedElementsAre(ExpectId(session_id_1),
384                                                   ExpectId(session_id_2)));
385 }
386 
TEST_F(SessionServiceImplTest,DeleteSession)387 TEST_F(SessionServiceImplTest, DeleteSession) {
388   ScopedTestFetcher scoped_test_fetcher(kSessionId);
389 
390   auto fetch_param = RegistrationFetcherParam::CreateInstanceForTesting(
391       kTestUrl, {crypto::SignatureVerifier::SignatureAlgorithm::ECDSA_SHA256},
392       "challenge", /*authorization=*/std::nullopt);
393   service().RegisterBoundSession(base::DoNothing(), std::move(fetch_param),
394                                  IsolationInfo::CreateTransient());
395 
396   ASSERT_TRUE(
397       service().GetSessionForTesting(SchemefulSite(kTestUrl), kSessionId));
398 
399   service().DeleteSession(SchemefulSite(kTestUrl), Session::Id(kSessionId));
400 
401   EXPECT_FALSE(
402       service().GetSessionForTesting(SchemefulSite(kTestUrl), kSessionId));
403 }
404 
405 }  // namespace
406 
407 class SessionServiceImplWithStoreTest : public TestWithTaskEnvironment {
408  public:
SessionServiceImplWithStoreTest()409   SessionServiceImplWithStoreTest()
410       : context_(CreateTestURLRequestContextBuilder()->Build()),
411         store_(std::make_unique<StrictMock<SessionStoreMock>>()),
412         service_(*UnexportableKeyServiceFactory::GetInstance()->GetShared(),
413                  context_.get(),
414                  store_.get()) {}
415 
service()416   SessionServiceImpl& service() { return service_; }
store()417   StrictMock<SessionStoreMock>& store() { return *store_; }
418 
OnSessionsLoaded()419   void OnSessionsLoaded() {
420     service().OnLoadSessionsComplete(SessionStore::SessionsMap());
421   }
422 
FinishLoadingSessions(SessionStore::SessionsMap loaded_sessions)423   void FinishLoadingSessions(SessionStore::SessionsMap loaded_sessions) {
424     service().OnLoadSessionsComplete(std::move(loaded_sessions));
425   }
426 
GetSiteSessionsCount(const SchemefulSite & site)427   size_t GetSiteSessionsCount(const SchemefulSite& site) {
428     auto [begin, end] = service().GetSessionsForSite(site);
429     return std::distance(begin, end);
430   }
431 
432  private:
433   crypto::ScopedMockUnexportableKeyProvider scoped_mock_key_provider_;
434   std::unique_ptr<URLRequestContext> context_;
435   std::unique_ptr<StrictMock<SessionStoreMock>> store_;
436   SessionServiceImpl service_;
437 };
438 
TEST_F(SessionServiceImplWithStoreTest,UsesSessionStore)439 TEST_F(SessionServiceImplWithStoreTest, UsesSessionStore) {
440   {
441     InSequence seq;
442     EXPECT_CALL(store(), LoadSessions)
443         .Times(1)
444         .WillOnce(
445             Invoke(this, &SessionServiceImplWithStoreTest::OnSessionsLoaded));
446     EXPECT_CALL(store(), SaveSession).Times(1);
447     EXPECT_CALL(store(), DeleteSession).Times(1);
448   }
449 
450   // Will invoke the store's load session method.
451   service().LoadSessionsAsync();
452 
453   ScopedTestFetcher scoped_test_fetcher(kSessionId);
454   auto fetch_param = RegistrationFetcherParam::CreateInstanceForTesting(
455       kTestUrl, {crypto::SignatureVerifier::SignatureAlgorithm::ECDSA_SHA256},
456       "challenge", /*authorization=*/std::nullopt);
457   // Will invoke the store's save session method.
458   service().RegisterBoundSession(base::DoNothing(), std::move(fetch_param),
459                                  IsolationInfo::CreateTransient());
460 
461   auto site = SchemefulSite(kTestUrl);
462   Session* session = service().GetSessionForTesting(site, kSessionId);
463   ASSERT_TRUE(session);
464   EXPECT_EQ(GetSiteSessionsCount(site), 1u);
465   session->set_expiry_date(base::Time::Now() - base::Days(1));
466   // Will invoke the store's delete session method.
467   EXPECT_EQ(GetSiteSessionsCount(site), 0u);
468 }
469 
TEST_F(SessionServiceImplWithStoreTest,GetAllSessionsWaitsForSessionsToLoad)470 TEST_F(SessionServiceImplWithStoreTest, GetAllSessionsWaitsForSessionsToLoad) {
471   // Start loading
472   EXPECT_CALL(store(), LoadSessions).Times(1);
473   service().LoadSessionsAsync();
474 
475   // Request sessions, which should wait until we finish loading.
476   base::test::TestFuture<std::vector<SessionKey>> future;
477   service().GetAllSessionsAsync(
478       future.GetCallback<const std::vector<SessionKey>&>());
479 
480   std::unique_ptr<Session> session = Session::CreateIfValid(
481       SessionParams("session_id", "https://example.com/refresh", /*scope=*/{},
482                     /*creds=*/{}),
483       kTestUrl);
484   ASSERT_TRUE(session);
485 
486   // Complete loading. If we did not defer, we'd miss this session.
487   SessionStore::SessionsMap session_map;
488   session_map.insert({SchemefulSite(kTestUrl), std::move(session)});
489   FinishLoadingSessions(std::move(session_map));
490 
491   // But we did defer, so we found it.
492   EXPECT_THAT(future.Take(), UnorderedElementsAre(ExpectId("session_id")));
493 }
494 
495 }  // namespace net::device_bound_sessions
496