fix: return error on invalid ordinal with points

Previously, if the ordinal contained dots (like 1.2.3-beta.3.4.5),
although it was invalid, an error would not be returned, which was
problematic. This has now been fixed.

Test cases are also included to prevent future regressions.
This commit is contained in:
Erica Marigold 2024-11-21 18:21:19 +00:00
parent ab6e993879
commit fe321dda7b
2 changed files with 136 additions and 108 deletions

View file

@ -293,11 +293,9 @@ function Semver.parse(ver: string): SemverResult<SemverImpl>
-- b) The component has build metadata after prerelease info -- b) The component has build metadata after prerelease info
-- Here, we handle both those cases -- Here, we handle both those cases
local potentialOrdinalNumber, potentialBuildMetadata = string.match(components[4], "(%d)+(.*)") local potentialOrdinalNumber, potentialBuildMetadata = string.match(components[4], "(.*)+(.*)")
if potentialOrdinalNumber == nil then if potentialOrdinalNumber == nil then
return badPrereleaseType(components[4]) return badPrereleaseType(components[4])
-- TODO: If there are components after the 4th index, we
-- should error
end end
if potentialBuildMetadata ~= nil then if potentialBuildMetadata ~= nil then
@ -315,6 +313,14 @@ function Semver.parse(ver: string): SemverResult<SemverImpl>
return badPrereleaseType(components[4]) return badPrereleaseType(components[4])
end end
end end
elseif #components > 4 then
-- The ordinal component was bad, we should error
local badPrerelease = ""
for i = 4, #components do
badPrerelease ..= "." .. components[i]
end
return badPrereleaseType(badPrerelease)
end end
prereleaseOrdinal = Option.Some(ordinalNum :: number) :: Option<number> prereleaseOrdinal = Option.Some(ordinalNum :: number) :: Option<number>

View file

@ -5,118 +5,140 @@ local check = frktest.assert.check
local Semver = require("../lib") local Semver = require("../lib")
return function() return function()
test.suite("Invalid semver parsing tests", function() test.suite("Invalid semver parsing tests", function()
test.case("Rejects missing components", function() test.case("Rejects missing components", function()
local res = Semver.parse("1.2") local res = Semver.parse("1.2")
check.is_true(res:isErr()) check.is_true(res:isErr())
check.table.contains(res:unwrapErr(), { check.table.contains(res:unwrapErr(), {
kind = { kind = {
id = "MandatoryComponentMissing", id = "MandatoryComponentMissing",
components = { "1", "2" }, components = { "1", "2" },
}, },
}) })
end) end)
test.case("Rejects invalid component types", function() test.case("Rejects invalid component types", function()
-- Test invalid major -- Test invalid major
local res = Semver.parse("a.2.3") local res = Semver.parse("a.2.3")
check.is_true(res:isErr()) check.is_true(res:isErr())
check.table.contains(res:unwrapErr(), { check.table.contains(res:unwrapErr(), {
kind = { kind = {
id = "InvalidComponentType", id = "InvalidComponentType",
component = "major", component = "major",
got = "char", got = "char",
}, },
}) })
-- Test invalid minor with symbols -- Test invalid minor with symbols
res = Semver.parse("1.$.3") res = Semver.parse("1.$.3")
check.is_true(res:isErr()) check.is_true(res:isErr())
check.table.contains(res:unwrapErr(), { check.table.contains(res:unwrapErr(), {
kind = { kind = {
id = "InvalidComponentType", id = "InvalidComponentType",
component = "minor", component = "minor",
got = "symbol", got = "symbol",
}, },
}) })
-- Test invalid patch -- Test invalid patch
res = Semver.parse("1.2.x") res = Semver.parse("1.2.x")
check.is_true(res:isErr()) check.is_true(res:isErr())
check.table.contains(res:unwrapErr(), { check.table.contains(res:unwrapErr(), {
kind = { kind = {
id = "InvalidComponentType", id = "InvalidComponentType",
component = "patch", component = "patch",
got = "char", got = "char",
}, },
}) })
end) end)
test.case("Rejects leading zeros", function() test.case("Rejects leading zeros", function()
-- Test leading zeros in major -- Test leading zeros in major
local res = Semver.parse("01.2.3") local res = Semver.parse("01.2.3")
check.is_true(res:isErr()) check.is_true(res:isErr())
check.table.contains(res:unwrapErr(), { check.table.contains(res:unwrapErr(), {
kind = { kind = {
id = "LeadingZerosPresent", id = "LeadingZerosPresent",
component = "major", component = "major",
}, },
}) })
-- Test leading zeros in minor -- Test leading zeros in minor
res = Semver.parse("1.02.3") res = Semver.parse("1.02.3")
check.is_true(res:isErr()) check.is_true(res:isErr())
check.table.contains(res:unwrapErr(), { check.table.contains(res:unwrapErr(), {
kind = { kind = {
id = "LeadingZerosPresent", id = "LeadingZerosPresent",
component = "minor", component = "minor",
}, },
}) })
-- Test leading zeros in patch -- Test leading zeros in patch
res = Semver.parse("1.2.03") res = Semver.parse("1.2.03")
check.is_true(res:isErr()) check.is_true(res:isErr())
check.table.contains(res:unwrapErr(), { check.table.contains(res:unwrapErr(), {
kind = { kind = {
id = "LeadingZerosPresent", id = "LeadingZerosPresent",
component = "patch", component = "patch",
}, },
}) })
end) end)
test.case("Rejects invalid prerelease types", function() test.case("Rejects invalid prerelease types", function()
local res = Semver.parse("1.2.3-gamma.1") local res = Semver.parse("1.2.3-gamma.1")
check.is_true(res:isErr()) check.is_true(res:isErr())
check.table.contains(res:unwrapErr(), { check.table.contains(res:unwrapErr(), {
kind = { kind = {
id = "InvalidPrereleaseType", id = "InvalidPrereleaseType",
type = "gamma", type = "gamma",
}, },
}) })
end) end)
test.case("Rejects invalid prerelease ordinals", function() test.case("Rejects invalid prerelease ordinals", function()
-- Test with character ordinal -- Test with character ordinal
local res = Semver.parse("1.2.3-beta.a") local res = Semver.parse("1.2.3-beta.a")
check.is_true(res:isErr()) check.is_true(res:isErr())
check.table.contains(res:unwrapErr(), { check.table.contains(res:unwrapErr(), {
kind = { kind = {
id = "InvalidPrereleaseOrdinalType", id = "InvalidPrereleaseOrdinalType",
expected = "number", expected = "number",
got = "char", got = "char",
}, },
}) })
-- Test with symbol ordinal -- Test with symbol ordinal
res = Semver.parse("1.2.3-beta.$") res = Semver.parse("1.2.3-beta.$")
check.is_true(res:isErr()) check.is_true(res:isErr())
check.table.contains(res:unwrapErr(), { check.table.contains(res:unwrapErr(), {
kind = { kind = {
id = "InvalidPrereleaseOrdinalType", id = "InvalidPrereleaseOrdinalType",
expected = "number", expected = "number",
got = "symbol", got = "symbol",
}, },
}) })
end)
end) -- Test with extra symbols in ordinal
res = Semver.parse("1.2.3-beta.3.4.5")
check.is_true(res:isErr())
check.table.contains(res:unwrapErr(), {
kind = {
id = "InvalidPrereleaseOrdinalType",
expected = "number",
got = "symbol",
},
})
-- Test with extra symbols in ordinal and build metadata
res = Semver.parse("1.2.3-beta.3.4.5+build.1732213169")
check.is_true(res:isErr())
check.table.contains(res:unwrapErr(), {
kind = {
id = "InvalidPrereleaseOrdinalType",
expected = "number",
got = "symbol",
},
})
end)
end)
end end