local fs_snapshot = require("assay.fs_snapshot")
local function mock_shell(script)
local captured = {}
local orig = shell.exec
shell.exec = function(cmd, opts)
table.insert(captured, { cmd = cmd, opts = opts })
for _, entry in ipairs(script) do
if string.find(cmd, entry.match, 1, true) then
return { status = entry.status or 0, stdout = entry.stdout or "", stderr = entry.stderr or "" }
end
end
return { status = 0, stdout = "", stderr = "" }
end
return captured, function() shell.exec = orig end
end
local function with_mock(script, fn)
local cap, restore = mock_shell(script)
local ok, ret = pcall(fn)
restore()
if not ok then error(ret) end
return ret, cap
end
do
local r = with_mock({
{ match = "id -u", status = 0, stdout = "1000\n" },
{ match = "findmnt", status = 0, stdout = "/dev/sda3 btrfs\n" },
}, function() return fs_snapshot.detect("/var/lib/machines") end)
assert.eq(r.backend, "btrfs", "expected btrfs, got " .. tostring(r.backend))
assert.eq(r.source, "/dev/sda3", "btrfs source")
assert.eq(r.fstype, "btrfs", "btrfs fstype")
end
do
local r = with_mock({
{ match = "id -u", status = 0, stdout = "1000\n" },
{ match = "findmnt", status = 0, stdout = "tank/data zfs\n" },
}, function() return fs_snapshot.detect("/srv/data") end)
assert.eq(r.backend, "zfs", "expected zfs, got " .. tostring(r.backend))
assert.eq(r.source, "tank/data", "zfs source")
end
do
local r = with_mock({
{ match = "id -u", status = 0, stdout = "1000\n" },
{ match = "findmnt", status = 0, stdout = "/dev/sda1 ext4\n" },
}, function() return fs_snapshot.detect("/etc") end)
assert.eq(r.backend, "none", "expected none for ext4")
end
do
local _, cap = with_mock({
{ match = "id -u", status = 0, stdout = "1000\n" }, { match = "findmnt", status = 0, stdout = "/dev/sda3 btrfs\n" },
{ match = "btrfs subvolume snapshot", status = 0, stdout = "" },
}, function() return fs_snapshot.take("manual", "/var/lib/machines") end)
local found = false
for _, c in ipairs(cap) do
if string.find(c.cmd, "btrfs subvolume snapshot -r", 1, true) then
assert.contains(c.cmd, "sudo -n", "expected sudo prefix as non-root")
assert.contains(c.cmd, "/var/lib/machines", "source path in cmd")
assert.contains(c.cmd, ".assay-snap-manual-", "snap path naming")
found = true
end
end
assert.eq(found, true, "expected btrfs snapshot command in captured calls")
end
do
local _, cap = with_mock({
{ match = "id -u", status = 0, stdout = "0\n" }, { match = "findmnt", status = 0, stdout = "tank/data zfs\n" },
{ match = "zfs snapshot", status = 0, stdout = "" },
}, function() return fs_snapshot.take("daily", "/srv/data") end)
local found = false
for _, c in ipairs(cap) do
if string.find(c.cmd, "zfs snapshot", 1, true) then
assert.eq(string.find(c.cmd, "sudo -n", 1, true), nil, "no sudo prefix as root")
assert.contains(c.cmd, "tank/data@daily-", "snap ref shape: " .. c.cmd)
found = true
end
end
assert.eq(found, true, "expected zfs snapshot command")
end
do
local r = with_mock({
{ match = "id -u", status = 0, stdout = "0\n" },
{ match = "findmnt", status = 0, stdout = "/dev/sda1 ext4\n" },
}, function() return fs_snapshot.take("x", "/etc") end)
assert.eq(r.backend, "none", "none backend")
assert.eq(r.path, "/etc", "no-op path is original")
end
do
local _, cap = with_mock({
{ match = "id -u", status = 0, stdout = "0\n" },
{ match = "btrfs subvolume delete", status = 0, stdout = "" },
}, function()
return fs_snapshot.release({ backend = "btrfs", path = "/foo/.assay-snap-x-1" })
end)
local found = false
for _, c in ipairs(cap) do
if string.find(c.cmd, "btrfs subvolume delete", 1, true) then found = true end
end
assert.eq(found, true, "expected btrfs delete cmd")
end
do
local r = fs_snapshot.release({ backend = "none", path = "/x" })
assert.eq(r.ok, true, "none release is ok")
end
do
local released = false
local orig_release = fs_snapshot.release
fs_snapshot.release = function(h) released = true; return { ok = true } end
local _, cap = with_mock({
{ match = "id -u", status = 0, stdout = "0\n" },
{ match = "findmnt", status = 0, stdout = "/dev/sda1 ext4\n" },
}, function()
local ok = pcall(fs_snapshot.with_snapshot, "x", "/x", function(_h) error("boom") end)
assert.eq(ok, false, "wrapped fn error should propagate")
end)
fs_snapshot.release = orig_release
assert.eq(released, true, "release should still run on error")
end
print("fs_snapshot backend_dispatch.lua: 8 cases passed")