use super::*;
const PUBSPEC_DIR_RELATIVE: &str = "e2e/dart";
const DART_DEPENDENCY: &str = "demo_pkg";
const DART_STALE_REQUIREMENT: &str = "1.3.0";
const DART_FRESH_PIN: &str = "1.2.3";
fn pubspec_dir(root: &Path) -> PathBuf {
root.join(PUBSPEC_DIR_RELATIVE)
}
fn write_pubspec(root: &Path, requirement: &str) -> PathBuf {
let dir = pubspec_dir(root);
std::fs::create_dir_all(&dir).expect("create pubspec dir");
let manifest = dir.join("pubspec.yaml");
std::fs::write(
&manifest,
format!(
"name: e2e_dart\nversion: 0.1.0\npublish_to: none\n\nenvironment:\n sdk: '>=3.0.0 <4.0.0'\n\n\
dependencies:\n {DART_DEPENDENCY}: {requirement}\n"
),
)
.expect("write pubspec.yaml");
manifest
}
fn write_pubspec_lock(root: &Path, locked_version: &str) {
std::fs::write(
pubspec_dir(root).join("pubspec.lock"),
format!(
"packages:\n {DART_DEPENDENCY}:\n dependency: \"direct main\"\n description:\n \
name: {DART_DEPENDENCY}\n url: \"https://pub.dev\"\n source: hosted\n version: \
\"{locked_version}\"\nsdks:\n dart: \">=3.0.0 <4.0.0\"\n"
),
)
.expect("write pubspec.lock");
}
#[test]
fn stale_dart_lock_findings_reports_a_requirement_no_locked_version_satisfies() {
let temp = tempfile::tempdir().expect("tempdir");
let root = temp.path();
write_pubspec(root, DART_STALE_REQUIREMENT);
write_pubspec_lock(root, DART_FRESH_PIN);
let findings = stale_dart_lock_findings(&pubspec_dir(root));
assert_eq!(findings.len(), 1, "expected exactly one finding, got: {findings:?}");
let finding = &findings[0];
assert_eq!(finding.dependency, DART_DEPENDENCY);
assert_eq!(finding.bucket, "dependencies");
assert_eq!(finding.requirement, DART_STALE_REQUIREMENT);
assert_eq!(finding.locked_version, DART_FRESH_PIN);
assert_eq!(finding.lock, pubspec_dir(root).join("pubspec.lock"));
assert_eq!(finding.declared_in, pubspec_dir(root).join("pubspec.yaml"));
}
#[test]
fn stale_dart_lock_findings_accepts_a_lock_that_matches() {
let temp = tempfile::tempdir().expect("tempdir");
let root = temp.path();
write_pubspec(root, DART_FRESH_PIN);
write_pubspec_lock(root, DART_FRESH_PIN);
let findings = stale_dart_lock_findings(&pubspec_dir(root));
assert!(
findings.is_empty(),
"a lock matching pubspec.yaml must be reported clean: {findings:?}"
);
}
#[test]
fn stale_dart_lock_findings_ignores_a_dependency_absent_from_the_lock() {
let temp = tempfile::tempdir().expect("tempdir");
let root = temp.path();
write_pubspec(root, DART_STALE_REQUIREMENT);
std::fs::write(
pubspec_dir(root).join("pubspec.lock"),
"packages:\n other_pkg:\n dependency: \"direct main\"\n source: hosted\n version: \"2.0.0\"\n\
sdks:\n dart: \">=3.0.0 <4.0.0\"\n",
)
.expect("write pubspec.lock");
let findings = stale_dart_lock_findings(&pubspec_dir(root));
assert!(
findings.is_empty(),
"a package missing from the lock is not evidence of staleness: {findings:?}"
);
}
#[test]
fn stale_dart_lock_findings_ignores_a_path_dependency() {
let temp = tempfile::tempdir().expect("tempdir");
let root = temp.path();
let dir = pubspec_dir(root);
std::fs::create_dir_all(&dir).expect("create pubspec dir");
std::fs::write(
dir.join("pubspec.yaml"),
format!(
"name: e2e_dart\nversion: 0.1.0\n\ndependencies:\n {DART_DEPENDENCY}:\n path: \
../../packages/dart\n"
),
)
.expect("write pubspec.yaml");
write_pubspec_lock(root, DART_FRESH_PIN);
let findings = stale_dart_lock_findings(&dir);
assert!(
findings.is_empty(),
"a path dependency carries no pub version pin to compare: {findings:?}"
);
}
#[test]
fn stale_dart_lock_findings_rejects_a_caret_violation_on_a_zero_major() {
let temp = tempfile::tempdir().expect("tempdir");
let root = temp.path();
write_pubspec(root, "^0.2.3");
write_pubspec_lock(root, "0.3.0");
let findings = stale_dart_lock_findings(&pubspec_dir(root));
assert_eq!(
findings.len(),
1,
"^0.2.3 must reject 0.3.0, which crossed the zero-major narrowed boundary: {findings:?}"
);
}
#[test]
fn stale_dart_lock_findings_accepts_a_caret_patch_bump_on_a_zero_major() {
let temp = tempfile::tempdir().expect("tempdir");
let root = temp.path();
write_pubspec(root, "^0.2.3");
write_pubspec_lock(root, "0.2.9");
let findings = stale_dart_lock_findings(&pubspec_dir(root));
assert!(
findings.is_empty(),
"^0.2.3 must accept 0.2.9, a patch bump within the narrowed zero-major range: {findings:?}"
);
}
#[test]
fn stale_dart_lock_findings_ignores_an_unsupported_compound_range() {
let temp = tempfile::tempdir().expect("tempdir");
let root = temp.path();
write_pubspec(root, ">=1.0.0 <2.0.0");
write_pubspec_lock(root, "5.0.0");
let findings = stale_dart_lock_findings(&pubspec_dir(root));
assert!(
findings.is_empty(),
"a compound range this reader cannot confidently judge must not be reported: {findings:?}"
);
}
#[test]
fn stale_dart_lock_findings_skips_a_directory_with_no_committed_lock() {
let temp = tempfile::tempdir().expect("tempdir");
let root = temp.path();
write_pubspec(root, DART_STALE_REQUIREMENT);
let findings = stale_dart_lock_findings(&pubspec_dir(root));
assert!(
findings.is_empty(),
"a directory with no lock has nothing to check: {findings:?}"
);
}
#[test]
fn check_generated_dart_lock_freshness_names_the_dependency_and_the_remedy() {
let temp = tempfile::tempdir().expect("tempdir");
let root = temp.path();
let manifest = write_pubspec(root, DART_STALE_REQUIREMENT);
write_pubspec_lock(root, DART_FRESH_PIN);
let generated: HashSet<PathBuf> = [manifest].into_iter().collect();
let error = check_generated_dart_lock_freshness(&generated, root).expect("a stale lock must fail the run");
let message = format!("{error:#}");
assert!(
message.contains(DART_DEPENDENCY),
"message must name the dependency: {message}"
);
assert!(
message.contains(DART_STALE_REQUIREMENT),
"message must name the pubspec.yaml requirement: {message}"
);
assert!(
message.contains(DART_FRESH_PIN),
"message must name the locked version: {message}"
);
assert!(message.contains("dart pub"), "message must name the remedy: {message}");
assert!(
message.contains(&pubspec_dir(root).join("pubspec.lock").display().to_string()),
"message must name the lock: {message}"
);
}
#[test]
fn check_generated_dart_lock_freshness_passes_a_resolvable_lock() {
let temp = tempfile::tempdir().expect("tempdir");
let root = temp.path();
let manifest = write_pubspec(root, DART_FRESH_PIN);
write_pubspec_lock(root, DART_FRESH_PIN);
let generated: HashSet<PathBuf> = [manifest].into_iter().collect();
assert!(
check_generated_dart_lock_freshness(&generated, root).is_none(),
"a resolvable lock must not fail the run"
);
}
#[test]
fn check_generated_dart_lock_freshness_ignores_non_manifest_paths() {
let temp = tempfile::tempdir().expect("tempdir");
let root = temp.path();
write_pubspec(root, DART_STALE_REQUIREMENT);
write_pubspec_lock(root, DART_FRESH_PIN);
let generated: HashSet<PathBuf> = [pubspec_dir(root).join("test/basic_test.dart")].into_iter().collect();
assert!(check_generated_dart_lock_freshness(&generated, root).is_none());
}
mod pending_publish {
use super::*;
use crate::core::config::ResolvedCrateConfig;
use crate::core::config::e2e::{E2eConfig, PackageRef, RegistryConfig};
fn resolved_cfg_with_dart_registry_package(pkg_name: &str, pkg_version: &str) -> ResolvedCrateConfig {
let e2e = E2eConfig {
registry: RegistryConfig {
packages: [(
"dart".to_string(),
PackageRef {
name: Some(pkg_name.to_string()),
version: Some(pkg_version.to_string()),
..PackageRef::default()
},
)]
.into_iter()
.collect(),
..RegistryConfig::default()
},
..E2eConfig::default()
};
ResolvedCrateConfig {
e2e: Some(e2e),
..ResolvedCrateConfig::default()
}
}
#[test]
fn plain_check_still_fails_on_a_pending_publish_disagreement() {
let temp = tempfile::tempdir().expect("tempdir");
let root = temp.path();
let manifest = write_pubspec(root, DART_STALE_REQUIREMENT);
write_pubspec_lock(root, DART_FRESH_PIN);
let generated: HashSet<PathBuf> = [manifest].into_iter().collect();
assert!(
check_generated_dart_lock_freshness(&generated, root).is_some(),
"control: the plain check has no pending-publish exemption and must still fail here"
);
}
#[test]
fn tolerating_variant_warns_instead_of_failing_when_the_requirement_matches_the_configured_registry_package() {
let temp = tempfile::tempdir().expect("tempdir");
let root = temp.path();
let manifest = write_pubspec(root, DART_STALE_REQUIREMENT);
write_pubspec_lock(root, DART_FRESH_PIN);
let generated: HashSet<PathBuf> = [manifest].into_iter().collect();
let resolved_cfg = resolved_cfg_with_dart_registry_package(DART_DEPENDENCY, DART_STALE_REQUIREMENT);
let result =
check_generated_dart_lock_freshness_tolerating_pending_publish(&generated, root, Some(&resolved_cfg));
assert!(
result.is_none(),
"a disagreement fully explained by this crate's own configured registry \
self-dependency must not fail the run: {result:?}"
);
}
#[test]
fn tolerating_variant_without_resolved_cfg_behaves_like_the_plain_check() {
let temp = tempfile::tempdir().expect("tempdir");
let root = temp.path();
let manifest = write_pubspec(root, DART_STALE_REQUIREMENT);
write_pubspec_lock(root, DART_FRESH_PIN);
let generated: HashSet<PathBuf> = [manifest].into_iter().collect();
assert!(
check_generated_dart_lock_freshness_tolerating_pending_publish(&generated, root, None).is_some(),
"no resolved config means no exemption is possible; this must still fail"
);
}
#[test]
fn tolerating_variant_still_fails_on_a_disagreement_not_explained_by_pending_publish() {
let temp = tempfile::tempdir().expect("tempdir");
let root = temp.path();
let manifest = write_pubspec(root, DART_STALE_REQUIREMENT);
write_pubspec_lock(root, DART_FRESH_PIN);
let generated: HashSet<PathBuf> = [manifest].into_iter().collect();
let resolved_cfg = resolved_cfg_with_dart_registry_package("unrelated_pkg", "9.9.9");
assert!(
check_generated_dart_lock_freshness_tolerating_pending_publish(&generated, root, Some(&resolved_cfg))
.is_some(),
"a third-party lock drift unrelated to this crate's own registry self-dependency \
must still fail the run"
);
}
}