Your browser sucks, JavaScript disabled! Gunnar's console needs JavaScript and WebAssembly. Git over SSH on port 2222 does not.

nordisk/gunnar-ui

crates/gunnar-public-site/tests/sitemap.rs

//! **S-3: `/sitemap.xml`**: the public pages and every PUBLIC repository with
//! its `lastmod`, served without a login, built with the other pages on the
//! timer (never per request), and never naming a private repository.

mod common;
use common::*;

use http::{header, StatusCode};

#[tokio::test]
async fn a_crawler_reads_the_sitemap_without_a_login() {
    let (site, calls) = site().await;
    let before = calls.total();
    let a = get(&app(site), "/sitemap.xml").await;
    assert_eq!(a.status, StatusCode::OK, "{}", a.body);
    assert!(a.headers[header::CONTENT_TYPE].to_str().unwrap().starts_with("application/xml"));
    assert!(a.body.starts_with("<?xml version=\"1.0\" encoding=\"UTF-8\"?>"), "{}", a.body);
    assert!(a.body.contains("<urlset xmlns=\"http://www.sitemaps.org/schemas/sitemap/0.9\">") && a.body.trim_end().ends_with("</urlset>"));
    assert!(a.body.contains("<url><loc>https://gunnar.example/</loc><lastmod>2025-10-25</lastmod></url>"), "{}", a.body);
    assert!(
        a.body.contains(&format!("<url><loc>https://gunnar.example/{NS}/{PUBLIC}</loc><lastmod>2025-10-25</lastmod></url>")),
        "every public repository, with the day of its newest commit: {}",
        a.body
    );
    assert!(a.body.contains(&format!("<loc>https://gunnar.example/{NS}</loc>")), "the namespace's page: {}", a.body);
    assert_eq!(calls.total(), before, "the sitemap was built on request");
}

#[tokio::test]
async fn the_sitemap_names_no_private_repository() {
    let (site, _) = site().await;
    let body = get(&app(site), "/sitemap.xml").await.body;
    assert!(!body.contains(PRIVATE) && !body.contains(PRIVATE_TEXT), "{body}");
}

#[tokio::test]
async fn the_sign_up_page_is_listed_only_where_people_sign_up() {
    let (site, _) = site().await;
    assert!(!get(&app(site), "/sitemap.xml").await.body.contains("/signup"));
    let mut c = config();
    c.signup = true;
    let (site, _) = site_with(Fake::new(), c).await;
    assert!(get(&app(site), "/sitemap.xml").await.body.contains("<loc>https://gunnar.example/signup</loc>"));
}

#[tokio::test]
async fn the_sitemap_lists_no_console_or_login_path() {
    let (site, _) = site().await;
    let body = get(&app(site), "/sitemap.xml").await.body;
    for loc in body.split("<loc>").skip(1).map(|l| &l[..l.find("</loc>").unwrap()]) {
        let path = loc.strip_prefix("https://gunnar.example").expect("every URL is this site's");
        assert!(
            !gunnar_public_site::robots::DISALLOWED.iter().any(|d| path.starts_with(d)),
            "the sitemap lists {path}, which robots.txt disallows"
        );
    }
}

#[tokio::test]
async fn a_signed_in_person_reads_the_same_sitemap() {
    let (site, _) = site().await;
    let a = get_as(&app(site), "/sitemap.xml", Some(SESSION), None, Some("me")).await;
    assert_eq!(a.status, StatusCode::OK);
    assert!(a.body.contains("<urlset"));
}

/// The owner (2026-10-25): each lastmod is real, the time of that public
/// repository's own newest commit; the home's is the newest of them all.
#[tokio::test]
async fn each_lastmod_is_its_repositorys_newest_commit() {
    let mut fake = Fake::new();
    fake.extra.push(gunnar_public_site::RepoRef { namespace: "other".into(), name: "older".into(), description: String::new(), public: true });
    fake.at.push(("older".into(), 1_700_000_000));
    let (site, _) = site_with(fake, config()).await;
    let body = get(&app(site), "/sitemap.xml").await.body;
    assert!(body.contains("<url><loc>https://gunnar.example/other/older</loc><lastmod>2023-11-14</lastmod></url>"), "{body}");
    assert!(body.contains("<url><loc>https://gunnar.example/other</loc><lastmod>2023-11-14</lastmod></url>"), "{body}");
    assert!(body.contains(&format!("<url><loc>https://gunnar.example/{NS}/{PUBLIC}</loc><lastmod>2025-10-25</lastmod></url>")), "{body}");
    assert!(body.contains("<url><loc>https://gunnar.example/</loc><lastmod>2025-10-25</lastmod></url>"), "{body}");
}