johnandersen777.bsky.social/knotless
git that lives in your Atmosphere account
Forked from chadtmiller.com/knotless
0
0
//! 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<String> = 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}));
}