import { describe, it, expect } from 'vitest'; import { clampTasksLimit, parseTasksCursor, buildTasksNextCursor, TASKS_LIST_DEFAULT_LIMIT, TASKS_LIST_MAX_LIMIT, } from '../server/utils/task-pagination'; describe('clampTasksLimit', () => { it('без значения возвращает дефолт 50', () => { expect(clampTasksLimit(undefined)).toBe(TASKS_LIST_DEFAULT_LIMIT); expect(clampTasksLimit(null)).toBe(TASKS_LIST_DEFAULT_LIMIT); expect(TASKS_LIST_DEFAULT_LIMIT).toBe(50); }); it('неположительные и невалидные значения — дефолт 50', () => { expect(clampTasksLimit(0)).toBe(50); expect(clampTasksLimit(-5)).toBe(50); expect(clampTasksLimit(NaN)).toBe(50); }); it('явный лимит в пределах максимума проходит как есть', () => { expect(clampTasksLimit(1)).toBe(1); expect(clampTasksLimit(100)).toBe(100); expect(clampTasksLimit(200)).toBe(200); }); it('капает сверху на 200', () => { expect(clampTasksLimit(201)).toBe(TASKS_LIST_MAX_LIMIT); expect(clampTasksLimit(500)).toBe(200); expect(clampTasksLimit(10000)).toBe(200); expect(TASKS_LIST_MAX_LIMIT).toBe(200); }); }); describe('parseTasksCursor', () => { it('парсит валидный курсор _', () => { const cursor = parseTasksCursor('2026-09-08T12:34:56.789Z_123'); expect(cursor).not.toBeNull(); expect(cursor!.id).toBe(123); expect(cursor!.updatedAt.toISOString()).toBe('2026-09-08T12:34:56.789Z'); }); it('null/пустая строка — null (первая страница)', () => { expect(parseTasksCursor(null)).toBeNull(); expect(parseTasksCursor(undefined)).toBeNull(); expect(parseTasksCursor('')).toBeNull(); }); it('невалидные форматы — null', () => { expect(parseTasksCursor('abc')).toBeNull(); expect(parseTasksCursor('_123')).toBeNull(); expect(parseTasksCursor('2026-09-08T12:34:56.789Z_')).toBeNull(); expect(parseTasksCursor('2026-09-08T12:34:56.789Z_abc')).toBeNull(); expect(parseTasksCursor('not-a-date_123')).toBeNull(); expect(parseTasksCursor('2026-09-08T12:34:56.789Z_0')).toBeNull(); expect(parseTasksCursor('2026-09-08T12:34:56.789Z_-1')).toBeNull(); expect(parseTasksCursor('2026-09-08T12:34:56.789Z_1.5')).toBeNull(); }); }); describe('buildTasksNextCursor', () => { it('собирает курсор из последней строки выдачи', () => { const cursor = buildTasksNextCursor({ id: 42, updatedAt: new Date('2026-09-08T10:00:00.000Z') }); expect(cursor).toBe('2026-09-08T10:00:00.000Z_42'); }); it('принимает updatedAt строкой', () => { const cursor = buildTasksNextCursor({ id: 7, updatedAt: '2026-09-08T10:00:00.000Z' }); expect(cursor).toBe('2026-09-08T10:00:00.000Z_7'); }); it('без updatedAt — null', () => { expect(buildTasksNextCursor({ id: 1, updatedAt: null })).toBeNull(); }); it('round-trip: build → parse возвращает ту же пару', () => { const built = buildTasksNextCursor({ id: 555, updatedAt: new Date('2026-01-02T03:04:05.006Z') }); const parsed = parseTasksCursor(built); expect(parsed).not.toBeNull(); expect(parsed!.id).toBe(555); expect(parsed!.updatedAt.toISOString()).toBe('2026-01-02T03:04:05.006Z'); }); });