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/llms.rs

//! **S-7: AI tools read gunnar.rs directly** (owner 2026-10-25): `/llms.txt`,
//! `/llms-full.txt`, and per public repository its README as Markdown and its
//! file tree as plain text, all without a login. No private repository is
//! named in any of them.

mod common;
use common::*;

use http::{header, StatusCode};

#[tokio::test]
async fn llms_txt_is_served_without_a_login_and_lists_the_public_repositories() {
    let (site, _) = site().await;
    let a = get(&app(site), "/llms.txt").await;
    assert_eq!(a.status, StatusCode::OK, "{}", a.body);
    assert!(a.headers[header::CONTENT_TYPE].to_str().unwrap().starts_with("text/plain"));
    assert!(a.body.starts_with("# Gunnar\n"), "llms.txt opens with the site's name: {}", a.body);
    assert!(a.body.starts_with("# Gunnar\n\n> Gunnar is a European git server in Rust: self-hosted or SaaS, with SLSA Level 3 releases.\n\n"), "{}", a.body);
    assert!(a.body.contains("## Public repositories"), "{}", a.body);
    assert!(a.body.contains(&format!("[{NS}/{PUBLIC}](https://gunnar.example/{NS}/{PUBLIC})")), "{}", a.body);
    assert!(a.body.contains(&format!("https://gunnar.example/{NS}/{PUBLIC}/readme.md")), "{}", a.body);
    assert!(a.body.contains(&format!("https://gunnar.example/{NS}/{PUBLIC}/tree.txt")), "{}", a.body);
    assert!(a.body.contains("(https://gunnar.example/login)"), "{}", a.body);
}

/// ★ The owner (2026-10-29): llms.txt is the one line of what Gunnar is, the way
/// in and the public repositories. No feature list, no docs, nothing else.
#[tokio::test]
async fn llms_txt_carries_only_the_one_line_the_way_in_and_the_repositories() {
    let (site, _) = site().await;
    let body = get(&app(site), "/llms.txt").await.body;
    let headings: Vec<&str> = body.lines().filter(|l| l.starts_with('#')).collect();
    assert_eq!(headings, ["# Gunnar", "## Public repositories"], "{body}");
    let quoted: Vec<&str> = body.lines().filter(|l| l.starts_with('>')).collect();
    assert_eq!(quoted, ["> Gunnar is a European git server in Rust: self-hosted or SaaS, with SLSA Level 3 releases."], "{body}");
    for gone in ["LFS", "signed push", "twin", "appliance", "Passkey", "AGPL", "Release signing", "llms-full.txt", "Leptos", "gRPC"] {
        assert!(!body.contains(gone), "llms.txt still says {gone:?}: {body}");
    }
}

#[tokio::test]
async fn llms_full_txt_carries_the_one_line_and_each_public_readme() {
    let (site, _) = site().await;
    let a = get(&app(site), "/llms-full.txt").await;
    assert_eq!(a.status, StatusCode::OK);
    assert!(a.headers[header::CONTENT_TYPE].to_str().unwrap().starts_with("text/markdown"));
    assert!(a.body.starts_with("# Gunnar\n\n> Gunnar is a European git server in Rust: self-hosted or SaaS, with SLSA Level 3 releases.\n\n"), "{}", a.body);
    assert!(!a.body.contains("# What Gunnar is") && !a.body.contains("# Using Gunnar") && !a.body.contains("Release signing"), "no docs beside the READMEs: {}", a.body);
    assert!(a.body.contains(&format!("## {NS}/{PUBLIC}")), "{}", a.body);
    assert!(a.body.contains(&format!("#### {README_HEADING}")), "the README nests under its repository: {}", a.body);
    assert!(a.body.contains(&format!("git clone ssh://git.gunnar.example:2222/{NS}/{PUBLIC}")), "{}", a.body);
}

#[tokio::test]
async fn a_public_repository_has_its_readme_and_tree_at_stable_urls() {
    let (site, _) = site().await;
    let app = app(site);
    let readme = get(&app, &format!("/{NS}/{PUBLIC}/readme.md")).await;
    assert_eq!(readme.status, StatusCode::OK);
    assert!(readme.headers[header::CONTENT_TYPE].to_str().unwrap().starts_with("text/markdown"));
    assert!(readme.body.starts_with(&format!("# {README_HEADING}")), "{}", readme.body);
    let tree = get(&app, &format!("/{NS}/{PUBLIC}/tree.txt")).await;
    assert_eq!(tree.status, StatusCode::OK);
    assert_eq!(tree.body, format!("# {NS}/{PUBLIC}: files at aa11bb22cc33\nREADME.md\nsrc/\nsrc/main.rs\n"));
    // A trailing slash is the same page.
    assert_eq!(get(&app, &format!("/{NS}/{PUBLIC}/tree.txt/")).await.body, tree.body);
}

#[tokio::test]
async fn a_private_repository_has_no_markdown_view_and_is_named_nowhere() {
    let (site, calls) = site().await;
    let app = app(site.clone());
    for path in [format!("/{NS}/{PRIVATE}/readme.md"), format!("/{NS}/{PRIVATE}/tree.txt")] {
        let a = get(&app, &path).await;
        assert_eq!((a.status, a.body.as_str()), (StatusCode::SEE_OTHER, TODAY), "{path} must answer as the console does today");
    }
    for (path, body) in every_page(&site) {
        assert!(!body.contains(PRIVATE) && !path.contains(PRIVATE), "{path} names the private repository");
        assert!(!body.contains(PRIVATE_TEXT), "{path} carries the private repository's description");
    }
    assert_eq!(site.built().repos.len(), 1);
    assert_eq!(calls.snapshot.load(std::sync::atomic::Ordering::SeqCst), 1, "only the public repository was read");
}

#[tokio::test]
async fn no_page_names_a_person() {
    let (site, _) = site().await;
    for (path, body) in every_page(&site) {
        assert!(!body.contains("@mock.invalid") && !body.contains("author"), "{path}: {body}");
    }
}

#[tokio::test]
async fn a_head_request_gets_the_headers_and_no_body() {
    use tower::ServiceExt;
    let (site, _) = site().await;
    let r = app(site).oneshot(http::Request::head("/llms.txt").body(axum::body::Body::empty()).unwrap()).await.unwrap();
    assert_eq!(r.status(), StatusCode::OK);
    let body = axum::body::to_bytes(r.into_body(), 1 << 20).await.unwrap();
    assert!(body.is_empty());
}

#[tokio::test]
async fn a_post_is_never_the_sites() {
    use tower::ServiceExt;
    let (site, _) = site().await;
    let r = app(site).oneshot(http::Request::post("/llms.txt").body(axum::body::Body::empty()).unwrap()).await.unwrap();
    assert_eq!(r.status(), StatusCode::SEE_OTHER);
}