mirror of
https://github.com/Sea-Haven-Industries/sh-mcp.git
synced 2026-09-30 07:43:17 +00:00
98 lines
3.2 KiB
TypeScript
98 lines
3.2 KiB
TypeScript
|
|
import { describe, it, expect } from 'vitest';
|
||
|
|
import { InMemoryGmailClient } from '../src/dev-client.js';
|
||
|
|
|
||
|
|
describe('InMemoryGmailClient', () => {
|
||
|
|
describe('searchInbox', () => {
|
||
|
|
it('matches a case-insensitive substring against subject/snippet', async () => {
|
||
|
|
const client = new InMemoryGmailClient();
|
||
|
|
const results = await client.searchInbox({
|
||
|
|
userSub: 'lauren@seahavenind.com',
|
||
|
|
query: 'INVOICE',
|
||
|
|
maxResults: 10,
|
||
|
|
});
|
||
|
|
expect(results.map((m) => m.id)).toEqual(['msg-l-1']);
|
||
|
|
});
|
||
|
|
|
||
|
|
it('matches against the snippet body too', async () => {
|
||
|
|
const client = new InMemoryGmailClient();
|
||
|
|
const results = await client.searchInbox({
|
||
|
|
userSub: 'lauren@seahavenind.com',
|
||
|
|
query: 'walkthrough',
|
||
|
|
maxResults: 10,
|
||
|
|
});
|
||
|
|
expect(results.map((m) => m.id)).toEqual(['msg-l-2']);
|
||
|
|
});
|
||
|
|
|
||
|
|
it('caps results at maxResults', async () => {
|
||
|
|
const client = new InMemoryGmailClient();
|
||
|
|
const results = await client.searchInbox({
|
||
|
|
userSub: 'lauren@seahavenind.com',
|
||
|
|
query: '',
|
||
|
|
maxResults: 1,
|
||
|
|
});
|
||
|
|
expect(results).toHaveLength(1);
|
||
|
|
});
|
||
|
|
|
||
|
|
it("is partitioned by userSub — lauren cannot see adam's mail", async () => {
|
||
|
|
const client = new InMemoryGmailClient();
|
||
|
|
const results = await client.searchInbox({
|
||
|
|
userSub: 'lauren@seahavenind.com',
|
||
|
|
query: 'check #2087',
|
||
|
|
maxResults: 10,
|
||
|
|
});
|
||
|
|
expect(results).toEqual([]);
|
||
|
|
|
||
|
|
const adamResults = await client.searchInbox({
|
||
|
|
userSub: 'adam@seahavenind.com',
|
||
|
|
query: 'check #2087',
|
||
|
|
maxResults: 10,
|
||
|
|
});
|
||
|
|
expect(adamResults.map((m) => m.id)).toEqual(['msg-a-1']);
|
||
|
|
});
|
||
|
|
|
||
|
|
it('returns an empty list for an unknown sub', async () => {
|
||
|
|
const client = new InMemoryGmailClient();
|
||
|
|
const results = await client.searchInbox({
|
||
|
|
userSub: 'nobody@example.com',
|
||
|
|
query: 'invoice',
|
||
|
|
maxResults: 10,
|
||
|
|
});
|
||
|
|
expect(results).toEqual([]);
|
||
|
|
});
|
||
|
|
});
|
||
|
|
|
||
|
|
describe('getThreadDetail', () => {
|
||
|
|
it('returns an owned thread', async () => {
|
||
|
|
const client = new InMemoryGmailClient();
|
||
|
|
const thread = await client.getThreadDetail({
|
||
|
|
userSub: 'lauren@seahavenind.com',
|
||
|
|
threadId: 'thr-l-1',
|
||
|
|
});
|
||
|
|
expect(thread.threadId).toBe('thr-l-1');
|
||
|
|
expect(thread.subject).toBe('Invoice #4821 from Coastal Supply');
|
||
|
|
expect(thread.messages).toHaveLength(1);
|
||
|
|
});
|
||
|
|
|
||
|
|
it('throws for an unknown threadId', async () => {
|
||
|
|
const client = new InMemoryGmailClient();
|
||
|
|
await expect(
|
||
|
|
client.getThreadDetail({ userSub: 'lauren@seahavenind.com', threadId: 'thr-missing' }),
|
||
|
|
).rejects.toThrow(/not found/);
|
||
|
|
});
|
||
|
|
|
||
|
|
it('throws when the thread is not owned by the caller', async () => {
|
||
|
|
const client = new InMemoryGmailClient();
|
||
|
|
await expect(
|
||
|
|
client.getThreadDetail({ userSub: 'lauren@seahavenind.com', threadId: 'thr-a-1' }),
|
||
|
|
).rejects.toThrow(/not found/);
|
||
|
|
});
|
||
|
|
|
||
|
|
it('throws for an unknown user', async () => {
|
||
|
|
const client = new InMemoryGmailClient();
|
||
|
|
await expect(
|
||
|
|
client.getThreadDetail({ userSub: 'nobody@example.com', threadId: 'thr-l-1' }),
|
||
|
|
).rejects.toThrow(/not found/);
|
||
|
|
});
|
||
|
|
});
|
||
|
|
});
|