- Update all un.* implementations to use HMAC-SHA256 signing - Headers: Authorization (Bearer public_key), X-Timestamp, X-Signature - Signature: HMAC-SHA256(secret_key, "timestamp:METHOD:path:body") - Fix test suite issues (bash arithmetic, ES module compat, TCL shebang) - Add CLAUDE.md with inception testing documentation - Update README.md with HMAC auth and dependency table - All 38 implementations pass inception test via un2
228 lines
7.6 KiB
Lua
Executable file
228 lines
7.6 KiB
Lua
Executable file
#!/usr/bin/env lua
|
|
-- Test suite for UN CLI Lua implementation (un.lua)
|
|
-- Tests extension detection, API calls, and end-to-end functionality
|
|
|
|
-- Try to load optional dependencies
|
|
local has_https, https = pcall(require, "ssl.https")
|
|
local has_ltn12, ltn12 = pcall(require, "ltn12")
|
|
local has_json, json = pcall(require, "cjson")
|
|
|
|
-- Test configuration
|
|
local script_dir = arg[0]:match("(.*/)") or "./"
|
|
local UN_SCRIPT = script_dir .. "../un.lua"
|
|
local FIB_PY = script_dir .. "../../test/fib.py"
|
|
|
|
-- TestResults class
|
|
local TestResults = {}
|
|
TestResults.__index = TestResults
|
|
|
|
function TestResults:new()
|
|
local obj = {
|
|
passed = 0,
|
|
failed = 0,
|
|
skipped = 0
|
|
}
|
|
setmetatable(obj, TestResults)
|
|
return obj
|
|
end
|
|
|
|
function TestResults:passTest(name)
|
|
print("PASS: " .. name)
|
|
self.passed = self.passed + 1
|
|
end
|
|
|
|
function TestResults:failTest(name, error)
|
|
print("FAIL: " .. name .. " - " .. error)
|
|
self.failed = self.failed + 1
|
|
end
|
|
|
|
function TestResults:skipTest(name, reason)
|
|
print("SKIP: " .. name .. " - " .. reason)
|
|
self.skipped = self.skipped + 1
|
|
end
|
|
|
|
local results = TestResults:new()
|
|
|
|
-- Extension map for testing
|
|
local EXTENSION_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",
|
|
[".java"] = "java", [".kt"] = "kotlin", [".cs"] = "csharp", [".hs"] = "haskell",
|
|
[".ml"] = "ocaml", [".clj"] = "clojure", [".ex"] = "elixir", [".erl"] = "erlang",
|
|
[".swift"] = "swift", [".r"] = "r", [".jl"] = "julia", [".dart"] = "dart",
|
|
[".scala"] = "scala", [".groovy"] = "groovy", [".nim"] = "nim", [".cr"] = "crystal",
|
|
[".v"] = "vlang", [".zig"] = "zig", [".fs"] = "fsharp", [".vb"] = "vb",
|
|
[".pas"] = "pascal", [".f90"] = "fortran", [".asm"] = "assembly", [".d"] = "d",
|
|
[".rkt"] = "racket", [".scm"] = "scheme", [".lisp"] = "common_lisp",
|
|
[".sol"] = "solidity", [".cob"] = "cobol", [".ada"] = "ada", [".tcl"] = "tcl",
|
|
}
|
|
|
|
local function detect_language(filename)
|
|
local ext = filename:match("%.([^.]+)$")
|
|
if ext then
|
|
return EXTENSION_MAP["." .. ext:lower()]
|
|
end
|
|
return nil
|
|
end
|
|
|
|
-- Test 1: Extension detection for Python
|
|
local status, err = pcall(function()
|
|
local lang = detect_language('test.py')
|
|
if lang == 'python' then
|
|
results:passTest('Extension detection: .py -> python')
|
|
else
|
|
results:failTest('Extension detection: .py -> python', "Got " .. tostring(lang))
|
|
end
|
|
end)
|
|
if not status then
|
|
results:failTest('Extension detection: .py -> python', err)
|
|
end
|
|
|
|
-- Test 2: Extension detection for JavaScript
|
|
status, err = pcall(function()
|
|
local lang = detect_language('test.js')
|
|
if lang == 'javascript' then
|
|
results:passTest('Extension detection: .js -> javascript')
|
|
else
|
|
results:failTest('Extension detection: .js -> javascript', "Got " .. tostring(lang))
|
|
end
|
|
end)
|
|
if not status then
|
|
results:failTest('Extension detection: .js -> javascript', err)
|
|
end
|
|
|
|
-- Test 3: Extension detection for Ruby
|
|
status, err = pcall(function()
|
|
local lang = detect_language('test.rb')
|
|
if lang == 'ruby' then
|
|
results:passTest('Extension detection: .rb -> ruby')
|
|
else
|
|
results:failTest('Extension detection: .rb -> ruby', "Got " .. tostring(lang))
|
|
end
|
|
end)
|
|
if not status then
|
|
results:failTest('Extension detection: .rb -> ruby', err)
|
|
end
|
|
|
|
-- Test 4: Extension detection for Go
|
|
status, err = pcall(function()
|
|
local lang = detect_language('test.go')
|
|
if lang == 'go' then
|
|
results:passTest('Extension detection: .go -> go')
|
|
else
|
|
results:failTest('Extension detection: .go -> go', "Got " .. tostring(lang))
|
|
end
|
|
end)
|
|
if not status then
|
|
results:failTest('Extension detection: .go -> go', err)
|
|
end
|
|
|
|
-- Test 5: Extension detection for Rust
|
|
status, err = pcall(function()
|
|
local lang = detect_language('test.rs')
|
|
if lang == 'rust' then
|
|
results:passTest('Extension detection: .rs -> rust')
|
|
else
|
|
results:failTest('Extension detection: .rs -> rust', "Got " .. tostring(lang))
|
|
end
|
|
end)
|
|
if not status then
|
|
results:failTest('Extension detection: .rs -> rust', err)
|
|
end
|
|
|
|
-- Test 6: Extension detection for unknown extension
|
|
status, err = pcall(function()
|
|
local lang = detect_language('test.unknown')
|
|
if lang == nil then
|
|
results:passTest('Extension detection: .unknown -> nil')
|
|
else
|
|
results:failTest('Extension detection: .unknown -> nil', "Got " .. tostring(lang))
|
|
end
|
|
end)
|
|
if not status then
|
|
results:failTest('Extension detection: .unknown -> nil', err)
|
|
end
|
|
|
|
-- Test 7: API call test (requires UNSANDBOX auth)
|
|
local has_hmac = os.getenv("UNSANDBOX_PUBLIC_KEY") and os.getenv("UNSANDBOX_SECRET_KEY")
|
|
local has_legacy = os.getenv("UNSANDBOX_API_KEY")
|
|
if not (has_hmac or has_legacy) then
|
|
results:skipTest('API call test', 'UNSANDBOX authentication not set')
|
|
elseif not (has_https and has_ltn12 and has_json) then
|
|
results:skipTest('API call test', 'Required Lua libraries not available (luasocket, luasec, lua-cjson)')
|
|
else
|
|
status, err = pcall(function()
|
|
local payload = json.encode({
|
|
language = 'python',
|
|
code = 'print("Hello from API")'
|
|
})
|
|
|
|
local response_body = {}
|
|
local res, code, headers, status_text = https.request{
|
|
url = "https://api.unsandbox.com/execute",
|
|
method = "POST",
|
|
headers = {
|
|
["Authorization"] = "Bearer " .. os.getenv("UNSANDBOX_API_KEY"),
|
|
["Content-Type"] = "application/json",
|
|
["Content-Length"] = tostring(#payload)
|
|
},
|
|
source = ltn12.source.string(payload),
|
|
sink = ltn12.sink.table(response_body)
|
|
}
|
|
|
|
if code == 200 then
|
|
local result = json.decode(table.concat(response_body))
|
|
if result.stdout and result.stdout:find('Hello from API') then
|
|
results:passTest('API call test')
|
|
else
|
|
results:failTest('API call test', "Unexpected result: " .. json.encode(result))
|
|
end
|
|
else
|
|
results:failTest('API call test', "HTTP " .. code .. ": " .. table.concat(response_body))
|
|
end
|
|
end)
|
|
if not status then
|
|
results:failTest('API call test', err)
|
|
end
|
|
end
|
|
|
|
-- Test 8: End-to-end test with fib.py
|
|
if not (has_hmac or has_legacy) then
|
|
results:skipTest('End-to-end fib.py test', 'UNSANDBOX authentication not set')
|
|
else
|
|
-- Check if fib.py exists
|
|
local file = io.open(FIB_PY, "r")
|
|
if not file then
|
|
results:skipTest('End-to-end fib.py test', 'fib.py not found at ' .. FIB_PY)
|
|
else
|
|
file:close()
|
|
status, err = pcall(function()
|
|
local handle = io.popen(UN_SCRIPT .. ' ' .. FIB_PY .. ' 2>&1')
|
|
local output = handle:read("*a")
|
|
handle:close()
|
|
|
|
if output:find('fib%(10%) = 55') then
|
|
results:passTest('End-to-end fib.py test')
|
|
else
|
|
results:failTest('End-to-end fib.py test',
|
|
"Expected 'fib(10) = 55' in output, got: " .. output:sub(1, 200))
|
|
end
|
|
end)
|
|
if not status then
|
|
results:failTest('End-to-end fib.py test', err)
|
|
end
|
|
end
|
|
end
|
|
|
|
-- Print summary
|
|
print("\n" .. string.rep("=", 50))
|
|
print("Test Summary:")
|
|
print(" PASSED: " .. results.passed)
|
|
print(" FAILED: " .. results.failed)
|
|
print(" SKIPPED: " .. results.skipped)
|
|
print(" TOTAL: " .. (results.passed + results.failed + results.skipped))
|
|
print(string.rep("=", 50))
|
|
|
|
-- Exit with appropriate code
|
|
os.exit(results.failed == 0 and 0 or 1)
|