blob: ba88763afec72855fb3dcfb82840c59f8bda2438 [file] [log] [blame]
// Copyright 2016 The Fuchsia 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 "src/ledger/bin/cloud_sync/impl/batch_download.h"
#include <lib/async/cpp/task.h>
#include <lib/fit/function.h>
#include <map>
#include "gtest/gtest.h"
#include "src/ledger/bin/cloud_sync/impl/constants.h"
#include "src/ledger/bin/cloud_sync/impl/testing/test_page_cloud.h"
#include "src/ledger/bin/encryption/fake/fake_encryption_service.h"
#include "src/ledger/bin/storage/testing/page_storage_empty_impl.h"
#include "src/ledger/lib/convert/convert.h"
#include "src/ledger/lib/loop_fixture/test_loop_fixture.h"
#include "third_party/abseil-cpp/absl/strings/string_view.h"
namespace cloud_sync {
namespace {
// Creates a dummy continuation token.
std::unique_ptr<cloud_provider::PositionToken> MakeToken(convert::ExtendedStringView token_id) {
auto token = std::make_unique<cloud_provider::PositionToken>();
token->opaque_id = convert::ToArray(token_id);
return token;
}
// Fake implementation of storage::PageStorage. Injects the data that
// CommitUpload asks about: page id and unsynced objects to be uploaded.
// Registers the reported results of the upload: commits and objects marked as
// synced.
class TestPageStorage : public storage::PageStorageEmptyImpl {
public:
explicit TestPageStorage(async_dispatcher_t* dispatcher) : dispatcher_(dispatcher) {}
void AddCommitsFromSync(std::vector<storage::PageStorage::CommitIdAndBytes> ids_and_bytes,
storage::ChangeSource source,
fit::function<void(ledger::Status)> callback) override {
ASSERT_EQ(source, storage::ChangeSource::CLOUD);
if (should_fail_add_commit_from_sync) {
async::PostTask(dispatcher_,
[callback = std::move(callback)]() { callback(ledger::Status::IO_ERROR); });
return;
}
async::PostTask(dispatcher_, [this, ids_and_bytes = std::move(ids_and_bytes),
callback = std::move(callback)]() mutable {
for (auto& commit : ids_and_bytes) {
received_commits[std::move(commit.id)] = std::move(commit.bytes);
}
callback(ledger::Status::OK);
});
}
void SetSyncMetadata(absl::string_view key, absl::string_view value,
fit::function<void(ledger::Status)> callback) override {
sync_metadata[convert::ToString(key)] = convert::ToString(value);
async::PostTask(dispatcher_,
[callback = std::move(callback)]() { callback(ledger::Status::OK); });
}
bool should_fail_add_commit_from_sync = false;
std::map<storage::CommitId, std::string> received_commits;
std::map<std::string, std::string> sync_metadata;
private:
async_dispatcher_t* const dispatcher_;
};
class BatchDownloadTest : public ledger::TestLoopFixture {
public:
BatchDownloadTest() : storage_(dispatcher()), encryption_service_(dispatcher()) {}
BatchDownloadTest(const BatchDownloadTest&) = delete;
BatchDownloadTest& operator=(const BatchDownloadTest&) = delete;
~BatchDownloadTest() override = default;
protected:
TestPageStorage storage_;
encryption::FakeEncryptionService encryption_service_;
};
TEST_F(BatchDownloadTest, AddCommit) {
int done_calls = 0;
int error_calls = 0;
std::vector<cloud_provider::Commit> entries;
std::string value = "content1";
storage::CommitId id = storage::ComputeCommitId(value);
entries.push_back(MakeTestCommit(&encryption_service_, value));
BatchDownload batch_download(
&storage_, &encryption_service_, std::move(entries), MakeToken("42"),
[&done_calls] { done_calls++; }, [&error_calls] { error_calls++; });
batch_download.Start();
RunLoopUntilIdle();
EXPECT_EQ(done_calls, 1);
EXPECT_EQ(error_calls, 0);
EXPECT_EQ(storage_.received_commits.size(), 1u);
EXPECT_EQ(storage_.received_commits[id], value);
EXPECT_EQ(storage_.sync_metadata[convert::ToString(kTimestampKey)], "42");
}
TEST_F(BatchDownloadTest, AddMultipleCommits) {
int done_calls = 0;
int error_calls = 0;
std::vector<cloud_provider::Commit> entries;
std::string value1 = "content1";
storage::CommitId id1 = storage::ComputeCommitId(value1);
entries.push_back(MakeTestCommit(&encryption_service_, value1));
std::string value2 = "content2";
storage::CommitId id2 = storage::ComputeCommitId(value2);
entries.push_back(MakeTestCommit(&encryption_service_, value2));
BatchDownload batch_download(
&storage_, &encryption_service_, std::move(entries), MakeToken("43"),
[&done_calls] { done_calls++; }, [&error_calls] { error_calls++; });
batch_download.Start();
RunLoopUntilIdle();
EXPECT_EQ(done_calls, 1);
EXPECT_EQ(error_calls, 0);
EXPECT_EQ(storage_.received_commits.size(), 2u);
EXPECT_EQ(storage_.received_commits[id1], value1);
EXPECT_EQ(storage_.received_commits[id2], value2);
EXPECT_EQ(storage_.sync_metadata[convert::ToString(kTimestampKey)], "43");
}
TEST_F(BatchDownloadTest, FailToAddCommit) {
int done_calls = 0;
int error_calls = 0;
std::vector<cloud_provider::Commit> entries;
entries.push_back(MakeTestCommit(&encryption_service_, "content1"));
BatchDownload batch_download(
&storage_, &encryption_service_, std::move(entries), MakeToken("42"),
[&done_calls] { done_calls++; }, [&error_calls] { error_calls++; });
storage_.should_fail_add_commit_from_sync = true;
batch_download.Start();
RunLoopUntilIdle();
EXPECT_EQ(done_calls, 0);
EXPECT_EQ(error_calls, 1);
EXPECT_TRUE(storage_.received_commits.empty());
EXPECT_EQ(storage_.sync_metadata.count(convert::ToString(kTimestampKey)), 0u);
}
TEST_F(BatchDownloadTest, MissingId) {
int done_calls = 0;
int error_calls = 0;
std::vector<cloud_provider::Commit> entries;
// Upload a commit without id.
cloud_provider::Commit commit = MakeTestCommit(&encryption_service_, "content1");
commit.clear_id();
entries.push_back(std::move(commit));
BatchDownload batch_download(
&storage_, &encryption_service_, std::move(entries), MakeToken("42"),
[&done_calls] { done_calls++; }, [&error_calls] { error_calls++; });
batch_download.Start();
RunLoopUntilIdle();
EXPECT_EQ(done_calls, 0);
EXPECT_EQ(error_calls, 1);
EXPECT_TRUE(storage_.received_commits.empty());
EXPECT_EQ(storage_.sync_metadata.count(convert::ToString(kTimestampKey)), 0u);
}
TEST_F(BatchDownloadTest, MissingData) {
int done_calls = 0;
int error_calls = 0;
std::vector<cloud_provider::Commit> entries;
// Upload a commit without data.
cloud_provider::Commit commit = MakeTestCommit(&encryption_service_, "content1");
commit.clear_data();
entries.push_back(std::move(commit));
BatchDownload batch_download(
&storage_, &encryption_service_, std::move(entries), MakeToken("42"),
[&done_calls] { done_calls++; }, [&error_calls] { error_calls++; });
batch_download.Start();
RunLoopUntilIdle();
EXPECT_EQ(done_calls, 0);
EXPECT_EQ(error_calls, 1);
EXPECT_TRUE(storage_.received_commits.empty());
EXPECT_EQ(storage_.sync_metadata.count(convert::ToString(kTimestampKey)), 0u);
}
TEST_F(BatchDownloadTest, IdMismatch) {
int done_calls = 0;
int error_calls = 0;
std::vector<cloud_provider::Commit> entries;
// Upload a commit with an id which is not an encoded hash of the content.
cloud_provider::Commit commit = MakeTestCommit(&encryption_service_, "content1");
commit.set_id(convert::ToArray("id1"));
entries.push_back(std::move(commit));
BatchDownload batch_download(
&storage_, &encryption_service_, std::move(entries), MakeToken("42"),
[&done_calls] { done_calls++; }, [&error_calls] { error_calls++; });
batch_download.Start();
RunLoopUntilIdle();
EXPECT_EQ(done_calls, 0);
EXPECT_EQ(error_calls, 1);
EXPECT_TRUE(storage_.received_commits.empty());
EXPECT_EQ(storage_.sync_metadata.count(convert::ToString(kTimestampKey)), 0u);
}
} // namespace
} // namespace cloud_sync