import { describe, expect, it } from "vitest"; import { fromWallTime } from "@/lib/dates"; import { filterEvents, groupByMonth, matchesQuery, matchesRange, sortEvents } from "@/lib/filter"; import { makeEvent } from "@/lib/test-fixtures"; const now = new Date("2026-09-27T10:00:00Z"); // 12:00 Berlin describe("matchesRange", () => { it("keeps an event that ended earlier today in 'future' until midnight", () => { const earlierToday = makeEvent({ start: fromWallTime("2026-09-27", "08:00"), end: fromWallTime("2026-09-27", "09:00"), }); expect(matchesRange(earlierToday, "future", now)).toBe(true); expect(matchesRange(earlierToday, "past", now)).toBe(false); }); it("puts yesterday's event in 'past'", () => { const yesterday = makeEvent({ start: fromWallTime("2026-09-26", "19:00"), end: fromWallTime("2026-09-26", "21:00") }); expect(matchesRange(yesterday, "past", now)).toBe(true); expect(matchesRange(yesterday, "future", now)).toBe(false); }); it("treats any repeating event as current, whatever its calendar", () => { const old = { start: fromWallTime("1990-01-01", "00:00"), end: fromWallTime("1990-01-01", "23:59") }; const birthday = makeEvent({ ...old, calendarId: 5, repeatFrequency: "YEARLY" }); const weeklyRehearsal = makeEvent({ ...old, calendarId: 4, repeatFrequency: "WEEKLY" }); for (const e of [birthday, weeklyRehearsal]) { expect(matchesRange(e, "future", now)).toBe(true); expect(matchesRange(e, "past", now)).toBe(false); expect(matchesRange(e, "all", now)).toBe(true); } }); it("no longer exempts a non-repeating event just for being in Birthdays", () => { const oneOff = makeEvent({ calendarId: 5, start: fromWallTime("2020-01-01", "00:00"), end: fromWallTime("2020-01-01", "23:59") }); expect(matchesRange(oneOff, "future", now)).toBe(false); }); }); describe("matchesQuery", () => { const event = makeEvent({ name: "Probe Weihnachtskonzert", location: "Gemeindehaus Süd" }); it("matches title and location, case-insensitively, all words", () => { expect(matchesQuery(event, "")).toBe(true); expect(matchesQuery(event, "weihnacht")).toBe(true); expect(matchesQuery(event, "PROBE süd")).toBe(true); expect(matchesQuery(event, "probe nord")).toBe(false); }); }); describe("filterEvents", () => { it("combines calendar, range and search", () => { const events = [ makeEvent({ eventId: 1, calendarId: 1, name: "Konzert" }), makeEvent({ eventId: 2, calendarId: 2, name: "Konzert" }), makeEvent({ eventId: 3, calendarId: 1, name: "Probe" }), ]; const result = filterEvents(events, { calendarIds: [1], range: "future", query: "konz" }, now); expect(result.map((e) => e.eventId)).toEqual([1]); }); }); describe("sortEvents", () => { const a = makeEvent({ eventId: 1, name: "B", start: fromWallTime("2026-10-02", "19:00") }); const b = makeEvent({ eventId: 2, name: "A", start: fromWallTime("2026-10-02", "19:00") }); const c = makeEvent({ eventId: 3, name: "C", start: fromWallTime("2026-10-01", "19:00") }); it("orders by start and breaks ties by title", () => { expect(sortEvents([a, b, c], "start_asc", now).map((e) => e.eventId)).toEqual([3, 2, 1]); // Descending flips the dates only; equal starts still read A before B. expect(sortEvents([a, b, c], "start_desc", now).map((e) => e.eventId)).toEqual([2, 1, 3]); }); it("does not mutate its input", () => { const input = [a, b, c]; sortEvents(input, "start_asc", now); expect(input.map((e) => e.eventId)).toEqual([1, 2, 3]); }); it("sorts a birthday by its next occurrence, not its year", () => { const birthday = makeEvent({ eventId: 9, repeatFrequency: "YEARLY", start: fromWallTime("1990-10-01", "12:00"), end: fromWallTime("1990-10-01", "23:59") }); const concert = makeEvent({ eventId: 8, start: fromWallTime("2026-10-02", "19:00") }); expect(sortEvents([concert, birthday], "start_asc", now).map((e) => e.eventId)).toEqual([9, 8]); }); it("sorts by created date, missing ones first", () => { const x = makeEvent({ eventId: 1, created: new Date("2026-05-01T00:00:00Z") }); const y = makeEvent({ eventId: 2, created: null }); expect(sortEvents([x, y], "created_asc", now).map((e) => e.eventId)).toEqual([2, 1]); }); }); describe("groupByMonth", () => { it("groups consecutive events by Berlin month, labelled in German", () => { const events = [ makeEvent({ eventId: 1, start: fromWallTime("2026-10-01", "00:30") }), // still 30 Sep in UTC makeEvent({ eventId: 2, start: fromWallTime("2026-10-20", "19:00") }), makeEvent({ eventId: 3, start: fromWallTime("2026-11-02", "19:00") }), ]; const groups = groupByMonth(events, now); expect(groups.map((g) => [g.key, g.label, g.events.map((e) => e.eventId)])).toEqual([ ["2026-10", "Oktober 2026", [1, 2]], ["2026-11", "November 2026", [3]], ]); }); });