require('spec.helpers') local config = require('pending.config') local parse = require('pending.parse') describe('status', function() local tmpdir local pending before_each(function() tmpdir = vim.fn.tempname() vim.fn.mkdir(tmpdir, 'p') vim.g.pending = { data_path = tmpdir .. '/tasks.json' } config.reset() package.loaded['pending'] = nil pending = require('pending') pending.store():load() end) after_each(function() vim.fn.delete(tmpdir, 'rf') vim.g.pending = nil config.reset() package.loaded['pending'] = nil end) describe('counts', function() it('returns zeroes for empty store', function() local c = pending.counts() assert.are.equal(0, c.overdue) assert.are.equal(0, c.today) assert.are.equal(0, c.pending) assert.are.equal(0, c.priority) assert.is_nil(c.next_due) end) it('counts pending tasks', function() local s = pending.store() s:add({ description = 'One' }) s:add({ description = 'Two' }) s:save() pending._recompute_counts() local c = pending.counts() assert.are.equal(2, c.pending) end) it('counts priority tasks', function() local s = pending.store() s:add({ description = 'Urgent', priority = 1 }) s:add({ description = 'Normal' }) s:save() pending._recompute_counts() local c = pending.counts() assert.are.equal(1, c.priority) end) it('counts overdue tasks with date-only', function() local s = pending.store() s:add({ description = 'Old task', due = '2020-01-01' }) s:save() pending._recompute_counts() local c = pending.counts() assert.are.equal(1, c.overdue) end) it('counts overdue tasks with datetime', function() local s = pending.store() s:add({ description = 'Old task', due = '2020-01-01T08:00' }) s:save() pending._recompute_counts() local c = pending.counts() assert.are.equal(1, c.overdue) end) it('counts today tasks', function() local s = pending.store() local today = os.date('%Y-%m-%d') --[[@as string]] s:add({ description = 'Today task', due = today }) s:save() pending._recompute_counts() local c = pending.counts() assert.are.equal(1, c.today) assert.are.equal(0, c.overdue) end) it('counts mixed overdue and today', function() local s = pending.store() local today = os.date('%Y-%m-%d') --[[@as string]] s:add({ description = 'Overdue', due = '2020-01-01' }) s:add({ description = 'Today', due = today }) s:save() pending._recompute_counts() local c = pending.counts() assert.are.equal(1, c.overdue) assert.are.equal(1, c.today) end) it('excludes done tasks', function() local s = pending.store() local t = s:add({ description = 'Done', due = '2020-01-01' }) s:update(t.id, { status = 'done' }) s:save() pending._recompute_counts() local c = pending.counts() assert.are.equal(0, c.overdue) assert.are.equal(0, c.pending) end) it('excludes deleted tasks', function() local s = pending.store() local t = s:add({ description = 'Deleted', due = '2020-01-01' }) s:delete(t.id) s:save() pending._recompute_counts() local c = pending.counts() assert.are.equal(0, c.overdue) assert.are.equal(0, c.pending) end) it('excludes someday sentinel', function() local s = pending.store() s:add({ description = 'Someday', due = '9999-12-30' }) s:save() pending._recompute_counts() local c = pending.counts() assert.are.equal(0, c.overdue) assert.are.equal(0, c.today) assert.are.equal(1, c.pending) end) it('picks earliest future date as next_due', function() local s = pending.store() local today = os.date('%Y-%m-%d') --[[@as string]] s:add({ description = 'Soon', due = '2099-06-01' }) s:add({ description = 'Sooner', due = '2099-03-01' }) s:add({ description = 'Today', due = today }) s:save() pending._recompute_counts() local c = pending.counts() assert.are.equal(today, c.next_due) end) it('lazy loads on first counts() call', function() local path = config.get().data_path local f = io.open(path, 'w') f:write(vim.json.encode({ version = 1, next_id = 2, tasks = { { id = 1, description = 'Overdue', status = 'pending', due = '2020-01-01', entry = '2020-01-01T00:00:00Z', modified = '2020-01-01T00:00:00Z', }, }, })) f:close() package.loaded['pending'] = nil pending = require('pending') local c = pending.counts() assert.are.equal(1, c.overdue) end) end) describe('statusline', function() it('returns empty string when nothing actionable', function() local s = pending.store() s:save() pending._recompute_counts() assert.are.equal('', pending.statusline()) end) it('formats overdue only', function() local s = pending.store() s:add({ description = 'Old', due = '2020-01-01' }) s:save() pending._recompute_counts() assert.are.equal('1 overdue', pending.statusline()) end) it('formats today only', function() local s = pending.store() local today = os.date('%Y-%m-%d') --[[@as string]] s:add({ description = 'Today', due = today }) s:save() pending._recompute_counts() assert.are.equal('1 today', pending.statusline()) end) it('formats overdue and today', function() local s = pending.store() local today = os.date('%Y-%m-%d') --[[@as string]] s:add({ description = 'Old', due = '2020-01-01' }) s:add({ description = 'Today', due = today }) s:save() pending._recompute_counts() assert.are.equal('1 overdue, 1 today', pending.statusline()) end) end) describe('has_due', function() it('returns false when nothing due', function() local s = pending.store() s:add({ description = 'Future', due = '2099-01-01' }) s:save() pending._recompute_counts() assert.is_false(pending.has_due()) end) it('returns true when overdue', function() local s = pending.store() s:add({ description = 'Old', due = '2020-01-01' }) s:save() pending._recompute_counts() assert.is_true(pending.has_due()) end) it('returns true when today', function() local s = pending.store() local today = os.date('%Y-%m-%d') --[[@as string]] s:add({ description = 'Now', due = today }) s:save() pending._recompute_counts() assert.is_true(pending.has_due()) end) end) describe('parse.is_overdue', function() it('date before today is overdue', function() assert.is_true(parse.is_overdue('2020-01-01')) end) it('date after today is not overdue', function() assert.is_false(parse.is_overdue('2099-01-01')) end) it('today date-only is not overdue', function() local today = os.date('%Y-%m-%d') --[[@as string]] assert.is_false(parse.is_overdue(today)) end) end) describe('parse.is_today', function() it('today date-only is today', function() local today = os.date('%Y-%m-%d') --[[@as string]] assert.is_true(parse.is_today(today)) end) it('yesterday is not today', function() assert.is_false(parse.is_today('2020-01-01')) end) it('tomorrow is not today', function() assert.is_false(parse.is_today('2099-01-01')) end) end) end)