// Copyright 2024 Google LLC
//
// 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 "tink/jwt/jwt_hmac_parameters.h"

#include <memory>
#include <tuple>

#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include "absl/status/status.h"
#include "absl/status/statusor.h"
#include "tink/parameters.h"
#include "tink/util/test_matchers.h"

namespace crypto {
namespace tink {
namespace {

using ::crypto::tink::test::IsOk;
using ::crypto::tink::test::StatusIs;
using ::testing::Combine;
using ::testing::Eq;
using ::testing::HasSubstr;
using ::testing::TestWithParam;
using ::testing::Values;

struct KidStrategyTuple {
  JwtHmacParameters::KidStrategy kid_strategy;
  bool allowed_kid_absent;
  bool has_id_requirement;
};

using JwtHmacParametersTest = TestWithParam<
    std::tuple<int, KidStrategyTuple, JwtHmacParameters::Algorithm>>;

INSTANTIATE_TEST_SUITE_P(
    JwtHmacParametersTestSuite, JwtHmacParametersTest,
    Combine(Values(16, 32),
            Values(
                KidStrategyTuple{
                    JwtHmacParameters::KidStrategy::kBase64EncodedKeyId,
                    /*allowed_kid_absent=*/false, /*has_id_requirement=*/true},
                KidStrategyTuple{JwtHmacParameters::KidStrategy::kCustom,
                                 /*allowed_kid_absent=*/true,
                                 /*has_id_requirement=*/false},
                KidStrategyTuple{JwtHmacParameters::KidStrategy::kIgnored,
                                 /*allowed_kid_absent=*/true,
                                 /*has_id_requirement=*/false}),
            Values(JwtHmacParameters::Algorithm::kHs256,
                   JwtHmacParameters::Algorithm::kHs384,
                   JwtHmacParameters::Algorithm::kHs512)));

TEST_P(JwtHmacParametersTest, Create) {
  int key_size_in_bytes;
  KidStrategyTuple tuple;
  JwtHmacParameters::Algorithm algorithm;
  std::tie(key_size_in_bytes, tuple, algorithm) = GetParam();

  absl::StatusOr<JwtHmacParameters> parameters = JwtHmacParameters::Create(
      key_size_in_bytes, tuple.kid_strategy, algorithm);
  ASSERT_THAT(parameters, IsOk());

  EXPECT_THAT(parameters->KeySizeInBytes(), Eq(key_size_in_bytes));
  EXPECT_THAT(parameters->GetKidStrategy(), Eq(tuple.kid_strategy));
  EXPECT_THAT(parameters->AllowKidAbsent(), Eq(tuple.allowed_kid_absent));
  EXPECT_THAT(parameters->HasIdRequirement(), Eq(tuple.has_id_requirement));
  EXPECT_THAT(parameters->GetAlgorithm(), Eq(algorithm));
}

TEST(JwtHmacParametersTest, CreateWithInvalidKidStrategyFails) {
  EXPECT_THAT(JwtHmacParameters::Create(
                  /*key_size_in_bytes=*/16,
                  JwtHmacParameters::KidStrategy::
                      kDoNotUseInsteadUseDefaultWhenWritingSwitchStatements,
                  JwtHmacParameters::Algorithm::kHs512)
                  .status(),
              StatusIs(absl::StatusCode::kInvalidArgument,
                       HasSubstr("unknown kid strategy")));
}

TEST(JwtHmacParametersTest, CreateWithInvalidAlgorithmFails) {
  EXPECT_THAT(JwtHmacParameters::Create(
                  /*key_size_in_bytes=*/16,
                  JwtHmacParameters::KidStrategy::kBase64EncodedKeyId,
                  JwtHmacParameters::Algorithm::
                      kDoNotUseInsteadUseDefaultWhenWritingSwitchStatements)
                  .status(),
              StatusIs(absl::StatusCode::kInvalidArgument,
                       HasSubstr("unknown algorithm")));
}

TEST(JwtHmacParametersTest, CreateWithInvalidKeySizeFails) {
  EXPECT_THAT(JwtHmacParameters::Create(
                  /*key_size_in_bytes=*/15,
                  JwtHmacParameters::KidStrategy::kBase64EncodedKeyId,
                  JwtHmacParameters::Algorithm::kHs512)
                  .status(),
              StatusIs(absl::StatusCode::kInvalidArgument,
                       HasSubstr("Key size should be at least 16 bytes")));
}

TEST(JwtHmacParametersTest, CopyConstructor) {
  absl::StatusOr<JwtHmacParameters> parameters = JwtHmacParameters::Create(
      /*key_size_in_bytes=*/16,
      JwtHmacParameters::KidStrategy::kBase64EncodedKeyId,
      JwtHmacParameters::Algorithm::kHs512);
  ASSERT_THAT(parameters, IsOk());

  JwtHmacParameters copy(*parameters);

  EXPECT_THAT(copy, Eq(*parameters));
}

TEST(JwtHmacParametersTest, CopyAssignment) {
  absl::StatusOr<JwtHmacParameters> parameters = JwtHmacParameters::Create(
      /*key_size_in_bytes=*/16,
      JwtHmacParameters::KidStrategy::kBase64EncodedKeyId,
      JwtHmacParameters::Algorithm::kHs512);
  ASSERT_THAT(parameters, IsOk());

  absl::StatusOr<JwtHmacParameters> other_parameters =
      JwtHmacParameters::Create(
          /*key_size_in_bytes=*/32, JwtHmacParameters::KidStrategy::kCustom,
          JwtHmacParameters::Algorithm::kHs256);
  ASSERT_THAT(other_parameters, IsOk());

  *other_parameters = *parameters;

  EXPECT_THAT(*other_parameters, Eq(*parameters));
}

TEST(JwtHmacParametersTest, MoveConstructor) {
  absl::StatusOr<JwtHmacParameters> parameters = JwtHmacParameters::Create(
      /*key_size_in_bytes=*/16,
      JwtHmacParameters::KidStrategy::kBase64EncodedKeyId,
      JwtHmacParameters::Algorithm::kHs512);
  ASSERT_THAT(parameters, IsOk());

  JwtHmacParameters copy(*parameters);
  JwtHmacParameters move(std::move(*parameters));

  EXPECT_THAT(move, Eq(copy));
}

TEST(JwtHmacParametersTest, MoveAssignment) {
  absl::StatusOr<JwtHmacParameters> parameters = JwtHmacParameters::Create(
      /*key_size_in_bytes=*/16,
      JwtHmacParameters::KidStrategy::kBase64EncodedKeyId,
      JwtHmacParameters::Algorithm::kHs512);
  ASSERT_THAT(parameters, IsOk());

  absl::StatusOr<JwtHmacParameters> other_parameters =
      JwtHmacParameters::Create(
          /*key_size_in_bytes=*/32, JwtHmacParameters::KidStrategy::kCustom,
          JwtHmacParameters::Algorithm::kHs256);
  ASSERT_THAT(other_parameters, IsOk());

  JwtHmacParameters copy(*parameters);
  *other_parameters = std::move(*parameters);

  EXPECT_THAT(*other_parameters, Eq(copy));
}

TEST_P(JwtHmacParametersTest, ParametersEquals) {
  int key_size_in_bytes;
  KidStrategyTuple tuple;
  JwtHmacParameters::Algorithm algorithm;
  std::tie(key_size_in_bytes, tuple, algorithm) = GetParam();

  absl::StatusOr<JwtHmacParameters> parameters = JwtHmacParameters::Create(
      key_size_in_bytes, tuple.kid_strategy, algorithm);
  ASSERT_THAT(parameters, IsOk());

  absl::StatusOr<JwtHmacParameters> other_parameters =
      JwtHmacParameters::Create(key_size_in_bytes, tuple.kid_strategy,
                                algorithm);
  ASSERT_THAT(other_parameters, IsOk());

  EXPECT_TRUE(*parameters == *other_parameters);
  EXPECT_TRUE(*other_parameters == *parameters);
  EXPECT_FALSE(*parameters != *other_parameters);
  EXPECT_FALSE(*other_parameters != *parameters);
}

TEST(JwtHmacParametersTest, KeySizeNotEqual) {
  absl::StatusOr<JwtHmacParameters> parameters = JwtHmacParameters::Create(
      /*key_size_in_bytes=*/16,
      JwtHmacParameters::KidStrategy::kBase64EncodedKeyId,
      JwtHmacParameters::Algorithm::kHs256);
  ASSERT_THAT(parameters, IsOk());

  absl::StatusOr<JwtHmacParameters> other_parameters =
      JwtHmacParameters::Create(
          /*key_size_in_bytes=*/32,
          JwtHmacParameters::KidStrategy::kBase64EncodedKeyId,
          JwtHmacParameters::Algorithm::kHs256);
  ASSERT_THAT(other_parameters, IsOk());

  EXPECT_TRUE(*parameters != *other_parameters);
  EXPECT_FALSE(*parameters == *other_parameters);
}

TEST(JwtHmacParametersTest, KidStrategyNotEqual) {
  absl::StatusOr<JwtHmacParameters> parameters = JwtHmacParameters::Create(
      /*key_size_in_bytes=*/16,
      JwtHmacParameters::KidStrategy::kBase64EncodedKeyId,
      JwtHmacParameters::Algorithm::kHs256);
  ASSERT_THAT(parameters, IsOk());

  absl::StatusOr<JwtHmacParameters> other_parameters =
      JwtHmacParameters::Create(
          /*key_size_in_bytes=*/16, JwtHmacParameters::KidStrategy::kCustom,
          JwtHmacParameters::Algorithm::kHs256);
  ASSERT_THAT(other_parameters, IsOk());

  EXPECT_TRUE(*parameters != *other_parameters);
  EXPECT_FALSE(*parameters == *other_parameters);
}

TEST(JwtHmacParametersTest, AlgorithmNotEqual) {
  absl::StatusOr<JwtHmacParameters> parameters = JwtHmacParameters::Create(
      /*key_size_in_bytes=*/16,
      JwtHmacParameters::KidStrategy::kBase64EncodedKeyId,
      JwtHmacParameters::Algorithm::kHs256);
  ASSERT_THAT(parameters, IsOk());

  absl::StatusOr<JwtHmacParameters> other_parameters =
      JwtHmacParameters::Create(
          /*key_size_in_bytes=*/16,
          JwtHmacParameters::KidStrategy::kBase64EncodedKeyId,
          JwtHmacParameters::Algorithm::kHs384);
  ASSERT_THAT(other_parameters, IsOk());

  EXPECT_TRUE(*parameters != *other_parameters);
  EXPECT_FALSE(*parameters == *other_parameters);
}

TEST(JwtHmacParametersTest, Clone) {
  absl::StatusOr<JwtHmacParameters> parameters = JwtHmacParameters::Create(
      /*key_size_in_bytes=*/16,
      JwtHmacParameters::KidStrategy::kBase64EncodedKeyId,
      JwtHmacParameters::Algorithm::kHs256);
  ASSERT_THAT(parameters, IsOk());

  std::unique_ptr<Parameters> cloned_parameters = parameters->Clone();
  ASSERT_THAT(*cloned_parameters, Eq(*parameters));
}

}  // namespace
}  // namespace tink
}  // namespace crypto
