// Copyright (c) The Geekeey Authors // SPDX-License-Identifier: EUPL-1.2 namespace Geekeey.SemVer.Tests; internal sealed class SemanticVersionRangeTests { [Test] [Arguments("1.2.3", "1.2.3", true)] [Arguments("1.2.3", "1.2.4", false)] [Arguments(">1.2.3", "1.2.4", true)] [Arguments(">1.2.3", "1.2.3", false)] [Arguments(">=1.2.3", "1.2.3", true)] [Arguments("<1.2.3", "1.2.2", true)] [Arguments("<=1.2.3", "1.2.3", true)] public async Task I_can_satisfy_npm_basic_ranges(string range, string version, bool expected) { var r = SemanticVersionRange.Parse(range); var v = SemanticVersion.Parse(version); await Assert.That(r.Contains(v)).IsEqualTo(expected); } [Test] [Arguments("1.2.3 - 2.3.4", "1.2.3", true)] [Arguments("1.2.3 - 2.3.4", "2.3.4", true)] [Arguments("1.2.3 - 2.3.4", "1.2.2", false)] [Arguments("1.2.3 - 2.3.4", "2.3.5", false)] public async Task I_can_satisfy_npm_hyphen_ranges(string range, string version, bool expected) { var r = SemanticVersionRange.Parse(range); var v = SemanticVersion.Parse(version); await Assert.That(r.Contains(v)).IsEqualTo(expected); } [Test] [Arguments("^1.2.3", "1.2.3", true)] [Arguments("^1.2.3", "1.9.9", true)] [Arguments("^1.2.3", "2.0.0", false)] [Arguments("^0.2.3", "0.2.3", true)] [Arguments("^0.2.3", "0.2.4", true)] [Arguments("^0.2.3", "0.3.0", false)] [Arguments("^0.0.3", "0.0.3", true)] [Arguments("^0.0.3", "0.0.4", false)] public async Task I_can_satisfy_npm_caret_ranges(string range, string version, bool expected) { var r = SemanticVersionRange.Parse(range); var v = SemanticVersion.Parse(version); await Assert.That(r.Contains(v)).IsEqualTo(expected); } [Test] [Arguments("~1.2.3", "1.2.3", true)] [Arguments("~1.2.3", "1.2.9", true)] [Arguments("~1.2.3", "1.3.0", false)] [Arguments("~1.2", "1.2.0", true)] [Arguments("~1.2", "1.2.9", true)] [Arguments("~1.2", "1.3.0", false)] [Arguments("~1", "1.0.0", true)] [Arguments("~1", "1.9.9", true)] [Arguments("~1", "2.0.0", false)] public async Task I_can_satisfy_npm_tilde_ranges(string range, string version, bool expected) { var r = SemanticVersionRange.Parse(range); var v = SemanticVersion.Parse(version); await Assert.That(r.Contains(v)).IsEqualTo(expected); } [Test] [Arguments("1.2.x", "1.2.0", true)] [Arguments("1.2.x", "1.2.9", true)] [Arguments("1.2.x", "1.3.0", false)] [Arguments("1.x", "1.0.0", true)] [Arguments("1.x", "1.9.9", true)] [Arguments("1.x", "2.0.0", false)] [Arguments("*", "0.0.0", true)] [Arguments("*", "9.9.9", true)] public async Task I_can_satisfy_npm_wildcard_ranges(string range, string version, bool expected) { var r = SemanticVersionRange.Parse(range); var v = SemanticVersion.Parse(version); await Assert.That(r.Contains(v)).IsEqualTo(expected); } [Test] public async Task I_can_parse_exact_version_ranges_via_public_api() { var range = SemanticVersionRange.Parse("=1.2.3"); await Assert.That(range.Contains(SemanticVersion.Parse("1.2.3"))).IsTrue(); await Assert.That(range.Contains(SemanticVersion.Parse("1.2.4"))).IsFalse(); await Assert.That(range.ToString("n", null)).IsEqualTo("1.2.3"); } [Test] [Arguments(">1.2.3 || <1.0.0", "1.2.4", true)] [Arguments(">1.2.3 || <1.0.0", "0.9.9", true)] [Arguments(">1.2.3 || <1.0.0", "1.1.0", false)] [Arguments(">1.0.0 <=3.2.6 || >=1.0.0 <6.0.0", "2.0.0", true)] [Arguments(">1.0.0 <=3.2.6 || >=1.0.0 <6.0.0", "5.0.0", true)] [Arguments(">1.0.0 <=3.2.6 || >=1.0.0 <6.0.0", "7.0.0", false)] public async Task I_can_satisfy_npm_or_ranges(string range, string version, bool expected) { var r = SemanticVersionRange.Parse(range); var v = SemanticVersion.Parse(version); await Assert.That(r.Contains(v)).IsEqualTo(expected); } [Test] [Arguments("^6.2.3-alpha.1", "6.2.3-alpha.1", true)] [Arguments("^6.2.3-alpha.1", "6.2.3-beta", true)] [Arguments("^6.2.3-alpha.1", "6.2.2", false)] [Arguments("^6.2.3-alpha.1", "7.0.0", false)] public async Task I_can_satisfy_complex_npm_ranges(string range, string version, bool expected) { var r = SemanticVersionRange.Parse(range); var v = SemanticVersion.Parse(version); await Assert.That(r.Contains(v)).IsEqualTo(expected); } [Test] public async Task I_can_handle_two_constraints_without_combining_them() { var range = SemanticVersionRange.Parse(">=1.0.0 !=2.0.0"); await Assert.That(range.Contains(SemanticVersion.Parse("1.5.0"))).IsTrue(); await Assert.That(range.Contains(SemanticVersion.Parse("2.0.0"))).IsFalse(); await Assert.That(range.ToString("n", null)).IsEqualTo(">=1.0.0 !=2.0.0"); } [Test] [Arguments("^5.*", "5.1.1", "6.1.0", ">=5.0.0 <6.0.0")] [Arguments("5.*", "5.1.1", "6.1.0", ">=5.0.0 <6.0.0")] [Arguments(">=5.*", "5.1.1", "6.1.0", ">=5.0.0 <6.0.0")] [Arguments(">5.*", "5.0.1", "5.0.0", ">5.0.0 <6.0.0")] [Arguments("<=5.*", "5.9.9", "6.0.0", "<6.0.0")] [Arguments("<5.*", "4.9.9", "5.0.0", "<5.0.0")] public async Task I_can_handle_ranges_with_wildcard(string range, string inside, string outside, string expected) { var r = SemanticVersionRange.Parse(range); await Assert.That(r.Contains(SemanticVersion.Parse(inside))).IsTrue(); await Assert.That(r.Contains(SemanticVersion.Parse(outside))).IsFalse(); await Assert.That(r.ToString("n", null)).IsEqualTo(expected); } [Test] [Arguments("[1.2.3]", "1.2.3", true)] [Arguments("[1.2.3]", "1.2.4", false)] [Arguments("[1.2.3, 1.4.0)", "1.2.3", true)] [Arguments("[1.2.3, 1.4.0)", "1.3.9", true)] [Arguments("[1.2.3, 1.4.0)", "1.4.0", false)] [Arguments("(1.2.3, 1.4.0]", "1.2.3", false)] [Arguments("(1.2.3, 1.4.0]", "1.4.0", true)] [Arguments("[1.2.3,)", "1.2.3", true)] [Arguments("[1.2.3,)", "9.9.9", true)] [Arguments("(,1.4.0]", "1.4.0", true)] [Arguments("(,1.4.0]", "0.0.0", true)] [Arguments("(,)", "0.0.0", true)] [Arguments("(,)", "9.9.9", true)] public async Task I_can_satisfy_maven_ranges(string range, string version, bool expected) { var r = SemanticVersionRange.Parse(range); var v = SemanticVersion.Parse(version); await Assert.That(r.Contains(v)).IsEqualTo(expected); } [Test] [Arguments("[1.2,1.3],[1.5,)", "1.2.0", true)] [Arguments("[1.2,1.3],[1.5,)", "1.4.0", false)] [Arguments("[1.2,1.3],[1.5,)", "1.5.0", true)] public async Task I_can_satisfy_maven_or_ranges(string range, string version, bool expected) { var r = SemanticVersionRange.Parse(range); var v = SemanticVersion.Parse(version); await Assert.That(r.Contains(v)).IsEqualTo(expected); } [Test] [Arguments("^1.2.3", "1.2.3-alpha", false)] [Arguments("^1.2.3-alpha", "1.2.3-beta", true)] [Arguments("^1.2.3-alpha", "1.2.4", true)] [Arguments("^1.2.3-alpha", "1.3.0-alpha", true)] public async Task I_can_satisfy_prerelease_rules(string range, string version, bool expected) { var r = SemanticVersionRange.Parse(range); var v = SemanticVersion.Parse(version); await Assert.That(r.Contains(v)).IsEqualTo(expected); } [Test] [Arguments(">=1.0.0 !2.0.0")] [Arguments("1.2.3 - ")] [Arguments("!=1.x")] public async Task I_can_not_parse_invalid_ranges(string range) { await Assert.That(() => SemanticVersionRange.Parse(range)) .Throws(); } [Test] public async Task I_can_serialize_to_json() { var r = SemanticVersionRange.Parse("[1.2.3,2.0.0)"); var json = System.Text.Json.JsonSerializer.Serialize(r); await Assert.That(json).IsEqualTo("\"^1.2.3\""); } [Test] public async Task I_can_deserialize_from_json() { var json = "\"^1.2.3\""; var r = System.Text.Json.JsonSerializer.Deserialize(json); await Assert.That(r.ToString()).IsEqualTo("^1.2.3"); await Assert.That(r.Contains(new SemanticVersion(1, 2, 4))).IsTrue(); } [Test] public async Task I_use_npm_short_format_by_default() { var value = SemanticVersionRange.Parse("[1.2.3,2.0.0)"); await Assert.That(value.ToString()).IsEqualTo("^1.2.3"); } [Test] [Arguments("^1.2.3", "m", "[1.2.3,2.0.0)")] [Arguments("~1.2.3", "m", "[1.2.3,1.3.0)")] [Arguments("1.2.3", "m", "[1.2.3]")] [Arguments("[1.2.3,2.0.0)", "n", ">=1.2.3 <2.0.0")] [Arguments("[1.2.3,2.0.0)", "ns", "^1.2.3")] [Arguments("[1.2.3,1.3.0)", "ns", "~1.2.3")] [Arguments("[1.2,1.3],[1.5,)", "n", ">=1.2.0 <=1.3.0 || >=1.5.0")] [Arguments("*", "m", "(,)")] [Arguments("(,)", "ns", "*")] public async Task I_can_convert_range_formats(string range, string format, string expected) { var value = SemanticVersionRange.Parse(range); await Assert.That(value.ToString(format, null)).IsEqualTo(expected); } [Test] public async Task I_can_format_ranges_to_chars() { var value = SemanticVersionRange.Parse("[1.2.3,2.0.0)"); var destination = new char[32]; var success = value.TryFormat(destination, out var charsWritten, "ns", null); await Assert.That(success).IsTrue(); await Assert.That(new string(destination[..charsWritten])).IsEqualTo("^1.2.3"); } [Test] public async Task I_fail_formatting_when_the_tentative_short_form_overflows() { var value = SemanticVersionRange.Parse("[1.2.3,2.0.0)"); var destination = new char[5]; var success = value.TryFormat(destination, out var charsWritten, "ns", null); await Assert.That(success).IsFalse(); await Assert.That(charsWritten).IsEqualTo(0); } }