/ tests / test_mcp_tools_meta_ads_other.py
test_mcp_tools_meta_ads_other.py
  1  """Meta Ads その他ハンドラーテスト
  2  
  3  ページ投稿、Instagram、スプリットテスト、自動ルールハンドラーをカバーする。
  4  """
  5  
  6  from __future__ import annotations
  7  
  8  import json
  9  from unittest.mock import AsyncMock, MagicMock, patch
 10  
 11  import pytest
 12  
 13  
 14  def _import_meta_ads_tools():
 15      from mureo.mcp import tools_meta_ads
 16  
 17      return tools_meta_ads
 18  
 19  
 20  def _import_handlers():
 21      from mureo.mcp import _handlers_meta_ads
 22  
 23      return _handlers_meta_ads
 24  
 25  
 26  # ---------------------------------------------------------------------------
 27  # ヘルパー
 28  # ---------------------------------------------------------------------------
 29  
 30  
 31  def _mock_meta_ads_context():
 32      """Meta Ads認証情報とクライアントのモックを返す"""
 33      mock_client = AsyncMock()
 34      mock_creds = MagicMock()
 35      return mock_creds, mock_client
 36  
 37  
 38  # ---------------------------------------------------------------------------
 39  # ページ投稿ハンドラー
 40  # ---------------------------------------------------------------------------
 41  
 42  
 43  @pytest.mark.unit
 44  class TestPagePostsHandlers:
 45      """ページ投稿系ハンドラーテスト"""
 46  
 47      async def test_page_posts_list(self) -> None:
 48          mod = _import_meta_ads_tools()
 49          handlers = _import_handlers()
 50          creds, client = _mock_meta_ads_context()
 51          client.list_page_posts.return_value = [{"id": "post_1"}]
 52  
 53          with (
 54              patch.object(handlers, "load_meta_ads_credentials", return_value=creds),
 55              patch.object(handlers, "create_meta_ads_client", return_value=client),
 56          ):
 57              result = await mod.handle_tool(
 58                  "meta_ads.page_posts.list",
 59                  {"account_id": "act_123", "page_id": "pg_1"},
 60              )
 61  
 62          client.list_page_posts.assert_awaited_once()
 63          parsed = json.loads(result[0].text)
 64          assert parsed[0]["id"] == "post_1"
 65  
 66      async def test_page_posts_boost(self) -> None:
 67          mod = _import_meta_ads_tools()
 68          handlers = _import_handlers()
 69          creds, client = _mock_meta_ads_context()
 70          client.boost_post.return_value = {"id": "boosted_1"}
 71  
 72          with (
 73              patch.object(handlers, "load_meta_ads_credentials", return_value=creds),
 74              patch.object(handlers, "create_meta_ads_client", return_value=client),
 75          ):
 76              result = await mod.handle_tool(
 77                  "meta_ads.page_posts.boost",
 78                  {
 79                      "account_id": "act_123",
 80                      "page_id": "pg_1",
 81                      "post_id": "post_1",
 82                      "ad_set_id": "20",
 83                  },
 84              )
 85  
 86          client.boost_post.assert_awaited_once()
 87          parsed = json.loads(result[0].text)
 88          assert parsed["id"] == "boosted_1"
 89  
 90  
 91  # ---------------------------------------------------------------------------
 92  # Instagramハンドラー
 93  # ---------------------------------------------------------------------------
 94  
 95  
 96  @pytest.mark.unit
 97  class TestInstagramHandlers:
 98      """Instagram系ハンドラーテスト"""
 99  
100      async def test_instagram_accounts(self) -> None:
101          mod = _import_meta_ads_tools()
102          handlers = _import_handlers()
103          creds, client = _mock_meta_ads_context()
104          client.list_instagram_accounts.return_value = [{"id": "ig_1"}]
105  
106          with (
107              patch.object(handlers, "load_meta_ads_credentials", return_value=creds),
108              patch.object(handlers, "create_meta_ads_client", return_value=client),
109          ):
110              result = await mod.handle_tool(
111                  "meta_ads.instagram.accounts",
112                  {"account_id": "act_123"},
113              )
114  
115          client.list_instagram_accounts.assert_awaited_once()
116          parsed = json.loads(result[0].text)
117          assert parsed[0]["id"] == "ig_1"
118  
119      async def test_instagram_media(self) -> None:
120          mod = _import_meta_ads_tools()
121          handlers = _import_handlers()
122          creds, client = _mock_meta_ads_context()
123          client.list_instagram_media.return_value = [{"id": "media_1"}]
124  
125          with (
126              patch.object(handlers, "load_meta_ads_credentials", return_value=creds),
127              patch.object(handlers, "create_meta_ads_client", return_value=client),
128          ):
129              result = await mod.handle_tool(
130                  "meta_ads.instagram.media",
131                  {"account_id": "act_123", "ig_user_id": "ig_1"},
132              )
133  
134          client.list_instagram_media.assert_awaited_once()
135          parsed = json.loads(result[0].text)
136          assert parsed[0]["id"] == "media_1"
137  
138      async def test_instagram_boost(self) -> None:
139          mod = _import_meta_ads_tools()
140          handlers = _import_handlers()
141          creds, client = _mock_meta_ads_context()
142          client.boost_instagram_post.return_value = {"id": "boosted_ig_1"}
143  
144          with (
145              patch.object(handlers, "load_meta_ads_credentials", return_value=creds),
146              patch.object(handlers, "create_meta_ads_client", return_value=client),
147          ):
148              result = await mod.handle_tool(
149                  "meta_ads.instagram.boost",
150                  {
151                      "account_id": "act_123",
152                      "ig_user_id": "ig_1",
153                      "media_id": "media_1",
154                      "ad_set_id": "20",
155                  },
156              )
157  
158          client.boost_instagram_post.assert_awaited_once()
159          parsed = json.loads(result[0].text)
160          assert parsed["id"] == "boosted_ig_1"
161  
162  
163  # ---------------------------------------------------------------------------
164  # Split Test (A/Bテスト) ハンドラー
165  # ---------------------------------------------------------------------------
166  
167  
168  @pytest.mark.unit
169  class TestSplitTestHandlers:
170      """スプリットテスト系ハンドラーテスト"""
171  
172      async def test_split_tests_list(self) -> None:
173          mod = _import_meta_ads_tools()
174          handlers = _import_handlers()
175          creds, client = _mock_meta_ads_context()
176          client.list_split_tests.return_value = [{"id": "st_1"}]
177  
178          with (
179              patch.object(handlers, "load_meta_ads_credentials", return_value=creds),
180              patch.object(handlers, "create_meta_ads_client", return_value=client),
181          ):
182              result = await mod.handle_tool(
183                  "meta_ads.split_tests.list",
184                  {"account_id": "act_123"},
185              )
186  
187          client.list_split_tests.assert_awaited_once()
188          parsed = json.loads(result[0].text)
189          assert parsed[0]["id"] == "st_1"
190  
191      async def test_split_tests_get(self) -> None:
192          mod = _import_meta_ads_tools()
193          handlers = _import_handlers()
194          creds, client = _mock_meta_ads_context()
195          client.get_split_test.return_value = {"id": "st_1", "name": "Test1"}
196  
197          with (
198              patch.object(handlers, "load_meta_ads_credentials", return_value=creds),
199              patch.object(handlers, "create_meta_ads_client", return_value=client),
200          ):
201              result = await mod.handle_tool(
202                  "meta_ads.split_tests.get",
203                  {"account_id": "act_123", "study_id": "st_1"},
204              )
205  
206          client.get_split_test.assert_awaited_once_with("st_1")
207          parsed = json.loads(result[0].text)
208          assert parsed["id"] == "st_1"
209  
210      async def test_split_tests_create(self) -> None:
211          mod = _import_meta_ads_tools()
212          handlers = _import_handlers()
213          creds, client = _mock_meta_ads_context()
214          client.create_split_test.return_value = {"id": "st_2"}
215  
216          with (
217              patch.object(handlers, "load_meta_ads_credentials", return_value=creds),
218              patch.object(handlers, "create_meta_ads_client", return_value=client),
219          ):
220              result = await mod.handle_tool(
221                  "meta_ads.split_tests.create",
222                  {
223                      "account_id": "act_123",
224                      "name": "ABTest",
225                      "cells": [{"cell_id": "c1"}],
226                      "objectives": ["REACH"],
227                      "start_time": "2026-04-01T00:00:00",
228                      "end_time": "2026-04-30T00:00:00",
229                  },
230              )
231  
232          client.create_split_test.assert_awaited_once()
233          parsed = json.loads(result[0].text)
234          assert parsed["id"] == "st_2"
235  
236      async def test_split_tests_end(self) -> None:
237          mod = _import_meta_ads_tools()
238          handlers = _import_handlers()
239          creds, client = _mock_meta_ads_context()
240          client.end_split_test.return_value = {"success": True}
241  
242          with (
243              patch.object(handlers, "load_meta_ads_credentials", return_value=creds),
244              patch.object(handlers, "create_meta_ads_client", return_value=client),
245          ):
246              result = await mod.handle_tool(
247                  "meta_ads.split_tests.end",
248                  {"account_id": "act_123", "study_id": "st_1"},
249              )
250  
251          client.end_split_test.assert_awaited_once_with("st_1")
252          parsed = json.loads(result[0].text)
253          assert parsed["success"] is True
254  
255  
256  # ---------------------------------------------------------------------------
257  # Ad Rules (自動ルール) ハンドラー
258  # ---------------------------------------------------------------------------
259  
260  
261  @pytest.mark.unit
262  class TestAdRulesHandlers:
263      """自動ルール系ハンドラーテスト"""
264  
265      async def test_ad_rules_list(self) -> None:
266          mod = _import_meta_ads_tools()
267          handlers = _import_handlers()
268          creds, client = _mock_meta_ads_context()
269          client.list_ad_rules.return_value = [{"id": "rule_1"}]
270  
271          with (
272              patch.object(handlers, "load_meta_ads_credentials", return_value=creds),
273              patch.object(handlers, "create_meta_ads_client", return_value=client),
274          ):
275              result = await mod.handle_tool(
276                  "meta_ads.ad_rules.list",
277                  {"account_id": "act_123"},
278              )
279  
280          client.list_ad_rules.assert_awaited_once()
281          parsed = json.loads(result[0].text)
282          assert parsed[0]["id"] == "rule_1"
283  
284      async def test_ad_rules_get(self) -> None:
285          mod = _import_meta_ads_tools()
286          handlers = _import_handlers()
287          creds, client = _mock_meta_ads_context()
288          client.get_ad_rule.return_value = {"id": "rule_1", "name": "Rule1"}
289  
290          with (
291              patch.object(handlers, "load_meta_ads_credentials", return_value=creds),
292              patch.object(handlers, "create_meta_ads_client", return_value=client),
293          ):
294              result = await mod.handle_tool(
295                  "meta_ads.ad_rules.get",
296                  {"account_id": "act_123", "rule_id": "rule_1"},
297              )
298  
299          client.get_ad_rule.assert_awaited_once_with("rule_1")
300          parsed = json.loads(result[0].text)
301          assert parsed["id"] == "rule_1"
302  
303      async def test_ad_rules_create(self) -> None:
304          mod = _import_meta_ads_tools()
305          handlers = _import_handlers()
306          creds, client = _mock_meta_ads_context()
307          client.create_ad_rule.return_value = {"id": "rule_2"}
308  
309          with (
310              patch.object(handlers, "load_meta_ads_credentials", return_value=creds),
311              patch.object(handlers, "create_meta_ads_client", return_value=client),
312          ):
313              result = await mod.handle_tool(
314                  "meta_ads.ad_rules.create",
315                  {
316                      "account_id": "act_123",
317                      "name": "AutoRule",
318                      "evaluation_spec": {"field": "impressions"},
319                      "execution_spec": {"type": "PAUSE"},
320                  },
321              )
322  
323          client.create_ad_rule.assert_awaited_once()
324          parsed = json.loads(result[0].text)
325          assert parsed["id"] == "rule_2"
326  
327      async def test_ad_rules_update(self) -> None:
328          mod = _import_meta_ads_tools()
329          handlers = _import_handlers()
330          creds, client = _mock_meta_ads_context()
331          client.update_ad_rule.return_value = {"success": True}
332  
333          with (
334              patch.object(handlers, "load_meta_ads_credentials", return_value=creds),
335              patch.object(handlers, "create_meta_ads_client", return_value=client),
336          ):
337              result = await mod.handle_tool(
338                  "meta_ads.ad_rules.update",
339                  {
340                      "account_id": "act_123",
341                      "rule_id": "rule_1",
342                      "name": "Updated Rule",
343                  },
344              )
345  
346          client.update_ad_rule.assert_awaited_once()
347          parsed = json.loads(result[0].text)
348          assert parsed["success"] is True
349  
350      async def test_ad_rules_delete(self) -> None:
351          mod = _import_meta_ads_tools()
352          handlers = _import_handlers()
353          creds, client = _mock_meta_ads_context()
354          client.delete_ad_rule.return_value = {"success": True}
355  
356          with (
357              patch.object(handlers, "load_meta_ads_credentials", return_value=creds),
358              patch.object(handlers, "create_meta_ads_client", return_value=client),
359          ):
360              result = await mod.handle_tool(
361                  "meta_ads.ad_rules.delete",
362                  {"account_id": "act_123", "rule_id": "rule_1"},
363              )
364  
365          client.delete_ad_rule.assert_awaited_once_with("rule_1")
366          parsed = json.loads(result[0].text)
367          assert parsed["success"] is True