#!/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