use bzod::config::Config; use bzod::db::Db; use bzod::utils::validation::{validate_custom_slug, validate_page_code, validate_redirect_code}; use std::fs; use std::path::PathBuf; #[test] fn test_custom_slug_validation() { // Valid slugs assert!(validate_custom_slug("!a")); assert!(validate_custom_slug("!home")); assert!(validate_custom_slug("!office")); assert!(validate_custom_slug("!project-ae06")); assert!(validate_custom_slug("!customer_01")); // Invalid slugs assert!(!validate_custom_slug("!")); assert!(!validate_custom_slug("!home page")); assert!(!validate_custom_slug("!home/page")); assert!(!validate_custom_slug("!home?")); assert!(!validate_custom_slug("!home&")); assert!(!validate_custom_slug("!!")); assert!(!validate_custom_slug( "!this-is-a-very-long-slug-which-exceeds-the-maximum-allowed-length-limit" )); // Redirect & page validation assert!(validate_redirect_code("abcdef")); // 6-hex assert!(validate_redirect_code("!home")); // custom slug assert!(!validate_redirect_code("abcde")); // invalid redirect code assert!(validate_page_code("abcd")); // 4-hex assert!(validate_page_code("!home")); // custom slug assert!(!validate_page_code("abc")); // invalid page code } fn create_temp_config(temp_dir: PathBuf) -> Config { let mut config = Config::load_with_cli(Some(&temp_dir)).unwrap(); config.backup_dir = temp_dir.clone(); config.base_url = Some("http://bzo.in".to_string()); config } #[tokio::test] async fn test_cli_shorten_and_expand() { let temp_dir = std::env::temp_dir().join(format!("bzod_test_cli_{}", uuid::Uuid::new_v4())); fs::create_dir_all(&temp_dir).unwrap(); let config = create_temp_config(temp_dir.clone()); // 0. Create a standard user let _ = bzod::cli::create_user::run( Some("cliuser".to_string()), Some("password123".to_string()), None, config.clone(), ) .await .unwrap(); // 1. Shorten with generated code let res = bzod::cli::shorten::run( "https://example.com/one".to_string(), None, None, config.clone(), ) .await; assert!(res.is_ok()); // 2. Shorten with custom slug let res = bzod::cli::shorten::run( "https://example.com/two".to_string(), Some("!office".to_string()), None, config.clone(), ) .await; assert!(res.is_ok()); // 3. Shorten duplicate slug (should fail) let res_dup = bzod::cli::shorten::run( "https://example.com/three".to_string(), Some("!OFFICE".to_string()), // case-insensitive None, config.clone(), ) .await; assert!(res_dup.is_err()); assert!(res_dup.unwrap_err().to_string().contains("already exists")); // 4. CLI expand round-trip validation let expand_res = bzod::cli::expand::run("!office".to_string(), None, config.clone()).await; assert!(expand_res.is_ok()); // 5. Case-insensitive CLI expand validation let expand_res_upper = bzod::cli::expand::run("!OFFICE".to_string(), None, config.clone()).await; assert!(expand_res_upper.is_ok()); let _ = fs::remove_dir_all(&temp_dir); } #[tokio::test] async fn test_perform_restore_and_validation() { let temp_dir = std::env::temp_dir().join(format!("bzod_test_restore_{}", uuid::Uuid::new_v4())); let restore_dir = std::env::temp_dir().join(format!("bzod_test_restore_dest_{}", uuid::Uuid::new_v4())); fs::create_dir_all(&temp_dir).unwrap(); fs::create_dir_all(&restore_dir).unwrap(); let config = create_temp_config(temp_dir.clone()); // Create user and url let _ = bzod::cli::create_user::run( Some("restoreuser".to_string()), Some("password123".to_string()), None, config.clone(), ) .await .unwrap(); let db = Db::init(&config).unwrap(); let (uid, tid) = { let conn = db.users.lock().unwrap(); let u = bzod::db::users::get_user_by_username(&conn, "restoreuser") .unwrap() .unwrap(); (u.id, u.tenant_id.unwrap()) }; // 1. Create a mock database record { let conn = bzod::jobs::open_user_content_conn(&db, uid).unwrap(); let url = bzod::db::content::create_url_extended( &conn, "!home", "https://my-home.com", None, None, &[], None, None, None, ) .unwrap(); let urls_conn = db.global_urls.lock().unwrap(); bzod::db::slugs::register_url_slug(&urls_conn, "!home", &tid, &url.id, "active").unwrap(); } // 2. Perform a backup let backup_path = bzod::jobs::backup::perform_backup(&db, &config) .await .unwrap(); assert!(PathBuf::from(&backup_path).exists()); // 3. Validate backup archive structure let validation_res = bzod::cli::restore::perform_restore(&PathBuf::from(&backup_path), &restore_dir); assert!(validation_res.is_ok()); // Verify database files were extracted assert!(restore_dir.join("admin/admin.db").exists()); assert!(restore_dir.join("admin/users.db").exists()); assert!(restore_dir.join("admin/system.db").exists()); assert!(restore_dir.join("slugs/global_urls.db").exists()); assert!(restore_dir .join(format!("users/{}/content.db", tid.as_str())) .exists()); // Verify custom slug was preserved in the restored DB let restore_config = create_temp_config(restore_dir.clone()); let expand_res = bzod::cli::expand::run("!home".to_string(), None, restore_config).await; assert!(expand_res.is_ok()); let _ = fs::remove_dir_all(&temp_dir); let _ = fs::remove_dir_all(&restore_dir); }