use std::path::{Path, PathBuf};
use std::process::Command;
fn repo_root() -> PathBuf {
Path::new(env!("CARGO_MANIFEST_DIR"))
.ancestors()
.nth(2)
.expect("repo root is two levels above crates/umbral-cli")
.to_path_buf()
}
#[test]
#[ignore = "type-checks an entire generated app (minutes, GBs). CI runs it; locally: --ignored"]
fn the_scaffolded_project_compiles_with_no_errors_and_no_warnings() {
let repo = repo_root();
assert!(
repo.join("crates/umbral-core/Cargo.toml").exists(),
"expected a source checkout at {}",
repo.display()
);
let tmp = tempfile::tempdir().expect("tempdir");
let report = umbral_cli::scaffold::scaffold_project("checkme", tmp.path(), Some(&repo))
.expect("scaffold_project");
let target_dir = repo.join("target/scaffold-check");
let out = Command::new(env!("CARGO"))
.current_dir(&report.root)
.args(["check", "--all-targets", "--message-format=short"])
.env("CARGO_TARGET_DIR", &target_dir)
.env("RUSTFLAGS", "-D warnings")
.output()
.expect("cargo check should be runnable");
let stderr = String::from_utf8_lossy(&out.stderr);
if !out.status.success() {
panic!(
"`umbral startproject` emitted a project that does not compile.\n\
This is the first thing a new user runs. Fix the SCAFFOLD, not the test.\n\n\
cargo check said:\n{stderr}"
);
}
let is_build_script_notice = |l: &str| {
l.starts_with("warning: ")
&& l[9..]
.split_once(": ")
.is_some_and(|(pkg, _)| pkg.contains('@'))
};
let warnings: Vec<&str> = stderr
.lines()
.filter(|l| l.starts_with("warning:") && !is_build_script_notice(l))
.collect();
assert!(
warnings.is_empty(),
"the scaffolded project compiles but is not clean:\n{}",
warnings.join("\n")
);
}
#[test]
#[ignore = "type-checks an entire generated app (minutes, GBs). CI runs it; locally: --ignored"]
fn the_generated_commands_and_rest_classes_compile_with_no_warnings() {
use umbral::codegen::Target;
use umbral_cli::scaffold::scaffold_command;
let repo = repo_root();
let tmp = tempfile::tempdir().expect("tempdir");
let report =
umbral_cli::scaffold::scaffold_project("genme", tmp.path(), Some(&repo)).expect("project");
let root = &report.root;
let target_dir = repo.join("target/scaffold-check");
umbral_cli::scaffold::scaffold_app("blog", root, Some(&repo)).expect("startapp");
scaffold_command("backfill_slugs", &Target::Root, root).expect("startcommand --in root");
scaffold_command("reindex", &Target::Plugin("blog".into()), root)
.expect("startcommand --in blog");
for (cmd, name) in [
("startpermission", "IsOwner"),
("startauthentication", "ApiKeyAuth"),
("startpagination", "CursorPagination"),
("startthrottle", "BurstThrottle"),
] {
let out = Command::new(env!("CARGO"))
.current_dir(root)
.args(["run", "--quiet", "--", cmd, name, "--in", "root"])
.env("CARGO_TARGET_DIR", &target_dir)
.output()
.unwrap_or_else(|e| panic!("running `{cmd}`: {e}"));
assert!(
out.status.success(),
"`cargo run -- {cmd} {name}` failed:\n{}",
String::from_utf8_lossy(&out.stderr)
);
}
let main_rs = root.join("src/main.rs");
let src = std::fs::read_to_string(&main_rs).expect("read main.rs");
let src = src.replace(
"use umbral_rest::{RestPlugin, ResourceConfig};",
"use umbral_rest::{RestPlugin, ResourceConfig};\n\
use crate::authentication::ApiKeyAuth;\n\
use crate::pagination::CursorPagination;\n\
use crate::permissions::IsOwner;\n\
use crate::throttles::BurstThrottle;",
);
let src = src.replace(
" RestPlugin::default()\n .resource(ResourceConfig::new(\"post\")),",
" RestPlugin::default()\n .resource(ResourceConfig::new(\"post\"))\n\
\x20 .default_permission(IsOwner)\n\
\x20 .authenticate(ApiKeyAuth)\n\
\x20 .paginate(CursorPagination)\n\
\x20 .default_throttle(BurstThrottle::new()),",
);
assert!(
src.contains(".default_permission(IsOwner)"),
"the startproject main.rs no longer has the RestPlugin shape this test wires into — \
update the fixture, and check the generators' printed instructions still match too"
);
std::fs::write(&main_rs, src).expect("write main.rs");
let out = Command::new(env!("CARGO"))
.current_dir(root)
.args(["check", "--all-targets", "--message-format=short"])
.env("CARGO_TARGET_DIR", &target_dir)
.env("RUSTFLAGS", "-D warnings")
.output()
.expect("cargo check should be runnable");
let stderr = String::from_utf8_lossy(&out.stderr);
assert!(
out.status.success(),
"a generator emitted code that does not compile. Fix the TEMPLATE, not the test.\n\n\
cargo check said:\n{stderr}"
);
}