fuzzyworld

when-am-i / files / src / utils / calendarConflicts.test.ts

calendarConflicts.test.ts

3.8 KB · history · blame

1import { describe, it, expect } from 'vitest';
2import { buildConflictGraph } from './calendarConflicts';
3import type { CalendarRegion, Era } from '../types';
4
5function box(minLng: number, minLat: number, maxLng: number, maxLat: number) {
6 return {
7 type: 'Polygon' as const,
8 coordinates: [[[minLng, minLat], [maxLng, minLat], [maxLng, maxLat], [minLng, minLat]] as [number, number][]],
9 };
10}
11
12function regionWith(eras: Pick<Era, 'calendar' | 'start' | 'end'>[], geo = box(0, 0, 1, 1)): CalendarRegion {
13 return {
14 region: { name: 'Test', geo },
15 eras: eras.map((e) => ({ name: e.calendar, ...e })),
16 };
17}
18
19describe('buildConflictGraph', () => {
20 it('connects two eras in the same region that overlap in time', () => {
21 const regions = [
22 regionWith([
23 { calendar: 'A', start: '1700-01-01', end: '1800-01-01' },
24 { calendar: 'B', start: '1750-01-01', end: '1850-01-01' },
25 ]),
26 ];
27 const graph = buildConflictGraph(regions);
28 expect(graph.get('a')?.has('b')).toBe(true);
29 expect(graph.get('b')?.has('a')).toBe(true);
30 });
31
32 it('does not connect two eras in the same region that do not overlap in time', () => {
33 const regions = [
34 regionWith([
35 { calendar: 'A', start: '1700-01-01', end: '1750-01-01' },
36 { calendar: 'B', start: '1750-01-01', end: '1800-01-01' },
37 ]),
38 ];
39 const graph = buildConflictGraph(regions);
40 expect(graph.get('a')?.has('b')).toBeFalsy();
41 });
42
43 it('connects eras in different regions only when their bounding boxes overlap', () => {
44 const overlapping = [
45 regionWith([{ calendar: 'A', start: null, end: null }], box(0, 0, 10, 10)),
46 regionWith([{ calendar: 'B', start: null, end: null }], box(5, 5, 15, 15)),
47 ];
48 const graph1 = buildConflictGraph(overlapping);
49 expect(graph1.get('a')?.has('b')).toBe(true);
50
51 const farApart = [
52 regionWith([{ calendar: 'A', start: null, end: null }], box(0, 0, 10, 10)),
53 regionWith([{ calendar: 'C', start: null, end: null }], box(100, 100, 110, 110)),
54 ];
55 const graph2 = buildConflictGraph(farApart);
56 expect(graph2.get('a')?.has('c')).toBeFalsy();
57 });
58
59 it('does not connect eras in overlapping regions if their time ranges do not overlap', () => {
60 const regions = [
61 regionWith([{ calendar: 'A', start: '1700-01-01', end: '1800-01-01' }], box(0, 0, 10, 10)),
62 regionWith([{ calendar: 'B', start: '1900-01-01', end: '2000-01-01' }], box(5, 5, 15, 15)),
63 ];
64 const graph = buildConflictGraph(regions);
65 expect(graph.get('a')?.has('b')).toBeFalsy();
66 });
67
68 it('connects any calendar still active after 1927 to the modern global Gregorian default', () => {
69 const regions = [regionWith([{ calendar: 'A', start: '1900-01-01', end: null }])];
70 const graph = buildConflictGraph(regions);
71 expect(graph.get('a')?.has('gregorian')).toBe(true);
72 });
73
74 it('does not connect a calendar that ended before 1927 to the modern Gregorian default', () => {
75 const regions = [regionWith([{ calendar: 'A', start: '1700-01-01', end: '1800-01-01' }])];
76 const graph = buildConflictGraph(regions);
77 expect(graph.get('a')?.has('gregorian')).toBeFalsy();
78 });
79
80 it('treats null start/end as open-ended for overlap purposes', () => {
81 const regions = [
82 regionWith([
83 { calendar: 'A', start: null, end: null },
84 { calendar: 'B', start: '3000-01-01', end: '3100-01-01' },
85 ]),
86 ];
87 const graph = buildConflictGraph(regions);
88 expect(graph.get('a')?.has('b')).toBe(true);
89 });
90});