use {crate::specifier::Specifier, std::rc::Rc};
#[test]
fn exact_version_satisfies_multiple_ranges() {
let cases: Vec<(&str, Vec<&str>, bool)> = vec![
("1.2.3", vec!["^1.0.0"], true),
("1.2.3", vec!["^2.0.0"], false),
("1.2.3", vec!["^1.0.0", "~1.2.0"], true),
("1.2.3", vec![">=1.0.0", "<2.0.0"], true),
("1.2.3", vec!["^1.0.0", ">=1.2.0", "<=1.3.0"], true),
("1.2.3", vec!["^1.0.0", "^2.0.0"], false),
("1.2.3", vec![">=1.0.0", "<1.2.0"], false),
("1.2.3", vec!["^1.0.0", "~1.3.0"], false),
("1.2.3", vec![], true),
("1.2.3", vec![">=1.0.0", ">=1.2.0", ">=1.2.3", "<2.0.0", "<=1.2.3"], true),
];
for (version, ranges_str, expected) in cases {
let spec = Specifier::new(version);
let others: Vec<Rc<Specifier>> = ranges_str.iter().map(|r| Specifier::new(r)).collect();
assert_eq!(
spec.satisfies_all(&others),
expected,
"'{version}'.satisfies_all({ranges_str:?}) should be {expected}"
);
}
}
#[test]
fn range_specifiers_satisfy_multiple_ranges() {
let cases: Vec<(&str, Vec<&str>, bool)> = vec![
("^1.2.3", vec!["^1.0.0"], true),
("^1.2.3", vec!["^1.0.0", ">=1.2.0"], true),
("^1.2.3", vec!["^1.0.0", "^2.0.0"], false),
("~1.2.3", vec!["~1.2.0"], true),
("~1.2.3", vec!["^1.0.0", "~1.2.0"], true),
("~1.2.3", vec!["~1.2.0", "~1.3.0"], false),
("1", vec!["^1.0.0", ">=1.0.0"], true),
("1", vec!["^1.0.0", "^2.0.0"], false),
("1.2", vec!["^1.0.0", "~1.2.0"], true),
("1.2", vec!["^1.0.0", "^2.0.0"], false),
];
for (specifier, ranges_str, expected) in cases {
let spec = Specifier::new(specifier);
let others: Vec<Rc<Specifier>> = ranges_str.iter().map(|r| Specifier::new(r)).collect();
assert_eq!(
spec.satisfies_all(&others),
expected,
"'{specifier}'.satisfies_all({ranges_str:?}) should be {expected}"
);
}
}
#[test]
#[ignore] fn prerelease_versions_with_multiple_ranges() {
let cases: Vec<(&str, Vec<&str>, bool)> = vec![
("1.2.3-alpha.1", vec!["^1.0.0", ">=1.2.0"], true),
("1.2.3-alpha.1", vec![">=1.2.3-alpha.0", "<1.2.3-beta.0"], true),
("1.2.3-alpha.1", vec!["^1.2.3-alpha.0", ">=1.2.3-alpha.1"], true),
("1.2.3-alpha.1", vec!["^1.0.0", ">=1.2.3"], false),
("1.2.3-alpha.1", vec![">=1.2.3-alpha.0", ">=1.2.3-beta.0"], false),
("1.2.3", vec![">=1.2.3-alpha.0", "<=2.0.0"], true),
("1.2.3", vec!["^1.2.3-alpha.0", ">=1.2.3"], false),
];
for (version, ranges_str, expected) in cases {
let spec = Specifier::new(version);
let others: Vec<Rc<Specifier>> = ranges_str.iter().map(|r| Specifier::new(r)).collect();
assert_eq!(
spec.satisfies_all(&others),
expected,
"'{version}'.satisfies_all({ranges_str:?}) should be {expected}"
);
}
}
#[test]
fn non_version_specifiers_return_false() {
let specifiers = vec![
"",
"alpha",
"beta",
"workspace:^",
"https://example.com/package.tgz",
"file:../foo",
"github:user/repo#main",
"}invalid{",
];
let ranges_str = vec!["^1.0.0", ">=1.2.0"];
let others: Vec<Rc<Specifier>> = ranges_str.iter().map(|r| Specifier::new(r)).collect();
for specifier in specifiers {
let spec = Specifier::new(specifier);
assert!(
!spec.satisfies_all(&others),
"'{specifier}'.satisfies_all({ranges_str:?}) should return false (no concrete version or range)"
);
}
}
#[test]
fn edge_cases() {
let cases: Vec<(&str, Vec<&str>, bool)> = vec![
("0.0.0", vec![">=0.0.0", "<=1.0.0"], true),
("0.0.0", vec![">0.0.0", "<1.0.0"], false),
("999.999.999", vec![">=0.0.0", "<1000.0.0"], true),
("1.2.3", vec![">=1.2.3", "<=1.2.3"], true),
("1.2.3", vec![">1.2.3", "<1.2.3"], false),
("5.0.0", vec![">=4.0.0", ">=5.0.0", "<=6.0.0", "<=5.0.0", "^5.0.0", "~5.0.0"], true),
("5.1.0", vec![">=4.0.0", ">=5.0.0", "<=6.0.0", "<=5.0.0", "^5.0.0", "~5.0.0"], false),
];
for (version, ranges_str, expected) in cases {
let spec = Specifier::new(version);
let others: Vec<Rc<Specifier>> = ranges_str.iter().map(|r| Specifier::new(r)).collect();
assert_eq!(
spec.satisfies_all(&others),
expected,
"'{version}'.satisfies_all({ranges_str:?}) should be {expected}"
);
}
}
#[test]
fn alias_and_git_with_multiple_ranges() {
let cases: Vec<(&str, Vec<&str>, bool)> = vec![
("npm:lodash@4.17.21", vec!["^4.0.0", ">=4.17.0"], true),
("npm:lodash@4.17.21", vec!["^4.0.0", "^5.0.0"], false),
("npm:react@^18.2.0", vec![">=18.0.0", "<19.0.0"], true),
("github:user/repo#v1.2.3", vec!["^1.0.0", "~1.2.0"], true),
("github:user/repo#v1.2.3", vec!["^1.0.0", "^2.0.0"], false),
("github:user/repo#semver:^1.2.0", vec![">=1.2.0", "<2.0.0"], true),
];
for (specifier, ranges_str, expected) in cases {
let spec = Specifier::new(specifier);
let others: Vec<Rc<Specifier>> = ranges_str.iter().map(|r| Specifier::new(r)).collect();
assert_eq!(
spec.satisfies_all(&others),
expected,
"'{specifier}'.satisfies_all({ranges_str:?}) should be {expected}"
);
}
}
#[test]
fn empty_ranges_array() {
let cases = vec!["1.2.3", "^1.2.3", "~1.2.3", "npm:pkg@1.0.0", "github:user/repo#v1.0.0"];
for specifier in cases {
let spec = Specifier::new(specifier);
let others: Vec<Rc<Specifier>> = vec![];
assert!(
spec.satisfies_all(&others),
"'{specifier}'.satisfies_all([]) should be true (vacuous truth)"
);
}
}
#[test]
fn complex_range_specifiers_as_left_side() {
let cases: Vec<(&str, Vec<&str>, bool)> = vec![
("~1.4.2 || ^1.4.2", vec!["1.4.2"], true),
("~1.0.0 || ^1.0.0", vec!["1.4.2"], true),
("~1.4.2 || ^1.4.2", vec!["^1.0.0", ">=1.4.0"], true),
("~1.0.0 || ^1.0.0", vec!["^1.5.0"], true),
(">=1.0.0 <2.0.0", vec!["^1.5.0"], true),
(">=1.0.0 <2.0.0", vec!["^2.0.0"], false),
(">1.0.0 <=2.0.0", vec!["1.5.0"], true),
(">1.0.0 <=2.0.0", vec!["1.0.0"], false),
(">=1.0.0 <2.0.0", vec![">=1.5.0", "<=1.8.0"], true),
(">=1.0.0 <2.0.0", vec![">=1.5.0", ">=2.0.0"], false),
("<1.5.0 || >=1.6.0", vec!["1.4.0"], true),
("<1.5.0 || >=1.6.0", vec!["1.7.0"], true),
("<1.5.0 || >=1.6.0", vec!["1.5.5"], false),
("<1.5.0 || >=1.6.0", vec![">=1.3.0", "<=2.0.0"], true),
("<1.6.16 || >=1.7.0 <1.7.11 || >=1.8.0 <1.8.2", vec!["1.6.0"], true),
("<1.6.16 || >=1.7.0 <1.7.11 || >=1.8.0 <1.8.2", vec!["1.7.5"], true),
("<1.6.16 || >=1.7.0 <1.7.11 || >=1.8.0 <1.8.2", vec!["1.8.1"], true),
("<1.6.16 || >=1.7.0 <1.7.11 || >=1.8.0 <1.8.2", vec!["1.6.20"], false),
("<1.6.16 || >=1.7.0 <1.7.11 || >=1.8.0 <1.8.2", vec![">=1.0.0", "<2.0.0"], true),
];
for (specifier, ranges_str, expected) in cases {
let spec = Specifier::new(specifier);
let others: Vec<Rc<Specifier>> = ranges_str.iter().map(|r| Specifier::new(r)).collect();
assert_eq!(
spec.satisfies_all(&others),
expected,
"'{specifier}'.satisfies_all({ranges_str:?}) should be {expected}"
);
}
}
#[test]
fn original_test_coverage() {
let cases: Vec<(&str, Vec<&str>, bool)> = vec![
("^1.4.2", vec!["1.4.2"], true),
("1.4.2", vec!["1.4.2"], true),
(">1.4.2", vec!["1.4.2"], false),
(">=1.4.2", vec!["1.4.2"], true),
("<1.4.2", vec!["1.4.2"], false),
("<=1.4.2", vec!["1.4.2"], true),
("~1.4.2", vec!["1.4.2"], true),
("^1.0.0", vec!["1.4.2"], true),
("~1.0.0", vec!["1.4.2"], false),
("*", vec!["1.4.2"], true),
("~1.4.2 || ^1.4.2", vec!["1.4.2"], true),
("~1.0.0 || ^1.0.0", vec!["1.4.2"], true),
];
for (specifier, ranges_str, expected) in cases {
let spec = Specifier::new(specifier);
let others: Vec<Rc<Specifier>> = ranges_str.iter().map(|r| Specifier::new(r)).collect();
assert_eq!(
spec.satisfies_all(&others),
expected,
"'{specifier}'.satisfies_all({ranges_str:?}) should be {expected} (original test compatibility)"
);
}
}