/*
 * Copyright 2021 Bloomberg Finance LP
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include <buildboxcasd_fslocalactionstorage.h>

#include <buildboxcasd_proxymodefixture.h>
#include <buildboxcommon_digestgenerator.h>
#include <buildboxcommon_fileutils.h>
#include <buildboxcommon_logging.h>

#include <cstdlib>
#include <fstream>
#include <gtest/gtest.h>

using namespace buildboxcasd;
using namespace buildboxcommon;

namespace {
const auto digestFunctionInitializer = []() {
    buildboxcommon::DigestGenerator::init();
    return 0;
}();
} // anonymous namespace

class FsLocalActionStorageTest : public ::testing::Test {
  public:
    Digest digest_a;
    Digest digest_b;
    Digest digest_c;
    ActionResult actionresult_a;
    ActionResult actionresult_b;
    ActionResult read_actionresult_a;
    ActionResult read_actionresult_b;
    buildboxcommon::TemporaryDirectory dir;
    std::unique_ptr<FsLocalActionStorage> storage;

    FsLocalActionStorageTest()
        : dir("casd-tests-fs"),
          storage(std::make_unique<FsLocalActionStorage>(dir.strname()))
    {
        digest_a = DigestGenerator::hash("testa");
        digest_b = DigestGenerator::hash("testb");
        digest_c = DigestGenerator::hash("testc");
        actionresult_a.set_exit_code(static_cast<google::protobuf::int32>(8));
        actionresult_a.mutable_stdout_digest()->CopyFrom(digest_a);
        actionresult_b.set_exit_code(static_cast<google::protobuf::int32>(3));
        actionresult_b.mutable_stdout_digest()->CopyFrom(digest_b);
    }

    FsLocalActionStorageTest(const FsLocalActionStorageTest &other) = delete;
    FsLocalActionStorageTest(FsLocalActionStorageTest &&other) = delete;
    FsLocalActionStorageTest &
    operator=(const FsLocalActionStorageTest &other) = delete;
    FsLocalActionStorageTest &
    operator=(FsLocalActionStorageTest &&other) = delete;

    void truncateActionCacheEntry(const Digest &actionDigest)
    {
        const std::string &hash = actionDigest.hash();
        const std::string directoryName = hash.substr(0, 2);
        const std::string fileName = hash.substr(2);

        const std::string path = dir.strname() + "/actioncache/objects/" +
                                 directoryName + "/" + fileName;

        ASSERT_EQ(truncate(path.c_str(), 0), 0);
    }

  public:
    ~FsLocalActionStorageTest() override {}
};

TEST_F(FsLocalActionStorageTest, StoreAndReadAction)
{
    storage->storeAction(digest_a, actionresult_a);

    bool read = storage->readAction(digest_a, &read_actionresult_a);
    ASSERT_TRUE(read);
    ASSERT_TRUE(read_actionresult_a.exit_code() == actionresult_a.exit_code());
    ASSERT_TRUE(read_actionresult_a.stdout_digest().hash() ==
                actionresult_a.stdout_digest().hash());
}

TEST_F(FsLocalActionStorageTest, TruncatedActionResult)
{
    storage->storeAction(digest_a, actionresult_a);

    truncateActionCacheEntry(digest_a);

    bool read = storage->readAction(digest_a, &read_actionresult_a);
    ASSERT_FALSE(read);
}

TEST_F(FsLocalActionStorageTest, StoreSecondDigest)
{
    storage->storeAction(digest_a, actionresult_a);

    storage->storeAction(digest_b, actionresult_b);

    bool read = storage->readAction(digest_a, &read_actionresult_a);
    ASSERT_TRUE(read);
    ASSERT_TRUE(read_actionresult_a.exit_code() == actionresult_a.exit_code());
    ASSERT_TRUE(read_actionresult_a.stdout_digest().hash() ==
                actionresult_a.stdout_digest().hash());

    read = storage->readAction(digest_b, &read_actionresult_b);
    ASSERT_TRUE(read);
    ASSERT_TRUE(read_actionresult_b.exit_code() == actionresult_b.exit_code());
    ASSERT_TRUE(read_actionresult_b.stdout_digest().hash() ==
                actionresult_b.stdout_digest().hash());
}

TEST_F(FsLocalActionStorageTest, StoreOverwrite)
{
    storage->storeAction(digest_b, actionresult_b);

    storage->storeAction(digest_b, actionresult_a);

    bool read = storage->readAction(digest_b, &read_actionresult_a);
    ASSERT_TRUE(read);
    ASSERT_TRUE(read_actionresult_a.exit_code() == actionresult_a.exit_code());
    ASSERT_TRUE(read_actionresult_a.stdout_digest().hash() ==
                actionresult_a.stdout_digest().hash());
}

TEST_F(FsLocalActionStorageTest, ReadActionNoFile)
{
    bool read = storage->readAction(digest_c, &read_actionresult_a);
    ASSERT_FALSE(read);
}
