1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
|
// tasks-board.test.mjs — Unit tests for the Tasks tab's pure column logic:
// column-mapping completeness, fallback-state behaviour, and per-column
// grouping/sorting correctness.
//
// Run with: node --test web/test/tasks-board.test.mjs
import { describe, it } from 'node:test';
import assert from 'node:assert/strict';
import {
TASK_COLUMNS,
columnForTaskState,
groupTasksByColumn,
} from '../app.js';
describe('columnForTaskState', () => {
it('maps every documented task state to a column', () => {
assert.equal(columnForTaskState('PENDING'), 'queue');
assert.equal(columnForTaskState('QUEUED'), 'queue');
assert.equal(columnForTaskState('RUNNING'), 'running');
assert.equal(columnForTaskState('BLOCKED'), 'running');
assert.equal(columnForTaskState('READY'), 'ready');
assert.equal(columnForTaskState('FAILED'), 'interrupted');
assert.equal(columnForTaskState('TIMED_OUT'), 'interrupted');
assert.equal(columnForTaskState('CANCELLED'), 'interrupted');
assert.equal(columnForTaskState('BUDGET_EXCEEDED'), 'interrupted');
assert.equal(columnForTaskState('COMPLETED'), 'done');
});
it('falls back to queue for an unrecognized/empty state', () => {
assert.equal(columnForTaskState(''), 'queue');
assert.equal(columnForTaskState(undefined), 'queue');
assert.equal(columnForTaskState('SOME_FUTURE_STATE'), 'queue');
});
it('every column key is unique and covers all 10 task states', () => {
const keys = TASK_COLUMNS.map(c => c.key);
assert.equal(new Set(keys).size, keys.length, 'column keys must be unique');
const allStates = TASK_COLUMNS.flatMap(c => c.states);
for (const s of [
'PENDING', 'QUEUED', 'RUNNING', 'BLOCKED', 'READY',
'FAILED', 'TIMED_OUT', 'CANCELLED', 'BUDGET_EXCEEDED', 'COMPLETED',
]) {
assert.ok(allStates.includes(s), `${s} missing from any column`);
}
});
});
describe('groupTasksByColumn', () => {
it('returns an object with a key per column, all initially empty arrays', () => {
const groups = groupTasksByColumn([]);
for (const col of TASK_COLUMNS) {
assert.ok(Array.isArray(groups[col.key]), `groups.${col.key} should be an array`);
assert.equal(groups[col.key].length, 0);
}
});
it('places tasks in the correct column', () => {
const tasks = [
{ id: '1', state: 'PENDING', created_at: '2024-01-01T00:00:00Z' },
{ id: '2', state: 'RUNNING', created_at: '2024-01-02T00:00:00Z' },
{ id: '3', state: 'READY', created_at: '2024-01-03T00:00:00Z' },
{ id: '4', state: 'FAILED', created_at: '2024-01-04T00:00:00Z' },
{ id: '5', state: 'COMPLETED', created_at: '2024-01-05T00:00:00Z' },
];
const groups = groupTasksByColumn(tasks);
assert.equal(groups.queue.length, 1);
assert.equal(groups.queue[0].id, '1');
assert.equal(groups.running.length, 1);
assert.equal(groups.running[0].id, '2');
assert.equal(groups.ready.length, 1);
assert.equal(groups.ready[0].id, '3');
assert.equal(groups.interrupted.length, 1);
assert.equal(groups.interrupted[0].id, '4');
assert.equal(groups.done.length, 1);
assert.equal(groups.done[0].id, '5');
});
it('sorts queue oldest-first (ascending created_at)', () => {
const tasks = [
{ id: 'newer', state: 'QUEUED', created_at: '2024-02-01T00:00:00Z' },
{ id: 'older', state: 'PENDING', created_at: '2024-01-01T00:00:00Z' },
];
const groups = groupTasksByColumn(tasks);
assert.deepEqual(groups.queue.map(t => t.id), ['older', 'newer']);
});
it('sorts ready oldest-first (ascending created_at)', () => {
const tasks = [
{ id: 'newer', state: 'READY', created_at: '2024-02-01T00:00:00Z' },
{ id: 'older', state: 'READY', created_at: '2024-01-01T00:00:00Z' },
];
const groups = groupTasksByColumn(tasks);
assert.deepEqual(groups.ready.map(t => t.id), ['older', 'newer']);
});
it('sorts interrupted newest-first (descending created_at)', () => {
const tasks = [
{ id: 'older', state: 'FAILED', created_at: '2024-01-01T00:00:00Z' },
{ id: 'newer', state: 'CANCELLED', created_at: '2024-02-01T00:00:00Z' },
];
const groups = groupTasksByColumn(tasks);
assert.deepEqual(groups.interrupted.map(t => t.id), ['newer', 'older']);
});
it('sorts done newest-first (descending created_at)', () => {
const tasks = [
{ id: 'older', state: 'COMPLETED', created_at: '2024-01-01T00:00:00Z' },
{ id: 'newer', state: 'COMPLETED', created_at: '2024-02-01T00:00:00Z' },
];
const groups = groupTasksByColumn(tasks);
assert.deepEqual(groups.done.map(t => t.id), ['newer', 'older']);
});
it('sorts running newest-first (descending created_at)', () => {
const tasks = [
{ id: 'older', state: 'RUNNING', created_at: '2024-01-01T00:00:00Z' },
{ id: 'newer', state: 'BLOCKED', created_at: '2024-02-01T00:00:00Z' },
];
const groups = groupTasksByColumn(tasks);
assert.deepEqual(groups.running.map(t => t.id), ['newer', 'older']);
});
it('handles null/undefined gracefully', () => {
const groups = groupTasksByColumn(null);
for (const col of TASK_COLUMNS) {
assert.equal(groups[col.key].length, 0);
}
});
});
|