Skip to content

Commit 71093cb

Browse files
Copilotavivkeller
andauthored
Exit gracefully when no meeting found instead of throwing error (#203)
Co-authored-by: avivkeller <38299977+avivkeller@users.noreply.github.com> Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: Aviv Keller <me@aviv.sh>
1 parent 9bc19d7 commit 71093cb

File tree

4 files changed

+153
-10
lines changed

4 files changed

+153
-10
lines changed

create-node-meeting-artifacts.mjs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,18 @@ const events = await calendar.getEventsFromCalendar(
7171

7272
const meetingDate = await calendar.findNextMeetingDate(events, meetingConfig);
7373

74+
// If no meeting is found, exit gracefully
75+
if (!meetingDate) {
76+
const [weekStart, weekEnd] = calendar.getWeekBounds();
77+
78+
console.log(
79+
`No meeting found for ${meetingConfig.properties.GROUP_NAME || 'this group'} ` +
80+
`in the next week (${weekStart.toISOString().split('T')[0]} to ${weekEnd.toISOString().split('T')[0]}). ` +
81+
`This is expected for bi-weekly meetings or meetings that don't occur every week.`
82+
);
83+
process.exit(0);
84+
}
85+
7486
// Step 8: Get Meeting Title
7587
const meetingTitle = meetings.generateMeetingTitle(
7688
config,

package.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -35,9 +35,9 @@
3535
"format": "prettier --write .",
3636
"format:check": "prettier --check .",
3737
"check": "npm run lint && npm run format:check",
38-
"test": "node --test test/**/*.test.mjs",
39-
"test:watch": "node --test --watch test/**/*.test.mjs",
40-
"test:coverage": "node --test --experimental-test-coverage test/**/*.test.mjs"
38+
"test": "node --test",
39+
"test:watch": "node --test --watch",
40+
"test:coverage": "node --test --experimental-test-coverage"
4141
},
4242
"author": "",
4343
"license": "MIT"

src/calendar.mjs

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ export const getEventsFromCalendar = async url => {
1414
/**
1515
* @param {Date} start
1616
*/
17-
const getWeekBounds = (start = new Date()) => {
17+
export const getWeekBounds = (start = new Date()) => {
1818
start.setUTCHours(0, 0, 0, 0);
1919

2020
const end = new Date(start);
@@ -27,7 +27,7 @@ const getWeekBounds = (start = new Date()) => {
2727
* Finds the next meeting event in any iCal feed for the current week
2828
* @param {ical.CalendarComponent[]} allEvents - The events
2929
* @param {import('./types').MeetingConfig} meetingConfig - Meeting configuration object
30-
* @returns {Promise<Date>} The date of the next meeting
30+
* @returns {Promise<Date|null>} The date of the next meeting, or null if no meeting is found
3131
*/
3232
export const findNextMeetingDate = async (allEvents, { properties }) => {
3333
const [weekStart, weekEnd] = getWeekBounds();
@@ -50,9 +50,5 @@ export const findNextMeetingDate = async (allEvents, { properties }) => {
5050
}
5151
}
5252

53-
throw new Error(
54-
`No meeting found for ${properties.GROUP_NAME || 'this group'} ` +
55-
`in the next week (${weekStart.toISOString().split('T')[0]} to ${weekEnd.toISOString().split('T')[0]}). ` +
56-
`This is expected for bi-weekly meetings or meetings that don't occur every week.`
57-
);
53+
return null;
5854
};

test/calendar.test.mjs

Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
import assert from 'node:assert';
2+
import { describe, it } from 'node:test';
3+
4+
import { findNextMeetingDate } from '../src/calendar.mjs';
5+
6+
describe('Calendar', () => {
7+
describe('findNextMeetingDate', () => {
8+
it('should return null when no matching events are found', async () => {
9+
const allEvents = [];
10+
const meetingConfig = {
11+
properties: {
12+
CALENDAR_FILTER: 'Test Meeting',
13+
GROUP_NAME: 'Test Group',
14+
},
15+
};
16+
17+
const result = await findNextMeetingDate(allEvents, meetingConfig);
18+
19+
assert.strictEqual(result, null);
20+
});
21+
22+
it('should return null when events exist but do not match the filter', async () => {
23+
const allEvents = [
24+
{
25+
summary: 'Different Meeting',
26+
rrule: {
27+
options: {},
28+
between: () => [new Date('2025-11-04T14:00:00Z')],
29+
},
30+
tzid: 'UTC',
31+
},
32+
];
33+
const meetingConfig = {
34+
properties: {
35+
CALENDAR_FILTER: 'Test Meeting',
36+
GROUP_NAME: 'Test Group',
37+
},
38+
};
39+
40+
const result = await findNextMeetingDate(allEvents, meetingConfig);
41+
42+
assert.strictEqual(result, null);
43+
});
44+
45+
it('should return null when matching events exist but have no recurrences in the week', async () => {
46+
const allEvents = [
47+
{
48+
summary: 'Test Meeting',
49+
rrule: {
50+
options: {},
51+
between: () => [],
52+
},
53+
tzid: 'UTC',
54+
},
55+
];
56+
const meetingConfig = {
57+
properties: {
58+
CALENDAR_FILTER: 'Test Meeting',
59+
GROUP_NAME: 'Test Group',
60+
},
61+
};
62+
63+
const result = await findNextMeetingDate(allEvents, meetingConfig);
64+
65+
assert.strictEqual(result, null);
66+
});
67+
68+
it('should return the meeting date when a matching event with recurrence is found', async () => {
69+
const expectedDate = new Date('2025-11-04T14:00:00Z');
70+
const allEvents = [
71+
{
72+
summary: 'Test Meeting',
73+
rrule: {
74+
options: {},
75+
between: () => [expectedDate],
76+
},
77+
tzid: 'UTC',
78+
},
79+
];
80+
const meetingConfig = {
81+
properties: {
82+
CALENDAR_FILTER: 'Test Meeting',
83+
GROUP_NAME: 'Test Group',
84+
},
85+
};
86+
87+
const result = await findNextMeetingDate(allEvents, meetingConfig);
88+
89+
assert.strictEqual(result, expectedDate);
90+
});
91+
92+
it('should match events using the description field', async () => {
93+
const expectedDate = new Date('2025-11-04T14:00:00Z');
94+
const allEvents = [
95+
{
96+
description: 'This is a Test Meeting',
97+
rrule: {
98+
options: {},
99+
between: () => [expectedDate],
100+
},
101+
tzid: 'UTC',
102+
},
103+
];
104+
const meetingConfig = {
105+
properties: {
106+
CALENDAR_FILTER: 'Test Meeting',
107+
GROUP_NAME: 'Test Group',
108+
},
109+
};
110+
111+
const result = await findNextMeetingDate(allEvents, meetingConfig);
112+
113+
assert.strictEqual(result, expectedDate);
114+
});
115+
116+
it('should return null when events do not have rrule', async () => {
117+
const allEvents = [
118+
{
119+
summary: 'Test Meeting',
120+
tzid: 'UTC',
121+
},
122+
];
123+
const meetingConfig = {
124+
properties: {
125+
CALENDAR_FILTER: 'Test Meeting',
126+
GROUP_NAME: 'Test Group',
127+
},
128+
};
129+
130+
const result = await findNextMeetingDate(allEvents, meetingConfig);
131+
132+
assert.strictEqual(result, null);
133+
});
134+
});
135+
});

0 commit comments

Comments
 (0)