tests — email
tests — email
This document provides developer-focused documentation for the core email module, located at src/email/index.js and its related files. While the request specifically mentions tests/email/email.test.ts, this test file serves as the most accurate and comprehensive specification for the public API and expected behavior of the src/email module. Therefore, this documentation describes the src/email module's functionality, using the tests as a guide to its design and usage.
Email Module Overview
The src/email module provides a robust set of tools for interacting with email services. It encapsulates functionalities ranging from basic email address parsing to full-fledged IMAP and SMTP client implementations, and a higher-level EmailService that integrates these components with webhook capabilities and operational statistics.
Its primary goals are:
- To provide low-level clients for IMAP and SMTP protocols.
- To offer a unified, high-level service for common email operations (send, receive, manage).
- To support event-driven integrations via webhooks.
- To manage email account state and provide operational statistics.
Key Components and Functionality
The module is composed of several distinct classes and utility functions, each serving a specific purpose.
1. Email Utilities
These are standalone functions for common email-related tasks.
parseEmailAddress(address: string | { name?: string; address: string }): { name?: string; address: string }
Parses a string email address into an object with name and address properties. It can also handle already-parsed objects, returning them as-is.
Examples from tests:
parseEmailAddress(39;test@example.com39;); class="hl-cmt">// Returns { address: 39;test@example.com39; }
parseEmailAddress(39;John Doe <john@example.com>39;); class="hl-cmt">// Returns { name: 39;John Doe39;, address: 39;john@example.com39; }
formatEmailAddress(address: { name?: string; address: string }): string
Formats an email address object back into a string, optionally including the name.
Examples from tests:
formatEmailAddress({ address: 39;test@example.com39; }); class="hl-cmt">// Returns 39;test@example.com39;
formatEmailAddress({ name: 39;John39;, address: 39;john@example.com39; }); class="hl-cmt">// Returns 39;John <john@example.com>39;
generateMessageId(domain?: string): string
Generates a unique message ID suitable for email headers, following RFC 5322. By default, it uses @codebuddy.local as the domain, but a custom domain can be provided.
Examples from tests:
generateMessageId(); class="hl-cmt">// Returns something like 39;<a1b2c3d4e5f6@codebuddy.local>39;
generateMessageId(39;custom.domain39;); class="hl-cmt">// Returns something like 39;<a1b2c3d4e5f6@custom.domain>39;
2. IMAP Client (ImapClient)
The ImapClient class provides a low-level interface for interacting with an IMAP server. It handles connection management, folder operations, and message manipulation.
Constructor:
new ImapClient(config: ImapClientConfig);
ImapClientConfig includes host, port, secure, user, and password.
Key Methods:
- Connection Management:
connect(): Promise: Establishes a connection to the IMAP server.disconnect(): Promise: Closes the connection.isConnected(): boolean: Checks if the client is currently connected.- Events: Emits
connectedanddisconnectedevents.
- Folder Management:
listFolders(): Promise: Retrieves a list of all folders.selectFolder(folderName: string): Promise: Selects a specific folder for operations.getSelectedFolder(): string | null: Returns the name of the currently selected folder.createFolder(folderName: string): Promise: Creates a new folder.deleteFolder(folderName: string): Promise: Deletes an existing folder.
- Message Management:
search(criteria: SearchCriteria): Promise: Searches the currently selected folder for messages matching the criteria and returns their UIDs.SearchCriteriasupportsall,unseen,subject,from, etc.fetch(uids: string[]): Promise: Fetches full message details for given UIDs.fetchOne(uid: string): Promise: Fetches a single message by UID.addFlags(uid: string, flags: string | string[]): Promise: Adds flags (e.g., 'seen') to a message.removeFlags(uid: string, flags: string | string[]): Promise: Removes flags from a message.move(uid: string, destinationFolder: string): Promise: Moves a message to another folder.copy(uid: string, destinationFolder: string): Promise: Copies a message to another folder.delete(uid: string): Promise: Marks a message for deletion (typically moves to Trash).
- Mocking (for testing/development):
addMockMessage(folder: string, message: Partial: Adds a mock message to a specified folder, returning its UID. Used extensively in tests.): string
3. SMTP Client (SmtpClient)
The SmtpClient class provides a low-level interface for sending emails via an SMTP server.
Constructor:
new SmtpClient(config: SmtpClientConfig);
SmtpClientConfig includes host, port, secure, user, and password.
Key Methods:
- Connection Management:
connect(): Promise: Establishes a connection to the SMTP server.disconnect(): Promise: Closes the connection.isConnected(): boolean: Checks if the client is currently connected.
- Sending Emails:
send(mailOptions: MailOptions): Promise: Sends an email.MailOptionsincludesfrom,to,cc,bcc,subject,text,html,attachments, etc.SendResultcontainsmessageId,accepted,rejected, etc.- Events: Emits a
sentevent upon successful email transmission.
4. Webhook Manager (WebhookManager)
The WebhookManager handles the registration, removal, and triggering of webhooks based on defined events.
Constructor:
new WebhookManager();
Key Methods:
addWebhook(webhook: WebhookConfig): void: Registers a new webhook.WebhookConfigincludesurlandevents(an array of event names).removeWebhook(url: string): void: Removes a webhook by its URL.getWebhooks(): WebhookConfig[]: Returns all currently registered webhooks.trigger(event: string, payload: any): Promise: Triggers all webhooks subscribed to the giveneventwith the providedpayload.- Events: Emits
webhook-sentwhen a webhook is successfully triggered.
5. Email Service (EmailService)
The EmailService is the central orchestrator, integrating ImapClient, SmtpClient, and WebhookManager to provide a comprehensive email management solution. It also maintains operational statistics.
Constructor:
new EmailService(config: EmailServiceConfig);
EmailServiceConfig can include imap and smtp client configurations.
Key Methods:
- Lifecycle & Connection:
connect(): Promise: Connects both IMAP and SMTP clients (if configured).disconnect(): Promise: Disconnects both clients.isConnected(): boolean: Reports if the service is connected (at least IMAP or SMTP).- Events: Emits
connectedanddisconnectedevents.
- IMAP Operations (delegated to
ImapClient): listFolders(): PromiseselectFolder(folderName: string): Promisesearch(criteria: SearchCriteria): PromisefetchMessages(uids: string[], folder?: string): PromisefetchMessage(uid: string, folder?: string): PromisemarkAsRead(uid: string, folder?: string): PromiseaddMockMessage(folder: string, message: Partial: For testing/development.): string
- SMTP Operations (delegated to
SmtpClient): sendEmail(mailOptions: MailOptions): Promise
- Webhook Integration (delegated to
WebhookManager): addWebhook(webhook: WebhookConfig): voidremoveWebhook(url: string): voidgetWebhooks(): WebhookConfig[]- Events: Re-emits
webhook-sentevents from theWebhookManager.
- Synchronization:
syncFolder(folderName: string): Promise: Connects to the IMAP server, selects the specified folder, fetches new messages, and triggersmessage.receivedwebhooks. Returns the count of new messages.
- Statistics:
getStats(): EmailServiceStats: Returns an object containing operational statistics likeconnected,messagesReceived,messagesSent,errors,uptime,lastSync.resetStats(): void: Resets all collected statistics.
6. Singleton Access
The EmailService can be accessed as a singleton, ensuring only one instance exists application-wide, especially useful for managing a single email account's connection and state.
- ####
getEmailService(config?: EmailServiceConfig): EmailService
Retrieves the singleton instance of EmailService. If an instance doesn't exist, it creates one using the provided config. If an instance already exists, it returns the existing one, ignoring any new config.
- ####
resetEmailService(): void
Resets the singleton instance, allowing a new EmailService to be created with different configurations on the next call to getEmailService. This is primarily used in testing to ensure isolation between test suites.
Architectural Overview
The EmailService acts as a facade, orchestrating the underlying clients and managers.
graph TD
A[EmailService] --> B(ImapClient)
A --> C(SmtpClient)
A --> D(WebhookManager)
EmailServicehandles the overall lifecycle, connection state, and statistics.- It delegates IMAP-specific tasks to
ImapClient. - It delegates SMTP-specific tasks to
SmtpClient. - It integrates
WebhookManagerto dispatch events based on email activities (e.g.,message.received,message.sent).
How to Contribute
When contributing to the src/email module, consider the following:
- Understand the Separation of Concerns:
ImapClientandSmtpClientare low-level protocol clients. Changes here should focus on protocol-specific details or adding new IMAP/SMTP commands.WebhookManageris purely for webhook management and triggering.EmailServiceis the integration layer. New features that combine IMAP/SMTP/Webhooks or manage overall state should go here.
- Leverage Existing Tests: The
tests/email/email.test.tsfile is comprehensive.
- Adding New Features: Always start by writing new tests that define the expected behavior of your feature.
- Modifying Existing Features: Ensure existing tests still pass, and add new tests if the behavior changes or new edge cases are handled.
- The
ImapClientandSmtpClienthave mock implementations that are heavily used in tests. Familiarize yourself withaddMockMessageand how client events are tested.
- Eventing: Both
ImapClient,SmtpClient, andWebhookManageremit events.EmailServicecan listen to these and re-emit or trigger further actions (like webhooks). When adding new asynchronous operations, consider if new events should be emitted to inform higher-level components.
- Error Handling: Ensure robust error handling, especially for network operations in
ImapClientandSmtpClient. Propagate meaningful errors up to theEmailService.
- Singleton Pattern: Be mindful of the
getEmailServiceandresetEmailServicefunctions. If you need to manage multiple email accounts concurrently, the current singleton pattern forEmailServicemight need re-evaluation or a different approach (e.g., a factory that returns named instances). For single-account scenarios, it works well.
By following these guidelines and using the existing test suite as a blueprint, you can effectively understand, extend, and maintain the email module.