[storage][volume_image]: Implement Serialization.
Implement and test serialization for VolumeImage.
Bug: 48256
TEST=storage-volume-image-tests
Change-Id: I6c1d66b7e9626e9a872794a2d1536bec8012c076
Reviewed-on: https://fuchsia-review.googlesource.com/c/fuchsia/+/372553
Reviewed-by: Richard Chen <tianruichen@google.com>
Testability-Review: Gianfranco Valentino <gevalentino@google.com>
Commit-Queue: Gianfranco Valentino <gevalentino@google.com>
diff --git a/src/storage/volume_image/BUILD.gn b/src/storage/volume_image/BUILD.gn
index 0862b7a..7aa1b1c 100644
--- a/src/storage/volume_image/BUILD.gn
+++ b/src/storage/volume_image/BUILD.gn
@@ -17,6 +17,7 @@
]
public_deps = [
"utils:guid",
+ "//third_party/rapidjson",
"//zircon/public/lib/fbl",
"//zircon/public/lib/fit",
]
@@ -30,7 +31,10 @@
]
deps = [
":volume-descriptor",
+ "serialization:schema",
"//src/lib/fxl/test:gtest_main",
+ "//src/lib/json_parser",
+ "//third_party/googletest:gmock",
"//third_party/googletest:gtest",
"//zircon/public/lib/fdio",
]
@@ -39,6 +43,7 @@
unittest_package("storage-volume-image-tests") {
deps = [
":volume-descriptor-test",
+ "serialization:serialization-test",
"utils:utils-test",
]
@@ -51,6 +56,18 @@
name = "utils-test"
environments = basic_envs
},
+ {
+ name = "serialization-test"
+ environments = basic_envs
+ },
+ ]
+
+ resources = [
+ {
+ path = rebase_path(
+ "//src/storage/volume_image/serialization/volume_descriptor.schema.json")
+ dest = "schema/volume_descriptor.schema.json"
+ },
]
}
diff --git a/src/storage/volume_image/serialization/BUILD.gn b/src/storage/volume_image/serialization/BUILD.gn
new file mode 100644
index 0000000..5eee566
--- /dev/null
+++ b/src/storage/volume_image/serialization/BUILD.gn
@@ -0,0 +1,42 @@
+# Copyright 2020 The Fuchsia Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file
+
+config("schema_path") {
+ if (!is_fuchsia) {
+ defines = [ "STORAGE_VOLUME_IMAGE_SCHEMA_PATH=\"schema/\"" ]
+ } else {
+ defines = [ "STORAGE_VOLUME_IMAGE_SCHEMA_PATH=\"pkg/data/schema/\"" ]
+ }
+}
+
+copy("schema_json") {
+ sources = [ "volume_descriptor.schema.json" ]
+ outputs = [ "$root_out_dir/schema/{{source_file_part}}" ]
+}
+
+source_set("schema") {
+ sources = [ "schema.cc" ]
+ public = [ "schema.h" ]
+
+ configs += [ ":schema_path" ]
+
+ if (!is_fuchsia) {
+ deps += [ ":schema_json" ]
+ metadata = {
+ runtime_deps = get_target_outputs(":copy")
+ }
+ }
+}
+
+executable("serialization-test") {
+ testonly = true
+ sources = [ "schema_test.cc" ]
+ deps = [
+ ":schema",
+ "//src/lib/fxl/test:gtest_main",
+ "//src/lib/json_parser",
+ "//third_party/googletest:gtest",
+ "//zircon/public/lib/fdio",
+ ]
+}
diff --git a/src/storage/volume_image/serialization/schema.cc b/src/storage/volume_image/serialization/schema.cc
new file mode 100644
index 0000000..fd5d49a
--- /dev/null
+++ b/src/storage/volume_image/serialization/schema.cc
@@ -0,0 +1,29 @@
+// Copyright 2020 The Fuchsia Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "src/storage/volume_image/serialization/schema.h"
+
+#include <fstream>
+#include <iterator>
+#include <streambuf>
+#include <string>
+
+namespace storage::volume_image {
+
+// Path to where the schema leaves. Left to be injected by a compile time definition.
+static constexpr char kPath[] = STORAGE_VOLUME_IMAGE_SCHEMA_PATH;
+
+std::string GetSchema(Schema schema) {
+ std::ifstream file;
+
+ switch (schema) {
+ case Schema::kVolumeDescriptor:
+ file.open(std::string(kPath).append("volume_descriptor.schema.json"));
+ return std::string(std::istreambuf_iterator<char>(file), std::istreambuf_iterator<char>());
+ default:
+ return std::string();
+ }
+}
+
+} // namespace storage::volume_image
diff --git a/src/storage/volume_image/serialization/schema.h b/src/storage/volume_image/serialization/schema.h
new file mode 100644
index 0000000..8352576
--- /dev/null
+++ b/src/storage/volume_image/serialization/schema.h
@@ -0,0 +1,24 @@
+// Copyright 2020 The Fuchsia Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SRC_STORAGE_VOLUME_IMAGE_SERIALIZATION_SCHEMA_H_
+#define SRC_STORAGE_VOLUME_IMAGE_SERIALIZATION_SCHEMA_H_
+
+#include <string>
+
+namespace storage::volume_image {
+
+// Defines available schemas.
+enum Schema {
+ kVolumeDescriptor,
+};
+
+// Returns a string with the respective schema.
+//
+// On error, returns an empty string.
+std::string GetSchema(Schema schema);
+
+} // namespace storage::volume_image
+
+#endif // SRC_STORAGE_VOLUME_IMAGE_SERIALIZATION_SCHEMA_H_
diff --git a/src/storage/volume_image/serialization/schema_test.cc b/src/storage/volume_image/serialization/schema_test.cc
new file mode 100644
index 0000000..537104c
--- /dev/null
+++ b/src/storage/volume_image/serialization/schema_test.cc
@@ -0,0 +1,24 @@
+// Copyright 2020 The Fuchsia Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "schema.h"
+
+#include <gtest/gtest.h>
+
+#include "rapidjson/schema.h"
+#include "src/lib/json_parser/json_parser.h"
+
+namespace storage::volume_image {
+namespace {
+
+TEST(SchemaTest, VolumeDescriptorSchemaIsValid) {
+ auto schema_json = GetSchema(Schema::kVolumeDescriptor);
+
+ json_parser::JSONParser parser;
+ auto document = parser.ParseFromString(schema_json, "volume_descriptor.schema.json");
+ ASSERT_FALSE(parser.HasError()) << parser.error_str();
+}
+
+} // namespace
+} // namespace storage::volume_image
diff --git a/src/storage/volume_image/serialization/volume_descriptor.schema.json b/src/storage/volume_image/serialization/volume_descriptor.schema.json
index 5cc7e47..a681d25 100644
--- a/src/storage/volume_image/serialization/volume_descriptor.schema.json
+++ b/src/storage/volume_image/serialization/volume_descriptor.schema.json
@@ -54,13 +54,14 @@
},
"options": {
"type": "array",
- "items": {
+ "contains": {
"type": "integer",
"minimum": 0
},
"uniqueItems": true
}
},
+ "additionalProperties": false,
"required": [
"magic",
"instance_guid",
@@ -70,4 +71,6 @@
]
}
},
+ "type": "object",
+ "$ref": "#/definitions/volume_descriptor"
}
\ No newline at end of file
diff --git a/src/storage/volume_image/utils/guid.cc b/src/storage/volume_image/utils/guid.cc
index ca3124f..1b472a9 100644
--- a/src/storage/volume_image/utils/guid.cc
+++ b/src/storage/volume_image/utils/guid.cc
@@ -107,7 +107,9 @@
fit::result<std::string, std::string> Guid::ToString(fbl::Span<const uint8_t> guid) {
if (guid.size() != kGuidLength) {
- return fit::error("Input GUID size must be equal to |kGuidLength|.\n");
+ std::string error = "Input GUID size must be equal to |kGuidLength|. Input Size: ";
+ error.append(std::to_string(guid.size())).append(".\n");
+ return fit::error(error);
}
std::array<char, kGuidStrLength> out_guid;
@@ -135,7 +137,9 @@
fit::result<std::array<uint8_t, kGuidLength>, std::string> Guid::FromString(
fbl::Span<const char> guid) {
if (guid.size() != kGuidStrLength) {
- return fit::error("Input GUID size must be equal to |kGuidStrLength|.\n");
+ std::string error = "Input GUID size must be equal to |kGuidStrLength|. Input Size: ";
+ error.append(std::to_string(guid.size())).append(".\n");
+ return fit::error(error);
}
std::array<uint8_t, kGuidLength> out_guid;
diff --git a/src/storage/volume_image/volume_descriptor.cc b/src/storage/volume_image/volume_descriptor.cc
index fdce49f..2e936d0 100644
--- a/src/storage/volume_image/volume_descriptor.cc
+++ b/src/storage/volume_image/volume_descriptor.cc
@@ -4,4 +4,158 @@
#include "src/storage/volume_image/volume_descriptor.h"
-namespace storage::volume_image {}
+#include <algorithm>
+#include <array>
+#include <cstdlib>
+#include <sstream>
+#include <string_view>
+#include <vector>
+
+#include "rapidjson/document.h"
+#include "rapidjson/error/en.h"
+#include "rapidjson/error/error.h"
+#include "rapidjson/stringbuffer.h"
+#include "rapidjson/writer.h"
+#include "src/storage/volume_image/options.h"
+#include "src/storage/volume_image/utils/guid.h"
+
+namespace storage::volume_image {
+
+fit::result<VolumeDescriptor, std::string> VolumeDescriptor::Deserialize(
+ fbl::Span<const uint8_t> serialized) {
+ rapidjson::Document document;
+ rapidjson::ParseResult result =
+ document.Parse(reinterpret_cast<const char*>(serialized.data()), serialized.size());
+
+ if (result.IsError()) {
+ std::ostringstream error;
+ error << "Error parsing serialized VolumeDescriptor. "
+ << rapidjson::GetParseError_En(result.Code()) << std::endl;
+ return fit::error(error.str());
+ }
+
+ uint64_t magic = document["magic"].GetUint64();
+ if (magic != kMagic) {
+ return fit::error("Invalid Magic\n");
+ }
+
+ VolumeDescriptor descriptor = {};
+ const std::string& instance_guid = document["instance_guid"].GetString();
+ // The stringified version includes 4 Hyphens.
+ if (instance_guid.length() != kGuidStrLength) {
+ return fit::error("instance_guid length must be 36 bytes.\n");
+ }
+ auto instance_bytes = Guid::FromString(instance_guid);
+ if (instance_bytes.is_error()) {
+ return instance_bytes.take_error_result();
+ }
+ descriptor.instance = instance_bytes.take_value();
+
+ const std::string& type_guid = document["type_guid"].GetString();
+ // The stringified version includes 4 Hyphens.
+ if (type_guid.length() != kGuidStrLength) {
+ return fit::error("type_guid length must be 36 bytes.\n");
+ }
+
+ auto type_bytes = Guid::FromString(type_guid);
+ if (type_bytes.is_error()) {
+ return type_bytes.take_error_result();
+ }
+ descriptor.type = type_bytes.take_value();
+
+ const std::string& name = document["name"].GetString();
+ memcpy(descriptor.name.data(), name.c_str(), name.length());
+
+ descriptor.block_size = document["block_size"].GetUint64();
+ auto& compression = descriptor.compression;
+ auto compression_enum =
+ StringAsEnum<CompressionSchema>(document["compression_schema"].GetString());
+ if (compression_enum.is_error()) {
+ return compression_enum.take_error_result();
+ }
+ compression.schema = compression_enum.take_value();
+
+ if (document.HasMember("compression_options")) {
+ const auto& option_map = document["compression_options"].GetObject();
+ for (auto& option : option_map) {
+ compression.options[option.name.GetString()] = option.value.GetUint64();
+ }
+ }
+ auto encryption_enum = StringAsEnum<EncryptionType>(document["encryption_type"].GetString());
+ if (encryption_enum.is_error()) {
+ return encryption_enum.take_error_result();
+ }
+ descriptor.encryption = encryption_enum.take_value();
+
+ if (document.HasMember("options")) {
+ const auto& option_set = document["options"].GetArray();
+ for (auto& option : option_set) {
+ auto option_enum = StringAsEnum<Option>(option.GetString());
+ if (option_enum.is_error()) {
+ return option_enum.take_error_result();
+ }
+ descriptor.options.insert(option_enum.take_value());
+ }
+ }
+
+ return fit::ok(descriptor);
+}
+
+fit::result<std::vector<uint8_t>, std::string> VolumeDescriptor::Serialize() const {
+ rapidjson::Document document;
+ document.SetObject();
+
+ document.AddMember("magic", kMagic, document.GetAllocator());
+ auto instance_str = Guid::ToString(instance);
+ if (instance_str.is_error()) {
+ return instance_str.take_error_result();
+ }
+ document.AddMember("instance_guid", instance_str.take_value(), document.GetAllocator());
+
+ auto type_str = Guid::ToString(type);
+ if (type_str.is_error()) {
+ return type_str.take_error_result();
+ }
+ document.AddMember("type_guid", type_str.take_value(), document.GetAllocator());
+ document.AddMember("name", std::string(reinterpret_cast<const char*>(name.data())),
+ document.GetAllocator());
+ document.AddMember("block_size", block_size, document.GetAllocator());
+ document.AddMember("encryption_type", EnumAsString(encryption), document.GetAllocator());
+ document.AddMember("compression_schema", EnumAsString(compression.schema),
+ document.GetAllocator());
+
+ if (!compression.options.empty()) {
+ rapidjson::Value option_map;
+ option_map.SetObject();
+ for (const auto& option : compression.options) {
+ rapidjson::Value key(option.first.c_str(), document.GetAllocator());
+ rapidjson::Value value(option.second);
+ option_map.AddMember(key, value, document.GetAllocator());
+ }
+ document.AddMember("compression_options", option_map, document.GetAllocator());
+ }
+
+ if (!options.empty()) {
+ rapidjson::Value option_set;
+ option_set.SetArray();
+ for (const auto& option : options) {
+ rapidjson::Value value(EnumAsString(option).c_str(), document.GetAllocator());
+ option_set.PushBack(value, document.GetAllocator());
+ }
+ document.AddMember("options", option_set, document.GetAllocator());
+ }
+
+ rapidjson::StringBuffer buffer;
+ rapidjson::Writer<rapidjson::StringBuffer> writer(buffer);
+ if (!document.Accept(writer)) {
+ return fit::error("Failed to obtain string representation of VolumeDescriptor.\n");
+ }
+
+ const auto* serialized_content = reinterpret_cast<const uint8_t*>(buffer.GetString());
+ std::vector<uint8_t> data(serialized_content, serialized_content + buffer.GetLength());
+ data.push_back('\0');
+
+ return fit::ok(data);
+}
+
+} // namespace storage::volume_image
diff --git a/src/storage/volume_image/volume_descriptor.h b/src/storage/volume_image/volume_descriptor.h
index abfef65..a7910e5 100644
--- a/src/storage/volume_image/volume_descriptor.h
+++ b/src/storage/volume_image/volume_descriptor.h
@@ -14,10 +14,10 @@
#include "src/storage/volume_image/block_io.h"
#include "src/storage/volume_image/options.h"
+#include "src/storage/volume_image/utils/guid.h"
namespace storage::volume_image {
-constexpr uint64_t kGuidLength = 16;
constexpr uint64_t kNameLength = 40;
// Metadata describing the block image to be generated.
@@ -25,7 +25,14 @@
static constexpr uint64_t kMagic = 0xB10C14;
// On success returns the VolumeDescriptor with the deserialized contents of |serialized|.
- static fit::result<VolumeDescriptor, std::string> Deserialize(fbl::Span<uint8_t> serialized);
+ static fit::result<VolumeDescriptor, std::string> Deserialize(
+ fbl::Span<const uint8_t> serialized);
+
+ // On success returns the VolumeDescriptor with the deserialized contents of |serialized|.
+ static fit::result<VolumeDescriptor, std::string> Deserialize(fbl::Span<const char> serialized) {
+ return Deserialize(fbl::Span<const uint8_t>(reinterpret_cast<const uint8_t*>(serialized.data()),
+ serialized.size() * sizeof(char)));
+ }
// Returns a byte vector containing the serialized version data.
// The serialization is meant to be human readable.
diff --git a/src/storage/volume_image/volume_descriptor_test.cc b/src/storage/volume_image/volume_descriptor_test.cc
index 4329a80..224fce0 100644
--- a/src/storage/volume_image/volume_descriptor_test.cc
+++ b/src/storage/volume_image/volume_descriptor_test.cc
@@ -4,16 +4,175 @@
#include "src/storage/volume_image/volume_descriptor.h"
-#include <gtest/gtest.h>
+#include <lib/fit/function.h>
+
+#include <limits>
+#include <string_view>
+#include <type_traits>
+
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+#include "rapidjson/document.h"
+#include "rapidjson/schema.h"
+#include "rapidjson/stringbuffer.h"
+#include "rapidjson/writer.h"
+#include "src/lib/fxl/strings/string_view.h"
+#include "src/lib/json_parser/json_parser.h"
+#include "src/lib/json_parser/rapidjson_validation.h"
+#include "src/storage/volume_image/options.h"
+#include "src/storage/volume_image/serialization/schema.h"
namespace storage::volume_image {
namespace {
-TEST(VolumeDescriptorTest, SerializeReturnsValidData) {}
+TEST(VolumeDescriptorTest, SerializeReturnsSchemaValidData) {
+ VolumeDescriptor descriptor = {};
+ descriptor.compression.schema = CompressionSchema::kLz4;
+ descriptor.compression.options = {{"option_1", 1}};
+ descriptor.options = {Option::kNone};
+ descriptor.encryption = EncryptionType::kZxcrypt;
+ auto schema_json = GetSchema(Schema::kVolumeDescriptor);
-TEST(VolumeDescriptorTest, DeserializeFromValidDataReturns) {}
+ json_parser::JSONParser parser;
+ auto result = descriptor.Serialize();
+ ASSERT_TRUE(result.is_ok()) << result.error();
+ auto value = result.take_value();
+ auto document =
+ parser.ParseFromString(std::string(value.begin(), value.end()), "serialized.json");
+ ASSERT_FALSE(parser.HasError()) << parser.error_str();
+ std::unique_ptr<rapidjson::SchemaDocument> schema = json_parser::InitSchema(schema_json);
+ EXPECT_TRUE(json_parser::ValidateSchema(document, *schema, "VolumeDescriptor::Serialize output"));
+}
-TEST(VolumeDescriptorTest, DeserializeWithInvalidDataReturnsError) {}
+std::string GetSerializedJson(fit::function<void(rapidjson::Document*)> mutator = nullptr) {
+ // A Valid JSON being serialized.
+ static constexpr std::string_view kSerializedVolumeDescriptor = R"(
+ {
+ "magic": 11602964,
+ "instance_guid": "04030201-0605-0807-1009-111213141516",
+ "type_guid": "A4A3A2A1-B6B5-C8C7-D0D1-E0E1E2E3E4E5",
+ "name": "i-have-a-name",
+ "block_size": 512,
+ "encryption_type": "ENCRYPTION_TYPE_ZXCRYPT",
+ "compression_schema": "COMPRESSION_SCHEMA_LZ4",
+ "compression_options": {
+ "random_option": 24,
+ "random_option_2": 25
+ },
+ "options" : [
+ "OPTION_NONE",
+ "OPTION_EMPTY"
+ ]
+ }
+ )";
+ json_parser::JSONParser parser;
+ rapidjson::Document parsed_document = parser.ParseFromString(kSerializedVolumeDescriptor.data(),
+ "serialized_volume_descriptor.json");
+ ZX_ASSERT_MSG(!parser.HasError(), "%s\n", parser.error_str().c_str());
+ if (mutator != nullptr) {
+ mutator(&parsed_document);
+ }
+ rapidjson::StringBuffer buffer;
+ rapidjson::Writer<rapidjson::StringBuffer> writer(buffer);
+ parsed_document.Accept(writer);
+ return buffer.GetString();
+}
+
+TEST(VolumeDescriptorTest, DeserializeSerializedDataIsOk) {
+ const auto deserialized_result = VolumeDescriptor::Deserialize(GetSerializedJson());
+ ASSERT_TRUE(deserialized_result.is_ok());
+
+ const auto serialized_result = deserialized_result.value().Serialize();
+ ASSERT_TRUE(serialized_result.is_ok());
+
+ const auto redeserialized_result = VolumeDescriptor::Deserialize(serialized_result.value());
+ ASSERT_TRUE(redeserialized_result.is_ok());
+
+ const auto& deserialized_1 = deserialized_result.value();
+ const auto& deserialized_2 = deserialized_result.value();
+
+ ASSERT_EQ(deserialized_1.type, deserialized_2.type);
+ ASSERT_EQ(deserialized_1.block_size, deserialized_2.block_size);
+ ASSERT_EQ(deserialized_1.instance, deserialized_2.instance);
+ ASSERT_EQ(deserialized_1.name, deserialized_2.name);
+ ASSERT_EQ(deserialized_1.compression.schema, deserialized_2.compression.schema);
+ ASSERT_EQ(deserialized_1.compression.options, deserialized_2.compression.options);
+ ASSERT_EQ(deserialized_1.encryption, deserialized_2.encryption);
+ ASSERT_EQ(deserialized_1.options, deserialized_2.options);
+}
+
+TEST(VolumeDescriptorTest, DeserializeFromValidDataReturnsVolumeDescriptor) {
+ constexpr std::string_view kTypeGuid = "A4A3A2A1-B6B5-C8C7-D0D1-E0E1E2E3E4E5";
+ constexpr std::string_view kInstanceGuid = "04030201-0605-0807-1009-111213141516";
+ constexpr std::string_view kName = "i-have-a-name";
+ const std::string kSerializedVolumeDescriptor = GetSerializedJson();
+
+ auto descriptor_result = VolumeDescriptor::Deserialize(kSerializedVolumeDescriptor);
+ ASSERT_TRUE(descriptor_result.is_ok()) << descriptor_result.take_error();
+ auto descriptor = descriptor_result.take_value();
+ auto expected_type_guid = Guid::FromString(kTypeGuid);
+ auto expected_instance_guid = Guid::FromString(kInstanceGuid);
+ ASSERT_TRUE(expected_type_guid.is_ok());
+ ASSERT_TRUE(expected_instance_guid.is_ok());
+
+ EXPECT_EQ(expected_type_guid.value(), descriptor.type);
+ EXPECT_EQ(expected_instance_guid.value(), descriptor.instance);
+ EXPECT_EQ(kName, std::string(reinterpret_cast<const char*>(descriptor.name.data())));
+ EXPECT_EQ(512u, descriptor.block_size);
+ EXPECT_EQ(EncryptionType::kZxcrypt, descriptor.encryption);
+ EXPECT_EQ(CompressionSchema::kLz4, descriptor.compression.schema);
+ std::map<std::string, uint64_t> kExpectedCompressionOptions = {{"random_option", 24},
+ {"random_option_2", 25}};
+ EXPECT_THAT(descriptor.compression.options,
+ ::testing::UnorderedElementsAreArray(kExpectedCompressionOptions));
+ EXPECT_THAT(descriptor.options, ::testing::UnorderedElementsAre(Option::kNone, Option::kEmpty));
+}
+
+TEST(VolumeDescriptorTest, DeserializeWithBadTypeGuidIsError) {
+ ASSERT_TRUE(VolumeDescriptor::Deserialize(GetSerializedJson([](auto* document) {
+ (*document)["type_guid"] = "012345678";
+ })).is_error());
+}
+
+TEST(VolumeDescriptorTest, DeserializeWithBadInstanceGuidIsError) {
+ ASSERT_TRUE(VolumeDescriptor::Deserialize(GetSerializedJson([](auto* document) {
+ (*document)["instance_guid"] = "012345678";
+ })).is_error());
+}
+
+TEST(VolumeDescriptorTest, DeserializeWithLongNameIsTruncated) {
+ constexpr std::string_view kName = "01234567890123456789012345678901234567891";
+ auto descriptor_result = VolumeDescriptor::Deserialize(GetSerializedJson(
+ [kName](auto* document) { (*document)["name"] = rapidjson::StringRef(kName.data()); }));
+ ASSERT_TRUE(descriptor_result.is_ok());
+ ASSERT_EQ(kName.substr(0, kNameLength),
+ std::string(reinterpret_cast<const char*>(descriptor_result.value().name.data())));
+}
+
+TEST(VolumeDescriptorTest, DeserializeWithBadMagicIsError) {
+ ASSERT_TRUE(VolumeDescriptor::Deserialize(GetSerializedJson([](auto* document) {
+ (*document)["magic"] = 0xB201C4;
+ })).is_error());
+}
+
+TEST(VolumeDescriptorTest, DeserializeWithBadCompressionSchemaIsError) {
+ ASSERT_TRUE(VolumeDescriptor::Deserialize(GetSerializedJson([](auto* document) {
+ (*document)["compression_schema"] = "BAD_OR_UNKNOWN_SCHEMA";
+ })).is_error());
+}
+
+TEST(VolumeDescriptorTest, DeserializeWithBadEncryptionTypeIsError) {
+ ASSERT_TRUE(VolumeDescriptor::Deserialize(GetSerializedJson([](auto* document) {
+ (*document)["encryption_type"] = "BAD_OR_UNKNOWN_ENCRYPTION";
+ })).is_error());
+}
+
+TEST(VolumeDescriptorTest, DeserializeWithBadOptionIsError) {
+ ASSERT_TRUE(VolumeDescriptor::Deserialize(GetSerializedJson([](auto* document) {
+ (*document)["options"].GetArray().PushBack("BAD_OR_UNKNOWN_OPTION",
+ document->GetAllocator());
+ })).is_error());
+}
} // namespace
} // namespace storage::volume_image