aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--README.md9
-rw-r--r--doc/mason.txt9
-rw-r--r--lua/mason-core/functional/function.lua5
-rw-r--r--lua/mason-core/functional/init.lua3
-rw-r--r--lua/mason-core/functional/list.lua7
-rw-r--r--lua/mason-core/functional/string.lua4
-rw-r--r--lua/mason-core/managers/github/client.lua13
-rw-r--r--lua/mason-core/managers/npm/init.lua4
-rw-r--r--lua/mason-core/managers/pip3/init.lua48
-rw-r--r--lua/mason-core/optional.lua8
-rw-r--r--lua/mason-core/providers/init.lua77
-rw-r--r--lua/mason-core/spawn.lua2
-rw-r--r--lua/mason-registry/api.lua9
-rw-r--r--lua/mason/providers/client/gh.lua43
-rw-r--r--lua/mason/providers/client/init.lua6
-rw-r--r--lua/mason/providers/client/npm.lua16
-rw-r--r--lua/mason/providers/client/pypi.lua52
-rw-r--r--lua/mason/providers/registry-api/init.lua40
-rw-r--r--lua/mason/settings.lua9
-rw-r--r--tests/mason-core/functional/function_spec.lua7
-rw-r--r--tests/mason-core/functional/list_spec.lua8
-rw-r--r--tests/mason-core/functional/string_spec.lua5
-rw-r--r--tests/mason-core/managers/pip3_spec.lua30
-rw-r--r--tests/mason-core/providers/provider_spec.lua61
24 files changed, 419 insertions, 56 deletions
diff --git a/README.md b/README.md
index f21aa12e..0c0cfaba 100644
--- a/README.md
+++ b/README.md
@@ -187,6 +187,15 @@ local DEFAULT_SETTINGS = {
download_url_template = "https://github.com/%s/releases/download/%s/%s",
},
+ -- The provider implementations to use for resolving package metadata (latest version, available versions, etc.).
+ -- Accepts multiple entries, where later entries will be used as fallback should prior providers fail.
+ -- Builtin providers are:
+ -- - mason.providers.registry-api (default) - uses the https://api.mason-registry.dev API
+ -- - mason.providers.client - uses only client-side tooling to resolve metadata
+ providers = {
+ "mason.providers.registry-api",
+ },
+
ui = {
-- Whether to automatically check for new versions when opening the :Mason window.
check_outdated_packages_on_open = true,
diff --git a/doc/mason.txt b/doc/mason.txt
index f75b36c4..3a3a9ac6 100644
--- a/doc/mason.txt
+++ b/doc/mason.txt
@@ -239,6 +239,15 @@ Example:
download_url_template = "https://github.com/%s/releases/download/%s/%s",
},
+ -- The provider implementations to use for resolving package metadata (latest version, available versions, etc.).
+ -- Accepts multiple entries, where later entries will be used as fallback should prior providers fail.
+ -- Builtin providers are:
+ -- - mason.providers.registry-api (default) - uses the https://api.mason-registry.dev API
+ -- - mason.providers.client - uses only client-side tooling to resolve metadata
+ providers = {
+ "mason.providers.registry-api",
+ },
+
ui = {
-- Whether to automatically check for new versions when opening the :Mason window.
check_outdated_packages_on_open = true,
diff --git a/lua/mason-core/functional/function.lua b/lua/mason-core/functional/function.lua
index c6d460a5..f1475229 100644
--- a/lua/mason-core/functional/function.lua
+++ b/lua/mason-core/functional/function.lua
@@ -89,4 +89,9 @@ _.lazy = function(fn)
end
end
+_.tap = _.curryN(function(fn, value)
+ fn(value)
+ return value
+end, 2)
+
return _
diff --git a/lua/mason-core/functional/init.lua b/lua/mason-core/functional/init.lua
index ed502a4b..04ef1534 100644
--- a/lua/mason-core/functional/init.lua
+++ b/lua/mason-core/functional/init.lua
@@ -27,6 +27,7 @@ _.T = fun.T
_.F = fun.F
_.memoize = fun.memoize
_.lazy = fun.lazy
+_.tap = fun.tap
---@module "mason-core.functional.list"
local list = lazy_require "mason-core.functional.list"
@@ -45,6 +46,7 @@ _.prepend = list.prepend
_.zip_table = list.zip_table
_.nth = list.nth
_.head = list.head
+_.last = list.last
_.length = list.length
_.flatten = list.flatten
_.sort_by = list.sort_by
@@ -80,6 +82,7 @@ _.dec = number.dec
---@module "mason-core.functional.string"
local string = lazy_require "mason-core.functional.string"
_.matches = string.matches
+_.match = string.match
_.format = string.format
_.split = string.split
_.gsub = string.gsub
diff --git a/lua/mason-core/functional/list.lua b/lua/mason-core/functional/list.lua
index 31b631b8..2a5378fe 100644
--- a/lua/mason-core/functional/list.lua
+++ b/lua/mason-core/functional/list.lua
@@ -148,6 +148,13 @@ end, 2)
_.head = _.nth(1)
+---@generic T
+---@param list T[]
+---@return T?
+_.last = function(list)
+ return list[#list]
+end
+
---@param value string|any[]
_.length = function(value)
return #value
diff --git a/lua/mason-core/functional/string.lua b/lua/mason-core/functional/string.lua
index 7726c8e1..9b7da979 100644
--- a/lua/mason-core/functional/string.lua
+++ b/lua/mason-core/functional/string.lua
@@ -8,6 +8,10 @@ _.matches = fun.curryN(function(pattern, str)
return str:match(pattern) ~= nil
end, 2)
+_.match = fun.curryN(function(pattern, str)
+ return { str:match(pattern) }
+end, 2)
+
---@param template string
---@param str string
_.format = fun.curryN(function(template, str)
diff --git a/lua/mason-core/managers/github/client.lua b/lua/mason-core/managers/github/client.lua
index 3ef307c1..699e3fa4 100644
--- a/lua/mason-core/managers/github/client.lua
+++ b/lua/mason-core/managers/github/client.lua
@@ -2,13 +2,10 @@ local _ = require "mason-core.functional"
local log = require "mason-core.log"
local fetch = require "mason-core.fetch"
local spawn = require "mason-core.spawn"
-local api = require "mason-registry.api"
+local providers = require "mason-core.providers"
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}
local stringify_params = _.compose(_.join "&", _.map(_.join "="), _.sort_by(_.head), _.to_pairs)
@@ -66,11 +63,7 @@ end
---@return Result # Result<GitHubRelease>
function M.fetch_latest_release(repo, opts)
opts = opts or { include_prerelease = false }
- return api.repo.releases.latest({ repo = repo }, {
- params = {
- include_prerelease = opts.include_prerelease and "true" or "false",
- },
- })
+ return providers.github.get_latest_release(repo, { include_prerelease = opts.include_prerelease })
end
---@async
@@ -87,7 +80,7 @@ end
---@param repo string The GitHub repo ("username/repo").
---@return Result # Result<string> The latest tag name.
function M.fetch_latest_tag(repo)
- return api.repo.tags.latest({ repo = repo }):map(_.prop "tag")
+ return providers.github.get_latest_tag(repo):map(_.prop "tag")
end
---@async
diff --git a/lua/mason-core/managers/npm/init.lua b/lua/mason-core/managers/npm/init.lua
index 61ee4042..96571420 100644
--- a/lua/mason-core/managers/npm/init.lua
+++ b/lua/mason-core/managers/npm/init.lua
@@ -4,7 +4,7 @@ local Result = require "mason-core.result"
local path = require "mason-core.path"
local _ = require "mason-core.functional"
local platform = require "mason-core.platform"
-local api = require "mason-registry.api"
+local providers = require "mason-core.providers"
local list_copy = _.list_copy
@@ -112,7 +112,7 @@ function M.check_outdated_primary_package(receipt, install_dir)
local primary_package = receipt.primary_source.package
return M.get_installed_primary_package_version(receipt, install_dir)
:and_then(function(installed_version)
- return api.npm.versions.latest({ package = primary_package }):map(function(response)
+ return providers.npm.get_latest_version(primary_package):map(function(response)
return {
installed = installed_version,
latest = response.version,
diff --git a/lua/mason-core/managers/pip3/init.lua b/lua/mason-core/managers/pip3/init.lua
index 4581bc0b..67f70a89 100644
--- a/lua/mason-core/managers/pip3/init.lua
+++ b/lua/mason-core/managers/pip3/init.lua
@@ -7,6 +7,7 @@ local Optional = require "mason-core.optional"
local installer = require "mason-core.installer"
local Result = require "mason-core.result"
local spawn = require "mason-core.spawn"
+local providers = require "mason-core.providers"
local VENV_DIR = "venv"
@@ -103,36 +104,27 @@ function M.check_outdated_primary_package(receipt, install_dir)
return Result.failure "Receipt does not have a primary source of type pip3"
end
local normalized_package = M.normalize_package(receipt.primary_source.package)
- return spawn
- .python({
- "-m",
- "pip",
- "list",
- "--outdated",
- "--format=json",
- cwd = install_dir,
- with_paths = { M.venv_path(install_dir) },
- })
- :map_catching(function(result)
- ---@alias PipOutdatedPackage {name: string, version: string, latest_version: string}
- ---@type PipOutdatedPackage[]
- local packages = vim.json.decode(result.stdout)
-
- local outdated_primary_package = _.find_first(function(outdated_package)
- return outdated_package.name == normalized_package
- and outdated_package.version ~= outdated_package.latest_version
- end, packages)
-
- return Optional.of_nilable(outdated_primary_package)
- :map(function(pkg)
- return {
+ return M.get_installed_primary_package_version(receipt, install_dir):and_then(function(installed_version)
+ return providers.pypi
+ .get_latest_version(normalized_package)
+ :map(function(latest)
+ return {
+ current = installed_version,
+ latest = latest.version,
+ }
+ end)
+ :and_then(function(versions)
+ if versions.current ~= versions.latest then
+ return Result.success {
name = normalized_package,
- current_version = assert(pkg.version, "missing current pip3 package version"),
- latest_version = assert(pkg.latest_version, "missing latest pip3 package version"),
+ current_version = versions.current,
+ latest_version = versions.latest,
}
- end)
- :or_else_throw "Primary package is not outdated."
- end)
+ else
+ return Result.failure "Primary package is not outdated."
+ end
+ end)
+ end)
end
---@async
diff --git a/lua/mason-core/optional.lua b/lua/mason-core/optional.lua
index 6eb0719e..cc40da77 100644
--- a/lua/mason-core/optional.lua
+++ b/lua/mason-core/optional.lua
@@ -105,13 +105,17 @@ function Optional:is_present()
return self._value ~= nil
end
----@param err fun(): any
+---@param err (fun(): any)|string
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())
+ if type(err) == "string" then
+ return Result.failure(err)
+ else
+ return Result.failure(err())
+ end
end
end
diff --git a/lua/mason-core/providers/init.lua b/lua/mason-core/providers/init.lua
new file mode 100644
index 00000000..76bd431b
--- /dev/null
+++ b/lua/mason-core/providers/init.lua
@@ -0,0 +1,77 @@
+local settings = require "mason.settings"
+local log = require "mason-core.log"
+local Result = require "mason-core.result"
+
+---@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 }
+
+---@class GitHubProvider
+---@field get_latest_release? async fun(repo: string, opts?: { include_prerelease?: boolean }): Result # Result<GitHubRelease>
+---@field get_all_release_versions? async fun(repo: string): Result # Result<string[]>
+---@field get_latest_tag? async fun(repo: string): Result # Result<GitHubTag>
+---@field get_all_tags? async fun(repo: string): Result # Result<string[]>
+
+---@alias NpmPackage { name: string, version: string }
+
+---@class NpmProvider
+---@field get_latest_version? async fun(pkg: string): Result # Result<NpmPackage>
+---@field get_all_versions? async fun(pkg: string): Result # Result<string[]>
+
+---@alias PyPiPackage { name: string, version: string }
+
+---@class PyPiProvider
+---@field get_latest_version? async fun(pkg: string): Result # Result<PyPiPackage>
+---@field get_all_versions? async fun(pkg: string): Result # Result<string[]> # Sorting should not be relied upon due to "proprietary" sorting algo in pip that is difficult to replicate in mason-registry-api.
+
+---@class Provider
+---@field github? GitHubProvider
+---@field npm? NpmProvider
+---@field pypi? PyPiProvider
+
+local function service_mt(service)
+ return setmetatable({}, {
+ __index = function(_, method)
+ return function(...)
+ if #settings.current.providers < 1 then
+ log.error "No providers configured."
+ return Result.failure "1 or more providers are required."
+ end
+ for _, provider_module in ipairs(settings.current.providers) do
+ local ok, provider = pcall(require, provider_module)
+ if ok and provider then
+ local impl = provider[service] and provider[service][method]
+ if impl then
+ ---@type boolean, Result
+ local ok, result = pcall(impl, ...)
+ if ok and result:is_success() then
+ return result
+ else
+ if getmetatable(result) == Result then
+ log.fmt_error("Provider %s %s failed: %s", service, method, result:err_or_nil())
+ else
+ log.fmt_error("Provider %s %s errored: %s", service, method, result)
+ end
+ end
+ end
+ else
+ log.fmt_error("Unable to find provider %s is not registered. %s", provider_module, provider)
+ end
+ end
+ local err = ("No provider implementation found for %s.%s"):format(service, method)
+ log.error(err)
+ return Result.failure(err)
+ end
+ end,
+ })
+end
+
+---@type Provider
+local providers = setmetatable({}, {
+ __index = function(tbl, service)
+ tbl[service] = service_mt(service)
+ return tbl[service]
+ end,
+})
+
+return providers
diff --git a/lua/mason-core/spawn.lua b/lua/mason-core/spawn.lua
index fd01c97a..814ef8f7 100644
--- a/lua/mason-core/spawn.lua
+++ b/lua/mason-core/spawn.lua
@@ -5,7 +5,7 @@ local process = require "mason-core.process"
local platform = require "mason-core.platform"
local log = require "mason-core.log"
----@alias JobSpawn table<string, async fun(opts: JobSpawnOpts): Result>
+---@alias JobSpawn table<string, async fun(opts: SpawnArgs): Result>
---@type JobSpawn
local spawn = {
_aliases = {
diff --git a/lua/mason-registry/api.lua b/lua/mason-registry/api.lua
index b700ba47..25a9a8bb 100644
--- a/lua/mason-registry/api.lua
+++ b/lua/mason-registry/api.lua
@@ -69,4 +69,13 @@ api.npm = {
},
}
+api.pypi = {
+ versions = {
+ ---@type ApiSignature<{ package: string }>
+ latest = get "/api/pypi/{package}/versions/latest",
+ ---@type ApiSignature<{ package: string }>
+ all = get "/api/pypi/{package}/versions/all",
+ },
+}
+
return api
diff --git a/lua/mason/providers/client/gh.lua b/lua/mason/providers/client/gh.lua
new file mode 100644
index 00000000..21d916a3
--- /dev/null
+++ b/lua/mason/providers/client/gh.lua
@@ -0,0 +1,43 @@
+local spawn = require "mason-core.spawn"
+local _ = require "mason-core.functional"
+local Result = require "mason-core.result"
+local Optional = require "mason-core.optional"
+
+---@type GitHubProvider
+return {
+ get_latest_release = function(repo, opts)
+ opts = opts or {}
+ if not opts.include_prerelease then
+ return spawn
+ .gh({ "api", ("repos/%s/releases/latest"):format(repo) })
+ :map(_.prop "stdout")
+ :map_catching(vim.json.decode)
+ else
+ return spawn
+ .gh({ "api", ("repos/%s/releases"):format(repo) })
+ :map(_.prop "stdout")
+ :map_catching(vim.json.decode)
+ :map(_.find_first(_.prop_eq("draft", false)))
+ :and_then(function(release)
+ return Optional.of_nilable(release):ok_or "Failed to find latest release."
+ end)
+ end
+ end,
+ get_all_release_versions = function(repo)
+ return spawn
+ .gh({ "api", ("repos/%s/releases"):format(repo) })
+ :map(_.prop "stdout")
+ :map_catching(vim.json.decode)
+ :map(_.map(_.prop "tag_name"))
+ end,
+ get_latest_tag = function(repo)
+ return Result.failure "Unimplemented"
+ end,
+ get_all_tags = function(repo)
+ return spawn
+ .gh({ "api", ("repos/%s/git/matching-refs/tags"):format(repo) })
+ :map(_.prop "stdout")
+ :map_catching(vim.json.decode)
+ :map(_.map(_.compose(_.gsub("^refs/tags/", ""), _.prop "ref")))
+ end,
+}
diff --git a/lua/mason/providers/client/init.lua b/lua/mason/providers/client/init.lua
new file mode 100644
index 00000000..6bb8afee
--- /dev/null
+++ b/lua/mason/providers/client/init.lua
@@ -0,0 +1,6 @@
+---@type Provider
+return {
+ gh = require "mason.providers.client.gh",
+ npm = require "mason.providers.client.npm",
+ pypi = require "mason.providers.client.pypi",
+}
diff --git a/lua/mason/providers/client/npm.lua b/lua/mason/providers/client/npm.lua
new file mode 100644
index 00000000..d5bf6da5
--- /dev/null
+++ b/lua/mason/providers/client/npm.lua
@@ -0,0 +1,16 @@
+local spawn = require "mason-core.spawn"
+local _ = require "mason-core.functional"
+
+---@type NpmProvider
+return {
+ get_latest_version = function(pkg)
+ return spawn
+ .npm({ "view", "--json", pkg .. "@latest" })
+ :map(_.prop "stdout")
+ :map_catching(vim.json.decode)
+ :map(_.pick { "name", "version" })
+ end,
+ get_all_versions = function(pkg)
+ return spawn.npm({ "view", pkg, "versions" }):map(_.prop "stdout"):map_catching(vim.json.decode)
+ end,
+}
diff --git a/lua/mason/providers/client/pypi.lua b/lua/mason/providers/client/pypi.lua
new file mode 100644
index 00000000..5dc357d7
--- /dev/null
+++ b/lua/mason/providers/client/pypi.lua
@@ -0,0 +1,52 @@
+local a = require "mason-core.async"
+local fs = require "mason-core.fs"
+local spawn = require "mason-core.spawn"
+local platform = require "mason-core.platform"
+local _ = require "mason-core.functional"
+local Optional = require "mason-core.optional"
+
+---@param args SpawnArgs
+local function python(args)
+ a.scheduler()
+ local py_exec = platform.is.win and "python" or "python3"
+ -- run in tmpdir in case pip inadvertently produces some output
+ args.cwd = vim.fn.tempname()
+ fs.async.mkdir(args.cwd)
+ return spawn[py_exec](args)
+end
+
+---@async
+---@param pkg string
+local function get_all_versions(pkg)
+ -- https://stackoverflow.com/a/26664162
+ return python({
+ "-m",
+ "pip",
+ "install",
+ "--disable-pip-version-check",
+ "--use-deprecated=legacy-resolver", -- for pip >= 20.3
+ ("%s=="):format(pkg), -- invalid version specifier to trigger the wanted error message
+ })
+ :map_err(_.compose(_.split ", ", _.head, _.match "%(from versions: (.+)%)", _.prop "stderr"))
+ :recover(_.identity)
+end
+
+---@param pkg string
+local function synthesize_pkg(pkg)
+ ---@param version Optional
+ return function(version)
+ return version
+ :map(function(v)
+ return { name = pkg, version = v }
+ end)
+ :ok_or "Unable to find latest version."
+ end
+end
+
+---@type PyPiProvider
+return {
+ get_latest_version = function(pkg)
+ return get_all_versions(pkg):map(_.compose(Optional.of_nilable, _.last)):and_then(synthesize_pkg(pkg))
+ end,
+ get_all_versions_unsorted = get_all_versions,
+}
diff --git a/lua/mason/providers/registry-api/init.lua b/lua/mason/providers/registry-api/init.lua
new file mode 100644
index 00000000..24c35ee8
--- /dev/null
+++ b/lua/mason/providers/registry-api/init.lua
@@ -0,0 +1,40 @@
+local api = require "mason-registry.api"
+
+---@type Provider
+return {
+ github = {
+ get_latest_release = function(repo, opts)
+ opts = opts or {}
+ return api.repo.releases.latest({ repo = repo }, {
+ params = {
+ include_prerelease = (opts and opts.include_prerelease) and "true" or "false",
+ },
+ })
+ end,
+ get_all_release_versions = function(repo)
+ return api.repo.releases.all { repo = repo }
+ end,
+ get_latest_tag = function(repo)
+ return api.repo.tags.latest { repo = repo }
+ end,
+ get_all_tags = function(repo)
+ return api.repo.tags.all { repo = repo }
+ end,
+ },
+ npm = {
+ get_latest_version = function(pkg)
+ return api.npm.versions.latest { package = pkg }
+ end,
+ get_all_versions = function(pkg)
+ return api.npm.versions.all { package = pkg }
+ end,
+ },
+ pypi = {
+ get_latest_version = function(pkg)
+ return api.pypi.versions.latest { package = pkg }
+ end,
+ get_all_versions = function(pkg)
+ return api.pypi.versions.all { package = pkg }
+ end,
+ },
+}
diff --git a/lua/mason/settings.lua b/lua/mason/settings.lua
index afe77953..4322988f 100644
--- a/lua/mason/settings.lua
+++ b/lua/mason/settings.lua
@@ -39,6 +39,15 @@ local DEFAULT_SETTINGS = {
download_url_template = "https://github.com/%s/releases/download/%s/%s",
},
+ -- The provider implementations to use for resolving package metadata (latest version, available versions, etc.).
+ -- Accepts multiple entries, where later entries will be used as fallback should prior providers fail.
+ -- Builtin providers are:
+ -- - mason.providers.registry-api (default) - uses the https://api.mason-registry.dev API
+ -- - mason.providers.client - uses only client-side tooling to resolve metadata
+ providers = {
+ "mason.providers.registry-api",
+ },
+
ui = {
-- Whether to automatically check for new versions when opening the :Mason window.
check_outdated_packages_on_open = true,
diff --git a/tests/mason-core/functional/function_spec.lua b/tests/mason-core/functional/function_spec.lua
index 4da41d40..28abce20 100644
--- a/tests/mason-core/functional/function_spec.lua
+++ b/tests/mason-core/functional/function_spec.lua
@@ -139,4 +139,11 @@ describe("functional: function", function()
assert.is_true(_.T())
assert.is_false(_.F())
end)
+
+ it("should tap values", function()
+ local fn = spy.new()
+ assert.equals(42, _.tap(fn, 42))
+ assert.spy(fn).was_called()
+ assert.spy(fn).was_called_with(42)
+ end)
end)
diff --git a/tests/mason-core/functional/list_spec.lua b/tests/mason-core/functional/list_spec.lua
index add9076e..bbc60763 100644
--- a/tests/mason-core/functional/list_spec.lua
+++ b/tests/mason-core/functional/list_spec.lua
@@ -203,4 +203,12 @@ describe("functional: list", function()
{ "person", "camera" },
}, _.partition(_.matches "%u", words))
end)
+
+ it("should return head", function()
+ assert.equals("Head", _.head { "Head", "Tail", "Tail" })
+ end)
+
+ it("should return last", function()
+ assert.equals("Last", _.last { "Head", "List", "Last" })
+ end)
end)
diff --git a/tests/mason-core/functional/string_spec.lua b/tests/mason-core/functional/string_spec.lua
index 25409f64..6fb99c45 100644
--- a/tests/mason-core/functional/string_spec.lua
+++ b/tests/mason-core/functional/string_spec.lua
@@ -10,6 +10,11 @@ describe("functional: string", function()
assert.is_false(_.matches("bar", "foobaz"))
end)
+ it("returns string pattern matches", function()
+ assert.same({ "foo" }, _.match("foo", "foo"))
+ assert.same({ "foo", "bar", "baz" }, _.match("(foo) (bar) (baz)", "foo bar baz"))
+ end)
+
it("should format strings", function()
assert.equals("Hello World!", _.format("%s", "Hello World!"))
assert.equals("special manouvers", _.format("%s manouvers", "special"))
diff --git a/tests/mason-core/managers/pip3_spec.lua b/tests/mason-core/managers/pip3_spec.lua
index 6f78cdb4..1ec2dc28 100644
--- a/tests/mason-core/managers/pip3_spec.lua
+++ b/tests/mason-core/managers/pip3_spec.lua
@@ -1,5 +1,6 @@
local mock = require "luassert.mock"
local spy = require "luassert.spy"
+local stub = require "luassert.stub"
local path = require "mason-core.path"
local a = require "mason-core.async"
@@ -8,6 +9,7 @@ local installer = require "mason-core.installer"
local Result = require "mason-core.result"
local settings = require "mason.settings"
local spawn = require "mason-core.spawn"
+local api = require "mason-registry.api"
describe("pip3 manager", function()
it("normalizes pip3 packages", function()
@@ -192,11 +194,16 @@ describe("pip3 version check", function()
it(
"should return outdated primary package",
async_test(function()
+ stub(api, "get")
+ api.get.on_call_with("/api/pypi/python-lsp-server/versions/latest").returns(Result.success {
+ name = "python-lsp-server",
+ version = "1.4.0",
+ })
spawn.python = spy.new(function()
return Result.success {
stdout = [[
-[{"name": "astroid", "version": "2.9.3", "latest_version": "2.11.0", "latest_filetype": "wheel"}, {"name": "mccabe", "version": "0.6.1", "latest_version": "0.7.0", "latest_filetype": "wheel"}, {"name": "python-lsp-server", "version": "1.3.0", "latest_version": "1.4.0", "latest_filetype": "wheel"}, {"name": "wrapt", "version": "1.13.3", "latest_version": "1.14.0", "latest_filetype": "wheel"}]
- ]],
+ [{"name": "astroid", "version": "2.9.3"}, {"name": "mccabe", "version": "0.6.1"}, {"name": "python-lsp-server", "version": "1.3.0", "latest_version": "1.4.0", "latest_filetype": "wheel"}, {"name": "wrapt", "version": "1.13.3", "latest_version": "1.14.0", "latest_filetype": "wheel"}]
+ ]],
}
end)
@@ -210,16 +217,6 @@ describe("pip3 version check", function()
path.package_prefix "dummy"
)
- assert.spy(spawn.python).was_called(1)
- assert.spy(spawn.python).was_called_with {
- "-m",
- "pip",
- "list",
- "--outdated",
- "--format=json",
- cwd = path.package_prefix "dummy",
- with_paths = { path.concat { path.package_prefix "dummy", "venv", "bin" } },
- }
assert.is_true(result:is_success())
assert.same({
name = "python-lsp-server",
@@ -236,9 +233,16 @@ describe("pip3 version check", function()
async_test(function()
spawn.python = spy.new(function()
return Result.success {
- stdout = "[]",
+ stdout = [[
+ [{"name": "astroid", "version": "2.9.3"}, {"name": "mccabe", "version": "0.6.1"}, {"name": "python-lsp-server", "version": "1.3.0", "latest_version": "1.4.0", "latest_filetype": "wheel"}, {"name": "wrapt", "version": "1.13.3", "latest_version": "1.14.0", "latest_filetype": "wheel"}]
+ ]],
}
end)
+ stub(api, "get")
+ api.get.on_call_with("/api/pypi/python-lsp-server/versions/latest").returns(Result.success {
+ name = "python-lsp-server",
+ version = "1.3.0",
+ })
local result = pip3.check_outdated_primary_package(
mock.new {
diff --git a/tests/mason-core/providers/provider_spec.lua b/tests/mason-core/providers/provider_spec.lua
new file mode 100644
index 00000000..ba2d8f1f
--- /dev/null
+++ b/tests/mason-core/providers/provider_spec.lua
@@ -0,0 +1,61 @@
+local spy = require "luassert.spy"
+local Result = require "mason-core.result"
+
+describe("providers", function()
+ ---@module "mason-core.providers"
+ local provider
+ ---@module "mason.settings"
+ local settings
+
+ before_each(function()
+ package.loaded["mason-core.providers"] = nil
+ package.loaded["mason.settings"] = nil
+ provider = require "mason-core.providers"
+ settings = require "mason.settings"
+ end)
+
+ it("should run provided providers", function()
+ package.loaded["failing-provider"] = {
+ github = {
+ get_all_release_versions = spy.new(function()
+ return Result.failure "Failed."
+ end),
+ },
+ }
+ package.loaded["really-failing-provider"] = {
+ github = {
+ get_all_release_versions = spy.new(function()
+ error "Failed."
+ end),
+ },
+ }
+ package.loaded["successful-provider"] = {
+ github = {
+ get_all_release_versions = spy.new(function()
+ return Result.success { "1.0.0", "2.0.0" }
+ end),
+ },
+ }
+
+ settings.set {
+ providers = { "failing-provider", "really-failing-provider", "successful-provider" },
+ }
+
+ assert.same(
+ Result.success { "1.0.0", "2.0.0" },
+ provider.github.get_all_release_versions "sumneko/lua-language-server"
+ )
+ assert.spy(package.loaded["failing-provider"].github.get_all_release_versions).was_called()
+ assert
+ .spy(package.loaded["failing-provider"].github.get_all_release_versions)
+ .was_called_with "sumneko/lua-language-server"
+ assert.spy(package.loaded["really-failing-provider"].github.get_all_release_versions).was_called()
+ assert
+ .spy(package.loaded["really-failing-provider"].github.get_all_release_versions)
+ .was_called_with "sumneko/lua-language-server"
+ assert.spy(package.loaded["successful-provider"].github.get_all_release_versions).was_called()
+ assert
+ .spy(package.loaded["successful-provider"].github.get_all_release_versions)
+ .was_called_with "sumneko/lua-language-server"
+ end)
+end)