//! The HTML site: listing, repository pages, and what stays hidden. mod common; use assert2::check; use common::{Fixture, PUBLIC_URL}; use poem::{http::StatusCode, test::TestClient}; const README: &str = "# Widget\n\nA *widget* for testing.\n\n\n"; fn fixture() -> Fixture { let fx = Fixture::new(); fx.repo( "widget", true, "Widgets for everyone", &[("README.md", README)], ); fx.repo( "secret", false, "Nobody's business", &[("README.md", "# Secret\n")], ); fx.repo("plain", true, "", &[("README", "Just text \n")]); fx.repo("bare", true, "Nothing committed", &[]); fx } #[tokio::test] async fn index_lists_only_public_repositories() { let fx = fixture(); let client = TestClient::new(fx.app.routes()); let resp = client.get("/").send().await; resp.assert_status_is_ok(); let body = resp.0.into_body().into_string().await.unwrap(); check!(body.contains("href=\"/widget\"")); check!(body.contains("Widgets for everyone")); check!(body.contains("href=\"/plain\"")); check!(body.contains("href=\"/bare\"")); check!(!body.contains("secret")); check!(body.contains("Test Site")); } #[tokio::test] async fn index_survives_a_repository_that_cannot_be_opened() { let fx = fixture(); let broken = fx.dir.path().join("broken.git"); std::fs::create_dir(&broken).unwrap(); common::set_public(&broken, true); let client = TestClient::new(fx.app.routes()); let resp = client.get("/").send().await; resp.assert_status_is_ok(); let body = resp.0.into_body().into_string().await.unwrap(); check!(body.contains("href=\"/widget\"")); check!(!body.contains("broken")); } #[tokio::test] async fn a_directory_named_dot_git_dot_git_is_not_a_second_listing() { let fx = Fixture::new(); fx.repo("tool", true, "The tool", &[]); fx.repo("tool.git", true, "Named with a suffix", &[]); let client = TestClient::new(fx.app.routes()); let body = client .get("/") .send() .await .0 .into_body() .into_string() .await .unwrap(); check!(body.matches("href=\"/tool\"").count() == 1); check!(!body.contains("Named with a suffix")); check!(client.get("/tool.git.git").send().await.0.status() == StatusCode::NOT_FOUND); } #[tokio::test] async fn index_is_sorted_case_insensitively() { let fx = Fixture::new(); for name in ["zeta", "Alpha", "beta", "Gamma"] { fx.repo(name, true, "", &[]); } let client = TestClient::new(fx.app.routes()); let body = client .get("/") .send() .await .0 .into_body() .into_string() .await .unwrap(); let position = |name: &str| body.find(&format!("href=\"/{name}\"")).expect(name); check!(position("Alpha") < position("beta")); check!(position("beta") < position("Gamma")); check!(position("Gamma") < position("zeta")); } #[tokio::test] async fn index_paginates_when_there_are_more_repositories_than_fit() { let fx = Fixture::with_page_size(2); for name in ["a", "b", "c", "d", "e"] { fx.repo(name, true, "", &[]); } let client = TestClient::new(fx.app.routes()); let first = client .get("/") .send() .await .0 .into_body() .into_string() .await .unwrap(); check!(first.contains("href=\"/a\"") && first.contains("href=\"/b\"")); check!(!first.contains("href=\"/c\"")); check!(first.contains("Page 1 of 3")); check!(first.contains("href=\"/?page=2\"")); let last = client .get("/?page=3") .send() .await .0 .into_body() .into_string() .await .unwrap(); check!(last.contains("href=\"/e\"") && !last.contains("href=\"/d\"")); check!(last.contains("href=\"/?page=2\"") && !last.contains("href=\"/?page=4\"")); let beyond = client.get("/?page=4").send().await; check!(beyond.0.status() == StatusCode::NOT_FOUND); } #[tokio::test] async fn index_has_no_pager_when_everything_fits() { let fx = fixture(); let client = TestClient::new(fx.app.routes()); let body = client .get("/") .send() .await .0 .into_body() .into_string() .await .unwrap(); check!(!body.contains("class=\"pager\"")); } #[tokio::test] async fn repository_page_shows_readme_and_clone_url() { let fx = fixture(); let client = TestClient::new(fx.app.routes()); let resp = client.get("/widget").send().await; resp.assert_status_is_ok(); let body = resp.0.into_body().into_string().await.unwrap(); check!(body.contains("

Widget

")); check!(body.contains("widget")); check!(!body.contains("