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

nordisk/facett

xtask/tests/licenses_ship_with_every_crate.rs

//! **The license text travels with every published artifact.**
//!
//! facett is `MIT` from 0.2.1 (owner decision 2026-10-03; everything published up to
//! and including 0.2.0 stays `MIT OR Apache-2.0`, which `NOTICE` says). MIT expects
//! its text to accompany every copy. The copyright holder is the company, Ignalina ApS.
//!
//! Where the texts must be, and why each place:
//!
//! * the repository root — `LICENSE` (MIT) and `NOTICE`, the ONE writer of each;
//! * every crate cargo may publish — `cargo package` packs only files under the
//!   crate's own directory (an `include` cannot reach `..`), so each publishable
//!   crate carries byte-identical copies (copies, not symlinks);
//! * both PyPI packages — `license-files` in each pyproject; the wheel/sdist build
//!   stages the root texts beside the pyproject (`facett-ops` `pip::LICENSE_TEXTS`).

use std::path::{Path, PathBuf};

fn repo_root() -> PathBuf {
    Path::new(env!("CARGO_MANIFEST_DIR")).parent().unwrap().to_path_buf()
}

const TEXTS: [&str; 2] = ["LICENSE", "NOTICE"];

fn read(p: &Path) -> Vec<u8> {
    std::fs::read(p).unwrap_or_else(|e| panic!("{}: {e}", p.display()))
}

fn version_free_license_line(path: &str) -> String {
    let text = std::fs::read_to_string(repo_root().join(path)).unwrap();
    text.lines().find(|l| l.trim_start().starts_with("license =")).unwrap_or_default().trim().to_string()
}

#[test]
fn the_root_texts_are_mit_by_the_company_and_say_what_changed() {
    let root = repo_root();
    let mit = String::from_utf8(read(&root.join("LICENSE"))).unwrap();
    assert!(mit.starts_with("MIT License\n"), "LICENSE is not the MIT text");
    assert!(
        mit.lines().any(|l| l.starts_with("Copyright (c) 2026 Ignalina ApS")),
        "LICENSE's copyright line must name the company"
    );
    assert!(mit.contains("THE SOFTWARE IS PROVIDED \"AS IS\""), "LICENSE is missing the MIT warranty disclaimer");
    assert!(!root.join("LICENSE-APACHE").exists() && !root.join("LICENSE-MIT").exists(), "one license file: LICENSE");
    let notice = String::from_utf8(read(&root.join("NOTICE"))).unwrap();
    assert!(notice.contains("up to and including 0.2.0") && notice.contains("MIT OR Apache-2.0"), "{notice}");
    assert!(notice.contains("From 0.2.1 facett is MIT only"), "{notice}");
}

#[test]
fn every_license_declaration_says_mit() {
    assert_eq!(version_free_license_line("Cargo.toml"), "license = \"MIT\"", "[workspace.package]");
    for file in ["pkg-facet-wrapped/pyproject.toml", "py/pyproject.toml", "facett-build-thing/Cargo.toml"] {
        assert_eq!(version_free_license_line(file), "license = \"MIT\"", "{file}");
    }
}

#[test]
fn every_publishable_crate_carries_byte_identical_copies() {
    let root = repo_root();
    let want: Vec<Vec<u8>> = TEXTS.iter().map(|t| read(&root.join(t))).collect();
    let mut checked = 0;
    let mut bad = Vec::new();
    for entry in std::fs::read_dir(&root).unwrap() {
        let dir = entry.unwrap().path();
        let manifest = dir.join("Cargo.toml");
        if !manifest.is_file() || dir == root {
            continue;
        }
        let text = std::fs::read_to_string(&manifest).unwrap();
        if text.lines().any(|l| l.trim().replace(' ', "") == "publish=false") {
            continue;
        }
        checked += 1;
        for (t, bytes) in TEXTS.iter().zip(&want) {
            let p = dir.join(t);
            match std::fs::symlink_metadata(&p) {
                Ok(m) if m.file_type().is_symlink() => bad.push(format!("{} is a symlink — cargo needs a copy", p.display())),
                Ok(_) if read(&p) != *bytes => bad.push(format!("{} differs from the root's", p.display())),
                Ok(_) => {}
                Err(_) => bad.push(format!("{} is missing", p.display())),
            }
        }
        for gone in ["LICENSE-MIT", "LICENSE-APACHE"] {
            if dir.join(gone).exists() {
                bad.push(format!("{} — one license file per crate: LICENSE", dir.join(gone).display()));
            }
        }
    }
    assert!(checked >= 70, "only {checked} publishable crates found — the walk is wrong");
    assert!(bad.is_empty(), "{} problem(s):\n  {}", bad.len(), bad.join("\n  "));
}

#[test]
fn both_pyprojects_ship_the_texts() {
    for file in ["pkg-facet-wrapped/pyproject.toml", "py/pyproject.toml"] {
        let text = std::fs::read_to_string(repo_root().join(file)).unwrap();
        assert!(
            text.contains("license-files = [\"LICENSE\", \"NOTICE\"]"),
            "{file} must list LICENSE and NOTICE in [project] license-files"
        );
    }
}