// Copyright 2018 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/password_requirements_service.h"

#include <map>

#include "base/logging.h"
#include "base/test/bind_test_util.h"
#include "components/autofill/core/browser/password_requirements_spec_fetcher.h"
#include "components/autofill/core/browser/proto/password_requirements.pb.h"
#include "testing/gtest/include/gtest/gtest.h"

namespace password_manager {

namespace {

class MockPasswordRequirementsSpecFetcher
    : public autofill::PasswordRequirementsSpecFetcher {
 public:
  MockPasswordRequirementsSpecFetcher() = default;
  ~MockPasswordRequirementsSpecFetcher() override = default;

  void Fetch(GURL origin, FetchCallback callback) override {
    auto iter = data_to_return_.find(origin);
    std::move(callback).Run(iter != data_to_return_.end()
                                ? iter->second
                                : autofill::PasswordRequirementsSpec());
  }

  void SetDataToReturn(const GURL& origin,
                       const autofill::PasswordRequirementsSpec& spec) {
    data_to_return_[origin] = spec;
  }

 private:
  std::map<GURL, autofill::PasswordRequirementsSpec> data_to_return_;
};

class PasswordRequirementsServiceTest : public testing::Test {
 public:
  PasswordRequirementsServiceTest()
      : test_origin_("http://www.example.com"),
        // Ownership is passed to the |service_| below.
        fetcher_ptr_(new MockPasswordRequirementsSpecFetcher()),
        service_(std::unique_ptr<MockPasswordRequirementsSpecFetcher>(
            fetcher_ptr_)) {}
  ~PasswordRequirementsServiceTest() override = default;

 protected:
  // Prepopulated test data.
  GURL test_origin_;
  autofill::FormSignature test_form_signature_ = 123;
  autofill::FieldSignature test_field_signature_ = 22;

  // Weak pointer.
  MockPasswordRequirementsSpecFetcher* fetcher_ptr_;
  PasswordRequirementsService service_;
};

TEST_F(PasswordRequirementsServiceTest, ExerciseEverything) {
  // The following specs are names according to the following scheme:
  // spec_l${max_length value}_p${priority value}
  // values of 0 imply that no value is specified.
  // It would be possible to test the behavior with fewer instances than below
  // but these are chosen to be representative of what we expect the server
  // to send with regards to priorities.
  autofill::PasswordRequirementsSpec spec_l0_p0;  // empty spec.
  autofill::PasswordRequirementsSpec spec_l7_p0;
  spec_l7_p0.set_max_length(7u);
  autofill::PasswordRequirementsSpec spec_l8_p10;
  spec_l8_p10.set_max_length(8u);
  spec_l8_p10.set_priority(10);
  autofill::PasswordRequirementsSpec spec_l9_p20;
  spec_l9_p20.set_max_length(9u);
  spec_l9_p20.set_priority(20);
  autofill::PasswordRequirementsSpec spec_l10_p30;
  spec_l10_p30.set_max_length(10u);
  spec_l10_p30.set_priority(30);

  struct {
    const char* test_name;
    autofill::PasswordRequirementsSpec* spec_for_signature = nullptr;
    autofill::PasswordRequirementsSpec* spec_for_domain = nullptr;
    autofill::PasswordRequirementsSpec* expected;
  } tests[] = {
      {
          .test_name = "No data prefechted", .expected = &spec_l0_p0,
      },
      {
          .test_name = "Only domain wide spec",
          .spec_for_domain = &spec_l7_p0,
          .expected = &spec_l7_p0,
      },
      {
          .test_name = "Only signature based spec",
          .spec_for_signature = &spec_l7_p0,
          .expected = &spec_l7_p0,
      },
      {
          .test_name = "Domain spec can override spec based on signature",
          .spec_for_signature = &spec_l8_p10,
          .spec_for_domain = &spec_l9_p20,
          .expected = &spec_l9_p20,  // priority 20 trumps priority 10.
      },
      {
          .test_name = "Signature spec can override spec based on domain",
          .spec_for_signature = &spec_l10_p30,
          .spec_for_domain = &spec_l9_p20,
          .expected = &spec_l10_p30,  // priority 30 trumps priority 20.
      },
      {
          .test_name = "Dealing with unset priority in domain",
          .spec_for_signature = &spec_l8_p10,
          .spec_for_domain = &spec_l7_p0,  // No prioritiy specified.
          .expected = &spec_l8_p10,
      },
      {
          .test_name = "Dealing with unset priority in signature",
          .spec_for_signature = &spec_l7_p0,  // No prioritiy specified.
          .spec_for_domain = &spec_l8_p10,
          .expected = &spec_l8_p10,
      },
  };

  for (const auto& test : tests) {
    SCOPED_TRACE(test.test_name);

    // Populate the service with data.
    if (test.spec_for_domain) {
      fetcher_ptr_->SetDataToReturn(test_origin_, *(test.spec_for_domain));
      service_.PrefetchSpec(test_origin_);
    }
    if (test.spec_for_signature) {
      service_.AddSpec(test_form_signature_, test_field_signature_,
                       *(test.spec_for_signature));
    }

    // Perform lookup.
    auto result = service_.GetSpec(test_origin_, test_form_signature_,
                                   test_field_signature_);

    // Validate answer.
    EXPECT_EQ(test.expected->has_priority(), result.has_priority());
    if (test.expected->has_priority()) {
      EXPECT_EQ(test.expected->priority(), result.priority());
    }

    EXPECT_EQ(test.expected->has_max_length(), result.has_max_length());
    if (test.expected->has_max_length()) {
      EXPECT_EQ(test.expected->max_length(), result.max_length());
    }
  }
}

}  // namespace

}  // namespace password_manager
