From 5444ab46ca3e60756081f2e75789f944b702aa25 Mon Sep 17 00:00:00 2001 From: h7x4 Date: Tue, 28 Apr 2026 07:27:55 +0900 Subject: [PATCH] core/protocol: test de/serialization of all protocol messages --- src/core/database_privileges/base.rs | 2 +- .../protocol/commands/check_authorization.rs | 40 +++++++++++++++++ .../protocol/commands/create_databases.rs | 38 ++++++++++++++++ src/core/protocol/commands/create_users.rs | 34 ++++++++++++++ src/core/protocol/commands/drop_databases.rs | 34 ++++++++++++++ src/core/protocol/commands/drop_users.rs | 34 ++++++++++++++ .../protocol/commands/list_all_databases.rs | 33 ++++++++++++++ .../protocol/commands/list_all_privileges.rs | 31 +++++++++++++ src/core/protocol/commands/list_all_users.rs | 34 ++++++++++++++ src/core/protocol/commands/list_databases.rs | 41 +++++++++++++++++ src/core/protocol/commands/list_privileges.rs | 41 +++++++++++++++++ src/core/protocol/commands/list_users.rs | 45 +++++++++++++++++++ src/core/protocol/commands/lock_users.rs | 30 +++++++++++++ .../protocol/commands/modify_privileges.rs | 40 +++++++++++++++++ src/core/protocol/commands/passwd_user.rs | 32 +++++++++++++ src/core/protocol/commands/unlock_users.rs | 30 +++++++++++++ 16 files changed, 538 insertions(+), 1 deletion(-) diff --git a/src/core/database_privileges/base.rs b/src/core/database_privileges/base.rs index 65f0f4c..63351fb 100644 --- a/src/core/database_privileges/base.rs +++ b/src/core/database_privileges/base.rs @@ -29,7 +29,7 @@ pub const DATABASE_PRIVILEGE_FIELDS: [&str; 13] = [ // doesn't have any natural implementation semantics. /// Representation of the set of privileges for a single user on a single database. -#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, PartialOrd, Ord)] +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, PartialOrd, Ord, Default)] pub struct DatabasePrivilegeRow { // TODO: don't store the db and user here, let the type be stored in a mapping pub db: MySQLDatabase, diff --git a/src/core/protocol/commands/check_authorization.rs b/src/core/protocol/commands/check_authorization.rs index 368a811..8f687ab 100644 --- a/src/core/protocol/commands/check_authorization.rs +++ b/src/core/protocol/commands/check_authorization.rs @@ -67,3 +67,43 @@ impl CheckAuthorizationError { self.0.error_type() } } + +#[cfg(test)] +mod tests { + use crate::core::protocol::request_validation::NameValidationError; + + use super::*; + + #[test] + fn test_serialize_deserialize_request() { + let request: CheckAuthorizationRequest = vec![ + DbOrUser::Database("test_db".into()), + DbOrUser::User("test_user".into()), + ]; + + let json = serde_json::to_string_pretty(&request).unwrap(); + println!("Serialized request:\n{}", json); + + let deserialized: CheckAuthorizationRequest = serde_json::from_str(&json).unwrap(); + assert_eq!(request, deserialized); + } + + #[test] + fn test_serialize_deserialize_response() { + let response: CheckAuthorizationResponse = BTreeMap::from([ + (DbOrUser::Database("test_db".into()), Ok(())), + ( + DbOrUser::User("test_user".into()), + Err(CheckAuthorizationError( + ValidationError::NameValidationError(NameValidationError::TooLong), + )), + ), + ]); + + let json = serde_json::to_string_pretty(&response).unwrap(); + println!("Serialized response:\n{}", json); + + let deserialized: CheckAuthorizationResponse = serde_json::from_str(&json).unwrap(); + assert_eq!(response, deserialized); + } +} diff --git a/src/core/protocol/commands/create_databases.rs b/src/core/protocol/commands/create_databases.rs index eb63205..fe2de3d 100644 --- a/src/core/protocol/commands/create_databases.rs +++ b/src/core/protocol/commands/create_databases.rs @@ -87,3 +87,41 @@ impl CreateDatabaseError { } } } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_serialize_deserialize_request() { + let request: CreateDatabasesRequest = + vec!["test_db1".into(), "test_db2".into(), "test_db3".into()]; + + let json = serde_json::to_string_pretty(&request).unwrap(); + println!("Serialized request:\n{}", json); + + let deserialized: CreateDatabasesRequest = serde_json::from_str(&json).unwrap(); + assert_eq!(request, deserialized); + } + + #[test] + fn test_serialize_deserialize_response() { + let response: CreateDatabasesResponse = BTreeMap::from([ + ("test_db1".into(), Ok(())), + ( + "test_db2".into(), + Err(CreateDatabaseError::DatabaseAlreadyExists), + ), + ( + "test_db3".into(), + Err(CreateDatabaseError::MySqlError("Some MySQL error".into())), + ), + ]); + + let json = serde_json::to_string_pretty(&response).unwrap(); + println!("Serialized response:\n{}", json); + + let deserialized: CreateDatabasesResponse = serde_json::from_str(&json).unwrap(); + assert_eq!(response, deserialized); + } +} diff --git a/src/core/protocol/commands/create_users.rs b/src/core/protocol/commands/create_users.rs index 0ee9f58..72c4019 100644 --- a/src/core/protocol/commands/create_users.rs +++ b/src/core/protocol/commands/create_users.rs @@ -87,3 +87,37 @@ impl CreateUserError { } } } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_serialize_deserialize_request() { + let request: CreateUsersRequest = vec!["alice".into(), "bob".into(), "charlie".into()]; + + let json = serde_json::to_string_pretty(&request).unwrap(); + println!("Serialized request:\n{}", json); + + let deserialized: CreateUsersRequest = serde_json::from_str(&json).unwrap(); + assert_eq!(request, deserialized); + } + + #[test] + fn test_serialize_deserialize_response() { + let response: CreateUsersResponse = BTreeMap::from([ + ("alice".into(), Ok(())), + ("bob".into(), Err(CreateUserError::UserAlreadyExists)), + ( + "charlie".into(), + Err(CreateUserError::MySqlError("Some MySQL error".into())), + ), + ]); + + let json = serde_json::to_string_pretty(&response).unwrap(); + println!("Serialized response:\n{}", json); + + let deserialized: CreateUsersResponse = serde_json::from_str(&json).unwrap(); + assert_eq!(response, deserialized); + } +} diff --git a/src/core/protocol/commands/drop_databases.rs b/src/core/protocol/commands/drop_databases.rs index 174fe96..274f033 100644 --- a/src/core/protocol/commands/drop_databases.rs +++ b/src/core/protocol/commands/drop_databases.rs @@ -90,3 +90,37 @@ impl DropDatabaseError { } } } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_serialize_deserialize_request() { + let request: DropDatabasesRequest = vec!["db1".into(), "db2".into(), "db3".into()]; + + let json = serde_json::to_string_pretty(&request).unwrap(); + println!("Serialized request:\n{}", json); + + let deserialized: DropDatabasesRequest = serde_json::from_str(&json).unwrap(); + assert_eq!(request, deserialized); + } + + #[test] + fn test_serialize_deserialize_response() { + let response: DropDatabasesResponse = BTreeMap::from([ + ("db1".into(), Ok(())), + ("db2".into(), Err(DropDatabaseError::DatabaseDoesNotExist)), + ( + "db3".into(), + Err(DropDatabaseError::MySqlError("Some MySQL error".into())), + ), + ]); + + let json = serde_json::to_string_pretty(&response).unwrap(); + println!("Serialized response:\n{}", json); + + let deserialized: DropDatabasesResponse = serde_json::from_str(&json).unwrap(); + assert_eq!(response, deserialized); + } +} diff --git a/src/core/protocol/commands/drop_users.rs b/src/core/protocol/commands/drop_users.rs index f767324..a9b652d 100644 --- a/src/core/protocol/commands/drop_users.rs +++ b/src/core/protocol/commands/drop_users.rs @@ -87,3 +87,37 @@ impl DropUserError { } } } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_serialize_deserialize_request() { + let request: DropUsersRequest = vec!["alice".into(), "bob".into(), "charlie".into()]; + + let json = serde_json::to_string_pretty(&request).unwrap(); + println!("Serialized request:\n{}", json); + + let deserialized: DropUsersRequest = serde_json::from_str(&json).unwrap(); + assert_eq!(request, deserialized); + } + + #[test] + fn test_serialize_deserialize_response() { + let response: DropUsersResponse = BTreeMap::from([ + ("alice".into(), Ok(())), + ("bob".into(), Err(DropUserError::UserDoesNotExist)), + ( + "charlie".into(), + Err(DropUserError::MySqlError("Some MySQL error".into())), + ), + ]); + + let json = serde_json::to_string_pretty(&response).unwrap(); + println!("Serialized response:\n{}", json); + + let deserialized: DropUsersResponse = serde_json::from_str(&json).unwrap(); + assert_eq!(response, deserialized); + } +} diff --git a/src/core/protocol/commands/list_all_databases.rs b/src/core/protocol/commands/list_all_databases.rs index fbd86f7..5334293 100644 --- a/src/core/protocol/commands/list_all_databases.rs +++ b/src/core/protocol/commands/list_all_databases.rs @@ -27,3 +27,36 @@ impl ListAllDatabasesError { } } } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_serialize_deserialize_response() { + let response: ListAllDatabasesResponse = Ok(vec![ + DatabaseRow { + database: "db1".into(), + tables: vec!["table1".into(), "table2".into()], + users: vec!["user1".into(), "user2".into()], + collation: Some("utf8mb4_general_ci".into()), + character_set: Some("utf8mb4".into()), + size_bytes: 1024, + }, + DatabaseRow { + database: "db2".into(), + tables: vec!["table3".into(), "table4".into()], + users: vec!["user3".into(), "user4".into()], + collation: Some("utf8mb4_general_ci".into()), + character_set: Some("utf8mb4".into()), + size_bytes: 2048, + }, + ]); + + let json = serde_json::to_string_pretty(&response).unwrap(); + println!("Serialized response:\n{}", json); + + let deserialized: ListAllDatabasesResponse = serde_json::from_str(&json).unwrap(); + assert_eq!(response, deserialized); + } +} diff --git a/src/core/protocol/commands/list_all_privileges.rs b/src/core/protocol/commands/list_all_privileges.rs index 45812e9..f2cb3df 100644 --- a/src/core/protocol/commands/list_all_privileges.rs +++ b/src/core/protocol/commands/list_all_privileges.rs @@ -27,3 +27,34 @@ impl ListAllPrivilegesError { } } } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_serialize_deserialize_response() { + let response: ListAllPrivilegesResponse = Ok(vec![ + DatabasePrivilegeRow { + user: "user1".into(), + db: "db1".into(), + select_priv: true, + insert_priv: false, + ..Default::default() + }, + DatabasePrivilegeRow { + user: "user2".into(), + db: "db2".into(), + select_priv: false, + insert_priv: true, + ..Default::default() + }, + ]); + + let json = serde_json::to_string_pretty(&response).unwrap(); + println!("Serialized response:\n{}", json); + + let deserialized: ListAllPrivilegesResponse = serde_json::from_str(&json).unwrap(); + assert_eq!(response, deserialized); + } +} diff --git a/src/core/protocol/commands/list_all_users.rs b/src/core/protocol/commands/list_all_users.rs index 21d460f..f307633 100644 --- a/src/core/protocol/commands/list_all_users.rs +++ b/src/core/protocol/commands/list_all_users.rs @@ -27,3 +27,37 @@ impl ListAllUsersError { } } } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_serialize_deserialize_response() { + let response: ListAllUsersResponse = Ok(vec![ + DatabaseUser { + user: "user1".into(), + host: "%".into(), + has_password: true, + is_locked: false, + databases: vec!["db1".into(), "db2".into()], + }, + DatabaseUser { + user: "user2".into(), + host: "%".into(), + has_password: false, + is_locked: true, + databases: vec!["db3".into()], + }, + ]); + + let json = serde_json::to_string_pretty(&response).unwrap(); + println!("Serialized response:\n{}", json); + + let mut deserialized: ListAllUsersResponse = serde_json::from_str(&json).unwrap(); + deserialized.as_mut().unwrap()[0].host = "%".into(); + deserialized.as_mut().unwrap()[1].host = "%".into(); + + assert_eq!(response, deserialized); + } +} diff --git a/src/core/protocol/commands/list_databases.rs b/src/core/protocol/commands/list_databases.rs index 735210d..ddc5235 100644 --- a/src/core/protocol/commands/list_databases.rs +++ b/src/core/protocol/commands/list_databases.rs @@ -137,3 +137,44 @@ impl ListDatabasesError { } } } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_serialize_deserialize_request() { + let request = Some(vec!["db1".into(), "db2".into()]); + let json = serde_json::to_string_pretty(&request).unwrap(); + println!("Serialized request:\n{}", json); + + let deserialized: ListDatabasesRequest = serde_json::from_str(&json).unwrap(); + assert_eq!(request, deserialized); + } + + #[test] + fn test_serialize_deserialize_response() { + let response: ListDatabasesResponse = vec![ + ( + "db1".into(), + Ok(DatabaseRow { + database: "db1".into(), + tables: vec!["table1".to_string(), "table2".to_string()], + users: vec!["user1".into(), "user2".into()], + collation: Some("utf8mb4_general_ci".to_string()), + character_set: Some("utf8mb4".to_string()), + size_bytes: 1024, + }), + ), + ("db2".into(), Err(ListDatabasesError::DatabaseDoesNotExist)), + ] + .into_iter() + .collect(); + + let json = serde_json::to_string_pretty(&response).unwrap(); + println!("Serialized response:\n{}", json); + + let deserialized: ListDatabasesResponse = serde_json::from_str(&json).unwrap(); + assert_eq!(response, deserialized); + } +} diff --git a/src/core/protocol/commands/list_privileges.rs b/src/core/protocol/commands/list_privileges.rs index 65a82de..def8db5 100644 --- a/src/core/protocol/commands/list_privileges.rs +++ b/src/core/protocol/commands/list_privileges.rs @@ -153,3 +153,44 @@ impl ListPrivilegesError { } } } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_serialize_deserialize_request() { + let request: ListPrivilegesRequest = Some(vec!["test_db1".into(), "test_db2".into()]); + let json = serde_json::to_string_pretty(&request).unwrap(); + println!("Serialized request:\n{}", json); + + let deserialized: ListPrivilegesRequest = serde_json::from_str(&json).unwrap(); + assert_eq!(request, deserialized); + } + + #[test] + fn test_serialize_deserialize_response() { + let response: ListPrivilegesResponse = BTreeMap::from([ + ( + "test_db1".into(), + Ok(vec![DatabasePrivilegeRow { + db: "test_db1".into(), + user: "user1".into(), + select_priv: true, + insert_priv: false, + ..Default::default() + }]), + ), + ( + "test_db2".into(), + Err(ListPrivilegesError::DatabaseDoesNotExist), + ), + ]); + + let json = serde_json::to_string_pretty(&response).unwrap(); + println!("Serialized response:\n{}", json); + + let deserialized: ListPrivilegesResponse = serde_json::from_str(&json).unwrap(); + assert_eq!(response, deserialized); + } +} diff --git a/src/core/protocol/commands/list_users.rs b/src/core/protocol/commands/list_users.rs index 86834a3..ef7058a 100644 --- a/src/core/protocol/commands/list_users.rs +++ b/src/core/protocol/commands/list_users.rs @@ -121,3 +121,48 @@ impl ListUsersError { } } } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_serialize_deserialize_request() { + let request: ListUsersRequest = Some(vec!["test_user1".into(), "test_user2".into()]); + + let json = serde_json::to_string_pretty(&request).unwrap(); + println!("Serialized request:\n{}", json); + + let deserialized: ListUsersRequest = serde_json::from_str(&json).unwrap(); + assert_eq!(request, deserialized); + } + + #[test] + fn test_serialize_deserialize_response() { + let response_ok: ListUsersResponse = BTreeMap::from([ + ( + "test_user1".into(), + Ok(DatabaseUser { + user: "test_user1".into(), + host: "%".into(), + has_password: true, + is_locked: false, + databases: vec!["db1".into(), "db2".into()], + }), + ), + ("test_user2".into(), Err(ListUsersError::UserDoesNotExist)), + ]); + + let json = serde_json::to_string_pretty(&response_ok).unwrap(); + println!("Serialized response:\n{}", json); + + let mut deserialized: ListUsersResponse = serde_json::from_str(&json).unwrap(); + deserialized + .get_mut(&"test_user1".into()) + .unwrap() + .as_mut() + .unwrap() + .host = "%".into(); + assert_eq!(response_ok, deserialized); + } +} diff --git a/src/core/protocol/commands/lock_users.rs b/src/core/protocol/commands/lock_users.rs index c44e0b2..dcd5a3c 100644 --- a/src/core/protocol/commands/lock_users.rs +++ b/src/core/protocol/commands/lock_users.rs @@ -94,3 +94,33 @@ impl LockUserError { } } } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_serialize_deserialize_request() { + let request: LockUsersRequest = vec!["test_user1".into(), "test_user2".into()]; + + let json = serde_json::to_string_pretty(&request).unwrap(); + println!("Serialized request:\n{}", json); + + let deserialized: LockUsersRequest = serde_json::from_str(&json).unwrap(); + assert_eq!(request, deserialized); + } + + #[test] + fn test_serialize_deserialize_response() { + let response_ok: LockUsersResponse = BTreeMap::from([ + ("test_user1".into(), Ok(())), + ("test_user2".into(), Err(LockUserError::UserDoesNotExist)), + ]); + + let json = serde_json::to_string_pretty(&response_ok).unwrap(); + println!("Serialized response:\n{}", json); + + let deserialized: LockUsersResponse = serde_json::from_str(&json).unwrap(); + assert_eq!(response_ok, deserialized); + } +} diff --git a/src/core/protocol/commands/modify_privileges.rs b/src/core/protocol/commands/modify_privileges.rs index f701820..35fbd11 100644 --- a/src/core/protocol/commands/modify_privileges.rs +++ b/src/core/protocol/commands/modify_privileges.rs @@ -144,3 +144,43 @@ impl DiffDoesNotApplyError { } } } + +#[cfg(test)] +mod tests { + use super::*; + use crate::core::*; + + #[test] + fn test_serialize_deserialize_request() { + let request = + BTreeSet::from([DatabasePrivilegesDiff::Modified(DatabasePrivilegeRowDiff { + db: "test_db".into(), + user: "test_user".into(), + select_priv: Some(database_privileges::DatabasePrivilegeChange::NoToYes), + ..Default::default() + })]); + + let json = serde_json::to_string_pretty(&request).unwrap(); + println!("Serialized request:\n{}", json); + + let deserialized: ModifyPrivilegesRequest = serde_json::from_str(&json).unwrap(); + assert_eq!(request, deserialized); + } + + #[test] + fn test_serialize_deserialize_response() { + let response: ModifyPrivilegesResponse = BTreeMap::from([ + (("test_db".into(), "test_user".into()), Ok(())), + ( + ("test_db".into(), "invalid_user".into()), + Err(ModifyDatabasePrivilegesError::UserDoesNotExist), + ), + ]); + + let json = serde_json::to_string_pretty(&response).unwrap(); + println!("Serialized response:\n{}", json); + + let deserialized: ModifyPrivilegesResponse = serde_json::from_str(&json).unwrap(); + assert_eq!(response, deserialized); + } +} diff --git a/src/core/protocol/commands/passwd_user.rs b/src/core/protocol/commands/passwd_user.rs index 7922fcd..8e79976 100644 --- a/src/core/protocol/commands/passwd_user.rs +++ b/src/core/protocol/commands/passwd_user.rs @@ -60,3 +60,35 @@ impl SetPasswordError { } } } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_serialize_deserialize_request() { + let request: SetUserPasswordRequest = ("test_user".into(), "new_password".into()); + + let json = serde_json::to_string_pretty(&request).unwrap(); + println!("Serialized request:\n{}", json); + + let deserialized: SetUserPasswordRequest = serde_json::from_str(&json).unwrap(); + assert_eq!(request, deserialized); + } + + #[test] + fn test_serialize_deserialize_response() { + let response_ok: SetUserPasswordResponse = Ok(()); + let response_err: SetUserPasswordResponse = Err(SetPasswordError::UserDoesNotExist); + + let json_ok = serde_json::to_string_pretty(&response_ok).unwrap(); + let json_err = serde_json::to_string_pretty(&response_err).unwrap(); + println!("Serialized OK response:\n{}", json_ok); + println!("Serialized Error response:\n{}", json_err); + + let deserialized_ok: SetUserPasswordResponse = serde_json::from_str(&json_ok).unwrap(); + let deserialized_err: SetUserPasswordResponse = serde_json::from_str(&json_err).unwrap(); + assert_eq!(response_ok, deserialized_ok); + assert_eq!(response_err, deserialized_err); + } +} diff --git a/src/core/protocol/commands/unlock_users.rs b/src/core/protocol/commands/unlock_users.rs index 6de59c8..867fe88 100644 --- a/src/core/protocol/commands/unlock_users.rs +++ b/src/core/protocol/commands/unlock_users.rs @@ -94,3 +94,33 @@ impl UnlockUserError { } } } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_serialize_deserialize_request() { + let request: UnlockUsersRequest = vec!["test_user1".into(), "test_user2".into()]; + + let json = serde_json::to_string_pretty(&request).unwrap(); + println!("Serialized request:\n{}", json); + + let deserialized: UnlockUsersRequest = serde_json::from_str(&json).unwrap(); + assert_eq!(request, deserialized); + } + + #[test] + fn test_serialize_deserialize_response() { + let response_ok: UnlockUsersResponse = BTreeMap::from([ + ("test_user1".into(), Ok(())), + ("test_user2".into(), Err(UnlockUserError::UserDoesNotExist)), + ]); + + let json = serde_json::to_string_pretty(&response_ok).unwrap(); + println!("Serialized response:\n{}", json); + + let deserialized: UnlockUsersResponse = serde_json::from_str(&json).unwrap(); + assert_eq!(response_ok, deserialized); + } +}