diff --git a/__tests__/mongoClient.test.ts b/__tests__/mongoClient.test.ts new file mode 100644 index 000000000..67b49bd6d --- /dev/null +++ b/__tests__/mongoClient.test.ts @@ -0,0 +1,58 @@ +/** @jest-environment node */ +import { getClient, resetClient } from '@utils/mongodb/mongoClient.mjs'; +import { MongoClient } from 'mongodb'; + +describe('getClient', () => { + beforeEach(async () => { + await resetClient(); + jest.restoreAllMocks(); + }); + + it('should throw an error if MONGODB_URI is missing', async () => { + const originalUri = process.env.MONGODB_URI; + delete process.env.MONGODB_URI; + + await resetClient(); + + await expect(getClient()).rejects.toThrow( + 'Missing MONGODB_URI environment variable.' + ); + + process.env.MONGODB_URI = originalUri; + }); + + it('should return the same instance on multiple calls', async () => { + const mockDb = { db: jest.fn() }; + const spy = jest + .spyOn(MongoClient.prototype, 'connect') + .mockResolvedValue(mockDb as any); + + const c1 = await getClient(); + const c2 = await getClient(); + + expect(c1).toBe(c2); + expect(spy).toHaveBeenCalledTimes(1); + }); + + it('should dedupe concurrent callers using cachedPromise', async () => { + const mockDb = { db: jest.fn() }; + const spy = jest + .spyOn(MongoClient.prototype, 'connect') + .mockResolvedValue(mockDb as any); + const [c1, c2] = await Promise.all([getClient(), getClient()]); + expect(c1).toBe(c2); + expect(spy).toHaveBeenCalledTimes(1); + }); + + it('should retry after a failed connection', async () => { + const spy = jest + .spyOn(MongoClient.prototype, 'connect') + .mockRejectedValueOnce(new Error('Network Fail')) + .mockResolvedValueOnce({ db: jest.fn() } as any); + + await expect(getClient()).rejects.toThrow('Network Fail'); + + await expect(getClient()).resolves.toBeDefined(); + expect(spy).toHaveBeenCalledTimes(2); + }); +}); diff --git a/app/(api)/_actions/emails/emailTemplates/2026JudgeHubInviteTemplate.ts b/app/(api)/_actions/emails/emailTemplates/2026JudgeHubInviteTemplate.ts new file mode 100644 index 000000000..db6eda9e9 --- /dev/null +++ b/app/(api)/_actions/emails/emailTemplates/2026JudgeHubInviteTemplate.ts @@ -0,0 +1,79 @@ +export default function judgeHubInviteTemplate( + fname: string, + inviteLink: string +) { + const EMAIL_SUBJECT = '[ACTION REQUIRED] HackDavis 2026 Judging App Invite'; + const HEADER_IMAGE_URL = `${process.env.BASE_URL}/email/2025_email_header.png`; + const FOOTER_IMAGE_URL = `${process.env.BASE_URL}/email/2025_email_footer.png`; + const MEETING_RECORDING_URL = + 'https://drive.google.com/file/d/1Lit5fvhev2q8mkv2QyDgTgeh3cfLeX9l/view?usp=sharing'; + const JUDGING_GUIDE_URL = + 'https://www.notion.so/hackdavis/HackDavis-2025-Judging-Guide-1c32d37fcae880b1ba3aeb0a9a7841b7?pvs=4'; + const INVITATION_TO_REGISTER_GUIDE_URL = + 'https://www.notion.so/hackdavis/HackDavis-2025-Judging-Guide-1c32d37fcae880b1ba3aeb0a9a7841b7?pvs=4#1cb2d37fcae880b6a5f4e3d793349bf6'; + const DISCORD_SERVER_URL = 'https://discord.gg/wc6QQEc'; + + return ` + +
+ + +Hi ${fname},
+Thank you again for joining us as a judge, weβre thrilled to have you on board! Here are some key resources from our virtual orientation:
+πΉ Meeting Recording: ${MEETING_RECORDING_URL}
+πΉ Judging Guide: ${JUDGING_GUIDE_URL}
+You are requested to carefully review the judging guide and familiarize yourself with its content before the event for a smooth judging experience. Kindly do not share the Judging Guide with anyone outside the judging team.
+IMPORTANT NEXT STEP: Create an account on our Judging Application
+β οΈ The Judging Application is a key prerequisite for the day of the event! Please carefully review the Invitation to Register section of the Judging Guide before proceeding to create your account.
+Please use the following unique invite link below to create your judge account. Do NOT share it with anyone else.
+π Invite Link: ${inviteLink}
+OPTIONAL: Join our Discord
+Weβll be using Discord server as our main space for announcements and support during the event. Joining is totally optional for judges, but itβs a great way to:
+πΉ Get quick answers from the team
+πΉ Stay in the loop on event updates
+πΉ Connect with other judges & participants
+π Discord Server: ${DISCORD_SERVER_URL}
+Lastly, we are grateful for your thoughtful feedback during the orientation. As suggested, we will be sharing more details soon about the prize tracks and their eligibility criteria and rubrics to help you get a sense of the tracks ahead of time.
+Please feel free to reach out if you have any questions or concerns. Looking forward to seeing you at the event!
+Thank you,
+The HackDavis Team
+Hi ${fname},
+We are thrilled to welcome you as a mentor at HackDavis 2026! We're excited to have your expertise help our hackers bring their ideas to life.
+Here's what we need from you:
+If the button doesn't work, copy and paste this link into your browser:
+ +After claiming your ticket, you will receive a unique QR code for check-in at the event.
+See you at HackDavis! β¨
+The HackDavis Team
+