// Copyright 2021 Bloomberg Finance L.P
//
// 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 <buildboxcommon_protojsonutils.h>

#include <buildboxcommon_protos.h>

#include <gtest/gtest.h>

using namespace buildboxcommon;

TEST(ProtobufJsonUtilsTest, TestEmptyProtos)
{
    ProtoJsonUtils::JsonPrintOptions printOptions;

    ASSERT_NE(ProtoJsonUtils::protoToJsonString(Digest(), printOptions),
              nullptr);

    ASSERT_NE(ProtoJsonUtils::protoToJsonString(Action(), printOptions),
              nullptr);

    ASSERT_NE(ProtoJsonUtils::protoToJsonString(ActionResult(), printOptions),
              nullptr);

    ASSERT_NE(ProtoJsonUtils::protoToJsonString(Command(), printOptions),
              nullptr);

    ASSERT_NE(ProtoJsonUtils::protoToJsonString(ExecutedActionMetadata(),
                                                printOptions),
              nullptr);
}

TEST(ProtobufJsonUtilsTest, TestDigestToJson)
{
    Digest digest;
    digest.set_hash("hash");
    digest.set_size_bytes(123);

    const auto json = ProtoJsonUtils::protoToJsonString(
        digest, ProtoJsonUtils::JsonPrintOptions());
    ASSERT_NE(json, nullptr);
    ASSERT_EQ(*json, "{\"hash\":\"hash\",\"sizeBytes\":\"123\"}");
}

TEST(ProtobufJsonUtilsTest, TestExecutionStatisticsToJson)
{
    ExecutionStatistics::ProcessResourceUsage processUsage;
    processUsage.set_maxrss(1024);
    processUsage.mutable_utime()->set_seconds(10);
    processUsage.mutable_utime()->set_nanos(20);
    processUsage.mutable_stime()->set_seconds(1);
    processUsage.mutable_stime()->set_nanos(23);

    ExecutionStatistics executionStats;
    *executionStats.mutable_command_rusage() = processUsage;

    const auto json = ProtoJsonUtils::protoToJsonString(
        processUsage, ProtoJsonUtils::JsonPrintOptions());
    ASSERT_NE(json, nullptr);

    ASSERT_NE(json->find("\"maxrss\":\"1024\""), std::string::npos);
}

TEST(ProtobufJsonUtilsTest, TestExecutedActionAuxiliaryMetadataDigestToJson)
{
    ExecutedActionMetadata executedActionMetadata;
    executedActionMetadata.set_worker("worker1");

    Digest digest;
    digest.set_hash("metadata-hash");
    digest.set_size_bytes(12345);

    executedActionMetadata.add_auxiliary_metadata()->PackFrom(digest);
    // A `Digest` is considered a valid type for this field, it should be
    // included in the JSON.

    const auto json = ProtoJsonUtils::protoToJsonString(
        executedActionMetadata, ProtoJsonUtils::JsonPrintOptions());
    ASSERT_NE(json, nullptr);
    ASSERT_NE(json->find("auxiliaryMetadata"), std::string::npos);
    ASSERT_NE(json->find("\"hash\":\"metadata-hash\""), std::string::npos);
    ASSERT_NE(json->find("\"sizeBytes\":\"12345\""), std::string::npos);
}

TEST(ProtobufJsonUtilsTest,
     TestExecutedActionMetadataToJsonWithNonDigestAuxiliaryMetadata)
{
    ExecutedActionMetadata executedActionMetadata;
    executedActionMetadata.add_auxiliary_metadata()->PackFrom(Action());

    const auto json = ProtoJsonUtils::protoToJsonString(
        executedActionMetadata, ProtoJsonUtils::JsonPrintOptions());
    ASSERT_NE(json, nullptr);
    ASSERT_EQ(json->find("auxiliaryMetadata"), std::string::npos);
}

TEST(ProtobufJsonUtilsTest, TestActionResultValidAuxiliaryMetadata)
{
    ExecutedActionMetadata executedActionMetadata;
    Digest digest;
    digest.set_hash("metadata-hash");
    digest.set_size_bytes(12345);
    executedActionMetadata.add_auxiliary_metadata()->PackFrom(digest);
    // A `Digest` is considered a valid type for this field, it should be
    // included in the JSON.

    ActionResult actionResult;
    actionResult.mutable_execution_metadata()->CopyFrom(
        executedActionMetadata);

    const auto json = ProtoJsonUtils::protoToJsonString(
        actionResult, ProtoJsonUtils::JsonPrintOptions());
    ASSERT_NE(json, nullptr);

    ASSERT_NE(json->find("auxiliaryMetadata"), std::string::npos);
    ASSERT_NE(json->find("\"hash\":\"metadata-hash\""), std::string::npos);
    ASSERT_NE(json->find("\"sizeBytes\":\"12345\""), std::string::npos);
}

TEST(ProtobufJsonUtilsTest, TestActionResultInValidAuxiliaryMetadata)
{
    ExecutedActionMetadata executedActionMetadata;
    executedActionMetadata.add_auxiliary_metadata()->PackFrom(Action());

    ActionResult actionResult;
    *actionResult.mutable_execution_metadata() = executedActionMetadata;

    const auto json = ProtoJsonUtils::protoToJsonString(
        actionResult, ProtoJsonUtils::JsonPrintOptions());
    ASSERT_NE(json, nullptr);

    ASSERT_EQ(json->find("auxiliaryMetadata"), std::string::npos);
}

TEST(ProtobufJsonUtilsTest,
     TestActionResultAuxiliaryMetadataWithMultipleDigestEntries)
{
    ExecutedActionMetadata executedActionMetadata;
    Digest digest1;
    digest1.set_hash("metadata-hash");
    digest1.set_size_bytes(12345);

    Digest digest2;
    executedActionMetadata.add_auxiliary_metadata()->PackFrom(digest1);
    executedActionMetadata.add_auxiliary_metadata()->PackFrom(digest2);
    // At most a single `Digest` entry is considered a valid in this field.

    ActionResult actionResult;
    actionResult.mutable_execution_metadata()->CopyFrom(
        executedActionMetadata);

    const auto json = ProtoJsonUtils::protoToJsonString(
        actionResult, ProtoJsonUtils::JsonPrintOptions());
    ASSERT_NE(json, nullptr);

    ASSERT_EQ(json->find("auxiliaryMetadata"), std::string::npos);
}
