diff --git a/dongle-smartcontract/src/errors.rs b/dongle-smartcontract/src/errors.rs index e69de29..06c01c4 100644 --- a/dongle-smartcontract/src/errors.rs +++ b/dongle-smartcontract/src/errors.rs @@ -0,0 +1,6 @@ +#[derive(Debug, PartialEq, Eq)] +pub enum Error { + InvalidProjectName, + InvalidProjectNameFormat, + ProjectNameTooLong, +} diff --git a/dongle-smartcontract/src/project_registry.rs b/dongle-smartcontract/src/project_registry.rs index 4e855fc..b868efa 100644 --- a/dongle-smartcontract/src/project_registry.rs +++ b/dongle-smartcontract/src/project_registry.rs @@ -1,17 +1,50 @@ +extern crate alloc; +use alloc::string::ToString; use crate::types::Project; -use soroban_sdk::{Env, Address, Map}; +use soroban_sdk::{Env, Address, Map, String}; +use crate::errors::Error; pub struct ProjectRegistry; +pub const MAX_NAME_LEN: usize = 50; + impl ProjectRegistry { - pub fn register_project(env: &Env, owner: Address, name: String, description: String, category: String, website: Option, logo_cid: Option, metadata_cid: Option) -> u64 { + pub fn register_project( + env: &Env, + owner: Address, + name: String, + description: String, + category: String, + website: Option, + logo_cid: Option, + metadata_cid: Option + ) -> Result { + let name_str = name.to_string(); + + // 1. Validate Non-empty and not only whitespace + if name_str.trim().is_empty() { + return Err(Error::InvalidProjectName); + } + + // 2. Validate max length + if name_str.len() > MAX_NAME_LEN { + return Err(Error::ProjectNameTooLong); + } + + // 3. Validate alphanumeric, underscore, hyphen + for c in name_str.chars() { + if !c.is_ascii_alphanumeric() && c != '_' && c != '-' { + return Err(Error::InvalidProjectNameFormat); + } + } + // Generate unique project ID // Save project in Map // Emit ProjectRegistered event - 0 + Ok(0) } - pub fn update_project(env: &Env, project_id: u64, caller: Address, ...) { + pub fn update_project(env: &Env, project_id: u64, caller: Address) { // Validate ownership // Update project metadata } @@ -20,3 +53,67 @@ impl ProjectRegistry { None } } + +// Unit tests covering valid and invalid cases +#[cfg(test)] +mod tests { + use super::*; + use soroban_sdk::Env; + + #[test] + fn test_valid_project_name() { + let env = Env::default(); + let owner = Address::generate(&env); + let name = String::from_str(&env, "Valid-Project_Name123"); + + let result = ProjectRegistry::register_project( + &env, owner, name, + String::from_str(&env, "Desc"), String::from_str(&env, "Cat"), + None, None, None + ); + assert!(result.is_ok()); + } + + #[test] + fn test_empty_or_whitespace_name() { + let env = Env::default(); + let owner = Address::generate(&env); + let name = String::from_str(&env, " "); + + let result = ProjectRegistry::register_project( + &env, owner, name, + String::from_str(&env, "Desc"), String::from_str(&env, "Cat"), + None, None, None + ); + assert_eq!(result, Err(Error::InvalidProjectName)); + } + + #[test] + fn test_invalid_characters_in_name() { + let env = Env::default(); + let owner = Address::generate(&env); + let name = String::from_str(&env, "My Project *"); + + let result = ProjectRegistry::register_project( + &env, owner, name, + String::from_str(&env, "Desc"), String::from_str(&env, "Cat"), + None, None, None + ); + assert_eq!(result, Err(Error::InvalidProjectNameFormat)); + } + + #[test] + fn test_name_too_long() { + let env = Env::default(); + let owner = Address::generate(&env); + // 51 characters + let name = String::from_str(&env, "ThisProjectNameIsWayTooLongAndExceedsTheFiftyCharL1"); + + let result = ProjectRegistry::register_project( + &env, owner, name, + String::from_str(&env, "Desc"), String::from_str(&env, "Cat"), + None, None, None + ); + assert_eq!(result, Err(Error::ProjectNameTooLong)); + } +} diff --git a/dongle-smartcontract/src/types.rs b/dongle-smartcontract/src/types.rs index e69de29..5707ae5 100644 --- a/dongle-smartcontract/src/types.rs +++ b/dongle-smartcontract/src/types.rs @@ -0,0 +1,3 @@ +pub struct Project { + pub id: u64, +}