Add unit tests and correct bugs detected by the tests

This commit is contained in:
DanyLE
2023-01-25 17:01:01 +01:00
parent d4bb12c6b5
commit 7711526a7c
29 changed files with 1814 additions and 1024 deletions

3
test/ad.ls Normal file
View File

@@ -0,0 +1,3 @@
<?lua
echo(ad)
?>

3
test/detail.ls Normal file
View File

@@ -0,0 +1,3 @@
<?lua
echo("Post ID:", data.id, "\n", "Content:", data.content,"\n")
?>

4
test/layout.ls Normal file
View File

@@ -0,0 +1,4 @@
<?lua
__main__:render()
ad:render()
?>

46
test/lunit.lua Normal file
View File

@@ -0,0 +1,46 @@
TESTS = {}
function test(description, fn)
TESTS[#TESTS + 1] = {description = description, test = fn }
end
function run()
local report = {
ok = 0,
fail= 0,
total = #TESTS
}
for l,ts in ipairs(TESTS) do
io.write(string.format("Executing: %s...",ts.description))
local status,err = pcall(ts.test)
if status then
io.write("\27[32mOK\27[0m\n")
report.ok = report.ok + 1
else
io.write("\27[31mFAIL\27[0m\n")
print(err)
report.fail = report.fail + 1
end
end
print("----------------------------")
print(string.format("Total tests: %d", report.total))
print(string.format("Tests passed: %d", report.ok))
print(string.format("Tests failed: %d", report.fail))
TESTS = {}
end
function assert(b, e,...)
if not b then
error(string.format(e,...))
print(debug.traceback())
end
end
function expect(v1,v2)
assert(v1 == v2, "Expect: [%s] get: [%s]", tostring(v2), tostring(v1))
end
function unexpect(v1,v2)
assert(v1 ~= v2, "Unexpect value", tostring(v2))
end

37
test/request.json Normal file
View File

@@ -0,0 +1,37 @@
{
"PATH_INFO": "luacgi/lua/test.lua",
"REDIRECT_STATUS": "200",
"SCRIPT_NAME": "test.lua",
"HTTP_ACCEPT_ENCODING": "gzip, deflate",
"DOCUMENT_ROOT": "/tmp/www",
"REMOTE_ADDR": "192.168.1.44",
"REQUEST_URI": "/luacgi/lua/test.lua?r=1&id=3&name=John",
"SERVER_PROTOCOL": "HTTP/1.1",
"SERVER_NAME": "Antd",
"PATH_TRANSLATED": "/opt/www/htdocs/lua/test.lua",
"RAW_DATA": "firstname=Dany&lastname=LE&form_submitted=1",
"CONTENT_TYPE": "application/x-www-form-urlencoded",
"HTTP_ORIGIN": "http://192.168.1.27",
"HTTP_ACCEPT": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9",
"REMOTE_HOST": "192.168.1.44",
"HTTP_ACCEPT_LANGUAGE": "en-US,en;q=0.9",
"HTTP_CONTENT_LENGTH": "43",
"HTTP_CONNECTION": "keep-alive",
"HTTP_COOKIE": "PHPSESSID=298zf09hf012fh2; csrftoken=u32t4o3tb3gg43; _gat=1",
"LIB_DIR": "/tmp/lib",
"REQUEST_METHOD": "POST",
"HTTP_REFERER": "http://192.168.1.27/php/sign.html",
"SERVER_SOFTWARE": "Antd",
"QUERY_STRING": "r=post/id/1&id=3&name=John",
"TMP_DIR": "/tmp",
"HTTP_USER_AGENT": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/109.0.0.0 Safari/537.36",
"HTTP_CONTENT_TYPE": "application/x-www-form-urlencoded",
"HTTP_UPGRADE_INSECURE_REQUESTS": "1",
"CONTENT_LENGTH": "43",
"GATEWAY_INTERFACE": "CGI/1.1",
"HTTP_HOST": "192.168.1.27",
"SERVER_PORT": "80",
"SCRIPT_FILENAME": "/opt/www/htdocs/lua/test.lua",
"DB_DIR": "/tmp",
"HTTP_CACHE_CONTROL": "max-age=0"
}

396
test/test_core.lua Normal file
View File

@@ -0,0 +1,396 @@
--- the binary shall be compiled with
--- make CFLAGS=-DLUA_SLICE_MAGIC=0x8AD73B9F
--- otherwise the tests unable to load C modules
require("lunit")
package.cpath = "/tmp/lib/lua/?.so"
package.path = ""
test("IO setup", function()
fcgio = { OUTPUT = "",
LOG = {
INFO = "",
ERROR = "",
DEBUG = "",
WARN = ""
}
}
function fcgio:flush()
fcgio.OUTPUT = ""
end
function fcgio:echo(...)
local args = table.pack(...)
for i=1,args.n do
-- do something with args[i], careful, it might be nil!
fcgio.OUTPUT = fcgio.OUTPUT..tostring(args[i])
end
end
function fcgio:log_info(fmt,...)
fcgio.LOG.INFO = string.format(fmt,...)
io.stderr:write("INFO: ", fcgio.LOG.INFO)
io.stderr:write("\n")
end
function fcgio:log_error(fmt,...)
fcgio.LOG.ERROR = string.format(fmt,...)
io.stderr:write("ERROR: ",fcgio.LOG.ERROR)
io.stderr:write("\n")
end
function fcgio:log_debug(fmt,...)
fcgio.LOG.DEBUG = string.format(fmt,...)
io.stderr:write("DEBUG: ", fcgio.LOG.DEBUG)
io.stderr:write("\n")
end
function fcgio:log_warn(fmt,...)
fcgio.LOG.WARN = string.format(fmt,...)
io.stderr:write("WARN: ", fcgio.LOG.WARN)
io.stderr:write("\n")
end
function fcgio:send_file(path)
local f = io.open(path, "rb")
local content = f:read("*all")
f:close()
fcgio.OUTPUT = fcgio.OUTPUT..content
end
end)
test("Setup request", function()
local json = require("json")
_SERVER = json.decodeFile("request.json")
assert(_SERVER ~= nil, "Global _SERVER object not found")
end)
test("SEVER PATH", function()
expect(_SERVER["LIB_DIR"], "/tmp/lib")
expect(_SERVER["TMP_DIR"], "/tmp")
expect(_SERVER["DB_DIR"], "/tmp")
expect(_SERVER["DOCUMENT_ROOT"], "/tmp/www")
end)
test("Import the hook", function()
package.path = "../silkmvc/?.lua"
local ret = require("core.hook")
expect(ret, true)
end)
test("Lua path", function()
expect(package.cpath, "/tmp/lib/lua/?.so")
expect(package.path, "/tmp/lib/lua/?.lua;/tmp/www/?.lua")
unexpect(ulib, nil)
unexpect(utils, nil)
unexpect(std, nil)
end)
test("HTTP Headers", function()
expect(HEADER["mobile"], false)
for k,v in pairs(_SERVER) do
if k:match("^HTTP_.*") then
local key = (k:gsub("HTTP_",""):gsub("_","-")):lower()
expect(HEADER[key],v)
end
end
end)
test("HTTP request", function()
expect(REQUEST.method, "POST")
expect(REQUEST.r, "post/id/1")
expect(REQUEST.id, "3")
expect(REQUEST.name, "John")
expect(REQUEST.firstname, "Dany")
expect(REQUEST.lastname, "LE")
expect(REQUEST.form_submitted, "1")
end)
test('HTTP COOKIE', function()
unexpect(SESSION, nil)
expect(SESSION.PHPSESSID, "298zf09hf012fh2")
expect(SESSION.csrftoken, "u32t4o3tb3gg43")
expect(SESSION._gat, "1")
end)
test("Echo", function()
echo("Hello ", "World: ", 10, true)
expect(fcgio.OUTPUT, "Hello World: 10true")
end)
test("STD response", function()
std.status(500)
expect(RESPONSE_HEADER.status, 500)
std.header("Content-Type", "text/html")
expect(RESPONSE_HEADER.header["Content-Type"], "text/html")
end)
test("STD Error", function()
fcgio:flush()
std.error(404, "No page found")
expect(fcgio.OUTPUT, "Status: 404 Not Found\r\nContent-Type: text/html\r\n\r\n<HTML><HEAD><TITLE>No page found</TITLE></HEAD><BODY><h2>No page found</h2></BODY></HTML>")
end)
test("STD header with cookie", function()
RESPONSE_HEADER.sent = false
fcgio:flush()
std.status(200)
std.header("Content-Type", "text/html")
std.setCookie("sessionid=12345;user=dany; path=/")
std.setCookie("date=now", "_gcat=1")
--print(JSON.encode(RESPONSE_HEADER))
std.header_flush()
echo("hello")
expect(fcgio.OUTPUT, "Status: 200 OK\r\nContent-Type: text/html\r\nSet-Cookie: sessionid=12345;user=dany; path=/\r\nSet-Cookie: date=now;_gcat=1\r\n\r\nhello")
end)
--- mimes test
test("STD Mime", function()
expect(std.mimeOf("request.json"), "application/json")
expect(std.mimeOf("test.exe"), "application/octet-stream")
end)
test("STD send file", function()
RESPONSE_HEADER.sent = false
fcgio:flush()
std.sendFile("request.json")
print(fcgio.OUTPUT)
end)
test("utils.is_array", function()
local tb = { name = "Dany", test = true}
expect(utils.is_array(tb), false)
local arr = {[1] = "Dany", [2] = true}
expect(utils.is_array(arr), true)
end)
test("utils.escape and utils.unescape", function()
local before = 'this is a escape string \\ " % \n \t \r'
local escaped = utils.escape(before)
expect(escaped, 'this is a escape string \\\\ \\" %% \\n \\t \\r')
expect(utils.unescape(escaped), before)
end)
test("utils.decodeURI", function()
local uri = "https://mozilla.org/?x=%D1%88%D0%B5%D0%BB%D0%BB%D1%8B"
local decoded = utils.decodeURI(uri)
expect(decoded, "https://mozilla.org/?x=шеллы")
end)
test("utils.file_exists", function()
expect(utils.file_exists("request.json"), true)
expect(utils.file_exists("test1.json"), false)
end)
test("utils.parse_query", function()
local query = "r=1&id=3&name=John&desc=some%20thing&enc=this+is+encode"
local tb = utils.parse_query(query)
expect(tb.r, "1")
expect(tb.id, "3")
expect(tb.desc, "some thing")
expect(tb.enc, "this is encode")
expect(tb.name, "John")
end)
test("utils.url_parser", function()
local uri = "https://mozilla.org:9000/?x=%D1%88%D0%B5%D0%BB%D0%BB%D1%8B"
local obj = utils.url_parser(uri)
expect(obj.query, "/?x=%D1%88%D0%B5%D0%BB%D0%BB%D1%8B")
expect(obj.hostname, "mozilla.org")
expect(obj.protocol, "https")
expect(obj.port, 9000)
end)
test("utils explode/implode", function()
local str = "this is a test"
tbl = explode(str, " ")
expect(tbl[1], "this")
expect(tbl[2], "is")
expect(tbl[3], "a")
expect(tbl[4], "test")
local str1 = implode(tbl, "|")
expect(str1, "this|is|a|test")
end)
test("utils firstToUpper", function()
local str = "this is a test"
expect(firstToUpper(str), "This is a test")
end)
test("utils.ext", function()
expect(utils.ext("foo.bar"), "bar")
expect(utils.ext("foo.bar.baz"), "baz")
expect(utils.ext("foo"), nil)
end)
test("utils.basename", function()
expect(utils.basename("path/to/foo.bar"), "foo.bar")
end)
--- Test for sqlite database
test("sqlite.getdb", function()
require("silk.core.sqlite")
local path = "/tmp/test.db"
local db = sqlite.getdb("/tmp/test.db")
sqlite.dbclose(db)
expect(ulib.exists(path), true)
db = sqlite.getdb("system")
sqlite.dbclose(db)
expect(ulib.exists("/tmp/system.db"), true)
ulib.delete("/tmp/secret.db")
expect(ulib.exists("/tmp/secret.db"), false)
DB = DBModel:new{db="secret"}
DB:open()
expect(ulib.exists("/tmp/secret.db"), true)
unexpect(DB.db, nil)
unexpect(DB.db,"secret")
end)
test("DBModel:createTable", function()
ret = DB:createTable("test", {
first_name = "TEXT NOT NULL",
last_name = "TEXT NOT NULL",
age = "INTEGER"
})
expect(ret, true)
end)
test("DBModel:available", function()
expect(DB:available("test"), true)
end)
test("DBModel:insert", function()
local data = {
first_name = "Dany",
last_name = "LE",
age = 30,
phone = "Unknown"
}
expect(DB:insert("test",data), false)
data.phone = nil
expect(DB:insert("test",data), true)
data = {
first_name = "Lisa",
last_name = "LE",
age = 5
}
expect(DB:insert("test",data), true)
end)
test("DBModel:lastInsertID", function()
local id = DB:lastInsertID()
expect(id, 2)
end)
test("DBModel:get", function()
local record = DB:get("test", 2)
expect(record.id, 2)
expect(record.first_name, "Lisa")
expect(record.last_name, "LE")
expect(record.age, 5)
end)
test("DBModel:getAll", function()
local records = DB:getAll("test")
expect(#records, 2)
expect(records[1].id, 1)
expect(records[1].first_name, "Dany")
expect(records[1].last_name, "LE")
expect(records[1].age, 30)
expect(records[2].id, 2)
expect(records[2].first_name, "Lisa")
expect(records[2].last_name, "LE")
expect(records[2].age, 5)
end)
test("DBModel:find", function()
local cond = {
exp = {
["and"] = {
{
["="] = {
first_name = "Dany"
}
},
{
["="] = {
age = 25
}
}
}
}
}
local records = DB:find("test", cond)
expect(#records, 0)
cond.exp["and"][2]["="].age = 30
records = DB:find("test", cond)
expect(#records, 1)
cond = {
exp = {
["="] = {
last_name = "LE"
}
},
order = {
id = "DESC"
}
}
records = DB:find("test", cond)
expect(#records, 2)
expect(records[1].id, 2)
expect(records[1].first_name, "Lisa")
expect(records[1].last_name, "LE")
expect(records[1].age, 5)
end)
test("DBModel:update", function()
local data = {
id = 1,
first_name = "Dany Xuan-Sang",
age = 35,
}
expect(DB:update("test", data), true)
local record = DB:get("test", 1)
unexpect(record, nil)
expect(record.age , 35)
expect(record.first_name, "Dany Xuan-Sang")
end)
test("DBModel:deleteByID", function()
expect(DB:deleteByID("test", 1), true)
local record = DB:get("test", 1)
expect(record, nil)
end)
test("DBModel:delete", function()
local cond = {
["="] = {
last_name = "LE"
}
}
expect(DB:delete("test", cond), true)
local records = DB:getAll("test")
expect(#records, 0)
end)
--- test enc module
test("Base64 encode/decode", function()
enc = require("enc")
local string = "this is the test"
local encode = enc.b64encode(string)
expect(encode,"dGhpcyBpcyB0aGUgdGVzdA==")
local buf = enc.b64decode(encode)
unexpect(buf,nil)
expect(tostring(buf), string)
end)
test("md5 encode", function()
expect(enc.md5("this is a test"), "54b0c58c7ce9f2a8b551351102ee0938")
end)
test("sha1 encode", function()
expect(enc.sha1("this is a test"), "fa26be19de6bff93f70bc2308434e4a440bbad02")
end)
--- run all unit tests
run()

270
test/test_silk.lua Normal file
View File

@@ -0,0 +1,270 @@
--- the binary shall be compiled with
--- make CFLAGS=-DLUA_SLICE_MAGIC=0x8AD73B9F
--- otherwise the tests unable to load C modules
require("lunit")
package.cpath = "/tmp/lib/lua/?.so"
package.path = "/tmp/lib/lua/?.lua"
test("IO setup", function()
fcgio = { OUTPUT = "",
LOG = {
INFO = "",
ERROR = "",
DEBUG = "",
WARN = ""
}
}
function fcgio:flush()
fcgio.OUTPUT = ""
fcgio.LOG.INFO = ""
fcgio.LOG.DEBUG = ""
fcgio.LOG.WARN = ""
fcgio.LOG.ERROR = ""
RESPONSE_HEADER.sent = false
end
function fcgio:echo(...)
local args = table.pack(...)
for i=1,args.n do
-- do something with args[i], careful, it might be nil!
fcgio.OUTPUT = fcgio.OUTPUT..tostring(args[i])
end
end
function fcgio:log_info(fmt,...)
fcgio.LOG.INFO = string.format(fmt,...)
io.stderr:write("INFO: ", fcgio.LOG.INFO)
io.stderr:write("\n")
end
function fcgio:log_error(fmt,...)
fcgio.LOG.ERROR = string.format(fmt,...)
io.stderr:write("ERROR: ",fcgio.LOG.ERROR)
io.stderr:write("\n")
end
function fcgio:log_debug(fmt,...)
fcgio.LOG.DEBUG = string.format(fmt,...)
io.stderr:write("DEBUG: ", fcgio.LOG.DEBUG)
io.stderr:write("\n")
end
function fcgio:log_warn(fmt,...)
fcgio.LOG.WARN = string.format(fmt,...)
io.stderr:write("WARN: ", fcgio.LOG.WARN)
io.stderr:write("\n")
end
function fcgio:send_file(path)
local f = io.open(path, "rb")
local content = f:read("*all")
f:close()
fcgio.OUTPUT = fcgio.OUTPUT..content
end
end)
test("Setup request", function()
local json = require("json")
_SERVER = json.decodeFile("request.json")
assert(_SERVER ~= nil, "Global _SERVER object not found")
end)
test("SEVER PATH", function()
expect(_SERVER["LIB_DIR"], "/tmp/lib")
expect(_SERVER["TMP_DIR"], "/tmp")
expect(_SERVER["DB_DIR"], "/tmp")
expect(_SERVER["DOCUMENT_ROOT"], "/tmp/www")
end)
test("Import the api", function()
local ret = require("silk.api")
end)
test("Lua path", function()
expect(package.cpath, "/tmp/lib/lua/?.so")
expect(package.path, "/tmp/lib/lua/?.lua;/tmp/www/?.lua")
unexpect(ulib, nil)
unexpect(utils, nil)
unexpect(std, nil)
end)
test("Logger", function()
local logger = Logger:new{ level = Logger.ERROR}
logger:info("Info message")
logger:debug("Debug message")
logger:warn("Warning message")
logger:error("Error message")
expect(fcgio.LOG.INFO, "")
expect(fcgio.LOG.DEBUG, "")
expect(fcgio.LOG.WARN, "")
expect(fcgio.LOG.ERROR, "Error message")
logger.level = Logger.INFO
logger:info("Info message")
logger:debug("Debug message")
logger:warn("Warning message")
logger:error("Error message")
expect(fcgio.LOG.ERROR, "Error message")
expect(fcgio.LOG.DEBUG, "")
expect(fcgio.LOG.WARN, "Warning message")
expect(fcgio.LOG.INFO, "Info message")
end)
test("BaseObject", function()
fcgio:flush()
local obj = BaseObject:new{ registry = {
logger = Logger:new{level = Logger.DEBUG}
} }
obj:info('Info message')
expect(fcgio.LOG.INFO, "Info message")
obj:debug("Debug message")
expect(fcgio.LOG.DEBUG, "Debug message")
obj:warn("Warning message")
expect(fcgio.LOG.WARN, "Warning message")
obj:print()
expect(fcgio.LOG.DEBUG, "BaseObject")
--obj:error("Error message")
end)
test("Silk define env", function()
DIR_SEP = "/"
BASE_FRW = ""
WWW_ROOT = "/tmp/www"
HTTP_ROOT = "https://apps.localhost:9195/"
CONTROLLER_ROOT = ""
-- class path: path.to.class
MODEL_ROOT = BASE_FRW
-- file path: path/to/file
VIEW_ROOT = WWW_ROOT
ulib.delete(WWW_ROOT)
expect(ulib.mkdir(WWW_ROOT), true)
expect(ulib.mkdir(WWW_ROOT.."/post"), true)
expect(ulib.send_file("request.json", WWW_ROOT.."/rq.json"), true)
expect(ulib.send_file("layout.ls", WWW_ROOT.."/layout.ls"), true)
expect(ulib.send_file("detail.ls", WWW_ROOT.."/post/detail.ls"), true)
expect(ulib.send_file("ad.ls", WWW_ROOT.."/post/ad.ls"), true)
end)
test("Define model", function()
BaseModel:subclass("NewsModel",{
registry = {},
name = "news",
fields = {
content = "TEXT"
}
})
local REGISTRY = {}
ulib.delete("/tmp/news.db")
-- set logging level
REGISTRY.logger = Logger:new{ level = Logger.INFO }
REGISTRY.layout = '/'
REGISTRY.db = DBModel:new {db = "news"}
REGISTRY.db:open()
local model = NewsModel:new{registry = REGISTRY}
-- insert data
expect(model:create({content = "Hello HELL"}), true)
expect(model:create({content = "Goodbye"}), true)
local records = model:findAll()
expect(#records, 2)
expect(model:update({id =1, content = "Hello World"}), true)
expect(model:delete({ ["="] = {id = 2} }), true)
records = model:findAll()
expect(#records, 1)
local record = model:get(1)
unexpect(record, nil)
expect(record.content, "Hello World")
records = model:select("id as ID, content", "1=1")
unexpect(records, nil)
expect(#records, 1)
expect(records[1].ID,1)
REGISTRY.db:close()
end)
test("Define controller", function()
BaseController:subclass("PostController",{
registry = {},
models = {"news"}
})
function PostController:id(n)
local record = self.news:get(n)
self.template:set("data", record)
--self.template:set("id", n)
self.template:setView("detail")
return true
end
function PostController:ad()
self.template:set("ad", "AD HERE")
return true
end
end)
test("Router infer controller", function()
local router = Router:new{}
local action = router:infer()
expect(action.controller.class, "PostController")
expect(action.action, "id")
expect(action.args[1], "1")
end)
test("Router infer asset", function()
fcgio:flush()
local router = Router:new{registry = {
fileaccess = true
}}
local action = router:infer("/rq.json")
expect(action.controller.class, "AssetController")
expect(action.action, "get")
expect(action.args[1], "rq.json")
local ret = router:call(action)
expect(ret, false)
io.stderr:write(fcgio.OUTPUT)
io.stderr:write("\n")
end)
test("Router fetch views with dependencies", function()
fcgio:flush()
local REGISTRY = {}
REGISTRY.db = DBModel:new {db = "news"}
REGISTRY.db:open()
-- set logging level
REGISTRY.logger = Logger:new{ level = Logger.INFO }
REGISTRY.layout = '/'
local default_routes_dependencies = {
ad = {
url = "post/ad",
visibility = {
shown = true,
routes = {
["post/id"] = true
}
}
}
}
local router = Router:new{registry = REGISTRY}
router:route('/', default_routes_dependencies )
router:delegate()
REGISTRY.db:close()
expect(fcgio.OUTPUT, "Status: 200 OK\r\nContent-Type: text/html; charset=utf-8\r\n\r\nPost ID:1.0\nContent:Hello World\nAD HERE")
end)
test("Controller action not found", function()
fcgio:flush()
REQUEST.r = "/post/all"
local router = Router:new{registry = {}}
local s,e = pcall(router.delegate, router)
expect(s, false)
expect(fcgio.OUTPUT,"Status: 200 OK\r\nContent-Type: text/html; charset=utf-8\r\n\r\n#action all is not found in controller PostController")
end)
test("Controller not found", function()
fcgio:flush()
REQUEST.r = "/user/dany"
local REGISTRY = {}
-- set logging level
--REGISTRY.logger = Logger:new{ level = Logger.INFO }
REGISTRY.layout = '/'
local router = Router:new{registry = REGISTRY}
local s,e = pcall(router.delegate, router)
expect(s, false)
print(fcgio.OUTPUT)
end)
-- run all test
run()