diff --git a/Cargo.lock b/Cargo.lock index 78a45a72..1214323f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4478,6 +4478,7 @@ dependencies = [ "propolis_types", "schemars", "serde", + "serde_json", "serde_with", "thiserror", "uuid", diff --git a/crates/propolis-api-types/Cargo.toml b/crates/propolis-api-types/Cargo.toml index 6a510015..55874a4e 100644 --- a/crates/propolis-api-types/Cargo.toml +++ b/crates/propolis-api-types/Cargo.toml @@ -15,3 +15,6 @@ serde.workspace = true serde_with.workspace = true thiserror.workspace = true uuid.workspace = true + +[dev-dependencies] +serde_json.workspace = true diff --git a/crates/propolis-api-types/src/instance_spec/mod.rs b/crates/propolis-api-types/src/instance_spec/mod.rs index 868c20dd..927c4ac7 100644 --- a/crates/propolis-api-types/src/instance_spec/mod.rs +++ b/crates/propolis-api-types/src/instance_spec/mod.rs @@ -250,3 +250,57 @@ impl From for SpecKey { pub enum VersionedInstanceSpec { V0(v0::InstanceSpecV0), } + +#[cfg(test)] +mod test { + use std::collections::BTreeMap; + + use uuid::Uuid; + + use super::{components::devices::QemuPvpanic, v0::ComponentV0, SpecKey}; + + type TestMap = BTreeMap; + + // Verifies that UUID-type spec keys that are serialized and deserialized + // continue to be interpreted as UUID-type spec keys. + #[test] + fn spec_key_uuid_roundtrip() { + let id = Uuid::new_v4(); + let mut map = TestMap::new(); + map.insert( + SpecKey::Uuid(id), + ComponentV0::QemuPvpanic(QemuPvpanic { enable_isa: true }), + ); + + let ser = serde_json::to_string(&map).unwrap(); + let unser: TestMap = serde_json::from_str(&ser).unwrap(); + let key = unser.keys().next().expect("one key in the map"); + let SpecKey::Uuid(got_id) = key else { + panic!("expected SpecKey::Uuid, got {}", key); + }; + + assert_eq!(*got_id, id); + } + + // Verifies that serializing a name-type spec key that happens to be the + // string representation of a UUID causes the key to deserialize as a + // UUID-type key. + #[test] + fn spec_key_uuid_string_deserializes_as_uuid_variant() { + let id = Uuid::new_v4(); + let mut map = TestMap::new(); + map.insert( + SpecKey::Name(id.to_string()), + ComponentV0::QemuPvpanic(QemuPvpanic { enable_isa: true }), + ); + + let ser = serde_json::to_string(&map).unwrap(); + let unser: TestMap = serde_json::from_str(&ser).unwrap(); + let key = unser.keys().next().expect("one key in the map"); + let SpecKey::Uuid(got_id) = key else { + panic!("expected SpecKey::Uuid, got {}", key); + }; + + assert_eq!(*got_id, id); + } +}