johnandersen777.bsky.social/knotless
git that lives in your Atmosphere account
Forked from chadtmiller.com/knotless
0
0
//! `/_status` against a reference PDS: a mirror read as its code page
//! reads it, one that can no longer be, and `knotless status` showing it.
#[path = "../../knotless/tests/common/mod.rs"]
mod common;
use common::*;
use knotless::commit::{Change, Spec};
use knotless::status::Status;
use knotless_serve::{App, router};
use std::time::Duration;
fn get(url: &str) -> (u16, Status) {
let rt = tokio::runtime::Runtime::new().unwrap();
rt.block_on(async {
let res = reqwest::get(url).await.unwrap();
let code = res.status().as_u16();
(code, res.json().await.unwrap())
})
}
fn cli(args: &[&str]) -> (i32, String) {
let mut argv = vec!["knotless".to_string()];
argv.extend(args.iter().map(|a| a.to_string()));
// On a thread of its own: the command's poll is more than a test
// thread's stack holds, unoptimised.
let run = std::thread::Builder::new()
.stack_size(32 << 20)
.spawn(move || {
let mut out = Vec::new();
// No appview, so a bare `status` never asks knotless.dev.
let env =
knotless::env::Env::from_vars(|k| (k == "KNOTLESS_APPVIEW").then(String::new));
let code = knotless::cli::main_with(argv, env, &mut &b""[..], &mut out);
(code, String::from_utf8(out).unwrap())
});
run.unwrap().join().unwrap()
}
#[test]
fn status_reads_a_mirror() {
let s = Sandbox::new();
let rt = tokio::runtime::Runtime::new().unwrap();
let url = knotless::url::RepoUrl::parse(&s.url("probe")).unwrap();
let spec = Spec {
branch: "main".into(),
message: "first".into(),
changes: vec![Change::Put {
path: "a.txt".into(),
data: b"a\n".to_vec(),
executable: false,
}],
retries: 3,
..Default::default()
};
let tip = rt
.block_on(knotless::commit::commit(&ctx(&s), &url, &spec))
.unwrap()
.sha;
// Trusted for a minute, so the mirror is not synced again under the test.
let app = App::new(ctx(&s), Duration::from_secs(60));
let listener = rt
.block_on(tokio::net::TcpListener::bind("127.0.0.1:0"))
.unwrap();
let base = format!("http://{}", listener.local_addr().unwrap());
let routes = router(app.clone());
rt.spawn(async move { axum::serve(listener, routes).await.unwrap() });
let handle = s.account.handle.clone();
// Nothing followed, no index, no mirror asked after: well.
let (code, status) = get(&format!("{base}/_status"));
assert_eq!(code, 200);
assert!(status.ok && status.problems.is_empty(), "{status:?}");
// The mirror reads: its commit, and its packs counted.
let probe = format!("{handle}/probe");
let (code, status) = get(&format!("{base}/_status?repo={probe}.git"));
assert_eq!(code, 200, "{status:?}");
let mirror = status.mirror.unwrap();
assert_eq!(mirror.commit.as_deref(), Some(tip.as_str()));
assert!(mirror.packs.unwrap() >= 1, "{mirror:?}");
let (code, text) = cli(&["status", &base, "--repo", &probe]);
assert_eq!(code, 0, "{text}");
assert!(text.starts_with(&format!("ok\t{base}")), "{text}");
assert!(
text.contains(&format!("mirror\t{probe}\tok at {}", &tip[..12])),
"{text}"
);
let (code, json) = cli(&["status", &base, "--json"]);
assert_eq!(code, 0);
assert!(serde_json::from_str::<Status>(&json).unwrap().ok);
// No such repository, or no repository named at all.
let (code, status) = get(&format!("{base}/_status?repo={handle}/nope"));
assert_eq!(code, 503);
let error = status.mirror.unwrap().error.unwrap();
assert!(error.contains("no such repository"), "{error}");
assert!(status.problems[0].starts_with(&format!("mirror {handle}/nope: ")));
let (code, status) = get(&format!("{base}/_status?repo=probe"));
assert_eq!(code, 503);
let error = status.mirror.unwrap().error.unwrap();
assert_eq!(error, "?repo= takes <handle or did>/<name>");
// Its packs gone: the objects can no longer be read, as when a mirror
// outgrew the room its open handle had for packs.
let git = rt.block_on(app.mirror(&handle, "probe")).unwrap();
for pack in std::fs::read_dir(git.dir.join("objects/pack")).unwrap() {
std::fs::remove_file(pack.unwrap().path()).unwrap();
}
let (code, status) = get(&format!("{base}/_status?repo={probe}"));
assert_eq!(code, 503);
let mirror = status.mirror.unwrap();
assert_eq!(
(mirror.ok, mirror.packs, mirror.commit),
(false, None, None)
);
assert!(mirror.error.is_some());
let (code, text) = cli(&["status", &base, "--repo", &probe]);
assert_eq!(code, 1);
assert!(text.starts_with("UNHEALTHY"), "{text}");
// What the command is asked wrongly.
assert_eq!(cli(&["status"]).0, 1);
assert_eq!(cli(&["status", &base, "--bogus"]).0, 1);
assert_eq!(cli(&["status", "http://127.0.0.1:1"]).0, 1);
assert_eq!(cli(&["status", &format!("{base}/healthz")]).0, 1);
}