| // 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 "formats/pdf/manifest_store_merger.h" | |
| #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 "constants/labels.h" | |
| #include "gmock/gmock.h" | |
| #include "gtest/gtest.h" | |
| #include "jumbf/box.h" | |
| #include "jumbf/box_builder.h" | |
| #include "jumbf/constants.h" | |
| #include "jumbf/encode_params.h" | |
| #include "jumbf/parse.h" | |
| #include "uuid/uuid.h" | |
| namespace credentio { | |
| namespace { | |
| using ::absl_testing::IsOkAndHolds; | |
| using ::absl_testing::StatusIs; | |
| using ::credentio::kManifestStoreLabel; | |
| using ::credentio::kManifestStoreUuid; | |
| using ::credentio::kStandardManifestUuid; | |
| using ::jumbf::DescriptionBoxOptions; | |
| using ::jumbf::kCborBoxType; | |
| using ::jumbf::SuperBox; | |
| using ::jumbf::SuperBoxBuilder; | |
| std::string CreateTestManifestStore(absl::string_view manifest_label, | |
| absl::string_view payload_data) { | |
| SuperBoxBuilder manifest_builder( | |
| kStandardManifestUuid, | |
| DescriptionBoxOptions{.requestable_iff_label = true, | |
| .label = absl::Cord(manifest_label)}); | |
| ABSL_CHECK_OK( | |
| manifest_builder.AddContent(kCborBoxType, absl::Cord(payload_data))); | |
| absl::StatusOr<jumbf::BuiltSuperBox> manifest_box = | |
| std::move(manifest_builder).Finalize(); | |
| ABSL_CHECK_OK(manifest_box); | |
| SuperBoxBuilder store_builder( | |
| kManifestStoreUuid, | |
| DescriptionBoxOptions{.requestable_iff_label = true, | |
| .label = absl::Cord(kManifestStoreLabel)}); | |
| ABSL_CHECK_OK(store_builder.AddChild(*std::move(manifest_box))); | |
| absl::StatusOr<jumbf::BuiltSuperBox> store_box = | |
| std::move(store_builder).Finalize(); | |
| ABSL_CHECK_OK(store_box); | |
| return std::string(std::move(*store_box).AsCord()); | |
| } | |
| TEST(ManifestStoreMergerTest, EmptyStoresListReturnsError) { | |
| EXPECT_THAT(MergeManifestStores({}), | |
| StatusIs(absl::StatusCode::kInvalidArgument)); | |
| } | |
| TEST(ManifestStoreMergerTest, SingleStoreReturnedUnmodified) { | |
| std::string store = CreateTestManifestStore("test_manifest", "data"); | |
| EXPECT_THAT(MergeManifestStores({store}), IsOkAndHolds(store)); | |
| } | |
| TEST(ManifestStoreMergerTest, MultipleStoresMergedInCorrectOrder) { | |
| std::string store1 = CreateTestManifestStore("manifest_1", "data_1"); | |
| std::string store2 = CreateTestManifestStore("manifest_2", "data_2"); | |
| absl::StatusOr<std::string> merged = MergeManifestStores({store1, store2}); | |
| ABSL_ASSERT_OK(merged); | |
| absl::string_view view = *merged; | |
| absl::StatusOr<SuperBox> superbox = | |
| jumbf::ConsumeSuperBox(&view, /*recursion_limit=*/9); | |
| ABSL_ASSERT_OK(superbox); | |
| EXPECT_TRUE(view.empty()); // Entire buffer consumed. | |
| EXPECT_EQ(superbox->description.type_uuid, kManifestStoreUuid); | |
| ASSERT_EQ(superbox->contents.size(), 2); | |
| // Active manifest from store2 should be placed LAST in contents. | |
| const auto& box2 = superbox->contents.back(); | |
| EXPECT_TRUE(box2.Holds<SuperBox>()); | |
| EXPECT_EQ(box2.Get<SuperBox>().description.type_uuid, kStandardManifestUuid); | |
| EXPECT_EQ(box2.Get<SuperBox>().description.label, "manifest_2"); | |
| // Initial manifest from store1 should be placed FIRST in contents. | |
| const auto& box1 = superbox->contents.front(); | |
| EXPECT_TRUE(box1.Holds<SuperBox>()); | |
| EXPECT_EQ(box1.Get<SuperBox>().description.type_uuid, kStandardManifestUuid); | |
| EXPECT_EQ(box1.Get<SuperBox>().description.label, "manifest_1"); | |
| } | |
| TEST(ManifestStoreMergerTest, MalformedStoreReturnsError) { | |
| std::string valid_store = CreateTestManifestStore("manifest_1", "data_1"); | |
| std::string malformed_store = "not a jumbf box"; | |
| EXPECT_FALSE(MergeManifestStores({valid_store, malformed_store}).ok()); | |
| } | |
| } // namespace | |
| } // namespace credentio |