From 75cffd9260797b06249c5073c5060fe2cd39af3e Mon Sep 17 00:00:00 2001 From: aooohan Date: Sat, 12 Sep 2026 14:33:42 +0800 Subject: [PATCH] fix: propagate search failures and normalize Node.js package architecture Fixes #16 Fixes #23 --- .github/workflows/test.yml | 23 ++++++++++++++ hooks/available.lua | 9 ++++-- hooks/pre_install.lua | 8 +++++ tests/hooks_test.lua | 61 ++++++++++++++++++++++++++++++++++++++ 4 files changed, 98 insertions(+), 3 deletions(-) create mode 100644 .github/workflows/test.yml create mode 100644 tests/hooks_test.lua diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 0000000..4456c1e --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,23 @@ +name: Test Plugin + +on: + pull_request: + push: + branches: [main] + +permissions: + contents: read + +jobs: + test: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7 + with: + persist-credentials: false + - uses: actions/setup-go@924ae3a1cded613372ab5595356fb5720e22ba16 # v6 + with: + go-version: '1.24.0' + cache: false + - name: Test Node.js hooks with vfox's Lua interpreter + run: go run github.com/yuin/gopher-lua/cmd/glua@v1.1.1 tests/hooks_test.lua diff --git a/hooks/available.lua b/hooks/available.lua index 5452239..4412be7 100644 --- a/hooks/available.lua +++ b/hooks/available.lua @@ -13,8 +13,11 @@ function PLUGIN:Available(ctx) local resp, err = http.get({ url = util.getBaseUrl() .. util.VersionSourceUrl }) - if err ~= nil or resp.status_code ~= 200 then - return {} + if err ~= nil then + error("Failed to fetch Node.js versions: " .. tostring(err)) + end + if resp.status_code ~= 200 then + error("Failed to fetch Node.js versions: HTTP " .. tostring(resp.status_code)) end local body = json.decode(resp.body) local result = {} @@ -34,4 +37,4 @@ function PLUGIN:Available(ctx) table.sort(result, util.compare_versions) available_result = result return result -end \ No newline at end of file +end diff --git a/hooks/pre_install.lua b/hooks/pre_install.lua index 92eb818..a08cb9c 100644 --- a/hooks/pre_install.lua +++ b/hooks/pre_install.lua @@ -24,10 +24,15 @@ function PLUGIN:PreInstall(ctx) end local arch_type = RUNTIME.archType + if arch_type == nil or arch_type == "" then + error("Cannot determine Node.js package architecture from RUNTIME.archType") + end local ext = ".tar.gz" local osType = RUNTIME.osType if RUNTIME.archType == "amd64" then arch_type = "x64" + elseif RUNTIME.archType == "386" then + arch_type = "x86" end if RUNTIME.osType == "windows" then ext = ".zip" @@ -52,6 +57,9 @@ function PLUGIN:PreInstall(ctx) error("get checksum failed") end local checksum = util.get_checksum(resp.body, filename) + if checksum == nil then + error("Node.js package " .. filename .. " is not listed in " .. baseUrl .. "SHASUMS256.txt") + end return { version = version, url = baseUrl .. filename, diff --git a/tests/hooks_test.lua b/tests/hooks_test.lua new file mode 100644 index 0000000..5f917f2 --- /dev/null +++ b/tests/hooks_test.lua @@ -0,0 +1,61 @@ +package.path = './lib/?.lua;' .. package.path +local state = {failed=0, passed=0} +package.preload.http = function() return {get=function(args) + state.requests = state.requests + 1 + return state.response, state.request_error +end} end +package.preload.json = function() return {decode=function() return state.decoded end} end +PLUGIN = {} +dofile('hooks/available.lua') +dofile('hooks/pre_install.lua') +local function test(name, fn) + state.response, state.request_error, state.decoded, state.requests = nil, nil, nil, 0 + available_result = nil + local ok, err = pcall(fn) + if ok then state.passed=state.passed+1 else state.failed=state.failed+1; print('FAIL '..name..': '..tostring(err)) end +end +local function expect_error(fn, message) + local ok, err=pcall(fn) + assert(not ok, 'expected error') + assert(tostring(err):find(message,1,true), tostring(err)) +end +for _, failure in ipairs({'timeout','HTTP 503'}) do + test('search failure and recovery '..failure, function() + if failure=='timeout' then state.request_error='timeout' else state.response={status_code=503} end + expect_error(function() PLUGIN:Available({}) end, failure) + state.request_error=nil; state.response={status_code=200,body='index'} + state.decoded={{version='v20.16.0',npm='10.8.1',lts='Iron'}} + assert(PLUGIN:Available({})[1].version=='20.16.0') + assert(state.requests==2, 'failed response was cached') + end) +end +local cases = { + {'windows','amd64','win-x64.zip'}, + {'windows','386','win-x86.zip'}, + {'windows','arm64','win-arm64.zip'}, + {'linux','amd64','linux-x64.tar.gz'}, + {'darwin','arm64','darwin-arm64.tar.gz'}, +} +for _, case in ipairs(cases) do + test('package '..case[1]..'/'..case[2], function() + RUNTIME={osType=case[1],archType=case[2]} + local filename='node-v20.16.0-'..case[3] + local hash=string.rep('a',64) + state.response={status_code=200,body=hash..' '..filename..'\n'} + local result=PLUGIN:PreInstall({version='20.16.0'}) + assert(result.url:sub(-#filename)==filename,result.url) + assert(result.sha256==hash,'checksum missing') + end) +end +test('missing architecture',function() + RUNTIME={osType='windows'} + expect_error(function() PLUGIN:PreInstall({version='20.16.0'}) end,'architecture') + assert(state.requests==0) +end) +test('missing package',function() + RUNTIME={osType='windows',archType='arm64'} + state.response={status_code=200,body=string.rep('a',64)..' node-v20.16.0-win-x64.zip\n'} + expect_error(function() PLUGIN:PreInstall({version='20.16.0'}) end,'node-v20.16.0-win-arm64.zip') +end) +print(state.passed..' passed, '..state.failed..' failed') +assert(state.failed==0)