diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml new file mode 100644 index 0000000..7df5b0b --- /dev/null +++ b/.github/workflows/tests.yml @@ -0,0 +1,43 @@ +name: kulala tests +on: + push: + branches: + - main + pull_request: ~ + + tests: + name: tests + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + include: + - os: ubuntu-22.04 + rev: v0.10.0/nvim-linux64.tar.gz + steps: + - uses: actions/checkout@v4 + - name: Restore cache for today's nightly. + uses: actions/cache@v4 + with: + path: _neovim + key: ${{ runner.os }}-${{ matrix.rev }} + + - name: Prepare dependencies + run: | + test -d _neovim || { + mkdir -p _neovim + curl -sL "https://github.com/neovim/neovim/releases/download/${{ matrix.rev }}" | tar xzf - --strip-components=1 -C "${PWD}/_neovim" + } + mkdir -p ~/.local/share/nvim/site/pack/vendor/start + git clone --depth 1 https://github.com/nvim-lua/plenary.nvim ~/.local/share/nvim/site/pack/vendor/start/plenary.nvim + ln -s $(pwd) ~/.local/share/nvim/site/pack/vendor/start + export PATH="${PWD}/_neovim/bin:${PATH}" + export VIM="${PWD}/_neovim/share/nvim/runtime" + nvim --headless -c 'TSInstallSync lua | quit' + + - name: Run tests + run: | + export PATH="${PWD}/_neovim/bin:${PATH}" + export VIM="${PWD}/_neovim/share/nvim/runtime" + nvim --version + /bin/bash ./scripts/test diff --git a/lua/kulala/parser/curl.lua b/lua/kulala/parser/curl.lua index 5a29503..743b3d5 100644 --- a/lua/kulala/parser/curl.lua +++ b/lua/kulala/parser/curl.lua @@ -69,6 +69,9 @@ function M.parse(curl) res.headers["content-type"] = "application/x-www-form-urlencoded" end elseif arg == "--json" then + if res.method == "" then + res.method = "POST" + end state = State.Body res.headers["content-type"] = "application/json" res.headers["accept"] = "application/json" @@ -89,7 +92,7 @@ function M.parse(curl) res.headers["user-agent"] = arg elseif state == State.Header then local header, value = Stringutils.cut(arg, ":") - res.headers[Stringutils.remove_extra_space(header)] = Stringutils.remove_extra_space(value) + res.headers[Stringutils.remove_extra_space(header):lower()] = Stringutils.remove_extra_space(value) elseif state == State.Body then res.body = arg end diff --git a/scripts/test.sh b/scripts/test.sh new file mode 100644 index 0000000..15523b4 --- /dev/null +++ b/scripts/test.sh @@ -0,0 +1,21 @@ +#!/bin/bash +# Copy from MIT licensed neotest plugin: https://raw.githubusercontent.com/nvim-neotest/neotest/master/scripts/test +tempfile=".test_output.tmp" + +if [[ -n $1 ]]; then + nvim --headless --noplugin -u tests/init.vim -c "PlenaryBustedFile $1" | tee "${tempfile}" +else + nvim --headless --noplugin -u tests/init.vim -c "PlenaryBustedDirectory tests/ {minimal_init = 'tests/init.vim'}" | tee "${tempfile}" +fi + +# Plenary doesn't emit exit code 1 when tests have errors during setup +errors=$(sed 's/\x1b\[[0-9;]*m//g' "${tempfile}" | awk '/(Errors|Failed) :/ {print $3}' | grep -v '0') + +rm "${tempfile}" + +if [[ -n $errors ]]; then + echo "Tests failed" + exit 1 +fi + +exit 0 diff --git a/tests/init.vim b/tests/init.vim new file mode 100644 index 0000000..5b10d82 --- /dev/null +++ b/tests/init.vim @@ -0,0 +1,2 @@ +runtime! plugin/plenary.vim +source tests/minimal_init.lua diff --git a/tests/minimal_init.lua b/tests/minimal_init.lua new file mode 100644 index 0000000..bf1c476 --- /dev/null +++ b/tests/minimal_init.lua @@ -0,0 +1,9 @@ +local lazypath = vim.fn.stdpath("data") .. "/lazy" +vim.notify = print +vim.opt.rtp:append(".") +vim.opt.rtp:append(lazypath .. "/plenary.nvim") +vim.opt.swapfile = false +vim.cmd("runtime! plugin/plenary.vim") +A = function(...) + print(vim.inspect(...)) +end diff --git a/tests/parser/curl_spec.lua b/tests/parser/curl_spec.lua new file mode 100644 index 0000000..bc24615 --- /dev/null +++ b/tests/parser/curl_spec.lua @@ -0,0 +1,159 @@ +local CURL = require("kulala.parser.curl") + +describe("curl parse", function() + it("bad command", function() + local args = [[urlc 'http://example.com/get']] + local parsed = CURL.parse(args) + assert.equal(nil, parsed) + end) + + it("default GET", function() + local args = [[curl 'http://example.com/get']] + local parsed = CURL.parse(args) + assert.equal("GET", parsed.method) + assert.equal("http://example.com/get", parsed.url) + assert.equal(nil, parsed.data) + assert.same({}, parsed.headers) + assert.equal("", parsed.http_version) + end) + + it("-X PATCH", function() + local args = [[curl -X PATCH 'http://example.com/patch']] + local parsed = CURL.parse(args) + assert.equal("PATCH", parsed.method) + assert.equal("http://example.com/patch", parsed.url) + end) + + it("--request HEAD", function() + local args = [[curl --request HEAD 'http://example.com/head']] + local parsed = CURL.parse(args) + assert.equal("HEAD", parsed.method) + assert.equal("http://example.com/head", parsed.url) + end) + + it("-A agent", function() + local args = [[curl -A agent 'http://example.com/get']] + local parsed = CURL.parse(args) + assert.equal("GET", parsed.method) + assert.equal("http://example.com/get", parsed.url) + assert.same({ ["user-agent"] = "agent" }, parsed.headers) + end) + + it("--user-agent user/agent", function() + local args = [[curl --user-agent user/agent 'http://example.com/get']] + local parsed = CURL.parse(args) + assert.equal("GET", parsed.method) + assert.equal("http://example.com/get", parsed.url) + assert.same({ ["user-agent"] = "user/agent" }, parsed.headers) + end) + + it("-H 'short: header'", function() + local args = [[curl -H 'short:header' 'http://example.com/get']] + local parsed = CURL.parse(args) + assert.equal("GET", parsed.method) + assert.equal("http://example.com/get", parsed.url) + assert.same({ ["short"] = "header" }, parsed.headers) + end) + + it("-H 'short: header' --header 'long:header'", function() + local args = [[curl -H 'short:header' --header 'long:header' 'http://example.com/get']] + local parsed = CURL.parse(args) + assert.equal("GET", parsed.method) + assert.equal("http://example.com/get", parsed.url) + assert.same({ ["short"] = "header", ["long"] = "header" }, parsed.headers) + end) + + it("-d data", function() + local args = [[curl -d data 'https://example.com/post']] + local parsed = CURL.parse(args) + assert.equal("POST", parsed.method) + assert.equal("https://example.com/post", parsed.url) + assert.equal("data", parsed.body) + end) + + it("-d data -X PUT", function() + local args = [[curl -d data -X PUT 'https://example.com/put']] + local parsed = CURL.parse(args) + assert.equal("PUT", parsed.method) + assert.equal("https://example.com/put", parsed.url) + assert.equal("data", parsed.body) + assert.equal("application/x-www-form-urlencoded", parsed.headers["content-type"]) + end) + + it("--data data", function() + local args = [[curl --data data 'https://example.com/post']] + local parsed = CURL.parse(args) + assert.equal("POST", parsed.method) + assert.equal("https://example.com/post", parsed.url) + assert.equal("data", parsed.body) + assert.equal("application/x-www-form-urlencoded", parsed.headers["content-type"]) + end) + + it("--data-raw data", function() + local args = [[curl --data-raw data 'https://example.com/post']] + local parsed = CURL.parse(args) + assert.equal("POST", parsed.method) + assert.equal("https://example.com/post", parsed.url) + assert.equal("data", parsed.body) + assert.equal("application/x-www-form-urlencoded", parsed.headers["content-type"]) + end) + + it("-d data -H 'content-type: text/plain'", function() + local args = [[curl -H 'content-type: text/plain' -d data 'https://example.com/post']] + local parsed = CURL.parse(args) + assert.equal("POST", parsed.method) + assert.equal("https://example.com/post", parsed.url) + assert.equal("data", parsed.body) + assert.equal("text/plain", parsed.headers["content-type"]) + end) + + it("--json", function() + local args = [[curl --json '{"j": "son"}' 'https://example.com/post']] + local parsed = CURL.parse(args) + assert.equal("POST", parsed.method) + assert.equal("https://example.com/post", parsed.url) + assert.equal('{"j": "son"}', parsed.body) + assert.equal("application/json", parsed.headers["content-type"]) + assert.equal("application/json", parsed.headers["accept"]) + end) + + it("--http1.1", function() + local args = [[curl --http1.1 'https://example.com/get']] + local parsed = CURL.parse(args) + assert.equal("GET", parsed.method) + assert.equal("https://example.com/get", parsed.url) + assert.equal("HTTP/1.1", parsed.http_version) + end) + + it("--http2", function() + local args = [[curl --http2 'https://example.com/get']] + local parsed = CURL.parse(args) + assert.equal("GET", parsed.method) + assert.equal("https://example.com/get", parsed.url) + assert.equal("HTTP/2", parsed.http_version) + end) + + it("--http3", function() + local args = [[curl --http3 'https://example.com/get']] + local parsed = CURL.parse(args) + assert.equal("GET", parsed.method) + assert.equal("https://example.com/get", parsed.url) + assert.equal("HTTP/3", parsed.http_version) + end) + + it("trim header value", function() + local args = [[curl -H 'key: value ' 'https://example.com/get']] + local parsed = CURL.parse(args) + assert.equal("GET", parsed.method) + assert.equal("https://example.com/get", parsed.url) + assert.equal("value", parsed.headers["key"]) + end) + + it("header key case", function() + local args = [[curl -H 'Header-Key:value' 'https://example.com/get']] + local parsed = CURL.parse(args) + assert.equal("GET", parsed.method) + assert.equal("https://example.com/get", parsed.url) + assert.equal("value", parsed.headers["header-key"]) + end) +end)