I've been saying "PDSes seem easy enough, they're what, some CRUD to a db? I can do that in my sleep". well i'm sleeping rn so let's go
at main 12 kB view raw
1use tranquil_pds::api::validation::{ 2 HandleValidationError, MAX_DOMAIN_LABEL_LENGTH, MAX_EMAIL_LENGTH, MAX_LOCAL_PART_LENGTH, 3 MAX_SERVICE_HANDLE_LOCAL_PART, is_valid_email, validate_short_handle, 4}; 5use tranquil_pds::validation::{ 6 is_valid_did, validate_collection_nsid, validate_password, validate_record_key, 7}; 8 9#[test] 10fn test_record_key_boundary_min() { 11 assert!(validate_record_key("a").is_ok()); 12 assert!(validate_record_key("1").is_ok()); 13 assert!(validate_record_key("-").is_ok()); 14 assert!(validate_record_key("_").is_ok()); 15 assert!(validate_record_key("~").is_ok()); 16} 17 18#[test] 19fn test_record_key_boundary_max() { 20 assert!(validate_record_key(&"a".repeat(512)).is_ok()); 21 assert!(validate_record_key(&"a".repeat(513)).is_err()); 22 assert!(validate_record_key(&"a".repeat(1000)).is_err()); 23} 24 25#[test] 26fn test_record_key_special_dot_cases() { 27 assert!(validate_record_key(".").is_err()); 28 assert!(validate_record_key("..").is_err()); 29 assert!(validate_record_key("...").is_ok()); 30 assert!(validate_record_key("a.b").is_ok()); 31 assert!(validate_record_key(".a").is_ok()); 32 assert!(validate_record_key("a.").is_ok()); 33 assert!(validate_record_key("a..b").is_ok()); 34} 35 36#[test] 37fn test_record_key_all_valid_chars() { 38 assert!(validate_record_key("abc").is_ok()); 39 assert!(validate_record_key("ABC").is_ok()); 40 assert!(validate_record_key("123").is_ok()); 41 assert!(validate_record_key("a-b").is_ok()); 42 assert!(validate_record_key("a_b").is_ok()); 43 assert!(validate_record_key("a~b").is_ok()); 44 assert!(validate_record_key("a.b").is_ok()); 45 assert!(validate_record_key("aA1-_.~").is_ok()); 46} 47 48#[test] 49fn test_record_key_invalid_chars() { 50 assert!(validate_record_key("a/b").is_err()); 51 assert!(validate_record_key("a\\b").is_err()); 52 assert!(validate_record_key("a b").is_err()); 53 assert!(validate_record_key("a@b").is_err()); 54 assert!(validate_record_key("a#b").is_err()); 55 assert!(validate_record_key("a$b").is_err()); 56 assert!(validate_record_key("a%b").is_err()); 57 assert!(validate_record_key("a&b").is_err()); 58 assert!(validate_record_key("a*b").is_err()); 59 assert!(validate_record_key("a+b").is_err()); 60 assert!(validate_record_key("a=b").is_err()); 61 assert!(validate_record_key("a?b").is_err()); 62 assert!(validate_record_key("a:b").is_err()); 63 assert!(validate_record_key("a;b").is_err()); 64 assert!(validate_record_key("a<b").is_err()); 65 assert!(validate_record_key("a>b").is_err()); 66 assert!(validate_record_key("a[b").is_err()); 67 assert!(validate_record_key("a]b").is_err()); 68 assert!(validate_record_key("a{b").is_err()); 69 assert!(validate_record_key("a}b").is_err()); 70 assert!(validate_record_key("a|b").is_err()); 71 assert!(validate_record_key("a`b").is_err()); 72 assert!(validate_record_key("a'b").is_err()); 73 assert!(validate_record_key("a\"b").is_err()); 74 assert!(validate_record_key("a\nb").is_err()); 75 assert!(validate_record_key("a\tb").is_err()); 76 assert!(validate_record_key("a\rb").is_err()); 77 assert!(validate_record_key("a\0b").is_err()); 78} 79 80#[test] 81fn test_record_key_unicode() { 82 assert!(validate_record_key("café").is_err()); 83 assert!(validate_record_key("日本語").is_err()); 84 assert!(validate_record_key("emoji😀").is_err()); 85} 86 87#[test] 88fn test_password_length_boundaries() { 89 let base_valid = "Aa1"; 90 91 let pass_7 = format!("{}{}", base_valid, "x".repeat(4)); 92 assert!(validate_password(&pass_7).is_err()); 93 94 let pass_8 = format!("{}{}", base_valid, "x".repeat(5)); 95 assert!(validate_password(&pass_8).is_ok()); 96 97 let pass_256 = format!("{}{}", base_valid, "x".repeat(253)); 98 assert!(validate_password(&pass_256).is_ok()); 99 100 let pass_257 = format!("{}{}", base_valid, "x".repeat(254)); 101 assert!(validate_password(&pass_257).is_err()); 102} 103 104#[test] 105fn test_password_missing_requirements() { 106 assert!(validate_password("abcdefgh").is_err()); 107 assert!(validate_password("ABCDEFGH").is_err()); 108 assert!(validate_password("12345678").is_err()); 109 110 assert!(validate_password("abcd1234").is_err()); 111 assert!(validate_password("ABCD1234").is_err()); 112 assert!(validate_password("abcdABCD").is_err()); 113 114 assert!(validate_password("aB1xxxxx").is_ok()); 115} 116 117#[test] 118fn test_password_common_passwords() { 119 assert!(validate_password("Password1").is_err()); 120 assert!(validate_password("PASSWORD1").is_err()); 121 assert!(validate_password("password1").is_err()); 122 assert!(validate_password("Qwerty123").is_err()); 123 assert!(validate_password("Bluesky123").is_err()); 124} 125 126#[test] 127fn test_password_special_chars_allowed() { 128 assert!(validate_password("Aa1!@#$%").is_ok()); 129 assert!(validate_password("Aa1^&*()").is_ok()); 130 assert!(validate_password("Aa1 space").is_ok()); 131} 132 133#[test] 134fn test_did_validation_basic() { 135 assert!(is_valid_did("did:plc:abc123")); 136 assert!(is_valid_did("did:web:example.com")); 137 assert!(is_valid_did( 138 "did:key:z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK" 139 )); 140} 141 142#[test] 143fn test_did_validation_invalid() { 144 assert!(!is_valid_did("")); 145 assert!(!is_valid_did("did")); 146 assert!(!is_valid_did("did:")); 147 assert!(!is_valid_did("did::")); 148 assert!(!is_valid_did("did:plc")); 149 assert!(!is_valid_did("did:plc:")); 150 assert!(!is_valid_did(":plc:abc")); 151 assert!(!is_valid_did("plc:abc")); 152} 153 154#[test] 155fn test_did_validation_method_case() { 156 assert!(!is_valid_did("did:PLC:abc123")); 157 assert!(!is_valid_did("did:Plc:abc123")); 158 assert!(!is_valid_did("DID:plc:abc123")); 159} 160 161#[test] 162fn test_did_validation_method_chars() { 163 assert!(!is_valid_did("did:plc1:abc")); 164 assert!(!is_valid_did("did:plc-x:abc")); 165 assert!(!is_valid_did("did:plc_x:abc")); 166} 167 168#[test] 169fn test_collection_nsid_minimum_segments() { 170 assert!(validate_collection_nsid("a.b.c").is_ok()); 171 assert!(validate_collection_nsid("a.b").is_err()); 172 assert!(validate_collection_nsid("a").is_err()); 173 assert!(validate_collection_nsid("").is_err()); 174} 175 176#[test] 177fn test_collection_nsid_many_segments() { 178 assert!(validate_collection_nsid("a.b.c.d.e.f.g.h.i.j").is_ok()); 179} 180 181#[test] 182fn test_collection_nsid_empty_segments() { 183 assert!(validate_collection_nsid("a..b.c").is_err()); 184 assert!(validate_collection_nsid(".a.b.c").is_err()); 185 assert!(validate_collection_nsid("a.b.c.").is_err()); 186 assert!(validate_collection_nsid("a.b..c").is_err()); 187} 188 189#[test] 190fn test_collection_nsid_valid_chars() { 191 assert!(validate_collection_nsid("app.bsky.feed.post").is_ok()); 192 assert!(validate_collection_nsid("com.example.my-record").is_ok()); 193 assert!(validate_collection_nsid("app.example.record123").is_ok()); 194 assert!(validate_collection_nsid("APP.BSKY.FEED.POST").is_ok()); 195} 196 197#[test] 198fn test_collection_nsid_invalid_chars() { 199 assert!(validate_collection_nsid("app.bsky.feed_post").is_err()); 200 assert!(validate_collection_nsid("app.bsky.feed/post").is_err()); 201 assert!(validate_collection_nsid("app.bsky.feed:post").is_err()); 202 assert!(validate_collection_nsid("app.bsky.feed@post").is_err()); 203} 204 205#[test] 206fn test_handle_boundary_lengths() { 207 let min_handle = "abc"; 208 assert!(validate_short_handle(min_handle).is_ok()); 209 210 let under_min = "ab"; 211 assert!(matches!( 212 validate_short_handle(under_min), 213 Err(HandleValidationError::TooShort) 214 )); 215 216 let at_max = "a".repeat(MAX_SERVICE_HANDLE_LOCAL_PART); 217 assert!(validate_short_handle(&at_max).is_ok()); 218 219 let over_max = "a".repeat(MAX_SERVICE_HANDLE_LOCAL_PART + 1); 220 assert!(matches!( 221 validate_short_handle(&over_max), 222 Err(HandleValidationError::TooLong) 223 )); 224} 225 226#[test] 227fn test_handle_hyphen_positions() { 228 assert!(validate_short_handle("a-b-c").is_ok()); 229 assert!(validate_short_handle("a--b").is_ok()); 230 assert!(validate_short_handle("---").is_err()); 231 assert!(matches!( 232 validate_short_handle("-abc"), 233 Err(HandleValidationError::StartsWithInvalidChar) 234 )); 235 assert!(matches!( 236 validate_short_handle("abc-"), 237 Err(HandleValidationError::EndsWithInvalidChar) 238 )); 239} 240 241#[test] 242fn test_handle_case_normalization() { 243 assert_eq!(validate_short_handle("ABC").unwrap(), "abc"); 244 assert_eq!(validate_short_handle("AbC123").unwrap(), "abc123"); 245 assert_eq!(validate_short_handle("MixedCase").unwrap(), "mixedcase"); 246} 247 248#[test] 249fn test_handle_whitespace_handling() { 250 assert_eq!(validate_short_handle(" abc ").unwrap(), "abc"); 251 assert!(matches!( 252 validate_short_handle("a b c"), 253 Err(HandleValidationError::ContainsSpaces) 254 )); 255 assert!(matches!( 256 validate_short_handle("a\tb"), 257 Err(HandleValidationError::ContainsSpaces) 258 )); 259 assert!(matches!( 260 validate_short_handle("a\nb"), 261 Err(HandleValidationError::ContainsSpaces) 262 )); 263} 264 265#[test] 266fn test_email_length_boundaries() { 267 let long_local = format!("{}@example.com", "a".repeat(MAX_LOCAL_PART_LENGTH)); 268 assert!(is_valid_email(&long_local)); 269 270 let too_long_local = format!("{}@example.com", "a".repeat(MAX_LOCAL_PART_LENGTH + 1)); 271 assert!(!is_valid_email(&too_long_local)); 272 273 let very_long_email = format!("a@{}.com", "a".repeat(240)); 274 if very_long_email.len() <= MAX_EMAIL_LENGTH { 275 assert!(is_valid_email(&very_long_email) || !is_valid_email(&very_long_email)); 276 } 277} 278 279#[test] 280fn test_email_local_part_special_chars() { 281 assert!(is_valid_email("user.name@example.com")); 282 assert!(is_valid_email("user+tag@example.com")); 283 assert!(is_valid_email("user!def@example.com")); 284 assert!(is_valid_email("user#abc@example.com")); 285 assert!(is_valid_email("user$def@example.com")); 286 assert!(is_valid_email("user%abc@example.com")); 287 assert!(is_valid_email("user&def@example.com")); 288 assert!(is_valid_email("user'abc@example.com")); 289 assert!(is_valid_email("user*def@example.com")); 290 assert!(is_valid_email("user=abc@example.com")); 291 assert!(is_valid_email("user?def@example.com")); 292 assert!(is_valid_email("user^abc@example.com")); 293 assert!(is_valid_email("user_def@example.com")); 294 assert!(is_valid_email("user`abc@example.com")); 295 assert!(is_valid_email("user{def@example.com")); 296 assert!(is_valid_email("user|abc@example.com")); 297 assert!(is_valid_email("user}def@example.com")); 298 assert!(is_valid_email("user~abc@example.com")); 299 assert!(is_valid_email("user-def@example.com")); 300} 301 302#[test] 303fn test_email_local_part_dots() { 304 assert!(!is_valid_email(".user@example.com")); 305 assert!(!is_valid_email("user.@example.com")); 306 assert!(!is_valid_email("user..name@example.com")); 307 assert!(is_valid_email("user.name@example.com")); 308 assert!(is_valid_email("u.s.e.r@example.com")); 309} 310 311#[test] 312fn test_email_domain_labels() { 313 let long_label = "a".repeat(MAX_DOMAIN_LABEL_LENGTH); 314 let valid_domain = format!("user@{}.com", long_label); 315 assert!(is_valid_email(&valid_domain)); 316 317 let too_long_label = "a".repeat(MAX_DOMAIN_LABEL_LENGTH + 1); 318 let invalid_domain = format!("user@{}.com", too_long_label); 319 assert!(!is_valid_email(&invalid_domain)); 320} 321 322#[test] 323fn test_email_domain_hyphens() { 324 assert!(!is_valid_email("user@-example.com")); 325 assert!(!is_valid_email("user@example-.com")); 326 assert!(is_valid_email("user@ex-ample.com")); 327 assert!(is_valid_email("user@ex--ample.com")); 328} 329 330#[test] 331fn test_email_domain_must_have_dot() { 332 assert!(!is_valid_email("user@localhost")); 333 assert!(!is_valid_email("user@example")); 334 assert!(is_valid_email("user@a.b")); 335} 336 337#[test] 338fn test_email_invalid_chars() { 339 assert!(!is_valid_email("user name@example.com")); 340 assert!(!is_valid_email("user\t@example.com")); 341 assert!(!is_valid_email("user\n@example.com")); 342 assert!(!is_valid_email("user@exam ple.com")); 343}