import { test } from 'node:test'; import assert from 'node:assert/strict'; import { sortBookmarks, groupBookmarksByRoom, isBookmarkSort } from './bookmarks'; import { Bookmark } from '../hooks/useBookmarks'; const bk = (eventId: string, roomId: string, savedAt: number, roomName = roomId): Bookmark => ({ eventId, roomId, savedAt, roomName, previewText: eventId, }); test('sortBookmarks newest orders by savedAt descending', () => { const input = [bk('a', 'r1', 100), bk('b', 'r1', 300), bk('c', 'r1', 200)]; const out = sortBookmarks(input, 'newest'); assert.deepEqual( out.map((b) => b.eventId), ['b', 'c', 'a'], ); }); test('sortBookmarks oldest orders by savedAt ascending', () => { const input = [bk('a', 'r1', 100), bk('b', 'r1', 300), bk('c', 'r1', 200)]; const out = sortBookmarks(input, 'oldest'); assert.deepEqual( out.map((b) => b.eventId), ['a', 'c', 'b'], ); }); test('sortBookmarks room falls back to newest for a flat list', () => { const input = [bk('a', 'r1', 100), bk('b', 'r2', 300)]; const out = sortBookmarks(input, 'room'); assert.deepEqual( out.map((b) => b.eventId), ['b', 'a'], ); }); test('sortBookmarks breaks savedAt ties deterministically by eventId', () => { const input = [bk('z', 'r1', 100), bk('a', 'r1', 100), bk('m', 'r1', 100)]; const newest = sortBookmarks(input, 'newest'); const oldest = sortBookmarks(input, 'oldest'); // Equal timestamps → ascending eventId in both directions. assert.deepEqual( newest.map((b) => b.eventId), ['a', 'm', 'z'], ); assert.deepEqual( oldest.map((b) => b.eventId), ['a', 'm', 'z'], ); }); test('sortBookmarks does not mutate its input', () => { const input = [bk('a', 'r1', 100), bk('b', 'r1', 300)]; const before = input.map((b) => b.eventId); sortBookmarks(input, 'oldest'); assert.deepEqual( input.map((b) => b.eventId), before, ); }); test('groupBookmarksByRoom buckets by room, newest-first within a group', () => { const input = [bk('a', 'r1', 100), bk('b', 'r2', 500), bk('c', 'r1', 300), bk('d', 'r2', 200)]; const groups = groupBookmarksByRoom(input); assert.equal(groups.length, 2); const r1 = groups.find((g) => g.roomId === 'r1')!; const r2 = groups.find((g) => g.roomId === 'r2')!; assert.deepEqual( r1.items.map((b) => b.eventId), ['c', 'a'], ); assert.deepEqual( r2.items.map((b) => b.eventId), ['b', 'd'], ); }); test('groupBookmarksByRoom orders groups by most-recent save (active rooms first)', () => { const input = [bk('a', 'r1', 100), bk('c', 'r1', 300), bk('b', 'r2', 500)]; // r2's newest is 500, r1's newest is 300 → r2 first. const groups = groupBookmarksByRoom(input); assert.deepEqual( groups.map((g) => g.roomId), ['r2', 'r1'], ); }); test('groupBookmarksByRoom carries the stored room name', () => { const groups = groupBookmarksByRoom([bk('a', 'r1', 100, 'General')]); assert.equal(groups[0].roomName, 'General'); }); test('groupBookmarksByRoom orders equal-newest groups deterministically by roomId', () => { // Both rooms' newest save is 200 → tie broken by roomId ascending. const input = [bk('a', 'rB', 200), bk('b', 'rA', 200), bk('c', 'rA', 100)]; const groups = groupBookmarksByRoom(input); assert.deepEqual( groups.map((g) => g.roomId), ['rA', 'rB'], ); }); test('groupBookmarksByRoom does not mutate its input', () => { const input = [bk('a', 'r1', 100), bk('b', 'r2', 300), bk('c', 'r1', 200)]; const before = input.map((b) => b.eventId); groupBookmarksByRoom(input); assert.deepEqual( input.map((b) => b.eventId), before, ); }); test('groupBookmarksByRoom returns empty for empty input', () => { assert.deepEqual(groupBookmarksByRoom([]), []); }); test('isBookmarkSort accepts valid values and rejects everything else', () => { assert.equal(isBookmarkSort('newest'), true); assert.equal(isBookmarkSort('oldest'), true); assert.equal(isBookmarkSort('room'), true); assert.equal(isBookmarkSort('bogus'), false); assert.equal(isBookmarkSort(''), false); assert.equal(isBookmarkSort(undefined), false); assert.equal(isBookmarkSort(null), false); assert.equal(isBookmarkSort(3), false); });