Signed by hanna SSH key fingerprint: SHA256:4g9cWhkAAw8gwqhJUcVbSnGEvdGwklW+1Aa/fMUu59k
crates/store/src/tests.rs +57 −0 crates/web/src/tests.rs +61 −0 crates/store/src/tests.rs +57 −0 Expand 86 hidden lines 87 87 } 88 88 89 89 # [ tokio :: test ] 90 + async fn update_profile_and_avatar_round_trip ( ) { 91 + for fx in sqlite_fixtures ( ) . await { 92 + let user = fx 93 + . store 94 + . create_user ( sample_user ( "prof" , "prof@example.com" ) ) 95 + . await 96 + . unwrap ( ) ; 97 + 98 + let ok = fx 99 + . store 100 + . update_profile ( 101 + & user. id, 102 + crate :: ProfileUpdate { 103 + display_name: Some ( "Professor" . to_string ( ) ) , 104 + pronouns: Some ( "they/them" . to_string ( ) ) , 105 + bio: Some ( "Hello there." . to_string ( ) ) , 106 + website: Some ( "https://example.com" . to_string ( ) ) , 107 + location: Some ( "Everywhere" . to_string ( ) ) , 108 + social_github: Some ( "prof" . to_string ( ) ) , 109 + social_mastodon: Some ( "@prof@example.social" . to_string ( ) ) , 110 + } , 111 + ) 112 + . await 113 + . unwrap ( ) ; 114 + assert ! ( ok) ; 115 + 116 + let got = fx. store. user_by_id ( & user. id) . await . unwrap ( ) . unwrap ( ) ; 117 + assert_eq ! ( got. pronouns. as_deref ( ) , Some ( "they/them" ) ) ; 118 + assert_eq ! ( got. bio. as_deref ( ) , Some ( "Hello there." ) ) ; 119 + assert_eq ! ( got. social_mastodon. as_deref ( ) , Some ( "@prof@example.social" ) ) ; 120 + assert ! ( got. avatar_mime. is_none ( ) , "no avatar yet" ) ; 121 + 122 + 123 + assert ! ( 124 + fx. store 125 + . set_avatar_mime ( & user. id, Some ( "image/png" ) ) 126 + . await 127 + . unwrap ( ) 128 + ) ; 129 + let got = fx. store. user_by_id ( & user. id) . await . unwrap ( ) . unwrap ( ) ; 130 + assert_eq ! ( got. avatar_mime. as_deref ( ) , Some ( "image/png" ) ) ; 131 + 132 + assert ! ( fx. store. set_avatar_mime ( & user. id, None ) . await . unwrap ( ) ) ; 133 + let got = fx. store. user_by_id ( & user. id) . await . unwrap ( ) . unwrap ( ) ; 134 + assert ! ( got. avatar_mime. is_none ( ) , "avatar cleared" ) ; 135 + 136 + 137 + assert ! ( 138 + !fx. store 139 + . update_profile ( "nope" , crate :: ProfileUpdate :: default ( ) ) 140 + . await 141 + . unwrap ( ) 142 + ) ; 143 + } 144 + } 145 + 146 + # [ tokio :: test ] 90 147 async fn duplicate_username_conflicts_case_insensitively ( ) { 91 148 for fx in sqlite_fixtures ( ) . await { 92 149 fx. store
crates/web/src/tests.rs +61 −0 Expand 265 hidden lines 266 266 let res = app. oneshot ( get ( "/nobody" ) ) . await . unwrap ( ) ; 267 267 assert_eq ! ( res. status ( ) , StatusCode :: NOT_FOUND ) ; 268 268 } 269 + 270 + # [ tokio :: test ] 271 + async fn profile_page_renders_with_identity ( ) { 272 + let ( _state, app) = app ( ) . await ; 273 + let res = app. oneshot ( get ( "/ada" ) ) . await . unwrap ( ) ; 274 + assert_eq ! ( res. status ( ) , StatusCode :: OK ) ; 275 + let html = body_string ( res) . await ; 276 + 277 + assert ! ( html. contains ( "Ada" ) , "display name shown" ) ; 278 + assert ! ( html. contains ( "User · ada" ) , "profile subtitle shown" ) ; 279 + assert ! ( html. contains ( "/avatar/ada" ) , "avatar image referenced" ) ; 280 + } 281 + 282 + # [ tokio :: test ] 283 + async fn avatar_falls_back_to_identicon_svg ( ) { 284 + let ( _state, app) = app ( ) . await ; 285 + let res = app. oneshot ( get ( "/avatar/ada" ) ) . await . unwrap ( ) ; 286 + assert_eq ! ( res. status ( ) , StatusCode :: OK ) ; 287 + assert_eq ! ( 288 + res. headers ( ) . get ( header:: CONTENT_TYPE ) . unwrap ( ) , 289 + "image/svg+xml; charset=utf-8" 290 + ) ; 291 + let body = body_string ( res) . await ; 292 + assert ! ( body. starts_with ( "<svg" ) , "identicon is an SVG" ) ; 293 + } 294 + 295 + # [ tokio :: test ] 296 + async fn explore_lists_public_repos_for_anonymous ( ) { 297 + let ( state, app) = app ( ) . await ; 298 + let ada = state. store. user_by_username ( "ada" ) . await . unwrap ( ) . unwrap ( ) ; 299 + state 300 + . store 301 + . create_repo ( NewRepo { 302 + owner_id: ada. id, 303 + group_id: None , 304 + name: "open" . to_string ( ) , 305 + path: "open" . to_string ( ) , 306 + description: None , 307 + is_private: false , 308 + default_branch: "main" . to_string ( ) , 309 + } ) 310 + . await 311 + . unwrap ( ) ; 312 + 313 + let res = app. oneshot ( get ( "/explore" ) ) . await . unwrap ( ) ; 314 + assert_eq ! ( res. status ( ) , StatusCode :: OK ) ; 315 + let html = body_string ( res) . await ; 316 + assert ! ( html. contains ( "Public repositories" ) , "explore heading" ) ; 317 + assert ! ( html. contains ( "ada/open" ) , "public repo listed" ) ; 318 + 319 + assert ! ( !html. contains ( "btn-primary" ) , "no content sign-in button" ) ; 320 + } 321 + 322 + # [ tokio :: test ] 323 + async fn settings_requires_authentication ( ) { 324 + let ( _state, app) = app ( ) . await ; 325 + let res = app. oneshot ( get ( "/settings" ) ) . await . unwrap ( ) ; 326 + 327 + assert_eq ! ( res. status ( ) , StatusCode :: SEE_OTHER ) ; 328 + assert_eq ! ( res. headers ( ) . get ( header:: LOCATION ) . unwrap ( ) , "/login" ) ; 329 + }