test_build_docs_api.py 5.73 KB
#!/usr/bin/env python3
"""
测试 POST /indexer/build-docs 接口:构造请求数据、调用接口、打印完整响应。

用法:
  1. 先启动 Indexer 服务: ./scripts/start_indexer.sh  (或 uvicorn api.indexer_app:app --port 6004)
  2. 执行: python scripts/test_build_docs_api.py

  也可指定地址: INDEXER_URL=http://localhost:6004 python scripts/test_build_docs_api.py
"""

import json
import os
import sys
from datetime import datetime, timezone

# 项目根目录
ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
sys.path.insert(0, ROOT)

# 默认使用 requests 调真实服务;若未安装则回退到 TestClient
try:
    import requests
    HAS_REQUESTS = True
except ImportError:
    HAS_REQUESTS = False


def build_sample_request():
    """构造一条完整的 build-docs 请求体(对应 shoplazza_product_spu / sku / option 表结构)。"""
    now = datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ")
    sample_image_url = os.getenv(
        "SAMPLE_IMAGE_URL",
        "https://oss.essa.cn/98532128-cf8e-456c-9e30-6f2a5ea0c19f.jpg",
    )
    return {
        "tenant_id": "162",
        "items": [
            {
                "spu": {
                    "id": 10001,
                    "tenant_id": "162",
                    "title": "测试T恤 纯棉短袖",
                    "brief": "舒适纯棉,多色可选",
                    "description": "这是一款适合日常穿着的纯棉T恤,透气吸汗。",
                    "vendor": "测试品牌",
                    "category": "服装/上衣/T恤",
                    "category_id": 100,
                    "category_level": 2,
                    "category_path": "服装/上衣/T恤",
                    "fake_sales": 1280,
                    "image_src": sample_image_url,
                    "tags": "T恤,纯棉,短袖,夏季",
                    "create_time": now,
                    "update_time": now,
                },
                "skus": [
                    {
                        "id": 20001,
                        "spu_id": 10001,
                        "price": 99.0,
                        "compare_at_price": 129.0,
                        "sku": "SKU-TSHIRT-001",
                        "inventory_quantity": 50,
                        "option1": "黑色",
                        "option2": "M",
                        "option3": None,
                    },
                    {
                        "id": 20002,
                        "spu_id": 10001,
                        "price": 99.0,
                        "compare_at_price": 129.0,
                        "sku": "SKU-TSHIRT-002",
                        "inventory_quantity": 30,
                        "option1": "白色",
                        "option2": "L",
                        "option3": None,
                    },
                ],
                "options": [
                    {"id": 1, "position": 1, "name": "颜色"},
                    {"id": 2, "position": 2, "name": "尺码"},
                ],
            }
        ],
    }


def call_via_http(base_url: str, body: dict):
    """通过 HTTP 调用 build-docs。"""
    url = f"{base_url.rstrip('/')}/indexer/build-docs"
    r = requests.post(url, json=body, timeout=30)
    return r.status_code, r.text, r.json() if r.headers.get("content-type", "").startswith("application/json") else None


def call_via_test_client(body: dict):
    """通过 FastAPI TestClient 调用(不依赖已启动服务,但需 DB/ES 已配置)。"""
    from fastapi.testclient import TestClient
    import api.indexer_app as indexer_app

    with TestClient(indexer_app.app) as client:
        r = client.post("/indexer/build-docs", json=body)
    return r.status_code, r.text, r.json() if r.headers.get("content-type", "").startswith("application/json") else None


def main():
    body = build_sample_request()

    print("=" * 60)
    print("【请求】POST /indexer/build-docs")
    print("=" * 60)
    print(json.dumps(body, ensure_ascii=False, indent=2))

    base_url = os.getenv("INDEXER_URL", "http://localhost:6004")
    use_http = HAS_REQUESTS and (os.getenv("USE_TEST_CLIENT", "").lower() not in ("1", "true", "yes"))

    if use_http:
        try:
            status, raw, data = call_via_http(base_url, body)
        except requests.RequestException as e:
            print("\n[错误] 无法连接 Indexer 服务:", e)
            print("请先启动: ./scripts/start_indexer.sh  或  uvicorn api.indexer_app:app --port 6004")
            if HAS_REQUESTS:
                print("或使用进程内测试: USE_TEST_CLIENT=1 python scripts/test_build_docs_api.py")
            sys.exit(1)
    else:
        if not use_http and not HAS_REQUESTS:
            print("\n[提示] 未安装 requests,使用 TestClient 调用(需配置 DB/ES)。")
        else:
            print("\n[提示] 使用 TestClient 调用(USE_TEST_CLIENT=1)。")
        try:
            status, raw, data = call_via_test_client(body)
        except Exception as e:
            print("\n[错误] TestClient 调用失败:", e)
            print("请确保已 source activate.sh 且 DB/ES 环境变量正确,或先启动 Indexer 再用 HTTP 调用。")
            sys.exit(1)

    print("\n" + "=" * 60)
    print("【响应】HTTP status =", status)
    print("=" * 60)
    if data is not None:
        print(json.dumps(data, ensure_ascii=False, indent=2, default=str))
        if data.get("docs"):
            doc = data["docs"][0]
            print("\n" + "=" * 60)
            print("【返回 doc 顶层字段】共 {} 个".format(len(doc)))
            print("=" * 60)
            for k in sorted(doc.keys()):
                print(" ", k)
    else:
        print(raw)

    if status != 200:
        sys.exit(1)


if __name__ == "__main__":
    main()