blob: 32b2ce4b292ad7a68866b3772a7505c8931b7923 [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 "generator/generator_impl.h"
#include <memory>
#include <string>
#include <utility>
#include <vector>
#include "absl/log/absl_check.h"
#include "absl/status/status.h"
#include "absl/status/status_matchers.h"
#include "absl/status/statusor.h"
#include "absl/strings/cord.h"
#include "absl/strings/string_view.h"
#include "absl/synchronization/notification.h"
#include "assertion/assertion_cbor_encoder.h"
#include "async/async_context.h"
#include "claim/claim_cbor_encoder.h"
#include "claim/generator.h"
#include "claim/local_signer.h"
#include "claim/manifest_box_assembler.h"
#include "claim/signer.h"
#include "crypto/algorithms.h"
#include "crypto/crypto_read_handler.h"
#include "crypto/crypto_write_handler.h"
#include "crypto/default/default_crypto_read_handler.h"
#include "crypto/default/hasher.h"
#include "formats/bmff/hasher.h"
#include "generator/generator.h"
#include "generator/manifest_store_transformer.h"
#include "gmock/gmock.h"
#include "google/protobuf/util/message_differencer.h"
#include "gtest/gtest.h"
#include "jumbf/box_builder.h"
#include "jumbf/constants.h"
#include "proto/bmff_based_hash_assertion.pb.h"
#include "proto/boxes_hash_assertion.pb.h"
#include "proto/collection_data_hash_assertion.pb.h"
#include "proto/data_hash_assertion.pb.h"
#include "proto/generator.pb.h"
#include "proto/generator_info.pb.h"
#include "proto/hashed_uri.pb.h"
#include "proto/ingredient_validation_result.pb.h"
#include "proto/manifest.pb.h"
#include "proto/signature_info.pb.h"
#include "proto/validation_result.pb.h"
#include "salt/salt_generator.h"
#include "testing/create_test_crypto_write_handler.h"
#include "testing/manifest_store_test_utils.h"
#include "testing/proto_test_utils.h"
#include "uuid/uuid.h"
namespace credentio {
namespace {
using ::absl_testing::IsOk;
using ::absl_testing::StatusIs;
using ::credentio_testing::ParseTextProtoOrDie;
using ::testing::HasSubstr;
using ::testing::Not;
MATCHER_P(EqualsProtoIgnoringTimestampAndOrdering, expected, "") {
google::protobuf::util::MessageDifferencer differencer;
differencer.set_repeated_field_comparison(
google::protobuf::util::MessageDifferencer::AS_SET);
differencer.IgnoreField(
SignatureInfo::descriptor()->FindFieldByName("timestamping_time"));
std::string diff;
differencer.ReportDifferencesToString(&diff);
bool match = differencer.Compare(expected, arg);
if (!match) {
*result_listener << "\nDifference:\n" << diff;
}
return match;
}
MATCHER_P(EqualsProtoIgnoringHashTimestampAndOrdering, expected, "") {
google::protobuf::util::MessageDifferencer differencer;
differencer.set_repeated_field_comparison(
google::protobuf::util::MessageDifferencer::AS_SET);
differencer.IgnoreField(HashedUri::descriptor()->FindFieldByName("hash"));
differencer.IgnoreField(
SignatureInfo::descriptor()->FindFieldByName("timestamping_time"));
std::string diff;
differencer.ReportDifferencesToString(&diff);
bool match = differencer.Compare(expected, arg);
if (!match) {
*result_listener << "\nDifference:\n" << diff;
}
return match;
}
absl::StatusOr<std::unique_ptr<CryptoReadHandler>>
CreateTrustAnyoneCryptoReadHandlerForTest() {
return CreateDefaultCryptoReadHandler({
.trust_environment = CryptoReadHandler::TrustEnvironment::kAny,
});
}
class MockManifestStoreTransformer : public ManifestStoreTransformer {
public:
MOCK_METHOD(absl::Status, Transform, (jumbf::BuiltSuperBox * box),
(const, override));
};
constexpr Uuid kUuid =
Uuid::FromStringOrDie("03020100-0504-0706-0908-0f0e0d0c0b0a");
constexpr Uuid kUuid_2 =
Uuid::FromStringOrDie("12345678-1234-1234-1234-123456789012");
class FakeUuidGenerator : public UuidGenerator {
public:
FakeUuidGenerator() : uuids_({kUuid, kUuid_2}) {}
Uuid Generate() const override {
if (index_ >= uuids_.size()) {
index_ = 0;
}
return uuids_[index_++];
}
private:
std::vector<Uuid> uuids_;
mutable int index_ = 0;
};
absl::StatusOr<GenerateManifestStoreResponse> GenerateManifestStore(
ManifestStoreGenerator& generator, GenerateManifestStoreRequest request) {
absl::Notification done;
absl::StatusOr<GenerateManifestStoreResponse> result;
generator.GenerateManifestStore(
std::move(request), AsyncContext(),
[&result, &done](absl::StatusOr<GenerateManifestStoreResponse> res) {
result = std::move(res);
done.Notify();
});
done.WaitForNotification();
return result;
}
absl::StatusOr<ManifestStoreTemplate> GenerateManifestStoreTemplate(
ManifestStoreGenerator& generator, GenerateManifestStoreRequest request) {
absl::Notification done;
absl::StatusOr<ManifestStoreTemplate> result;
generator.GenerateManifestStoreTemplate(
std::move(request), AsyncContext(),
[&result, &done](absl::StatusOr<ManifestStoreTemplate> res) {
result = std::move(res);
done.Notify();
});
done.WaitForNotification();
return result;
}
absl::StatusOr<FinalizeManifestStoreResponse> FinalizeManifestStore(
ManifestStoreGenerator& generator, FinalizeManifestStoreRequest request) {
absl::Notification done;
absl::StatusOr<FinalizeManifestStoreResponse> result;
generator.FinalizeManifestStore(
std::move(request), AsyncContext(),
[&result, &done](absl::StatusOr<FinalizeManifestStoreResponse> res) {
result = std::move(res);
done.Notify();
});
done.WaitForNotification();
return result;
}
class ManifestStoreGeneratorTest : public testing::Test {
protected:
ManifestStoreGeneratorTest() {
absl::StatusOr<std::unique_ptr<CryptoWriteHandler>> crypto_writer =
CreateTestCryptoWriteHandler({});
ABSL_CHECK_OK(crypto_writer);
crypto_writer_ = std::move(*crypto_writer);
generator_info_.set_name("Test generator");
generator_info_.set_spec_version("2.2.0");
absl::StatusOr<std::unique_ptr<CryptoReadHandler>> crypto_reader =
CreateTrustAnyoneCryptoReadHandlerForTest();
ABSL_CHECK_OK(crypto_reader);
crypto_reader_ = std::move(*crypto_reader);
claim_signer_ = std::make_unique<LocalClaimSigner>(crypto_writer_.get(),
&hash_factory_);
store_generator_ = CreateGenerator();
}
std::unique_ptr<ManifestStoreGenerator> CreateGenerator(
const ManifestStoreTransformer* manifest_store_transformer = nullptr) {
absl::StatusOr<std::unique_ptr<ManifestBoxAssembler>> manifest_assembler =
CreateManifestAssembler({.uuid_generator = &uuid_generator_,
.hash_factory = &hash_factory_});
ABSL_CHECK_OK(manifest_assembler);
absl::StatusOr<std::unique_ptr<ManifestEncoder>> manifest_encoder =
CreateManifestEncoder({
.generator_info = generator_info_,
.claim_signer = claim_signer_.get(),
.assertion_cbor_encoder = &GetAssertionCborEncoder(),
.claim_cbor_encoder = &GetClaimCborEncoder(),
.hash_factory = &hash_factory_,
.salt_generator = &SaltGenerator::NoOp(),
});
ABSL_CHECK_OK(manifest_encoder);
return std::make_unique<ManifestStoreGeneratorImpl>(
std::move(*manifest_assembler), std::move(*manifest_encoder),
manifest_store_transformer);
}
FakeUuidGenerator uuid_generator_;
std::unique_ptr<ClaimSigner> claim_signer_;
std::unique_ptr<CryptoWriteHandler> crypto_writer_;
std::unique_ptr<CryptoReadHandler> crypto_reader_;
Sha256HasherFactory hash_factory_;
GeneratorInfo generator_info_;
std::unique_ptr<ManifestStoreGenerator> store_generator_;
};
TEST_F(ManifestStoreGeneratorTest, Generate) {
GenerateManifestStoreRequest request = ParseTextProtoOrDie(R"pb(
instance_id: "12345678-1234-1234-1234-123456789012"
hard_binding {
box_hash {
alg: "some alg"
boxes { names: "some name" names: "other name" hash: "some hash" }
}
}
actions {
created {
action_metadata {
software_agent { name: "test generator" }
description: "created for testing"
}
digital_source_type: "https://cv.iptc.org/newscodes/digitalsourcetype/computationalCapture"
}
}
)pb");
PartialValidationResultProto expected_validated_result = ParseTextProtoOrDie(
R"pb(
active_manifest {
label: "urn:c2pa:03020100-0504-0706-0908-0f0e0d0c0b0a"
claim {
instance_id: "12345678-1234-1234-1234-123456789012"
claim_generator_info {
name: "Test generator"
spec_version: "2.2.0"
}
signature_info {
cert_serial_number: "02"
issuer: "OU=Test Unit,O=Test Org,CN=Test Claim Signer"
certificate_issuer: "OU=Test Unit,O=Test Org,CN=Test Root CA"
timestamping_time {}
}
created_assertions {
url: "self#jumbf=c2pa.assertions/c2pa.hash.boxes"
algorithm: "sha256"
hash: ")\255`[2\265\253\340\256(\265I\216_\222m\302@V\255O\204\017\316bb2\201\317\243\233\204"
}
created_assertions {
url: "self#jumbf=c2pa.assertions/c2pa.actions.v2"
algorithm: "sha256"
hash: "\331\271\373\3228\310\335>y\263\334\342\335\367\211Q\360\325\227\r\345$]\214\005\3047[\r#1\312"
}
default_algorithm: "sha256"
label: "c2pa.claim.v2"
}
assertions {
label: "c2pa.hash.boxes"
boxes_hash {
boxes { names: "some name" names: "other name" hash: "some hash" }
alg: "some alg"
}
}
assertions {
label: "c2pa.actions.v2"
actions {
actions {
action: "c2pa.created"
description: "created for testing"
digital_source_type: "https://cv.iptc.org/newscodes/digitalsourcetype/computationalCapture"
software_agent { name: "test generator" }
}
}
}
validation {
successes {
code: "timeStamp.validated"
url: "self#jumbf=/c2pa/urn:c2pa:03020100-0504-0706-0908-0f0e0d0c0b0a/c2pa.signature"
}
successes {
code: "timeStamp.trusted"
url: "self#jumbf=/c2pa/urn:c2pa:03020100-0504-0706-0908-0f0e0d0c0b0a/c2pa.signature"
}
successes {
code: "signingCredential.trusted"
url: "self#jumbf=/c2pa/urn:c2pa:03020100-0504-0706-0908-0f0e0d0c0b0a/c2pa.signature"
}
successes {
code: "claimSignature.insideValidity"
url: "self#jumbf=/c2pa/urn:c2pa:03020100-0504-0706-0908-0f0e0d0c0b0a/c2pa.signature"
}
successes {
code: "claimSignature.validated"
url: "self#jumbf=/c2pa/urn:c2pa:03020100-0504-0706-0908-0f0e0d0c0b0a/c2pa.signature"
}
successes {
code: "assertion.hashedURI.match"
url: "self#jumbf=/c2pa/urn:c2pa:03020100-0504-0706-0908-0f0e0d0c0b0a/c2pa.assertions/c2pa.hash.boxes"
}
successes {
code: "assertion.hashedURI.match"
url: "self#jumbf=/c2pa/urn:c2pa:03020100-0504-0706-0908-0f0e0d0c0b0a/c2pa.assertions/c2pa.actions.v2"
}
informationals {
code: "signingCredential.ocsp.skipped"
url: "self#jumbf=/c2pa/urn:c2pa:03020100-0504-0706-0908-0f0e0d0c0b0a/c2pa.signature"
}
}
}
hard_binding_uri: "self#jumbf=/c2pa/urn:c2pa:03020100-0504-0706-0908-0f0e0d0c0b0a/c2pa.assertions/c2pa.hash.boxes"
)pb");
absl::StatusOr<GenerateManifestStoreResponse> generated =
GenerateManifestStore(*store_generator_, request);
ABSL_ASSERT_OK(generated);
EXPECT_TRUE(generated->is_timestamped());
absl::StatusOr<std::unique_ptr<PartialValidationResultProto>> validated =
ValidateManifestStore(generated->manifest_store());
ABSL_ASSERT_OK(validated);
EXPECT_THAT(**validated, EqualsProtoIgnoringTimestampAndOrdering(
expected_validated_result));
}
TEST_F(ManifestStoreGeneratorTest, GenerateWithGatheredAssertions) {
GenerateManifestStoreRequest request = ParseTextProtoOrDie(R"pb(
instance_id: "12345678-1234-1234-1234-123456789012"
hard_binding {
box_hash {
alg: "some alg"
boxes { names: "some name" names: "other name" hash: "some hash" }
}
}
actions {
created {
action_metadata {
software_agent { name: "test generator" }
description: "created for testing"
}
digital_source_type: "https://cv.iptc.org/newscodes/digitalsourcetype/computationalCapture"
}
}
gathered_assertions {
label: "com.example.gathered"
data: "{\"test_key\": \"test_val\"}"
content_type: CONTENT_TYPE_JSON
}
)pb");
absl::StatusOr<GenerateManifestStoreResponse> generated =
GenerateManifestStore(*store_generator_, request);
ABSL_ASSERT_OK(generated);
EXPECT_TRUE(generated->is_timestamped());
absl::StatusOr<Claim> claim = ValidateClaim(generated->manifest_store());
ABSL_ASSERT_OK(claim);
ASSERT_EQ(claim->gathered_assertions_size(), 1);
EXPECT_EQ(claim->gathered_assertions(0).url(),
"self#jumbf=c2pa.assertions/com.example.gathered");
}
TEST_F(ManifestStoreGeneratorTest,
GenerateTemplateAndFinalizeWithGatheredAssertions) {
DataHashAssertion data_hash;
data_hash.set_hash(std::string(HashSizeBytes(HashAlgorithm::kSha256), '\0'));
data_hash.set_pad("");
GenerateManifestStoreRequest request;
*request.mutable_hard_binding()->mutable_data_hash() = data_hash;
*request.mutable_actions() = ParseTextProtoOrDie(R"pb(
created {
action_metadata {
software_agent { name: "test generator" }
description: "created for testing"
}
digital_source_type: "https://cv.iptc.org/newscodes/digitalsourcetype/computationalCapture"
}
)pb");
GatheredAssertion* gathered = request.add_gathered_assertions();
gathered->set_label("com.example.gathered");
gathered->set_data("{\"test_key\": \"test_val\"}");
gathered->set_content_type(GatheredAssertion::CONTENT_TYPE_JSON);
// Step 1: Generate template.
absl::StatusOr<ManifestStoreTemplate> manifest_store_template =
GenerateManifestStoreTemplate(*store_generator_, request);
ABSL_ASSERT_OK(manifest_store_template);
// Step 2: Update hash.
std::string new_hash(HashSizeBytes(HashAlgorithm::kSha256), '\1');
data_hash.set_hash(new_hash);
// Step 3: Finalize manifest store.
FinalizeManifestStoreRequest finalize_request;
*finalize_request.mutable_manifest_store_template() =
*manifest_store_template;
*finalize_request.mutable_hard_binding()->mutable_data_hash() = data_hash;
absl::StatusOr<FinalizeManifestStoreResponse> finalized =
FinalizeManifestStore(*store_generator_, finalize_request);
ABSL_ASSERT_OK(finalized);
EXPECT_TRUE(finalized->is_timestamped());
EXPECT_EQ(manifest_store_template->manifest_store_size(),
finalized->manifest_store().size());
absl::StatusOr<Claim> claim = ValidateClaim(finalized->manifest_store());
ABSL_ASSERT_OK(claim);
ASSERT_EQ(claim->gathered_assertions_size(), 1);
EXPECT_EQ(claim->gathered_assertions(0).url(),
"self#jumbf=c2pa.assertions/com.example.gathered");
}
TEST_F(ManifestStoreGeneratorTest, GenerateWithC2paOpenedAction) {
SimpleManifestStoreOptions options;
options.uuid_generator = &uuid_generator_;
absl::StatusOr<std::string> parent_manifest_store_bytes =
CreateSimpleManifestStoreBytes(options);
ABSL_ASSERT_OK(parent_manifest_store_bytes);
GenerateManifestStoreRequest request = ParseTextProtoOrDie(R"pb(
instance_id: "12345678-1234-1234-1234-123456789012"
hard_binding {
box_hash {
alg: "some alg"
boxes { names: "some name" names: "other name" hash: "some hash" }
}
}
actions {
opened {
action_metadata {
software_agent { name: "test generator" }
description: "opened for testing"
}
ingredient_index: 0
}
}
ingredients {
metadata { title: "test ingredient" }
ingredient_validation_results {
manifest_store_bytes: ""
ingredient_validation_results {
active_manifest { successes { code: "assertion.dataHash.match" } }
}
}
}
)pb");
request.mutable_ingredients(0)
->mutable_ingredient_validation_results()
->set_manifest_store_bytes(*parent_manifest_store_bytes);
PartialValidationResultProto expected_validated_result = ParseTextProtoOrDie(
R"pb(
active_manifest {
label: "urn:c2pa:12345678-1234-1234-1234-123456789012"
claim {
claim_generator_info {
name: "Test generator"
spec_version: "2.2.0"
}
signature_info {
cert_serial_number: "02"
issuer: "OU=Test Unit,O=Test Org,CN=Test Claim Signer"
certificate_issuer: "OU=Test Unit,O=Test Org,CN=Test Root CA"
timestamping_time {}
}
created_assertions {
url: "self#jumbf=c2pa.assertions/c2pa.hash.boxes"
algorithm: "sha256"
}
created_assertions {
url: "self#jumbf=c2pa.assertions/c2pa.ingredient.v3"
algorithm: "sha256"
}
created_assertions {
url: "self#jumbf=c2pa.assertions/c2pa.actions.v2"
algorithm: "sha256"
}
default_algorithm: "sha256"
label: "c2pa.claim.v2"
instance_id: "12345678-1234-1234-1234-123456789012"
}
assertions {
label: "c2pa.hash.boxes"
boxes_hash {
boxes { names: "some name" names: "other name" hash: "some hash" }
alg: "some alg"
}
}
assertions {
label: "c2pa.ingredient.v3"
ingredient_v3 {
title: "test ingredient"
relationship: "parentOf"
active_manifest {
url: "self#jumbf=/c2pa/urn:c2pa:03020100-0504-0706-0908-0f0e0d0c0b0a"
}
claim_signature {
url: "self#jumbf=/c2pa/urn:c2pa:03020100-0504-0706-0908-0f0e0d0c0b0a/c2pa.signature"
}
validation_results {
active_manifest {
successes { code: "assertion.dataHash.match" }
}
}
}
}
assertions {
label: "c2pa.actions.v2"
actions {
actions {
action: "c2pa.opened"
description: "opened for testing"
software_agent { name: "test generator" }
parameters {
ingredients {
url: "self#jumbf=c2pa.assertions/c2pa.ingredient.v3"
}
}
}
}
}
validation {
successes {
code: "timeStamp.validated"
url: "self#jumbf=/c2pa/urn:c2pa:12345678-1234-1234-1234-123456789012/c2pa.signature"
}
successes {
code: "timeStamp.trusted"
url: "self#jumbf=/c2pa/urn:c2pa:12345678-1234-1234-1234-123456789012/c2pa.signature"
}
successes {
code: "signingCredential.trusted"
url: "self#jumbf=/c2pa/urn:c2pa:12345678-1234-1234-1234-123456789012/c2pa.signature"
}
successes {
code: "claimSignature.insideValidity"
url: "self#jumbf=/c2pa/urn:c2pa:12345678-1234-1234-1234-123456789012/c2pa.signature"
}
successes {
code: "claimSignature.validated"
url: "self#jumbf=/c2pa/urn:c2pa:12345678-1234-1234-1234-123456789012/c2pa.signature"
}
successes {
code: "assertion.hashedURI.match"
url: "self#jumbf=/c2pa/urn:c2pa:12345678-1234-1234-1234-123456789012/c2pa.assertions/c2pa.hash.boxes"
}
successes {
code: "assertion.hashedURI.match"
url: "self#jumbf=/c2pa/urn:c2pa:12345678-1234-1234-1234-123456789012/c2pa.assertions/c2pa.ingredient.v3"
}
successes {
code: "assertion.hashedURI.match"
url: "self#jumbf=/c2pa/urn:c2pa:12345678-1234-1234-1234-123456789012/c2pa.assertions/c2pa.actions.v2"
}
successes {
code: "ingredient.claimSignature.validated"
url: "self#jumbf=/c2pa/urn:c2pa:12345678-1234-1234-1234-123456789012/c2pa.assertions/c2pa.ingredient.v3"
}
informationals {
code: "signingCredential.ocsp.skipped"
url: "self#jumbf=/c2pa/urn:c2pa:12345678-1234-1234-1234-123456789012/c2pa.signature"
}
}
}
ingredient_manifests {
label: "urn:c2pa:03020100-0504-0706-0908-0f0e0d0c0b0a"
claim {
claim_generator_info { name: "Test C2PA Generator" }
signature_info {
cert_serial_number: "02"
issuer: "OU=Test Unit,O=Test Org,CN=Test Claim Signer"
certificate_issuer: "OU=Test Unit,O=Test Org,CN=Test Root CA"
timestamping_time {}
}
created_assertions {
url: "self#jumbf=c2pa.assertions/c2pa.hash.boxes"
algorithm: "sha256"
}
created_assertions {
url: "self#jumbf=c2pa.assertions/c2pa.actions.v2"
algorithm: "sha256"
}
default_algorithm: "sha256"
label: "c2pa.claim.v2"
instance_id: "12345678-1234-1234-1234-123456789012"
}
assertions {
label: "c2pa.hash.boxes"
boxes_hash {
boxes { names: "some name" names: "other name" hash: "some hash" }
alg: "some alg"
}
}
assertions {
label: "c2pa.actions.v2"
actions {
actions {
action: "c2pa.created"
description: "created for testing"
digital_source_type: "https://cv.iptc.org/newscodes/digitalsourcetype/computationalCapture"
software_agent { name: "test generator" }
}
}
}
validation {
successes {
code: "timeStamp.validated"
url: "self#jumbf=/c2pa/urn:c2pa:03020100-0504-0706-0908-0f0e0d0c0b0a/c2pa.signature"
}
successes {
code: "timeStamp.trusted"
url: "self#jumbf=/c2pa/urn:c2pa:03020100-0504-0706-0908-0f0e0d0c0b0a/c2pa.signature"
}
successes {
code: "signingCredential.trusted"
url: "self#jumbf=/c2pa/urn:c2pa:03020100-0504-0706-0908-0f0e0d0c0b0a/c2pa.signature"
}
successes {
code: "claimSignature.insideValidity"
url: "self#jumbf=/c2pa/urn:c2pa:03020100-0504-0706-0908-0f0e0d0c0b0a/c2pa.signature"
}
successes {
code: "claimSignature.validated"
url: "self#jumbf=/c2pa/urn:c2pa:03020100-0504-0706-0908-0f0e0d0c0b0a/c2pa.signature"
}
successes {
code: "assertion.hashedURI.match"
url: "self#jumbf=/c2pa/urn:c2pa:03020100-0504-0706-0908-0f0e0d0c0b0a/c2pa.assertions/c2pa.hash.boxes"
}
successes {
code: "assertion.hashedURI.match"
url: "self#jumbf=/c2pa/urn:c2pa:03020100-0504-0706-0908-0f0e0d0c0b0a/c2pa.assertions/c2pa.actions.v2"
}
informationals {
code: "signingCredential.ocsp.skipped"
url: "self#jumbf=/c2pa/urn:c2pa:03020100-0504-0706-0908-0f0e0d0c0b0a/c2pa.signature"
}
}
}
hard_binding_uri: "self#jumbf=/c2pa/urn:c2pa:12345678-1234-1234-1234-123456789012/c2pa.assertions/c2pa.hash.boxes"
)pb");
absl::StatusOr<GenerateManifestStoreResponse> generated =
GenerateManifestStore(*store_generator_, request);
ABSL_ASSERT_OK(generated);
absl::StatusOr<std::unique_ptr<PartialValidationResultProto>> validated =
ValidateManifestStore(generated->manifest_store());
ABSL_ASSERT_OK(validated);
EXPECT_THAT(**validated, EqualsProtoIgnoringHashTimestampAndOrdering(
expected_validated_result));
}
TEST_F(ManifestStoreGeneratorTest,
GenerateC2paOpenedActionMissingManifestStoreBytes) {
GenerateManifestStoreRequest request = ParseTextProtoOrDie(R"pb(
instance_id: "03020100-0504-0706-0908-0f0e0d0c0b0a"
hard_binding {
box_hash {
alg: "some alg"
boxes { names: "some name" names: "other name" hash: "some hash" }
}
}
actions {
opened {
action_metadata {
software_agent { name: "test generator" }
description: "opened for testing"
}
ingredient_index: 0
}
}
ingredients {
metadata { title: "test ingredient" }
ingredient_validation_results {}
}
)pb");
PartialValidationResultProto expected_validated_result = ParseTextProtoOrDie(
R"pb(
active_manifest {
label: "urn:c2pa:03020100-0504-0706-0908-0f0e0d0c0b0a"
claim {
claim_generator_info {
name: "Test generator"
spec_version: "2.2.0"
}
signature_info {
cert_serial_number: "02"
issuer: "OU=Test Unit,O=Test Org,CN=Test Claim Signer"
certificate_issuer: "OU=Test Unit,O=Test Org,CN=Test Root CA"
timestamping_time {}
}
created_assertions {
url: "self#jumbf=c2pa.assertions/c2pa.hash.boxes"
algorithm: "sha256"
}
created_assertions {
url: "self#jumbf=c2pa.assertions/c2pa.ingredient.v3"
algorithm: "sha256"
}
created_assertions {
url: "self#jumbf=c2pa.assertions/c2pa.actions.v2"
algorithm: "sha256"
}
default_algorithm: "sha256"
label: "c2pa.claim.v2"
instance_id: "03020100-0504-0706-0908-0f0e0d0c0b0a"
}
assertions {
label: "c2pa.hash.boxes"
boxes_hash {
boxes { names: "some name" names: "other name" hash: "some hash" }
alg: "some alg"
}
}
assertions {
label: "c2pa.ingredient.v3"
ingredient_v3 { title: "test ingredient" relationship: "parentOf" }
}
assertions {
label: "c2pa.actions.v2"
actions {
actions {
action: "c2pa.opened"
description: "opened for testing"
software_agent { name: "test generator" }
parameters {
ingredients {
url: "self#jumbf=c2pa.assertions/c2pa.ingredient.v3"
}
}
}
}
}
validation {
successes {
code: "timeStamp.validated"
url: "self#jumbf=/c2pa/urn:c2pa:03020100-0504-0706-0908-0f0e0d0c0b0a/c2pa.signature"
}
successes {
code: "timeStamp.trusted"
url: "self#jumbf=/c2pa/urn:c2pa:03020100-0504-0706-0908-0f0e0d0c0b0a/c2pa.signature"
}
successes {
code: "signingCredential.trusted"
url: "self#jumbf=/c2pa/urn:c2pa:03020100-0504-0706-0908-0f0e0d0c0b0a/c2pa.signature"
}
successes {
code: "claimSignature.insideValidity"
url: "self#jumbf=/c2pa/urn:c2pa:03020100-0504-0706-0908-0f0e0d0c0b0a/c2pa.signature"
}
successes {
code: "claimSignature.validated"
url: "self#jumbf=/c2pa/urn:c2pa:03020100-0504-0706-0908-0f0e0d0c0b0a/c2pa.signature"
}
successes {
code: "assertion.hashedURI.match"
url: "self#jumbf=/c2pa/urn:c2pa:03020100-0504-0706-0908-0f0e0d0c0b0a/c2pa.assertions/c2pa.hash.boxes"
}
successes {
code: "assertion.hashedURI.match"
url: "self#jumbf=/c2pa/urn:c2pa:03020100-0504-0706-0908-0f0e0d0c0b0a/c2pa.assertions/c2pa.ingredient.v3"
}
successes {
code: "assertion.hashedURI.match"
url: "self#jumbf=/c2pa/urn:c2pa:03020100-0504-0706-0908-0f0e0d0c0b0a/c2pa.assertions/c2pa.actions.v2"
}
informationals {
code: "signingCredential.ocsp.skipped"
url: "self#jumbf=/c2pa/urn:c2pa:03020100-0504-0706-0908-0f0e0d0c0b0a/c2pa.signature"
}
informationals {
code: "ingredient.unknownProvenance"
url: "self#jumbf=/c2pa/urn:c2pa:03020100-0504-0706-0908-0f0e0d0c0b0a/c2pa.assertions/c2pa.ingredient.v3"
}
}
}
hard_binding_uri: "self#jumbf=/c2pa/urn:c2pa:03020100-0504-0706-0908-0f0e0d0c0b0a/c2pa.assertions/c2pa.hash.boxes"
)pb");
absl::StatusOr<GenerateManifestStoreResponse> generated =
GenerateManifestStore(*store_generator_, request);
ABSL_ASSERT_OK(generated);
absl::StatusOr<std::unique_ptr<PartialValidationResultProto>> validated =
ValidateManifestStore(generated->manifest_store());
ABSL_ASSERT_OK(validated);
EXPECT_THAT(**validated, EqualsProtoIgnoringHashTimestampAndOrdering(
expected_validated_result));
}
TEST_F(ManifestStoreGeneratorTest,
GenerateC2paOpenedActionMissingValidationResultsInvalid) {
GenerateManifestStoreRequest request = ParseTextProtoOrDie(R"pb(
hard_binding {
box_hash {
alg: "some alg"
boxes { names: "some name" names: "other name" hash: "some hash" }
}
}
actions {
opened {
action_metadata {
software_agent { name: "test generator" }
description: "opened for testing"
}
ingredient_index: 0
}
}
ingredients {
metadata { title: "test ingredient" }
ingredient_validation_results {
manifest_store_bytes: "test manifest store bytes"
}
}
)pb");
EXPECT_THAT(GenerateManifestStore(*store_generator_, request),
StatusIs(absl::StatusCode::kInvalidArgument,
HasSubstr("validation results cannot be empty")));
}
TEST_F(ManifestStoreGeneratorTest, GenerateMissingHardBindingInvalid) {
GenerateManifestStoreRequest request = ParseTextProtoOrDie(R"pb(
actions {
created {
action_metadata {
software_agent { name: "test generator" }
description: "created for testing"
}
digital_source_type: "https://cv.iptc.org/newscodes/digitalsourcetype/computationalCapture"
}
}
)pb");
EXPECT_THAT(
GenerateManifestStore(*store_generator_, request),
StatusIs(absl::StatusCode::kInvalidArgument, HasSubstr("hard binding")));
}
TEST_F(ManifestStoreGeneratorTest, GenerateMissingStartActionInvalid) {
GenerateManifestStoreRequest request = ParseTextProtoOrDie(R"pb(
hard_binding {
box_hash {
alg: "some alg"
boxes { names: "some name" names: "other name" hash: "some hash" }
}
}
)pb");
EXPECT_THAT(GenerateManifestStore(*store_generator_, request),
StatusIs(absl::StatusCode::kInvalidArgument,
HasSubstr("missing inception action")));
}
TEST_F(ManifestStoreGeneratorTest, GenerateTemplateWithBmffBasedHash) {
BmffBasedHashAssertion bmff_based_hash =
GetBmffHashTemplate(HashAlgorithm::kSha256);
GenerateManifestStoreRequest request;
*request.mutable_hard_binding()->mutable_bmff_based_hash() = bmff_based_hash;
*request.mutable_actions() = ParseTextProtoOrDie(R"pb(
created {
action_metadata {
software_agent { name: "test generator" }
description: "created for testing"
}
digital_source_type: "https://cv.iptc.org/newscodes/digitalsourcetype/computationalCapture"
}
)pb");
// Step 1: Generate template.
absl::StatusOr<ManifestStoreTemplate> manifest_store_template =
GenerateManifestStoreTemplate(*store_generator_, request);
ABSL_ASSERT_OK(manifest_store_template);
// Step 2: Update hash.
std::string new_hash(HashSizeBytes(HashAlgorithm::kSha256), '\1');
bmff_based_hash.set_hash(new_hash);
// Step 3: Finalize manifest store.
FinalizeManifestStoreRequest finalize_request;
*finalize_request.mutable_manifest_store_template() =
*manifest_store_template;
*finalize_request.mutable_hard_binding()->mutable_bmff_based_hash() =
bmff_based_hash;
absl::StatusOr<FinalizeManifestStoreResponse> finalized =
FinalizeManifestStore(*store_generator_, finalize_request);
ABSL_ASSERT_OK(finalized);
EXPECT_TRUE(finalized->is_timestamped());
EXPECT_EQ(manifest_store_template->manifest_store_size(),
finalized->manifest_store().size());
EXPECT_THAT(ValidateManifestStore(finalized->manifest_store()), IsOk());
EXPECT_THAT(finalized->manifest_store(), HasSubstr(new_hash));
};
TEST_F(ManifestStoreGeneratorTest, GenerateTemplateWithDataHash) {
DataHashAssertion data_hash;
data_hash.set_hash(std::string(HashSizeBytes(HashAlgorithm::kSha256), '\0'));
data_hash.set_pad("");
GenerateManifestStoreRequest request;
*request.mutable_hard_binding()->mutable_data_hash() = data_hash;
*request.mutable_actions() = ParseTextProtoOrDie(R"pb(
created {
action_metadata {
software_agent { name: "test generator" }
description: "created for testing"
}
digital_source_type: "https://cv.iptc.org/newscodes/digitalsourcetype/computationalCapture"
}
)pb");
// Step 1: Generate template.
absl::StatusOr<ManifestStoreTemplate> manifest_store_template =
GenerateManifestStoreTemplate(*store_generator_, request);
ABSL_ASSERT_OK(manifest_store_template);
// Step 2: Update hash.
std::string new_hash(HashSizeBytes(HashAlgorithm::kSha256), '\1');
data_hash.set_hash(new_hash);
// Step 3: Finalize manifest store.
FinalizeManifestStoreRequest finalize_request;
*finalize_request.mutable_manifest_store_template() =
*manifest_store_template;
*finalize_request.mutable_hard_binding()->mutable_data_hash() = data_hash;
absl::StatusOr<FinalizeManifestStoreResponse> finalized =
FinalizeManifestStore(*store_generator_, finalize_request);
ABSL_ASSERT_OK(finalized);
EXPECT_TRUE(finalized->is_timestamped());
EXPECT_EQ(manifest_store_template->manifest_store_size(),
finalized->manifest_store().size());
EXPECT_THAT(ValidateManifestStore(finalized->manifest_store()), IsOk());
EXPECT_THAT(finalized->manifest_store(), HasSubstr(new_hash));
};
TEST_F(ManifestStoreGeneratorTest, GenerateTemplateWithBoxesHash) {
BoxesHashAssertion box_hash;
BoxHash* box = box_hash.add_boxes();
box->add_names("some name");
box->set_hash(std::string(HashSizeBytes(HashAlgorithm::kSha256), '\0'));
GenerateManifestStoreRequest request;
*request.mutable_hard_binding()->mutable_box_hash() = box_hash;
*request.mutable_actions() = ParseTextProtoOrDie(R"pb(
created {
action_metadata {
software_agent { name: "test generator" }
description: "created for testing"
}
digital_source_type: "https://cv.iptc.org/newscodes/digitalsourcetype/computationalCapture"
}
)pb");
// Step 1: Generate template.
absl::StatusOr<ManifestStoreTemplate> manifest_store_template =
GenerateManifestStoreTemplate(*store_generator_, request);
ABSL_ASSERT_OK(manifest_store_template);
// Step 2: Update boxes hash.
std::string new_hash(HashSizeBytes(HashAlgorithm::kSha256), '\1');
box_hash.mutable_boxes(0)->set_hash(new_hash);
// Step 3: Finalize manifest store.
FinalizeManifestStoreRequest finalize_request;
*finalize_request.mutable_manifest_store_template() =
*manifest_store_template;
*finalize_request.mutable_hard_binding()->mutable_box_hash() = box_hash;
absl::StatusOr<FinalizeManifestStoreResponse> finalized =
FinalizeManifestStore(*store_generator_, finalize_request);
ABSL_ASSERT_OK(finalized);
EXPECT_TRUE(finalized->is_timestamped());
EXPECT_EQ(manifest_store_template->manifest_store_size(),
finalized->manifest_store().size());
EXPECT_THAT(ValidateManifestStore(finalized->manifest_store()), IsOk());
EXPECT_THAT(finalized->manifest_store(), HasSubstr(new_hash));
};
TEST_F(ManifestStoreGeneratorTest, GenerateTemplateWithCollectionDataHash) {
CollectionDataHashAssertion collection_data_hash;
collection_data_hash.set_zip_central_directory_hash(
std::string(HashSizeBytes(HashAlgorithm::kSha256), '\0'));
collection_data_hash.set_alg("sha256");
UriHashedData* uri_hash = collection_data_hash.add_uris();
uri_hash->set_uri("some uri");
uri_hash->set_type("some type");
uri_hash->set_hash(std::string(HashSizeBytes(HashAlgorithm::kSha256), '\0'));
GenerateManifestStoreRequest request;
*request.mutable_hard_binding()->mutable_collection_data_hash() =
collection_data_hash;
*request.mutable_actions() = ParseTextProtoOrDie(R"pb(
created {
action_metadata {
software_agent { name: "test generator" }
description: "created for testing"
}
digital_source_type: "https://cv.iptc.org/newscodes/digitalsourcetype/computationalCapture"
}
)pb");
// Step 1: Generate template.
absl::StatusOr<ManifestStoreTemplate> manifest_store_template =
GenerateManifestStoreTemplate(*store_generator_, request);
ABSL_ASSERT_OK(manifest_store_template);
// Step 2: Update hash.
std::string new_central_dir_hash(HashSizeBytes(HashAlgorithm::kSha256), '\1');
collection_data_hash.set_zip_central_directory_hash(new_central_dir_hash);
std::string new_uri_hash(HashSizeBytes(HashAlgorithm::kSha256), '\2');
collection_data_hash.mutable_uris(0)->set_hash(new_uri_hash);
// Step 3: Finalize manifest store.
FinalizeManifestStoreRequest finalize_request;
*finalize_request.mutable_manifest_store_template() =
*manifest_store_template;
*finalize_request.mutable_hard_binding()->mutable_collection_data_hash() =
collection_data_hash;
absl::StatusOr<FinalizeManifestStoreResponse> finalized =
FinalizeManifestStore(*store_generator_, finalize_request);
ABSL_ASSERT_OK(finalized);
EXPECT_TRUE(finalized->is_timestamped());
EXPECT_EQ(manifest_store_template->manifest_store_size(),
finalized->manifest_store().size());
EXPECT_THAT(ValidateManifestStore(finalized->manifest_store()), IsOk());
EXPECT_THAT(finalized->manifest_store(), HasSubstr(new_central_dir_hash));
EXPECT_THAT(finalized->manifest_store(), HasSubstr(new_uri_hash));
};
TEST_F(ManifestStoreGeneratorTest, EmptyGeneratorContext) {
BmffBasedHashAssertion bmff_based_hash =
GetBmffHashTemplate(HashAlgorithm::kSha256);
FinalizeManifestStoreRequest finalize_request;
*finalize_request.mutable_hard_binding()->mutable_bmff_based_hash() =
bmff_based_hash;
EXPECT_THAT(FinalizeManifestStore(*store_generator_, finalize_request),
StatusIs(absl::StatusCode::kInvalidArgument, HasSubstr("empty")));
}
TEST_F(ManifestStoreGeneratorTest, FailParsingGeneratorContext) {
BmffBasedHashAssertion bmff_based_hash =
GetBmffHashTemplate(HashAlgorithm::kSha256);
FinalizeManifestStoreRequest finalize_request;
finalize_request.mutable_manifest_store_template()->set_generator_context(
"invalid generator context");
*finalize_request.mutable_hard_binding()->mutable_bmff_based_hash() =
bmff_based_hash;
EXPECT_THAT(FinalizeManifestStore(*store_generator_, finalize_request),
StatusIs(absl::StatusCode::kInvalidArgument,
HasSubstr("Failed to parse the generator_context")));
}
TEST_F(ManifestStoreGeneratorTest, HashBindingMissing) {
DataHashAssertion data_hash;
data_hash.set_hash(std::string(HashSizeBytes(HashAlgorithm::kSha256), '\0'));
data_hash.set_pad("");
GenerateManifestStoreRequest request;
*request.mutable_hard_binding()->mutable_data_hash() = data_hash;
*request.mutable_actions() = ParseTextProtoOrDie(R"pb(
created {
action_metadata {
software_agent { name: "test generator" }
description: "created for testing"
}
digital_source_type: "https://cv.iptc.org/newscodes/digitalsourcetype/computationalCapture"
}
)pb");
absl::StatusOr<ManifestStoreTemplate> manifest_store_template =
GenerateManifestStoreTemplate(*store_generator_, request);
ABSL_ASSERT_OK(manifest_store_template);
FinalizeManifestStoreRequest finalize_request;
*finalize_request.mutable_manifest_store_template() =
*manifest_store_template;
EXPECT_THAT(
FinalizeManifestStore(*store_generator_, finalize_request),
StatusIs(absl::StatusCode::kInvalidArgument, HasSubstr("hard binding")));
}
TEST_F(ManifestStoreGeneratorTest, HashBindingMismatchDataHash) {
DataHashAssertion data_hash;
data_hash.set_hash(std::string(HashSizeBytes(HashAlgorithm::kSha256), '\0'));
data_hash.set_pad("");
GenerateManifestStoreRequest request;
*request.mutable_hard_binding()->mutable_data_hash() = data_hash;
*request.mutable_actions() = ParseTextProtoOrDie(R"pb(
created {
action_metadata {
software_agent { name: "test generator" }
description: "created for testing"
}
digital_source_type: "https://cv.iptc.org/newscodes/digitalsourcetype/computationalCapture"
}
)pb");
absl::StatusOr<ManifestStoreTemplate> manifest_store_template =
GenerateManifestStoreTemplate(*store_generator_, request);
ABSL_ASSERT_OK(manifest_store_template);
FinalizeManifestStoreRequest finalize_request;
*finalize_request.mutable_manifest_store_template() =
*manifest_store_template;
*finalize_request.mutable_hard_binding()->mutable_box_hash() =
ParseTextProtoOrDie(R"pb(
boxes { hash: "some hash" }
)pb");
EXPECT_THAT(FinalizeManifestStore(*store_generator_, finalize_request),
StatusIs(absl::StatusCode::kInvalidArgument,
HasSubstr("The hard binding in the finalize request "
"must match the original request")));
}
TEST_F(ManifestStoreGeneratorTest, HashBindingMismatchBoxesHash) {
BoxesHashAssertion box_hash;
BoxHash* box = box_hash.add_boxes();
box->add_names("some name");
box->set_hash(std::string(HashSizeBytes(HashAlgorithm::kSha256), '\0'));
GenerateManifestStoreRequest request;
*request.mutable_hard_binding()->mutable_box_hash() = box_hash;
*request.mutable_actions() = ParseTextProtoOrDie(R"pb(
created {
action_metadata {
software_agent { name: "test generator" }
description: "created for testing"
}
digital_source_type: "https://cv.iptc.org/newscodes/digitalsourcetype/computationalCapture"
}
)pb");
absl::StatusOr<ManifestStoreTemplate> manifest_store_template =
GenerateManifestStoreTemplate(*store_generator_, request);
ABSL_ASSERT_OK(manifest_store_template);
FinalizeManifestStoreRequest finalize_request;
*finalize_request.mutable_manifest_store_template() =
*manifest_store_template;
*finalize_request.mutable_hard_binding()->mutable_data_hash() =
ParseTextProtoOrDie(R"pb(
hash: "some hash"
)pb");
EXPECT_THAT(FinalizeManifestStore(*store_generator_, finalize_request),
StatusIs(absl::StatusCode::kInvalidArgument,
HasSubstr("The hard binding in the finalize request "
"must match the original request")));
}
TEST_F(ManifestStoreGeneratorTest, HashBindingMismatchBmffBasedHash) {
BmffBasedHashAssertion bmff_based_hash =
GetBmffHashTemplate(HashAlgorithm::kSha256);
GenerateManifestStoreRequest request;
*request.mutable_hard_binding()->mutable_bmff_based_hash() = bmff_based_hash;
*request.mutable_actions() = ParseTextProtoOrDie(R"pb(
created {
action_metadata {
software_agent { name: "test generator" }
description: "created for testing"
}
digital_source_type: "https://cv.iptc.org/newscodes/digitalsourcetype/computationalCapture"
}
)pb");
absl::StatusOr<ManifestStoreTemplate> manifest_store_template =
GenerateManifestStoreTemplate(*store_generator_, request);
ABSL_ASSERT_OK(manifest_store_template);
FinalizeManifestStoreRequest finalize_request;
*finalize_request.mutable_manifest_store_template() =
*manifest_store_template;
*finalize_request.mutable_hard_binding()->mutable_data_hash() =
ParseTextProtoOrDie(R"pb(
hash: "some hash"
)pb");
EXPECT_THAT(FinalizeManifestStore(*store_generator_, finalize_request),
StatusIs(absl::StatusCode::kInvalidArgument,
HasSubstr("The hard binding in the finalize request "
"must match the original request")));
}
TEST_F(ManifestStoreGeneratorTest, HashBindingMismatchCollectionDataHash) {
CollectionDataHashAssertion collection_data_hash = ParseTextProtoOrDie(R"pb(
uris { uri: "some uri" hash: "some hash" type: "some type" }
alg: "sha256"
zip_central_directory_hash: "some hash"
)pb");
GenerateManifestStoreRequest request;
*request.mutable_hard_binding()->mutable_collection_data_hash() =
collection_data_hash;
*request.mutable_actions() = ParseTextProtoOrDie(R"pb(
created {
action_metadata {
software_agent { name: "test generator" }
description: "created for testing"
}
digital_source_type: "https://cv.iptc.org/newscodes/digitalsourcetype/computationalCapture"
}
)pb");
absl::StatusOr<ManifestStoreTemplate> manifest_store_template =
GenerateManifestStoreTemplate(*store_generator_, request);
ABSL_ASSERT_OK(manifest_store_template);
FinalizeManifestStoreRequest finalize_request;
*finalize_request.mutable_manifest_store_template() =
*manifest_store_template;
*finalize_request.mutable_hard_binding()->mutable_data_hash() =
ParseTextProtoOrDie(R"pb(
hash: "some hash"
)pb");
EXPECT_THAT(FinalizeManifestStore(*store_generator_, finalize_request),
StatusIs(absl::StatusCode::kInvalidArgument,
HasSubstr("The hard binding in the finalize request "
"must match the original request")));
}
TEST_F(ManifestStoreGeneratorTest, DataHashByteSizeAlwaysMatch) {
DataHashAssertion data_hash;
data_hash.set_hash(std::string(HashSizeBytes(HashAlgorithm::kSha256), '\0'));
data_hash.set_pad("");
DataRange* exclusion = data_hash.add_exclusions();
exclusion->set_start(0);
exclusion->set_length(0);
GenerateManifestStoreRequest request;
*request.mutable_hard_binding()->mutable_data_hash() = data_hash;
*request.mutable_actions() = ParseTextProtoOrDie(R"pb(
created {
action_metadata {
software_agent { name: "test generator" }
description: "created for testing"
}
digital_source_type: "https://cv.iptc.org/newscodes/digitalsourcetype/computationalCapture"
}
)pb");
absl::StatusOr<ManifestStoreTemplate> manifest_store_template =
GenerateManifestStoreTemplate(*store_generator_, request);
ABSL_ASSERT_OK(manifest_store_template);
FinalizeManifestStoreRequest finalize_request;
*finalize_request.mutable_manifest_store_template() =
*manifest_store_template;
data_hash.set_pad("additional padding");
data_hash.mutable_exclusions(0)->set_start(10);
data_hash.mutable_exclusions(0)->set_length(4294967296);
*finalize_request.mutable_hard_binding()->mutable_data_hash() = data_hash;
absl::StatusOr<FinalizeManifestStoreResponse> generated =
FinalizeManifestStore(*store_generator_, finalize_request);
ABSL_ASSERT_OK(generated);
EXPECT_THAT(generated->manifest_store(),
Not(HasSubstr("additional padding")));
}
TEST_F(ManifestStoreGeneratorTest, DataHashByteSizeMismatchDueToDifferentName) {
DataHashAssertion data_hash;
data_hash.set_hash(std::string(HashSizeBytes(HashAlgorithm::kSha256), '\0'));
data_hash.set_pad("");
DataRange* exclusion = data_hash.add_exclusions();
exclusion->set_start(0);
exclusion->set_length(0);
GenerateManifestStoreRequest request;
*request.mutable_hard_binding()->mutable_data_hash() = data_hash;
*request.mutable_actions() = ParseTextProtoOrDie(R"pb(
created {
action_metadata {
software_agent { name: "test generator" }
description: "created for testing"
}
digital_source_type: "https://cv.iptc.org/newscodes/digitalsourcetype/computationalCapture"
}
)pb");
absl::StatusOr<ManifestStoreTemplate> manifest_store_template =
GenerateManifestStoreTemplate(*store_generator_, request);
ABSL_ASSERT_OK(manifest_store_template);
FinalizeManifestStoreRequest finalize_request;
*finalize_request.mutable_manifest_store_template() =
*manifest_store_template;
data_hash.clear_pad(); // Padding should add it back.
data_hash.mutable_exclusions(0)->set_start(10);
data_hash.mutable_exclusions(0)->set_length(4294967296);
data_hash.set_name("additional name");
*finalize_request.mutable_hard_binding()->mutable_data_hash() = data_hash;
EXPECT_THAT(FinalizeManifestStore(*store_generator_, finalize_request),
StatusIs(absl::StatusCode::kInvalidArgument,
HasSubstr("The final hard binding differs from template "
"in unsupported ways")));
}
TEST_F(ManifestStoreGeneratorTest, BoxesHashByteSizeMismatch) {
BoxesHashAssertion box_hash;
BoxHash* box = box_hash.add_boxes();
box->add_names("some name");
box->set_hash(std::string(HashSizeBytes(HashAlgorithm::kSha256), '\0'));
GenerateManifestStoreRequest request;
*request.mutable_hard_binding()->mutable_box_hash() = box_hash;
*request.mutable_actions() = ParseTextProtoOrDie(R"pb(
created {
action_metadata {
software_agent { name: "test generator" }
description: "created for testing"
}
digital_source_type: "https://cv.iptc.org/newscodes/digitalsourcetype/computationalCapture"
}
)pb");
absl::StatusOr<ManifestStoreTemplate> manifest_store_template =
GenerateManifestStoreTemplate(*store_generator_, request);
ABSL_ASSERT_OK(manifest_store_template);
FinalizeManifestStoreRequest finalize_request;
*finalize_request.mutable_manifest_store_template() =
*manifest_store_template;
box_hash.mutable_boxes(0)->add_names("additional name");
*finalize_request.mutable_hard_binding()->mutable_box_hash() = box_hash;
EXPECT_THAT(FinalizeManifestStore(*store_generator_, finalize_request),
StatusIs(absl::StatusCode::kInvalidArgument,
HasSubstr("The final hard binding differs from template "
"in unsupported ways")));
}
TEST_F(ManifestStoreGeneratorTest, BmffBasedHashByteSizeMismatch) {
BmffBasedHashAssertion bmff_based_hash =
GetBmffHashTemplate(HashAlgorithm::kSha256);
GenerateManifestStoreRequest request;
*request.mutable_hard_binding()->mutable_bmff_based_hash() = bmff_based_hash;
*request.mutable_actions() = ParseTextProtoOrDie(R"pb(
created {
action_metadata {
software_agent { name: "test generator" }
description: "created for testing"
}
digital_source_type: "https://cv.iptc.org/newscodes/digitalsourcetype/computationalCapture"
}
)pb");
absl::StatusOr<ManifestStoreTemplate> manifest_store_template =
GenerateManifestStoreTemplate(*store_generator_, request);
ABSL_ASSERT_OK(manifest_store_template);
FinalizeManifestStoreRequest finalize_request;
*finalize_request.mutable_manifest_store_template() =
*manifest_store_template;
bmff_based_hash.add_exclusions()->set_xpath("additional exclusion");
*finalize_request.mutable_hard_binding()->mutable_bmff_based_hash() =
bmff_based_hash;
EXPECT_THAT(FinalizeManifestStore(*store_generator_, finalize_request),
StatusIs(absl::StatusCode::kInvalidArgument,
HasSubstr("The final hard binding differs from template "
"in unsupported ways")));
}
TEST_F(ManifestStoreGeneratorTest,
CollectionDataHashByteSizeMismatchDueToDifferentUriLength) {
CollectionDataHashAssertion collection_data_hash = ParseTextProtoOrDie(R"pb(
uris { uri: "some uri" hash: "some hash" type: "some type" }
alg: "sha256"
zip_central_directory_hash: "some hash"
)pb");
GenerateManifestStoreRequest request;
*request.mutable_hard_binding()->mutable_collection_data_hash() =
collection_data_hash;
*request.mutable_actions() = ParseTextProtoOrDie(R"pb(
created {
action_metadata {
software_agent { name: "test generator" }
description: "created for testing"
}
digital_source_type: "https://cv.iptc.org/newscodes/digitalsourcetype/computationalCapture"
}
)pb");
absl::StatusOr<ManifestStoreTemplate> manifest_store_template =
GenerateManifestStoreTemplate(*store_generator_, request);
ABSL_ASSERT_OK(manifest_store_template);
FinalizeManifestStoreRequest finalize_request;
*finalize_request.mutable_manifest_store_template() =
*manifest_store_template;
// The URI is different in length.
collection_data_hash.mutable_uris(0)->set_uri("updated uri");
*finalize_request.mutable_hard_binding()->mutable_collection_data_hash() =
collection_data_hash;
EXPECT_THAT(FinalizeManifestStore(*store_generator_, finalize_request),
StatusIs(absl::StatusCode::kInvalidArgument,
HasSubstr("The final hard binding differs from template "
"in unsupported ways")));
}
TEST_F(ManifestStoreGeneratorTest,
CollectionDataHashByteSizeMismatchDueToDifferentUriType) {
CollectionDataHashAssertion collection_data_hash = ParseTextProtoOrDie(R"pb(
uris { uri: "some uri" hash: "some hash" type: "some type" }
alg: "sha256"
zip_central_directory_hash: "some hash"
)pb");
GenerateManifestStoreRequest request;
*request.mutable_hard_binding()->mutable_collection_data_hash() =
collection_data_hash;
*request.mutable_actions() = ParseTextProtoOrDie(R"pb(
created {
action_metadata {
software_agent { name: "test generator" }
description: "created for testing"
}
digital_source_type: "https://cv.iptc.org/newscodes/digitalsourcetype/computationalCapture"
}
)pb");
absl::StatusOr<ManifestStoreTemplate> manifest_store_template =
GenerateManifestStoreTemplate(*store_generator_, request);
ABSL_ASSERT_OK(manifest_store_template);
FinalizeManifestStoreRequest finalize_request;
*finalize_request.mutable_manifest_store_template() =
*manifest_store_template;
collection_data_hash.mutable_uris(0)->set_type("updated type");
*finalize_request.mutable_hard_binding()->mutable_collection_data_hash() =
collection_data_hash;
EXPECT_THAT(FinalizeManifestStore(*store_generator_, finalize_request),
StatusIs(absl::StatusCode::kInvalidArgument,
HasSubstr("The final hard binding differs from template "
"in unsupported ways")));
}
TEST_F(ManifestStoreGeneratorTest,
CollectionDataHashByteSizeMismatchDueToDifferentUriCount) {
CollectionDataHashAssertion collection_data_hash = ParseTextProtoOrDie(R"pb(
uris { uri: "some uri" hash: "some hash" type: "some type" }
alg: "sha256"
zip_central_directory_hash: "some hash"
)pb");
GenerateManifestStoreRequest request;
*request.mutable_hard_binding()->mutable_collection_data_hash() =
collection_data_hash;
*request.mutable_actions() = ParseTextProtoOrDie(R"pb(
created {
action_metadata {
software_agent { name: "test generator" }
description: "created for testing"
}
digital_source_type: "https://cv.iptc.org/newscodes/digitalsourcetype/computationalCapture"
}
)pb");
absl::StatusOr<ManifestStoreTemplate> manifest_store_template =
GenerateManifestStoreTemplate(*store_generator_, request);
ABSL_ASSERT_OK(manifest_store_template);
FinalizeManifestStoreRequest finalize_request;
*finalize_request.mutable_manifest_store_template() =
*manifest_store_template;
UriHashedData* new_uri = collection_data_hash.add_uris();
new_uri->set_uri("new uri");
new_uri->set_hash("new hash");
new_uri->set_type("new type");
*finalize_request.mutable_hard_binding()->mutable_collection_data_hash() =
collection_data_hash;
EXPECT_THAT(FinalizeManifestStore(*store_generator_, finalize_request),
StatusIs(absl::StatusCode::kInvalidArgument,
HasSubstr("The final hard binding differs from template "
"in unsupported ways")));
}
TEST_F(ManifestStoreGeneratorTest, PartBindingByteSizeMismatch) {
DataHashAssertion data_hash;
data_hash.set_hash(std::string(HashSizeBytes(HashAlgorithm::kSha256), '\0'));
data_hash.set_pad("");
GenerateManifestStoreRequest request;
*request.mutable_hard_binding()->mutable_data_hash() = data_hash;
*request.mutable_hard_binding()->add_part_bindings()->mutable_data_hash() =
data_hash;
*request.mutable_actions() = ParseTextProtoOrDie(R"pb(
created {
action_metadata {
software_agent { name: "test generator" }
description: "created for testing"
}
digital_source_type: "https://cv.iptc.org/newscodes/digitalsourcetype/computationalCapture"
}
)pb");
absl::StatusOr<ManifestStoreTemplate> manifest_store_template =
GenerateManifestStoreTemplate(*store_generator_, request);
ABSL_ASSERT_OK(manifest_store_template);
FinalizeManifestStoreRequest finalize_request;
*finalize_request.mutable_manifest_store_template() =
*manifest_store_template;
data_hash.set_name("additional name");
*finalize_request.mutable_hard_binding()->mutable_data_hash() =
request.hard_binding().data_hash();
*finalize_request.mutable_hard_binding()
->add_part_bindings()
->mutable_data_hash() = data_hash;
EXPECT_THAT(
FinalizeManifestStore(*store_generator_, finalize_request),
StatusIs(
absl::StatusCode::kInvalidArgument,
HasSubstr(
"The part binding differs from template in unsupported ways.")));
}
TEST_F(ManifestStoreGeneratorTest, PartBindingCountMismatch) {
DataHashAssertion data_hash;
data_hash.set_hash(std::string(HashSizeBytes(HashAlgorithm::kSha256), '\0'));
data_hash.set_pad("");
GenerateManifestStoreRequest request;
*request.mutable_hard_binding()->mutable_data_hash() = data_hash;
*request.mutable_hard_binding()->add_part_bindings()->mutable_data_hash() =
data_hash;
*request.mutable_actions() = ParseTextProtoOrDie(R"pb(
created {
action_metadata {
software_agent { name: "test generator" }
description: "created for testing"
}
digital_source_type: "https://cv.iptc.org/newscodes/digitalsourcetype/computationalCapture"
}
)pb");
absl::StatusOr<ManifestStoreTemplate> manifest_store_template =
GenerateManifestStoreTemplate(*store_generator_, request);
ABSL_ASSERT_OK(manifest_store_template);
FinalizeManifestStoreRequest finalize_request;
*finalize_request.mutable_manifest_store_template() =
*manifest_store_template;
*finalize_request.mutable_hard_binding()->mutable_data_hash() =
request.hard_binding().data_hash();
// Different number of part_bindings.
EXPECT_THAT(FinalizeManifestStore(*store_generator_, finalize_request),
StatusIs(absl::StatusCode::kInvalidArgument,
HasSubstr("The number of part bindings differs between "
"template and finalize.")));
}
TEST_F(ManifestStoreGeneratorTest, HardBindingNotSet) {
// Create a template with no hard binding in the generate request.
GenerateManifestStoreRequest request = ParseTextProtoOrDie(R"pb(
actions {
created {
action_metadata {
software_agent { name: "test generator" }
description: "created for testing"
}
digital_source_type: "https://cv.iptc.org/newscodes/digitalsourcetype/computationalCapture"
}
}
)pb");
// Generate a template (this will fail as hard_binding is required).
EXPECT_THAT(
GenerateManifestStoreTemplate(*store_generator_, request),
StatusIs(absl::StatusCode::kInvalidArgument, HasSubstr("hard binding")));
// To test the BINDING_NOT_SET case in FinalizeManifestStore, we need to
// manually create a generator_context with no hard binding.
GenerateManifestStoreRequest template_request = request;
template_request.clear_hard_binding();
FinalizeManifestStoreRequest finalize_request;
finalize_request.mutable_manifest_store_template()->set_generator_context(
template_request.SerializeAsString());
*finalize_request.mutable_hard_binding() = HardBinding();
EXPECT_THAT(
FinalizeManifestStore(*store_generator_, finalize_request),
StatusIs(
absl::StatusCode::kInvalidArgument,
HasSubstr("The hard binding in the finalize request must be set")));
}
TEST_F(ManifestStoreGeneratorTest, ManifestStoreSizeLimitExceeded) {
GenerateManifestStoreRequest request = ParseTextProtoOrDie(R"pb(
instance_id: "12345678-1234-1234-1234-123456789012"
hard_binding {
box_hash {
alg: "some alg"
boxes { names: "some name" names: "other name" hash: "some hash" }
}
}
actions {
created {
action_metadata {
software_agent { name: "test generator" }
description: "created for testing"
}
digital_source_type: "https://cv.iptc.org/newscodes/digitalsourcetype/computationalCapture"
}
}
options { max_manifest_store_size_bytes: 1000 }
)pb");
EXPECT_THAT(GenerateManifestStore(*store_generator_, request),
StatusIs(absl::StatusCode::kFailedPrecondition,
HasSubstr("manifest store too large")));
}
TEST_F(ManifestStoreGeneratorTest, ManifestStoreSizeLimitNotExceeded) {
GenerateManifestStoreRequest request = ParseTextProtoOrDie(R"pb(
instance_id: "12345678-1234-1234-1234-123456789012"
hard_binding {
box_hash {
alg: "some alg"
boxes { names: "some name" names: "other name" hash: "some hash" }
}
}
actions {
created {
action_metadata {
software_agent { name: "test generator" }
description: "created for testing"
}
digital_source_type: "https://cv.iptc.org/newscodes/digitalsourcetype/computationalCapture"
}
}
options { max_manifest_store_size_bytes: 16384 }
)pb");
ABSL_EXPECT_OK(GenerateManifestStore(*store_generator_, request));
}
TEST_F(ManifestStoreGeneratorTest, PartBindingBmffBasedHashSizeMismatch) {
DataHashAssertion data_hash;
data_hash.set_hash(std::string(HashSizeBytes(HashAlgorithm::kSha256), '\0'));
data_hash.set_pad("");
GenerateManifestStoreRequest request;
*request.mutable_hard_binding()->mutable_data_hash() = data_hash;
BmffBasedHashAssertion bmff_based_hash =
GetBmffHashTemplate(HashAlgorithm::kSha256);
*request.mutable_hard_binding()
->add_part_bindings()
->mutable_bmff_based_hash() = bmff_based_hash;
*request.mutable_actions() = ParseTextProtoOrDie(R"pb(
created {
action_metadata {
software_agent { name: "test generator" }
description: "created for testing"
}
digital_source_type: "https://cv.iptc.org/newscodes/digitalsourcetype/computationalCapture"
}
)pb");
absl::StatusOr<ManifestStoreTemplate> manifest_store_template =
GenerateManifestStoreTemplate(*store_generator_, request);
ABSL_ASSERT_OK(manifest_store_template);
FinalizeManifestStoreRequest finalize_request;
*finalize_request.mutable_manifest_store_template() =
*manifest_store_template;
*finalize_request.mutable_hard_binding()->mutable_data_hash() =
request.hard_binding().data_hash();
bmff_based_hash.add_exclusions()->set_xpath("additional exclusion");
*finalize_request.mutable_hard_binding()
->add_part_bindings()
->mutable_bmff_based_hash() = bmff_based_hash;
EXPECT_THAT(
FinalizeManifestStore(*store_generator_, finalize_request),
StatusIs(
absl::StatusCode::kInvalidArgument,
HasSubstr(
"The part binding differs from template in unsupported ways.")));
}
TEST_F(ManifestStoreGeneratorTest, PartBindingBoxesHashSizeMismatch) {
DataHashAssertion data_hash;
data_hash.set_hash(std::string(HashSizeBytes(HashAlgorithm::kSha256), '\0'));
data_hash.set_pad("");
GenerateManifestStoreRequest request;
*request.mutable_hard_binding()->mutable_data_hash() = data_hash;
BoxesHashAssertion box_hash;
BoxHash* box = box_hash.add_boxes();
box->add_names("some name");
box->set_hash(std::string(HashSizeBytes(HashAlgorithm::kSha256), '\0'));
*request.mutable_hard_binding()->add_part_bindings()->mutable_box_hash() =
box_hash;
*request.mutable_actions() = ParseTextProtoOrDie(R"pb(
created {
action_metadata {
software_agent { name: "test generator" }
description: "created for testing"
}
digital_source_type: "https://cv.iptc.org/newscodes/digitalsourcetype/computationalCapture"
}
)pb");
absl::StatusOr<ManifestStoreTemplate> manifest_store_template =
GenerateManifestStoreTemplate(*store_generator_, request);
ABSL_ASSERT_OK(manifest_store_template);
FinalizeManifestStoreRequest finalize_request;
*finalize_request.mutable_manifest_store_template() =
*manifest_store_template;
*finalize_request.mutable_hard_binding()->mutable_data_hash() =
request.hard_binding().data_hash();
box_hash.mutable_boxes(0)->add_names("additional name");
*finalize_request.mutable_hard_binding()
->add_part_bindings()
->mutable_box_hash() = box_hash;
EXPECT_THAT(
FinalizeManifestStore(*store_generator_, finalize_request),
StatusIs(
absl::StatusCode::kInvalidArgument,
HasSubstr(
"The part binding differs from template in unsupported ways.")));
}
TEST_F(ManifestStoreGeneratorTest, PartBindingDataHashSizeMismatch) {
DataHashAssertion data_hash;
data_hash.set_hash(std::string(HashSizeBytes(HashAlgorithm::kSha256), '\0'));
data_hash.set_pad("");
GenerateManifestStoreRequest request;
*request.mutable_hard_binding()->mutable_data_hash() = data_hash;
*request.mutable_hard_binding()->add_part_bindings()->mutable_data_hash() =
data_hash;
*request.mutable_actions() = ParseTextProtoOrDie(R"pb(
created {
action_metadata {
software_agent { name: "test generator" }
description: "created for testing"
}
digital_source_type: "https://cv.iptc.org/newscodes/digitalsourcetype/computationalCapture"
}
)pb");
absl::StatusOr<ManifestStoreTemplate> manifest_store_template =
GenerateManifestStoreTemplate(*store_generator_, request);
ABSL_ASSERT_OK(manifest_store_template);
FinalizeManifestStoreRequest finalize_request;
*finalize_request.mutable_manifest_store_template() =
*manifest_store_template;
*finalize_request.mutable_hard_binding()->mutable_data_hash() =
request.hard_binding().data_hash();
data_hash.set_name("additional name");
*finalize_request.mutable_hard_binding()
->add_part_bindings()
->mutable_data_hash() = data_hash;
EXPECT_THAT(
FinalizeManifestStore(*store_generator_, finalize_request),
StatusIs(
absl::StatusCode::kInvalidArgument,
HasSubstr(
"The part binding differs from template in unsupported ways.")));
}
TEST_F(ManifestStoreGeneratorTest, PartBindingMatchingSizes) {
DataHashAssertion data_hash;
data_hash.set_hash(std::string(HashSizeBytes(HashAlgorithm::kSha256), '\0'));
data_hash.set_pad("");
GenerateManifestStoreRequest request;
// Hard binding
*request.mutable_hard_binding()->mutable_data_hash() = data_hash;
// Part binding 1: DataHash
*request.mutable_hard_binding()->add_part_bindings()->mutable_data_hash() =
data_hash;
// Part binding 2: BmffBasedHash
BmffBasedHashAssertion bmff_based_hash =
GetBmffHashTemplate(HashAlgorithm::kSha256);
*request.mutable_hard_binding()
->add_part_bindings()
->mutable_bmff_based_hash() = bmff_based_hash;
// Part binding 3: BoxesHash
BoxesHashAssertion box_hash;
BoxHash* box = box_hash.add_boxes();
box->add_names("some name");
box->set_hash(std::string(HashSizeBytes(HashAlgorithm::kSha256), '\0'));
*request.mutable_hard_binding()->add_part_bindings()->mutable_box_hash() =
box_hash;
*request.mutable_actions() = ParseTextProtoOrDie(R"pb(
created {
action_metadata {
software_agent { name: "test generator" }
description: "created for testing"
}
digital_source_type: "https://cv.iptc.org/newscodes/digitalsourcetype/computationalCapture"
}
)pb");
absl::StatusOr<ManifestStoreTemplate> manifest_store_template =
GenerateManifestStoreTemplate(*store_generator_, request);
ABSL_ASSERT_OK(manifest_store_template);
FinalizeManifestStoreRequest finalize_request;
*finalize_request.mutable_manifest_store_template() =
*manifest_store_template;
// Set the same hard binding and part bindings in the finalize request.
*finalize_request.mutable_hard_binding() = request.hard_binding();
// Modify the hashes to ensure finalization works.
std::string new_hash(HashSizeBytes(HashAlgorithm::kSha256), '\1');
finalize_request.mutable_hard_binding()->mutable_data_hash()->set_hash(
new_hash);
finalize_request.mutable_hard_binding()
->mutable_part_bindings(0)
->mutable_data_hash()
->set_hash(new_hash);
finalize_request.mutable_hard_binding()
->mutable_part_bindings(1)
->mutable_bmff_based_hash()
->set_hash(new_hash);
finalize_request.mutable_hard_binding()
->mutable_part_bindings(2)
->mutable_box_hash()
->mutable_boxes(0)
->set_hash(new_hash);
ABSL_EXPECT_OK(FinalizeManifestStore(*store_generator_, finalize_request));
}
TEST_F(ManifestStoreGeneratorTest,
GenerateNegativeMaxManifestStoreSizeBytesFails) {
GenerateManifestStoreRequest request = ParseTextProtoOrDie(R"pb(
actions {
created {
action_metadata {
software_agent { name: "test generator" }
description: "created for testing"
}
digital_source_type: "https://cv.iptc.org/newscodes/digitalsourcetype/computationalCapture"
}
}
hard_binding {
data_hash { alg: "sha256" hash: "00000000000000000000000000000000" }
}
options { max_manifest_store_size_bytes: -1 }
)pb");
EXPECT_THAT(
GenerateManifestStore(*store_generator_, request),
StatusIs(
absl::StatusCode::kInvalidArgument,
HasSubstr("max_manifest_store_size_bytes must be non-negative")));
}
TEST_F(ManifestStoreGeneratorTest, NegativeMaxBytesToShiftFails) {
GenerateManifestStoreRequest request = ParseTextProtoOrDie(R"pb(
actions { created { action_metadata { description: "Test Action" } } }
hard_binding {
data_hash { alg: "sha256" hash: "00000000000000000000000000000000" }
}
options { max_bytes_to_shift: -1 }
)pb");
EXPECT_THAT(GenerateManifestStore(*store_generator_, request),
StatusIs(absl::StatusCode::kInvalidArgument,
HasSubstr("max_bytes_to_shift must be non-negative")));
}
TEST_F(ManifestStoreGeneratorTest, NonNegativeMaxBytesToShiftSucceeds) {
GenerateManifestStoreRequest request = ParseTextProtoOrDie(R"pb(
actions { created { action_metadata { description: "Test Action" } } }
hard_binding {
data_hash { alg: "sha256" hash: "00000000000000000000000000000000" }
}
options { max_bytes_to_shift: 0 }
)pb");
ABSL_EXPECT_OK(GenerateManifestStore(*store_generator_, request));
}
TEST_F(ManifestStoreGeneratorTest, FinalizeNegativeManifestStoreSizeFails) {
FinalizeManifestStoreRequest finalize_request;
finalize_request.mutable_manifest_store_template()->set_generator_context(
"context");
finalize_request.mutable_manifest_store_template()->set_manifest_store_size(
-1);
*finalize_request.mutable_hard_binding()->mutable_data_hash() =
ParseTextProtoOrDie(R"pb(alg: "sha256"
hash: "00000000000000000000000000000000")pb");
EXPECT_THAT(
FinalizeManifestStore(*store_generator_, finalize_request),
StatusIs(
absl::StatusCode::kInvalidArgument,
HasSubstr("manifest_store_size in template must be non-negative")));
}
TEST_F(ManifestStoreGeneratorTest, ManifestStoreTransformerSuccess) {
GenerateManifestStoreRequest request = ParseTextProtoOrDie(R"pb(
actions {
created {
action_metadata {
software_agent { name: "test generator" }
description: "created for testing"
}
digital_source_type: "https://cv.iptc.org/newscodes/digitalsourcetype/computationalCapture"
}
}
hard_binding {
data_hash { alg: "sha256" hash: "00000000000000000000000000000000" }
}
)pb");
MockManifestStoreTransformer mock_transformer;
EXPECT_CALL(mock_transformer, Transform(testing::NotNull()))
.WillOnce([](jumbf::BuiltSuperBox* box) {
EXPECT_THAT(box, testing::NotNull());
EXPECT_FALSE(box->Peek().empty());
return absl::OkStatus();
});
std::unique_ptr<ManifestStoreGenerator> generator =
CreateGenerator(&mock_transformer);
ABSL_EXPECT_OK(GenerateManifestStore(*generator, request));
}
TEST_F(ManifestStoreGeneratorTest, ManifestStoreTransformerModifiesSuperBox) {
GenerateManifestStoreRequest request = ParseTextProtoOrDie(R"pb(
actions { created { action_metadata { description: "Test Action" } } }
hard_binding {
data_hash { alg: "sha256" hash: "00000000000000000000000000000000" }
}
)pb");
jumbf::SuperBoxBuilder builder(jumbf::kCborBoxTypeUuid, {});
ABSL_CHECK_OK(builder.AddContent(jumbf::kCborBoxType,
absl::Cord("custom transformed data")));
absl::StatusOr<jumbf::BuiltSuperBox> transformed_box =
std::move(builder).Finalize();
ABSL_ASSERT_OK(transformed_box);
std::string expected_transformed_store(transformed_box->Peek());
MockManifestStoreTransformer mock_transformer;
EXPECT_CALL(mock_transformer, Transform(testing::NotNull()))
.WillOnce([transformed_box = *std::move(transformed_box)](
jumbf::BuiltSuperBox* box) mutable {
EXPECT_THAT(box, testing::NotNull());
EXPECT_FALSE(box->Peek().empty());
*box = std::move(transformed_box);
return absl::OkStatus();
});
std::unique_ptr<ManifestStoreGenerator> generator =
CreateGenerator(&mock_transformer);
absl::StatusOr<GenerateManifestStoreResponse> response =
GenerateManifestStore(*generator, request);
ABSL_ASSERT_OK(response);
EXPECT_EQ(response->manifest_store(), expected_transformed_store);
}
TEST_F(ManifestStoreGeneratorTest, ManifestStoreTransformerErrorPropagates) {
GenerateManifestStoreRequest request = ParseTextProtoOrDie(R"pb(
actions { created { action_metadata { description: "Test Action" } } }
hard_binding {
data_hash { alg: "sha256" hash: "00000000000000000000000000000000" }
}
)pb");
MockManifestStoreTransformer mock_transformer;
EXPECT_CALL(mock_transformer, Transform(testing::NotNull()))
.WillOnce(testing::Return(absl::InternalError("transform failed")));
std::unique_ptr<ManifestStoreGenerator> generator =
CreateGenerator(&mock_transformer);
EXPECT_THAT(
GenerateManifestStore(*generator, request),
StatusIs(absl::StatusCode::kInternal, HasSubstr("transform failed")));
}
TEST_F(ManifestStoreGeneratorTest,
ManifestStoreTransformerTemplateErrorPropagates) {
GenerateManifestStoreRequest request = ParseTextProtoOrDie(R"pb(
actions { created { action_metadata { description: "Test Action" } } }
hard_binding {
data_hash { alg: "sha256" hash: "00000000000000000000000000000000" }
}
)pb");
MockManifestStoreTransformer mock_transformer;
EXPECT_CALL(mock_transformer, Transform(testing::NotNull()))
.WillOnce(
testing::Return(absl::InternalError("transform template failed")));
std::unique_ptr<ManifestStoreGenerator> generator =
CreateGenerator(&mock_transformer);
EXPECT_THAT(GenerateManifestStoreTemplate(*generator, request),
StatusIs(absl::StatusCode::kInternal,
HasSubstr("transform template failed")));
}
} // namespace
} // namespace credentio