- Added home routes and corresponding route handler for fetching home summary. - Created GetHomeSummary use case to aggregate user data including groups, attendees, and upcoming payments. - Introduced HomeSummaryDto and related schemas for data validation. - Implemented nextClassOccurrence function to determine the next class based on user timezone. - Added tests for home summary functionality and next class occurrence logic.
112 lines
2.9 KiB
TypeScript
112 lines
2.9 KiB
TypeScript
import { describe, expect, it } from 'bun:test';
|
|
import { CLASS_WINDOW_HOURS, nextClassOccurrence } from '@/modules/home/lib/scheduler';
|
|
|
|
const TZ = 'America/Mexico_City';
|
|
|
|
describe('nextClassOccurrence', () => {
|
|
it('devuelve la próxima clase de hoy cuando aún no arrancó', () => {
|
|
const now = new Date('2026-09-23T18:00:00.000Z'); // miércoles 12:00 hora CDMX
|
|
|
|
const result = nextClassOccurrence({
|
|
days: ['WEDNESDAY'],
|
|
time: '18:00',
|
|
now,
|
|
timeZone: TZ,
|
|
});
|
|
|
|
expect(result).toEqual({
|
|
occurrenceAt: new Date('2026-09-24T00:00:00.000Z'),
|
|
isNow: false,
|
|
});
|
|
});
|
|
|
|
it('marca en curso si la clase arrancó hace menos de la ventana', () => {
|
|
const now = new Date('2026-09-24T01:00:00.000Z'); // miércoles 19:00 hora CDMX (1 h tras la clase)
|
|
|
|
const result = nextClassOccurrence({
|
|
days: ['WEDNESDAY'],
|
|
time: '18:00',
|
|
now,
|
|
timeZone: TZ,
|
|
});
|
|
|
|
expect(result).toEqual({
|
|
occurrenceAt: new Date('2026-09-24T00:00:00.000Z'),
|
|
isNow: true,
|
|
});
|
|
});
|
|
|
|
it('salta a la próxima semana si la clase ya pasó la ventana', () => {
|
|
const now = new Date('2026-09-24T03:00:00.000Z'); // miércoles 21:00 hora CDMX (3 h tras la clase)
|
|
|
|
const result = nextClassOccurrence({
|
|
days: ['WEDNESDAY'],
|
|
time: '18:00',
|
|
now,
|
|
timeZone: TZ,
|
|
});
|
|
|
|
expect(result).toEqual({
|
|
occurrenceAt: new Date('2026-10-01T00:00:00.000Z'),
|
|
isNow: false,
|
|
});
|
|
});
|
|
|
|
it('elige la ocurrencia más próxima entre varios días', () => {
|
|
const now = new Date('2026-09-23T18:00:00.000Z'); // miércoles 12:00 hora CDMX
|
|
|
|
const result = nextClassOccurrence({
|
|
days: ['MONDAY', 'WEDNESDAY', 'FRIDAY'],
|
|
time: '18:00',
|
|
now,
|
|
timeZone: TZ,
|
|
});
|
|
|
|
expect(result).toEqual({
|
|
occurrenceAt: new Date('2026-09-24T00:00:00.000Z'),
|
|
isNow: false,
|
|
});
|
|
});
|
|
|
|
it('cruza de mes sin problema', () => {
|
|
const now = new Date('2026-09-29T18:00:00.000Z'); // martes 12:00 hora CDMX (último día de septiembre es miércoles 30)
|
|
|
|
const result = nextClassOccurrence({
|
|
days: ['THURSDAY'],
|
|
time: '09:00',
|
|
now,
|
|
timeZone: TZ,
|
|
});
|
|
|
|
expect(result).toEqual({
|
|
occurrenceAt: new Date('2026-10-01T15:00:00.000Z'),
|
|
isNow: false,
|
|
});
|
|
});
|
|
|
|
it('devuelve null si no hay días de clase', () => {
|
|
const result = nextClassOccurrence({
|
|
days: [],
|
|
time: '18:00',
|
|
now: new Date('2026-09-23T18:00:00.000Z'),
|
|
timeZone: TZ,
|
|
});
|
|
|
|
expect(result).toBeNull();
|
|
});
|
|
|
|
it('devuelve null si la hora es inválida', () => {
|
|
const result = nextClassOccurrence({
|
|
days: ['WEDNESDAY'],
|
|
time: '99:99',
|
|
now: new Date('2026-09-23T18:00:00.000Z'),
|
|
timeZone: TZ,
|
|
});
|
|
|
|
expect(result).toBeNull();
|
|
});
|
|
|
|
it('expone el tamaño de la ventana de "en curso"', () => {
|
|
expect(CLASS_WINDOW_HOURS).toBeGreaterThan(0);
|
|
});
|
|
}); |