fix: support current official Miruro mirrors
This commit is contained in:
@@ -83,3 +83,105 @@ fn episodes_resolve_by_promise_id_with_independent_tables_or_fail_explicitly() {
|
||||
io::ErrorKind::NotFound
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn mirror_failover_remembers_success_and_stops_on_rate_limits_and_bad_requests() {
|
||||
use std::{
|
||||
io::{BufRead, BufReader, Write},
|
||||
net::TcpListener,
|
||||
thread,
|
||||
};
|
||||
|
||||
let listener = TcpListener::bind("127.0.0.1:0").unwrap();
|
||||
let base = format!("http://{}", listener.local_addr().unwrap());
|
||||
let server = thread::spawn(move || {
|
||||
for (path, status, mime, body) in [
|
||||
("/one/catalog?q=anime", 403, "text/html", "blocked"),
|
||||
("/two/catalog?q=anime", 200, "text/html", "protection page"),
|
||||
(
|
||||
"/three/catalog?q=anime",
|
||||
200,
|
||||
"application/json",
|
||||
r#"{"data":[{"id":"series"}]}"#,
|
||||
),
|
||||
("/three/detail", 503, "text/html", "unavailable"),
|
||||
(
|
||||
"/one/detail",
|
||||
200,
|
||||
"text/sveltekit-data",
|
||||
"episode metadata",
|
||||
),
|
||||
("/one/limited", 429, "text/html", "rate limited"),
|
||||
("/one/bad-query", 400, "application/json", "{}"),
|
||||
("/one/exhausted", 403, "text/html", "blocked"),
|
||||
("/two/exhausted", 404, "text/html", "gone"),
|
||||
("/three/exhausted", 502, "text/html", "unavailable"),
|
||||
("/one/recovered", 200, "application/json", r#"{"data":[]}"#),
|
||||
] {
|
||||
let (mut stream, _) = listener.accept().unwrap();
|
||||
stream
|
||||
.set_read_timeout(Some(Duration::from_secs(5)))
|
||||
.unwrap();
|
||||
let mut reader = BufReader::new(&stream);
|
||||
let mut line = String::new();
|
||||
reader.read_line(&mut line).unwrap();
|
||||
assert_eq!(line.trim_end(), format!("GET {path} HTTP/1.1"));
|
||||
loop {
|
||||
line.clear();
|
||||
reader.read_line(&mut line).unwrap();
|
||||
if line == "\r\n" {
|
||||
break;
|
||||
}
|
||||
}
|
||||
write!(
|
||||
stream,
|
||||
"HTTP/1.1 {status} Response\r\nContent-Type: {mime}\r\nContent-Length: {}\r\nConnection: close\r\n\r\n{body}",
|
||||
body.len()
|
||||
).unwrap();
|
||||
}
|
||||
});
|
||||
let bases = [
|
||||
format!("{base}/one"),
|
||||
format!("{base}/two"),
|
||||
format!("{base}/three"),
|
||||
];
|
||||
let mirrors: Vec<_> = bases.iter().map(String::as_str).collect();
|
||||
let client = Client::default();
|
||||
let body = client
|
||||
.get_from(
|
||||
&mirrors,
|
||||
"/catalog",
|
||||
&[("q", "anime")],
|
||||
&["application/json"],
|
||||
)
|
||||
.unwrap();
|
||||
assert_eq!(
|
||||
serde_json::from_str::<Value>(&body).unwrap()["data"][0]["id"],
|
||||
"series"
|
||||
);
|
||||
assert_eq!(
|
||||
client
|
||||
.get_from(&mirrors, "/detail", &[], &["text/sveltekit-data"])
|
||||
.unwrap(),
|
||||
"episode metadata"
|
||||
);
|
||||
for (path, status) in [("/limited", 429), ("/bad-query", 400)] {
|
||||
let error = client
|
||||
.get_from(&mirrors, path, &[], &["application/json"])
|
||||
.unwrap_err();
|
||||
assert!(error.to_string().contains(&format!("HTTP {status}")));
|
||||
}
|
||||
let error = client
|
||||
.get_from(&mirrors, "/exhausted", &[], &["application/json"])
|
||||
.unwrap_err();
|
||||
assert!(error.to_string().contains("failed on 3/3 mirrors"));
|
||||
assert!(error.to_string().contains("HTTP 502"));
|
||||
let body = client
|
||||
.get_from(&mirrors, "/recovered", &[], &["application/json"])
|
||||
.unwrap();
|
||||
assert_eq!(
|
||||
serde_json::from_str::<Value>(&body).unwrap()["data"],
|
||||
serde_json::json!([])
|
||||
);
|
||||
server.join().unwrap();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user