// sort-tasks.test.mjs — TDD contract tests for sortTasksByDate // // sortTasksByDate is defined inline here to establish expected behaviour. // Once sortTasksByDate is exported from web/app.js or a shared module, // remove the inline definition and import it instead. // // Run with: node --test web/test/sort-tasks.test.mjs import { describe, it } from 'node:test'; import assert from 'node:assert/strict'; // ── Implementation under contract ───────────────────────────────────────────── // Remove this block once sortTasksByDate is available from app.js. function sortTasksByDate(tasks, descend = false) { return [...tasks].sort((a, b) => { if (!a.created_at && !b.created_at) return 0; if (!a.created_at) return 1; if (!b.created_at) return -1; const diff = new Date(a.created_at) - new Date(b.created_at); return descend ? -diff : diff; }); } // ── Helpers ──────────────────────────────────────────────────────────────────── function makeTask(id, created_at, state = 'PENDING') { return { id, name: `task-${id}`, state, created_at }; } // ── Tests ────────────────────────────────────────────────────────────────────── describe('sortTasksByDate', () => { it('sorts tasks oldest-first by created_at', () => { const tasks = [ makeTask('c', '2026-03-06T12:00:00Z'), makeTask('a', '2026-03-04T08:00:00Z'), makeTask('b', '2026-03-05T10:00:00Z'), ]; const result = sortTasksByDate(tasks); assert.equal(result[0].id, 'a', 'oldest should be first'); assert.equal(result[1].id, 'b'); assert.equal(result[2].id, 'c', 'newest should be last'); }); it('sorts tasks newest-first when descend=true', () => { const tasks = [ makeTask('c', '2026-03-06T12:00:00Z'), makeTask('a', '2026-03-04T08:00:00Z'), makeTask('b', '2026-03-05T10:00:00Z'), ]; const result = sortTasksByDate(tasks, true); assert.equal(result[0].id, 'c', 'newest should be first'); assert.equal(result[1].id, 'b'); assert.equal(result[2].id, 'a', 'oldest should be last'); }); it('returns a new array (does not mutate input)', () => { const tasks = [ makeTask('b', '2026-03-05T10:00:00Z'), makeTask('a', '2026-03-04T08:00:00Z'), ]; const original = [...tasks]; const result = sortTasksByDate(tasks); assert.notStrictEqual(result, tasks, 'should return a new array'); assert.deepEqual(tasks, original, 'input should not be mutated'); }); it('returns an empty array when given an empty array', () => { assert.deepEqual(sortTasksByDate([]), []); }); it('returns a single-element array unchanged', () => { const tasks = [makeTask('x', '2026-03-01T00:00:00Z')]; const result = sortTasksByDate(tasks); assert.equal(result.length, 1); assert.equal(result[0].id, 'x'); }); it('places tasks with null created_at after tasks with a date', () => { const tasks = [ makeTask('no-date', null), makeTask('has-date', '2026-03-01T00:00:00Z'), ]; const result = sortTasksByDate(tasks); assert.equal(result[0].id, 'has-date', 'task with date should come first'); assert.equal(result[1].id, 'no-date', 'task without date should come last'); }); it('works with mixed states (not just PENDING)', () => { const tasks = [ makeTask('r', '2026-03-06T00:00:00Z', 'RUNNING'), makeTask('p', '2026-03-04T00:00:00Z', 'PENDING'), makeTask('q', '2026-03-05T00:00:00Z', 'QUEUED'), ]; const result = sortTasksByDate(tasks); assert.equal(result[0].id, 'p'); assert.equal(result[1].id, 'q'); assert.equal(result[2].id, 'r'); }); });