|
| 1 | +# src/tests/backend/kernel_tools/test_product_tools.py |
| 2 | + |
| 3 | +import sys |
| 4 | +import os |
| 5 | +import types |
| 6 | +import pytest |
| 7 | +import json |
| 8 | +from datetime import datetime |
| 9 | + |
| 10 | +# --- Stub out semantic_kernel.functions --- |
| 11 | +sk_pkg = types.ModuleType("semantic_kernel") |
| 12 | +sk_pkg.__path__ = [] |
| 13 | +sk_funcs = types.ModuleType("semantic_kernel.functions") |
| 14 | +def kernel_function(name=None, description=None): |
| 15 | + def decorator(func): |
| 16 | + # attach a __kernel_function__ marker |
| 17 | + setattr(func, "__kernel_function__", types.SimpleNamespace(name=name or func.__name__, description=description)) |
| 18 | + return func |
| 19 | + return decorator |
| 20 | +sk_funcs.kernel_function = kernel_function |
| 21 | +sys.modules["semantic_kernel"] = sk_pkg |
| 22 | +sys.modules["semantic_kernel.functions"] = sk_funcs |
| 23 | + |
| 24 | +# --- Stub out models.messages_kernel.AgentType --- |
| 25 | +models_pkg = types.ModuleType("models") |
| 26 | +msgs_mod = types.ModuleType("models.messages_kernel") |
| 27 | +from enum import Enum |
| 28 | +class AgentType(Enum): |
| 29 | + PRODUCT = "product_agent" |
| 30 | +msgs_mod.AgentType = AgentType |
| 31 | +models_pkg.messages_kernel = msgs_mod |
| 32 | +sys.modules["models"] = models_pkg |
| 33 | +sys.modules["models.messages_kernel"] = msgs_mod |
| 34 | + |
| 35 | +# --- Ensure src is on PYTHONPATH --- |
| 36 | +ROOT = os.path.abspath(os.path.join(os.path.dirname(__file__), "..", "..")) |
| 37 | +SRC = os.path.join(ROOT, "src") |
| 38 | +if SRC not in sys.path: |
| 39 | + sys.path.insert(0, SRC) |
| 40 | + |
| 41 | +from backend.kernel_tools.product_tools import ProductTools |
| 42 | + |
| 43 | + |
| 44 | +@pytest.mark.asyncio |
| 45 | +async def test_add_mobile_extras_pack(): |
| 46 | + result = await ProductTools.add_mobile_extras_pack("Roaming Plus", "2025-07-01") |
| 47 | + assert "## New Plan" in result |
| 48 | + assert "Roaming Plus" in result |
| 49 | + assert "2025-07-01" in result |
| 50 | + |
| 51 | + |
| 52 | +def test_generate_tools_json_doc(): |
| 53 | + doc = ProductTools.generate_tools_json_doc() |
| 54 | + tools = json.loads(doc) |
| 55 | + assert isinstance(tools, list) |
| 56 | + inv = next((t for t in tools if t["function"] == "check_inventory"), None) |
| 57 | + assert inv is not None |
| 58 | + assert inv["agent"] == ProductTools.agent_name |
| 59 | + assert "product_name" in inv["arguments"] |
| 60 | + |
| 61 | + |
| 62 | +@pytest.mark.asyncio |
| 63 | +async def test_update_and_check_inventory_prints(capsys): |
| 64 | + chk = await ProductTools.check_inventory("GadgetZ") |
| 65 | + upd = await ProductTools.update_inventory("GadgetZ", 10) |
| 66 | + assert chk == "## Inventory Status\nInventory status for **'GadgetZ'** checked." |
| 67 | + assert upd == "## Inventory Update\nInventory for **'GadgetZ'** updated by **10** units." |
| 68 | + captured = capsys.readouterr() |
| 69 | + assert "Inventory status for **'GadgetZ'** checked." in captured.out |
| 70 | + assert "Inventory for **'GadgetZ'** updated by **10** units." in captured.out |
| 71 | + |
| 72 | + |
| 73 | +def test_get_all_kernel_functions_filters_only_decorated(): |
| 74 | + funcs = ProductTools.get_all_kernel_functions() |
| 75 | + # Decorated methods should appear |
| 76 | + assert "add_mobile_extras_pack" in funcs |
| 77 | + assert "get_billing_date" in funcs |
| 78 | + # Introspection helpers should not |
| 79 | + assert "generate_tools_json_doc" not in funcs |
| 80 | + assert "get_all_kernel_functions" not in funcs |
| 81 | + |
| 82 | + |
| 83 | +# ----------------------------------------------------------------------------- |
| 84 | +# Parametrized test to cover all other async methods in ProductTools |
| 85 | +# ----------------------------------------------------------------------------- |
| 86 | + |
| 87 | +@pytest.mark.asyncio |
| 88 | +@pytest.mark.parametrize( |
| 89 | + "method,args,expected_substr", |
| 90 | + [ |
| 91 | + ("get_product_info", [], "Here is information to relay"), |
| 92 | + ("add_new_product", ["NewWidget details"], "## New Product Added"), |
| 93 | + ("update_product_price", ["WidgetX", 19.99], "## Price Update"), |
| 94 | + ("schedule_product_launch", ["WidgetX", "2025-09-01"], "## Product Launch Scheduled"), |
| 95 | + ("analyze_sales_data", ["WidgetX", "Q1"], "## Sales Data Analysis"), |
| 96 | + ("get_customer_feedback", ["WidgetX"], "## Customer Feedback"), |
| 97 | + ("manage_promotions", ["WidgetX", "Promo"], "## Promotion Managed"), |
| 98 | + ("coordinate_with_marketing", ["WidgetX", "Campaign"], "## Marketing Coordination"), |
| 99 | + ("review_product_quality", ["WidgetX"], "## Quality Review"), |
| 100 | + ("handle_product_recall", ["WidgetX", "Defect"], "## Product Recall"), |
| 101 | + ("provide_product_recommendations", ["pref"], "## Product Recommendations"), |
| 102 | + ("generate_product_report", ["WidgetX", "Summary"], "## Summary Report"), |
| 103 | + ("manage_supply_chain", ["WidgetX", "SupCo"], "## Supply Chain Management"), |
| 104 | + ("track_product_shipment", ["WidgetX", "TRACK123"], "## Shipment Tracking"), |
| 105 | + ("set_reorder_level", ["WidgetX", 50], "## Reorder Level Set"), |
| 106 | + ("monitor_market_trends", [], "## Market Trends"), |
| 107 | + ("develop_new_product_ideas", ["Cool idea"], "## New Product Idea"), |
| 108 | + ("collaborate_with_tech_team", ["WidgetX", "Spec"], "## Tech Team Collaboration"), |
| 109 | + ("update_product_description", ["WidgetX", "New desc"], "## Product Description Updated"), |
| 110 | + ("set_product_discount", ["WidgetX", 15.0], "## Discount Set"), |
| 111 | + ("manage_product_returns", ["WidgetX", "Broken"], "## Product Return Managed"), |
| 112 | + ("conduct_product_survey", ["WidgetX", "SurveyQ"], "## Product Survey Conducted"), |
| 113 | + ("handle_product_complaints", ["WidgetX", "Late"], "## Product Complaint Handled"), |
| 114 | + ("update_product_specifications", ["WidgetX", "Specs"], "## Product Specifications Updated"), |
| 115 | + ("organize_product_photoshoot", ["WidgetX", "2025-10-10"], "## Product Photoshoot Organized"), |
| 116 | + ("manage_product_listing", ["WidgetX", "Details"], "## Product Listing Managed"), |
| 117 | + ("set_product_availability", ["WidgetX", True], "now **available**"), |
| 118 | + ("coordinate_with_logistics", ["WidgetX", "Logistics"], "## Logistics Coordination"), |
| 119 | + ("calculate_product_margin", ["WidgetX", 10.0, 20.0], "## Profit Margin Calculated"), |
| 120 | + ("update_product_category", ["WidgetX", "Gadgets"], "## Product Category Updated"), |
| 121 | + ("manage_product_bundles", ["Bundle1", ["A", "B"]], "## Product Bundle Managed"), |
| 122 | + ("optimize_product_page", ["WidgetX", "Speed"], "## Product Page Optimized"), |
| 123 | + ("monitor_product_performance", ["WidgetX"], "## Product Performance Monitored"), |
| 124 | + ("handle_product_pricing", ["WidgetX", "Premium"], "## Pricing Strategy Set"), |
| 125 | + ("create_training_material", ["WidgetX", "Material"], "## Training Material Developed"), |
| 126 | + ("update_product_labels", ["WidgetX", "Labels"], "## Product Labels Updated"), |
| 127 | + ("manage_product_warranty", ["WidgetX", "1 year"], "## Product Warranty Managed"), |
| 128 | + ("forecast_product_demand", ["WidgetX", "NextMonth"], "## Demand Forecast"), |
| 129 | + ("handle_product_licensing", ["WidgetX", "License"], "## Product Licensing Handled"), |
| 130 | + ("manage_product_packaging", ["WidgetX", "Box"], "## Product Packaging Managed"), |
| 131 | + ("set_product_safety_standards", ["WidgetX", "Standards"], "## Safety Standards Set"), |
| 132 | + ("develop_product_features", ["WidgetX", "NewFeat"], "## New Features Developed"), |
| 133 | + ("evaluate_product_performance", ["WidgetX", "KPIs"], "## Product Performance Evaluated"), |
| 134 | + ("manage_custom_product_orders", ["OrderDetails"], "## Custom Product Order Managed"), |
| 135 | + ("update_product_images", ["WidgetX", ["url1","url2"]], "## Product Images Updated"), |
| 136 | + ("handle_product_obsolescence", ["WidgetX"], "## Product Obsolescence Handled"), |
| 137 | + ("manage_product_sku", ["WidgetX", "SKU123"], "## SKU Managed"), |
| 138 | + ("provide_product_training", ["WidgetX", "Session"], "## Product Training Provided"), |
| 139 | + ], |
| 140 | +) |
| 141 | +async def test_all_other_tools(method, args, expected_substr): |
| 142 | + fn = getattr(ProductTools, method) |
| 143 | + result = await fn(*args) |
| 144 | + assert expected_substr in result |
| 145 | + |
| 146 | + |
| 147 | +def test_check_inventory_and_update_prints(capsys): |
| 148 | + # ensure the print side-effects still occur |
| 149 | + import asyncio |
| 150 | + chk = asyncio.get_event_loop().run_until_complete(ProductTools.check_inventory("X")) |
| 151 | + upd = asyncio.get_event_loop().run_until_complete(ProductTools.update_inventory("X", 5)) |
| 152 | + out = capsys.readouterr().out |
| 153 | + assert "Inventory status for **'X'** checked." in out |
| 154 | + assert "Inventory for **'X'** updated by **5** units." in out |
0 commit comments