220 lines
6.5 KiB
Lua
220 lines
6.5 KiB
Lua
#!/usr/bin/env moonshark
|
|
|
|
-- Test script for JSON module functionality
|
|
local json = require("json")
|
|
|
|
local passed = 0
|
|
local total = 0
|
|
|
|
local function test(name, fn)
|
|
print("Testing " .. name .. "...")
|
|
total = total + 1
|
|
local ok, err = pcall(fn)
|
|
if ok then
|
|
passed = passed + 1
|
|
print(" ✓ PASS")
|
|
return true
|
|
else
|
|
print(" ✗ FAIL: " .. err)
|
|
return false
|
|
end
|
|
end
|
|
|
|
-- Test data
|
|
local test_data = {
|
|
name = "John Doe",
|
|
age = 30,
|
|
active = true,
|
|
scores = {85, 92, 78, 90},
|
|
address = {
|
|
street = "123 Main St",
|
|
city = "Springfield",
|
|
zip = "12345"
|
|
},
|
|
tags = {"developer", "golang", "lua"}
|
|
}
|
|
|
|
-- Test 1: Basic encoding
|
|
test("Basic JSON Encoding", function()
|
|
local encoded = json.encode(test_data)
|
|
assert(type(encoded) == "string", "encode should return string")
|
|
assert(string.find(encoded, "John Doe"), "should contain name")
|
|
assert(string.find(encoded, "30"), "should contain age")
|
|
end)
|
|
|
|
-- Test 2: Basic decoding
|
|
test("Basic JSON Decoding", function()
|
|
local encoded = json.encode(test_data)
|
|
local decoded = json.decode(encoded)
|
|
assert(decoded.name == "John Doe", "name should match")
|
|
assert(decoded.age == 30, "age should match")
|
|
assert(decoded.active == true, "active should be true")
|
|
assert(#decoded.scores == 4, "scores array length should be 4")
|
|
end)
|
|
|
|
-- Test 3: Round-trip encoding/decoding
|
|
test("Round-trip Encoding/Decoding", function()
|
|
local encoded = json.encode(test_data)
|
|
local decoded = json.decode(encoded)
|
|
local re_encoded = json.encode(decoded)
|
|
local re_decoded = json.decode(re_encoded)
|
|
|
|
assert(re_decoded.name == test_data.name, "name should survive round-trip")
|
|
assert(re_decoded.address.city == test_data.address.city, "nested data should survive")
|
|
end)
|
|
|
|
-- Test 4: Pretty printing
|
|
test("Pretty Printing", function()
|
|
local pretty = json.pretty(test_data)
|
|
assert(type(pretty) == "string", "pretty should return string")
|
|
assert(string.find(pretty, "\n"), "pretty should contain newlines")
|
|
assert(string.find(pretty, " "), "pretty should contain indentation")
|
|
|
|
-- Should still be valid JSON
|
|
local decoded = json.decode(pretty)
|
|
assert(decoded.name == test_data.name, "pretty JSON should still decode correctly")
|
|
end)
|
|
|
|
-- Test 5: Object merging
|
|
test("Object Merging", function()
|
|
local obj1 = {a = 1, b = 2}
|
|
local obj2 = {b = 3, c = 4}
|
|
local obj3 = {d = 5}
|
|
|
|
local merged = json.merge(obj1, obj2, obj3)
|
|
assert(merged.a == 1, "should preserve a from obj1")
|
|
assert(merged.b == 3, "should use b from obj2 (later wins)")
|
|
assert(merged.c == 4, "should include c from obj2")
|
|
assert(merged.d == 5, "should include d from obj3")
|
|
end)
|
|
|
|
-- Test 6: Data extraction
|
|
test("Data Extraction", function()
|
|
local name = json.extract(test_data, "name")
|
|
assert(name == "John Doe", "should extract name")
|
|
|
|
local city = json.extract(test_data, "address.city")
|
|
assert(city == "Springfield", "should extract nested city")
|
|
|
|
local first_score = json.extract(test_data, "scores.[0]")
|
|
assert(first_score == 85, "should extract array element")
|
|
|
|
local missing = json.extract(test_data, "nonexistent.field")
|
|
assert(missing == nil, "should return nil for missing path")
|
|
end)
|
|
|
|
-- Test 7: Schema validation
|
|
test("Schema Validation", function()
|
|
local schema = {
|
|
type = "table",
|
|
properties = {
|
|
name = {type = "string"},
|
|
age = {type = "number"},
|
|
active = {type = "boolean"}
|
|
},
|
|
required = {name = true, age = true}
|
|
}
|
|
|
|
local valid, err = json.validate(test_data, schema)
|
|
assert(valid == true, "test_data should be valid")
|
|
|
|
local invalid_data = {name = "John", age = "not_a_number"}
|
|
local invalid, err2 = json.validate(invalid_data, schema)
|
|
assert(invalid == false, "invalid_data should fail validation")
|
|
assert(type(err2) == "string", "should return error message")
|
|
end)
|
|
|
|
-- Test 8: File operations
|
|
test("File Save/Load", function()
|
|
local filename = "test_output.json"
|
|
|
|
-- Save to file
|
|
json.save_file(filename, test_data, true) -- pretty format
|
|
|
|
-- Check file exists
|
|
assert(moonshark.file_exists(filename), "file should exist after save")
|
|
|
|
-- Load from file
|
|
local loaded = json.load_file(filename)
|
|
assert(loaded.name == test_data.name, "loaded data should match original")
|
|
assert(loaded.address.zip == test_data.address.zip, "nested data should match")
|
|
|
|
-- Clean up
|
|
os.remove(filename)
|
|
end)
|
|
|
|
-- Test 9: Error handling
|
|
test("Error Handling", function()
|
|
-- Invalid JSON should throw error
|
|
local success, err = pcall(json.decode, '{"invalid": json}')
|
|
assert(success == false, "invalid JSON should cause error")
|
|
assert(type(err) == "string", "should return error message")
|
|
|
|
-- Missing file should throw error
|
|
local success2, err2 = pcall(json.load_file, "nonexistent_file.json")
|
|
assert(success2 == false, "missing file should cause error")
|
|
assert(type(err2) == "string", "should return error message")
|
|
end)
|
|
|
|
-- Test 10: Edge cases
|
|
test("Edge Cases", function()
|
|
-- Empty objects
|
|
local empty_obj = {}
|
|
local encoded_empty = json.encode(empty_obj)
|
|
local decoded_empty = json.decode(encoded_empty)
|
|
assert(type(decoded_empty) == "table", "empty object should decode to table")
|
|
|
|
-- Null values
|
|
local with_nil = {a = 1, b = nil, c = 3}
|
|
local encoded_nil = json.encode(with_nil)
|
|
local decoded_nil = json.decode(encoded_nil)
|
|
-- Note: nil values are typically omitted in JSON
|
|
|
|
-- Special numbers
|
|
local special = {
|
|
zero = 0,
|
|
negative = -42,
|
|
decimal = 3.14159
|
|
}
|
|
local encoded_special = json.encode(special)
|
|
local decoded_special = json.decode(encoded_special)
|
|
assert(decoded_special.zero == 0, "zero should encode/decode correctly")
|
|
assert(decoded_special.negative == -42, "negative should encode/decode correctly")
|
|
assert(math.abs(decoded_special.decimal - 3.14159) < 0.00001, "decimal should encode/decode correctly")
|
|
end)
|
|
|
|
-- Performance test
|
|
test("Performance Test", function()
|
|
local large_data = {}
|
|
for i = 1, 1000 do
|
|
large_data[i] = {
|
|
id = i,
|
|
name = "User " .. i,
|
|
data = {x = i * 2, y = i * 3, z = i * 4}
|
|
}
|
|
end
|
|
|
|
local start = os.clock()
|
|
local encoded = json.encode(large_data)
|
|
local encode_time = os.clock() - start
|
|
|
|
start = os.clock()
|
|
local decoded = json.decode(encoded)
|
|
local decode_time = os.clock() - start
|
|
|
|
print(string.format(" Encoded 1000 objects in %.3f seconds", encode_time))
|
|
print(string.format(" Decoded 1000 objects in %.3f seconds", decode_time))
|
|
|
|
assert(#decoded == 1000, "should have 1000 objects after decode")
|
|
assert(decoded[500].name == "User 500", "data should be intact")
|
|
end)
|
|
|
|
print("=" .. string.rep("=", 50))
|
|
print(string.format("Test Results: %d/%d passed", passed, total))
|
|
if passed == total then
|
|
print("🎉 All tests passed!")
|
|
os.exit(0)
|
|
else
|
|
print("❌ Some tests failed!")
|
|
os.exit(1)
|
|
end |