Skip to content
Open
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
98 changes: 98 additions & 0 deletions workspace-server/src/__tests__/services/GmailService.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -250,6 +250,104 @@ describe('GmailService', () => {
expect(response.attachments).toEqual([]);
});

it('should extract Cc, Bcc and Reply-To headers when present', async () => {
const mockMessage = {
id: 'msg1',
threadId: 'thread1',
payload: {
headers: [
{ name: 'From', value: 'sender@example.com' },
{ name: 'To', value: 'recipient@example.com' },
{ name: 'Cc', value: 'cc-recipient@example.com' },
{ name: 'Bcc', value: 'archive@example.com' },
{ name: 'Reply-To', value: 'replies@example.com' },
{ name: 'Subject', value: 'Test Email' },
],
body: {
data: 'SGVsbG8gV29ybGQh', // Base64 for "Hello World!"
},
},
};

mockGmailAPI.users.messages.get.mockResolvedValue({
data: mockMessage,
});

const result = await gmailService.get({
messageId: 'msg1',
format: 'full',
});

const response = JSON.parse(result.content[0].text);
expect(response.cc).toBe('cc-recipient@example.com');
expect(response.bcc).toBe('archive@example.com');
expect(response.replyTo).toBe('replies@example.com');
});

it('should omit Cc, Bcc and Reply-To when absent', async () => {
const mockMessage = {
id: 'msg1',
threadId: 'thread1',
payload: {
headers: [
{ name: 'From', value: 'sender@example.com' },
{ name: 'To', value: 'recipient@example.com' },
{ name: 'Subject', value: 'Test Email' },
],
body: {
data: 'SGVsbG8gV29ybGQh', // Base64 for "Hello World!"
},
},
};

mockGmailAPI.users.messages.get.mockResolvedValue({
data: mockMessage,
});

const result = await gmailService.get({
messageId: 'msg1',
format: 'full',
});

const response = JSON.parse(result.content[0].text);
expect(response).not.toHaveProperty('cc');
expect(response).not.toHaveProperty('bcc');
expect(response).not.toHaveProperty('replyTo');
});

it('should match header names case-insensitively', async () => {
const mockMessage = {
id: 'msg1',
threadId: 'thread1',
payload: {
headers: [
{ name: 'from', value: 'sender@example.com' },
{ name: 'TO', value: 'recipient@example.com' },
{ name: 'CC', value: 'cc-recipient@example.com' },
{ name: 'subject', value: 'Test Email' },
],
body: {
data: 'SGVsbG8gV29ybGQh', // Base64 for "Hello World!"
},
},
};

mockGmailAPI.users.messages.get.mockResolvedValue({
data: mockMessage,
});

const result = await gmailService.get({
messageId: 'msg1',
format: 'full',
});

const response = JSON.parse(result.content[0].text);
expect(response.from).toBe('sender@example.com');
expect(response.to).toBe('recipient@example.com');
expect(response.cc).toBe('cc-recipient@example.com');
expect(response.subject).toBe('Test Email');
});

it('should extract attachments in full format', async () => {
const mockMessage = {
id: 'msg_with_attach',
Expand Down
16 changes: 14 additions & 2 deletions workspace-server/src/services/GmailService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -198,12 +198,21 @@ export class GmailService {
// Extract useful information based on format
if (format === 'metadata' || format === 'full') {
const headers = message.payload?.headers || [];
const getHeader = (name: string) =>
headers.find((h) => h.name === name)?.value;
// Header names are case-insensitive per RFC 5322.
const headerMap = new Map<string, string | null>();
for (const h of headers) {
if (h.name && h.value !== undefined) {
headerMap.set(h.name.toLowerCase(), h.value);
}
}
Comment thread
mickey-mikey marked this conversation as resolved.
const getHeader = (name: string) => headerMap.get(name.toLowerCase());
Comment thread
mickey-mikey marked this conversation as resolved.
Outdated

const subject = getHeader('Subject');
const from = getHeader('From');
const to = getHeader('To');
const cc = getHeader('Cc');
const bcc = getHeader('Bcc');
const replyTo = getHeader('Reply-To');
const date = getHeader('Date');

// Extract body and attachments for full format
Expand All @@ -228,6 +237,9 @@ export class GmailService {
subject,
from,
to,
cc,
bcc,
replyTo,
date,
body: body || message.snippet,
attachments: attachments,
Expand Down