local Result = require "mason-core.result" local compiler = require "mason-core.installer.compiler" local match = require "luassert.match" local spy = require "luassert.spy" local stub = require "luassert.stub" local test_helpers = require "mason-test.helpers" local util = require "mason-core.installer.compiler.util" ---@type InstallerCompiler local dummy_compiler = { ---@param source RegistryPackageSource ---@param purl Purl ---@param opts PackageInstallOpts parse = function(source, purl, opts) return Result.try(function(try) return { package = purl.name, extra_info = source.extra_info, should_fail = source.should_fail, } end) end, install = function(ctx, source) if source.should_fail then return Result.failure "This is a failure." else return Result.success() end end, get_versions = function() return Result.success { "v1.0.0", "v2.0.0" } end, } describe("registry compiler :: parsing", function() it("should parse valid package specs", function() compiler.register_compiler("dummy", dummy_compiler) local result = compiler.parse({ schema = "registry+v1", source = { id = "pkg:dummy/package-name@v1.2.3", extra_info = "here", }, }, {}) local parsed = result:get_or_nil() assert.is_true(result:is_success()) assert.is_true(match.is_ref(dummy_compiler)(parsed.compiler)) assert.same({ name = "package-name", scheme = "pkg", type = "dummy", version = "v1.2.3", }, parsed.purl) assert.same({ id = "pkg:dummy/package-name@v1.2.3", package = "package-name", extra_info = "here", }, parsed.source) end) it("should keep unmapped fields", function() compiler.register_compiler("dummy", dummy_compiler) local result = compiler.parse({ schema = "registry+v1", source = { id = "pkg:dummy/package-name@v1.2.3", bin = "node:server.js", }, }, {}) local parsed = result:get_or_nil() assert.is_true(result:is_success()) assert.same({ id = "pkg:dummy/package-name@v1.2.3", package = "package-name", bin = "node:server.js", }, parsed.source) end) it("should reject incompatible schema versions", function() compiler.register_compiler("dummy", dummy_compiler) local result = compiler.parse({ schema = "registry+v1337", source = { id = "pkg:dummy/package-name@v1.2.3", }, }, {}) assert.same( Result.failure [[Current version of mason.nvim is not capable of parsing package schema version "registry+v1337".]], result ) end) it("should use requested version", function() compiler.register_compiler("dummy", dummy_compiler) local result = compiler.parse({ schema = "registry+v1", source = { id = "pkg:dummy/package-name@v1.2.3", }, }, { version = "v2.0.0" }) assert.is_true(result:is_success()) local parsed = result:get_or_nil() assert.same({ name = "package-name", scheme = "pkg", type = "dummy", version = "v2.0.0", }, parsed.purl) end) it("should handle PLATFORM_UNSUPPORTED", function() compiler.register_compiler("dummy", dummy_compiler) local result = compiler.compile_installer({ schema = "registry+v1", source = { id = "pkg:dummy/package-name@v1.2.3", supported_platforms = { "VIC64" }, }, }, { version = "v2.0.0" }) assert.same(Result.failure "The current platform is unsupported.", result) end) it("should error upon parsing failures", function() compiler.register_compiler("dummy", dummy_compiler) local result = compiler.compile_installer({ schema = "registry+v1", source = { id = "pkg:dummy/package-name@v1.2.3", supported_platforms = { "VIC64" }, }, }, { version = "v2.0.0" }) assert.same(Result.failure "The current platform is unsupported.", result) end) end) describe("registry compiler :: compiling", function() local snapshot before_each(function() snapshot = assert.snapshot() end) after_each(function() snapshot:revert() end) it("should run compiled installer function successfully", function() compiler.register_compiler("dummy", dummy_compiler) spy.on(dummy_compiler, "get_versions") ---@type PackageInstallOpts local opts = {} local result = compiler.compile_installer({ schema = "registry+v1", source = { id = "pkg:dummy/package-name@v1.2.3", }, }, opts) assert.is_true(result:is_success()) local installer_fn = result:get_or_throw() local ctx = test_helpers.create_context() local installer_result = ctx:execute(installer_fn) assert.same(Result.success(), installer_result) assert.spy(dummy_compiler.get_versions).was_not_called() end) it("should ensure valid version", function() compiler.register_compiler("dummy", dummy_compiler) spy.on(dummy_compiler, "get_versions") ---@type PackageInstallOpts local opts = { version = "v2.0.0" } local result = compiler.compile_installer({ schema = "registry+v1", source = { id = "pkg:dummy/package-name@v1.2.3", }, }, opts) assert.is_true(result:is_success()) local installer_fn = result:get_or_throw() local ctx = test_helpers.create_context { install_opts = opts } local installer_result = ctx:execute(installer_fn) assert.same(Result.success(), installer_result) assert.spy(dummy_compiler.get_versions).was_called(1) assert.spy(dummy_compiler.get_versions).was_called_with({ name = "package-name", scheme = "pkg", type = "dummy", version = "v2.0.0", }, { id = "pkg:dummy/package-name@v1.2.3", }) end) it("should reject invalid version", function() compiler.register_compiler("dummy", dummy_compiler) spy.on(dummy_compiler, "get_versions") ---@type PackageInstallOpts local opts = { version = "v13.3.7" } local result = compiler.compile_installer({ schema = "registry+v1", source = { id = "pkg:dummy/package-name@v1.2.3", }, }, opts) assert.is_true(result:is_success()) local installer_fn = result:get_or_throw() local ctx = test_helpers.create_context { install_opts = opts } local err = assert.has_error(function() ctx:execute(installer_fn):get_or_throw() end) assert.equals([[Version "v13.3.7" is not available.]], err) assert.spy(dummy_compiler.get_versions).was_called(1) assert.spy(dummy_compiler.get_versions).was_called_with({ name = "package-name", scheme = "pkg", type = "dummy", version = "v13.3.7", }, { id = "pkg:dummy/package-name@v1.2.3", }) end) it("should raise errors upon installer failures", function() compiler.register_compiler("dummy", dummy_compiler) ---@type PackageInstallOpts local opts = {} local result = compiler.compile_installer({ schema = "registry+v1", source = { id = "pkg:dummy/package-name@v1.2.3", should_fail = true, }, }, opts) assert.is_true(result:is_success()) local installer_fn = result:get_or_nil() local ctx = test_helpers.create_context() local err = assert.has_error(function() ctx:execute(installer_fn):get_or_throw() end) assert.equals("This is a failure.", err) end) it("should register links", function() compiler.register_compiler("dummy", dummy_compiler) local link = require "mason-core.installer.compiler.link" stub(link, "bin", mockx.returns(Result.success())) stub(link, "share", mockx.returns(Result.success())) stub(link, "opt", mockx.returns(Result.success())) local spec = { schema = "registry+v1", source = { id = "pkg:dummy/package-name@v1.2.3", }, bin = { ["exec"] = "exec" }, opt = { ["opt/"] = "opt/" }, share = { ["share/"] = "share/" }, } ---@type PackageInstallOpts local opts = {} local result = compiler.compile_installer(spec, opts) assert.is_true(result:is_success()) local installer_fn = result:get_or_nil() local ctx = test_helpers.create_context() local installer_result = ctx:execute(installer_fn) assert.is_true(installer_result:is_success()) for _, spy in ipairs { link.bin, link.share, link.opt } do assert.spy(spy).was_called(1) assert.spy(spy).was_called_with(match.is_ref(ctx), spec, { scheme = "pkg", type = "dummy", name = "package-name", version = "v1.2.3", }, { id = "pkg:dummy/package-name@v1.2.3", package = "package-name", }) end end) end)