local M = {} function M.same(expected, actual, message) if expected ~= actual then error((message or "values differ") .. ": expected " .. tostring(expected) .. ", got " .. tostring(actual), 2) end end function M.truthy(value, message) if not value then error(message or "value must be true", 2) end end function M.falsy(value, message) if value then error(message or "value must be false", 2) end end function M.fails(body, expected_text) local ok, failure = pcall(body) if ok then error("operation must fail", 2) end if expected_text and not tostring(failure):find(expected_text, 1, true) then error("failure does not contain '" .. expected_text .. "': " .. tostring(failure), 2) end end function M.list_same(expected, actual) M.same(#expected, #actual, "list lengths differ") for index = 1, #expected do M.same(expected[index], actual[index], "list item " .. tostring(index) .. " differs") end end return M