test_facets_fix.py 6.87 KB
#!/usr/bin/env python3
"""
测试 Facets 修复:验证 Pydantic 模型是否正确构建和序列化
"""

import sys
import os
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))

from api.models import SearchResponse, FacetResult, FacetValue


def test_facet_models():
    """测试 FacetValue 和 FacetResult 模型"""
    print("=== 测试 1: 创建 FacetValue 对象 ===")
    
    facet_value = FacetValue(
        value="玩具",
        label="玩具",
        count=100,
        selected=False
    )
    
    print(f"✓ FacetValue 创建成功")
    print(f"  - value: {facet_value.value}")
    print(f"  - count: {facet_value.count}")
    print(f"  - selected: {facet_value.selected}")
    
    print("\n=== 测试 2: 创建 FacetResult 对象 ===")
    
    facet_result = FacetResult(
        field="categoryName_keyword",
        label="商品类目",
        type="terms",
        values=[
            FacetValue(value="玩具", label="玩具", count=100, selected=False),
            FacetValue(value="益智玩具", label="益智玩具", count=50, selected=True),
        ]
    )
    
    print(f"✓ FacetResult 创建成功")
    print(f"  - field: {facet_result.field}")
    print(f"  - label: {facet_result.label}")
    print(f"  - type: {facet_result.type}")
    print(f"  - values count: {len(facet_result.values)}")


def test_search_response_with_facets():
    """测试 SearchResponse 与 Facets"""
    print("\n=== 测试 3: 创建 SearchResponse 对象(包含 Facets)===")
    
    # 创建 Facets
    facets = [
        FacetResult(
            field="categoryName_keyword",
            label="商品类目",
            type="terms",
            values=[
                FacetValue(value="玩具", label="玩具", count=100, selected=False),
                FacetValue(value="益智玩具", label="益智玩具", count=50, selected=False),
            ]
        ),
        FacetResult(
            field="brandName_keyword",
            label="品牌",
            type="terms",
            values=[
                FacetValue(value="乐高", label="乐高", count=80, selected=True),
                FacetValue(value="美泰", label="美泰", count=60, selected=False),
            ]
        )
    ]
    
    # 创建 SearchResponse
    response = SearchResponse(
        hits=[
            {
                "_id": "1",
                "_score": 10.5,
                "_source": {"name": "测试商品1"}
            }
        ],
        total=1,
        max_score=10.5,
        took_ms=50,
        facets=facets,
        query_info={"original_query": "玩具"}
    )
    
    print(f"✓ SearchResponse 创建成功")
    print(f"  - total: {response.total}")
    print(f"  - facets count: {len(response.facets) if response.facets else 0}")
    print(f"  - facets 类型: {type(response.facets)}")
    
    if response.facets:
        print(f"\n  Facet 1:")
        print(f"    - field: {response.facets[0].field}")
        print(f"    - label: {response.facets[0].label}")
        print(f"    - values count: {len(response.facets[0].values)}")
        print(f"    - first value: {response.facets[0].values[0].value} (count: {response.facets[0].values[0].count})")


def test_serialization():
    """测试序列化和反序列化"""
    print("\n=== 测试 4: 序列化和反序列化 ===")
    
    # 创建带 facets 的响应
    facets = [
        FacetResult(
            field="price",
            label="价格",
            type="range",
            values=[
                FacetValue(value="0-50", label="0-50元", count=30, selected=False),
                FacetValue(value="50-100", label="50-100元", count=45, selected=True),
            ]
        )
    ]
    
    response = SearchResponse(
        hits=[],
        total=0,
        max_score=0.0,
        took_ms=10,
        facets=facets,
        query_info={}
    )
    
    # 序列化为字典
    response_dict = response.model_dump()
    print(f"✓ 序列化为字典成功")
    print(f"  - facets 类型: {type(response_dict['facets'])}")
    print(f"  - facets 内容: {response_dict['facets']}")
    
    # 序列化为 JSON
    response_json = response.model_dump_json()
    print(f"\n✓ 序列化为 JSON 成功")
    print(f"  - JSON 长度: {len(response_json)} 字符")
    print(f"  - JSON 片段: {response_json[:200]}...")
    
    # 从 JSON 反序列化
    response_from_json = SearchResponse.model_validate_json(response_json)
    print(f"\n✓ 从 JSON 反序列化成功")
    print(f"  - facets 恢复: {len(response_from_json.facets) if response_from_json.facets else 0} 个")
    
    if response_from_json.facets:
        print(f"  - 第一个 facet field: {response_from_json.facets[0].field}")
        print(f"  - 第一个 facet values: {len(response_from_json.facets[0].values)} 个")


def test_pydantic_auto_conversion():
    """测试 Pydantic 是否能自动从字典转换(这是原来的方式)"""
    print("\n=== 测试 5: Pydantic 自动转换(字典 -> 模型)===")
    
    # 使用字典创建 SearchResponse(测试 Pydantic 的自动转换能力)
    facets_dict = [
        {
            "field": "categoryName_keyword",
            "label": "商品类目",
            "type": "terms",
            "values": [
                {
                    "value": "玩具",
                    "label": "玩具",
                    "count": 100,
                    "selected": False
                }
            ]
        }
    ]
    
    try:
        response = SearchResponse(
            hits=[],
            total=0,
            max_score=0.0,
            took_ms=10,
            facets=facets_dict,  # 传入字典而不是 FacetResult 对象
            query_info={}
        )
        print(f"✓ Pydantic 可以从字典自动转换")
        print(f"  - facets 类型: {type(response.facets)}")
        print(f"  - facets[0] 类型: {type(response.facets[0])}")
        print(f"  - 是否为 FacetResult: {isinstance(response.facets[0], FacetResult)}")
    except Exception as e:
        print(f"✗ Pydantic 自动转换失败: {e}")


def main():
    """运行所有测试"""
    print("开始测试 Facets 修复...\n")
    
    try:
        test_facet_models()
        test_search_response_with_facets()
        test_serialization()
        test_pydantic_auto_conversion()
        
        print("\n" + "="*60)
        print("✅ 所有测试通过!")
        print("="*60)
        
        print("\n总结:")
        print("1. FacetValue 和 FacetResult 模型创建正常")
        print("2. SearchResponse 可以正确接收 FacetResult 对象列表")
        print("3. 序列化和反序列化工作正常")
        print("4. Pydantic 可以自动将字典转换为模型(但我们现在直接使用模型更好)")
        
        return 0
        
    except Exception as e:
        print(f"\n❌ 测试失败: {e}")
        import traceback
        traceback.print_exc()
        return 1


if __name__ == "__main__":
    exit(main())