- Julia: Rename test() to run_test() to avoid name collision - Julia: Use lambda syntax instead of do-block for cleaner execution - Lua: Fix signature format test - verify structure instead of counting colons - OCaml: Use apt-get instead of failing setup-ocaml@v2 action
319 lines
10 KiB
Lua
Executable file
319 lines
10 KiB
Lua
Executable file
#!/usr/bin/env lua
|
|
-- Unit tests for un.lua - tests internal functions without API calls
|
|
|
|
local passed = 0
|
|
local failed = 0
|
|
|
|
local function test(name, fn)
|
|
local ok, err = pcall(fn)
|
|
if ok then
|
|
print(" ✓ " .. name)
|
|
passed = passed + 1
|
|
else
|
|
print(" ✗ " .. name)
|
|
print(" " .. tostring(err))
|
|
failed = failed + 1
|
|
end
|
|
end
|
|
|
|
local function assert_equal(actual, expected, msg)
|
|
if actual ~= expected then
|
|
error(string.format("Expected '%s' but got '%s' %s", tostring(expected), tostring(actual), msg or ""))
|
|
end
|
|
end
|
|
|
|
local function assert_not_equal(a, b)
|
|
if a == b then
|
|
error(string.format("Expected values to be different but both were '%s'", tostring(a)))
|
|
end
|
|
end
|
|
|
|
local function assert_contains(str, substr)
|
|
if not string.find(str, substr, 1, true) then
|
|
error(string.format("Expected '%s' to contain '%s'", str, substr))
|
|
end
|
|
end
|
|
|
|
local function assert_true(val)
|
|
if not val then
|
|
error("Expected true but got false")
|
|
end
|
|
end
|
|
|
|
-- Extension mapping (from un.lua)
|
|
local ext_map = {
|
|
[".py"] = "python", [".js"] = "javascript", [".ts"] = "typescript",
|
|
[".rb"] = "ruby", [".php"] = "php", [".pl"] = "perl", [".lua"] = "lua",
|
|
[".sh"] = "bash", [".go"] = "go", [".rs"] = "rust", [".c"] = "c",
|
|
[".cpp"] = "cpp", [".cc"] = "cpp", [".cxx"] = "cpp",
|
|
[".java"] = "java", [".kt"] = "kotlin", [".cs"] = "csharp", [".fs"] = "fsharp",
|
|
[".hs"] = "haskell", [".ml"] = "ocaml", [".clj"] = "clojure", [".scm"] = "scheme",
|
|
[".lisp"] = "commonlisp", [".erl"] = "erlang", [".ex"] = "elixir", [".exs"] = "elixir",
|
|
[".jl"] = "julia", [".r"] = "r", [".R"] = "r", [".cr"] = "crystal",
|
|
[".d"] = "d", [".nim"] = "nim", [".zig"] = "zig", [".v"] = "v",
|
|
[".dart"] = "dart", [".groovy"] = "groovy", [".scala"] = "scala",
|
|
[".f90"] = "fortran", [".f95"] = "fortran", [".cob"] = "cobol",
|
|
[".pro"] = "prolog", [".forth"] = "forth", [".4th"] = "forth",
|
|
[".tcl"] = "tcl", [".raku"] = "raku", [".m"] = "objc",
|
|
}
|
|
|
|
-- ============================================================================
|
|
-- Extension Mapping Tests
|
|
-- ============================================================================
|
|
|
|
print("\n=== Extension Mapping Tests ===")
|
|
|
|
test("Python extension maps correctly", function()
|
|
assert_equal(ext_map[".py"], "python")
|
|
end)
|
|
|
|
test("JavaScript extensions map correctly", function()
|
|
assert_equal(ext_map[".js"], "javascript")
|
|
assert_equal(ext_map[".ts"], "typescript")
|
|
end)
|
|
|
|
test("Ruby extension maps correctly", function()
|
|
assert_equal(ext_map[".rb"], "ruby")
|
|
end)
|
|
|
|
test("Go extension maps correctly", function()
|
|
assert_equal(ext_map[".go"], "go")
|
|
end)
|
|
|
|
test("Rust extension maps correctly", function()
|
|
assert_equal(ext_map[".rs"], "rust")
|
|
end)
|
|
|
|
test("C/C++ extensions map correctly", function()
|
|
assert_equal(ext_map[".c"], "c")
|
|
assert_equal(ext_map[".cpp"], "cpp")
|
|
assert_equal(ext_map[".cc"], "cpp")
|
|
assert_equal(ext_map[".cxx"], "cpp")
|
|
end)
|
|
|
|
test("Lua extension maps correctly", function()
|
|
assert_equal(ext_map[".lua"], "lua")
|
|
end)
|
|
|
|
test("JVM extensions map correctly", function()
|
|
assert_equal(ext_map[".java"], "java")
|
|
assert_equal(ext_map[".kt"], "kotlin")
|
|
assert_equal(ext_map[".groovy"], "groovy")
|
|
end)
|
|
|
|
test("Functional language extensions map correctly", function()
|
|
assert_equal(ext_map[".hs"], "haskell")
|
|
assert_equal(ext_map[".ml"], "ocaml")
|
|
assert_equal(ext_map[".clj"], "clojure")
|
|
assert_equal(ext_map[".erl"], "erlang")
|
|
end)
|
|
|
|
-- ============================================================================
|
|
-- HMAC Signature Tests (using openssl via shell)
|
|
-- ============================================================================
|
|
|
|
print("\n=== HMAC Signature Tests ===")
|
|
|
|
local function hmac_sha256(secret, message)
|
|
-- Use printf for better portability and awk to extract just the hash
|
|
local cmd = string.format("printf '%%s' '%s' | openssl dgst -sha256 -hmac '%s' | awk '{print $NF}'",
|
|
message:gsub("'", "'\\''"), secret:gsub("'", "'\\''"))
|
|
local handle = io.popen(cmd)
|
|
if handle then
|
|
local result = handle:read("*a"):gsub("%s+$", "")
|
|
handle:close()
|
|
return result
|
|
end
|
|
return nil
|
|
end
|
|
|
|
test("HMAC-SHA256 generates 64 character hex string", function()
|
|
local sig = hmac_sha256("test-secret", "test-message")
|
|
if sig then
|
|
assert_equal(#sig, 64)
|
|
end
|
|
end)
|
|
|
|
test("Same input produces same signature", function()
|
|
local sig1 = hmac_sha256("key", "message")
|
|
local sig2 = hmac_sha256("key", "message")
|
|
if sig1 and sig2 then
|
|
assert_equal(sig1, sig2)
|
|
end
|
|
end)
|
|
|
|
test("Different secrets produce different signatures", function()
|
|
local sig1 = hmac_sha256("key1", "message")
|
|
local sig2 = hmac_sha256("key2", "message")
|
|
if sig1 and sig2 then
|
|
assert_not_equal(sig1, sig2)
|
|
end
|
|
end)
|
|
|
|
test("Different messages produce different signatures", function()
|
|
local sig1 = hmac_sha256("key", "message1")
|
|
local sig2 = hmac_sha256("key", "message2")
|
|
if sig1 and sig2 then
|
|
assert_not_equal(sig1, sig2)
|
|
end
|
|
end)
|
|
|
|
test("Signature format is timestamp:METHOD:path:body", function()
|
|
local timestamp = "1704067200"
|
|
local method = "POST"
|
|
local endpoint = "/execute"
|
|
local body = '{"language":"python"}'
|
|
|
|
local message = timestamp .. ":" .. method .. ":" .. endpoint .. ":" .. body
|
|
|
|
-- Verify format by checking structure, not counting colons (body may contain colons)
|
|
assert_true(message:sub(1, #timestamp) == timestamp)
|
|
assert_contains(message, ":POST:")
|
|
assert_contains(message, ":/execute:")
|
|
end)
|
|
|
|
-- ============================================================================
|
|
-- Language Detection Tests
|
|
-- ============================================================================
|
|
|
|
print("\n=== Language Detection Tests ===")
|
|
|
|
local function get_extension(filename)
|
|
return filename:match("%.([^%.]+)$")
|
|
end
|
|
|
|
local function detect_from_shebang(first_line)
|
|
if first_line:sub(1, 2) == "#!" then
|
|
if first_line:find("python") then return "python" end
|
|
if first_line:find("node") then return "javascript" end
|
|
if first_line:find("ruby") then return "ruby" end
|
|
if first_line:find("perl") then return "perl" end
|
|
if first_line:find("bash") or first_line:find("/sh") then return "bash" end
|
|
if first_line:find("lua") then return "lua" end
|
|
if first_line:find("php") then return "php" end
|
|
end
|
|
return nil
|
|
end
|
|
|
|
test("Detect language from .py extension", function()
|
|
local ext = "." .. get_extension("script.py")
|
|
assert_equal(ext_map[ext], "python")
|
|
end)
|
|
|
|
test("Detect language from .lua extension", function()
|
|
local ext = "." .. get_extension("script.lua")
|
|
assert_equal(ext_map[ext], "lua")
|
|
end)
|
|
|
|
test("Python shebang detection", function()
|
|
assert_equal(detect_from_shebang("#!/usr/bin/env python3"), "python")
|
|
end)
|
|
|
|
test("Node shebang detection", function()
|
|
assert_equal(detect_from_shebang("#!/usr/bin/env node"), "javascript")
|
|
end)
|
|
|
|
test("Lua shebang detection", function()
|
|
assert_equal(detect_from_shebang("#!/usr/bin/env lua"), "lua")
|
|
end)
|
|
|
|
test("Bash shebang detection", function()
|
|
assert_equal(detect_from_shebang("#!/bin/bash"), "bash")
|
|
end)
|
|
|
|
-- ============================================================================
|
|
-- Argument Parsing Tests
|
|
-- ============================================================================
|
|
|
|
print("\n=== Argument Parsing Tests ===")
|
|
|
|
local function parse_env_var(arg)
|
|
local key, value = arg:match("^([^=]+)=(.*)$")
|
|
return key, value
|
|
end
|
|
|
|
test("Parse -e KEY=VALUE format", function()
|
|
local key, value = parse_env_var("DEBUG=1")
|
|
assert_equal(key, "DEBUG")
|
|
assert_equal(value, "1")
|
|
end)
|
|
|
|
test("Parse -e KEY=VALUE with equals in value", function()
|
|
local key, value = parse_env_var("URL=https://example.com?foo=bar")
|
|
assert_equal(key, "URL")
|
|
assert_equal(value, "https://example.com?foo=bar")
|
|
end)
|
|
|
|
test("Valid network modes", function()
|
|
local valid_modes = { zerotrust = true, semitrusted = true }
|
|
assert_true(valid_modes["zerotrust"])
|
|
assert_true(valid_modes["semitrusted"])
|
|
assert_true(not valid_modes["invalid"])
|
|
end)
|
|
|
|
test("Subcommand detection", function()
|
|
local subcommands = { session = true, service = true, key = true, restore = true }
|
|
assert_true(subcommands["session"])
|
|
assert_true(subcommands["service"])
|
|
assert_true(not subcommands["script.py"])
|
|
end)
|
|
|
|
-- ============================================================================
|
|
-- File Operations Tests
|
|
-- ============================================================================
|
|
|
|
print("\n=== File Operations Tests ===")
|
|
|
|
test("Read text file", function()
|
|
local tmpname = os.tmpname() .. ".py"
|
|
local f = io.open(tmpname, "w")
|
|
f:write('print("hello world")')
|
|
f:close()
|
|
|
|
f = io.open(tmpname, "r")
|
|
local content = f:read("*a")
|
|
f:close()
|
|
os.remove(tmpname)
|
|
|
|
assert_equal(content, 'print("hello world")')
|
|
end)
|
|
|
|
test("Extract file basename", function()
|
|
local path = "/home/user/project/script.py"
|
|
local basename = path:match("([^/]+)$")
|
|
assert_equal(basename, "script.py")
|
|
end)
|
|
|
|
test("Extract file extension", function()
|
|
local path = "/home/user/project/script.py"
|
|
local ext = path:match("%.([^%.]+)$")
|
|
assert_equal(ext, "py")
|
|
end)
|
|
|
|
-- ============================================================================
|
|
-- API Constants Tests
|
|
-- ============================================================================
|
|
|
|
print("\n=== API Constants Tests ===")
|
|
|
|
test("API base URL format", function()
|
|
local api_base = "https://api.unsandbox.com"
|
|
assert_true(api_base:sub(1, 8) == "https://")
|
|
assert_contains(api_base, "unsandbox.com")
|
|
end)
|
|
|
|
test("Portal base URL format", function()
|
|
local portal_base = "https://unsandbox.com"
|
|
assert_true(portal_base:sub(1, 8) == "https://")
|
|
end)
|
|
|
|
-- ============================================================================
|
|
-- Summary
|
|
-- ============================================================================
|
|
|
|
print("\n=== Summary ===")
|
|
print("Passed: " .. passed)
|
|
print("Failed: " .. failed)
|
|
print("Total: " .. (passed + failed))
|
|
|
|
os.exit(failed > 0 and 1 or 0)
|