use super::{ChatRoomParametersV1, ChatRoomStateV1};
use freenet_scaffold::ComposableState;
use serde::{Deserialize, Serialize};
pub const CURRENT_STATE_VERSION: u32 = 1;
#[derive(Serialize, Deserialize, Clone, Default, PartialEq, Debug)]
pub struct StateVersion(pub u32);
impl ComposableState for StateVersion {
type ParentState = ChatRoomStateV1;
type Summary = u32;
type Delta = ();
type Parameters = ChatRoomParametersV1;
fn verify(
&self,
_parent_state: &Self::ParentState,
_parameters: &Self::Parameters,
) -> Result<(), String> {
if self.0 > CURRENT_STATE_VERSION {
return Err(format!(
"Unknown state version {}. This contract supports versions 0-{}. \
Please upgrade your client.",
self.0, CURRENT_STATE_VERSION
));
}
Ok(())
}
fn summarize(
&self,
_parent_state: &Self::ParentState,
_parameters: &Self::Parameters,
) -> Self::Summary {
self.0
}
fn delta(
&self,
_parent_state: &Self::ParentState,
_parameters: &Self::Parameters,
_old_state_summary: &Self::Summary,
) -> Option<Self::Delta> {
None
}
fn apply_delta(
&mut self,
_parent_state: &Self::ParentState,
_parameters: &Self::Parameters,
_delta: &Option<Self::Delta>,
) -> Result<(), String> {
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
use freenet_stdlib::prelude::serde_json;
#[test]
fn test_version_default_is_zero() {
let v = StateVersion::default();
assert_eq!(v.0, 0);
}
#[test]
fn test_version_verify_accepts_current() {
let v = StateVersion(CURRENT_STATE_VERSION);
let parent = ChatRoomStateV1::default();
let params = ChatRoomParametersV1 {
owner: ed25519_dalek::SigningKey::generate(&mut rand::rngs::OsRng).verifying_key(),
};
assert!(v.verify(&parent, ¶ms).is_ok());
}
#[test]
fn test_version_verify_accepts_legacy() {
let v = StateVersion(0);
let parent = ChatRoomStateV1::default();
let params = ChatRoomParametersV1 {
owner: ed25519_dalek::SigningKey::generate(&mut rand::rngs::OsRng).verifying_key(),
};
assert!(v.verify(&parent, ¶ms).is_ok());
}
#[test]
fn test_version_verify_rejects_future() {
let v = StateVersion(CURRENT_STATE_VERSION + 1);
let parent = ChatRoomStateV1::default();
let params = ChatRoomParametersV1 {
owner: ed25519_dalek::SigningKey::generate(&mut rand::rngs::OsRng).verifying_key(),
};
assert!(v.verify(&parent, ¶ms).is_err());
}
#[test]
fn test_version_serialization_roundtrip() {
let v = StateVersion(CURRENT_STATE_VERSION);
let serialized = serde_json::to_string(&v).unwrap();
let deserialized: StateVersion = serde_json::from_str(&serialized).unwrap();
assert_eq!(v, deserialized);
}
#[test]
fn test_state_without_version_field_deserializes_with_default() {
use crate::room_state::configuration::{AuthorizedConfigurationV1, Configuration};
let owner_sk = ed25519_dalek::SigningKey::generate(&mut rand::rngs::OsRng);
let owner_vk = owner_sk.verifying_key();
let mut state = ChatRoomStateV1::default();
let config = Configuration {
owner_member_id: owner_vk.into(),
..Default::default()
};
state.configuration = AuthorizedConfigurationV1::new(config, &owner_sk);
let mut json_value: serde_json::Value = serde_json::to_value(&state).unwrap();
if let serde_json::Value::Object(ref mut map) = json_value {
map.remove("version");
}
let legacy_json = serde_json::to_string(&json_value).unwrap();
let deserialized: ChatRoomStateV1 = serde_json::from_str(&legacy_json).unwrap();
assert_eq!(
deserialized.version.0, 0,
"Legacy state without version field should deserialize with version=0"
);
}
#[test]
fn test_state_with_version_field_roundtrips() {
use crate::room_state::configuration::{AuthorizedConfigurationV1, Configuration};
let owner_sk = ed25519_dalek::SigningKey::generate(&mut rand::rngs::OsRng);
let owner_vk = owner_sk.verifying_key();
let mut state = ChatRoomStateV1::default();
let config = Configuration {
owner_member_id: owner_vk.into(),
..Default::default()
};
state.configuration = AuthorizedConfigurationV1::new(config, &owner_sk);
state.version = StateVersion(CURRENT_STATE_VERSION);
let json = serde_json::to_string(&state).unwrap();
let deserialized: ChatRoomStateV1 = serde_json::from_str(&json).unwrap();
assert_eq!(deserialized.version.0, CURRENT_STATE_VERSION);
}
}