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