mcp-server.test.ts•4 kB
import { describe, test, expect, beforeAll, afterAll } from "bun:test";
import { server } from "./mcp-server";
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
import { InMemoryTransport } from "@modelcontextprotocol/sdk/inMemory.js";
import rssManager from "./rss-manager";
// 型定義
interface FeedItem {
  title: string;
  summary: string;
  published: string;
  origin: string;
  link: string;
  categories: string[];
}
interface FeedResponse {
  items: FeedItem[];
}
interface Feed {
  id: string;
  title: string;
  url: string;
}
// モックの設定
import { mock } from "bun:test";
mock.module("./rss-manager", () => {
  return {
    default: {
      crawlFeed: async (limit: number) => ({
        items: [
          {
            title: "テスト記事1",
            summary: "テスト記事1の内容",
            published: "2025-03-16T08:00:00.000Z",
            origin: "テストブログ",
            link: "https://example.com/article1",
            categories: ["テスト", "ブログ"]
          },
          {
            title: "テスト記事2",
            summary: "テスト記事2の内容",
            published: "2025-03-16T07:00:00.000Z",
            origin: "テストブログ",
            link: "https://example.com/article2",
            categories: ["テスト", "ニュース"]
          }
        ]
      }),
      getFeeds: () => [
        {
          id: "feed1",
          title: "テストブログ",
          url: "https://example.com/feed"
        }
      ]
    }
  };
});
describe("MCP Server", () => {
  let client: Client;
  let transport: InMemoryTransport;
  beforeAll(async () => {
    // インメモリ通信チャネルの作成
    const [clientTp, serverTp] = InMemoryTransport.createLinkedPair();
    
    // サーバーをトランスポートに接続
    await server.connect(serverTp);
    
    // クライアントを作成
    client = new Client({
      name: "test client",
      version: "0.1.0",
    });
    
    // クライアントを接続
    await client.connect(clientTp);
    
    // トランスポートを保存
    transport = clientTp;
  });
  afterAll(async () => {
    // テスト終了後にトランスポートを閉じる
    await transport.close();
  });
  test("fetchRssFeeds tool should return feed items", async () => {
    // fetchRssFeedsツールを呼び出す
    const response = await client.callTool({
      name: "fetchRssFeeds",
      arguments: { limit: 10 }
    });
    
    // レスポンスをキャスト
    const feedResponse = response as unknown as FeedResponse;
    
    // レスポンスが期待通りの形式であることを確認
    expect(feedResponse).toBeDefined();
    expect(Array.isArray(feedResponse.items)).toBe(true);
    expect(feedResponse.items.length).toBeGreaterThan(0);
    
    // 最初の記事の内容を確認
    const firstItem = feedResponse.items[0];
    expect(firstItem.title).toBe("テスト記事1");
    expect(firstItem.summary).toBe("テスト記事1の内容");
    expect(firstItem.origin).toBe("テストブログ");
    expect(firstItem.link).toBe("https://example.com/article1");
    expect(Array.isArray(firstItem.categories)).toBe(true);
  });
  test("listRssFeeds tool should return available feeds", async () => {
    // listRssFeedsツールを呼び出す
    const response = await client.callTool({
      name: "listRssFeeds",
      arguments: {}
    });
    
    // レスポンスをキャスト
    const feeds = response as unknown as Feed[];
    
    // レスポンスが期待通りの形式であることを確認
    expect(feeds).toBeDefined();
    expect(Array.isArray(feeds)).toBe(true);
    expect(feeds.length).toBeGreaterThan(0);
    
    // 最初のフィードの内容を確認
    const firstFeed = feeds[0];
    expect(firstFeed.id).toBe("feed1");
    expect(firstFeed.title).toBe("テストブログ");
    expect(firstFeed.url).toBe("https://example.com/feed");
  });
});