local mod = require 'std.dbg.file' local encode = require 'std.dbg.encode' local write_event = mod.write local read_all = mod.read_all local read_new = mod.read_new local get_context = mod.get_context local get_path = mod.get_path local close_fd = mod.close_fd describe('std.dbg.file', function() describe('get_path()', function() it('should return a string path', function() local result = get_path() assert.is_string(result) assert.is_not_nil(result:match 'bugabinga_config_debug.jsonl') end) end) describe('get_context()', function() it('should return filename, function_name, and line_number', function() local filename, fn_name, line = get_context() assert.is_string(filename) assert.is_string(fn_name) assert.is_number(line) end) it('should return three values (types)', function() local filename, fn_name, line = get_context() assert.is_string(filename) assert.is_string(fn_name) assert.is_number(line) end) it('should skip std.dbg internal frames', function() local filename, fn_name, line = get_context() assert.is_nil(filename:match 'std/dbg/file%.lua') end) end) describe('write() and read_all() integration', function() local test_file before_each(function() mod.clear_log() test_file = vim.fn.stdpath 'state' .. '/test_debug_spec.jsonl' vim.fn.delete(test_file) end) after_each(function() vim.fn.delete(test_file) end) it('should write an event and read it back', function() local event = { kind = 'DEBUG', date = os.time(), format = 'test {val}', data = { val = 123 }, filename = '/test/file.lua', fn = 'test_fn', line = 42, } write_event(event, test_file) local events = read_all(test_file) assert.equal(1, #events) assert.equal('DEBUG', events[1].kind) assert.equal(42, events[1].line) end) it('should write multiple events, each on its own line', function() local event1 = { kind = 'DEBUG', date = os.time(), format = 'first', data = {}, filename = '/a.lua', fn = 'f', line = 1, } local event2 = { kind = 'ERROR', date = os.time(), format = 'second', data = {}, filename = '/b.lua', fn = 'g', line = 2, } write_event(event1, test_file) write_event(event2, test_file) local events = read_all(test_file) assert.equal(2, #events) assert.equal('DEBUG', events[1].kind) assert.equal('ERROR', events[2].kind) end) it('should append, not overwrite', function() local event1 = { kind = 'DEBUG', date = os.time(), format = 'a', data = {}, filename = '/a', fn = 'f', line = 1 } local event2 = { kind = 'TRACE', date = os.time(), format = 'b', data = {}, filename = '/b', fn = 'g', line = 2 } write_event(event1, test_file) write_event(event2, test_file) local events = read_all(test_file) assert.equal(2, #events) end) it('should return empty list when file does not exist', function() local events = read_all('/nonexistent/path/that/does/not/exist.jsonl') assert.equal(0, #events) end) it('should reopen fd when file is externally recreated', function() close_fd() vim.fn.delete(get_path()) local event1 = { kind = 'DEBUG', date = os.time(), message = 'e1', data = {}, filename = '/a.lua', fn = 'f', line = 1 } local event2 = { kind = 'ERROR', date = os.time(), message = 'e2', data = {}, filename = '/b.lua', fn = 'g', line = 2 } write_event(event1) vim.fn.delete(get_path()) write_event(event2) local events = read_all(get_path()) assert.equal(1, #events, 'new writes go to newly created file after deletion') assert.equal('e2', events[1].message) end) it('should handle events with nested data', function() local event = { kind = 'ERROR', date = os.time(), format = 'nested {data}', data = { { a = 1 }, { b = 2 } }, filename = '/c.lua', fn = 'h', line = 99, } write_event(event, test_file) local events = read_all(test_file) assert.equal(1, #events) assert.equal(1, events[1].data[1].a) assert.equal(2, events[1].data[2].b) end) it('should store date as os.time() integer', function() local before = os.time() local event = { kind = 'DEBUG', date = os.time(), format = 'time test', data = {}, filename = '/t.lua', fn = 't', line = 1, } write_event(event, test_file) local after = os.time() local events = read_all(test_file) assert.is_number(events[1].date) assert.is_true(events[1].date >= before) assert.is_true(events[1].date <= after) end) end) describe('read_new()', function() local test_file before_each(function() test_file = vim.fn.stdpath 'state' .. '/test_read_new_spec.jsonl' vim.fn.delete(test_file) close_fd() end) after_each(function() vim.fn.delete(test_file) end) it('should return only events after last_count', function() for i = 1, 5 do write_event({ kind = 'DEBUG', date = os.time(), message = 'event ' .. i, data = {}, filename = '/t.lua', fn = 'f', line = 1 }, test_file) end local new_events, total = read_new(3, test_file) assert.equal(2, #new_events) assert.equal(5, total) assert.equal('event 4', new_events[1].message) assert.equal('event 5', new_events[2].message) end) it('should return all events when last_count is 0', function() for i = 1, 3 do write_event({ kind = 'DEBUG', date = os.time(), message = 'e' .. i, data = {}, filename = '/t.lua', fn = 'f', line = 1 }, test_file) end local new_events, total = read_new(0, test_file) assert.equal(3, #new_events) assert.equal(3, total) end) it('should return empty when last_count equals total events', function() for i = 1, 3 do write_event({ kind = 'DEBUG', date = os.time(), message = 'e' .. i, data = {}, filename = '/t.lua', fn = 'f', line = 1 }, test_file) end local new_events, total = read_new(3, test_file) assert.equal(0, #new_events) assert.equal(3, total) end) it('should return empty and 0 when file does not exist', function() local new_events, total = read_new(0, '/nonexistent/path.jsonl') assert.equal(0, #new_events) assert.equal(0, total) end) end) end)