mirror of
https://github.com/0x5eal/semver-luau.git
synced 2025-04-10 21:30:53 +01:00
Compare commits
No commits in common. "v0.1.0" and "main" have entirely different histories.
16 changed files with 763 additions and 267 deletions
29
.github/actions/setup-pesde/action.yml
vendored
Normal file
29
.github/actions/setup-pesde/action.yml
vendored
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
name: Install pesde
|
||||||
|
description: Installs pesde CLI and authenticates with the registry
|
||||||
|
|
||||||
|
inputs:
|
||||||
|
pesde-token:
|
||||||
|
description: "Token for publishing to the pesde registry"
|
||||||
|
required: false
|
||||||
|
|
||||||
|
runs:
|
||||||
|
using: composite
|
||||||
|
steps:
|
||||||
|
- name: Download pesde
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
latest_release=$(curl -s https://api.github.com/repos/pesde-pkg/pesde/releases | jq '[.[] | select(.prerelease == true or .prerelease == false)][0]')
|
||||||
|
download_url=$(echo "$latest_release" | jq -r '.assets[] | select(.name | endswith("linux-x86_64.tar.gz")) | .browser_download_url')
|
||||||
|
|
||||||
|
curl -L -o /tmp/pesde.tar.gz "$download_url"
|
||||||
|
tar -xzvf /tmp/pesde.tar.gz
|
||||||
|
chmod +x pesde
|
||||||
|
|
||||||
|
./pesde self-install
|
||||||
|
rm ./pesde
|
||||||
|
echo "$HOME/.pesde/bin" >> $GITHUB_PATH
|
||||||
|
|
||||||
|
- name: Authenticate into pesde registry
|
||||||
|
if: inputs.pesde-token != ''
|
||||||
|
shell: bash
|
||||||
|
run: pesde auth login --token "${{ inputs.pesde-token }}"
|
82
.github/workflows/ci.yml
vendored
Normal file
82
.github/workflows/ci.yml
vendored
Normal file
|
@ -0,0 +1,82 @@
|
||||||
|
name: CI
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
pull_request:
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
|
defaults:
|
||||||
|
run:
|
||||||
|
shell: bash
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
fmt:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Checkout repository
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Install tooling
|
||||||
|
uses: CompeyDev/setup-rokit@v0.1.2
|
||||||
|
with:
|
||||||
|
cache: true
|
||||||
|
|
||||||
|
- name: Install pesde
|
||||||
|
uses: ./.github/actions/setup-pesde
|
||||||
|
with:
|
||||||
|
pesde-token: ${{ secrets.PESDE_TOKEN }}
|
||||||
|
|
||||||
|
- name: Install dependencies
|
||||||
|
run: pesde install
|
||||||
|
|
||||||
|
- name: Check formatting
|
||||||
|
run: lune run fmt -- --check
|
||||||
|
|
||||||
|
typecheck:
|
||||||
|
needs: ["fmt"]
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Checkout repository
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Install tooling
|
||||||
|
uses: CompeyDev/setup-rokit@v0.1.2
|
||||||
|
with:
|
||||||
|
cache: true
|
||||||
|
|
||||||
|
- name: Install pesde
|
||||||
|
uses: ./.github/actions/setup-pesde
|
||||||
|
with:
|
||||||
|
pesde-token: ${{ secrets.PESDE_TOKEN }}
|
||||||
|
|
||||||
|
- name: Install dependencies
|
||||||
|
run: pesde install
|
||||||
|
|
||||||
|
- name: Setup lune typedefs
|
||||||
|
run: lune setup
|
||||||
|
|
||||||
|
- name: Typecheck
|
||||||
|
run: lune run typecheck
|
||||||
|
|
||||||
|
test:
|
||||||
|
needs: ["typecheck"]
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Checkout repository
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Install tooling
|
||||||
|
uses: CompeyDev/setup-rokit@v0.1.2
|
||||||
|
with:
|
||||||
|
cache: true
|
||||||
|
|
||||||
|
- name: Install pesde
|
||||||
|
uses: ./.github/actions/setup-pesde
|
||||||
|
with:
|
||||||
|
pesde-token: ${{ secrets.PESDE_TOKEN }}
|
||||||
|
|
||||||
|
- name: Install dependencies
|
||||||
|
run: pesde install
|
||||||
|
|
||||||
|
- name: Run tests
|
||||||
|
run: lune run test
|
24
.github/workflows/publish.yml
vendored
Normal file
24
.github/workflows/publish.yml
vendored
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
name: Publish
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
tags:
|
||||||
|
- "v*"
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
publish:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Install pesde
|
||||||
|
uses: ./.github/actions/setup-pesde
|
||||||
|
with:
|
||||||
|
pesde-token: ${{ secrets.PESDE_TOKEN }}
|
||||||
|
|
||||||
|
- name: Install dependencies
|
||||||
|
run: pesde install
|
||||||
|
|
||||||
|
- name: Publish
|
||||||
|
run: pesde publish -y
|
110
.lune/exec.luau
Normal file
110
.lune/exec.luau
Normal file
|
@ -0,0 +1,110 @@
|
||||||
|
--> lib: Builder pattern class to spawn child processes
|
||||||
|
|
||||||
|
local stdio = require("@lune/stdio")
|
||||||
|
local process = require("@lune/process")
|
||||||
|
|
||||||
|
local Option = require("../luau_packages/option")
|
||||||
|
type Option<T> = Option.Option<T>
|
||||||
|
|
||||||
|
local CommandBuilder = {}
|
||||||
|
|
||||||
|
export type CommandBuilder = typeof(setmetatable({} :: CommandBuilderFields, { __index = CommandBuilder }))
|
||||||
|
type CommandBuilderFields = {
|
||||||
|
program: string,
|
||||||
|
args: { string },
|
||||||
|
stdioStrategy: Option<IoStrategyMapping>,
|
||||||
|
}
|
||||||
|
export type StdioStrategy = "pipe" | "forward" | "none"
|
||||||
|
export type IoStrategyMapping = {
|
||||||
|
stdout: Option<StdioStrategy>,
|
||||||
|
stderr: Option<StdioStrategy>,
|
||||||
|
}
|
||||||
|
|
||||||
|
-- FIXME: remove unknown usage
|
||||||
|
local DEFAULT_STDIO_STRATEGY: IoStrategyMapping = {
|
||||||
|
stdout = Option.Some("pipe" :: StdioStrategy) :: Option<unknown>,
|
||||||
|
stderr = Option.Some("pipe" :: StdioStrategy) :: Option<unknown>,
|
||||||
|
}
|
||||||
|
function CommandBuilder.new(program: string)
|
||||||
|
return setmetatable(
|
||||||
|
{
|
||||||
|
program = program,
|
||||||
|
args = {},
|
||||||
|
stdioStrategy = Option.None :: Option<IoStrategyMapping>,
|
||||||
|
} :: CommandBuilderFields,
|
||||||
|
{
|
||||||
|
__index = CommandBuilder,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
function CommandBuilder.withArg(self: CommandBuilder, arg: string): CommandBuilder
|
||||||
|
table.insert(self.args, arg)
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
function CommandBuilder.withArgs(self: CommandBuilder, args: { string }): CommandBuilder
|
||||||
|
for _, arg in args do
|
||||||
|
self:withArg(arg)
|
||||||
|
end
|
||||||
|
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
function CommandBuilder.withStdioStrategy(
|
||||||
|
self: CommandBuilder,
|
||||||
|
strategy: StdioStrategy | IoStrategyMapping
|
||||||
|
): CommandBuilder
|
||||||
|
-- FIXME: remove unknown usage
|
||||||
|
self.stdioStrategy = Option.Some(if typeof(strategy) == "string"
|
||||||
|
then {
|
||||||
|
stdout = Option.Some(strategy) :: Option<unknown>,
|
||||||
|
stderr = Option.Some(strategy) :: Option<unknown>,
|
||||||
|
}
|
||||||
|
else strategy) :: Option<IoStrategyMapping>
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
local function intoSpawnOptionsStdioKind(strategy: StdioStrategy): process.SpawnOptionsStdioKind
|
||||||
|
if strategy == "pipe" then
|
||||||
|
return "default"
|
||||||
|
end
|
||||||
|
|
||||||
|
if strategy == "forward" then
|
||||||
|
return "forward"
|
||||||
|
end
|
||||||
|
|
||||||
|
if strategy == "none" then
|
||||||
|
return "none"
|
||||||
|
end
|
||||||
|
|
||||||
|
error(`Non-strategy provided: {strategy}`)
|
||||||
|
end
|
||||||
|
|
||||||
|
function CommandBuilder.exec(self: CommandBuilder): process.SpawnResult
|
||||||
|
print("$", self.program, table.concat(self.args, " "))
|
||||||
|
local child = process.spawn(self.program, self.args, {
|
||||||
|
shell = true,
|
||||||
|
stdio = self
|
||||||
|
.stdioStrategy
|
||||||
|
-- FIXME: remove unknown usage
|
||||||
|
:orOpt(Option.Some(DEFAULT_STDIO_STRATEGY) :: Option<unknown>)
|
||||||
|
:map(function(mappings: IoStrategyMapping)
|
||||||
|
local translatedMappings: process.SpawnOptionsStdio = {}
|
||||||
|
for field, value in mappings do
|
||||||
|
translatedMappings[field] = intoSpawnOptionsStdioKind((value :: Option<StdioStrategy>):unwrap())
|
||||||
|
end
|
||||||
|
|
||||||
|
return translatedMappings
|
||||||
|
end)
|
||||||
|
:unwrap(),
|
||||||
|
})
|
||||||
|
|
||||||
|
if not child.ok then
|
||||||
|
print(`\n{stdio.color("red")}[luau-lsp]{stdio.color("reset")} Exited with code`, child.code)
|
||||||
|
end
|
||||||
|
|
||||||
|
return child
|
||||||
|
end
|
||||||
|
|
||||||
|
return CommandBuilder
|
7
.lune/fmt.luau
Normal file
7
.lune/fmt.luau
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
--> Run stylua to check for formatting errors
|
||||||
|
|
||||||
|
local process = require("@lune/process")
|
||||||
|
|
||||||
|
local CommandBuilder = require("./exec")
|
||||||
|
|
||||||
|
process.exit(CommandBuilder.new("stylua"):withArg("."):withArgs(process.args):withStdioStrategy("forward"):exec().code)
|
|
@ -4,21 +4,54 @@ local fs = require("@lune/fs")
|
||||||
local process = require("@lune/process")
|
local process = require("@lune/process")
|
||||||
|
|
||||||
local frktest = require("@pkg/frktest")
|
local frktest = require("@pkg/frktest")
|
||||||
|
local reporter = require("../tests/_reporter")
|
||||||
|
|
||||||
-- HACK: Cast require to allow for dynamic paths in strict mode
|
-- HACK: Cast require to allow for dynamic paths in strict mode
|
||||||
-- A more proper solution would be to use luau.load instead, but
|
-- A more proper solution would be to use luau.load instead, but
|
||||||
-- frktest requires its global state to be modified by test suites
|
-- frktest requires its global state to be modified by test suites
|
||||||
local require = require :: (path: string) -> () -> ()
|
local require = require :: (
|
||||||
|
path: string
|
||||||
|
) -> (
|
||||||
|
test: typeof(setmetatable(
|
||||||
|
{} :: {
|
||||||
|
case: (name: string, fn: () -> nil) -> (),
|
||||||
|
suite: (name: string, fn: () -> ()) -> (),
|
||||||
|
},
|
||||||
|
{ __index = frktest.test }
|
||||||
|
))
|
||||||
|
) -> ()
|
||||||
|
|
||||||
if process.args[1] ~= nil then
|
local allowed_tests = process.args
|
||||||
require("../tests/" .. process.args[1])()
|
for _, test in fs.readDir("tests") do
|
||||||
else
|
-- If we are given any arguments, we only run those tests, otherwise,
|
||||||
for _, test in fs.readDir("tests") do
|
-- we run all the tests
|
||||||
require("../tests/" .. test)()
|
|
||||||
end
|
-- So, to include only a certain set of test files, you can provide either
|
||||||
|
-- the full path to the test file (with or without the extension) or the test
|
||||||
|
-- file name
|
||||||
|
local withoutExt = string.sub(test, 1, -6)
|
||||||
|
local is_allowed = #process.args == 0
|
||||||
|
or table.find(allowed_tests, `tests/{test}`)
|
||||||
|
or table.find(allowed_tests, withoutExt)
|
||||||
|
or table.find(allowed_tests, `tests/{withoutExt}`)
|
||||||
|
|
||||||
|
local constructors = {
|
||||||
|
case = frktest.test.case,
|
||||||
|
suite = frktest.test.suite,
|
||||||
|
}
|
||||||
|
|
||||||
|
if not is_allowed then
|
||||||
|
constructors.case = frktest.test.skip.case
|
||||||
|
constructors.suite = frktest.test.skip.suite
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Ignore files starting with underscores, eg: _reporter.luau
|
||||||
|
if string.sub(test, 1, 1) == "_" then
|
||||||
|
continue
|
||||||
|
end
|
||||||
|
|
||||||
|
require("../tests/" .. test)(setmetatable(constructors, { __index = frktest.test }))
|
||||||
end
|
end
|
||||||
|
|
||||||
frktest._reporters.lune_console_reporter.init()
|
reporter.init()
|
||||||
if not frktest.run() then
|
process.exit(tonumber(frktest.run()))
|
||||||
process.exit(1)
|
|
||||||
end
|
|
||||||
|
|
15
.lune/typecheck.luau
Normal file
15
.lune/typecheck.luau
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
--> Run luau-lsp analysis to check for type errors
|
||||||
|
|
||||||
|
local process = require("@lune/process")
|
||||||
|
|
||||||
|
local CommandBuilder = require("./exec")
|
||||||
|
|
||||||
|
process.exit(
|
||||||
|
CommandBuilder.new("luau-lsp")
|
||||||
|
:withArg("analyze")
|
||||||
|
:withArgs({ "--settings", ".vscode/settings.json" })
|
||||||
|
:withArgs({ "--ignore", "'**/.pesde/**'" })
|
||||||
|
:withArg(".")
|
||||||
|
:withStdioStrategy("forward")
|
||||||
|
:exec().code
|
||||||
|
)
|
|
@ -28,17 +28,17 @@ Invalid versions should return a [`Result`] with the respective error. If not, t
|
||||||
filing an issue [here](https://github.com/0x5eal/semver-luau/issues).
|
filing an issue [here](https://github.com/0x5eal/semver-luau/issues).
|
||||||
|
|
||||||
```luau
|
```luau
|
||||||
local semver = require("semver")
|
local Semver = require("semver")
|
||||||
|
|
||||||
-- Parse version strings
|
-- Parse version strings
|
||||||
local v1 = semver.parse("1.2.3"):unwrap() --[[
|
local v1 = Semver.parse("1.2.3"):unwrap() --[[
|
||||||
major = 1,
|
major = 1,
|
||||||
minor = 2,
|
minor = 2,
|
||||||
patch = 3,
|
patch = 3,
|
||||||
buildMetadata = Option::None,
|
buildMetadata = Option::None,
|
||||||
prerelease = Option::None
|
prerelease = Option::None
|
||||||
]]
|
]]
|
||||||
local v2 = semver.parse("5.12.0+build.1731248766"):unwrap() --[[
|
local v2 = Semver.parse("5.12.0+build.1731248766"):unwrap() --[[
|
||||||
major = 5,
|
major = 5,
|
||||||
minor = 12,
|
minor = 12,
|
||||||
patch = 0,
|
patch = 0,
|
||||||
|
|
|
@ -145,7 +145,7 @@ export type SemverErrorKind = ParseError | {}
|
||||||
* `InvalidPrereleaseOrdinalType`: When prerelease ordinal is not a number
|
* `InvalidPrereleaseOrdinalType`: When prerelease ordinal is not a number
|
||||||
]=]
|
]=]
|
||||||
export type ParseError =
|
export type ParseError =
|
||||||
{ id: "MandatoryComponentMissing", components: { string } }
|
| { id: "MandatoryComponentMissing", components: { string } }
|
||||||
| {
|
| {
|
||||||
id: "InvalidComponentType",
|
id: "InvalidComponentType",
|
||||||
component: "major" | "minor" | "patch",
|
component: "major" | "minor" | "patch",
|
||||||
|
@ -170,6 +170,19 @@ local PRERELEASE_LEX_ORDER: { [PreleaseType]: number } = table.freeze({
|
||||||
rc = 3,
|
rc = 3,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
--[=[
|
||||||
|
@within Semver
|
||||||
|
@function new
|
||||||
|
|
||||||
|
Creates a new Semver instance from a [Version]
|
||||||
|
|
||||||
|
@param ver Version -- The version to create a Semver instance from
|
||||||
|
@return SemverImpl -- The new Semver instance
|
||||||
|
]=]
|
||||||
|
function Semver.new(ver: Version): SemverImpl
|
||||||
|
return setmetatable(ver, Semver)
|
||||||
|
end
|
||||||
|
|
||||||
--[=[
|
--[=[
|
||||||
@within Semver
|
@within Semver
|
||||||
@function parse
|
@function parse
|
||||||
|
@ -293,7 +306,7 @@ 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])
|
||||||
end
|
end
|
||||||
|
@ -313,6 +326,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>
|
||||||
|
@ -336,7 +357,7 @@ function Semver.parse(ver: string): SemverResult<SemverImpl>
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
return Result.Ok(setmetatable(parsed :: Version, Semver))
|
return Result.Ok(Semver.new(parsed))
|
||||||
end
|
end
|
||||||
|
|
||||||
local function prereleaseEq(leftPrerelease: PrereleaseVersion?, rightPrerelease: PrereleaseVersion?): boolean
|
local function prereleaseEq(leftPrerelease: PrereleaseVersion?, rightPrerelease: PrereleaseVersion?): boolean
|
||||||
|
@ -395,4 +416,24 @@ function Semver.__le(left: SemverImpl, right: SemverImpl): boolean
|
||||||
else prereleaseLe(left.prerelease:unwrapOrNil(), right.prerelease:unwrapOrNil())
|
else prereleaseLe(left.prerelease:unwrapOrNil(), right.prerelease:unwrapOrNil())
|
||||||
end
|
end
|
||||||
|
|
||||||
|
function Semver.__tostring(self: SemverImpl): string
|
||||||
|
local base = `{self.major}.{self.minor}.{self.patch}`
|
||||||
|
local prerelease = self.prerelease
|
||||||
|
:map(function(prerelease: PrereleaseVersion)
|
||||||
|
return `-{prerelease.type}{prerelease.ordinal
|
||||||
|
:map(function(ord: number)
|
||||||
|
return `.{ord}`
|
||||||
|
end)
|
||||||
|
:unwrapOr("")}`
|
||||||
|
end)
|
||||||
|
:unwrapOr("")
|
||||||
|
local build = self.buildMetadata
|
||||||
|
:map(function(buildMetadata: string)
|
||||||
|
return `+{buildMetadata}`
|
||||||
|
end)
|
||||||
|
:unwrapOr("")
|
||||||
|
|
||||||
|
return base .. prerelease .. build
|
||||||
|
end
|
||||||
|
|
||||||
return Semver
|
return Semver
|
||||||
|
|
|
@ -1,15 +1,10 @@
|
||||||
name = "0x5eal/semver"
|
name = "0x5eal/semver"
|
||||||
version = "0.1.0"
|
version = "0.1.1"
|
||||||
description = "Strongly typed semver (https://semver.org) parser for Luau"
|
description = "Strongly typed semver (https://semver.org) parser for Luau"
|
||||||
authors = ["Erica Marigold <hi@devcomp.xyz>"]
|
authors = ["Erica Marigold <hi@devcomp.xyz>"]
|
||||||
repository = "https://github.com/0x5eal/semver-luau.git"
|
repository = "https://github.com/0x5eal/semver-luau.git"
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
includes = [
|
includes = ["lib", "pesde.toml", "README.md", "LICENSE.md"]
|
||||||
"lib",
|
|
||||||
"pesde.toml",
|
|
||||||
"README.md",
|
|
||||||
"LICENSE.md",
|
|
||||||
]
|
|
||||||
|
|
||||||
[scripts]
|
[scripts]
|
||||||
test = ".lune/test"
|
test = ".lune/test"
|
||||||
|
|
4
rokit.toml
Normal file
4
rokit.toml
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
[tools]
|
||||||
|
lune = "lune-org/lune@0.8.9"
|
||||||
|
stylua = "JohnnyMorganz/StyLua@2.0.2"
|
||||||
|
luau-lsp = "JohnnyMorganz/luau-lsp@1.35.0"
|
55
tests/_reporter.luau
Normal file
55
tests/_reporter.luau
Normal file
|
@ -0,0 +1,55 @@
|
||||||
|
local stdio = require("@lune/stdio")
|
||||||
|
|
||||||
|
local frktest = require("@pkg/frktest")
|
||||||
|
local Reporter = frktest._reporters.lune_console_reporter
|
||||||
|
|
||||||
|
local STYLE = table.freeze({
|
||||||
|
suite = function(name: string)
|
||||||
|
return `{stdio.style("bold")}{stdio.color("purple")}SUITE{stdio.style("reset")} {name}`
|
||||||
|
end,
|
||||||
|
|
||||||
|
report = function(name: string, state: "run" | "success" | "error" | "skip")
|
||||||
|
local state_color: stdio.Color = if state == "run"
|
||||||
|
then "white"
|
||||||
|
elseif state == "success" then "green"
|
||||||
|
elseif state == "error" then "red"
|
||||||
|
elseif state == "skip" then "yellow"
|
||||||
|
else error("Invalid test state")
|
||||||
|
return ` {stdio.style("bold")}{stdio.color(state_color)}{if state == "skip" then "SKIP" else "TEST"}{stdio.style(
|
||||||
|
"reset"
|
||||||
|
)} {name}`
|
||||||
|
end,
|
||||||
|
})
|
||||||
|
|
||||||
|
--- Clears a the previous line, and moves to its beginning
|
||||||
|
local function clear_last_line(): ()
|
||||||
|
return stdio.write("\x1b[A\x1b[K\x1b[0G")
|
||||||
|
end
|
||||||
|
|
||||||
|
local ReporterExt = {}
|
||||||
|
function ReporterExt.init()
|
||||||
|
frktest.test.on_suite_enter(function(suite)
|
||||||
|
print(STYLE.suite(suite.name))
|
||||||
|
end)
|
||||||
|
|
||||||
|
frktest.test.on_suite_leave(function()
|
||||||
|
stdio.write("\n")
|
||||||
|
end)
|
||||||
|
|
||||||
|
frktest.test.on_test_enter(function(test)
|
||||||
|
print(STYLE.report(test.name, "run"))
|
||||||
|
end)
|
||||||
|
|
||||||
|
frktest.test.on_test_leave(function(test)
|
||||||
|
clear_last_line()
|
||||||
|
print(STYLE.report(test.name, if test.failed then "error" else "success"))
|
||||||
|
end)
|
||||||
|
|
||||||
|
frktest.test.on_test_skipped(function(test)
|
||||||
|
print(STYLE.report(test.name, "skip"))
|
||||||
|
end)
|
||||||
|
|
||||||
|
Reporter.init()
|
||||||
|
end
|
||||||
|
|
||||||
|
return setmetatable(ReporterExt, { __index = Reporter })
|
167
tests/cmp.luau
167
tests/cmp.luau
|
@ -1,5 +1,4 @@
|
||||||
local frktest = require("@pkg/frktest")
|
local frktest = require("@pkg/frktest")
|
||||||
local test = frktest.test
|
|
||||||
local check = frktest.assert.check
|
local check = frktest.assert.check
|
||||||
|
|
||||||
local Option = require("../luau_packages/option")
|
local Option = require("../luau_packages/option")
|
||||||
|
@ -7,96 +6,96 @@ type Option<T> = Option.Option<T>
|
||||||
|
|
||||||
local Semver = require("../lib")
|
local Semver = require("../lib")
|
||||||
|
|
||||||
return function()
|
return function(test: typeof(frktest.test))
|
||||||
test.suite("Semver comparison tests", function()
|
test.suite("Semver comparison tests", function()
|
||||||
test.case("Basic version comparisons", function()
|
test.case("Basic version comparisons", function()
|
||||||
local v1 = Semver.parse("1.2.3"):unwrap()
|
local v1 = Semver.parse("1.2.3"):unwrap()
|
||||||
local v2 = Semver.parse("1.2.4"):unwrap()
|
local v2 = Semver.parse("1.2.4"):unwrap()
|
||||||
local v3 = Semver.parse("1.3.0"):unwrap()
|
local v3 = Semver.parse("1.3.0"):unwrap()
|
||||||
local v4 = Semver.parse("2.0.0"):unwrap()
|
local v4 = Semver.parse("2.0.0"):unwrap()
|
||||||
local v5 = Semver.parse("2.1.0"):unwrap()
|
local v5 = Semver.parse("2.1.0"):unwrap()
|
||||||
local v6 = Semver.parse("3.0.0"):unwrap()
|
local v6 = Semver.parse("3.0.0"):unwrap()
|
||||||
|
|
||||||
check.is_true(v1 < v2)
|
check.is_true(v1 < v2)
|
||||||
check.is_true(v2 < v3)
|
check.is_true(v2 < v3)
|
||||||
check.is_true(v3 < v4)
|
check.is_true(v3 < v4)
|
||||||
check.is_true(v4 < v5)
|
check.is_true(v4 < v5)
|
||||||
check.is_true(v5 < v6)
|
check.is_true(v5 < v6)
|
||||||
check.is_true(v1 <= v2)
|
check.is_true(v1 <= v2)
|
||||||
check.is_true(v2 > v1)
|
check.is_true(v2 > v1)
|
||||||
check.is_true(v4 >= v3)
|
check.is_true(v4 >= v3)
|
||||||
check.is_true(v6 > v1)
|
check.is_true(v6 > v1)
|
||||||
end)
|
end)
|
||||||
|
|
||||||
test.case("Equal version comparisons", function()
|
test.case("Equal version comparisons", function()
|
||||||
local v1 = Semver.parse("1.2.3"):unwrap()
|
local v1 = Semver.parse("1.2.3"):unwrap()
|
||||||
local v2 = Semver.parse("1.2.3"):unwrap()
|
local v2 = Semver.parse("1.2.3"):unwrap()
|
||||||
local v3 = Semver.parse("1.2.3"):unwrap()
|
local v3 = Semver.parse("1.2.3"):unwrap()
|
||||||
local v4 = Semver.parse("1.2.3"):unwrap()
|
local v4 = Semver.parse("1.2.3"):unwrap()
|
||||||
|
|
||||||
check.is_true(v1 == v2)
|
check.is_true(v1 == v2)
|
||||||
check.is_true(v2 == v3)
|
check.is_true(v2 == v3)
|
||||||
check.is_true(v3 == v4)
|
check.is_true(v3 == v4)
|
||||||
check.is_true(v1 <= v2)
|
check.is_true(v1 <= v2)
|
||||||
check.is_true(v1 >= v2)
|
check.is_true(v1 >= v2)
|
||||||
check.is_false(v1 < v2)
|
check.is_false(v1 < v2)
|
||||||
check.is_false(v1 > v2)
|
check.is_false(v1 > v2)
|
||||||
check.is_false(v3 > v4)
|
check.is_false(v3 > v4)
|
||||||
end)
|
end)
|
||||||
|
|
||||||
test.case("Prerelease version comparisons", function()
|
test.case("Prerelease version comparisons", function()
|
||||||
local v1 = Semver.parse("1.2.3-alpha.1"):unwrap()
|
local v1 = Semver.parse("1.2.3-alpha.1"):unwrap()
|
||||||
local v2 = Semver.parse("1.2.3-alpha.2"):unwrap()
|
local v2 = Semver.parse("1.2.3-alpha.2"):unwrap()
|
||||||
local v3 = Semver.parse("1.2.3-beta.1"):unwrap()
|
local v3 = Semver.parse("1.2.3-beta.1"):unwrap()
|
||||||
local v4 = Semver.parse("1.2.3"):unwrap()
|
local v4 = Semver.parse("1.2.3"):unwrap()
|
||||||
local v5 = Semver.parse("1.2.3-rc.1"):unwrap()
|
local v5 = Semver.parse("1.2.3-rc.1"):unwrap()
|
||||||
local v6 = Semver.parse("1.2.3-rc.2"):unwrap()
|
local v6 = Semver.parse("1.2.3-rc.2"):unwrap()
|
||||||
|
|
||||||
check.is_true(v1 < v2)
|
check.is_true(v1 < v2)
|
||||||
check.is_true(v2 < v3)
|
check.is_true(v2 < v3)
|
||||||
check.is_true(v3 < v4)
|
check.is_true(v3 < v4)
|
||||||
check.is_true(v3 < v5)
|
check.is_true(v3 < v5)
|
||||||
check.is_true(v5 < v6)
|
check.is_true(v5 < v6)
|
||||||
check.is_true(v6 < v4)
|
check.is_true(v6 < v4)
|
||||||
check.is_false(v4 < v1)
|
check.is_false(v4 < v1)
|
||||||
check.is_true(v4 > v3)
|
check.is_true(v4 > v3)
|
||||||
end)
|
end)
|
||||||
|
|
||||||
test.case("Build metadata comparisons", function()
|
test.case("Build metadata comparisons", function()
|
||||||
local v1 = Semver.parse("1.2.3+build.1"):unwrap()
|
local v1 = Semver.parse("1.2.3+build.1"):unwrap()
|
||||||
local v2 = Semver.parse("1.2.3+build.2"):unwrap()
|
local v2 = Semver.parse("1.2.3+build.2"):unwrap()
|
||||||
local v3 = Semver.parse("1.2.3+build.123"):unwrap()
|
local v3 = Semver.parse("1.2.3+build.123"):unwrap()
|
||||||
local v4 = Semver.parse("1.2.3+20230615"):unwrap()
|
local v4 = Semver.parse("1.2.3+20230615"):unwrap()
|
||||||
local v5 = Semver.parse("1.2.3+exp.sha.5114f85"):unwrap()
|
local v5 = Semver.parse("1.2.3+exp.sha.5114f85"):unwrap()
|
||||||
|
|
||||||
-- Build metadata should be ignored in comparisons
|
-- Build metadata should be ignored in comparisons
|
||||||
check.is_true(v1 == v2)
|
check.is_true(v1 == v2)
|
||||||
check.is_true(v2 == v3)
|
check.is_true(v2 == v3)
|
||||||
check.is_true(v3 == v4)
|
check.is_true(v3 == v4)
|
||||||
check.is_true(v4 == v5)
|
check.is_true(v4 == v5)
|
||||||
check.is_false(v1 < v2)
|
check.is_false(v1 < v2)
|
||||||
check.is_false(v2 > v3)
|
check.is_false(v2 > v3)
|
||||||
check.is_false(v4 > v5)
|
check.is_false(v4 > v5)
|
||||||
end)
|
end)
|
||||||
|
|
||||||
test.case("Complex version comparisons", function()
|
test.case("Complex version comparisons", function()
|
||||||
local v1 = Semver.parse("2.0.0-alpha.1+build.123"):unwrap()
|
local v1 = Semver.parse("2.0.0-alpha.1+build.123"):unwrap()
|
||||||
local v2 = Semver.parse("2.0.0-beta.1+build.123"):unwrap()
|
local v2 = Semver.parse("2.0.0-beta.1+build.123"):unwrap()
|
||||||
local v3 = Semver.parse("2.0.0+build.123"):unwrap()
|
local v3 = Semver.parse("2.0.0+build.123"):unwrap()
|
||||||
local v4 = Semver.parse("2.1.0-alpha.1"):unwrap()
|
local v4 = Semver.parse("2.1.0-alpha.1"):unwrap()
|
||||||
local v5 = Semver.parse("2.1.0"):unwrap()
|
local v5 = Semver.parse("2.1.0"):unwrap()
|
||||||
local v6 = Semver.parse("2.1.0-rc.1+build.999"):unwrap()
|
local v6 = Semver.parse("2.1.0-rc.1+build.999"):unwrap()
|
||||||
local v7 = Semver.parse("2.1.1-alpha.1+sha.xyz"):unwrap()
|
local v7 = Semver.parse("2.1.1-alpha.1+sha.xyz"):unwrap()
|
||||||
|
|
||||||
check.is_true(v1 < v2)
|
check.is_true(v1 < v2)
|
||||||
check.is_true(v2 < v3)
|
check.is_true(v2 < v3)
|
||||||
check.is_true(v3 < v4)
|
check.is_true(v3 < v4)
|
||||||
check.is_true(v4 < v5)
|
check.is_true(v4 < v5)
|
||||||
check.is_true(v4 < v6)
|
check.is_true(v4 < v6)
|
||||||
check.is_true(v6 < v5)
|
check.is_true(v6 < v5)
|
||||||
check.is_true(v5 < v7)
|
check.is_true(v5 < v7)
|
||||||
check.is_false(v5 < v1)
|
check.is_false(v5 < v1)
|
||||||
check.is_false(v7 < v6)
|
check.is_false(v7 < v6)
|
||||||
end)
|
end)
|
||||||
end)
|
end)
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,122 +1,143 @@
|
||||||
local frktest = require("@pkg/frktest")
|
local frktest = require("@pkg/frktest")
|
||||||
local test = frktest.test
|
|
||||||
local check = frktest.assert.check
|
local check = frktest.assert.check
|
||||||
|
|
||||||
local Semver = require("../lib")
|
local Semver = require("../lib")
|
||||||
|
|
||||||
return function()
|
return function(test: typeof(frktest.test))
|
||||||
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.abc")
|
||||||
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
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
local frktest = require("@pkg/frktest")
|
local frktest = require("@pkg/frktest")
|
||||||
local test = frktest.test
|
|
||||||
local check = frktest.assert.check
|
local check = frktest.assert.check
|
||||||
|
|
||||||
local Option = require("../luau_packages/option")
|
local Option = require("../luau_packages/option")
|
||||||
|
@ -7,66 +6,66 @@ type Option<T> = Option.Option<T>
|
||||||
|
|
||||||
local Semver = require("../lib")
|
local Semver = require("../lib")
|
||||||
|
|
||||||
return function()
|
return function(test: typeof(frktest.test))
|
||||||
test.suite("Basic tests", function()
|
test.suite("Basic tests", function()
|
||||||
test.case("Semver creates valid version objects", function()
|
test.case("Semver creates valid version objects", function()
|
||||||
local res = Semver.parse("1.2.3-beta.1")
|
local res = Semver.parse("1.2.3-beta.1")
|
||||||
check.is_true(res:isOk())
|
check.is_true(res:isOk())
|
||||||
|
|
||||||
local version = res:unwrap()
|
local version = res:unwrap()
|
||||||
check.equal(version.major, 1)
|
check.equal(version.major, 1)
|
||||||
check.equal(version.minor, 2)
|
check.equal(version.minor, 2)
|
||||||
check.equal(version.patch, 3)
|
check.equal(version.patch, 3)
|
||||||
end)
|
end)
|
||||||
|
|
||||||
test.case("Semver handles prerelease versions", function()
|
test.case("Semver handles prerelease versions", function()
|
||||||
local res = Semver.parse("1.2.3-beta.1")
|
local res = Semver.parse("1.2.3-beta.1")
|
||||||
check.is_true(res:isOk())
|
check.is_true(res:isOk())
|
||||||
|
|
||||||
local version = res:unwrap()
|
local version = res:unwrap()
|
||||||
check.equal(version.major, 1)
|
check.equal(version.major, 1)
|
||||||
check.equal(version.minor, 2)
|
check.equal(version.minor, 2)
|
||||||
check.equal(version.patch, 3)
|
check.equal(version.patch, 3)
|
||||||
|
|
||||||
check.table.equal(
|
check.table.equal(
|
||||||
version.prerelease,
|
version.prerelease,
|
||||||
Option.Some({
|
Option.Some({
|
||||||
type = "beta",
|
type = "beta",
|
||||||
ordinal = Option.Some(1),
|
ordinal = Option.Some(1),
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
end)
|
end)
|
||||||
|
|
||||||
test.case("Semver handles build metadata", function()
|
test.case("Semver handles build metadata", function()
|
||||||
local res = Semver.parse("1.2.3+build.123")
|
local res = Semver.parse("1.2.3+build.123")
|
||||||
check.is_true(res:isOk())
|
check.is_true(res:isOk())
|
||||||
|
|
||||||
local version = res:unwrap()
|
local version = res:unwrap()
|
||||||
|
|
||||||
check.equal(version.major, 1)
|
check.equal(version.major, 1)
|
||||||
check.equal(version.minor, 2)
|
check.equal(version.minor, 2)
|
||||||
check.equal(version.patch, 3)
|
check.equal(version.patch, 3)
|
||||||
check.table.equal(version.buildMetadata, Option.Some("build.123"))
|
check.table.equal(version.buildMetadata, Option.Some("build.123"))
|
||||||
end)
|
end)
|
||||||
|
|
||||||
test.case("Semver handles prerelease versions with build metadata", function()
|
test.case("Semver handles prerelease versions with build metadata", function()
|
||||||
local res = Semver.parse("1.2.3-beta.1+build.123")
|
local res = Semver.parse("1.2.3-beta.1+build.123")
|
||||||
check.is_true(res:isOk())
|
check.is_true(res:isOk())
|
||||||
|
|
||||||
local version = res:unwrap()
|
local version = res:unwrap()
|
||||||
check.equal(version.major, 1)
|
check.equal(version.major, 1)
|
||||||
check.equal(version.minor, 2)
|
check.equal(version.minor, 2)
|
||||||
check.equal(version.patch, 3)
|
check.equal(version.patch, 3)
|
||||||
|
|
||||||
check.table.equal(
|
check.table.equal(
|
||||||
version.prerelease,
|
version.prerelease,
|
||||||
Option.Some({
|
Option.Some({
|
||||||
type = "beta",
|
type = "beta",
|
||||||
ordinal = Option.Some(1),
|
ordinal = Option.Some(1),
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
|
|
||||||
check.table.equal(version.buildMetadata, Option.Some("build.123"))
|
check.table.equal(version.buildMetadata, Option.Some("build.123"))
|
||||||
end)
|
end)
|
||||||
end)
|
end)
|
||||||
end
|
end
|
||||||
|
|
82
tests/tostring.luau
Normal file
82
tests/tostring.luau
Normal file
|
@ -0,0 +1,82 @@
|
||||||
|
local frktest = require("@pkg/frktest")
|
||||||
|
local check = frktest.assert.check
|
||||||
|
|
||||||
|
local Option = require("../luau_packages/option")
|
||||||
|
type Option<T> = Option.Option<T>
|
||||||
|
|
||||||
|
local Semver = require("../lib")
|
||||||
|
|
||||||
|
return function(test: typeof(frktest.test))
|
||||||
|
test.suite("Stringification tests", function()
|
||||||
|
test.case("A version constructed with new() when stringified should match expected string", function()
|
||||||
|
local versionMap: { [string]: Semver.Version } = {
|
||||||
|
["1.2.3"] = { major = 1, minor = 2, patch = 3, prerelease = Option.None, buildMetadata = Option.None },
|
||||||
|
["1.0.0-alpha"] = {
|
||||||
|
major = 1,
|
||||||
|
minor = 0,
|
||||||
|
patch = 0,
|
||||||
|
prerelease = Option.Some({ type = "alpha" :: Semver.PreleaseType, ordinal = Option.None }),
|
||||||
|
buildMetadata = Option.None,
|
||||||
|
},
|
||||||
|
["2.3.4-beta.1"] = {
|
||||||
|
major = 2,
|
||||||
|
minor = 3,
|
||||||
|
patch = 4,
|
||||||
|
prerelease = Option.Some({
|
||||||
|
type = "beta" :: Semver.PreleaseType,
|
||||||
|
ordinal = Option.Some(1),
|
||||||
|
}),
|
||||||
|
buildMetadata = Option.None,
|
||||||
|
},
|
||||||
|
["3.0.0-rc.1+build.123"] = {
|
||||||
|
major = 3,
|
||||||
|
minor = 0,
|
||||||
|
patch = 0,
|
||||||
|
prerelease = Option.Some({
|
||||||
|
type = "rc" :: Semver.PreleaseType,
|
||||||
|
ordinal = Option.Some(1),
|
||||||
|
}),
|
||||||
|
buildMetadata = Option.Some("build.123"),
|
||||||
|
},
|
||||||
|
["4.5.6+sha.xyz"] = {
|
||||||
|
major = 4,
|
||||||
|
minor = 5,
|
||||||
|
patch = 6,
|
||||||
|
prerelease = Option.None,
|
||||||
|
buildMetadata = Option.Some("sha.xyz"),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
-- FIXME: unknown usage here is because these types are too complex for
|
||||||
|
-- Luau to typecheck properly, we cast it manually since the above
|
||||||
|
-- map is typed properly anyway
|
||||||
|
for expectedString, version: unknown in versionMap do
|
||||||
|
local constructed = Semver.new(version :: Semver.Version)
|
||||||
|
local stringified = tostring(constructed)
|
||||||
|
|
||||||
|
check.equal(stringified, expectedString)
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
|
||||||
|
test.case("A parsed version when stringified should not change in roundtrip", function()
|
||||||
|
local versions = {
|
||||||
|
"1.2.3",
|
||||||
|
"1.0.0-alpha",
|
||||||
|
"2.3.4-beta.1",
|
||||||
|
"3.0.0-rc.1+build.123",
|
||||||
|
"4.5.6+sha.xyz",
|
||||||
|
"5.0.0-alpha.1+build.999",
|
||||||
|
"6.7.8-beta.2+exp.sha.5114f85",
|
||||||
|
"7.0.0-alpha.1",
|
||||||
|
"9.9.9+20230615",
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, version in versions do
|
||||||
|
local parsed = Semver.parse(version):unwrap()
|
||||||
|
local stringified = tostring(parsed)
|
||||||
|
|
||||||
|
check.equal(stringified, version)
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
end)
|
||||||
|
end
|
Loading…
Add table
Reference in a new issue