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

#include "net/dns/dns_task_results_manager.h"

#include <algorithm>
#include <memory>
#include <optional>
#include <string_view>

#include "base/check.h"
#include "base/functional/callback_forward.h"
#include "base/test/task_environment.h"
#include "base/time/time.h"
#include "net/base/connection_endpoint_metadata.h"
#include "net/base/net_errors.h"
#include "net/dns/host_resolver_internal_result.h"
#include "net/dns/host_resolver_results_test_util.h"
#include "net/dns/https_record_rdata.h"
#include "net/test/test_with_task_environment.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "url/gurl.h"
#include "url/scheme_host_port.h"

using ::testing::ElementsAre;
using ::testing::IsEmpty;
using ::testing::UnorderedElementsAre;

namespace net {

namespace {

class FakeDelegate : public DnsTaskResultsManager::Delegate {
 public:
  FakeDelegate() = default;
  ~FakeDelegate() override = default;

 private:
  void OnServiceEndpointsUpdated() override {
    // Do nothing for now.
  }
};

IPEndPoint MakeIPEndPoint(std::string_view ip_literal, uint16_t port = 0) {
  std::optional<IPAddress> ip = IPAddress::FromIPLiteral(std::move(ip_literal));
  return IPEndPoint(*ip, port);
}

std::unique_ptr<HostResolverInternalDataResult> CreateDataResult(
    std::string_view domain_name,
    std::vector<IPEndPoint> ip_endpoints,
    DnsQueryType query_type) {
  return std::make_unique<HostResolverInternalDataResult>(
      std::string(domain_name), query_type, /*expiration=*/base::TimeTicks(),
      /*timed_expiration=*/base::Time(),
      HostResolverInternalResult::Source::kDns, std::move(ip_endpoints),
      std::vector<std::string>(), std::vector<HostPortPair>());
}

std::unique_ptr<HostResolverInternalErrorResult> CreateNoData(
    std::string_view domain_name,
    DnsQueryType query_type) {
  return std::make_unique<HostResolverInternalErrorResult>(
      std::string(domain_name), query_type, /*expiration=*/base::TimeTicks(),
      /*timed_expiration=*/base::Time(),
      HostResolverInternalResult::Source::kDns, ERR_NAME_NOT_RESOLVED);
}

std::unique_ptr<HostResolverInternalMetadataResult> CreateMetadata(
    std::string_view domain_name,
    std::multimap<HttpsRecordPriority, ConnectionEndpointMetadata> metadatas) {
  return std::make_unique<HostResolverInternalMetadataResult>(
      std::string(domain_name), DnsQueryType::HTTPS,
      /*expiration=*/base::TimeTicks(), /*timed_expiration=*/base::Time(),
      HostResolverInternalResult::Source::kDns, std::move(metadatas));
}

std::unique_ptr<HostResolverInternalAliasResult> CreateAlias(
    std::string_view domain_name,
    DnsQueryType query_type,
    std::string_view alias_target) {
  return std::make_unique<HostResolverInternalAliasResult>(
      std::string(domain_name), query_type, /*expiration=*/base::TimeTicks(),
      /*timed_expiration=*/base::Time(),
      HostResolverInternalResult::Source::kDns, std::string(alias_target));
}

std::vector<IPEndPoint> WithPort(const std::vector<IPEndPoint>& endpoints,
                                 uint16_t port) {
  std::vector<IPEndPoint> out_endpoints;
  for (const auto& endpoint : endpoints) {
    out_endpoints.emplace_back(endpoint.address(), port);
  }
  return out_endpoints;
}

static constexpr std::string_view kHostName = "www.example.com";
static constexpr std::string_view kAliasTarget1 = "alias1.example.net";
static constexpr std::string_view kAliasTarget2 = "alias2.example.net";

static const ConnectionEndpointMetadata kMetadata1(
    /*supported_protocol_alpns=*/{"h3"},
    /*ech_config_list=*/{},
    std::string(kHostName));

static const ConnectionEndpointMetadata kMetadata2(
    /*supported_protocol_alpns=*/{"h2", "http/1.1"},
    /*ech_config_list=*/{},
    std::string(kHostName));

static const std::multimap<HttpsRecordPriority, ConnectionEndpointMetadata>
    kMetadatas{{1, kMetadata1}, {2, kMetadata2}};

// A helper class to create a DnsTaskResultsManager.
class ManagerFactory {
 public:
  explicit ManagerFactory(DnsTaskResultsManager::Delegate* delegate)
      : delegate_(delegate),
        host_(
            HostResolver::Host(url::SchemeHostPort("https", kHostName, 443))) {}

  std::unique_ptr<DnsTaskResultsManager> Create() {
    return std::make_unique<DnsTaskResultsManager>(
        delegate_, host_, query_types_, NetLogWithSource());
  }

  ManagerFactory& query_types(DnsQueryTypeSet query_types) {
    query_types_ = query_types;
    return *this;
  }

 private:
  raw_ptr<DnsTaskResultsManager::Delegate> delegate_;
  HostResolver::Host host_;
  DnsQueryTypeSet query_types_ = {DnsQueryType::A, DnsQueryType::AAAA,
                                  DnsQueryType::HTTPS};
};

}  // namespace

class DnsTaskResultsManagerTest : public TestWithTaskEnvironment {
 public:
  DnsTaskResultsManagerTest()
      : TestWithTaskEnvironment(
            base::test::TaskEnvironment::TimeSource::MOCK_TIME) {}

  void SetUp() override { delegate_ = std::make_unique<FakeDelegate>(); }

 protected:
  ManagerFactory factory() { return ManagerFactory(delegate_.get()); }

 private:
  std::unique_ptr<FakeDelegate> delegate_;
};

TEST_F(DnsTaskResultsManagerTest, IsMetadataReady) {
  // HTTPS RR is not queried.
  std::unique_ptr<DnsTaskResultsManager> manager =
      factory().query_types({DnsQueryType::A, DnsQueryType::AAAA}).Create();
  ASSERT_TRUE(manager->IsMetadataReady());

  // HTTPS RR is queried.
  manager = factory()
                .query_types(
                    {DnsQueryType::A, DnsQueryType::AAAA, DnsQueryType::HTTPS})
                .Create();
  ASSERT_FALSE(manager->IsMetadataReady());

  manager->ProcessDnsTransactionResults(DnsQueryType::HTTPS, /*results=*/{});
  ASSERT_TRUE(manager->IsMetadataReady());
}

TEST_F(DnsTaskResultsManagerTest, IPv6NotQueried) {
  std::unique_ptr<DnsTaskResultsManager> manager =
      factory().query_types({DnsQueryType::A, DnsQueryType::HTTPS}).Create();

  std::set<std::unique_ptr<HostResolverInternalResult>> results;
  results.insert(CreateDataResult(kHostName, {MakeIPEndPoint("192.0.2.1")},
                                  DnsQueryType::A));
  manager->ProcessDnsTransactionResults(DnsQueryType::A, results);

  EXPECT_THAT(manager->GetCurrentEndpoints(),
              ElementsAre(ExpectServiceEndpoint(
                  ElementsAre(MakeIPEndPoint("192.0.2.1", 443)))));
  EXPECT_THAT(manager->GetAliases(), UnorderedElementsAre(kHostName));
}

TEST_F(DnsTaskResultsManagerTest, IPv4First) {
  std::unique_ptr<DnsTaskResultsManager> manager = factory().Create();

  // A comes first. Service endpoints creation should be delayed.
  std::set<std::unique_ptr<HostResolverInternalResult>> results1;
  results1.insert(CreateDataResult(kHostName, {MakeIPEndPoint("192.0.2.1")},
                                   DnsQueryType::A));
  manager->ProcessDnsTransactionResults(DnsQueryType::A, results1);

  ASSERT_TRUE(manager->GetCurrentEndpoints().empty());

  // AAAA is responded. Service endpoints should be available.
  std::set<std::unique_ptr<HostResolverInternalResult>> results2;
  results2.insert(CreateDataResult(kHostName, {MakeIPEndPoint("2001:db8::1")},
                                   DnsQueryType::AAAA));
  manager->ProcessDnsTransactionResults(DnsQueryType::AAAA, results2);

  EXPECT_THAT(manager->GetCurrentEndpoints(),
              ElementsAre(ExpectServiceEndpoint(
                  ElementsAre(MakeIPEndPoint("192.0.2.1", 443)),
                  ElementsAre(MakeIPEndPoint("2001:db8::1", 443)))));
}

TEST_F(DnsTaskResultsManagerTest, IPv6First) {
  std::unique_ptr<DnsTaskResultsManager> manager = factory().Create();

  // AAAA comes first. Service endpoints should be available immediately.
  std::set<std::unique_ptr<HostResolverInternalResult>> results1;
  results1.insert(CreateDataResult(kHostName, {MakeIPEndPoint("2001:db8::1")},
                                   DnsQueryType::AAAA));
  manager->ProcessDnsTransactionResults(DnsQueryType::AAAA, results1);

  EXPECT_THAT(manager->GetCurrentEndpoints(),
              ElementsAre(ExpectServiceEndpoint(
                  IsEmpty(), ElementsAre(MakeIPEndPoint("2001:db8::1", 443)))));

  // A is responded. Service endpoints should be updated.
  std::set<std::unique_ptr<HostResolverInternalResult>> results;
  results.insert(CreateDataResult(
      kHostName, {MakeIPEndPoint("192.0.2.1"), MakeIPEndPoint("192.0.2.2")},
      DnsQueryType::A));
  manager->ProcessDnsTransactionResults(DnsQueryType::A, results);

  EXPECT_THAT(manager->GetCurrentEndpoints(),
              ElementsAre(ExpectServiceEndpoint(
                  ElementsAre(MakeIPEndPoint("192.0.2.1", 443),
                              MakeIPEndPoint("192.0.2.2", 443)),
                  ElementsAre(MakeIPEndPoint("2001:db8::1", 443)))));
}

TEST_F(DnsTaskResultsManagerTest, IPv6Timedout) {
  std::unique_ptr<DnsTaskResultsManager> manager = factory().Create();

  // A comes first. Service endpoints creation should be delayed.
  std::set<std::unique_ptr<HostResolverInternalResult>> results1;
  results1.insert(CreateDataResult(kHostName, {MakeIPEndPoint("192.0.2.1")},
                                   DnsQueryType::A));
  manager->ProcessDnsTransactionResults(DnsQueryType::A, results1);

  ASSERT_TRUE(manager->GetCurrentEndpoints().empty());

  // AAAA is timed out. Service endpoints should be available after timeout.
  FastForwardBy(DnsTaskResultsManager::kResolutionDelay +
                base::Milliseconds(1));

  EXPECT_THAT(manager->GetCurrentEndpoints(),
              ElementsAre(ExpectServiceEndpoint(
                  ElementsAre(MakeIPEndPoint("192.0.2.1", 443)))));

  // AAAA is responded after timeout. Service endpoints should be updated.
  std::set<std::unique_ptr<HostResolverInternalResult>> results2;
  results2.insert(CreateDataResult(kHostName, {MakeIPEndPoint("2001:db8::1")},
                                   DnsQueryType::AAAA));
  manager->ProcessDnsTransactionResults(DnsQueryType::AAAA, results2);

  EXPECT_THAT(manager->GetCurrentEndpoints(),
              ElementsAre(ExpectServiceEndpoint(
                  ElementsAre(MakeIPEndPoint("192.0.2.1", 443)),
                  ElementsAre(MakeIPEndPoint("2001:db8::1", 443)))));
}

TEST_F(DnsTaskResultsManagerTest, IPv6NoDataBeforeIPv4) {
  std::unique_ptr<DnsTaskResultsManager> manager = factory().Create();

  // AAAA is responded with no data. Service endpoints should not be available.
  std::set<std::unique_ptr<HostResolverInternalResult>> results1;
  results1.insert(CreateNoData(kHostName, DnsQueryType::AAAA));
  manager->ProcessDnsTransactionResults(DnsQueryType::AAAA, results1);

  ASSERT_TRUE(manager->GetCurrentEndpoints().empty());

  // A is responded. Service endpoints creation should happen without resolution
  // delay.
  std::set<std::unique_ptr<HostResolverInternalResult>> results2;
  results2.insert(CreateDataResult(kHostName, {MakeIPEndPoint("192.0.2.1")},
                                   DnsQueryType::A));
  manager->ProcessDnsTransactionResults(DnsQueryType::A, results2);

  EXPECT_THAT(manager->GetCurrentEndpoints(),
              ElementsAre(ExpectServiceEndpoint(
                  ElementsAre(MakeIPEndPoint("192.0.2.1", 443)))));
}

TEST_F(DnsTaskResultsManagerTest, IPv6NoDataAfterIPv4) {
  std::unique_ptr<DnsTaskResultsManager> manager = factory().Create();

  // A is responded. Service endpoints creation should be delayed.
  std::set<std::unique_ptr<HostResolverInternalResult>> results1;
  results1.insert(CreateDataResult(kHostName, {MakeIPEndPoint("192.0.2.1")},
                                   DnsQueryType::A));
  manager->ProcessDnsTransactionResults(DnsQueryType::A, results1);

  ASSERT_TRUE(manager->GetCurrentEndpoints().empty());

  // AAAA is responded with no data before the resolution delay timer. Service
  // endpoints should be available without waiting for the timeout.
  std::set<std::unique_ptr<HostResolverInternalResult>> results2;
  results2.insert(CreateNoData(kHostName, DnsQueryType::AAAA));
  manager->ProcessDnsTransactionResults(DnsQueryType::AAAA, results2);

  EXPECT_THAT(manager->GetCurrentEndpoints(),
              ElementsAre(ExpectServiceEndpoint(
                  ElementsAre(MakeIPEndPoint("192.0.2.1", 443)))));
}

TEST_F(DnsTaskResultsManagerTest, IPv6EmptyDataAfterIPv4) {
  std::unique_ptr<DnsTaskResultsManager> manager = factory().Create();

  // A is responded. Service endpoints creation should be delayed.
  std::set<std::unique_ptr<HostResolverInternalResult>> results1;
  results1.insert(CreateDataResult(kHostName, {MakeIPEndPoint("192.0.2.1")},
                                   DnsQueryType::A));
  manager->ProcessDnsTransactionResults(DnsQueryType::A, results1);

  ASSERT_TRUE(manager->GetCurrentEndpoints().empty());

  // AAAA is responded with a non-cacheable result (an empty result) before the
  // resolution delay timer. Service endpoints should be available without
  // waiting for the timeout.
  std::set<std::unique_ptr<HostResolverInternalResult>> results2;
  manager->ProcessDnsTransactionResults(DnsQueryType::AAAA, results2);

  EXPECT_THAT(manager->GetCurrentEndpoints(),
              ElementsAre(ExpectServiceEndpoint(
                  ElementsAre(MakeIPEndPoint("192.0.2.1", 443)))));
}

TEST_F(DnsTaskResultsManagerTest, IPv4AndIPv6NoData) {
  std::unique_ptr<DnsTaskResultsManager> manager = factory().Create();

  // AAAA is responded with no data. Service endpoints should not be available.
  std::set<std::unique_ptr<HostResolverInternalResult>> results1;
  results1.insert(CreateNoData(kHostName, DnsQueryType::AAAA));
  manager->ProcessDnsTransactionResults(DnsQueryType::AAAA, results1);

  ASSERT_TRUE(manager->GetCurrentEndpoints().empty());

  // A is responded with no data. Service endpoints should not be available.
  std::set<std::unique_ptr<HostResolverInternalResult>> results2;
  results2.insert(CreateNoData(kHostName, DnsQueryType::A));
  manager->ProcessDnsTransactionResults(DnsQueryType::A, results2);

  ASSERT_TRUE(manager->GetCurrentEndpoints().empty());
}

TEST_F(DnsTaskResultsManagerTest, IPv4NoDataIPv6AfterResolutionDelay) {
  std::unique_ptr<DnsTaskResultsManager> manager = factory().Create();

  // A comes first with no data. Service endpoints creation should be delayed
  // and the resolution delay timer should not start.
  std::set<std::unique_ptr<HostResolverInternalResult>> results1;
  results1.insert(CreateNoData(kHostName, DnsQueryType::A));
  manager->ProcessDnsTransactionResults(DnsQueryType::A, results1);

  ASSERT_FALSE(manager->IsResolutionDelayTimerRunningForTest());
  ASSERT_TRUE(manager->GetCurrentEndpoints().empty());

  // The resolution delay passed. Service endpoints should not be available yet.
  FastForwardBy(DnsTaskResultsManager::kResolutionDelay +
                base::Milliseconds(1));

  ASSERT_TRUE(manager->GetCurrentEndpoints().empty());

  // AAAA is responded. Service endpoints should be updated.
  std::set<std::unique_ptr<HostResolverInternalResult>> results2;
  results2.insert(CreateDataResult(kHostName, {MakeIPEndPoint("2001:db8::1")},
                                   DnsQueryType::AAAA));
  manager->ProcessDnsTransactionResults(DnsQueryType::AAAA, results2);

  EXPECT_THAT(manager->GetCurrentEndpoints(),
              ElementsAre(ExpectServiceEndpoint(
                  IsEmpty(), ElementsAre(MakeIPEndPoint("2001:db8::1", 443)))));
}

TEST_F(DnsTaskResultsManagerTest, MetadataFirst) {
  std::unique_ptr<DnsTaskResultsManager> manager = factory().Create();

  // HTTPS comes first. Service endpoints should not be available yet since
  // Chrome doesn't support ipv{4,6}hint yet.
  std::set<std::unique_ptr<HostResolverInternalResult>> results1;
  results1.insert(CreateMetadata(kHostName, kMetadatas));
  manager->ProcessDnsTransactionResults(DnsQueryType::HTTPS, results1);

  ASSERT_TRUE(manager->GetCurrentEndpoints().empty());
  ASSERT_TRUE(manager->IsMetadataReady());

  // A is responded. Service endpoints creation should be delayed.
  std::set<std::unique_ptr<HostResolverInternalResult>> results2;
  results2.insert(CreateDataResult(kHostName, {MakeIPEndPoint("192.0.2.1")},
                                   DnsQueryType::A));
  manager->ProcessDnsTransactionResults(DnsQueryType::A, results2);

  ASSERT_TRUE(manager->GetCurrentEndpoints().empty());

  // AAAA is responded. Service endpoints should be available with metadatas.
  std::set<std::unique_ptr<HostResolverInternalResult>> results3;
  results3.insert(CreateDataResult(kHostName, {MakeIPEndPoint("2001:db8::1")},
                                   DnsQueryType::AAAA));
  manager->ProcessDnsTransactionResults(DnsQueryType::AAAA, results3);

  EXPECT_THAT(
      manager->GetCurrentEndpoints(),
      ElementsAre(
          ExpectServiceEndpoint(ElementsAre(MakeIPEndPoint("192.0.2.1", 443)),
                                ElementsAre(MakeIPEndPoint("2001:db8::1", 443)),
                                kMetadata1),
          ExpectServiceEndpoint(ElementsAre(MakeIPEndPoint("192.0.2.1", 443)),
                                ElementsAre(MakeIPEndPoint("2001:db8::1", 443)),
                                kMetadata2)));
}

TEST_F(DnsTaskResultsManagerTest, MetadataDifferentTargetName) {
  std::unique_ptr<DnsTaskResultsManager> manager = factory().Create();

  // HTTPS is responded and the target name is different from QNAME.
  const ConnectionEndpointMetadata kMetadataDifferentTargetName(
      /*supported_protocol_alpns=*/{"h2", "http/1.1"},
      /*ech_config_list=*/{},
      /*target_name=*/"other.example.net.");
  std::set<std::unique_ptr<HostResolverInternalResult>> results1;
  results1.insert(
      CreateMetadata(kHostName, {{1, kMetadataDifferentTargetName}}));
  manager->ProcessDnsTransactionResults(DnsQueryType::HTTPS, results1);

  ASSERT_TRUE(manager->IsMetadataReady());

  // AAAA is responded. Service endpoints should be available without metadatas
  // since the target name is different.
  std::set<std::unique_ptr<HostResolverInternalResult>> results2;
  results2.insert(CreateDataResult(kHostName, {MakeIPEndPoint("2001:db8::1")},
                                   DnsQueryType::AAAA));
  manager->ProcessDnsTransactionResults(DnsQueryType::AAAA, results2);

  ASSERT_TRUE(manager->IsMetadataReady());
  EXPECT_THAT(manager->GetCurrentEndpoints(),
              ElementsAre(ExpectServiceEndpoint(
                  IsEmpty(), ElementsAre(MakeIPEndPoint("2001:db8::1", 443)))));
}

TEST_F(DnsTaskResultsManagerTest, MetadataAfterIPv6) {
  std::unique_ptr<DnsTaskResultsManager> manager = factory().Create();

  // AAAA comes first. Service endpoints should be available without metadatas.
  std::set<std::unique_ptr<HostResolverInternalResult>> results1;
  results1.insert(CreateDataResult(kHostName, {MakeIPEndPoint("2001:db8::1")},
                                   DnsQueryType::AAAA));
  manager->ProcessDnsTransactionResults(DnsQueryType::AAAA, results1);

  ASSERT_FALSE(manager->IsMetadataReady());
  EXPECT_THAT(manager->GetCurrentEndpoints(),
              ElementsAre(ExpectServiceEndpoint(
                  IsEmpty(), ElementsAre(MakeIPEndPoint("2001:db8::1", 443)))));

  // HTTPS is responded. Metadata should be available.
  std::set<std::unique_ptr<HostResolverInternalResult>> results2;
  results2.insert(CreateMetadata(kHostName, kMetadatas));
  manager->ProcessDnsTransactionResults(DnsQueryType::HTTPS, results2);

  ASSERT_TRUE(manager->IsMetadataReady());
  EXPECT_THAT(
      manager->GetCurrentEndpoints(),
      ElementsAre(
          ExpectServiceEndpoint(IsEmpty(),
                                ElementsAre(MakeIPEndPoint("2001:db8::1", 443)),
                                kMetadata1),
          ExpectServiceEndpoint(IsEmpty(),
                                ElementsAre(MakeIPEndPoint("2001:db8::1", 443)),
                                kMetadata2)));
}

TEST_F(DnsTaskResultsManagerTest, IPv6TimedoutAfterMetadata) {
  std::unique_ptr<DnsTaskResultsManager> manager = factory().Create();

  // A comes first. Service endpoints creation should be delayed.
  std::set<std::unique_ptr<HostResolverInternalResult>> results1;
  results1.insert(CreateDataResult(kHostName, {MakeIPEndPoint("192.0.2.1")},
                                   DnsQueryType::A));
  manager->ProcessDnsTransactionResults(DnsQueryType::A, results1);

  ASSERT_FALSE(manager->IsMetadataReady());
  ASSERT_TRUE(manager->GetCurrentEndpoints().empty());

  // HTTPS is responded. Service endpoints should not be available because
  // the manager is waiting for the resolution delay and Chrome doesn't support
  // ipv6hint yet.
  std::set<std::unique_ptr<HostResolverInternalResult>> results2;
  results2.insert(CreateMetadata(kHostName, kMetadatas));
  manager->ProcessDnsTransactionResults(DnsQueryType::HTTPS, results2);

  ASSERT_TRUE(manager->IsMetadataReady());
  ASSERT_TRUE(manager->GetCurrentEndpoints().empty());

  // AAAA is timed out. Service endpoints should be available with metadatas.
  FastForwardBy(DnsTaskResultsManager::kResolutionDelay +
                base::Milliseconds(1));

  ASSERT_TRUE(manager->IsMetadataReady());
  EXPECT_THAT(
      manager->GetCurrentEndpoints(),
      ElementsAre(
          ExpectServiceEndpoint(ElementsAre(MakeIPEndPoint("192.0.2.1", 443)),
                                IsEmpty(), kMetadata1),
          ExpectServiceEndpoint(ElementsAre(MakeIPEndPoint("192.0.2.1", 443)),
                                IsEmpty(), kMetadata2)));
}

TEST_F(DnsTaskResultsManagerTest, IPv4NoDataIPv6TimedoutAfterMetadata) {
  std::unique_ptr<DnsTaskResultsManager> manager = factory().Create();

  // HTTPS is responded. Service endpoints should not be available because
  // the manager is waiting for the resolution delay and Chrome doesn't support
  // address hints yet.
  std::set<std::unique_ptr<HostResolverInternalResult>> results1;
  results1.insert(CreateMetadata(kHostName, kMetadatas));
  manager->ProcessDnsTransactionResults(DnsQueryType::HTTPS, results1);

  ASSERT_TRUE(manager->IsMetadataReady());
  ASSERT_TRUE(manager->GetCurrentEndpoints().empty());

  // A is responded with no address. Service endpoints should not be available
  // since there are no addresses.
  std::set<std::unique_ptr<HostResolverInternalResult>> results2;
  results2.insert(CreateNoData(kHostName, DnsQueryType::A));
  manager->ProcessDnsTransactionResults(DnsQueryType::A, results2);

  ASSERT_TRUE(manager->GetCurrentEndpoints().empty());

  // AAAA is timed out. Service endpoints should not be available since there
  // are no addresses.
  FastForwardBy(DnsTaskResultsManager::kResolutionDelay +
                base::Milliseconds(1));

  ASSERT_TRUE(manager->GetCurrentEndpoints().empty());
}

TEST_F(DnsTaskResultsManagerTest, EndpointOrdering) {
  // Has both IPv4/v6 addresses and metadata.
  constexpr static const std::string_view kSvcbHost1 = "svc1.example.com";
  // Has both IPv4/v6 addresses but no metadata.
  constexpr static const std::string_view kSvcbHost2 = "svc2.example.com";
  // Only has IPv4 addresses.
  constexpr static const std::string_view kSvcbHost3 = "svc3.example.com";

  const std::vector<IPEndPoint> kSvcbHost1IPv4s = {MakeIPEndPoint("192.0.2.1")};
  const std::vector<IPEndPoint> kSvcbHost2IPv4s = {MakeIPEndPoint("192.0.2.2")};
  const std::vector<IPEndPoint> kSvcbHost3IPv4s = {MakeIPEndPoint("192.0.2.3")};

  const std::vector<IPEndPoint> kSvcbHost1IPv6s = {
      MakeIPEndPoint("2001:db8::1")};
  const std::vector<IPEndPoint> kSvcbHost2IPv6s = {
      MakeIPEndPoint("2001:db8::2")};

  const ConnectionEndpointMetadata kSvcbHost1Metadata1(
      /*supported_protocol_alpns=*/{"h2", "http/1.1"},
      /*ech_config_list=*/{},
      /*target_name=*/std::string(kSvcbHost1));
  const ConnectionEndpointMetadata kSvcbHost1Metadata2(
      /*supported_protocol_alpns=*/{"h3"},
      /*ech_config_list=*/{},
      /*target_name=*/std::string(kSvcbHost1));

  const std::multimap<HttpsRecordPriority, ConnectionEndpointMetadata>
      kSvcbHost1Metadatas{{1, kSvcbHost1Metadata1}, {2, kSvcbHost1Metadata2}};

  struct TestData {
    std::string_view host;
    std::vector<IPEndPoint> ipv4_endpoints;
    std::vector<IPEndPoint> ipv6_endpoints;
    std::multimap<HttpsRecordPriority, ConnectionEndpointMetadata> metadatas;
  };
  const TestData kTestDatas[] = {
      {kSvcbHost1,
       /*ipv4_addresses=*/kSvcbHost1IPv4s,
       /*ipv6_addresses=*/kSvcbHost1IPv6s,
       /*metadatas=*/kSvcbHost1Metadatas},
      {kSvcbHost2,
       /*ipv4_addresses=*/kSvcbHost2IPv4s,
       /*ipv6_addresses=*/kSvcbHost2IPv6s,
       /*metadatas=*/{}},
      {kSvcbHost3, /*ipv4_addresses=*/kSvcbHost3IPv4s,
       /*ipv6_addresses=*/{}, /*metadatas=*/{}},
  };

  std::unique_ptr<DnsTaskResultsManager> manager = factory().Create();

  for (const auto& testdata : kTestDatas) {
    if (!testdata.ipv4_endpoints.empty()) {
      std::set<std::unique_ptr<HostResolverInternalResult>> results;
      results.insert(CreateDataResult(testdata.host, testdata.ipv4_endpoints,
                                      DnsQueryType::A));
      manager->ProcessDnsTransactionResults(DnsQueryType::A, results);
    }
    if (!testdata.ipv6_endpoints.empty()) {
      std::set<std::unique_ptr<HostResolverInternalResult>> results;
      results.insert(CreateDataResult(testdata.host, testdata.ipv6_endpoints,
                                      DnsQueryType::AAAA));
      manager->ProcessDnsTransactionResults(DnsQueryType::AAAA, results);
    }
    if (!testdata.metadatas.empty()) {
      std::set<std::unique_ptr<HostResolverInternalResult>> results;
      results.insert(CreateMetadata(testdata.host, testdata.metadatas));
      manager->ProcessDnsTransactionResults(DnsQueryType::HTTPS, results);
    }
  }

  const std::vector<ServiceEndpoint> kExpects = {
      ServiceEndpoint(WithPort(kSvcbHost1IPv4s, 443),
                      WithPort(kSvcbHost1IPv6s, 443), kSvcbHost1Metadata1),
      ServiceEndpoint(WithPort(kSvcbHost1IPv4s, 443),
                      WithPort(kSvcbHost1IPv6s, 443), kSvcbHost1Metadata2),
      ServiceEndpoint(WithPort(kSvcbHost2IPv4s, 443),
                      WithPort(kSvcbHost2IPv6s, 443),
                      ConnectionEndpointMetadata()),
      ServiceEndpoint(WithPort(kSvcbHost3IPv4s, 443), {},
                      ConnectionEndpointMetadata()),
  };

  ASSERT_EQ(manager->GetCurrentEndpoints().size(), kExpects.size());
  for (size_t i = 0; i < manager->GetCurrentEndpoints().size(); ++i) {
    SCOPED_TRACE(i);
    EXPECT_THAT(manager->GetCurrentEndpoints()[i], kExpects[i]);
  }
}

TEST_F(DnsTaskResultsManagerTest, Aliases) {
  std::unique_ptr<DnsTaskResultsManager> manager = factory().Create();

  // AAAA is responded with aliases.
  std::set<std::unique_ptr<HostResolverInternalResult>> results1;
  results1.insert(CreateAlias(kHostName, DnsQueryType::AAAA, kAliasTarget1));
  results1.insert(
      CreateAlias(kAliasTarget1, DnsQueryType::AAAA, kAliasTarget2));
  results1.insert(CreateDataResult(kHostName, {MakeIPEndPoint("2001:db8::1")},
                                   DnsQueryType::AAAA));
  manager->ProcessDnsTransactionResults(DnsQueryType::AAAA, results1);

  EXPECT_THAT(manager->GetCurrentEndpoints(),
              ElementsAre(ExpectServiceEndpoint(
                  IsEmpty(), ElementsAre(MakeIPEndPoint("2001:db8::1", 443)))));
  EXPECT_THAT(manager->GetAliases(),
              UnorderedElementsAre(kHostName, kAliasTarget1, kAliasTarget2));
}

}  // namespace net
