//! A browser logged in to several accounts at once: logging in to another //! keeps the one before, switching needs no login, and logging out of one //! goes on as the other used last. The pages take the theme of the account //! in use, or the system's when it chose none. use super::*; /// The cookies `jar` holds (`;` between) once a response `set` them: a /// cookie set empty is gone. fn kept(jar: &str, set: &[String]) -> String { let mut all: Vec<(String, String)> = jar .split(';') .filter_map(|p| p.split_once('=')) .map(|(k, v)| (k.to_string(), v.to_string())) .collect(); for cookie in set { let (name, value) = pair(cookie).split_once('=').unwrap(); all.retain(|(k, _)| k != name); if !value.is_empty() && name != "knotless_login" { all.push((name.to_string(), value.to_string())); } } let pairs: Vec = all.iter().map(|(k, v)| format!("{k}={v}")).collect(); pairs.join(";") } #[test] fn switching_between_accounts_on_the_pages() { let server = gateway_with(None, true); let one = person(account()); let two = person(account()); let viewer = |jar: &str| { let url = format!("{}/xrpc/dev.knotless.getViewer", server.base); browse("GET", &url, Some(jar), None, None).2 }; let switch = |jar: &str, did: &str, origin: Option<&str>| { let url = format!("{}/oauth/switch", server.base); browse("POST", &url, Some(jar), origin, Some(json!({ "did": did }))) }; let logout = |jar: &str| { let url = format!("{}/oauth/logout", server.base); browse("POST", &url, Some(jar), None, None) }; let themed = |jar: &str| { let theme = jar.split(';').find_map(|p| p.strip_prefix("theme=")); theme.map(str::to_string) }; // One account: nothing else to switch to. It chooses Dracula, and // logging in again brings it. let set = log_in(&server, &one.account, ""); assert!(set.iter().all(|c| !c.starts_with("knotless_accounts="))); let first = kept("", &set); let url = format!("{}/xrpc/dev.knotless.putPreferences", server.base); let dracula = json!({"$type": "dev.knotless.getPreferences#appearancePref", "theme": "dracula"}); let body = json!({ "preferences": [dracula] }); assert_eq!(browse("POST", &url, Some(&first), None, Some(body)).0, 200); let set = log_in_beside(&server, &one.account, "", &first); let first = kept(&first, &set); assert_eq!(themed(&first).as_deref(), Some("dracula")); let shown = viewer(&first); assert_eq!(shown["actor"]["did"], one.account.did); assert_eq!(shown.get("accounts"), None); // Logging in to another keeps the first beside it, and again keeps it // once. let set = log_in_beside(&server, &two.account, "", &first); let both = kept(&first, &set); assert_eq!(themed(&both), None, "the second chose no theme"); let shown = viewer(&both); assert_eq!(shown["actor"]["did"], two.account.did); assert_eq!(shown["accounts"][0]["did"], one.account.did); assert_eq!(shown["accounts"][0]["handle"], one.account.handle); let set = log_in_beside(&server, &two.account, "", &both); let again = kept(&both, &set); let shown = viewer(&again); assert_eq!(shown["actor"]["did"], two.account.did); assert_eq!(shown["accounts"].as_array().unwrap().len(), 1); // Switching swaps them, with no login; only from this site, and only // to an account this browser is logged in to. let away = Some("https://evil.example"); assert_eq!(switch(&both, &one.account.did, away).0, 403); assert_eq!(switch(&first, &two.account.did, None).0, 400); let (status, set, _) = switch(&both, &one.account.did, None); assert_eq!(status, 200); let swapped = kept(&both, &set); assert_eq!(themed(&swapped).as_deref(), Some("dracula")); let shown = viewer(&swapped); assert_eq!(shown["actor"]["did"], one.account.did); assert_eq!(shown["accounts"][0]["did"], two.account.did); // Logging out of one goes on as the other, and leaves none of the // first's cookies standing. let (status, set, _) = logout(&swapped); assert_eq!(status, 200); let left = kept(&swapped, &set); assert!(!left.contains("knotless_accounts="), "{left}"); assert_eq!(themed(&left), None, "the other chose no theme"); let shown = viewer(&left); assert_eq!(shown["actor"]["did"], two.account.did); assert_eq!(shown.get("accounts"), None); assert_eq!(viewer(&swapped), json!({"login": true})); // And out of the last, as no one. let (_, set, _) = logout(&left); assert!(set[0].starts_with("knotless_session=;"), "{set:?}"); assert_eq!(viewer(&kept(&left, &set)), json!({"login": true})); }