use std::process::Command;
fn packaged_files(package: &str) -> Vec<String> {
let cargo = std::env::var("CARGO").unwrap_or_else(|_| "cargo".into());
let output = Command::new(cargo)
.args([
"package",
"--list",
"--quiet",
"--allow-dirty",
"-p",
package,
])
.current_dir(env!("CARGO_MANIFEST_DIR"))
.output()
.expect("run `cargo package --list`");
assert!(
output.status.success(),
"`cargo package --list` failed:\n{}",
String::from_utf8_lossy(&output.stderr)
);
String::from_utf8(output.stdout)
.expect("utf8 file list")
.lines()
.map(str::trim)
.filter(|l| !l.is_empty())
.map(str::to_owned)
.collect()
}
#[test]
fn heavy_doc_assets_are_excluded_from_the_package() {
let files = packaged_files("tuika");
let bundled_heavy_assets: Vec<&String> = files
.iter()
.filter(|f| {
(f.ends_with(".gif") || f.ends_with(".png"))
&& (f.starts_with("docs/demos/")
|| f.starts_with("docs/showcases/")
|| f.starts_with("examples/codex/")
|| f.starts_with("docs/styling/")
|| f.starts_with("docs/themes/"))
})
.collect();
assert!(
bundled_heavy_assets.is_empty(),
"these assets must not ship in the crate (see Cargo.toml `exclude`): {bundled_heavy_assets:?}"
);
}
#[test]
fn repository_machinery_is_excluded_from_the_package() {
let files = packaged_files("tuika");
const INTERNAL_PREFIXES: [&str; 5] =
[".agents/", ".claude/", ".github/", "knowledge/", "scripts/"];
const INTERNAL_FILES: [&str; 6] = [
"AGENTS.md",
"CLAUDE.md",
"CODE_OF_CONDUCT.md",
"CONTRIBUTING.md",
"SECURITY.md",
"rust-toolchain.toml",
];
let leaked: Vec<&String> = files
.iter()
.filter(|f| {
INTERNAL_PREFIXES.iter().any(|p| f.starts_with(p))
|| INTERNAL_FILES.iter().any(|n| f == n)
})
.collect();
assert!(
leaked.is_empty(),
"repository-only files must not ship in the crate (see Cargo.toml `exclude`): {leaked:?}"
);
let nested: Vec<&String> = files.iter().filter(|f| f.starts_with("crates/")).collect();
assert!(
nested.is_empty(),
"nested workspace packages must not ship inside tuika's crate: {nested:?}"
);
}
#[test]
fn crates_io_readme_assets_and_source_are_kept() {
let files = packaged_files("tuika");
let has = |p: &str| files.iter().any(|f| f == p);
assert!(has("docs/hero.gif"), "README hero image must ship");
assert!(
has("docs/demos/image.svg"),
"README image-protocol asset must ship"
);
assert!(
has("docs/demos/split-footer.svg"),
"README split-footer recording must ship"
);
assert!(has("src/lib.rs"), "library source must ship");
assert!(has("Cargo.toml"), "manifest must ship");
assert!(has("README.md"), "README must ship");
}
#[test]
fn codeformatters_ships_source_but_not_its_demo_recording() {
let files = packaged_files("tuika-codeformatters");
let gifs: Vec<&String> = files.iter().filter(|f| f.ends_with(".gif")).collect();
assert!(
gifs.is_empty(),
"demo recordings must not ship in tuika-codeformatters (see its Cargo.toml `exclude`): {gifs:?}"
);
let has = |p: &str| files.iter().any(|f| f == p);
assert!(has("src/lib.rs"), "library source must ship");
assert!(has("Cargo.toml"), "manifest must ship");
assert!(has("README.md"), "README must ship");
}
#[test]
fn mermaid_keeps_the_recording_its_readme_embeds() {
let files = packaged_files("tuika-mermaid");
let has = |p: &str| files.iter().any(|f| f == p);
assert!(
has("examples/mermaid_markdown/mermaid.gif"),
"the recording the crates.io README embeds by relative path must ship"
);
assert!(has("src/lib.rs"), "library source must ship");
assert!(has("Cargo.toml"), "manifest must ship");
assert!(has("README.md"), "README must ship");
}