describe('JSON Module', function() local json setup(function() json = require('src.json') end) it('should encode strings', function() assert.are.equal('"hello"', json.encode('hello')) assert.are.equal('"hello world"', json.encode('hello world')) end) it('should encode numbers', function() assert.are.equal('123', json.encode(123)) assert.are.equal('12.5', json.encode(12.5)) assert.are.equal('-42', json.encode(-42)) end) it('should encode booleans', function() assert.are.equal('true', json.encode(true)) assert.are.equal('false', json.encode(false)) end) it('should encode null', function() assert.are.equal('null', json.encode(nil)) end) it('should encode arrays', function() local arr = { 1, 2, 3 } local encoded = json.encode(arr) assert.are.equal('{ 1, 2, 3 }', encoded) end) it('should encode objects', function() local obj = { name = 'test', value = 42 } local encoded = json.encode(obj) assert.are_not.equal(nil, encoded:find('name')) assert.are_not.equal(nil, encoded:find('value')) end) it('should decode strings', function() local decoded = json.decode('"hello"') assert.are.equal('hello', decoded) end) it('should decode numbers', function() local decoded = json.decode('123') assert.are.equal(123, decoded) local decoded2 = json.decode('12.5') assert.are.equal(12.5, decoded2) end) it('should decode booleans', function() local decoded = json.decode('true') assert.are.equal(true, decoded) local decoded2 = json.decode('false') assert.are.equal(false, decoded2) end) it('should decode null', function() local decoded = json.decode('null') assert.are.equal(nil, decoded) end) it('should decode arrays', function() local decoded = json.decode('{ 1, 2, 3 }') assert.are.equal(1, decoded[1]) assert.are.equal(2, decoded[2]) assert.are.equal(3, decoded[3]) end) it('should decode objects', function() local decoded = json.decode('{ "name": "test", "value": 42 }') assert.are.equal('test', decoded.name) assert.are.equal(42, decoded.value) end) it('should handle round-trip encoding/decoding', function() local original = { name = 'Test Cat', age = 3, active = true, tags = { 'cute', 'playful' }, stats = { comfort = 0.8, hunger = 0.6 } } local encoded = json.encode(original) local decoded = json.decode(encoded) assert.are.equal(original.name, decoded.name) assert.are.equal(original.age, decoded.age) assert.are.equal(original.active, decoded.active) assert.are.equal(original.tags[1], decoded.tags[1]) assert.are.equal(original.stats.comfort, decoded.stats.comfort) end) it('should handle escaped characters in strings', function() local original = 'hello\nworld\t"quoted"' local encoded = json.encode(original) local decoded = json.decode(encoded) assert.are.equal(original, decoded) end) it('should handle empty arrays', function() local original = {} local encoded = json.encode(original) local decoded = json.decode(encoded) assert.are.equal(0, #decoded) end) it('should handle nested structures', function() local original = { cats = { { name = 'Cat1', personality = { id = 'social' } }, { name = 'Cat2', personality = { id = 'lone_wolf' } } } } local encoded = json.encode(original) local decoded = json.decode(encoded) assert.are.equal('Cat1', decoded.cats[1].name) assert.are.equal('social', decoded.cats[1].personality.id) end) it('should handle non-string keys by converting to strings', function() local original = { [123] = 'value', [true] = 'bool' } local encoded = json.encode(original) local decoded = json.decode(encoded) assert.are.equal('value', decoded['123']) assert.are.equal('bool', decoded['true']) end) it('should handle NaN and infinity as null', function() assert.are.equal('null', json.encode(0/0)) assert.are.equal('null', json.encode(math.huge)) assert.are.equal('null', json.encode(-math.huge)) end) it('should return error for invalid JSON', function() local decoded, err = json.decode('{ invalid }') assert.are_not.equal(nil, err) assert.are.equal(nil, decoded) end) it('should return error for non-string input', function() local decoded, err = json.encode({ function() end }) assert.are.equal('null', decoded) end) end)