Tests for push passes, downloads, OPML round trips and directory refresh

Each against a local stand-in server: Apple's push service answering per token, a feed host
serving a login page and a torrent, a directory refresh of feeds that cannot be read. push.rs
takes the APNs address as a parameter so the test can point it at the stand-in. coverage.sh asks
for 8 GB free, not 9: its container is capped at 6.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RqkrVARreS9wxzH5o2rF7N
This commit is contained in:
2026-11-11 23:04:10 +00:00
parent 75d099474c
commit a55f08d4ff
5 changed files with 209 additions and 8 deletions

View File

@@ -217,4 +217,49 @@ mod tests {
dir.pages_list.lock().await.as_mut().unwrap().0 -= PAGES_LIST_FOR;
assert!(!Arc::ptr_eq(&first, &dir.pages_list().await.unwrap()));
}
#[tokio::test]
async fn a_refresh_lists_a_feed_and_not_one_that_could_not_be_read() {
use axum::routing::get;
let rss = r#"<?xml version="1.0"?><rss version="2.0"><channel><title>Good Show</title>
<item><title>One</title><guid>g1</guid><enclosure url="https://x/1.mp3" type="audio/mpeg" length="1"/></item>
</channel></rss>"#;
let opml =
r#"<?xml version="1.0"?><opml version="2.0"><head/><body><outline xmlUrl="https://x/f"/></body></opml>"#;
let app = axum::Router::new()
.route("/good.xml", get(move || async move { rss }))
.route("/list.opml", get(move || async move { opml }))
.route("/garbled.xml", get(|| async { "<rss><channel" }))
.route("/gone.xml", get(|| async { axum::http::StatusCode::NOT_FOUND }));
let listener = tokio::net::TcpListener::bind("127.0.0.1:0").await.unwrap();
let at = listener.local_addr().unwrap();
tokio::spawn(async move { axum::serve(listener, app).await });
let allowed = ipx_feed::guard::Allowed::parse(&["127.0.0.1".into()]).unwrap();
let dir = Directory::new(Store::scratch().await, vec![], allowed).unwrap();
let paths = ["/good.xml", "/list.opml", "/garbled.xml", "/gone.xml"];
let entries: Vec<crate::SnapshotEntry> = paths
.iter()
.map(|p| crate::SnapshotEntry { url: format!("http://{at}{p}"), subscribers: 1, category: None })
.collect();
assert_eq!(dir.store.snapshot("tower", &entries).await.unwrap().accepted, 4);
let due = dir.store.due(REFRESH_EVERY, 10).await.unwrap();
assert_eq!(due.len(), 4);
for d in &due {
dir.refresh(d).await.unwrap();
}
assert!(dir.store.due(REFRESH_EVERY, 10).await.unwrap().is_empty(), "each was checked, failed or not");
let page = |path: &str| {
let d = due.iter().find(|d| d.url.ends_with(path)).unwrap();
dir.store.feed(&d.id)
};
let good = page("/good.xml").await.unwrap().unwrap();
assert_eq!(good.listing.title.as_deref(), Some("Good Show"));
assert!(good.listing.podcast && good.failing.is_none() && good.items.len() == 1);
for bad in ["/list.opml", "/garbled.xml", "/gone.xml"] {
// Never read, so never listed: a page of nothing is no use to anyone browsing.
assert!(page(bad).await.unwrap().is_none(), "{bad} is not listed");
}
}
}