blob: 9839bc5c7b211265784145cd7b4bf0c1749a9816 [file] [edit]
// Copyright 2026 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
//
// https://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 "bindings/input_hasher.h"
#include <memory>
#include <string>
#include <utility>
#include "absl/status/status.h"
#include "absl/status/status_matchers.h"
#include "absl/status/statusor.h"
#include "absl/strings/escaping.h"
#include "absl/strings/string_view.h"
#include "crypto/algorithms.h"
#include "crypto/default/hasher.h"
#include "crypto/hash.h"
#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include "riegeli/bytes/string_reader.h"
namespace credentio {
namespace {
using ::absl_testing::IsOk;
using ::absl_testing::StatusIs;
using ::testing::Eq;
using ::testing::HasSubstr;
using ::testing::Return;
std::string Sha256Digest(absl::string_view data) {
absl::StatusOr<std::unique_ptr<Hasher>> hasher = CreateHasher("sha256");
if (!hasher.ok()) return "";
(*hasher)->Update(data);
return (*hasher)->Digest();
}
TEST(InputHasherTest, CreateWithValidAlgString) {
EXPECT_THAT(InputHasher::Create("sha256"), IsOk());
EXPECT_THAT(InputHasher::Create("sha384"), IsOk());
EXPECT_THAT(InputHasher::Create("sha512"), IsOk());
}
TEST(InputHasherTest, CreateWithInvalidAlgString) {
EXPECT_THAT(InputHasher::Create("invalid"),
StatusIs(absl::StatusCode::kInvalidArgument));
}
TEST(InputHasherTest, CreateWithValidAlgEnum) {
EXPECT_THAT(InputHasher::Create(HashAlgorithm::kSha256), IsOk());
EXPECT_THAT(InputHasher::Create(HashAlgorithm::kSha384), IsOk());
EXPECT_THAT(InputHasher::Create(HashAlgorithm::kSha512), IsOk());
}
class MockHasher : public Hasher {
public:
MOCK_METHOD(void, Update, (absl::string_view data), (override));
MOCK_METHOD(std::string, Digest, (), (override));
};
TEST(InputHasherTest, CreateWithCustomHasher) {
auto mock_hasher = std::make_unique<MockHasher>();
EXPECT_CALL(*mock_hasher, Update("test"));
EXPECT_CALL(*mock_hasher, Digest()).WillOnce(Return("mock_digest"));
auto input_hasher = InputHasher::Create(std::move(mock_hasher));
ASSERT_THAT(input_hasher, IsOk());
(*input_hasher)->Update("test");
EXPECT_THAT((*input_hasher)->Digest(), Eq("mock_digest"));
}
TEST(InputHasherTest, UpdateStringView) {
auto hasher = InputHasher::Create("sha256");
ASSERT_THAT(hasher, IsOk());
(*hasher)->Update("hello");
(*hasher)->Update(" ");
(*hasher)->Update("world");
EXPECT_THAT(absl::BytesToHexString((*hasher)->Digest()),
Eq(absl::BytesToHexString(Sha256Digest("hello world"))));
}
TEST(InputHasherTest, UpdateRiegeliEmpty) {
auto hasher = InputHasher::Create("sha256");
ASSERT_THAT(hasher, IsOk());
std::string data = "";
riegeli::StringReader reader(data);
EXPECT_THAT((*hasher)->Update(reader, 0, 0), IsOk());
EXPECT_THAT(absl::BytesToHexString((*hasher)->Digest()),
Eq(absl::BytesToHexString(Sha256Digest(data))));
}
TEST(InputHasherTest, UpdateRiegeliSmall) {
auto hasher = InputHasher::Create("sha256");
ASSERT_THAT(hasher, IsOk());
std::string data = "small data";
riegeli::StringReader reader(data);
EXPECT_THAT((*hasher)->Update(reader, 0, data.length()), IsOk());
EXPECT_THAT(absl::BytesToHexString((*hasher)->Digest()),
Eq(absl::BytesToHexString(Sha256Digest(data))));
}
TEST(InputHasherTest, UpdateRiegeliLarge) {
auto hasher = InputHasher::Create("sha256");
ASSERT_THAT(hasher, IsOk());
std::string data(20000, 'a'); // Larger than default chunk size
riegeli::StringReader reader(data);
EXPECT_THAT((*hasher)->Update(reader, 0, data.length()), IsOk());
EXPECT_THAT(absl::BytesToHexString((*hasher)->Digest()),
Eq(absl::BytesToHexString(Sha256Digest(data))));
}
TEST(InputHasherTest, UpdateRiegeliWithOffset) {
auto hasher = InputHasher::Create("sha256");
ASSERT_THAT(hasher, IsOk());
std::string data = "prefix_and_data";
riegeli::StringReader reader(data);
EXPECT_THAT((*hasher)->Update(reader, 7, 3), IsOk()); // "and"
EXPECT_THAT(absl::BytesToHexString((*hasher)->Digest()),
Eq(absl::BytesToHexString(Sha256Digest("and"))));
}
TEST(InputHasherTest, UpdateRiegeliMultiple) {
auto hasher = InputHasher::Create("sha256");
ASSERT_THAT(hasher, IsOk());
std::string data1 = "first part";
std::string data2 = " second part";
riegeli::StringReader reader1(data1);
riegeli::StringReader reader2(data2);
EXPECT_THAT((*hasher)->Update(reader1, 0, data1.length()), IsOk());
(*hasher)->Update(" middle ");
EXPECT_THAT((*hasher)->Update(reader2, 0, data2.length()), IsOk());
EXPECT_THAT(absl::BytesToHexString((*hasher)->Digest()),
Eq(absl::BytesToHexString(
Sha256Digest("first part middle second part"))));
}
TEST(InputHasherTest, UpdateRiegeliClosedReaderFails) {
auto hasher = InputHasher::Create("sha256");
ASSERT_THAT(hasher, IsOk());
riegeli::StringReader reader("test");
reader.Close();
EXPECT_THAT((*hasher)->Update(reader, 0, 4),
StatusIs(absl::StatusCode::kFailedPrecondition,
HasSubstr("Object closed")));
}
TEST(InputHasherTest, UpdateRiegeliOffsetOutOfRange) {
auto hasher = InputHasher::Create("sha256");
ASSERT_THAT(hasher, IsOk());
riegeli::StringReader reader("test");
EXPECT_THAT(
(*hasher)->Update(reader, 10, 4),
StatusIs(absl::StatusCode::kInternal, HasSubstr("Failed to seek to 10")));
}
TEST(InputHasherTest, UpdateRiegeliLengthExceedsSourceFails) {
auto hasher = InputHasher::Create("sha256");
ASSERT_THAT(hasher, IsOk());
riegeli::StringReader reader("test");
EXPECT_THAT((*hasher)->Update(reader, 0, 100),
StatusIs(absl::StatusCode::kDataLoss,
HasSubstr("Failed to read from source")));
}
} // namespace
} // namespace credentio