91 lines
3.2 KiB
TypeScript
91 lines
3.2 KiB
TypeScript
import { expect, test } from 'bun:test';
|
|
|
|
import { isSlotInPast } from '@/lib/slot-validator';
|
|
|
|
/**
|
|
* Helper to create a bookingDate like parseIsoDate does (UTC midnight).
|
|
*/
|
|
function bookingDate(year: number, month: number, day: number): Date {
|
|
return new Date(Date.UTC(year, month - 1, day));
|
|
}
|
|
|
|
/**
|
|
* Helper to create a "now" date at a specific local time.
|
|
* Using local date constructor guarantees the date parts
|
|
* (getFullYear, getMonth, getDate) match the arguments
|
|
* regardless of the test runner's timezone.
|
|
*/
|
|
function localDate(year: number, month: number, day: number, hours: number, minutes: number): Date {
|
|
return new Date(year, month - 1, day, hours, minutes, 0, 0);
|
|
}
|
|
|
|
test('future date — always allowed', () => {
|
|
const future = bookingDate(2027, 6, 10); // 2027-06-10
|
|
const now = localDate(2026, 6, 8, 12, 0); // 2026-06-08 12:00
|
|
|
|
expect(isSlotInPast(future, '10:00', 60, now)).toBe(false);
|
|
});
|
|
|
|
test('past date — rejected', () => {
|
|
const past = bookingDate(2025, 6, 8); // 2025-06-08
|
|
const now = localDate(2026, 6, 8, 12, 0); // 2026-06-08 12:00
|
|
|
|
expect(isSlotInPast(past, '10:00', 60, now)).toBe(true);
|
|
});
|
|
|
|
test('today, slot not started yet — allowed', () => {
|
|
const today = bookingDate(2026, 6, 8); // 2026-06-08
|
|
const now = localDate(2026, 6, 8, 9, 0); // 09:00, slot empieza a las 10:00
|
|
|
|
expect(isSlotInPast(today, '10:00', 60, now)).toBe(false);
|
|
});
|
|
|
|
test('today, slot in progress less than half elapsed — allowed', () => {
|
|
const today = bookingDate(2026, 6, 8);
|
|
const now = localDate(2026, 6, 8, 10, 10); // 10:10, slot 10:00-11:00 (60 min), 10 min elapsed
|
|
|
|
expect(isSlotInPast(today, '10:00', 60, now)).toBe(false);
|
|
});
|
|
|
|
test('today, slot in progress more than half elapsed — rejected', () => {
|
|
const today = bookingDate(2026, 6, 8);
|
|
const now = localDate(2026, 6, 8, 10, 40); // 10:40, slot 10:00-11:00 (60 min), 40 min elapsed
|
|
|
|
expect(isSlotInPast(today, '10:00', 60, now)).toBe(true);
|
|
});
|
|
|
|
test('today, slot already ended — rejected', () => {
|
|
const today = bookingDate(2026, 6, 8);
|
|
const now = localDate(2026, 6, 8, 11, 1); // 11:01, slot 10:00-11:00 terminó
|
|
|
|
expect(isSlotInPast(today, '10:00', 60, now)).toBe(true);
|
|
});
|
|
|
|
test('today, exactly half elapsed — allowed (inclusive boundary)', () => {
|
|
const today = bookingDate(2026, 6, 8);
|
|
const now = localDate(2026, 6, 8, 10, 30); // 10:30, slot 10:00-11:00 (60 min), exactamente 30 min
|
|
|
|
expect(isSlotInPast(today, '10:00', 60, now)).toBe(false);
|
|
});
|
|
|
|
test('today, slot starts exactly now — allowed', () => {
|
|
const today = bookingDate(2026, 6, 8);
|
|
const now = localDate(2026, 6, 8, 10, 0); // 10:00 exacto
|
|
|
|
expect(isSlotInPast(today, '10:00', 60, now)).toBe(false);
|
|
});
|
|
|
|
test('90 min slot with 10 min elapsed — allowed', () => {
|
|
const today = bookingDate(2026, 6, 8);
|
|
const now = localDate(2026, 6, 8, 12, 10); // 12:10, slot 12:00-13:30, 10/90 elapsed
|
|
|
|
expect(isSlotInPast(today, '12:00', 90, now)).toBe(false);
|
|
});
|
|
|
|
test('90 min slot with 50 min elapsed — rejected', () => {
|
|
const today = bookingDate(2026, 6, 8);
|
|
const now = localDate(2026, 6, 8, 12, 50); // 12:50, slot 12:00-13:30, 50/90 elapsed
|
|
|
|
expect(isSlotInPast(today, '12:00', 90, now)).toBe(true);
|
|
});
|