#![cfg(feature = "github")]
use std::sync::{Arc, Mutex};
use std::time::Duration;
use self_update::backends::github;
use self_update::http_client::{HeaderMap, HttpClient, HttpResponse};
struct CannedResponse;
impl HttpResponse for CannedResponse {
fn headers(&self) -> &HeaderMap {
static EMPTY: std::sync::OnceLock<HeaderMap> = std::sync::OnceLock::new();
EMPTY.get_or_init(HeaderMap::new)
}
fn body(self: Box<Self>) -> Box<dyn std::io::Read> {
Box::new(std::io::Cursor::new(b"[]".to_vec()))
}
}
struct AuthRecorder(Arc<Mutex<Vec<Option<String>>>>);
impl HttpClient for AuthRecorder {
fn get(
&self,
_url: &str,
headers: &HeaderMap,
_timeout: Option<Duration>,
) -> self_update::Result<Box<dyn HttpResponse>> {
self.0.lock().unwrap().push(
headers
.get(self_update::http::header::AUTHORIZATION)
.map(|v| v.to_str().expect("a header value is ASCII").to_string()),
);
Ok(Box::new(CannedResponse))
}
}
fn auth_header_of(f: impl FnOnce(Arc<dyn HttpClient>)) -> Option<String> {
let seen = Arc::new(Mutex::new(Vec::new()));
f(Arc::new(AuthRecorder(seen.clone())));
let seen = seen.lock().unwrap();
assert_eq!(
seen.len(),
1,
"exactly one request must have gone through the transport, got {seen:?}"
);
seen[0].clone()
}
#[test]
fn a_blank_first_variable_falls_through_to_the_second() {
unsafe {
std::env::set_var("GH_TOKEN", " \t\n");
std::env::set_var("GITHUB_TOKEN", " second-var-token\n");
}
let mut upd = github::Update::configure();
upd.repo_owner("o")
.repo_name("r")
.bin_name("app")
.current_version("0.1.0");
assert!(!upd.has_auth_token(), "nothing is set before the call");
upd.auth_token_from_env();
assert!(
upd.has_auth_token(),
"a blank GH_TOKEN must not stop the lookup: GITHUB_TOKEN still supplies a token"
);
let built = upd.build().expect("an env-sourced token must still build");
assert_eq!(
self_update::UpdateConfig::auth_token(&built),
Some("second-var-token"),
"the second variable's value must be used, with surrounding whitespace trimmed (an \
untrimmed value would fail HTTP header encoding at request time)"
);
let mut list = github::ReleaseList::configure();
list.repo_owner("o").repo_name("r");
assert!(!list.has_auth_token());
list.auth_token_from_env();
assert!(
list.has_auth_token(),
"the ReleaseList builder must walk the same list past the blank first variable"
);
let header = auth_header_of(|client| {
let _ = list.http_client(client).build().unwrap().fetch();
});
assert_eq!(
header.as_deref(),
Some("token second-var-token"),
"the second variable's value must reach the wire, not the blank first one"
);
}