aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorWilliam Boman <william@redwill.se>2022-10-05 20:33:21 +0200
committerGitHub <noreply@github.com>2022-10-05 20:33:21 +0200
commit738684097dfdd9a4a67cd217b0beda3e169bd85d (patch)
treed2eb8485dedcac66061b0d99ad7f810828911073
parenttest(cargo): stub crates.io http call (#508) (diff)
downloadmason-738684097dfdd9a4a67cd217b0beda3e169bd85d.tar
mason-738684097dfdd9a4a67cd217b0beda3e169bd85d.tar.gz
mason-738684097dfdd9a4a67cd217b0beda3e169bd85d.tar.bz2
mason-738684097dfdd9a4a67cd217b0beda3e169bd85d.tar.lz
mason-738684097dfdd9a4a67cd217b0beda3e169bd85d.tar.xz
mason-738684097dfdd9a4a67cd217b0beda3e169bd85d.tar.zst
mason-738684097dfdd9a4a67cd217b0beda3e169bd85d.zip
feat(cargo): improve handling of git-based crates (#512)
This is all pretty overkill, especially considering the small amount of packages based on git-based crates.
-rw-r--r--lua/mason-core/managers/cargo/init.lua167
-rw-r--r--lua/mason-core/managers/github/client.lua26
-rw-r--r--lua/mason-core/optional.lua10
-rw-r--r--lua/mason-core/result.lua9
-rw-r--r--lua/mason-registry/flux-lsp/init.lua7
-rw-r--r--lua/mason-registry/move-analyzer/init.lua4
-rw-r--r--lua/mason-registry/wgsl-analyzer/init.lua9
-rw-r--r--tests/mason-core/managers/cargo_spec.lua172
-rw-r--r--tests/mason-core/managers/github_client_spec.lua21
-rw-r--r--tests/mason-core/optional_spec.lua17
-rw-r--r--tests/mason-core/result_spec.lua13
11 files changed, 369 insertions, 86 deletions
diff --git a/lua/mason-core/managers/cargo/init.lua b/lua/mason-core/managers/cargo/init.lua
index b8cd4fa7..8890daf6 100644
--- a/lua/mason-core/managers/cargo/init.lua
+++ b/lua/mason-core/managers/cargo/init.lua
@@ -5,6 +5,8 @@ local a = require "mason-core.async"
local Optional = require "mason-core.optional"
local installer = require "mason-core.installer"
local client = require "mason-core.managers.cargo.client"
+local github = require "mason-core.managers.github"
+local github_client = require "mason-core.managers.github.client"
local _ = require "mason-core.functional"
local get_bin_path = _.compose(path.concat, function(executable)
@@ -23,32 +25,50 @@ local M = {}
---@async
---@param crate string The crate to install.
----@param opts {git: boolean | string, features: string?, bin: string[]? }?
+---@param opts { git: { url: string, tag: boolean? }, features: string?, bin: string[]? }?
function M.crate(crate, opts)
return function()
- M.install(crate, opts).with_receipt()
+ if opts and opts.git and opts.git.tag then
+ local ctx = installer.context()
+ local repo = assert(opts.git.url:match "^https://github%.com/(.+)$", "git url needs to be github.com")
+ local source = github.tag { repo = repo }
+ source.with_receipt()
+ ctx.requested_version = Optional.of(source.tag)
+ M.install(crate, opts)
+ else
+ M.install(crate, opts).with_receipt()
+ end
end
end
---@async
---@param crate string The crate to install.
----@param opts {git: boolean | string, features: string?, bin: string[]? }?
+---@param opts { git: { url: string, tag: boolean? }, features: string?, bin: string[]? }?
function M.install(crate, opts)
local ctx = installer.context()
opts = opts or {}
- ctx.requested_version:if_present(function()
- assert(not opts.git, "Providing a version when installing a git crate is not allowed.")
- end)
- ---@type string | string[]
- local final_crate = crate
+ local version
if opts.git then
- final_crate = { "--git" }
- if type(opts.git) == "string" then
- table.insert(final_crate, opts.git)
+ if opts.git.tag then
+ assert(ctx.requested_version:is_present(), "version is required when installing tagged git crate.")
end
- table.insert(final_crate, crate)
+ version = ctx.requested_version
+ :map(function(version)
+ if opts.git.tag then
+ return { "--tag", version }
+ else
+ return { "--rev", version }
+ end
+ end)
+ :or_else(vim.NIL)
+ else
+ version = ctx.requested_version
+ :map(function(version)
+ return { "--version", version }
+ end)
+ :or_else(vim.NIL)
end
ctx.spawn.cargo {
@@ -56,13 +76,10 @@ function M.install(crate, opts)
"--root",
".",
"--locked",
- ctx.requested_version
- :map(function(version)
- return { "--version", version }
- end)
- :or_else(vim.NIL),
+ version,
+ opts.git and { "--git", opts.git.url } or vim.NIL,
opts.features and { "--features", opts.features } or vim.NIL,
- final_crate,
+ crate,
}
if opts.bin then
@@ -76,42 +93,39 @@ function M.install(crate, opts)
}
end
+---@alias InstalledCrate { name: string, version: string, github_ref: { owner: string, repo: string, ref: string }? }
+
+---@param line string
+---@return InstalledCrate? crate
+local function parse_installed_crate(line)
+ local name, version, context = line:match "^(.+)%s+v([^%s:]+) ?(.*):$"
+ if context then
+ local owner, repo, ref = context:match "^%(https://github%.com/(.+)/([^?]+).*#(.+)%)$"
+ if ref then
+ return { name = name, version = ref, github_ref = { owner = owner, repo = repo, ref = ref } }
+ end
+ end
+ if name and version then
+ return { name = name, version = version }
+ end
+end
+
---@param output string The `cargo install --list` output.
----@return table<string, string> # Key is the crate name, value is its version.
+---@return table<string, InstalledCrate> # Key is the crate name, value is its version.
function M.parse_installed_crates(output)
local installed_crates = {}
for _, line in ipairs(vim.split(output, "\n")) do
- local name, version = line:match "^(.+)%s+v([.%S]+)[%s:]"
- if name and version then
- installed_crates[name] = version
+ local installed_crate = parse_installed_crate(line)
+ if installed_crate then
+ installed_crates[installed_crate.name] = installed_crate
end
end
return installed_crates
end
---@async
----@param receipt InstallReceipt<InstallReceiptPackageSource>
---@param install_dir string
-function M.check_outdated_primary_package(receipt, install_dir)
- return M.get_installed_primary_package_version(receipt, install_dir):map_catching(function(installed_version)
- ---@type CrateResponse
- local crate_response = client.fetch_crate(receipt.primary_source.package):get_or_throw()
- if installed_version ~= crate_response.crate.max_stable_version then
- return {
- name = receipt.primary_source.package,
- current_version = installed_version,
- latest_version = crate_response.crate.max_stable_version,
- }
- else
- error "Primary package is not outdated."
- end
- end)
-end
-
----@async
----@param receipt InstallReceipt<InstallReceiptPackageSource>
----@param install_dir string
-function M.get_installed_primary_package_version(receipt, install_dir)
+local function get_installed_crates(install_dir)
return spawn
.cargo({
"install",
@@ -121,13 +135,68 @@ function M.get_installed_primary_package_version(receipt, install_dir)
cwd = install_dir,
})
:map_catching(function(result)
- local installed_crates = M.parse_installed_crates(result.stdout)
- if vim.in_fast_event() then
- a.scheduler() -- needed because vim.fn.* call
- end
- local pkg = vim.fn.fnamemodify(receipt.primary_source.package, ":t")
- return Optional.of_nilable(installed_crates[pkg]):or_else_throw "Failed to find cargo package version."
+ return M.parse_installed_crates(result.stdout)
end)
end
+---@async
+---@param receipt InstallReceipt<InstallReceiptPackageSource>
+---@param install_dir string
+function M.check_outdated_primary_package(receipt, install_dir)
+ if vim.in_fast_event() then
+ a.scheduler()
+ end
+ local crate_name = vim.fn.fnamemodify(receipt.primary_source.package, ":t")
+ return get_installed_crates(install_dir)
+ :ok()
+ :map(_.prop(crate_name))
+ :map(
+ ---@param installed_crate InstalledCrate
+ function(installed_crate)
+ if installed_crate.github_ref then
+ ---@type GitHubCommit
+ local latest_commit = github_client
+ .fetch_commits(
+ ("%s/%s"):format(installed_crate.github_ref.owner, installed_crate.github_ref.repo),
+ { page = 1, per_page = 1 }
+ )
+ :get_or_throw("Failed to fetch latest commits.")[1]
+ if not vim.startswith(latest_commit.sha, installed_crate.github_ref.ref) then
+ return {
+ name = receipt.primary_source.package,
+ current_version = installed_crate.github_ref.ref,
+ latest_version = latest_commit.sha,
+ }
+ end
+ else
+ ---@type CrateResponse
+ local crate_response = client.fetch_crate(crate_name):get_or_throw()
+ if installed_crate.version ~= crate_response.crate.max_stable_version then
+ return {
+ name = receipt.primary_source.package,
+ current_version = installed_crate.version,
+ latest_version = crate_response.crate.max_stable_version,
+ }
+ end
+ end
+ end
+ )
+ :ok_or(_.always "Primary package is not outdated.")
+end
+
+---@async
+---@param receipt InstallReceipt<InstallReceiptPackageSource>
+---@param install_dir string
+function M.get_installed_primary_package_version(receipt, install_dir)
+ return get_installed_crates(install_dir):map(function(pkgs)
+ if vim.in_fast_event() then
+ a.scheduler()
+ end
+ local pkg = vim.fn.fnamemodify(receipt.primary_source.package, ":t")
+ return Optional.of_nilable(pkgs[pkg])
+ :map(_.prop "version")
+ :or_else_throw "Failed to find cargo package version."
+ end)
+end
+
return M
diff --git a/lua/mason-core/managers/github/client.lua b/lua/mason-core/managers/github/client.lua
index 8f4545e8..788b402b 100644
--- a/lua/mason-core/managers/github/client.lua
+++ b/lua/mason-core/managers/github/client.lua
@@ -8,10 +8,16 @@ local M = {}
---@alias GitHubReleaseAsset {url: string, id: integer, name: string, browser_download_url: string, created_at: string, updated_at: string, size: integer, download_count: integer}
---@alias GitHubRelease {tag_name: string, prerelease: boolean, draft: boolean, assets:GitHubReleaseAsset[]}
---@alias GitHubTag {name: string}
+---@alias GitHubCommit {sha: string}
---@param path string
+---@param opts { params: table<string, any>? }?
---@return Result # JSON decoded response.
-local function api_call(path)
+local function api_call(path, opts)
+ if opts and opts.params then
+ local params = _.join("&", _.map(_.join "=", _.sort_by(_.head, _.to_pairs(opts.params))))
+ path = ("%s?%s"):format(path, params)
+ end
return spawn
.gh({ "api", path, env = { CLICOLOR_FORCE = 0 } })
:map(_.prop "stdout")
@@ -21,6 +27,8 @@ local function api_call(path)
:map_catching(vim.json.decode)
end
+M.api_call = api_call
+
---@async
---@param repo string The GitHub repo ("username/repo").
---@return Result # Result<GitHubRelease[]>
@@ -105,6 +113,22 @@ function M.fetch_latest_tag(repo)
:map(_.prop "tag")
end
+---@async
+---@param repo string The GitHub repo ("username/repo").
+---@param opts { page: integer?, per_page: integer? }?
+---@return Result # Result<GitHubCommit[]>
+function M.fetch_commits(repo, opts)
+ local path = ("repos/%s/commits"):format(repo)
+ return api_call(path, {
+ params = {
+ page = opts and opts.page or 1,
+ per_page = opts and opts.per_page or 30,
+ },
+ }):map_err(function()
+ return ("Failed to fetch commits for GitHub repository %s."):format(repo)
+ end)
+end
+
---@alias GitHubRateLimit {limit: integer, remaining: integer, reset: integer, used: integer}
---@alias GitHubRateLimitResponse {resources: { core: GitHubRateLimit }}
diff --git a/lua/mason-core/optional.lua b/lua/mason-core/optional.lua
index 65317b7f..6eb0719e 100644
--- a/lua/mason-core/optional.lua
+++ b/lua/mason-core/optional.lua
@@ -105,4 +105,14 @@ function Optional:is_present()
return self._value ~= nil
end
+---@param err fun(): any
+function Optional:ok_or(err)
+ local Result = require "mason-core.result"
+ if self:is_present() then
+ return Result.success(self:get())
+ else
+ return Result.failure(err())
+ end
+end
+
return Optional
diff --git a/lua/mason-core/result.lua b/lua/mason-core/result.lua
index f2efd36a..95c69afa 100644
--- a/lua/mason-core/result.lua
+++ b/lua/mason-core/result.lua
@@ -138,6 +138,15 @@ function Result:on_success(fn)
return self
end
+function Result:ok()
+ local Optional = require "mason-core.optional"
+ if self:is_success() then
+ return Optional.of(self.value)
+ else
+ return Optional.empty()
+ end
+end
+
---@param fn fun(): any
---@return Result
function Result.run_catching(fn)
diff --git a/lua/mason-registry/flux-lsp/init.lua b/lua/mason-registry/flux-lsp/init.lua
index 2568f951..afef8c72 100644
--- a/lua/mason-registry/flux-lsp/init.lua
+++ b/lua/mason-registry/flux-lsp/init.lua
@@ -7,8 +7,11 @@ return Pkg.new {
homepage = "https://github.com/influxdata/flux-lsp",
languages = { Pkg.Lang.Flux },
categories = { Pkg.Cat.LSP },
- install = cargo.crate("https://github.com/influxdata/flux-lsp", {
- git = true,
+ install = cargo.crate("flux-lsp", {
+ git = {
+ url = "https://github.com/influxdata/flux-lsp",
+ tag = true,
+ },
bin = { "flux-lsp" },
}),
}
diff --git a/lua/mason-registry/move-analyzer/init.lua b/lua/mason-registry/move-analyzer/init.lua
index 7ef589d7..e7e15093 100644
--- a/lua/mason-registry/move-analyzer/init.lua
+++ b/lua/mason-registry/move-analyzer/init.lua
@@ -8,7 +8,9 @@ return Pkg.new {
languages = { Pkg.Lang.Move },
categories = { Pkg.Cat.LSP },
install = cargo.crate("move-analyzer", {
- git = "https://github.com/move-language/move",
+ git = {
+ url = "https://github.com/move-language/move",
+ },
bin = { "move-analyzer" },
}),
}
diff --git a/lua/mason-registry/wgsl-analyzer/init.lua b/lua/mason-registry/wgsl-analyzer/init.lua
index 965d4a01..c5ff2bb8 100644
--- a/lua/mason-registry/wgsl-analyzer/init.lua
+++ b/lua/mason-registry/wgsl-analyzer/init.lua
@@ -1,16 +1,17 @@
local Pkg = require "mason-core.package"
local cargo = require "mason-core.managers.cargo"
-local github_url = "https://github.com/wgsl-analyzer/wgsl-analyzer"
-
return Pkg.new {
name = "wgsl-analyzer",
desc = [[A language server implementation for the WGSL shading language]],
- homepage = github_url,
+ homepage = "https://github.com/wgsl-analyzer/wgsl-analyzer",
languages = { Pkg.Lang.WGSL },
categories = { Pkg.Cat.LSP },
install = cargo.crate("wgsl_analyzer", {
- git = github_url,
+ git = {
+ url = "https://github.com/wgsl-analyzer/wgsl-analyzer",
+ tag = true,
+ },
bin = { "wgsl_analyzer" },
}),
}
diff --git a/tests/mason-core/managers/cargo_spec.lua b/tests/mason-core/managers/cargo_spec.lua
index ddcb7300..27de7253 100644
--- a/tests/mason-core/managers/cargo_spec.lua
+++ b/tests/mason-core/managers/cargo_spec.lua
@@ -4,7 +4,9 @@ local match = require "luassert.match"
local mock = require "luassert.mock"
local installer = require "mason-core.installer"
local cargo = require "mason-core.managers.cargo"
-local client = require "mason-core.managers.cargo.client"
+local github = require "mason-core.managers.github"
+local cargo_client = require "mason-core.managers.cargo.client"
+local github_client = require "mason-core.managers.github.client"
local Result = require "mason-core.result"
local spawn = require "mason-core.spawn"
local path = require "mason-core.path"
@@ -23,6 +25,7 @@ describe("cargo manager", function()
".",
"--locked",
{ "--version", "42.13.37" },
+ vim.NIL, -- --git
vim.NIL, -- --features
"my-crate",
}
@@ -34,16 +37,17 @@ describe("cargo manager", function()
async_test(function()
local handle = InstallHandleGenerator "dummy"
local ctx = InstallContextGenerator(handle)
- installer.run_installer(ctx, cargo.crate("https://my-crate.git", { git = true }))
+ installer.run_installer(ctx, cargo.crate("my-crate", { git = { url = "https://my-crate.git" } }))
assert.spy(ctx.spawn.cargo).was_called(1)
assert.spy(ctx.spawn.cargo).was_called_with {
"install",
"--root",
".",
"--locked",
- vim.NIL,
- vim.NIL, -- --features
+ vim.NIL, -- version
{ "--git", "https://my-crate.git" },
+ vim.NIL, -- --features
+ "my-crate",
}
end)
)
@@ -53,16 +57,17 @@ describe("cargo manager", function()
async_test(function()
local handle = InstallHandleGenerator "dummy"
local ctx = InstallContextGenerator(handle)
- installer.run_installer(ctx, cargo.crate("crate-name", { git = "https://my-crate.git" }))
+ installer.run_installer(ctx, cargo.crate("crate-name", { git = { url = "https://my-crate.git" } }))
assert.spy(ctx.spawn.cargo).was_called(1)
assert.spy(ctx.spawn.cargo).was_called_with {
"install",
"--root",
".",
"--locked",
- vim.NIL,
+ vim.NIL, -- version
+ { "--git", "https://my-crate.git" },
vim.NIL, -- --features
- { "--git", "https://my-crate.git", "crate-name" },
+ "crate-name",
}
end)
)
@@ -80,6 +85,7 @@ describe("cargo manager", function()
".",
"--locked",
{ "--version", "42.13.37" },
+ vim.NIL, -- --git
{ "--features", "lsp" },
"my-crate",
}
@@ -87,20 +93,32 @@ describe("cargo manager", function()
)
it(
- "should not allow combining version with git crate",
+ "should target tagged git crates",
async_test(function()
local handle = InstallHandleGenerator "dummy"
+ stub(github, "tag")
+ github.tag.returns { tag = "v2.1.1", with_receipt = mockx.just_runs }
local ctx = InstallContextGenerator(handle, { requested_version = "42.13.37" })
- local err = assert.has_error(function()
- installer.run_installer(
- ctx,
- cargo.crate("my-crate", {
- git = true,
- })
- )
- end)
- assert.equals("Providing a version when installing a git crate is not allowed.", err)
- assert.spy(ctx.spawn.cargo).was_called(0)
+ installer.run_installer(
+ ctx,
+ cargo.crate("my-crate", {
+ git = {
+ url = "https://github.com/crate/my-crate",
+ tag = true,
+ },
+ features = "lsp",
+ })
+ )
+ assert.spy(ctx.spawn.cargo).was_called_with {
+ "install",
+ "--root",
+ ".",
+ "--locked",
+ { "--tag", "v2.1.1" },
+ { "--git", "https://github.com/crate/my-crate" }, -- --git
+ { "--features", "lsp" },
+ "my-crate",
+ }
end)
)
@@ -122,13 +140,22 @@ describe("cargo version check", function()
it("parses cargo installed packages output", function()
assert.same(
{
- ["bat"] = "0.18.3",
- ["exa"] = "0.10.1",
- ["git-select-branch"] = "0.1.1",
- ["hello_world"] = "0.0.1",
- ["rust-analyzer"] = "0.0.0",
- ["stylua"] = "0.11.2",
- ["zoxide"] = "0.5.0",
+ ["bat"] = { name = "bat", version = "0.18.3" },
+ ["exa"] = { name = "exa", version = "0.10.1" },
+ ["git-select-branch"] = { name = "git-select-branch", version = "0.1.1" },
+ ["hello_world"] = { name = "hello_world", version = "0.0.1" },
+ ["rust-analyzer"] = {
+ name = "rust-analyzer",
+ version = "187bee0b",
+ github_ref = { owner = "rust-lang", repo = "rust-analyzer", ref = "187bee0b" },
+ },
+ ["move-analyzer"] = {
+ name = "move-analyzer",
+ version = "3cef7fa8",
+ github_ref = { owner = "move-language", repo = "move", ref = "3cef7fa8" },
+ },
+ ["stylua"] = { name = "stylua", version = "0.11.2" },
+ ["zoxide"] = { name = "zoxide", version = "0.5.0" },
},
cargo.parse_installed_crates [[bat v0.18.3:
bat
@@ -138,7 +165,9 @@ git-select-branch v0.1.1:
git-select-branch
hello_world v0.0.1 (/private/var/folders/ky/s6yyhm_d24d0jsrql4t8k4p40000gn/T/tmp.LGbguATJHj):
hello_world
-rust-analyzer v0.0.0 (/private/var/folders/ky/s6yyhm_d24d0jsrql4t8k4p40000gn/T/tmp.YlsHeA9JVL/crates/rust-analyzer):
+move-analyzer v1.0.0 (https://github.com/move-language/move#3cef7fa8):
+ move-analyzer
+rust-analyzer v0.0.0 (https://github.com/rust-lang/rust-analyzer?tag=2022-09-19#187bee0b):
rust-analyzer
stylua v0.11.2:
stylua
@@ -178,7 +207,7 @@ zoxide v0.5.0:
cwd = path.package_prefix "dummy",
})
assert.is_true(result:is_success())
- assert.equals("0.8.8", result:get_or_nil())
+ assert.equals("4e452f07", result:get_or_nil())
spawn.cargo = nil
end)
@@ -194,8 +223,8 @@ zoxide v0.5.0:
]],
}
end)
- stub(client, "fetch_crate")
- client.fetch_crate.returns(Result.success {
+ stub(cargo_client, "fetch_crate")
+ cargo_client.fetch_crate.returns(Result.success {
crate = {
id = "lelwel",
max_stable_version = "0.4.2",
@@ -232,4 +261,89 @@ zoxide v0.5.0:
spawn.cargo = nil
end)
)
+
+ it(
+ "should recognize up-to-date crates",
+ async_test(function()
+ spawn.cargo = spy.new(function()
+ return Result.success {
+ stdout = [[lelwel v0.4.0:
+ lelwel-ls
+]],
+ }
+ end)
+ stub(cargo_client, "fetch_crate")
+ cargo_client.fetch_crate.returns(Result.success {
+ crate = {
+ id = "lelwel",
+ max_stable_version = "0.4.0",
+ max_version = "0.4.0",
+ newest_version = "0.4.0",
+ },
+ })
+
+ local result = cargo.check_outdated_primary_package(
+ mock.new {
+ primary_source = mock.new {
+ type = "cargo",
+ package = "lelwel",
+ },
+ },
+ path.package_prefix "dummy"
+ )
+
+ assert.is_true(result:is_failure())
+ assert.equals("Primary package is not outdated.", result:err_or_nil())
+ spawn.cargo = nil
+ end)
+ )
+
+ it(
+ "should return outdated primary package from git source",
+ async_test(function()
+ spawn.cargo = spy.new(function()
+ return Result.success {
+ stdout = [[move-analyzer v1.0.0 (https://github.com/move-language/move#3cef7fa8):
+ move-analyzer
+]],
+ }
+ end)
+
+ stub(github_client, "fetch_commits")
+ github_client.fetch_commits
+ .on_call_with("move-language/move", { page = 1, per_page = 1 })
+ .returns(Result.success {
+ {
+ sha = "b243f1fb",
+ },
+ })
+
+ local result = cargo.check_outdated_primary_package(
+ mock.new {
+ primary_source = mock.new {
+ type = "cargo",
+ package = "move-analyzer",
+ },
+ },
+ path.package_prefix "dummy"
+ )
+
+ assert.spy(spawn.cargo).was_called(1)
+ assert.spy(spawn.cargo).was_called_with(match.tbl_containing {
+ "install",
+ "--list",
+ "--root",
+ ".",
+ cwd = path.package_prefix "dummy",
+ })
+ assert.is_true(result:is_success())
+ assert.is_true(match.tbl_containing {
+ current_version = "3cef7fa8",
+ latest_version = "b243f1fb",
+ name = "move-analyzer",
+ }(result:get_or_nil()))
+
+ spawn.cargo = nil
+ end)
+ )
end)
diff --git a/tests/mason-core/managers/github_client_spec.lua b/tests/mason-core/managers/github_client_spec.lua
index 11b06880..8dec1ca8 100644
--- a/tests/mason-core/managers/github_client_spec.lua
+++ b/tests/mason-core/managers/github_client_spec.lua
@@ -1,4 +1,8 @@
+local spy = require "luassert.spy"
+local stub = require "luassert.stub"
local client = require "mason-core.managers.github.client"
+local spawn = require "mason-core.spawn"
+local Result = require "mason-core.result"
describe("github client", function()
---@type GitHubRelease
@@ -38,4 +42,21 @@ describe("github client", function()
assert.is_false(predicate(stub_release { prerelease = true }))
assert.is_false(predicate(stub_release { draft = true }))
end)
+
+ it("should provide query parameters in api calls", function()
+ stub(spawn, "gh")
+ spawn.gh.returns(Result.success { stdout = "response data" })
+ client.api_call("repos/some/repo", {
+ params = {
+ page = 23,
+ page_limit = 82,
+ },
+ })
+ assert.spy(spawn.gh).was_called(1)
+ assert.spy(spawn.gh).was_called_with {
+ "api",
+ "repos/some/repo?page=23&page_limit=82",
+ env = { CLICOLOR_FORCE = 0 },
+ }
+ end)
end)
diff --git a/tests/mason-core/optional_spec.lua b/tests/mason-core/optional_spec.lua
index 4e6b1325..b1a4f41a 100644
--- a/tests/mason-core/optional_spec.lua
+++ b/tests/mason-core/optional_spec.lua
@@ -1,4 +1,5 @@
local Optional = require "mason-core.optional"
+local Result = require "mason-core.result"
local spy = require "luassert.spy"
describe("Optional.of_nilable", function()
@@ -75,3 +76,19 @@ describe("Optional.if_not_present()", function()
assert.spy(present).was_called(1)
end)
end)
+
+describe("Optional.ok_or()", function()
+ it("should return success variant if non-empty", function()
+ local result = Optional.of_nilable("Hello world!"):ok_or()
+ assert.is_true(getmetatable(result) == Result)
+ assert.equals("Hello world!", result:get_or_nil())
+ end)
+
+ it("should return failure variant if empty", function()
+ local result = Optional.empty():ok_or(function()
+ return "I'm empty."
+ end)
+ assert.is_true(getmetatable(result) == Result)
+ assert.equals("I'm empty.", result:err_or_nil())
+ end)
+end)
diff --git a/tests/mason-core/result_spec.lua b/tests/mason-core/result_spec.lua
index 777e268b..737d8c56 100644
--- a/tests/mason-core/result_spec.lua
+++ b/tests/mason-core/result_spec.lua
@@ -1,6 +1,7 @@
local Result = require "mason-core.result"
local match = require "luassert.match"
local spy = require "luassert.spy"
+local Optional = require "mason-core.optional"
describe("result", function()
it("should create success", function()
@@ -140,4 +141,16 @@ describe("result", function()
assert.spy(on_success).was_called(1)
assert.spy(on_success).was_called_with "Oh noes"
end)
+
+ it("should convert success variants to non-empty optionals", function()
+ local opt = Result.success("Hello world!"):ok()
+ assert.is_true(getmetatable(opt) == Optional)
+ assert.equals("Hello world!", opt:get())
+ end)
+
+ it("should convert failure variants to empty optionals", function()
+ local opt = Result.failure("Hello world!"):ok()
+ assert.is_true(getmetatable(opt) == Optional)
+ assert.is_false(opt:is_present())
+ end)
end)