@ 261105.2
current
local shell = require("fzf-lua.shell")
-- local MiniTest = require("mini.test")
local ok = function(f, ...) assert(pcall(f, ...)) end
local err = function(f, ...)
  local _ok, _err = pcall(f, ...)
  assert(not _ok, _err)
end

-- Drives the function registered by `stringify_data2` (the RPC receiving
-- function) synchronously, as if fzf sent `lines` via the action pipe:
-- stubs `uv.pipe_connect` to succeed immediately and `vim.schedule` to
-- run inline. `cmd` is the stringified shell command, from which the
-- registered function id is parsed as the argument following `rpc.lua`.
-- NOTE: the rpc.lua path is shellescaped differently per platform
-- ('...' on Unix, "^"...^"" or "..." on Windows), so we match any
-- non-digit run after `rpc.lua` up to the id rather than a hard quote.
local function simulate_fzf_output(cmd, lines)
  local id = tonumber(assert(cmd:match("rpc%.lua%D-(%d+)")))
  local uv = vim.uv
  local orig_pipe_connect, orig_schedule = uv.pipe_connect, vim.schedule
  ---@diagnostic disable: duplicate-set-field
  ---@param cb? fun(err: string?)
  uv.pipe_connect = function(_, _, cb) if cb then cb(nil) end end
  vim.schedule = function(f) f() end
  ---@diagnostic enable: duplicate-set-field
  local ok_, err_ = pcall(shell.get_func(id), {
    pipe_path = "",
    selection = lines,
    env = {},
  })
  uv.pipe_connect, vim.schedule = orig_pipe_connect, orig_schedule
  assert(ok_, err_)
end

describe("Testing shell module", function()
  it("stringify_data2 appends '{n} {q}' and extracts idx, query in that order", function()
    local opts = { __resume_key = "shell_spec" }
    ---@type string[]
    local captured
    local cmd = shell.stringify_data2(function(items, _)
      captured = vim.deepcopy(items)
      return ""
    end, opts, "{+}")
    -- "{n} {q}" (not "{q} {n}") is appended to the field index
    assert(cmd:match("{%+} {n} {q}"), cmd)
    -- simulate fzf output: last two lines are "{n}" (idx) then "{q}" (query)
    simulate_fzf_output(cmd, { "entry", "3", "query" })
    -- trailing "{n} {q}" lines are stripped from the items
    assert(#captured == 1 and captured[1] == "entry")
    -- resume query is set from the last line ("{q}")
    assert(FzfLua.config.resume_get("query", opts) == "query")
    assert(opts.last_query == "query")
  end)

  it("stringify_data2 '{n} {q}' order keeps zero-match/zero-selected detection", function()
    local opts = { __resume_key = "shell_spec" }
    ---@type string[]
    local captured
    local cmd = shell.stringify_data2(function(items, _)
      captured = vim.deepcopy(items)
      return ""
    end, opts, "{+}")
    -- no match + no selection: both {n} and {+} expand to "", items
    -- arriving as { "", "", "<query>" } must be converted to {}
    simulate_fzf_output(cmd, { "", "", "query" })
    assert(#captured == 0)
  end)

  it("check_upvalue", function()
    ok(shell.check_upvalue, function() end, "no upvalue")

    ---@diagnostic disable: need-check-nil
    err(shell.check_upvalue, (function()
      local upvalue
      ---@diagnostic disable-next-line: undefined-field
      return function() return upvalue.index end
    end)(), "index upvalue")

    err(shell.check_upvalue, (function()
      local upvalue
      return function() return upvalue() end
    end)(), "call upvalue")
    ---@diagnostic enable: need-check-nil
  end)
end)