import { describe, it, expect, vi, beforeEach } from 'vitest';
import { inbox } from '../../src/tools/inbox.js';
import type { RegistryApiClient } from '../../src/client/api.js';
import { testMessage } from '../fixtures/keys.js';
describe('tools/inbox', () => {
let mockClient: RegistryApiClient;
beforeEach(() => {
mockClient = {
getInbox: vi.fn(),
markAsRead: vi.fn(),
} as unknown as RegistryApiClient;
});
it('should fetch all messages', async () => {
vi.mocked(mockClient.getInbox).mockResolvedValue({
messages: [testMessage],
total: 1,
unread: 1,
});
const result = await inbox({}, mockClient);
expect(mockClient.getInbox).toHaveBeenCalledWith({
unreadOnly: undefined,
threadId: undefined,
limit: undefined,
offset: undefined,
});
expect(result.success).toBe(true);
expect(result.messages).toHaveLength(1);
expect(result.messages![0]!.body).toBe(testMessage.body);
expect(result.total).toBe(1);
expect(result.unread).toBe(1);
});
it('should filter by unread only', async () => {
vi.mocked(mockClient.getInbox).mockResolvedValue({
messages: [testMessage],
total: 1,
unread: 1,
});
await inbox({ unreadOnly: true }, mockClient);
expect(mockClient.getInbox).toHaveBeenCalledWith(
expect.objectContaining({ unreadOnly: true })
);
});
it('should filter by thread ID', async () => {
vi.mocked(mockClient.getInbox).mockResolvedValue({
messages: [testMessage],
total: 1,
unread: 1,
});
await inbox({ threadId: testMessage.thread_id! }, mockClient);
expect(mockClient.getInbox).toHaveBeenCalledWith(
expect.objectContaining({ threadId: testMessage.thread_id })
);
});
it('should support pagination', async () => {
vi.mocked(mockClient.getInbox).mockResolvedValue({
messages: [],
total: 50,
unread: 10,
});
await inbox({ limit: 10, offset: 20 }, mockClient);
expect(mockClient.getInbox).toHaveBeenCalledWith(
expect.objectContaining({ limit: 10, offset: 20 })
);
});
it('should mark messages as read when requested', async () => {
vi.mocked(mockClient.getInbox).mockResolvedValue({
messages: [testMessage],
total: 1,
unread: 1,
});
vi.mocked(mockClient.markAsRead).mockResolvedValue();
await inbox({ markAsRead: true }, mockClient);
expect(mockClient.markAsRead).toHaveBeenCalledWith(testMessage.id);
});
it('should not mark already-read messages', async () => {
const readMessage = { ...testMessage, read_at: '2024-01-01T00:00:00Z' };
vi.mocked(mockClient.getInbox).mockResolvedValue({
messages: [readMessage],
total: 1,
unread: 0,
});
await inbox({ markAsRead: true }, mockClient);
expect(mockClient.markAsRead).not.toHaveBeenCalled();
});
it('should handle empty inbox', async () => {
vi.mocked(mockClient.getInbox).mockResolvedValue({
messages: [],
total: 0,
unread: 0,
});
const result = await inbox({}, mockClient);
expect(result.success).toBe(true);
expect(result.messages).toHaveLength(0);
expect(result.total).toBe(0);
expect(result.unread).toBe(0);
});
it('should handle fetch failure', async () => {
vi.mocked(mockClient.getInbox).mockRejectedValue(new Error('Unauthorized'));
const result = await inbox({}, mockClient);
expect(result.success).toBe(false);
expect(result.error).toContain('Unauthorized');
});
it('should transform message format correctly', async () => {
vi.mocked(mockClient.getInbox).mockResolvedValue({
messages: [testMessage],
total: 1,
unread: 1,
});
const result = await inbox({}, mockClient);
const msg = result.messages![0]!;
expect(msg.id).toBe(testMessage.id);
expect(msg.threadId).toBe(testMessage.thread_id);
expect(msg.from.agentId).toBe(testMessage.from_agent_id);
expect(msg.from.origin).toBe(testMessage.from_origin);
expect(msg.subject).toBe(testMessage.subject);
expect(msg.body).toBe(testMessage.body);
expect(msg.signature).toBe(testMessage.signature);
expect(msg.createdAt).toBe(testMessage.created_at);
expect(msg.readAt).toBe(testMessage.read_at);
});
});