// Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "components/password_manager/core/browser/credential_cache.h"

#include <memory>
#include <string>

#include "base/strings/string_piece.h"
#include "base/strings/utf_string_conversions.h"
#include "components/autofill/core/common/password_form.h"
#include "components/password_manager/core/browser/origin_credential_store.h"
#include "components/password_manager/core/browser/password_manager_test_utils.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"

namespace password_manager {

namespace {

using autofill::PasswordForm;
using url::Origin;

using IsPublicSuffixMatch = UiCredential::IsPublicSuffixMatch;
using IsAffiliationBasedMatch = UiCredential::IsAffiliationBasedMatch;
using IsOriginBlacklisted = CredentialCache::IsOriginBlacklisted;

constexpr char kExampleSite[] = "https://example.com/";
constexpr char kExampleSite2[] = "https://example.two.com/";
constexpr char kExampleSiteMobile[] = "https://m.example.com/";
constexpr char kExampleSiteSubdomain[] = "https://accounts.example.com/";

UiCredential MakeUiCredential(
    base::StringPiece username,
    base::StringPiece password,
    base::StringPiece origin = kExampleSite,
    IsPublicSuffixMatch is_public_suffix_match = IsPublicSuffixMatch(false),
    IsAffiliationBasedMatch is_affiliation_based_match =
        IsAffiliationBasedMatch(false)) {
  return UiCredential(base::UTF8ToUTF16(username), base::UTF8ToUTF16(password),
                      Origin::Create(GURL(origin)), is_public_suffix_match,
                      is_affiliation_based_match);
}

}  // namespace

class CredentialCacheTest : public testing::Test {
 public:
  CredentialCache* cache() { return &cache_; }

 private:
  CredentialCache cache_;
};

TEST_F(CredentialCacheTest, ReturnsSameStoreForSameOriginOnly) {
  EXPECT_EQ(&cache()->GetCredentialStore(Origin::Create(GURL(kExampleSite))),
            &cache()->GetCredentialStore(Origin::Create(GURL(kExampleSite))));

  EXPECT_NE(&cache()->GetCredentialStore(Origin::Create(GURL(kExampleSite))),
            &cache()->GetCredentialStore(Origin::Create(GURL(kExampleSite2))));
}

TEST_F(CredentialCacheTest, StoresCredentialsSortedByAplhabetAndOrigins) {
  Origin origin = Origin::Create(GURL(kExampleSite));
  cache()->SaveCredentialsAndBlacklistedForOrigin(
      {CreateEntry("Berta", "30948", GURL(kExampleSite), false, false).get(),
       CreateEntry("Adam", "Pas83B", GURL(kExampleSite), false, false).get(),
       CreateEntry("Dora", "PakudC", GURL(kExampleSite), false, false).get(),
       CreateEntry("Carl", "P1238C", GURL(kExampleSite), false, false).get(),
       // These entries need to be ordered but come after the examples above.
       CreateEntry("Cesar", "V3V1V", GURL(kExampleSite), false, true).get(),
       CreateEntry("Rolf", "A4nd0m", GURL(kExampleSiteMobile), true, false)
           .get(),
       CreateEntry("Greg", "5fnd1m", GURL(kExampleSiteSubdomain), true, false)
           .get(),
       CreateEntry("Elfi", "a65ddm", GURL(kExampleSiteSubdomain), true, false)
           .get(),
       CreateEntry("Alf", "R4nd50m", GURL(kExampleSiteMobile), true, false)
           .get()},
      IsOriginBlacklisted(false), origin);

  EXPECT_THAT(
      cache()->GetCredentialStore(origin).GetCredentials(),
      testing::ElementsAre(

          // Alphabetical entries of the exactly matching https://example.com:
          MakeUiCredential("Adam", "Pas83B"),
          MakeUiCredential("Berta", "30948"),
          MakeUiCredential("Carl", "P1238C"),
          // Affiliation based matches are first class citizens and should be
          // treated as a first-party credential.
          MakeUiCredential("Cesar", "V3V1V", kExampleSite,
                           IsPublicSuffixMatch(false),
                           IsAffiliationBasedMatch(true)),
          MakeUiCredential("Dora", "PakudC"),

          // Alphabetical entries of PSL-match https://accounts.example.com:
          MakeUiCredential("Elfi", "a65ddm", kExampleSiteSubdomain,
                           IsPublicSuffixMatch(true)),
          MakeUiCredential("Greg", "5fnd1m", kExampleSiteSubdomain,
                           IsPublicSuffixMatch(true)),

          // Alphabetical entries of PSL-match https://m.example.com:
          MakeUiCredential("Alf", "R4nd50m", kExampleSiteMobile,
                           IsPublicSuffixMatch(true)),
          MakeUiCredential("Rolf", "A4nd0m", kExampleSiteMobile,
                           IsPublicSuffixMatch(true))));
}

TEST_F(CredentialCacheTest, StoredCredentialsForIndependentOrigins) {
  Origin origin = Origin::Create(GURL(kExampleSite));
  Origin origin2 = Origin::Create(GURL(kExampleSite2));

  cache()->SaveCredentialsAndBlacklistedForOrigin(
      {CreateEntry("Ben", "S3cur3", GURL(kExampleSite), false, false).get()},
      IsOriginBlacklisted(false), origin);
  cache()->SaveCredentialsAndBlacklistedForOrigin(
      {CreateEntry("Abe", "B4dPW", GURL(kExampleSite2), false, false).get()},
      IsOriginBlacklisted(false), origin2);

  EXPECT_THAT(cache()->GetCredentialStore(origin).GetCredentials(),
              testing::ElementsAre(MakeUiCredential("Ben", "S3cur3")));
  EXPECT_THAT(
      cache()->GetCredentialStore(origin2).GetCredentials(),
      testing::ElementsAre(MakeUiCredential("Abe", "B4dPW", kExampleSite2)));
}

TEST_F(CredentialCacheTest, ClearsCredentials) {
  Origin origin = Origin::Create(GURL(kExampleSite));
  cache()->SaveCredentialsAndBlacklistedForOrigin(
      {CreateEntry("Ben", "S3cur3", GURL(kExampleSite), false, false).get()},
      IsOriginBlacklisted(false), Origin::Create(GURL(kExampleSite)));
  ASSERT_THAT(cache()->GetCredentialStore(origin).GetCredentials(),
              testing::ElementsAre(MakeUiCredential("Ben", "S3cur3")));

  cache()->ClearCredentials();
  EXPECT_EQ(cache()->GetCredentialStore(origin).GetCredentials().size(), 0u);
}

TEST_F(CredentialCacheTest, StoresBlacklistedWithCredentials) {
  Origin origin = Origin::Create(GURL(kExampleSite));
  cache()->SaveCredentialsAndBlacklistedForOrigin(
      {CreateEntry("Ben", "S3cur3", GURL(kExampleSite), false, false).get()},
      IsOriginBlacklisted(true), Origin::Create(GURL(kExampleSite)));
  EXPECT_EQ(OriginCredentialStore::BlacklistedStatus::kIsBlacklisted,
            cache()->GetCredentialStore(origin).GetBlacklistedStatus());
}

}  // namespace password_manager
