blob: 7c8f85e73e7159fd6d3174b2caa7f9fe4676c15e [file]
// 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 "generator/generator_factory.h"
#include <memory>
#include <string>
#include <utility>
#include "absl/functional/any_invocable.h"
#include "absl/status/status.h"
#include "absl/status/status_matchers.h"
#include "absl/strings/string_view.h"
#include "absl/synchronization/notification.h"
#include "async/async_context.h"
#include "claim/generator.h"
#include "claim/manifest_box.h"
#include "claim/manifest_box_assembler.h"
#include "claim/signer.h"
#include "crypto/hash.h"
#include "generator/generator.h"
#include "generator/generator_options.h"
#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include "proto/generator.pb.h"
#include "proto/generator_info.pb.h"
namespace credentio {
namespace {
using ::absl_testing::StatusIs;
using ::testing::HasSubstr;
using ::testing::NotNull;
class FakeClaimSigner : public ClaimSigner {
public:
void Sign(absl::string_view claim, Options options,
absl::AnyInvocable<void(absl::StatusOr<SignResult>) &&> cb)
const override {
std::move(cb)(SignResult{.signature = "fake_sig"});
}
};
class TrackingManifestBoxAssembler : public ManifestBoxAssembler {
public:
explicit TrackingManifestBoxAssembler(std::string name, int* call_count)
: name_(std::move(name)), call_count_(call_count) {}
absl::StatusOr<ManifestBox> Assemble(
const GenerateManifestStoreRequest& request) const override {
(*call_count_)++;
return absl::InternalError(name_);
}
private:
std::string name_;
int* call_count_;
};
TEST(GeneratorFactoryTest, ValidateOptionsRejectsNullSigningClaimSigner) {
FakeClaimSigner template_signer;
GeneratorOptions options;
options.signing_claim_signer = nullptr;
options.template_claim_signer = &template_signer;
EXPECT_THAT(CreateGenerator(std::move(options)),
StatusIs(absl::StatusCode::kInvalidArgument,
HasSubstr("signing_claim_signer is required")));
}
TEST(GeneratorFactoryTest, ValidateOptionsRejectsNullTemplateClaimSigner) {
FakeClaimSigner signing_signer;
GeneratorOptions options;
options.signing_claim_signer = &signing_signer;
options.template_claim_signer = nullptr;
EXPECT_THAT(CreateGenerator(std::move(options)),
StatusIs(absl::StatusCode::kInvalidArgument,
HasSubstr("template_claim_signer is required")));
}
TEST(GeneratorFactoryTest, ValidateOptionsRejectsNullUuidGenerator) {
FakeClaimSigner signing_signer;
FakeClaimSigner template_signer;
GeneratorOptions options;
options.signing_claim_signer = &signing_signer;
options.template_claim_signer = &template_signer;
options.uuid_generator = nullptr;
EXPECT_THAT(CreateGenerator(std::move(options)),
StatusIs(absl::StatusCode::kInvalidArgument,
HasSubstr("uuid_generator is required")));
}
TEST(GeneratorFactoryTest, ValidateOptionsRejectsNullHashFactory) {
FakeClaimSigner signing_signer;
FakeClaimSigner template_signer;
GeneratorOptions options;
options.signing_claim_signer = &signing_signer;
options.template_claim_signer = &template_signer;
options.hash_factory = nullptr;
EXPECT_THAT(CreateGenerator(std::move(options)),
StatusIs(absl::StatusCode::kInvalidArgument,
HasSubstr("hash_factory is required")));
}
TEST(GeneratorFactoryTest, ValidateOptionsRejectsNullManifestAssemblerFactory) {
FakeClaimSigner signing_signer;
FakeClaimSigner template_signer;
GeneratorOptions options;
options.signing_claim_signer = &signing_signer;
options.template_claim_signer = &template_signer;
options.manifest_assembler_factory = nullptr;
EXPECT_THAT(CreateGenerator(std::move(options)),
StatusIs(absl::StatusCode::kInvalidArgument,
HasSubstr("manifest_assembler_factory is required")));
}
TEST(GeneratorFactoryTest, CreateGeneratorSuccess) {
FakeClaimSigner signing_signer;
FakeClaimSigner template_signer;
GeneratorOptions options;
options.generator_info.set_name("TestGenerator");
options.generator_info.set_spec_version("2.2.0");
options.signing_claim_signer = &signing_signer;
options.template_claim_signer = &template_signer;
auto generator_or = CreateGenerator(std::move(options));
ABSL_ASSERT_OK(generator_or);
EXPECT_THAT(*generator_or, NotNull());
}
TEST(GeneratorFactoryTest, DualModeGeneratorDelegation) {
FakeClaimSigner signing_signer;
FakeClaimSigner template_signer;
int signing_assemble_count = 0;
int template_assemble_count = 0;
GeneratorOptions options;
options.generator_info.set_name("TestGenerator");
options.generator_info.set_spec_version("2.2.0");
options.signing_claim_signer = &signing_signer;
options.template_claim_signer = &template_signer;
options.manifest_assembler_factory =
[&](const CreateManifestAssemblerOptions& opts)
-> absl::StatusOr<std::unique_ptr<ManifestBoxAssembler>> {
if (dynamic_cast<const TemplatedHasherFactory*>(opts.hash_factory) !=
nullptr) {
return std::make_unique<TrackingManifestBoxAssembler>(
"template_assembler", &template_assemble_count);
}
return std::make_unique<TrackingManifestBoxAssembler>(
"signing_assembler", &signing_assemble_count);
};
auto generator_or = CreateGenerator(std::move(options));
ABSL_ASSERT_OK(generator_or);
auto generator = std::move(*generator_or);
// GenerateManifestStore should delegate to the signing generator.
GenerateManifestStoreRequest gen_request;
absl::Notification gen_done;
absl::Status gen_status;
generator->GenerateManifestStore(
gen_request, AsyncContext(),
[&](absl::StatusOr<GenerateManifestStoreResponse> res) {
gen_status = res.status();
gen_done.Notify();
});
gen_done.WaitForNotification();
EXPECT_THAT(gen_status, StatusIs(absl::StatusCode::kInternal,
HasSubstr("signing_assembler")));
EXPECT_EQ(signing_assemble_count, 1);
EXPECT_EQ(template_assemble_count, 0);
// GenerateManifestStoreTemplate should delegate to the template generator.
absl::Notification template_done;
absl::Status template_status;
generator->GenerateManifestStoreTemplate(
gen_request, AsyncContext(),
[&](absl::StatusOr<ManifestStoreTemplate> res) {
template_status = res.status();
template_done.Notify();
});
template_done.WaitForNotification();
EXPECT_THAT(template_status, StatusIs(absl::StatusCode::kInternal,
HasSubstr("template_assembler")));
EXPECT_EQ(signing_assemble_count, 1);
EXPECT_EQ(template_assemble_count, 1);
// FinalizeManifestStore should delegate to the signing generator.
FinalizeManifestStoreRequest fin_request;
absl::Notification fin_done;
absl::Status fin_status;
generator->FinalizeManifestStore(
fin_request, AsyncContext(),
[&](absl::StatusOr<FinalizeManifestStoreResponse> res) {
fin_status = res.status();
fin_done.Notify();
});
fin_done.WaitForNotification();
EXPECT_THAT(fin_status, StatusIs(absl::StatusCode::kInvalidArgument,
HasSubstr("generator_context")));
}
} // namespace
} // namespace credentio