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

#include "components/sync_bookmarks/initial_account_bookmark_deduplicator.h"

#include <memory>
#include <string>
#include <vector>

#include "base/uuid.h"
#include "components/bookmarks/browser/bookmark_model.h"
#include "components/bookmarks/test/test_bookmark_client.h"
#include "components/sync_bookmarks/test_node_builders.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "url/gurl.h"

namespace sync_bookmarks {

namespace {

using sync_bookmarks::test::FolderBuilder;
using sync_bookmarks::test::UrlBuilder;
using testing::ElementsAre;
using testing::IsEmpty;
using testing::SizeIs;

class InitialAccountBookmarkDeduplicatorTest : public testing::Test {
 public:
  InitialAccountBookmarkDeduplicatorTest() {
    bookmark_model_ = bookmarks::TestBookmarkClient::CreateModel();
    // Enable account storage, which is where the deduplicator will look for
    // the canonical version of the data.
    bookmark_model_->CreateAccountPermanentFolders();
    bookmark_deduplicator_ =
        std::make_unique<InitialAccountBookmarkDeduplicator>(
            bookmark_model_.get());
  }

 protected:
  void AddLocalNodes(
      const std::vector<FolderBuilder::FolderOrUrl>& children_of_bookmark_bar) {
    FolderBuilder::AddChildrenTo(bookmark_model_.get(),
                                 bookmark_model_->bookmark_bar_node(),
                                 children_of_bookmark_bar);
  }

  void AddAccountNodes(
      const std::vector<FolderBuilder::FolderOrUrl>& children_of_bookmark_bar) {
    FolderBuilder::AddChildrenTo(bookmark_model_.get(),
                                 bookmark_model_->account_bookmark_bar_node(),
                                 children_of_bookmark_bar);
  }

  std::unique_ptr<bookmarks::BookmarkModel> bookmark_model_;
  std::unique_ptr<InitialAccountBookmarkDeduplicator> bookmark_deduplicator_;
};

TEST_F(InitialAccountBookmarkDeduplicatorTest, ShouldDoNothingIfNoDuplicates) {
  AddLocalNodes({UrlBuilder(u"Local", GURL("http://local.com"))});
  AddAccountNodes({UrlBuilder(u"Account", GURL("http://account.com"))});

  const bookmarks::BookmarkNode* local_bar =
      bookmark_model_->bookmark_bar_node();
  const bookmarks::BookmarkNode* account_bar =
      bookmark_model_->account_bookmark_bar_node();
  ASSERT_THAT(local_bar->children(), SizeIs(1));
  ASSERT_THAT(account_bar->children(), SizeIs(1));

  bookmark_deduplicator_->Deduplicate();

  // No changes should have been made.
  EXPECT_THAT(local_bar->children(), SizeIs(1));
  EXPECT_THAT(account_bar->children(), SizeIs(1));
}

TEST_F(InitialAccountBookmarkDeduplicatorTest, ShouldRemoveDuplicateUrl) {
  const base::Uuid uuid = base::Uuid::GenerateRandomV4();
  const GURL url("http://example.com");
  const std::u16string title = u"Title";

  AddLocalNodes({UrlBuilder(title, url).SetUuid(uuid)});
  AddAccountNodes({UrlBuilder(title, url).SetUuid(uuid)});

  const bookmarks::BookmarkNode* local_bar =
      bookmark_model_->bookmark_bar_node();
  const bookmarks::BookmarkNode* account_bar =
      bookmark_model_->account_bookmark_bar_node();
  ASSERT_THAT(local_bar->children(), SizeIs(1));
  ASSERT_THAT(account_bar->children(), SizeIs(1));

  bookmark_deduplicator_->Deduplicate();

  // The local bookmark should have been removed.
  EXPECT_THAT(local_bar->children(), IsEmpty());
  EXPECT_THAT(account_bar->children(), SizeIs(1));
}

TEST_F(InitialAccountBookmarkDeduplicatorTest, ShouldRemoveDuplicateFolder) {
  const base::Uuid folder_uuid = base::Uuid::GenerateRandomV4();
  const base::Uuid child_uuid = base::Uuid::GenerateRandomV4();
  const GURL child_url("http://child.com");
  const std::u16string folder_title = u"Folder";
  const std::u16string child_title = u"Child";

  AddLocalNodes(
      {FolderBuilder(folder_title)
           .SetUuid(folder_uuid)
           .SetChildren(
               {UrlBuilder(child_title, child_url).SetUuid(child_uuid)})});
  AddAccountNodes(
      {FolderBuilder(folder_title)
           .SetUuid(folder_uuid)
           .SetChildren(
               {UrlBuilder(child_title, child_url).SetUuid(child_uuid)})});

  const bookmarks::BookmarkNode* local_bar =
      bookmark_model_->bookmark_bar_node();
  const bookmarks::BookmarkNode* account_bar =
      bookmark_model_->account_bookmark_bar_node();
  ASSERT_THAT(local_bar->children(), SizeIs(1));
  ASSERT_THAT(account_bar->children(), SizeIs(1));
  ASSERT_THAT(local_bar->children()[0]->children(), SizeIs(1));
  ASSERT_THAT(account_bar->children()[0]->children(), SizeIs(1));

  bookmark_deduplicator_->Deduplicate();

  // The local folder and its contents should have been removed.
  EXPECT_THAT(local_bar->children(), IsEmpty());
  EXPECT_THAT(account_bar->children(), SizeIs(1));
}

TEST_F(InitialAccountBookmarkDeduplicatorTest,
       ShouldRemoveLocalFolderIfItIsASubgraphOfAccountFolder) {
  const base::Uuid folder_uuid = base::Uuid::GenerateRandomV4();
  const base::Uuid child_uuid = base::Uuid::GenerateRandomV4();
  const GURL child_url("http://child.com");
  const std::u16string folder_title = u"Folder";
  const std::u16string child_title = u"Child";

  AddLocalNodes(
      {FolderBuilder(folder_title)
           .SetUuid(folder_uuid)
           .SetChildren(
               {UrlBuilder(child_title, child_url).SetUuid(child_uuid)})});
  AddAccountNodes(
      {FolderBuilder(folder_title)
           .SetUuid(folder_uuid)
           .SetChildren({UrlBuilder(child_title, child_url).SetUuid(child_uuid),
                         UrlBuilder(u"Extra", GURL("http://extra.com"))})});

  const bookmarks::BookmarkNode* local_bar =
      bookmark_model_->bookmark_bar_node();
  const bookmarks::BookmarkNode* account_bar =
      bookmark_model_->account_bookmark_bar_node();
  ASSERT_THAT(local_bar->children(), SizeIs(1));
  ASSERT_THAT(account_bar->children(), SizeIs(1));
  ASSERT_THAT(local_bar->children()[0]->children(), SizeIs(1));
  ASSERT_THAT(account_bar->children()[0]->children(), SizeIs(2));

  bookmark_deduplicator_->Deduplicate();

  // The local folder should have been removed because it's a duplicate.
  EXPECT_THAT(local_bar->children(), IsEmpty());
  EXPECT_THAT(account_bar->children(), SizeIs(1));
  EXPECT_THAT(account_bar->children()[0]->children(), SizeIs(2));
}

TEST_F(InitialAccountBookmarkDeduplicatorTest,
       ShouldNotRemoveLocalFolderIfChildUrlDiffers) {
  const base::Uuid folder_uuid = base::Uuid::GenerateRandomV4();
  const base::Uuid child_uuid = base::Uuid::GenerateRandomV4();
  const std::u16string folder_title = u"Folder";
  const std::u16string child_title = u"Child";

  AddLocalNodes(
      {FolderBuilder(folder_title)
           .SetUuid(folder_uuid)
           .SetChildren({UrlBuilder(child_title, GURL("http://local-child.com"))
                             .SetUuid(child_uuid)})});
  AddAccountNodes(
      {FolderBuilder(folder_title)
           .SetUuid(folder_uuid)
           .SetChildren(
               {UrlBuilder(child_title, GURL("http://account-child.com"))
                    .SetUuid(child_uuid)})});

  const bookmarks::BookmarkNode* local_bar =
      bookmark_model_->bookmark_bar_node();
  const bookmarks::BookmarkNode* account_bar =
      bookmark_model_->account_bookmark_bar_node();
  ASSERT_THAT(local_bar->children(), SizeIs(1));
  ASSERT_THAT(account_bar->children(), SizeIs(1));

  bookmark_deduplicator_->Deduplicate();

  // The local folder should NOT have been removed.
  EXPECT_THAT(local_bar->children(), SizeIs(1));
  EXPECT_THAT(account_bar->children(), SizeIs(1));
}

TEST_F(InitialAccountBookmarkDeduplicatorTest,
       ShouldHandleVariousDuplicationScenarios) {
  const base::Uuid social_folder_uuid = base::Uuid::GenerateRandomV4();
  const base::Uuid twitter_uuid = base::Uuid::GenerateRandomV4();
  const base::Uuid linkedin_uuid = base::Uuid::GenerateRandomV4();
  const base::Uuid shopping_folder_uuid = base::Uuid::GenerateRandomV4();
  const base::Uuid amazon_uuid = base::Uuid::GenerateRandomV4();
  const base::Uuid ebay_uuid = base::Uuid::GenerateRandomV4();
  const base::Uuid news_folder_uuid = base::Uuid::GenerateRandomV4();
  const base::Uuid nyt_uuid = base::Uuid::GenerateRandomV4();
  const base::Uuid bbc_uuid = base::Uuid::GenerateRandomV4();
  const base::Uuid email_uuid = base::Uuid::GenerateRandomV4();

  // 1. Account Bookmarks
  // 🗂️ Bookmarks Bar (Account)
  // ├── 📁 Social
  // │   ├── 🔖 Twitter
  // │   └── 🔖 LinkedIn
  // ├── 📁 Shopping
  // │   ├── 🔖 Amazon
  // │   └── 🔖 eBay
  // ├── 📁 News
  // │   ├── 🔖 NYT
  // │   └── 🔖 BBC
  // └── 🔖 Email
  AddAccountNodes(
      {FolderBuilder(u"Social")
           .SetUuid(social_folder_uuid)
           .SetChildren({UrlBuilder(u"Twitter", GURL("http://twitter.com"))
                             .SetUuid(twitter_uuid),
                         UrlBuilder(u"LinkedIn", GURL("http://linkedin.com"))
                             .SetUuid(linkedin_uuid)}),
       FolderBuilder(u"Shopping")
           .SetUuid(shopping_folder_uuid)
           .SetChildren({UrlBuilder(u"Amazon", GURL("http://amazon.com"))
                             .SetUuid(amazon_uuid),
                         UrlBuilder(u"eBay", GURL("http://ebay.com"))
                             .SetUuid(ebay_uuid)}),
       FolderBuilder(u"News")
           .SetUuid(news_folder_uuid)
           .SetChildren(
               {UrlBuilder(u"NYT", GURL("http://nyt.com")).SetUuid(nyt_uuid),
                UrlBuilder(u"BBC", GURL("http://bbc.com")).SetUuid(bbc_uuid)}),
       UrlBuilder(u"Email", GURL("http://email.com")).SetUuid(email_uuid)});

  // 2. Local Bookmarks (BEFORE Deduplication)
  // 🗂️ Bookmarks Bar (Local)
  // ├── 📁 Social
  // │   ├── 🔖 LinkedIn
  // │   └── 🔖 Twitter
  // ├── 📁 Shopping
  // │   ├── 🔖 Amazon
  // │   ├── 🔖 eBay
  // │   └── 🔖 Etsy
  // ├── 📁 News
  // │   └── 🔖 NYT
  // ├── 📁 Finance
  // │   └── 🔖 Bank
  // ├── 🔖 Email
  // └── 🔖 Maps
  AddLocalNodes(
      {FolderBuilder(u"Social")
           .SetUuid(social_folder_uuid)
           .SetChildren({UrlBuilder(u"LinkedIn", GURL("http://linkedin.com"))
                             .SetUuid(linkedin_uuid),
                         UrlBuilder(u"Twitter", GURL("http://twitter.com"))
                             .SetUuid(twitter_uuid)}),
       FolderBuilder(u"Shopping")
           .SetUuid(shopping_folder_uuid)
           .SetChildren(
               {UrlBuilder(u"Amazon", GURL("http://amazon.com"))
                    .SetUuid(amazon_uuid),
                UrlBuilder(u"eBay", GURL("http://ebay.com")).SetUuid(ebay_uuid),
                UrlBuilder(u"Etsy", GURL("http://etsy.com"))}),
       FolderBuilder(u"News")
           .SetUuid(news_folder_uuid)
           .SetChildren(
               {UrlBuilder(u"NYT", GURL("http://nyt.com")).SetUuid(nyt_uuid)}),
       FolderBuilder(u"Finance")
           .SetChildren({UrlBuilder(u"Bank", GURL("http://bank.com"))}),
       UrlBuilder(u"Email", GURL("http://email.com")).SetUuid(email_uuid),
       UrlBuilder(u"Maps", GURL("http://maps.com"))});

  const bookmarks::BookmarkNode* account_bar =
      bookmark_model_->account_bookmark_bar_node();
  const bookmarks::BookmarkNode* local_bar =
      bookmark_model_->bookmark_bar_node();
  ASSERT_THAT(account_bar->children(), SizeIs(4));
  ASSERT_THAT(local_bar->children(), SizeIs(6));

  bookmark_deduplicator_->Deduplicate();

  // Social folder (local) is identical to account -> removed.
  // News folder (local) is a subset of account -> removed.
  // Email bookmark (local) is identical to account -> removed.
  // Shopping folder (local) is a superset of account -> kept.
  // Finance folder (local) is unique -> kept.
  // Maps bookmark (local) is unique -> kept.
  //
  // 3. Local Bookmarks (AFTER Deduplication)
  // 🗂️ Bookmarks Bar (Local)
  // ├── 📁 Shopping
  // │   ├── 🔖 Amazon
  // │   ├── 🔖 eBay
  // │   └── 🔖 Etsy
  // ├── 📁 Finance
  // │   └── 🔖 Bank
  // └── 🔖 Maps
  EXPECT_THAT(local_bar->children(), SizeIs(3));
  EXPECT_EQ(u"Shopping", local_bar->children()[0]->GetTitle());
  EXPECT_EQ(u"Finance", local_bar->children()[1]->GetTitle());
  EXPECT_EQ(u"Maps", local_bar->children()[2]->GetTitle());

  // Children of the kept local folders should be untouched.
  EXPECT_THAT(local_bar->children()[0]->children(), SizeIs(3));
  EXPECT_THAT(local_bar->children()[1]->children(), SizeIs(1));

  // Account bookmarks should be untouched.
  EXPECT_THAT(account_bar->children(), SizeIs(4));
}

TEST_F(InitialAccountBookmarkDeduplicatorTest,
       ShouldNotRemoveIfUuidMatchesButTypeIsDifferentUrlVsFolder) {
  const base::Uuid uuid = base::Uuid::GenerateRandomV4();
  const std::u16string title = u"Title";

  AddLocalNodes({UrlBuilder(title, GURL("http://example.com")).SetUuid(uuid)});
  AddAccountNodes({FolderBuilder(title).SetUuid(uuid)});

  const bookmarks::BookmarkNode* local_bar =
      bookmark_model_->bookmark_bar_node();
  const bookmarks::BookmarkNode* account_bar =
      bookmark_model_->account_bookmark_bar_node();
  ASSERT_THAT(local_bar->children(), SizeIs(1));
  ASSERT_THAT(account_bar->children(), SizeIs(1));

  bookmark_deduplicator_->Deduplicate();

  // No changes should have been made.
  EXPECT_THAT(local_bar->children(), SizeIs(1));
  EXPECT_THAT(account_bar->children(), SizeIs(1));
}

TEST_F(InitialAccountBookmarkDeduplicatorTest,
       ShouldNotRemoveIfUuidMatchesButTypeIsDifferentFolderVsUrl) {
  const base::Uuid uuid = base::Uuid::GenerateRandomV4();
  const std::u16string title = u"Title";

  AddLocalNodes({FolderBuilder(title).SetUuid(uuid)});
  AddAccountNodes(
      {UrlBuilder(title, GURL("http://example.com")).SetUuid(uuid)});

  const bookmarks::BookmarkNode* local_bar =
      bookmark_model_->bookmark_bar_node();
  const bookmarks::BookmarkNode* account_bar =
      bookmark_model_->account_bookmark_bar_node();
  ASSERT_THAT(local_bar->children(), SizeIs(1));
  ASSERT_THAT(account_bar->children(), SizeIs(1));

  bookmark_deduplicator_->Deduplicate();

  // No changes should have been made.
  EXPECT_THAT(local_bar->children(), SizeIs(1));
  EXPECT_THAT(account_bar->children(), SizeIs(1));
}

TEST_F(InitialAccountBookmarkDeduplicatorTest,
       ShouldNotRemoveIfUrlUuidMatchesButUrlIsDifferent) {
  const base::Uuid uuid = base::Uuid::GenerateRandomV4();
  const std::u16string title = u"Title";

  AddLocalNodes({UrlBuilder(title, GURL("http://local.com")).SetUuid(uuid)});
  AddAccountNodes(
      {UrlBuilder(title, GURL("http://account.com")).SetUuid(uuid)});

  const bookmarks::BookmarkNode* local_bar =
      bookmark_model_->bookmark_bar_node();
  const bookmarks::BookmarkNode* account_bar =
      bookmark_model_->account_bookmark_bar_node();
  ASSERT_THAT(local_bar->children(), SizeIs(1));
  ASSERT_THAT(account_bar->children(), SizeIs(1));

  bookmark_deduplicator_->Deduplicate();

  // No changes should have been made.
  EXPECT_THAT(local_bar->children(), SizeIs(1));
  EXPECT_THAT(account_bar->children(), SizeIs(1));
}

TEST_F(InitialAccountBookmarkDeduplicatorTest,
       ShouldNotRemoveIfFolderUuidMatchesButTitleIsDifferent) {
  const base::Uuid uuid = base::Uuid::GenerateRandomV4();

  AddLocalNodes({FolderBuilder(u"Local Title").SetUuid(uuid)});
  AddAccountNodes({FolderBuilder(u"Account Title").SetUuid(uuid)});

  const bookmarks::BookmarkNode* local_bar =
      bookmark_model_->bookmark_bar_node();
  const bookmarks::BookmarkNode* account_bar =
      bookmark_model_->account_bookmark_bar_node();
  ASSERT_THAT(local_bar->children(), SizeIs(1));
  ASSERT_THAT(account_bar->children(), SizeIs(1));

  bookmark_deduplicator_->Deduplicate();

  // No changes should have been made.
  EXPECT_THAT(local_bar->children(), SizeIs(1));
  EXPECT_THAT(account_bar->children(), SizeIs(1));
}

TEST_F(InitialAccountBookmarkDeduplicatorTest,
       ShouldRemoveDuplicateEmptyFolder) {
  const base::Uuid uuid = base::Uuid::GenerateRandomV4();
  const std::u16string title = u"Empty Folder";

  AddLocalNodes({FolderBuilder(title).SetUuid(uuid)});
  AddAccountNodes({FolderBuilder(title).SetUuid(uuid)});

  const bookmarks::BookmarkNode* local_bar =
      bookmark_model_->bookmark_bar_node();
  const bookmarks::BookmarkNode* account_bar =
      bookmark_model_->account_bookmark_bar_node();
  ASSERT_THAT(local_bar->children(), SizeIs(1));
  ASSERT_THAT(account_bar->children(), SizeIs(1));

  bookmark_deduplicator_->Deduplicate();

  // The local folder should have been removed.
  EXPECT_THAT(local_bar->children(), IsEmpty());
  EXPECT_THAT(account_bar->children(), SizeIs(1));
}

TEST_F(InitialAccountBookmarkDeduplicatorTest,
       ShouldRemoveDuplicatesUnderOtherAndMobileNodes) {
  const base::Uuid other_uuid = base::Uuid::GenerateRandomV4();
  const base::Uuid mobile_uuid = base::Uuid::GenerateRandomV4();
  const GURL url("http://example.com");
  const std::u16string title = u"Title";

  FolderBuilder::AddChildrenTo(bookmark_model_.get(),
                               bookmark_model_->other_node(),
                               {UrlBuilder(title, url).SetUuid(other_uuid)});
  FolderBuilder::AddChildrenTo(bookmark_model_.get(),
                               bookmark_model_->account_other_node(),
                               {UrlBuilder(title, url).SetUuid(other_uuid)});

  FolderBuilder::AddChildrenTo(bookmark_model_.get(),
                               bookmark_model_->mobile_node(),
                               {UrlBuilder(title, url).SetUuid(mobile_uuid)});
  FolderBuilder::AddChildrenTo(bookmark_model_.get(),
                               bookmark_model_->account_mobile_node(),
                               {UrlBuilder(title, url).SetUuid(mobile_uuid)});

  ASSERT_THAT(bookmark_model_->other_node()->children(), SizeIs(1));
  ASSERT_THAT(bookmark_model_->account_other_node()->children(), SizeIs(1));
  ASSERT_THAT(bookmark_model_->mobile_node()->children(), SizeIs(1));
  ASSERT_THAT(bookmark_model_->account_mobile_node()->children(), SizeIs(1));

  bookmark_deduplicator_->Deduplicate();

  // The local bookmarks should have been removed.
  EXPECT_THAT(bookmark_model_->other_node()->children(), IsEmpty());
  EXPECT_THAT(bookmark_model_->mobile_node()->children(), IsEmpty());
  EXPECT_THAT(bookmark_model_->account_other_node()->children(), SizeIs(1));
  EXPECT_THAT(bookmark_model_->account_mobile_node()->children(), SizeIs(1));
}

TEST_F(InitialAccountBookmarkDeduplicatorTest,
       ShouldNotRemoveIfAccountNodeHasDifferentParent) {
  const base::Uuid uuid = base::Uuid::GenerateRandomV4();
  const GURL url("http://example.com");
  const std::u16string title = u"Title";

  AddLocalNodes({UrlBuilder(title, url).SetUuid(uuid)});

  // Add the "duplicate" to a different folder in the account model.
  const bookmarks::BookmarkNode* account_folder = bookmark_model_->AddFolder(
      bookmark_model_->account_bookmark_bar_node(), 0, u"Some Folder");
  FolderBuilder::AddChildrenTo(bookmark_model_.get(), account_folder,
                               {UrlBuilder(title, url).SetUuid(uuid)});

  const bookmarks::BookmarkNode* local_bar =
      bookmark_model_->bookmark_bar_node();
  const bookmarks::BookmarkNode* account_bar =
      bookmark_model_->account_bookmark_bar_node();
  ASSERT_THAT(local_bar->children(), SizeIs(1));
  ASSERT_THAT(account_bar->children(), SizeIs(1));

  bookmark_deduplicator_->Deduplicate();

  // No changes should have been made because the parent doesn't match.
  EXPECT_THAT(local_bar->children(), SizeIs(1));
  EXPECT_THAT(account_bar->children(), SizeIs(1));
}

TEST_F(InitialAccountBookmarkDeduplicatorTest,
       DoesAccountSubgraphContainLocalSubgraphWithIdenticalUrls) {
  const base::Uuid uuid = base::Uuid::GenerateRandomV4();
  const GURL url("http://example.com");
  const std::u16string title = u"Title";

  AddLocalNodes({UrlBuilder(title, url).SetUuid(uuid)});
  AddAccountNodes({UrlBuilder(title, url).SetUuid(uuid)});

  ASSERT_THAT(bookmark_model_->bookmark_bar_node()->children(), SizeIs(1));
  ASSERT_THAT(bookmark_model_->account_bookmark_bar_node()->children(),
              SizeIs(1));

  const bookmarks::BookmarkNode* local_node =
      bookmark_model_->bookmark_bar_node()->children()[0].get();
  const bookmarks::BookmarkNode* account_node =
      bookmark_model_->account_bookmark_bar_node()->children()[0].get();

  EXPECT_TRUE(
      bookmark_deduplicator_->DoesAccountSubgraphContainLocalSubgraphForTest(
          local_node, account_node));
}

TEST_F(InitialAccountBookmarkDeduplicatorTest,
       DoesAccountSubgraphContainLocalSubgraphWithDifferentUrls) {
  const base::Uuid uuid = base::Uuid::GenerateRandomV4();
  const std::u16string title = u"Title";

  AddLocalNodes({UrlBuilder(title, GURL("http://local.com")).SetUuid(uuid)});
  AddAccountNodes(
      {UrlBuilder(title, GURL("http://account.com")).SetUuid(uuid)});

  ASSERT_THAT(bookmark_model_->bookmark_bar_node()->children(), SizeIs(1));
  ASSERT_THAT(bookmark_model_->account_bookmark_bar_node()->children(),
              SizeIs(1));

  const bookmarks::BookmarkNode* local_node =
      bookmark_model_->bookmark_bar_node()->children()[0].get();
  const bookmarks::BookmarkNode* account_node =
      bookmark_model_->account_bookmark_bar_node()->children()[0].get();

  EXPECT_FALSE(
      bookmark_deduplicator_->DoesAccountSubgraphContainLocalSubgraphForTest(
          local_node, account_node));
}

TEST_F(InitialAccountBookmarkDeduplicatorTest,
       DoesAccountSubgraphContainLocalSubgraphWithDifferentTypes) {
  const base::Uuid uuid = base::Uuid::GenerateRandomV4();
  const std::u16string title = u"Title";

  AddLocalNodes({UrlBuilder(title, GURL("http://example.com")).SetUuid(uuid)});
  AddAccountNodes({FolderBuilder(title).SetUuid(uuid)});

  ASSERT_THAT(bookmark_model_->bookmark_bar_node()->children(), SizeIs(1));
  ASSERT_THAT(bookmark_model_->account_bookmark_bar_node()->children(),
              SizeIs(1));

  const bookmarks::BookmarkNode* local_node =
      bookmark_model_->bookmark_bar_node()->children()[0].get();
  const bookmarks::BookmarkNode* account_node =
      bookmark_model_->account_bookmark_bar_node()->children()[0].get();

  EXPECT_FALSE(
      bookmark_deduplicator_->DoesAccountSubgraphContainLocalSubgraphForTest(
          local_node, account_node));
}

TEST_F(InitialAccountBookmarkDeduplicatorTest,
       DoesAccountSubgraphContainLocalSubgraphWithIdenticalFolders) {
  const base::Uuid folder_uuid = base::Uuid::GenerateRandomV4();
  const base::Uuid child_uuid = base::Uuid::GenerateRandomV4();
  const GURL child_url("http://child.com");
  const std::u16string folder_title = u"Folder";
  const std::u16string child_title = u"Child";

  AddLocalNodes(
      {FolderBuilder(folder_title)
           .SetUuid(folder_uuid)
           .SetChildren(
               {UrlBuilder(child_title, child_url).SetUuid(child_uuid)})});
  AddAccountNodes(
      {FolderBuilder(folder_title)
           .SetUuid(folder_uuid)
           .SetChildren(
               {UrlBuilder(child_title, child_url).SetUuid(child_uuid)})});

  ASSERT_THAT(bookmark_model_->bookmark_bar_node()->children(), SizeIs(1));
  ASSERT_THAT(bookmark_model_->account_bookmark_bar_node()->children(),
              SizeIs(1));

  const bookmarks::BookmarkNode* local_node =
      bookmark_model_->bookmark_bar_node()->children()[0].get();
  const bookmarks::BookmarkNode* account_node =
      bookmark_model_->account_bookmark_bar_node()->children()[0].get();

  EXPECT_TRUE(
      bookmark_deduplicator_->DoesAccountSubgraphContainLocalSubgraphForTest(
          local_node, account_node));
}

TEST_F(InitialAccountBookmarkDeduplicatorTest,
       DoesAccountSubgraphContainLocalSubgraphWithIdenticalEmptyFolders) {
  const base::Uuid folder_uuid = base::Uuid::GenerateRandomV4();
  const std::u16string folder_title = u"Folder";

  AddLocalNodes({FolderBuilder(folder_title).SetUuid(folder_uuid)});
  AddAccountNodes({FolderBuilder(folder_title).SetUuid(folder_uuid)});

  ASSERT_THAT(bookmark_model_->bookmark_bar_node()->children(), SizeIs(1));
  ASSERT_THAT(bookmark_model_->account_bookmark_bar_node()->children(),
              SizeIs(1));

  const bookmarks::BookmarkNode* local_node =
      bookmark_model_->bookmark_bar_node()->children()[0].get();
  const bookmarks::BookmarkNode* account_node =
      bookmark_model_->account_bookmark_bar_node()->children()[0].get();

  EXPECT_TRUE(
      bookmark_deduplicator_->DoesAccountSubgraphContainLocalSubgraphForTest(
          local_node, account_node));
}

TEST_F(InitialAccountBookmarkDeduplicatorTest,
       DoesAccountSubgraphContainLocalSubgraphWhenLocalIsSubgraph) {
  const base::Uuid folder_uuid = base::Uuid::GenerateRandomV4();
  const base::Uuid child_uuid = base::Uuid::GenerateRandomV4();
  const GURL child_url("http://child.com");
  const std::u16string folder_title = u"Folder";
  const std::u16string child_title = u"Child";

  AddLocalNodes(
      {FolderBuilder(folder_title)
           .SetUuid(folder_uuid)
           .SetChildren(
               {UrlBuilder(child_title, child_url).SetUuid(child_uuid)})});
  AddAccountNodes(
      {FolderBuilder(folder_title)
           .SetUuid(folder_uuid)
           .SetChildren({UrlBuilder(child_title, child_url).SetUuid(child_uuid),
                         UrlBuilder(u"Extra", GURL("http://extra.com"))})});

  ASSERT_THAT(bookmark_model_->bookmark_bar_node()->children(), SizeIs(1));
  ASSERT_THAT(bookmark_model_->account_bookmark_bar_node()->children(),
              SizeIs(1));

  const bookmarks::BookmarkNode* local_node =
      bookmark_model_->bookmark_bar_node()->children()[0].get();
  const bookmarks::BookmarkNode* account_node =
      bookmark_model_->account_bookmark_bar_node()->children()[0].get();

  EXPECT_TRUE(
      bookmark_deduplicator_->DoesAccountSubgraphContainLocalSubgraphForTest(
          local_node, account_node));
}

TEST_F(InitialAccountBookmarkDeduplicatorTest,
       DoesAccountSubgraphContainLocalSubgraphWhenAccountIsSubgraph) {
  const base::Uuid folder_uuid = base::Uuid::GenerateRandomV4();
  const base::Uuid child_uuid = base::Uuid::GenerateRandomV4();
  const GURL child_url("http://child.com");
  const std::u16string folder_title = u"Folder";
  const std::u16string child_title = u"Child";

  AddLocalNodes(
      {FolderBuilder(folder_title)
           .SetUuid(folder_uuid)
           .SetChildren({UrlBuilder(child_title, child_url).SetUuid(child_uuid),
                         UrlBuilder(u"Extra", GURL("http://extra.com"))})});
  AddAccountNodes(
      {FolderBuilder(folder_title)
           .SetUuid(folder_uuid)
           .SetChildren(
               {UrlBuilder(child_title, child_url).SetUuid(child_uuid)})});

  ASSERT_THAT(bookmark_model_->bookmark_bar_node()->children(), SizeIs(1));
  ASSERT_THAT(bookmark_model_->account_bookmark_bar_node()->children(),
              SizeIs(1));

  const bookmarks::BookmarkNode* local_node =
      bookmark_model_->bookmark_bar_node()->children()[0].get();
  const bookmarks::BookmarkNode* account_node =
      bookmark_model_->account_bookmark_bar_node()->children()[0].get();

  EXPECT_FALSE(
      bookmark_deduplicator_->DoesAccountSubgraphContainLocalSubgraphForTest(
          local_node, account_node));
}

TEST_F(InitialAccountBookmarkDeduplicatorTest,
       DoesAccountSubgraphContainLocalSubgraphWithDifferentFolderTitles) {
  const base::Uuid uuid = base::Uuid::GenerateRandomV4();

  AddLocalNodes({FolderBuilder(u"Local Title").SetUuid(uuid)});
  AddAccountNodes({FolderBuilder(u"Account Title").SetUuid(uuid)});

  ASSERT_THAT(bookmark_model_->bookmark_bar_node()->children(), SizeIs(1));
  ASSERT_THAT(bookmark_model_->account_bookmark_bar_node()->children(),
              SizeIs(1));

  const bookmarks::BookmarkNode* local_node =
      bookmark_model_->bookmark_bar_node()->children()[0].get();
  const bookmarks::BookmarkNode* account_node =
      bookmark_model_->account_bookmark_bar_node()->children()[0].get();

  EXPECT_FALSE(
      bookmark_deduplicator_->DoesAccountSubgraphContainLocalSubgraphForTest(
          local_node, account_node));
}

TEST_F(InitialAccountBookmarkDeduplicatorTest,
       DoesAccountSubgraphContainLocalSubgraphWithDifferentChildUuid) {
  const base::Uuid folder_uuid = base::Uuid::GenerateRandomV4();
  const GURL child_url("http://child.com");
  const std::u16string folder_title = u"Folder";
  const std::u16string child_title = u"Child";

  AddLocalNodes(
      {FolderBuilder(folder_title)
           .SetUuid(folder_uuid)
           .SetChildren({UrlBuilder(child_title, child_url)
                             .SetUuid(base::Uuid::GenerateRandomV4())})});
  AddAccountNodes(
      {FolderBuilder(folder_title)
           .SetUuid(folder_uuid)
           .SetChildren({UrlBuilder(child_title, child_url)
                             .SetUuid(base::Uuid::GenerateRandomV4())})});

  ASSERT_THAT(bookmark_model_->bookmark_bar_node()->children(), SizeIs(1));
  ASSERT_THAT(bookmark_model_->account_bookmark_bar_node()->children(),
              SizeIs(1));

  const bookmarks::BookmarkNode* local_node =
      bookmark_model_->bookmark_bar_node()->children()[0].get();
  const bookmarks::BookmarkNode* account_node =
      bookmark_model_->account_bookmark_bar_node()->children()[0].get();

  EXPECT_FALSE(
      bookmark_deduplicator_->DoesAccountSubgraphContainLocalSubgraphForTest(
          local_node, account_node));
}

}  // namespace
}  // namespace sync_bookmarks
