crates/appliance-grow/tests/one_monetize_embed_line.rs
//! RA2-13: appliance-grow takes ONE monetize-embed line, 0.3, in its dependency
//! AND its dev-dependency, and edda's lock holds exactly that one.
//!
//! Before: the dependency asked `>=0.1.3, <0.4`, a range across three semver-breaking
//! 0.x lines (crates.io's 0.2.0 is the old API, without the fleet forms), while the
//! dev-dependency pinned 0.1.3. So edda's own tests never built the 0.3 line the
//! published crate is meant for, against A3's "one line" rule (monetize
//! `xtask/tests/the_published_line.rs`).
//!
//! The reader is a small line scanner, as in monetize's guard: no TOML crate is added
//! for one test.
use std::path::{Path, PathBuf};
/// The ONE monetize-embed line (A3: monetize's crates.io line is 0.3.0).
const LINE: &str = "0.3";
fn manifest_dir() -> PathBuf {
PathBuf::from(env!("CARGO_MANIFEST_DIR"))
}
fn read(p: &Path) -> String {
std::fs::read_to_string(p).unwrap_or_else(|e| panic!("read {}: {e}", p.display()))
}
/// The `monetize-embed = …` line under the `[section]` header, if any.
fn entry<'a>(manifest: &'a str, section: &str) -> Option<&'a str> {
let mut inside = false;
for line in manifest.lines() {
let t = line.trim();
if t.starts_with('[') {
inside = t == format!("[{section}]");
continue;
}
if inside && t.starts_with("monetize-embed") && t["monetize-embed".len()..].trim_start().starts_with('=') {
return Some(t);
}
}
None
}
/// The requirement an entry asks: the bare string, or `version = "…"` in a table.
fn requirement(entry: &str) -> String {
let rhs = entry.split_once('=').map(|(_, r)| r.trim()).unwrap_or_default();
let quoted = |s: &str| s.split('"').nth(1).map(str::to_owned);
if rhs.starts_with('"') {
return quoted(rhs).unwrap_or_default();
}
let at = rhs.find("version").unwrap_or_else(|| panic!("no version in `{entry}`"));
quoted(&rhs[at..]).unwrap_or_default()
}
/// Every `version` of a `[[package]] name = "monetize-embed"` in a lock.
fn locked_versions(lock: &str) -> Vec<String> {
let mut out = Vec::new();
let mut lines = lock.lines().peekable();
while let Some(l) = lines.next() {
if l.trim() == "name = \"monetize-embed\"" {
if let Some(v) = lines.peek().and_then(|v| v.trim().strip_prefix("version = \"")) {
out.push(v.trim_end_matches('"').to_owned());
}
}
}
out
}
#[test]
fn the_dependency_and_the_dev_dependency_ask_the_same_one_line() {
let m = read(&manifest_dir().join("Cargo.toml"));
let dep = entry(&m, "dependencies").expect("appliance-grow names monetize-embed under [dependencies]");
let dev = entry(&m, "dev-dependencies").expect("appliance-grow names monetize-embed under [dev-dependencies]");
let (dep, dev) = (requirement(dep), requirement(dev));
assert_eq!(dep, LINE, "the dependency must ask exactly the {LINE} line, not a range across 0.x lines");
assert_eq!(dev, dep, "the dev-dependency must ask the same line the published crate asks");
}
#[test]
fn eddas_lock_holds_one_monetize_embed_on_that_line() {
let lock = read(&manifest_dir().join("../../Cargo.lock"));
let got = locked_versions(&lock);
assert_eq!(got.len(), 1, "edda's Cargo.lock must hold ONE monetize-embed, holds {got:?}");
assert!(
got[0].starts_with(&format!("{LINE}.")),
"edda's Cargo.lock has monetize-embed {}, not on the {LINE} line",
got[0]
);
}
#[test]
fn the_scanner_reads_both_entry_forms_and_the_lock() {
let m = "[dependencies]\nmonetize-embed = { version = \">=0.1.3, <0.4\", optional = true }\n\
[dev-dependencies]\nmonetize-embed = \"0.1.3\"\nmonetize-embedx = \"9\"\n";
assert_eq!(requirement(entry(m, "dependencies").unwrap()), ">=0.1.3, <0.4");
assert_eq!(requirement(entry(m, "dev-dependencies").unwrap()), "0.1.3");
assert_eq!(entry(m, "build-dependencies"), None);
let lock = "[[package]]\nname = \"monetize-embed\"\nversion = \"0.1.3\"\n\n[[package]]\nname = \"monetize-embed\"\nversion = \"0.3.0\"\n";
assert_eq!(locked_versions(lock), ["0.1.3", "0.3.0"]);
}