diff options
| author | William Boman <william@redwill.se> | 2022-03-26 13:41:50 +0100 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2022-03-26 13:41:50 +0100 |
| commit | 212d17a039da449043b67529c29851db37acc236 (patch) | |
| tree | 38411b14487895cef0d7648e198b79fd28793fe6 /tests/core | |
| parent | run autogen_metadata.lua (diff) | |
| download | mason-212d17a039da449043b67529c29851db37acc236.tar mason-212d17a039da449043b67529c29851db37acc236.tar.gz mason-212d17a039da449043b67529c29851db37acc236.tar.bz2 mason-212d17a039da449043b67529c29851db37acc236.tar.lz mason-212d17a039da449043b67529c29851db37acc236.tar.xz mason-212d17a039da449043b67529c29851db37acc236.tar.zst mason-212d17a039da449043b67529c29851db37acc236.zip | |
add async managers (#536)
Diffstat (limited to 'tests/core')
| -rw-r--r-- | tests/core/async/async_spec.lua | 19 | ||||
| -rw-r--r-- | tests/core/managers/cargo_spec.lua | 210 | ||||
| -rw-r--r-- | tests/core/managers/composer_spec.lua | 188 | ||||
| -rw-r--r-- | tests/core/managers/dotnet_spec.lua | 47 | ||||
| -rw-r--r-- | tests/core/managers/gem_spec.lua | 226 | ||||
| -rw-r--r-- | tests/core/managers/git_spec.lua | 179 | ||||
| -rw-r--r-- | tests/core/managers/go_spec.lua | 187 | ||||
| -rw-r--r-- | tests/core/managers/npm_spec.lua | 205 | ||||
| -rw-r--r-- | tests/core/managers/opam_spec.lua | 64 | ||||
| -rw-r--r-- | tests/core/managers/pip3_spec.lua | 253 | ||||
| -rw-r--r-- | tests/core/optional_spec.lua | 63 | ||||
| -rw-r--r-- | tests/core/result_spec.lua | 32 | ||||
| -rw-r--r-- | tests/core/spawn_spec.lua (renamed from tests/core/async/spawn_spec.lua) | 34 |
13 files changed, 1704 insertions, 3 deletions
diff --git a/tests/core/async/async_spec.lua b/tests/core/async/async_spec.lua index 662e493a..0e881c8b 100644 --- a/tests/core/async/async_spec.lua +++ b/tests/core/async/async_spec.lua @@ -13,14 +13,29 @@ describe("async", function() it("should run in blocking mode", function() local start = timestamp() a.run_blocking(function() - a.sleep(1000) + a.sleep(100) end) local stop = timestamp() local grace_ms = 25 - assert.is_true((stop - start) >= (1000 - grace_ms)) + assert.is_true((stop - start) >= (100 - grace_ms)) end) it( + "should pass arguments to .run", + async_test(function() + local callback = spy.new() + local start = timestamp() + a.run(a.sleep, callback, 100) + assert.wait_for(function() + assert.spy(callback).was_called(1) + local stop = timestamp() + local grace_ms = 25 + assert.is_true((stop - start) >= (100 - grace_ms)) + end, 150) + end) + ) + + it( "should wrap callback-style async functions", async_test(function() local stdio = process.in_memory_sink() diff --git a/tests/core/managers/cargo_spec.lua b/tests/core/managers/cargo_spec.lua new file mode 100644 index 00000000..303bf5d0 --- /dev/null +++ b/tests/core/managers/cargo_spec.lua @@ -0,0 +1,210 @@ +local spy = require "luassert.spy" +local match = require "luassert.match" +local mock = require "luassert.mock" +local Optional = require "nvim-lsp-installer.core.optional" +local cargo = require "nvim-lsp-installer.core.managers.cargo" +local Result = require "nvim-lsp-installer.core.result" +local spawn = require "nvim-lsp-installer.core.spawn" + +describe("cargo manager", function() + ---@type InstallContext + local ctx + before_each(function() + ctx = InstallContextGenerator { + spawn = mock.new { + cargo = mockx.returns {}, + }, + } + end) + + it( + "should call cargo install", + async_test(function() + ctx.requested_version = Optional.of "42.13.37" + cargo.crate "my-crate"(ctx) + assert.spy(ctx.spawn.cargo).was_called(1) + assert.spy(ctx.spawn.cargo).was_called_with { + "install", + "--root", + ".", + "--locked", + { "--version", "42.13.37" }, + vim.NIL, -- --features + "my-crate", + } + end) + ) + + it( + "should call cargo install with git source", + async_test(function() + cargo.crate("https://my-crate.git", { git = true })(ctx) + assert.spy(ctx.spawn.cargo).was_called(1) + assert.spy(ctx.spawn.cargo).was_called_with { + "install", + "--root", + ".", + "--locked", + vim.NIL, + vim.NIL, -- --features + { "--git", "https://my-crate.git" }, + } + end) + ) + + it( + "should respect options", + async_test(function() + ctx.requested_version = Optional.of "42.13.37" + cargo.crate("my-crate", { + features = "lsp", + })(ctx) + assert.spy(ctx.spawn.cargo).was_called(1) + assert.spy(ctx.spawn.cargo).was_called_with { + "install", + "--root", + ".", + "--locked", + { "--version", "42.13.37" }, + { "--features", "lsp" }, + "my-crate", + } + end) + ) + + it( + "should not allow combining version with git crate", + async_test(function() + ctx.requested_version = Optional.of "42.13.37" + local err = assert.has_error(function() + cargo.crate("my-crate", { + git = true, + })(ctx) + end) + assert.equals("Providing a version when installing a git crate is not allowed.", err) + assert.spy(ctx.spawn.cargo).was_called(0) + end) + ) + + it( + "should provide receipt information", + async_test(function() + cargo.crate "main-package"(ctx) + assert.equals( + vim.inspect { + type = "cargo", + package = "main-package", + }, + vim.inspect(ctx.receipt.primary_source) + ) + end) + ) +end) + +describe("cargo version check", function() + it("parses cargo installed packages output", function() + assert.equal( + vim.inspect { + ["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", + }, + vim.inspect(cargo.parse_installed_crates [[bat v0.18.3: + bat +exa v0.10.1: + exa +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): + rust-analyzer +stylua v0.11.2: + stylua +zoxide v0.5.0: + zoxide +]]) + ) + end) + + it( + "should return current version", + async_test(function() + spawn.cargo = spy.new(function() + return Result.success { + stdout = [[flux-lsp v0.8.8 (https://github.com/influxdata/flux-lsp#4e452f07): + flux-lsp +]], + } + end) + + local result = cargo.get_installed_primary_package_version( + mock.new { + primary_source = mock.new { + type = "cargo", + package = "https://github.com/influxdata/flux-lsp", + }, + }, + "/tmp/install/dir" + ) + + assert.spy(spawn.cargo).was_called(1) + assert.spy(spawn.cargo).was_called_with(match.tbl_containing { + "install", + "--list", + "--root", + ".", + cwd = "/tmp/install/dir", + }) + assert.is_true(result:is_success()) + assert.equals("0.8.8", result:get_or_nil()) + + spawn.cargo = nil + end) + ) + + -- XXX: This test will actually send http request to crates.io's API. It's not mocked. + it( + "should return outdated primary package", + async_test(function() + spawn.cargo = spy.new(function() + return Result.success { + stdout = [[lelwel v0.4.0: + lelwel-ls +]], + } + end) + + local result = cargo.check_outdated_primary_package( + mock.new { + primary_source = mock.new { + type = "cargo", + package = "lelwel", + }, + }, + "/tmp/install/dir" + ) + + assert.spy(spawn.cargo).was_called(1) + assert.spy(spawn.cargo).was_called_with(match.tbl_containing { + "install", + "--list", + "--root", + ".", + cwd = "/tmp/install/dir", + }) + assert.is_true(result:is_success()) + assert.is_true(match.tbl_containing { + current_version = "0.4.0", + latest_version = match.matches "%d.%d.%d", + name = "lelwel", + }(result:get_or_nil())) + + spawn.cargo = nil + end) + ) +end) diff --git a/tests/core/managers/composer_spec.lua b/tests/core/managers/composer_spec.lua new file mode 100644 index 00000000..caa0721e --- /dev/null +++ b/tests/core/managers/composer_spec.lua @@ -0,0 +1,188 @@ +local spy = require "luassert.spy" +local mock = require "luassert.mock" +local Optional = require "nvim-lsp-installer.core.optional" +local composer = require "nvim-lsp-installer.core.managers.composer" +local Result = require "nvim-lsp-installer.core.result" +local spawn = require "nvim-lsp-installer.core.spawn" + +describe("composer manager", function() + ---@type InstallContext + local ctx + before_each(function() + ctx = InstallContextGenerator { + spawn = mock.new { + composer = mockx.returns {}, + }, + } + end) + + it( + "should call composer require", + async_test(function() + ctx.fs.file_exists = mockx.returns(false) + ctx.requested_version = Optional.of "42.13.37" + composer.require { "main-package", "supporting-package", "supporting-package2" }(ctx) + assert.spy(ctx.spawn.composer).was_called(2) + assert.spy(ctx.spawn.composer).was_called_with { + "init", + "--no-interaction", + "--stability=stable", + } + assert.spy(ctx.spawn.composer).was_called_with { + "require", + { + "main-package:42.13.37", + "supporting-package", + "supporting-package2", + }, + } + end) + ) + + it( + "should provide receipt information", + async_test(function() + ctx.requested_version = Optional.of "42.13.37" + composer.require { "main-package", "supporting-package", "supporting-package2" }(ctx) + assert.equals( + vim.inspect { + type = "composer", + package = "main-package", + }, + vim.inspect(ctx.receipt.primary_source) + ) + assert.equals( + vim.inspect { + { + type = "composer", + package = "supporting-package", + }, + { + type = "composer", + package = "supporting-package2", + }, + }, + vim.inspect(ctx.receipt.secondary_sources) + ) + end) + ) +end) + +describe("composer version check", function() + it( + "should return current version", + async_test(function() + spawn.composer = spy.new(function() + return Result.success { + stdout = [[ +{ + "name": "vimeo/psalm", + "versions": [ + "4.0.0" + ] +} +]], + } + end) + + local result = composer.get_installed_primary_package_version( + mock.new { + primary_source = mock.new { + type = "composer", + package = "vimeo/psalm", + }, + }, + "/tmp/install/dir" + ) + + assert.spy(spawn.composer).was_called(1) + assert.spy(spawn.composer).was_called_with { + "info", + "--format=json", + "vimeo/psalm", + cwd = "/tmp/install/dir", + } + assert.is_true(result:is_success()) + assert.equals("4.0.0", result:get_or_nil()) + + spawn.composer = nil + end) + ) + + it( + "should return outdated primary package", + async_test(function() + spawn.composer = spy.new(function() + return Result.success { + stdout = [[ +{ + "installed": [ + { + "name": "vimeo/psalm", + "version": "4.0.0", + "latest": "4.22.0", + "latest-status": "semver-safe-update", + "description": "A static analysis tool for finding errors in PHP applications" + } + ] +} +]], + } + end) + + local result = composer.check_outdated_primary_package( + mock.new { + primary_source = mock.new { + type = "composer", + package = "vimeo/psalm", + }, + }, + "/tmp/install/dir" + ) + + assert.spy(spawn.composer).was_called(1) + assert.spy(spawn.composer).was_called_with { + "outdated", + "--no-interaction", + "--format=json", + cwd = "/tmp/install/dir", + } + assert.is_true(result:is_success()) + assert.equals( + vim.inspect { + name = "vimeo/psalm", + current_version = "4.0.0", + latest_version = "4.22.0", + }, + vim.inspect(result:get_or_nil()) + ) + + spawn.composer = nil + end) + ) + + it( + "should return failure if primary package is not outdated", + async_test(function() + spawn.composer = spy.new(function() + return Result.success { + stdout = [[{"installed": []}]], + } + end) + + local result = composer.check_outdated_primary_package( + mock.new { + primary_source = mock.new { + type = "composer", + package = "vimeo/psalm", + }, + }, + "/tmp/install/dir" + ) + + assert.is_true(result:is_failure()) + assert.equals("Primary package is not outdated.", result:err_or_nil()) + spawn.composer = nil + end) + ) +end) diff --git a/tests/core/managers/dotnet_spec.lua b/tests/core/managers/dotnet_spec.lua new file mode 100644 index 00000000..4a6887da --- /dev/null +++ b/tests/core/managers/dotnet_spec.lua @@ -0,0 +1,47 @@ +local mock = require "luassert.mock" +local Optional = require "nvim-lsp-installer.core.optional" +local dotnet = require "nvim-lsp-installer.core.managers.dotnet" + +describe("dotnet manager", function() + ---@type InstallContext + local ctx + before_each(function() + ctx = InstallContextGenerator { + spawn = mock.new { + dotnet = mockx.returns {}, + }, + } + end) + + it( + "should call dotnet tool update", + async_test(function() + ctx.requested_version = Optional.of "42.13.37" + dotnet.package "main-package"(ctx) + assert.spy(ctx.spawn.dotnet).was_called(1) + assert.spy(ctx.spawn.dotnet).was_called_with { + "tool", + "update", + "--tool-path", + ".", + { "--version", "42.13.37" }, + "main-package", + } + end) + ) + + it( + "should provide receipt information", + async_test(function() + ctx.requested_version = Optional.of "42.13.37" + dotnet.package "main-package"(ctx) + assert.equals( + vim.inspect { + type = "dotnet", + package = "main-package", + }, + vim.inspect(ctx.receipt.primary_source) + ) + end) + ) +end) diff --git a/tests/core/managers/gem_spec.lua b/tests/core/managers/gem_spec.lua new file mode 100644 index 00000000..e258c65b --- /dev/null +++ b/tests/core/managers/gem_spec.lua @@ -0,0 +1,226 @@ +local spy = require "luassert.spy" +local match = require "luassert.match" +local mock = require "luassert.mock" +local Optional = require "nvim-lsp-installer.core.optional" +local gem = require "nvim-lsp-installer.core.managers.gem" +local Result = require "nvim-lsp-installer.core.result" +local spawn = require "nvim-lsp-installer.core.spawn" + +describe("gem manager", function() + ---@type InstallContext + local ctx + before_each(function() + ctx = InstallContextGenerator { + spawn = mock.new { + gem = mockx.returns {}, + }, + } + end) + + it( + "should call gem install", + async_test(function() + ctx.requested_version = Optional.of "42.13.37" + gem.packages { "main-package", "supporting-package", "supporting-package2" }(ctx) + assert.spy(ctx.spawn.gem).was_called(1) + assert.spy(ctx.spawn.gem).was_called_with(match.tbl_containing { + "install", + "--no-user-install", + "--install-dir=.", + "--bindir=bin", + "--no-document", + match.tbl_containing { + "main-package:42.13.37", + "supporting-package", + "supporting-package2", + }, + }) + end) + ) + + it( + "should provide receipt information", + async_test(function() + ctx.requested_version = Optional.of "42.13.37" + gem.packages { "main-package", "supporting-package", "supporting-package2" }(ctx) + assert.equals( + vim.inspect { + type = "gem", + package = "main-package", + }, + vim.inspect(ctx.receipt.primary_source) + ) + assert.equals( + vim.inspect { + { + type = "gem", + package = "supporting-package", + }, + { + type = "gem", + package = "supporting-package2", + }, + }, + vim.inspect(ctx.receipt.secondary_sources) + ) + end) + ) +end) + +describe("gem version check", function() + it( + "should return current version", + async_test(function() + spawn.gem = spy.new(function() + return Result.success { + stdout = [[shellwords (default: 0.1.0) +singleton (default: 0.1.1) +solargraph (0.44.0) +stringio (default: 3.0.1) +strscan (default: 3.0.1) +]], + } + end) + + local result = gem.get_installed_primary_package_version( + mock.new { + primary_source = mock.new { + type = "gem", + package = "solargraph", + }, + }, + "/tmp/install/dir" + ) + + assert.spy(spawn.gem).was_called(1) + assert.spy(spawn.gem).was_called_with(match.tbl_containing { + "list", + cwd = "/tmp/install/dir", + env = match.all_of( + match.list_containing "GEM_HOME=/tmp/install/dir", + match.list_containing "GEM_PATH=/tmp/install/dir" + ), + }) + assert.is_true(result:is_success()) + assert.equals("0.44.0", result:get_or_nil()) + + spawn.gem = nil + end) + ) + + it( + "should return outdated primary package", + async_test(function() + spawn.gem = spy.new(function() + return Result.success { + stdout = [[bigdecimal (3.1.1 < 3.1.2) +cgi (0.3.1 < 0.3.2) +logger (1.5.0 < 1.5.1) +ostruct (0.5.2 < 0.5.3) +reline (0.3.0 < 0.3.1) +securerandom (0.1.1 < 0.2.0) +solargraph (0.44.0 < 0.44.3) +]], + } + end) + + local result = gem.check_outdated_primary_package( + mock.new { + primary_source = mock.new { + type = "gem", + package = "solargraph", + }, + }, + "/tmp/install/dir" + ) + + assert.spy(spawn.gem).was_called(1) + assert.spy(spawn.gem).was_called_with(match.tbl_containing { + "outdated", + cwd = "/tmp/install/dir", + env = match.all_of( + match.list_containing "GEM_HOME=/tmp/install/dir", + match.list_containing "GEM_PATH=/tmp/install/dir" + ), + }) + assert.is_true(result:is_success()) + assert.equals( + vim.inspect { + name = "solargraph", + current_version = "0.44.0", + latest_version = "0.44.3", + }, + vim.inspect(result:get_or_nil()) + ) + + spawn.gem = nil + end) + ) + + it( + "should return failure if primary package is not outdated", + async_test(function() + spawn.gem = spy.new(function() + return Result.success { + stdout = "", + } + end) + + local result = gem.check_outdated_primary_package( + mock.new { + primary_source = mock.new { + type = "gem", + package = "solargraph", + }, + }, + "/tmp/install/dir" + ) + + assert.is_true(result:is_failure()) + assert.equals("Primary package is not outdated.", result:err_or_nil()) + spawn.gem = nil + end) + ) + + it("parses outdated gem output", function() + local normalize = gem.parse_outdated_gem + assert.equal( + vim.inspect { + name = "solargraph", + current_version = "0.42.2", + latest_version = "0.44.2", + }, + vim.inspect(normalize [[solargraph (0.42.2 < 0.44.2)]]) + ) + assert.equal( + vim.inspect { + name = "sorbet-runtime", + current_version = "0.5.9307", + latest_version = "0.5.9468", + }, + vim.inspect(normalize [[sorbet-runtime (0.5.9307 < 0.5.9468)]]) + ) + end) + + it("returns nil when unable to parse outdated gem", function() + assert.is_nil(gem.parse_outdated_gem "a whole bunch of gibberish!") + assert.is_nil(gem.parse_outdated_gem "") + end) + + it("should parse gem list output", function() + assert.equals( + vim.inspect { + ["solargraph"] = "0.44.3", + ["unicode-display_width"] = "2.1.0", + }, + vim.inspect(gem.parse_gem_list_output [[ + +*** LOCAL GEMS *** + +nokogiri (1.13.3 arm64-darwin) +solargraph (0.44.3) +unicode-display_width (2.1.0) +]]) + ) + end) +end) diff --git a/tests/core/managers/git_spec.lua b/tests/core/managers/git_spec.lua new file mode 100644 index 00000000..2f6c6ace --- /dev/null +++ b/tests/core/managers/git_spec.lua @@ -0,0 +1,179 @@ +local spy = require "luassert.spy" +local mock = require "luassert.mock" +local spawn = require "nvim-lsp-installer.core.spawn" +local Result = require "nvim-lsp-installer.core.result" + +local git = require "nvim-lsp-installer.core.managers.git" +local Optional = require "nvim-lsp-installer.core.optional" + +describe("git manager", function() + ---@type InstallContext + local ctx + before_each(function() + ctx = InstallContextGenerator { + spawn = mock.new { + git = mockx.returns {}, + }, + } + end) + + it( + "should fail if no git repo provided", + async_test(function() + local err = assert.has_errors(function() + git.clone {}(ctx) + end) + assert.equals("No git URL provided.", err) + assert.spy(ctx.spawn.git).was_not_called() + end) + ) + + it( + "should clone provided repo", + async_test(function() + git.clone { "https://github.com/williamboman/nvim-lsp-installer.git" }(ctx) + assert.spy(ctx.spawn.git).was_called(1) + assert.spy(ctx.spawn.git).was_called_with { + "clone", + "--depth", + "1", + "https://github.com/williamboman/nvim-lsp-installer.git", + ".", + } + end) + ) + + it( + "should fetch and checkout revision if requested", + async_test(function() + ctx.requested_version = Optional.of "1337" + git.clone { "https://github.com/williamboman/nvim-lsp-installer.git" }(ctx) + assert.spy(ctx.spawn.git).was_called(3) + assert.spy(ctx.spawn.git).was_called_with { + "clone", + "--depth", + "1", + "https://github.com/williamboman/nvim-lsp-installer.git", + ".", + } + assert.spy(ctx.spawn.git).was_called_with { + "fetch", + "--depth", + "1", + "origin", + "1337", + } + assert.spy(ctx.spawn.git).was_called_with { "checkout", "FETCH_HEAD" } + end) + ) + + it( + "should provide receipt information", + async_test(function() + git.clone { "https://github.com/williamboman/nvim-lsp-installer.git" }(ctx) + assert.equals( + vim.inspect { + type = "git", + remote = "https://github.com/williamboman/nvim-lsp-installer.git", + }, + vim.inspect(ctx.receipt.primary_source) + ) + assert.is_true(#ctx.receipt.secondary_sources == 0) + end) + ) +end) + +describe("git version check", function() + it( + "should return current version", + async_test(function() + spawn.git = spy.new(function() + return Result.success { + stdout = [[19c668c]], + } + end) + + local result = git.get_installed_revision "/tmp/install/dir" + + assert.spy(spawn.git).was_called(1) + assert.spy(spawn.git).was_called_with { "rev-parse", "--short", "HEAD", cwd = "/tmp/install/dir" } + assert.is_true(result:is_success()) + assert.equals("19c668c", result:get_or_nil()) + + spawn.git = nil + end) + ) + + it( + "should check for outdated git clone", + async_test(function() + spawn.git = spy.new(function() + return Result.success { + stdout = [[728307a74cd5f2dec7ca2ca164785c25673d6328 +19c668cd10695b243b09452f0dfd53570c1a2e7d]], + } + end) + + local result = git.check_outdated_git_clone( + mock.new { + primary_source = mock.new { + type = "git", + remote = "https://github.com/williamboman/nvim-lsp-installer.git", + }, + }, + "/tmp/install/dir" + ) + + assert.spy(spawn.git).was_called(2) + assert.spy(spawn.git).was_called_with { + "fetch", + "origin", + "HEAD", + cwd = "/tmp/install/dir", + } + assert.spy(spawn.git).was_called_with { + "rev-parse", + "FETCH_HEAD", + "HEAD", + cwd = "/tmp/install/dir", + } + assert.is_true(result:is_success()) + assert.equals( + vim.inspect { + name = "https://github.com/williamboman/nvim-lsp-installer.git", + current_version = "19c668cd10695b243b09452f0dfd53570c1a2e7d", + latest_version = "728307a74cd5f2dec7ca2ca164785c25673d6328", + }, + vim.inspect(result:get_or_nil()) + ) + + spawn.git = nil + end) + ) + + it( + "should return failure if clone is not outdated", + async_test(function() + spawn.git = spy.new(function() + return Result.success { + stdout = [[19c668cd10695b243b09452f0dfd53570c1a2e7d +19c668cd10695b243b09452f0dfd53570c1a2e7d]], + } + end) + + local result = git.check_outdated_git_clone( + mock.new { + primary_source = mock.new { + type = "git", + remote = "https://github.com/williamboman/nvim-lsp-installer.git", + }, + }, + "/tmp/install/dir" + ) + + assert.is_true(result:is_failure()) + assert.equals("Git clone is up to date.", result:err_or_nil()) + spawn.git = nil + end) + ) +end) diff --git a/tests/core/managers/go_spec.lua b/tests/core/managers/go_spec.lua new file mode 100644 index 00000000..4152d671 --- /dev/null +++ b/tests/core/managers/go_spec.lua @@ -0,0 +1,187 @@ +local match = require "luassert.match" +local mock = require "luassert.mock" +local stub = require "luassert.stub" +local spy = require "luassert.spy" +local Optional = require "nvim-lsp-installer.core.optional" +local Result = require "nvim-lsp-installer.core.result" +local go = require "nvim-lsp-installer.core.managers.go" +local spawn = require "nvim-lsp-installer.core.spawn" + +describe("go manager", function() + ---@type InstallContext + local ctx + before_each(function() + ctx = InstallContextGenerator { + spawn = mock.new { + go = mockx.returns {}, + }, + } + end) + + it( + "should call go install", + async_test(function() + ctx.requested_version = Optional.of "42.13.37" + go.packages { "main-package", "supporting-package", "supporting-package2" }(ctx) + assert.spy(ctx.spawn.go).was_called(3) + assert.spy(ctx.spawn.go).was_called_with(match.tbl_containing { + "install", + "-v", + "main-package@42.13.37", + env = match.list_containing "GOBIN=/tmp/install-dir", + }) + assert.spy(ctx.spawn.go).was_called_with(match.tbl_containing { + "install", + "-v", + "supporting-package@latest", + env = match.list_containing "GOBIN=/tmp/install-dir", + }) + assert.spy(ctx.spawn.go).was_called_with(match.tbl_containing { + "install", + "-v", + "supporting-package2@latest", + env = match.list_containing "GOBIN=/tmp/install-dir", + }) + end) + ) + + it( + "should provide receipt information", + async_test(function() + ctx.requested_version = Optional.of "42.13.37" + go.packages { "main-package", "supporting-package", "supporting-package2" }(ctx) + assert.equals( + vim.inspect { + type = "go", + package = "main-package", + }, + vim.inspect(ctx.receipt.primary_source) + ) + assert.equals( + vim.inspect { + { + type = "go", + package = "supporting-package", + }, + { + type = "go", + package = "supporting-package2", + }, + }, + vim.inspect(ctx.receipt.secondary_sources) + ) + end) + ) +end) + +describe("go version check", function() + local go_version_output = [[ +gopls: go1.18 + path golang.org/x/tools/gopls + mod golang.org/x/tools/gopls v0.8.1 h1:q5nDpRopYrnF4DN/1o8ZQ7Oar4Yd4I5OtGMx5RyV2/8= + dep github.com/google/go-cmp v0.5.7 h1:81/ik6ipDQS2aGcBfIN5dHDB36BwrStyeAQquSYCV4o= + dep mvdan.cc/xurls/v2 v2.4.0 h1:tzxjVAj+wSBmDcF6zBB7/myTy3gX9xvi8Tyr28AuQgc= + build -compiler=gc + build GOOS=darwin +]] + + it("should parse go version output", function() + local parsed = go.parse_mod_version_output(go_version_output) + assert.equals( + vim.inspect { + path = { ["golang.org/x/tools/gopls"] = "" }, + mod = { ["golang.org/x/tools/gopls"] = "v0.8.1" }, + dep = { ["github.com/google/go-cmp"] = "v0.5.7", ["mvdan.cc/xurls/v2"] = "v2.4.0" }, + build = { ["-compiler=gc"] = "", ["GOOS=darwin"] = "" }, + }, + vim.inspect(parsed) + ) + end) + + it( + "should return current version", + async_test(function() + spawn.go = spy.new(function() + return Result.success { stdout = go_version_output } + end) + + local result = go.get_installed_primary_package_version( + mock.new { + primary_source = mock.new { + type = "go", + package = "golang.org/x/tools/gopls", + }, + }, + "/tmp/install/dir" + ) + + assert.spy(spawn.go).was_called(1) + assert.spy(spawn.go).was_called_with { + "version", + "-m", + "gopls", + cwd = "/tmp/install/dir", + } + assert.is_true(result:is_success()) + assert.equals("v0.8.1", result:get_or_nil()) + + spawn.go = nil + end) + ) + + it( + "should return outdated primary package", + async_test(function() + stub(spawn, "go") + spawn.go.on_call_with({ + "list", + "-json", + "-m", + "-versions", + "golang.org/x/tools/gopls", + cwd = "/tmp/install/dir", + }).returns(Result.success { + stdout = [[ + { + "Path": "/tmp/install/dir", + "Versions": [ + "v1.0.0", + "v1.0.1", + "v2.0.0" + ] + } + ]], + }) + spawn.go.on_call_with({ + "version", + "-m", + "gopls", + cwd = "/tmp/install/dir", + }).returns(Result.success { + stdout = go_version_output, + }) + + local result = go.check_outdated_primary_package( + mock.new { + primary_source = mock.new { + type = "go", + package = "golang.org/x/tools/gopls", + }, + }, + "/tmp/install/dir" + ) + + assert.is_true(result:is_success()) + assert.equals( + vim.inspect { + name = "golang.org/x/tools/gopls", + current_version = "v0.8.1", + latest_version = "v2.0.0", + }, + vim.inspect(result:get_or_nil()) + ) + + spawn.go = nil + end) + ) +end) diff --git a/tests/core/managers/npm_spec.lua b/tests/core/managers/npm_spec.lua new file mode 100644 index 00000000..c5e2b2b9 --- /dev/null +++ b/tests/core/managers/npm_spec.lua @@ -0,0 +1,205 @@ +local spy = require "luassert.spy" +local match = require "luassert.match" +local mock = require "luassert.mock" +local Optional = require "nvim-lsp-installer.core.optional" +local npm = require "nvim-lsp-installer.core.managers.npm" +local Result = require "nvim-lsp-installer.core.result" +local spawn = require "nvim-lsp-installer.core.spawn" + +describe("npm manager", function() + ---@type InstallContext + local ctx + before_each(function() + ctx = InstallContextGenerator { + spawn = mock.new { + npm = mockx.returns {}, + }, + } + end) + + it( + "should call npm install", + async_test(function() + ctx.requested_version = Optional.of "42.13.37" + npm.packages { "main-package", "supporting-package", "supporting-package2" }(ctx) + assert.spy(ctx.spawn.npm).was_called(1) + assert.spy(ctx.spawn.npm).was_called_with(match.tbl_containing { + "install", + match.tbl_containing { + "main-package@42.13.37", + "supporting-package", + "supporting-package2", + }, + }) + end) + ) + + it( + "should call npm init if node_modules and package.json doesnt exist", + async_test(function() + ctx.fs.file_exists = mockx.returns(false) + ctx.fs.dir_exists = mockx.returns(false) + npm.install { "main-package", "supporting-package", "supporting-package2" }(ctx) + assert.spy(ctx.spawn.npm).was_called_with { + "init", + "--yes", + "--scope=lsp-installer", + } + end) + ) + + it( + "should append .npmrc file", + async_test(function() + ctx.requested_version = Optional.of "42.13.37" + npm.packages { "main-package", "supporting-package", "supporting-package2" }(ctx) + assert.spy(ctx.fs.append_file).was_called(1) + assert.spy(ctx.fs.append_file).was_called_with(ctx.fs, ".npmrc", "global-style=true") + end) + ) + + it( + "should provide receipt information", + async_test(function() + ctx.requested_version = Optional.of "42.13.37" + npm.packages { "main-package", "supporting-package", "supporting-package2" }(ctx) + assert.equals( + vim.inspect { + type = "npm", + package = "main-package", + }, + vim.inspect(ctx.receipt.primary_source) + ) + assert.equals( + vim.inspect { + { + type = "npm", + package = "supporting-package", + }, + { + type = "npm", + package = "supporting-package2", + }, + }, + vim.inspect(ctx.receipt.secondary_sources) + ) + end) + ) +end) + +describe("npm version check", function() + it( + "should return current version", + async_test(function() + spawn.npm = spy.new(function() + return Result.success { + stdout = [[ + { + "name": "bash", + "dependencies": { + "bash-language-server": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/bash-language-server/-/bash-language-server-2.0.0.tgz" + } + } + } + ]], + } + end) + + local result = npm.get_installed_primary_package_version( + mock.new { + primary_source = mock.new { + type = "npm", + package = "bash-language-server", + }, + }, + "/tmp/install/dir" + ) + + assert.spy(spawn.npm).was_called(1) + assert.spy(spawn.npm).was_called_with { "ls", "--json", cwd = "/tmp/install/dir" } + assert.is_true(result:is_success()) + assert.equals("2.0.0", result:get_or_nil()) + + spawn.npm = nil + end) + ) + + it( + "should return outdated primary package", + async_test(function() + spawn.npm = spy.new(function() + -- npm outdated returns with exit code 1 if outdated packages are found! + return Result.failure { + exit_code = 1, + stdout = [[ + { + "bash-language-server": { + "current": "1.17.0", + "wanted": "1.17.0", + "latest": "2.0.0", + "dependent": "bash", + "location": "/tmp/install/dir" + } + } + ]], + } + end) + + local result = npm.check_outdated_primary_package( + mock.new { + primary_source = mock.new { + type = "npm", + package = "bash-language-server", + }, + }, + "/tmp/install/dir" + ) + + assert.spy(spawn.npm).was_called(1) + assert.spy(spawn.npm).was_called_with { + "outdated", + "--json", + "bash-language-server", + cwd = "/tmp/install/dir", + } + assert.is_true(result:is_success()) + assert.equals( + vim.inspect { + name = "bash-language-server", + current_version = "1.17.0", + latest_version = "2.0.0", + }, + vim.inspect(result:get_or_nil()) + ) + + spawn.npm = nil + end) + ) + + it( + "should return failure if primary package is not outdated", + async_test(function() + spawn.npm = spy.new(function() + return Result.success { + stdout = "{}", + } + end) + + local result = npm.check_outdated_primary_package( + mock.new { + primary_source = mock.new { + type = "npm", + package = "bash-language-server", + }, + }, + "/tmp/install/dir" + ) + + assert.is_true(result:is_failure()) + assert.equals("Primary package is not outdated.", result:err_or_nil()) + spawn.npm = nil + end) + ) +end) diff --git a/tests/core/managers/opam_spec.lua b/tests/core/managers/opam_spec.lua new file mode 100644 index 00000000..0ec003ec --- /dev/null +++ b/tests/core/managers/opam_spec.lua @@ -0,0 +1,64 @@ +local match = require "luassert.match" +local mock = require "luassert.mock" +local Optional = require "nvim-lsp-installer.core.optional" +local opam = require "nvim-lsp-installer.core.managers.opam" + +describe("opam manager", function() + ---@type InstallContext + local ctx + before_each(function() + ctx = InstallContextGenerator { + spawn = mock.new { + opam = mockx.returns {}, + }, + } + end) + + it( + "should call opam install", + async_test(function() + ctx.requested_version = Optional.of "42.13.37" + opam.packages { "main-package", "supporting-package", "supporting-package2" }(ctx) + assert.spy(ctx.spawn.opam).was_called(1) + assert.spy(ctx.spawn.opam).was_called_with(match.tbl_containing { + "install", + "--destdir=.", + "--yes", + "--verbose", + match.tbl_containing { + "main-package.42.13.37", + "supporting-package", + "supporting-package2", + }, + }) + end) + ) + + it( + "should provide receipt information", + async_test(function() + ctx.requested_version = Optional.of "42.13.37" + opam.packages { "main-package", "supporting-package", "supporting-package2" }(ctx) + assert.equals( + vim.inspect { + type = "opam", + package = "main-package", + }, + vim.inspect(ctx.receipt.primary_source) + ) + assert.equals( + vim.inspect { + { + type = "opam", + package = "supporting-package", + }, + { + type = "opam", + package = "supporting-package2", + }, + }, + vim.inspect(ctx.receipt.secondary_sources) + ) + end) + ) +end) diff --git a/tests/core/managers/pip3_spec.lua b/tests/core/managers/pip3_spec.lua new file mode 100644 index 00000000..704a32e8 --- /dev/null +++ b/tests/core/managers/pip3_spec.lua @@ -0,0 +1,253 @@ +local mock = require "luassert.mock" +local spy = require "luassert.spy" +local match = require "luassert.match" + +local pip3 = require "nvim-lsp-installer.core.managers.pip3" +local Optional = require "nvim-lsp-installer.core.optional" +local Result = require "nvim-lsp-installer.core.result" +local settings = require "nvim-lsp-installer.settings" +local spawn = require "nvim-lsp-installer.core.spawn" + +describe("pip3 manager", function() + ---@type InstallContext + local ctx + before_each(function() + ctx = InstallContextGenerator { + spawn = mock.new { + python3 = mockx.returns {}, + }, + } + end) + + it("normalizes pip3 packages", function() + local normalize = pip3.normalize_package + assert.equal("python-lsp-server", normalize "python-lsp-server[all]") + assert.equal("python-lsp-server", normalize "python-lsp-server[]") + assert.equal("python-lsp-server", normalize "python-lsp-server[[]]") + end) + + it( + "should create venv and call pip3 install", + async_test(function() + ctx.requested_version = Optional.of "42.13.37" + pip3.packages { "main-package", "supporting-package", "supporting-package2" }(ctx) + assert.spy(ctx.promote_cwd).was_called(1) + assert.spy(ctx.spawn.python3).was_called(2) + assert.spy(ctx.spawn.python3).was_called_with { + "-m", + "venv", + "venv", + } + assert.spy(ctx.spawn.python3).was_called_with(match.tbl_containing { + "-m", + "pip", + "install", + "-U", + match.table(), + match.tbl_containing { + "main-package==42.13.37", + "supporting-package", + "supporting-package2", + }, + env = match.is_table(), + }) + end) + ) + + it( + "should exhaust python3 executable candidates if all fail", + async_test(function() + vim.g.python3_host_prog = "/my/python3" + ctx.spawn = mock.new { + python3 = mockx.throws(), + python = mockx.throws(), + [vim.g.python3_host_prog] = mockx.throws(), + } + local err = assert.has_error(function() + pip3.packages { "package" }(ctx) + end) + vim.g.python3_host_prog = nil + + assert.equals("Unable to create python3 venv environment.", err) + assert.spy(ctx.spawn["/my/python3"]).was_called(1) + assert.spy(ctx.spawn.python3).was_called(1) + assert.spy(ctx.spawn.python).was_called(1) + end) + ) + + it( + "should not exhaust python3 executable if one succeeds", + async_test(function() + vim.g.python3_host_prog = "/my/python3" + ctx.spawn = mock.new { + python3 = mockx.throws(), + python = mockx.throws(), + [vim.g.python3_host_prog] = mockx.returns {}, + } + pip3.packages { "package" }(ctx) + vim.g.python3_host_prog = nil + assert.spy(ctx.spawn.python3).was_called(0) + assert.spy(ctx.spawn.python).was_called(0) + assert.spy(ctx.spawn["/my/python3"]).was_called() + end) + ) + + it( + "should use install_args from settings", + async_test(function() + settings.set { + pip = { + install_args = { "--proxy", "http://localhost:8080" }, + }, + } + pip3.packages { "package" }(ctx) + settings.set(settings._DEFAULT_SETTINGS) + assert.spy(ctx.spawn.python3).was_called_with(match.tbl_containing { + "-m", + "pip", + "install", + "-U", + match.tbl_containing { "--proxy", "http://localhost:8080" }, + match.tbl_containing { "package" }, + env = match.is_table(), + }) + end) + ) + + it( + "should provide receipt information", + async_test(function() + ctx.requested_version = Optional.of "42.13.37" + pip3.packages { "main-package", "supporting-package", "supporting-package2" }(ctx) + assert.equals( + vim.inspect { + type = "pip3", + package = "main-package", + }, + vim.inspect(ctx.receipt.primary_source) + ) + assert.equals( + vim.inspect { + { + type = "pip3", + package = "supporting-package", + }, + { + type = "pip3", + package = "supporting-package2", + }, + }, + vim.inspect(ctx.receipt.secondary_sources) + ) + end) + ) +end) + +describe("pip3 version check", function() + it( + "should return current version", + async_test(function() + spawn.python = spy.new(function() + return Result.success { + 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) + + local result = pip3.get_installed_primary_package_version( + mock.new { + primary_source = mock.new { + type = "pip3", + package = "python-lsp-server", + }, + }, + "/tmp/install/dir" + ) + + assert.spy(spawn.python).was_called(1) + assert.spy(spawn.python).was_called_with(match.tbl_containing { + "-m", + "pip", + "list", + "--format=json", + cwd = "/tmp/install/dir", + env = match.table(), + }) + assert.is_true(result:is_success()) + assert.equals("1.3.0", result:get_or_nil()) + + spawn.python = nil + end) + ) + + it( + "should return outdated primary package", + async_test(function() + 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"}] + ]], + } + end) + + local result = pip3.check_outdated_primary_package( + mock.new { + primary_source = mock.new { + type = "pip3", + package = "python-lsp-server", + }, + }, + "/tmp/install/dir" + ) + + assert.spy(spawn.python).was_called(1) + assert.spy(spawn.python).was_called_with(match.tbl_containing { + "-m", + "pip", + "list", + "--outdated", + "--format=json", + cwd = "/tmp/install/dir", + env = match.table(), + }) + assert.is_true(result:is_success()) + assert.equals( + vim.inspect { + name = "python-lsp-server", + current_version = "1.3.0", + latest_version = "1.4.0", + }, + vim.inspect(result:get_or_nil()) + ) + + spawn.python = nil + end) + ) + + it( + "should return failure if primary package is not outdated", + async_test(function() + spawn.python = spy.new(function() + return Result.success { + stdout = "[]", + } + end) + + local result = pip3.check_outdated_primary_package( + mock.new { + primary_source = mock.new { + type = "pip3", + package = "python-lsp-server", + }, + }, + "/tmp/install/dir" + ) + + assert.is_true(result:is_failure()) + assert.equals("Primary package is not outdated.", result:err_or_nil()) + spawn.python = nil + end) + ) +end) diff --git a/tests/core/optional_spec.lua b/tests/core/optional_spec.lua new file mode 100644 index 00000000..02f8800e --- /dev/null +++ b/tests/core/optional_spec.lua @@ -0,0 +1,63 @@ +local Optional = require "nvim-lsp-installer.core.optional" +local spy = require "luassert.spy" + +describe("Optional.of_nilable", function() + it("should create empty optionals", function() + local empty = Optional.empty() + assert.is_false(empty:is_present()) + end) + + it("should create non-empty optionals", function() + local empty = Optional.of_nilable "value" + assert.is_true(empty:is_present()) + end) + + it("should use memoized empty value", function() + assert.is_true(Optional.empty() == Optional.empty()) + end) +end) + +describe("Optional.get()", function() + it("should map non-empty values", function() + local str = Optional.of_nilable("world!") + :map(function(val) + return "Hello " .. val + end) + :get() + assert.equals("Hello world!", str) + end) + + it("should raise error when getting empty value", function() + local err = assert.has_error(function() + Optional.empty():get() + end) + assert.equals("No value present.", err) + end) +end) + +describe("Optional.or_else()", function() + it("should use .or_else() value if empty", function() + local value = Optional.empty():or_else "Hello!" + assert.equals("Hello!", value) + end) + + it("should not use .or_else() value if not empty", function() + local value = Optional.of_nilable("Good bye!"):or_else "Hello!" + assert.equals("Good bye!", value) + end) +end) + +describe("Optional.if_present()", function() + it("should not call .if_present() if value is empty", function() + local present = spy.new() + Optional.empty():if_present(present) + assert.spy(present).was_not_called() + end) + + it("should call .if_present() if value is not empty", function() + local present = spy.new() + Optional.of_nilable("value"):if_present(present) + assert.spy(present).was_called(1) + assert.spy(present).was_called_with "value" + end) +end) diff --git a/tests/core/result_spec.lua b/tests/core/result_spec.lua index 725041de..cd10acb5 100644 --- a/tests/core/result_spec.lua +++ b/tests/core/result_spec.lua @@ -85,4 +85,36 @@ describe("result", function() assert.is_true(mapped:is_failure()) assert.is_true(match.has_match "This is an error$"(mapped:err_or_nil())) end) + + it("should recover errors", function() + local result = Result.failure("call an ambulance"):recover(function(err) + return err .. ". but not for me!" + end) + assert.is_true(result:is_success()) + assert.equals("call an ambulance. but not for me!", result:get_or_nil()) + end) + + it("should catch errors in recover", function() + local result = Result.failure("call an ambulance"):recover_catching(function(err) + error("Oh no... " .. err, 2) + end) + assert.is_true(result:is_failure()) + assert.equals("Oh no... call an ambulance", result:err_or_nil()) + end) + + it("should return results in run_catching", function() + local result = Result.run_catching(function() + return "Hello world!" + end) + assert.is_true(result:is_success()) + assert.equals("Hello world!", result:get_or_nil()) + end) + + it("should return failures in run_catching", function() + local result = Result.run_catching(function() + error("Oh noes", 2) + end) + assert.is_true(result:is_failure()) + assert.equals("Oh noes", result:err_or_nil()) + end) end) diff --git a/tests/core/async/spawn_spec.lua b/tests/core/spawn_spec.lua index 721e4d08..3bfafbc3 100644 --- a/tests/core/async/spawn_spec.lua +++ b/tests/core/spawn_spec.lua @@ -1,4 +1,4 @@ -local spawn = require "nvim-lsp-installer.core.async.spawn" +local spawn = require "nvim-lsp-installer.core.spawn" local process = require "nvim-lsp-installer.process" describe("async spawn", function() @@ -44,4 +44,36 @@ describe("async spawn", function() assert.equals("", result:get_or_nil().stderr) end) ) + + it( + "should ignore vim.NIL args", + async_test(function() + local result = spawn.bash { + vim.NIL, + spawn._when(true, "-c"), + spawn._when(false, "shouldnotbeincluded"), + vim.NIL, + 'echo "Hello $VAR"', + env = { "VAR=world" }, + } + + assert.is_true(result:is_success()) + assert.equals("Hello world\n", result:get_or_nil().stdout) + assert.equals("", result:get_or_nil().stderr) + end) + ) + + it( + "should flatten table args", + async_test(function() + local result = spawn.bash { + { "-c", 'echo "Hello $VAR"' }, + env = { "VAR=world" }, + } + + assert.is_true(result:is_success()) + assert.equals("Hello world\n", result:get_or_nil().stdout) + assert.equals("", result:get_or_nil().stderr) + end) + ) end) |
