use super::*;
const NODE_DIR_RELATIVE: &str = "e2e/typescript";
const NODE_DEPENDENCY: &str = "sample-pkg";
const NODE_STALE_SPEC: &str = "1.3.0";
const NODE_FRESH_SPEC: &str = "1.2.3";
fn node_dir(root: &Path) -> PathBuf {
root.join(NODE_DIR_RELATIVE)
}
fn write_package_json(root: &Path, specifier: &str) -> PathBuf {
let dir = node_dir(root);
std::fs::create_dir_all(&dir).expect("create node dir");
let manifest = dir.join("package.json");
std::fs::write(
&manifest,
format!(
"{{\n \"name\": \"sample-pkg-e2e-typescript\",\n \"version\": \"0.1.0\",\n \"private\": \
true,\n \"devDependencies\": {{\n \"{NODE_DEPENDENCY}\": \"{specifier}\"\n }}\n}}\n"
),
)
.expect("write package.json");
manifest
}
fn write_pnpm_lock_v9(root: &Path, locked_specifier: &str) {
std::fs::write(
node_dir(root).join("pnpm-lock.yaml"),
format!(
"lockfileVersion: '9.0'\n\nimporters:\n .:\n devDependencies:\n \
{NODE_DEPENDENCY}:\n specifier: {locked_specifier}\n version: \
{locked_specifier}\n"
),
)
.expect("write pnpm-lock.yaml");
}
fn write_pnpm_lock_v6(root: &Path, locked_specifier: &str) {
std::fs::write(
node_dir(root).join("pnpm-lock.yaml"),
format!(
"lockfileVersion: '6.0'\n\ndevDependencies:\n {NODE_DEPENDENCY}:\n specifier: \
{locked_specifier}\n version: {locked_specifier}\n"
),
)
.expect("write pnpm-lock.yaml");
}
#[test]
fn stale_node_lock_findings_reports_a_specifier_mismatch() {
let temp = tempfile::tempdir().expect("tempdir");
let root = temp.path();
write_package_json(root, NODE_STALE_SPEC);
write_pnpm_lock_v9(root, NODE_FRESH_SPEC);
let findings = stale_node_lock_findings(&node_dir(root));
assert_eq!(findings.len(), 1, "expected exactly one finding, got: {findings:?}");
let finding = &findings[0];
assert_eq!(finding.dependency, NODE_DEPENDENCY);
assert_eq!(finding.bucket, "devDependencies");
assert_eq!(finding.requirement, NODE_STALE_SPEC);
assert_eq!(finding.locked_requirement, NODE_FRESH_SPEC);
assert_eq!(finding.lock, node_dir(root).join("pnpm-lock.yaml"));
assert_eq!(finding.declared_in, node_dir(root).join("package.json"));
}
#[test]
fn stale_node_lock_findings_accepts_a_lock_that_matches() {
let temp = tempfile::tempdir().expect("tempdir");
let root = temp.path();
write_package_json(root, NODE_FRESH_SPEC);
write_pnpm_lock_v9(root, NODE_FRESH_SPEC);
let findings = stale_node_lock_findings(&node_dir(root));
assert!(
findings.is_empty(),
"a lock matching package.json must be reported clean: {findings:?}"
);
}
#[test]
fn stale_node_lock_findings_reports_a_mismatch_in_the_flat_lockfile_v6_shape() {
let temp = tempfile::tempdir().expect("tempdir");
let root = temp.path();
write_package_json(root, NODE_STALE_SPEC);
write_pnpm_lock_v6(root, NODE_FRESH_SPEC);
let findings = stale_node_lock_findings(&node_dir(root));
assert_eq!(
findings.len(),
1,
"expected the flat lockfileVersion 6 shape to be read too, got: {findings:?}"
);
assert_eq!(findings[0].locked_requirement, NODE_FRESH_SPEC);
}
#[test]
fn stale_node_lock_findings_ignores_a_dependency_absent_from_the_lock() {
let temp = tempfile::tempdir().expect("tempdir");
let root = temp.path();
write_package_json(root, NODE_STALE_SPEC);
let dir = node_dir(root);
std::fs::write(
dir.join("pnpm-lock.yaml"),
"lockfileVersion: '9.0'\n\nimporters:\n .:\n devDependencies:\n other-pkg:\n \
specifier: 2.0.0\n version: 2.0.0\n",
)
.expect("write pnpm-lock.yaml");
let findings = stale_node_lock_findings(&dir);
assert!(
findings.is_empty(),
"a package missing from the lock's bucket is not evidence of drift: {findings:?}"
);
}
#[test]
fn stale_node_lock_findings_ignores_a_workspace_specifier() {
let temp = tempfile::tempdir().expect("tempdir");
let root = temp.path();
write_package_json(root, "workspace:*");
write_pnpm_lock_v9(root, "workspace:^1.0.0");
let findings = stale_node_lock_findings(&node_dir(root));
assert!(
findings.is_empty(),
"workspace: specifiers are not directly comparable: {findings:?}"
);
}
#[test]
fn stale_node_lock_findings_ignores_a_file_specifier() {
let temp = tempfile::tempdir().expect("tempdir");
let root = temp.path();
write_package_json(root, "file:../../..");
write_pnpm_lock_v9(root, "file:../../../dist");
let findings = stale_node_lock_findings(&node_dir(root));
assert!(
findings.is_empty(),
"file: specifiers are not directly comparable: {findings:?}"
);
}
#[test]
fn check_generated_node_lock_freshness_names_the_dependency_and_the_remedy() {
let temp = tempfile::tempdir().expect("tempdir");
let root = temp.path();
let manifest = write_package_json(root, NODE_STALE_SPEC);
write_pnpm_lock_v9(root, NODE_FRESH_SPEC);
let generated: HashSet<PathBuf> = [manifest].into_iter().collect();
let error = check_generated_node_lock_freshness(&generated, root).expect("a stale lock must fail the run");
let message = format!("{error:#}");
assert!(
message.contains(NODE_DEPENDENCY),
"message must name the dependency: {message}"
);
assert!(
message.contains(NODE_STALE_SPEC),
"message must name the package.json specifier: {message}"
);
assert!(
message.contains(NODE_FRESH_SPEC),
"message must name the locked specifier: {message}"
);
assert!(
message.contains("pnpm install"),
"message must name the remedy: {message}"
);
assert!(
message.contains(&node_dir(root).join("pnpm-lock.yaml").display().to_string()),
"message must name the lock: {message}"
);
}
#[test]
fn check_generated_node_lock_freshness_passes_a_matching_lock() {
let temp = tempfile::tempdir().expect("tempdir");
let root = temp.path();
let manifest = write_package_json(root, NODE_FRESH_SPEC);
write_pnpm_lock_v9(root, NODE_FRESH_SPEC);
let generated: HashSet<PathBuf> = [manifest].into_iter().collect();
assert!(
check_generated_node_lock_freshness(&generated, root).is_none(),
"a matching lock must not fail the run"
);
}
#[test]
fn check_generated_node_lock_freshness_ignores_non_manifest_paths() {
let temp = tempfile::tempdir().expect("tempdir");
let root = temp.path();
write_package_json(root, NODE_STALE_SPEC);
write_pnpm_lock_v9(root, NODE_FRESH_SPEC);
let generated: HashSet<PathBuf> = [node_dir(root).join("src/index.ts")].into_iter().collect();
assert!(check_generated_node_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_node_registry_package(pkg_name: &str, pkg_version: &str) -> ResolvedCrateConfig {
let e2e = E2eConfig {
registry: RegistryConfig {
packages: [(
"node".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()
}
}
const WASM_DIR_RELATIVE: &str = "e2e/wasm";
const WASM_DEPENDENCY: &str = "sample-pkg-wasm";
const WASM_STALE_SPEC: &str = "1.3.0";
const WASM_FRESH_SPEC: &str = "1.2.3";
fn wasm_dir(root: &Path) -> PathBuf {
root.join(WASM_DIR_RELATIVE)
}
fn write_wasm_package_json(root: &Path, specifier: &str) -> PathBuf {
let dir = wasm_dir(root);
std::fs::create_dir_all(&dir).expect("create wasm dir");
let manifest = dir.join("package.json");
std::fs::write(
&manifest,
format!(
"{{\n \"name\": \"sample-pkg-e2e-wasm\",\n \"version\": \"0.1.0\",\n \"private\": \
true,\n \"devDependencies\": {{\n \"{WASM_DEPENDENCY}\": \"{specifier}\"\n }}\n}}\n"
),
)
.expect("write package.json");
manifest
}
fn write_wasm_pnpm_lock(root: &Path, locked_specifier: &str) {
std::fs::write(
wasm_dir(root).join("pnpm-lock.yaml"),
format!(
"lockfileVersion: '9.0'\n\nimporters:\n .:\n devDependencies:\n \
{WASM_DEPENDENCY}:\n specifier: {locked_specifier}\n version: \
{locked_specifier}\n"
),
)
.expect("write pnpm-lock.yaml");
}
fn resolved_cfg_with_node_and_wasm_registry_packages(
node_name: &str,
node_version: &str,
wasm_name: &str,
wasm_version: &str,
) -> ResolvedCrateConfig {
let e2e = E2eConfig {
registry: RegistryConfig {
packages: [
(
"node".to_string(),
PackageRef {
name: Some(node_name.to_string()),
version: Some(node_version.to_string()),
..PackageRef::default()
},
),
(
"wasm".to_string(),
PackageRef {
name: Some(wasm_name.to_string()),
version: Some(wasm_version.to_string()),
..PackageRef::default()
},
),
]
.into_iter()
.collect(),
..RegistryConfig::default()
},
..E2eConfig::default()
};
ResolvedCrateConfig {
e2e: Some(e2e),
..ResolvedCrateConfig::default()
}
}
#[test]
fn tolerating_variant_downgrades_the_wasm_test_apps_own_pending_self_dependency() {
let temp = tempfile::tempdir().expect("tempdir");
let root = temp.path();
let manifest = write_wasm_package_json(root, WASM_STALE_SPEC);
write_wasm_pnpm_lock(root, WASM_FRESH_SPEC);
let generated: HashSet<PathBuf> = [manifest].into_iter().collect();
let resolved_cfg = resolved_cfg_with_node_and_wasm_registry_packages(
NODE_DEPENDENCY,
NODE_STALE_SPEC,
WASM_DEPENDENCY,
WASM_STALE_SPEC,
);
let result =
check_generated_node_lock_freshness_tolerating_pending_publish(&generated, root, Some(&resolved_cfg));
assert!(
result.is_none(),
"the wasm test_app's own pending self-dependency must be tolerated exactly like node's: {result:?}"
);
}
#[test]
fn tolerating_variant_downgrades_both_the_node_and_wasm_test_apps_pending_self_dependencies_together() {
let temp = tempfile::tempdir().expect("tempdir");
let root = temp.path();
let node_manifest = write_package_json(root, NODE_STALE_SPEC);
write_pnpm_lock_v9(root, NODE_FRESH_SPEC);
let wasm_manifest = write_wasm_package_json(root, WASM_STALE_SPEC);
write_wasm_pnpm_lock(root, WASM_FRESH_SPEC);
let generated: HashSet<PathBuf> = [node_manifest, wasm_manifest].into_iter().collect();
let resolved_cfg = resolved_cfg_with_node_and_wasm_registry_packages(
NODE_DEPENDENCY,
NODE_STALE_SPEC,
WASM_DEPENDENCY,
WASM_STALE_SPEC,
);
let result =
check_generated_node_lock_freshness_tolerating_pending_publish(&generated, root, Some(&resolved_cfg));
assert!(
result.is_none(),
"both test_apps' own pending self-dependencies must be tolerated in the same run: {result:?}"
);
}
#[test]
fn tolerating_variant_still_fails_on_the_wasm_test_apps_drift_not_explained_by_pending_publish() {
let temp = tempfile::tempdir().expect("tempdir");
let root = temp.path();
let manifest = write_wasm_package_json(root, WASM_STALE_SPEC);
write_wasm_pnpm_lock(root, WASM_FRESH_SPEC);
let generated: HashSet<PathBuf> = [manifest].into_iter().collect();
let resolved_cfg = resolved_cfg_with_node_and_wasm_registry_packages(
NODE_DEPENDENCY,
NODE_STALE_SPEC,
"unrelated-wasm-package",
"9.9.9",
);
assert!(
check_generated_node_lock_freshness_tolerating_pending_publish(&generated, root, Some(&resolved_cfg),)
.is_some(),
"a third-party lock drift in the wasm test_app unrelated to its own registry \
self-dependency must still fail the run"
);
}
const SIBLING_DEPENDENCY: &str = "sibling-pkg";
const SIBLING_STALE_SPEC: &str = "^1.0.0";
const SIBLING_LOCKED_SPEC: &str = "^2.0.0";
fn write_package_json_with_sibling(root: &Path, self_specifier: &str, sibling_specifier: &str) -> PathBuf {
let dir = node_dir(root);
std::fs::create_dir_all(&dir).expect("create node dir");
let manifest = dir.join("package.json");
std::fs::write(
&manifest,
format!(
"{{\n \"name\": \"sample-pkg-e2e-typescript\",\n \"version\": \"0.1.0\",\n \"private\": \
true,\n \"devDependencies\": {{\n \"{NODE_DEPENDENCY}\": \"{self_specifier}\",\n \
\"{SIBLING_DEPENDENCY}\": \"{sibling_specifier}\"\n }}\n}}\n"
),
)
.expect("write package.json");
manifest
}
fn write_pnpm_lock_v9_with_sibling(root: &Path, self_locked: &str, sibling_locked: &str) {
std::fs::write(
node_dir(root).join("pnpm-lock.yaml"),
format!(
"lockfileVersion: '9.0'\n\nimporters:\n .:\n devDependencies:\n \
{NODE_DEPENDENCY}:\n specifier: {self_locked}\n version: {self_locked}\n \
{SIBLING_DEPENDENCY}:\n specifier: {sibling_locked}\n version: \
{sibling_locked}\n"
),
)
.expect("write pnpm-lock.yaml");
}
#[test]
fn tolerating_variant_downgrades_sibling_findings_in_the_same_pending_lock() {
let temp = tempfile::tempdir().expect("tempdir");
let root = temp.path();
let manifest = write_package_json_with_sibling(root, NODE_STALE_SPEC, SIBLING_STALE_SPEC);
write_pnpm_lock_v9_with_sibling(root, NODE_FRESH_SPEC, SIBLING_LOCKED_SPEC);
let generated: HashSet<PathBuf> = [manifest].into_iter().collect();
let resolved_cfg = resolved_cfg_with_node_registry_package(NODE_DEPENDENCY, NODE_STALE_SPEC);
let result =
check_generated_node_lock_freshness_tolerating_pending_publish(&generated, root, Some(&resolved_cfg));
assert!(
result.is_none(),
"a sibling drift sharing a lock with a pending self-dependency must also be tolerated, \
since pnpm cannot resolve any of it until the self-dependency publishes: {result:?}"
);
}
#[test]
fn tolerating_variant_still_fails_on_a_different_locks_unrelated_drift() {
let temp = tempfile::tempdir().expect("tempdir");
let root = temp.path();
let manifest_a = write_package_json_with_sibling(root, NODE_STALE_SPEC, SIBLING_STALE_SPEC);
write_pnpm_lock_v9_with_sibling(root, NODE_FRESH_SPEC, SIBLING_LOCKED_SPEC);
let other_dir = root.join("e2e/typescript-other");
std::fs::create_dir_all(&other_dir).expect("create other node dir");
let manifest_b = other_dir.join("package.json");
std::fs::write(
&manifest_b,
format!(
"{{\n \"name\": \"other-pkg-e2e-typescript\",\n \"version\": \"0.1.0\",\n \"private\": \
true,\n \"devDependencies\": {{\n \"{SIBLING_DEPENDENCY}\": \"{SIBLING_STALE_SPEC}\"\n \
}}\n}}\n"
),
)
.expect("write package.json");
std::fs::write(
other_dir.join("pnpm-lock.yaml"),
format!(
"lockfileVersion: '9.0'\n\nimporters:\n .:\n devDependencies:\n \
{SIBLING_DEPENDENCY}:\n specifier: {SIBLING_LOCKED_SPEC}\n version: \
{SIBLING_LOCKED_SPEC}\n"
),
)
.expect("write pnpm-lock.yaml");
let generated: HashSet<PathBuf> = [manifest_a, manifest_b].into_iter().collect();
let resolved_cfg = resolved_cfg_with_node_registry_package(NODE_DEPENDENCY, NODE_STALE_SPEC);
let error =
check_generated_node_lock_freshness_tolerating_pending_publish(&generated, root, Some(&resolved_cfg))
.expect(
"lock B's unrelated drift, sharing no lock with the pending self-dependency, must \
still fail the run",
);
let message = format!("{error:#}");
assert!(
message.contains(&other_dir.join("pnpm-lock.yaml").display().to_string()),
"message must name lock B: {message}"
);
assert!(
!message.contains(&node_dir(root).join("pnpm-lock.yaml").display().to_string()),
"lock A must be fully tolerated and absent from the surviving error: {message}"
);
}
#[test]
fn plain_check_still_fails_on_a_pending_publish_disagreement() {
let temp = tempfile::tempdir().expect("tempdir");
let root = temp.path();
let manifest = write_package_json(root, NODE_STALE_SPEC);
write_pnpm_lock_v9(root, NODE_FRESH_SPEC);
let generated: HashSet<PathBuf> = [manifest].into_iter().collect();
assert!(
check_generated_node_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_package_json(root, NODE_STALE_SPEC);
write_pnpm_lock_v9(root, NODE_FRESH_SPEC);
let generated: HashSet<PathBuf> = [manifest].into_iter().collect();
let resolved_cfg = resolved_cfg_with_node_registry_package(NODE_DEPENDENCY, NODE_STALE_SPEC);
let result =
check_generated_node_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_package_json(root, NODE_STALE_SPEC);
write_pnpm_lock_v9(root, NODE_FRESH_SPEC);
let generated: HashSet<PathBuf> = [manifest].into_iter().collect();
assert!(
check_generated_node_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_package_json(root, NODE_STALE_SPEC);
write_pnpm_lock_v9(root, NODE_FRESH_SPEC);
let generated: HashSet<PathBuf> = [manifest].into_iter().collect();
let resolved_cfg = resolved_cfg_with_node_registry_package("unrelated-package", "9.9.9");
assert!(
check_generated_node_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"
);
}
}
#[path = "node_registered_manifest_tests.rs"]
mod registered_unmarkable_manifest_gap;