ether-to-astro 1.2.0 → 1.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +15 -5
- package/dist/astro-service/chart-output-service.d.ts +44 -0
- package/dist/astro-service/chart-output-service.js +110 -0
- package/dist/astro-service/date-input.d.ts +14 -0
- package/dist/astro-service/date-input.js +30 -0
- package/dist/astro-service/electional-service.d.ts +45 -0
- package/dist/astro-service/electional-service.js +305 -0
- package/dist/astro-service/natal-service.d.ts +41 -0
- package/dist/astro-service/natal-service.js +179 -0
- package/dist/astro-service/rising-sign-service.d.ts +37 -0
- package/dist/astro-service/rising-sign-service.js +137 -0
- package/dist/astro-service/service-types.d.ts +82 -0
- package/dist/astro-service/service-types.js +1 -0
- package/dist/astro-service/shared.d.ts +65 -0
- package/dist/astro-service/shared.js +98 -0
- package/dist/astro-service/sky-service.d.ts +48 -0
- package/dist/astro-service/sky-service.js +144 -0
- package/dist/astro-service/transit-service.d.ts +82 -0
- package/dist/astro-service/transit-service.js +353 -0
- package/dist/astro-service.d.ts +101 -89
- package/dist/astro-service.js +162 -1042
- package/dist/tool-registry.js +1 -1
- package/docs/product/architecture-boundaries.md +8 -0
- package/docs/releases/1.3.0.md +51 -0
- package/docs/releases/README.md +17 -0
- package/package.json +4 -1
- package/src/astro-service/chart-output-service.ts +155 -0
- package/src/astro-service/date-input.ts +40 -0
- package/src/astro-service/electional-service.ts +395 -0
- package/src/astro-service/natal-service.ts +235 -0
- package/src/astro-service/rising-sign-service.ts +181 -0
- package/src/astro-service/service-types.ts +90 -0
- package/src/astro-service/shared.ts +128 -0
- package/src/astro-service/sky-service.ts +191 -0
- package/src/astro-service/transit-service.ts +507 -0
- package/src/astro-service.ts +177 -1386
- package/src/tool-registry.ts +1 -1
- package/tests/README.md +15 -0
- package/tests/property/electional-service.property.test.ts +67 -0
- package/tests/property/helpers/arbitraries.ts +126 -0
- package/tests/property/helpers/config.ts +52 -0
- package/tests/property/helpers/runtime.ts +12 -0
- package/tests/property/houses.property.test.ts +74 -0
- package/tests/property/rising-sign-service.property.test.ts +255 -0
- package/tests/property/service-transits.property.test.ts +154 -0
- package/tests/property/time-utils.property.test.ts +91 -0
- package/tests/property/transits.property.test.ts +113 -0
- package/tests/unit/astro-service/chart-output-service.test.ts +102 -0
- package/tests/unit/astro-service/electional-service.test.ts +182 -0
- package/tests/unit/astro-service/natal-service.test.ts +126 -0
- package/tests/unit/astro-service/rising-sign-service.test.ts +145 -0
- package/tests/unit/astro-service/sky-service.test.ts +130 -0
- package/tests/unit/astro-service/transit-service.test.ts +312 -0
- package/tests/unit/astro-service.test.ts +136 -781
- package/tests/unit/rising-sign-windows.test.ts +93 -0
- package/tests/unit/tool-registry.test.ts +11 -0
- package/tests/validation/README.md +14 -0
- package/tests/validation/adapters/internal.ts +234 -4
- package/tests/validation/compare/electional.ts +151 -0
- package/tests/validation/compare/rising-sign-windows.ts +347 -0
- package/tests/validation/compare/service-transits.ts +205 -0
- package/tests/validation/fixtures/electional/core.ts +88 -0
- package/tests/validation/fixtures/rising-sign-windows/core.ts +57 -0
- package/tests/validation/fixtures/service-transits/core.ts +89 -0
- package/tests/validation/utils/fixtureTypes.ts +139 -1
- package/tests/validation/validation.spec.ts +82 -0
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import { describe, expect, it, vi } from 'vitest';
|
|
2
|
+
import { NatalService } from '../../../src/astro-service/natal-service.js';
|
|
3
|
+
import type { McpStartupDefaults } from '../../../src/entrypoint.js';
|
|
4
|
+
import type { NatalChart, PlanetPosition } from '../../../src/types.js';
|
|
5
|
+
|
|
6
|
+
function makePlanet(planet: PlanetPosition['planet'], longitude: number): PlanetPosition {
|
|
7
|
+
return {
|
|
8
|
+
planetId: 0,
|
|
9
|
+
planet,
|
|
10
|
+
longitude,
|
|
11
|
+
latitude: 0,
|
|
12
|
+
distance: 1,
|
|
13
|
+
speed: 1,
|
|
14
|
+
sign: 'Aries',
|
|
15
|
+
degree: longitude % 30,
|
|
16
|
+
isRetrograde: false,
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function makeNatalChart(): NatalChart {
|
|
21
|
+
return {
|
|
22
|
+
name: 'Test User',
|
|
23
|
+
birthDate: { year: 1990, month: 6, day: 12, hour: 14, minute: 35 },
|
|
24
|
+
location: { latitude: 37.7749, longitude: -122.4194, timezone: 'America/Los_Angeles' },
|
|
25
|
+
planets: [makePlanet('Sun', 10), makePlanet('Moon', 20)],
|
|
26
|
+
julianDay: 2451545,
|
|
27
|
+
houseSystem: 'P',
|
|
28
|
+
utcDateTime: { year: 1990, month: 6, day: 12, hour: 21, minute: 35 },
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function makeNatalService(mcpStartupDefaults: McpStartupDefaults = {}) {
|
|
33
|
+
const ephem = {
|
|
34
|
+
dateToJulianDay: vi.fn((date: Date) => date.getTime() / 86400000 + 2440587.5),
|
|
35
|
+
getAllPlanets: vi.fn(() => [makePlanet('Sun', 204), makePlanet('Moon', 270)]),
|
|
36
|
+
};
|
|
37
|
+
const houseCalc = {
|
|
38
|
+
calculateHouses: vi.fn(() => ({
|
|
39
|
+
ascendant: 270,
|
|
40
|
+
mc: 204,
|
|
41
|
+
cusps: [0, 270, 300, 330, 0, 30, 60, 90, 120, 150, 204, 240, 260],
|
|
42
|
+
system: 'W' as const,
|
|
43
|
+
})),
|
|
44
|
+
};
|
|
45
|
+
const isInitialized = vi.fn(() => true);
|
|
46
|
+
|
|
47
|
+
const natalService = new NatalService({
|
|
48
|
+
ephem: ephem as any,
|
|
49
|
+
houseCalc: houseCalc as any,
|
|
50
|
+
mcpStartupDefaults,
|
|
51
|
+
isInitialized,
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
return { natalService, ephem, houseCalc, isInitialized };
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
describe('When using the extracted NatalService', () => {
|
|
58
|
+
it('Given a polar latitude chart, then it preserves natal fallback house behavior', () => {
|
|
59
|
+
const { natalService } = makeNatalService();
|
|
60
|
+
|
|
61
|
+
const result = natalService.setNatalChart({
|
|
62
|
+
name: 'Polar User',
|
|
63
|
+
year: 1990,
|
|
64
|
+
month: 6,
|
|
65
|
+
day: 12,
|
|
66
|
+
hour: 14,
|
|
67
|
+
minute: 35,
|
|
68
|
+
latitude: 78,
|
|
69
|
+
longitude: 15,
|
|
70
|
+
timezone: 'UTC',
|
|
71
|
+
house_system: 'P',
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
expect(result.chart.houseSystem).toBe('W');
|
|
75
|
+
expect(result.data).toMatchObject({
|
|
76
|
+
name: 'Polar User',
|
|
77
|
+
requestedHouseSystem: 'P',
|
|
78
|
+
resolvedHouseSystem: 'W',
|
|
79
|
+
isPolar: true,
|
|
80
|
+
});
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
it('Given state and defaults, then it preserves server status and house validation behavior', () => {
|
|
84
|
+
const { natalService, isInitialized } = makeNatalService({
|
|
85
|
+
preferredTimezone: 'UTC',
|
|
86
|
+
preferredHouseStyle: 'W',
|
|
87
|
+
weekdayLabels: true,
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
const status = natalService.getServerStatus(makeNatalChart());
|
|
91
|
+
expect(isInitialized).toHaveBeenCalled();
|
|
92
|
+
expect(status.data).toMatchObject({
|
|
93
|
+
hasNatalChart: true,
|
|
94
|
+
natalChartName: 'Test User',
|
|
95
|
+
startupDefaults: {
|
|
96
|
+
preferredTimezone: 'UTC',
|
|
97
|
+
preferredHouseStyle: 'W',
|
|
98
|
+
weekdayLabels: true,
|
|
99
|
+
},
|
|
100
|
+
ephemerisInitialized: true,
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
expect(() => natalService.getHouses({ ...makeNatalChart(), julianDay: undefined })).toThrow(
|
|
104
|
+
/missing julianDay/i
|
|
105
|
+
);
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
it('Given missing Sun or Moon data, then it preserves the natal ephemeris error contract', () => {
|
|
109
|
+
const { natalService, ephem } = makeNatalService();
|
|
110
|
+
ephem.getAllPlanets.mockReturnValue([makePlanet('Sun', 200)]);
|
|
111
|
+
|
|
112
|
+
expect(() =>
|
|
113
|
+
natalService.setNatalChart({
|
|
114
|
+
name: 'No Moon',
|
|
115
|
+
year: 1990,
|
|
116
|
+
month: 1,
|
|
117
|
+
day: 1,
|
|
118
|
+
hour: 1,
|
|
119
|
+
minute: 1,
|
|
120
|
+
latitude: 1,
|
|
121
|
+
longitude: 1,
|
|
122
|
+
timezone: 'UTC',
|
|
123
|
+
})
|
|
124
|
+
).toThrow(/Sun\/Moon/);
|
|
125
|
+
});
|
|
126
|
+
});
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
import { describe, expect, it, vi } from 'vitest';
|
|
2
|
+
import { RisingSignService } from '../../../src/astro-service/rising-sign-service.js';
|
|
3
|
+
|
|
4
|
+
function makeRisingSignService() {
|
|
5
|
+
const ephem = {
|
|
6
|
+
dateToJulianDay: vi.fn((date: Date) => date.getTime() / 86400000 + 2440587.5),
|
|
7
|
+
};
|
|
8
|
+
const houseCalc = {
|
|
9
|
+
calculateHouses: vi.fn(() => ({
|
|
10
|
+
ascendant: 0,
|
|
11
|
+
mc: 204,
|
|
12
|
+
cusps: [0, 270, 300, 330, 0, 30, 60, 90, 120, 150, 204, 240, 260],
|
|
13
|
+
system: 'P' as const,
|
|
14
|
+
})),
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
const risingSignService = new RisingSignService({
|
|
18
|
+
ephem: ephem as any,
|
|
19
|
+
houseCalc: houseCalc as any,
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
return { risingSignService, ephem, houseCalc };
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
describe('When using the extracted RisingSignService', () => {
|
|
26
|
+
it('Given date and location inputs, then it preserves deterministic sign-interval payloads', () => {
|
|
27
|
+
const { risingSignService, houseCalc } = makeRisingSignService();
|
|
28
|
+
houseCalc.calculateHouses.mockImplementation((jd: number) => {
|
|
29
|
+
const dayFraction = ((jd % 1) + 1) % 1;
|
|
30
|
+
const ascendant = (dayFraction * 360 * 12) % 360;
|
|
31
|
+
return {
|
|
32
|
+
ascendant,
|
|
33
|
+
mc: 204,
|
|
34
|
+
cusps: [0, 270, 300, 330, 0, 30, 60, 90, 120, 150, 204, 240, 260],
|
|
35
|
+
system: 'P' as const,
|
|
36
|
+
};
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
const result = risingSignService.getRisingSignWindows({
|
|
40
|
+
date: '2026-03-28',
|
|
41
|
+
latitude: 40.7128,
|
|
42
|
+
longitude: -74.006,
|
|
43
|
+
timezone: 'America/New_York',
|
|
44
|
+
mode: 'exact',
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
const windows = (result.data as any).windows;
|
|
48
|
+
expect((result.data as any)).toMatchObject({
|
|
49
|
+
date: '2026-03-28',
|
|
50
|
+
timezone: 'America/New_York',
|
|
51
|
+
mode: 'exact',
|
|
52
|
+
});
|
|
53
|
+
expect(windows[0]).toMatchObject({
|
|
54
|
+
sign: expect.any(String),
|
|
55
|
+
start: expect.any(String),
|
|
56
|
+
end: expect.any(String),
|
|
57
|
+
durationMs: expect.any(Number),
|
|
58
|
+
});
|
|
59
|
+
expect(windows[0].start).toMatch(/[-+]\d{2}:\d{2}$/);
|
|
60
|
+
expect(result.text).toContain('Rising Sign Windows');
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
it('Given multiple transitions inside one scan bucket, then exact mode emits every sign window', () => {
|
|
64
|
+
const { risingSignService, houseCalc } = makeRisingSignService();
|
|
65
|
+
const transitions = [
|
|
66
|
+
Date.parse('2026-03-28T00:10:00Z'),
|
|
67
|
+
Date.parse('2026-03-28T00:30:00Z'),
|
|
68
|
+
Date.parse('2026-03-28T00:50:00Z'),
|
|
69
|
+
];
|
|
70
|
+
|
|
71
|
+
houseCalc.calculateHouses.mockImplementation((jd: number) => {
|
|
72
|
+
const date = new Date((jd - 2440587.5) * 86400000);
|
|
73
|
+
const millis = date.getTime();
|
|
74
|
+
const signIndex =
|
|
75
|
+
transitions.filter((transitionMs) => millis >= transitionMs).length % 2 === 0 ? 0 : 1;
|
|
76
|
+
return {
|
|
77
|
+
ascendant: signIndex * 30 + 0.1,
|
|
78
|
+
mc: 204,
|
|
79
|
+
cusps: [0, 270, 300, 330, 0, 30, 60, 90, 120, 150, 204, 240, 260],
|
|
80
|
+
system: 'P' as const,
|
|
81
|
+
};
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
const result = risingSignService.getRisingSignWindows({
|
|
85
|
+
date: '2026-03-28',
|
|
86
|
+
latitude: 40.7128,
|
|
87
|
+
longitude: -74.006,
|
|
88
|
+
timezone: 'UTC',
|
|
89
|
+
mode: 'exact',
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
const windows = (result.data as any).windows as Array<{ start: string; sign: string }>;
|
|
93
|
+
const firstHourWindows = windows.filter((window) => window.start < '2026-03-28T01:00:00+00:00');
|
|
94
|
+
|
|
95
|
+
expect(firstHourWindows).toHaveLength(4);
|
|
96
|
+
expect(firstHourWindows.map((window) => window.sign)).toEqual([
|
|
97
|
+
'Aries',
|
|
98
|
+
'Taurus',
|
|
99
|
+
'Aries',
|
|
100
|
+
'Taurus',
|
|
101
|
+
]);
|
|
102
|
+
expect(result.text).toContain('Rising Sign Windows');
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
it('Given invalid location or timezone inputs, then it preserves clear validation errors', () => {
|
|
106
|
+
const { risingSignService } = makeRisingSignService();
|
|
107
|
+
|
|
108
|
+
expect(() =>
|
|
109
|
+
risingSignService.getRisingSignWindows({
|
|
110
|
+
date: '2026-03-28',
|
|
111
|
+
latitude: 95,
|
|
112
|
+
longitude: -74,
|
|
113
|
+
timezone: 'America/New_York',
|
|
114
|
+
})
|
|
115
|
+
).toThrow(/Invalid latitude/);
|
|
116
|
+
|
|
117
|
+
expect(() =>
|
|
118
|
+
risingSignService.getRisingSignWindows({
|
|
119
|
+
date: '2026-03-28',
|
|
120
|
+
latitude: 40,
|
|
121
|
+
longitude: -190,
|
|
122
|
+
timezone: 'America/New_York',
|
|
123
|
+
})
|
|
124
|
+
).toThrow(/Invalid longitude/);
|
|
125
|
+
|
|
126
|
+
expect(() =>
|
|
127
|
+
risingSignService.getRisingSignWindows({
|
|
128
|
+
date: '2026-03-28',
|
|
129
|
+
latitude: 40,
|
|
130
|
+
longitude: -74,
|
|
131
|
+
timezone: 'Nope/Not-A-Timezone',
|
|
132
|
+
})
|
|
133
|
+
).toThrow(/Invalid timezone/);
|
|
134
|
+
|
|
135
|
+
expect(() =>
|
|
136
|
+
risingSignService.getRisingSignWindows({
|
|
137
|
+
date: '2026-03-28',
|
|
138
|
+
latitude: 40,
|
|
139
|
+
longitude: -74,
|
|
140
|
+
timezone: 'UTC',
|
|
141
|
+
mode: 'fast' as any,
|
|
142
|
+
})
|
|
143
|
+
).toThrow(/Invalid mode/);
|
|
144
|
+
});
|
|
145
|
+
});
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
import { describe, expect, it, vi } from 'vitest';
|
|
2
|
+
import { SkyService } from '../../../src/astro-service/sky-service.js';
|
|
3
|
+
import type { McpStartupDefaults } from '../../../src/entrypoint.js';
|
|
4
|
+
import type { NatalChart, PlanetPosition, RiseSetTime } from '../../../src/types.js';
|
|
5
|
+
|
|
6
|
+
function makePlanet(planet: PlanetPosition['planet'], longitude: number): PlanetPosition {
|
|
7
|
+
return {
|
|
8
|
+
planetId: 0,
|
|
9
|
+
planet,
|
|
10
|
+
longitude,
|
|
11
|
+
latitude: 0,
|
|
12
|
+
distance: 1,
|
|
13
|
+
speed: 1,
|
|
14
|
+
sign: 'Aries',
|
|
15
|
+
degree: longitude % 30,
|
|
16
|
+
isRetrograde: false,
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function makeNatalChart(): NatalChart {
|
|
21
|
+
return {
|
|
22
|
+
name: 'Test User',
|
|
23
|
+
birthDate: { year: 1990, month: 6, day: 12, hour: 14, minute: 35 },
|
|
24
|
+
location: { latitude: 37.7749, longitude: -122.4194, timezone: 'America/Los_Angeles' },
|
|
25
|
+
planets: [makePlanet('Sun', 10), makePlanet('Moon', 20)],
|
|
26
|
+
julianDay: 2451545,
|
|
27
|
+
houseSystem: 'P',
|
|
28
|
+
utcDateTime: { year: 1990, month: 6, day: 12, hour: 21, minute: 35 },
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function makeSkyService(mcpStartupDefaults: McpStartupDefaults = {}) {
|
|
33
|
+
const ephem = {
|
|
34
|
+
dateToJulianDay: vi.fn((date: Date) => date.getTime() / 86400000 + 2440587.5),
|
|
35
|
+
getAllPlanets: vi.fn(() => [makePlanet('Mercury', 42), { ...makePlanet('Saturn', 315), sign: 'Aquarius', isRetrograde: true }]),
|
|
36
|
+
};
|
|
37
|
+
const riseSetCalc = {
|
|
38
|
+
getAllRiseSet: vi.fn(async () => [
|
|
39
|
+
{
|
|
40
|
+
planet: 'Sun',
|
|
41
|
+
rise: new Date('2024-03-26T13:00:00Z'),
|
|
42
|
+
set: new Date('2024-03-27T01:00:00Z'),
|
|
43
|
+
},
|
|
44
|
+
] as RiseSetTime[]),
|
|
45
|
+
};
|
|
46
|
+
const eclipseCalc = {
|
|
47
|
+
findNextSolarEclipse: vi.fn(() => ({
|
|
48
|
+
type: 'solar' as const,
|
|
49
|
+
date: new Date('2024-04-08T18:00:00Z'),
|
|
50
|
+
eclipseType: 'Total',
|
|
51
|
+
maxTime: new Date('2024-04-08T18:00:00Z'),
|
|
52
|
+
})),
|
|
53
|
+
findNextLunarEclipse: vi.fn(() => null),
|
|
54
|
+
};
|
|
55
|
+
const now = vi.fn(() => new Date('2024-03-26T12:00:00Z'));
|
|
56
|
+
const formatTimestamp = vi.fn((date: Date, timezone: string) => `${timezone}:${date.toISOString()}`);
|
|
57
|
+
|
|
58
|
+
const skyService = new SkyService({
|
|
59
|
+
ephem: ephem as any,
|
|
60
|
+
riseSetCalc: riseSetCalc as any,
|
|
61
|
+
eclipseCalc: eclipseCalc as any,
|
|
62
|
+
mcpStartupDefaults,
|
|
63
|
+
now,
|
|
64
|
+
formatTimestamp,
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
return { skyService, ephem, riseSetCalc, eclipseCalc, now, formatTimestamp };
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
describe('When using the extracted SkyService', () => {
|
|
71
|
+
it('Given current planetary motion, then it preserves retrograde and asteroid payloads', () => {
|
|
72
|
+
const { skyService, ephem } = makeSkyService({ preferredTimezone: 'UTC' });
|
|
73
|
+
|
|
74
|
+
const retro = skyService.getRetrogradePlanets();
|
|
75
|
+
expect(retro.data).toMatchObject({
|
|
76
|
+
date: '2024-03-26',
|
|
77
|
+
timezone: 'UTC',
|
|
78
|
+
});
|
|
79
|
+
expect((retro.data as any).planets).toEqual(
|
|
80
|
+
expect.arrayContaining([expect.objectContaining({ planet: 'Saturn', isRetrograde: true })])
|
|
81
|
+
);
|
|
82
|
+
|
|
83
|
+
ephem.getAllPlanets.mockReturnValueOnce([
|
|
84
|
+
{ ...makePlanet('True Node', 120), sign: 'Leo' },
|
|
85
|
+
{ ...makePlanet('Chiron', 210), sign: 'Scorpio', isRetrograde: true },
|
|
86
|
+
]);
|
|
87
|
+
const asteroidPositions = skyService.getAsteroidPositions('UTC');
|
|
88
|
+
expect(asteroidPositions.text).toContain('Rx');
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
it('Given no retrograde planets, then it preserves the retrograde empty-state text', () => {
|
|
92
|
+
const { skyService, ephem } = makeSkyService();
|
|
93
|
+
ephem.getAllPlanets.mockReturnValue([{ ...makePlanet('Sun', 10), isRetrograde: false, speed: 1 }]);
|
|
94
|
+
|
|
95
|
+
const result = skyService.getRetrogradePlanets('UTC');
|
|
96
|
+
expect(result.text).toContain('No planets are currently retrograde');
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
it('Given runtime rise-set and eclipse lookups, then it preserves readable summaries', async () => {
|
|
100
|
+
const { skyService } = makeSkyService({ preferredTimezone: 'America/New_York' });
|
|
101
|
+
|
|
102
|
+
const riseSet = await skyService.getRiseSetTimes(makeNatalChart());
|
|
103
|
+
expect(riseSet.data).toMatchObject({
|
|
104
|
+
date: '2024-03-26',
|
|
105
|
+
timezone: 'America/Los_Angeles',
|
|
106
|
+
});
|
|
107
|
+
expect(riseSet.text).toContain('America/New_York');
|
|
108
|
+
|
|
109
|
+
const eclipses = skyService.getNextEclipses('UTC');
|
|
110
|
+
expect(eclipses.data).toMatchObject({
|
|
111
|
+
timezone: 'UTC',
|
|
112
|
+
eclipses: [expect.objectContaining({ type: 'solar', eclipseType: 'Total' })],
|
|
113
|
+
});
|
|
114
|
+
expect(eclipses.text).toContain('Next Solar Eclipse');
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
it('Given both eclipse types, then it includes both in the response payload', () => {
|
|
118
|
+
const { skyService, eclipseCalc } = makeSkyService();
|
|
119
|
+
eclipseCalc.findNextLunarEclipse.mockReturnValue({
|
|
120
|
+
type: 'lunar',
|
|
121
|
+
date: new Date('2024-09-18T00:00:00Z'),
|
|
122
|
+
eclipseType: 'Partial',
|
|
123
|
+
maxTime: new Date('2024-09-18T00:00:00Z'),
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
const result = skyService.getNextEclipses('UTC');
|
|
127
|
+
expect((result.data as any).eclipses).toHaveLength(2);
|
|
128
|
+
expect(result.text).toContain('Next Lunar Eclipse');
|
|
129
|
+
});
|
|
130
|
+
});
|