diff --git a/scripts/build-meetings.js b/scripts/build-meetings.js index b63a20fd4873..ee95803d9d44 100644 --- a/scripts/build-meetings.js +++ b/scripts/build-meetings.js @@ -2,13 +2,22 @@ const { writeFileSync } = require('fs'); const { resolve } = require('path'); const { google } = require('googleapis'); -async function buildMeetings() { - const auth = new google.auth.GoogleAuth({ - scopes: ['https://www.googleapis.com/auth/calendar'], - credentials: JSON.parse(process.env.CALENDAR_SERVICE_ACCOUNT), - }); +async function buildMeetings(writePath) { + let auth; + let calendar; + + try { + auth = new google.auth.GoogleAuth({ + scopes: ['https://www.googleapis.com/auth/calendar'], + credentials: process.env.CALENDAR_SERVICE_ACCOUNT ? JSON.parse(process.env.CALENDAR_SERVICE_ACCOUNT) : undefined, + }); + + calendar = google.calendar({ version: 'v3', auth }); + + } catch (err) { + throw new Error(`Authentication failed: ${err.message}`); + } - const calendar = google.calendar({ version: 'v3', auth }); let eventsItems; try { @@ -20,8 +29,9 @@ async function buildMeetings() { const timeMax = new Date( Date.parse(currentTime) + 30 * 24 * 60 * 60 * 1000 ).toISOString(); + const eventsList = await calendar.events.list({ - calendarId: process.env.CALENDAR_ID, + calendarId: process.env.CALENDAR_ID, timeMax: timeMax, timeMin: timeMin, }); @@ -40,14 +50,18 @@ async function buildMeetings() { }); const eventsForHuman = JSON.stringify(eventsItems, null, ' '); - // console.log('The following events got fetched', eventsForHuman); - - writeFileSync( - resolve(__dirname, '../config', 'meetings.json'), - eventsForHuman - ); - } catch (e) { - console.error(e); + console.log('The following events got fetched', eventsForHuman); + + writeFileSync(writePath, eventsForHuman); + + } catch (err) { + throw new Error(`Failed to fetch or process events: ${err.message}`); } } -buildMeetings(); + +/* istanbul ignore next */ +if (require.main === module) { + buildMeetings(resolve(__dirname, '../config', 'meetings.json')); +} + +module.exports = { buildMeetings }; diff --git a/tests/build-meetings.test.js b/tests/build-meetings.test.js new file mode 100644 index 000000000000..bd4d9db5b1ec --- /dev/null +++ b/tests/build-meetings.test.js @@ -0,0 +1,117 @@ +const { google } = require('googleapis'); +const path = require("path"); +const { readFileSync, mkdirSync, rmSync } = require('fs'); +const { buildMeetings } = require('../scripts/build-meetings'); +const { mockEvents, expectedContent } = require('../tests/fixtures/meetingsData'); + +jest.mock('googleapis', () => { + const events = { + list: jest.fn(), + }; + const calendar = { + events, + }; + const google = { + calendar: jest.fn(() => calendar), + auth: { + GoogleAuth: jest.fn(() => ({ + getClient: jest.fn(), + })), + }, + }; + return { google }; +}); + +describe('buildMeetings', () => { + const testDir = path.join(__dirname, 'testCache'); + const outputFilePath = path.join(testDir, 'meetings.json'); + + beforeEach(() => { + jest.clearAllMocks(); + process.env.CALENDAR_SERVICE_ACCOUNT = JSON.stringify({ key: 'test_key' }); + process.env.CALENDAR_ID = 'test_calendar_id'; + + mkdirSync(testDir, { recursive: true }); + }); + + afterEach(() => { + rmSync(testDir, { recursive: true, force: true }); + }); + + it('should fetch events, process them, and write to a file', async () => { + google.calendar().events.list.mockResolvedValue({ data: { items: mockEvents } }); + + await buildMeetings(outputFilePath); + + expect(google.auth.GoogleAuth).toHaveBeenCalledWith({ + scopes: ['https://www.googleapis.com/auth/calendar'], + credentials: { key: 'test_key' }, + }); + expect(google.calendar).toHaveBeenCalled(); + expect(google.calendar().events.list).toHaveBeenCalledWith({ + calendarId: 'test_calendar_id', + timeMax: expect.any(String), + timeMin: expect.any(String), + }); + + const fileContent = readFileSync(outputFilePath, 'utf8'); + const parsedContent = JSON.parse(fileContent); + + expect(parsedContent).toEqual(expectedContent); + }); + + it('should throw an error if the Google API call fails', async () => { + google.calendar().events.list.mockRejectedValue(new Error('Google API error')); + + try { + await buildMeetings(outputFilePath) + } catch (err) { + expect(err.message).toContain('Google API error'); + } + }); + + it('should handle undefined CALENDAR_SERVICE_ACCOUNT', async () => { + delete process.env.CALENDAR_SERVICE_ACCOUNT; + + google.calendar().events.list.mockResolvedValue({ data: { items: [] } }); + + await buildMeetings(outputFilePath); + + expect(google.auth.GoogleAuth).toHaveBeenCalledWith({ + scopes: ['https://www.googleapis.com/auth/calendar'], + credentials: undefined, + }); + + const fileContent = readFileSync(outputFilePath, 'utf8'); + expect(fileContent).toBe('[]'); + }); + + it('should throw an error if authentication fails', async () => { + google.auth.GoogleAuth.mockImplementation(() => { + throw new Error('Authentication failed'); + }); + + try { + await buildMeetings(outputFilePath) + } catch (err) { + expect(err.message).toContain('Authentication failed') + } + }); + + it('should handle file write errors', async () => { + google.auth.GoogleAuth.mockImplementation(() => ({ + getClient: jest.fn(), + })); + + google.calendar().events.list.mockResolvedValue({ data: { items: mockEvents } }); + + const invalidPath = '/root/invalid_dir/meetings.json'; + + try { + await buildMeetings(invalidPath); + } catch (err) { + expect(err.message).toMatch(/ENOENT|EACCES/); + } + }); + +}); diff --git a/tests/fixtures/meetingsData.js b/tests/fixtures/meetingsData.js new file mode 100644 index 000000000000..fa55b7695483 --- /dev/null +++ b/tests/fixtures/meetingsData.js @@ -0,0 +1,25 @@ +const mockEvents = [ + { + summary: 'Community Meeting', + htmlLink: 'https://www.google.com/calendar/event?eid=example', + extendedProperties: { + private: { + ISSUE_ID: '123', + BANNER: 'https://example.com/banner.jpg', + }, + }, + start: { dateTime: '2024-02-20T16:00:00.000Z' }, + }, +]; + +const expectedContent = [ + { + banner: "https://example.com/banner.jpg", + calLink: "https://www.google.com/calendar/event?eid=example", + date: "2024-02-20T16:00:00.000Z", + title: "Community Meeting", + url: "https://github.com/asyncapi/community/issues/123" + }, +]; + +module.exports = { mockEvents, expectedContent }