Skill: shenghoo123-png/test-data-gen (version 1.0.0)

# test-data-gen — 测试数据生成器 ## 痛点 - 手动造数据费时费力，格式不规范 - 不同项目需要不同数据格式（JSON/CSV/SQL） - 需要特定数据库的SQL语法（MySQL/PostgreSQL） - 难以生成符合业务规则的测试数据（如手机号、邮箱格式） ## 场景 - 快速生成100条用户订单数据进行接口测试 - 导出CSV格式给业务人员核对 - 生成MySQL/PostgreSQL INSERT语句导入测试库 - 批量造数据填充空库进行性能测试 ## 定价 - **免费**：基础数据生成（姓名、邮箱、手机号） - **Pro 19元**：自定义模板 + SQL导出 + 所有数据格式 - **Team 49元**：批量生成（10万条）+ API调用 ## 指令格式 ### 基本用法 ``` test-data-gen [字段名] [数量] [格式] ``` ### 生成示例 ``` test-data-gen users 100 json # 生成100条用户JSON test-data-gen orders 50 csv # 生成50条订单CSV test-data-gen products 20 sql mysql # 生成20条产品SQL（MySQL） test-data-gen products 20 sql pg # 生成20条产品SQL（PostgreSQL） ``` ### 内置数据模板 | 模板 | 字段 | |------|------| | users | id, name, email, phone, age, gender | | orders | id, user_id, product, amount, status, created_at | | products | id, name, price, stock, category | | reviews | id, user_id, product_id, rating, comment | | addresses | id, user_id, province, city, district, detail | ### 自定义模板（Pro） ``` test-data-gen --template my_template.json 100 json ``` ## 字段类型 - `name` — 中文姓名 - `email` — 随机邮箱 - `phone` — 中国手机号（以1开头） - `age` — 18-80岁 - `gender` — 男/女 - `amount` — 金额（0.01-9999.99） - `date` — 日期（2020-01-01 ~ 今天） - `enum` — 自定义枚举值 ## 输出格式 - `--format json` — JSON数组 - `--format csv` — CSV（带表头） - `--format sql --db mysql|pg` — SQL INSERT语句

path: README.md
content: # Test Data Generator - 测试数据生成器

专为测试工程师设计的测试数据生成工具，支持 JSON/CSV/SQL 多种格式输出。

## 安装

```bash
pip install -r requirements.txt
```

或者直接使用：
```bash
python cli.py users 100 json
```

## 快速开始

### 基本用法

```bash
# 生成用户数据（默认）
python cli.py users 100 json

# 生成订单数据 CSV
python cli.py orders 50 csv

# 生成产品数据 SQL (MySQL)
python cli.py products 20 sql mysql

# 生成产品数据 SQL (PostgreSQL)
python cli.py products 20 sql pg
```

### 列出所有模板

```bash
python cli.py --list
```

### 输出到文件

```bash
python cli.py users 1000 json -o users.json
python cli.py orders 100 csv -o orders.csv
```

## 内置模板

| 模板 | 说明 |
|------|------|
| users | 用户数据：id, name, email, phone, age, gender, created_at |
| orders | 订单数据：id, user_id, product, amount, quantity, status, payment_method, created_at |
| products | 产品数据：id, name, category, price, stock, created_at |
| reviews | 评论数据：id, user_id, product_id, rating, comment, created_at |
| addresses | 地址数据：id, user_id, province, city, district, detail, phone, is_default |

## 输出格式

### JSON
```json
[
  {
    "id": 1,
    "name": "张伟",
    "email": "[REDACTED_SECRET]",
    "phone": "13812345678",
    "age": 28,
    "gender": "男"
  }
]
```

### CSV
```csv
id,name,email,phone,age,gender
1,张伟,[REDACTED_SECRET],13812345678,28,男
```

### SQL (MySQL)
```sql
INSERT INTO users (id, name, email, phone, age, gender) VALUES (1, '张伟', '[REDACTED_SECRET]', '13812345678', 28, '男');
```

## 作为模块使用

```python
from generator import DataGenerator, generate_from_template

# 使用内置模板
gen = DataGenerator("users")
data = gen.generate(100)
json_data = gen.to_json(data)

# 直接生成
result = generate_from_template("orders", 50, "csv")
```

## 自定义模板

创建模板文件 `my_template.json`:

```json
{
  "fields": {
    "id": {"type": "auto_increment", "start": 1},
    "name": {"type": "name"},
    "custom_field": {"type": "enum", "values": ["A", "B", "C"]}
  }
}
```

使用自定义模板:
```bash
python cli.py custom 100 json --template-file my_template.json
```

## 字段类型

| 类型 | 说明 |
|------|------|
| name | 中文姓名 |
| email | 随机邮箱 |
| phone | 中国手机号 |
| age | 年龄 (默认18-80) |
| gender | 性别 (男/女) |
| amount | 金额 |
| date | 日期 |
| datetime | 日期时间 |
| enum | 自定义枚举 |
| uuid | 唯一ID |
| auto_increment | 自增ID |
| product_name | 产品名称 |
| province | 省份 |
| city | 城市 |
| bool | 布尔值 |
| int | 整数 |
| text | 文本 |

## 运行测试

```bash
pytest tests/ -v
```

## 项目结构

```
test-data-gen/
├── SKILL.md          # 技能描述
├── generator.py      # 核心生成器
├── cli.py            # 命令行入口
├── README.md         # 使用说明
├── requirements.txt  # 依赖
└── tests/
    └── test_generator.py
```

## License

MIT


path: cli.py
content: #!/usr/bin/env python3
"""
Test Data Generator CLI
命令行测试数据生成工具
"""

import argparse
import sys
import os

# 添加当前目录到路径
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))

from generator import DataGenerator, DEFAULT_TEMPLATES, generate_from_template


def main():
    parser = argparse.ArgumentParser(
        description="测试数据生成器 - 生成高质量测试数据",
        formatter_class=argparse.RawDescriptionHelpFormatter,
        epilog="""
示例:
  %(prog)s users 100 json           # 生成100条用户JSON
  %(prog)s orders 50 csv            # 生成50条订单CSV
  %(prog)s products 20 sql mysql     # 生成20条产品SQL(MySQL)
  %(prog)s products 20 sql pg        # 生成20条产品SQL(PostgreSQL)
  %(prog)s --list                    # 列出所有模板
  %(prog)s --template custom.json 100 json  # 使用自定义模板

可用模板: users, orders, products, reviews, addresses
"""
    )

    parser.add_argument("template", nargs="?", default="users",
                        help="模板名称 (默认: users)")
    parser.add_argument("count", nargs="?", type=int, default=10,
                        help="生成数量 (默认: 10)")
    parser.add_argument("format", nargs="?", choices=["json", "csv", "sql"],
                        default="json", help="输出格式 (默认: json)")

    parser.add_argument("--db", dest="db_type", choices=["mysql", "pg"],
                        default="mysql", help="SQL数据库类型 (默认: mysql)")
    parser.add_argument("--template-file", dest="template_file",
                        help="自定义模板文件路径")
    parser.add_argument("--list", action="store_true",
                        help="列出所有可用模板")
    parser.add_argument("--output", "-o", dest="output_file",
                        help="输出到文件而非stdout")

    args = parser.parse_args()

    # 列出模板
    if args.list:
        print("可用模板:")
        for name in DataGenerator.get_available_templates():
            print(f"  - {name}")
        print("\n字段说明:")
        print("  users: id, name, email, phone, age, gender, created_at")
        print("  orders: id, user_id, product, amount, quantity, status, payment_method, created_at")
        print("  products: id, name, category, price, stock, created_at")
        print("  reviews: id, user_id, product_id, rating, comment, created_at")
        print("  addresses: id, user_id, province, city, district, detail, phone, is_default")
        return 0

    # 验证模板
    if args.template not in DEFAULT_TEMPLATES and not os.path.exists(args.template):
        print(f"错误: 未知模板 '{args.template}'", file=sys.stderr)
        print("使用 --list 查看可用模板", file=sys.stderr)
        return 1

    # 验证数量
    if args.count <= 0 or args.count > 100000:
        print("错误: 数量必须在 1-100000 之间", file=sys.stderr)
        return 1

    # 生成数据
    try:
        result = generate_from_template(
            template_name=args.template,
            count=args.count,
            output_format=args.format,
            db_type=args.db_type,
            custom_template=args.template_file
        )

        # 输出
        if args.output_file:
            with open(args.output_file, "w", encoding="utf-8") as f:
                f.write(result)
            print(f"已生成 {args.count} 条数据，保存到: {args.output_file}")
        else:
            print(result)

    except Exception as e:
        print(f"错误: {e}", file=sys.stderr)
        return 1

    return 0


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


path: generator.py
content: """
Test Data Generator Core
生成高质量测试数据，支持多种格式和数据库
"""

import random
import json
import csv
import re
import os
from datetime import datetime, timedelta
from typing import List, Dict, Any, Optional
from io import StringIO

# ============= 数据源 =============

FIRST_NAMES = [
    "张","王","李","赵","刘","陈","杨","黄","周","吴",
    "徐","孙","马","朱","胡","郭","何","高","林","罗",
    "郑","梁","谢","宋","唐","许","韩","邓","冯","曹",
    "彭","曾","肖","田","董","潘","袁","蔡","贾","余",
    "于","杜","魏","叶","程","苏","傅","卢","汪","戴"
]

LAST_NAMES = [
    "伟","芳","娜","秀英","敏","静","丽","强","磊","军",
    "洋","勇","艳","杰","娟","涛","明","超","秀兰","霞",
    "平","刚","桂英","华","建","玲","国华","建华","建国",
    "志强","秀珍","海","波","军","辉","鹏","飞","欢","丹"
]

EMAIL_DOMAINS = [
    "gmail.com","163.com","qq.com","126.com","outlook.com",
    "sina.com","hotmail.com","yahoo.com","foxmail.com","edu.cn"
]

PHONE_PREFIXES = [
    "130","131","132","133","134","135","136","137","138","139",
    "150","151","152","153","155","156","157","158","159",
    "170","171","172","173","176","177","178",
    "180","181","182","183","184","185","186","187","188","189"
]

PROVINCES = [
    "北京市","上海市","天津市","重庆市",
    "广东省","江苏省","浙江省","四川省","湖北省","湖南省",
    "河南省","河北省","山东省","山西省","安徽省","福建省",
    "江西省","辽宁省","吉林省","黑龙江省","陕西省","甘肃省",
    "云南省","贵州省","青海省","内蒙古","广西","西藏","宁夏","新疆","海南省"
]

CITIES = {
    "北京市":["北京市"],
    "上海市":["上海市"],
    "天津市":["天津市"],
    "重庆市":["重庆市"],
    "广东省":["广州市","深圳市","东莞市","佛山市","珠海市","中山市","惠州市","汕头市","湛江市"],
    "江苏省":["南京市","苏州市","无锡市","常州市","南通市","徐州市","扬州市","盐城市"],
    "浙江省":["杭州市","宁波市","温州市","嘉兴市","湖州市","绍兴市","金华市","台州市"],
    "四川省":["成都市","绵阳市","德阳市","南充市","宜宾市","自贡市","泸州市","达州市"],
    "湖北省":["武汉市","宜昌市","襄阳市","荆州市","黄冈市","孝感市","咸宁市","十堰市"],
    "湖南省":["长沙市","株洲市","湘潭市","衡阳市","岳阳市","常德市","张家界市","益阳市"],
    "河南省":["郑州市","洛阳市","开封市","安阳市","新乡市","焦作市","许昌市","周口市"],
    "河北省":["石家庄市","保定市","唐山市","廊坊市","邯郸市","秦皇岛市","沧州市","邢台市"],
    "山东省":["济南市","青岛市","烟台市","威海市","潍坊市","淄博市","临沂市","济宁市"],
    "山西省":["太原市","大同市","长治市","临汾市","运城市","晋城市","阳泉市"],
    "安徽省":["合肥市","芜湖市","蚌埠市","淮南市","马鞍山市","淮北市","铜陵市"],
    "福建省":["福州市","厦门市","泉州市","漳州市","莆田市","宁德市","三明市"],
    "江西省":["南昌市","景德镇市","九江市","赣州市","吉安市","宜春市","抚州市"],
    "辽宁省":["沈阳市","大连市","鞍山市","抚顺市","本溪市","锦州市","葫芦岛市"],
    "吉林省":["长春市","吉林市","四平市","辽源市","通化市","延边朝鲜族自治州"],
    "黑龙江省":["哈尔滨市","齐齐哈尔市","牡丹江市","佳木斯市","大庆市","鸡西市"],
    "陕西省":["西安市","宝鸡市","咸阳市","铜川市","渭南市","延安市","汉中市"],
    "甘肃省":["兰州市","嘉峪关市","金昌市","白银市","天水市","武威市"],
    "云南省":["昆明市","曲靖市","玉溪市","保山市","昭通市","丽江市","普洱市"],
    "贵州省":["贵阳市","六盘水市","遵义市","安顺市","毕节市","铜仁市"],
    "青海省":["西宁市","海东市","海南藏族自治州","海北藏族自治州"],
    "内蒙古":["呼和浩特市","包头市","乌海市","赤峰市","呼伦贝尔市","鄂尔多斯市"],
    "广西":["南宁市","柳州市","桂林市","梧州市","北海市","防城港市","钦州市"],
    "西藏":["拉萨市","日喀则市","昌都市","林芝市","山南市"],
    "宁夏":["银川市","石嘴山市","吴忠市","固原市","中卫市"],
    "新疆":["乌鲁木齐市","克拉玛依市","吐鲁番市","哈密市","伊犁哈萨克自治州"],
    "海南省":["海口市","三亚市","三沙市","儋州市"]
}

DISTRICTS = ["朝阳区","海淀区","丰台区","石景山区","东城区","西城区","浦东新区","徐汇区","黄浦区","静安区",
             "天河区","越秀区","海珠区","荔湾区","白云区","南山区","福田区","宝安区","龙岗区","罗湖区"]

STREETS = ["中山路","人民路","建设路","解放路","文化路","和平路","民主路","胜利路","友谊路","光明路",
           "新华路","长江路","黄河路","珠江路","哈尔滨路","南京路","北京路","上海路","天津路","重庆路"]

ORDER_STATUSES = ["pending","paid","shipped","delivered","cancelled","refunded"]
PRODUCT_CATEGORIES = ["电子产品","服装","食品","图书","家居","玩具","体育","美妆","医药","汽车"]
PAYMENT_METHODS = ["alipay","wechat","card","cash"]
GENDERS = ["男","女"]

# ============= 工具函数 =============

def random_name() -> str:
    """生成随机中文姓名"""
    return random.choice(FIRST_NAMES) + random.choice(LAST_NAMES)

def random_email(name: str = None) -> str:
    """生成随机邮箱"""
    if name is None:
        name = random_name()
    # 拼音化处理
    pinyin_name = name.lower()
    separators = ["",".","_"]
    sep = random.choice(separators)
    nums = random.randint(0, 999)
    domain = random.choice(EMAIL_DOMAINS)
    patterns = [
        f"{pinyin_name}{sep}{nums}@{domain}",
        f"{pinyin_name}{nums}@{domain}",
        f"{pinyin_name[0]}{sep}{nums}@{domain}",
    ]
    return random.choice(patterns)

def random_phone() -> str:
    """生成随机中国手机号"""
    prefix = random.choice(PHONE_PREFIXES)
    suffix = f"{random.randint(10000000, 99999999)}"
    return prefix + suffix

def random_age(min_age: int = 18, max_age: int = 80) -> int:
    """生成随机年龄"""
    return random.randint(min_age, max_age)

def random_gender() -> str:
    """随机性别"""
    return random.choice(GENDERS)

def random_amount(min_amount: float = 0.01, max_amount: float = 9999.99) -> float:
    """生成随机金额"""
    return round(random.uniform(min_amount, max_amount), 2)

def random_date(start_year: int = 2020) -> str:
    """生成随机日期"""
    start = datetime(start_year, 1, 1)
    end = datetime.now()
    delta = end - start
    random_days = random.randint(0, delta.days)
    return (start + timedelta(days=random_days)).strftime("%Y-%m-%d")

def random_datetime(start_year: int = 2020) -> str:
    """生成随机日期时间"""
    start = datetime(start_year, 1, 1)
    end = datetime.now()
    delta = end - start
    random_seconds = random.randint(0, int(delta.total_seconds()))
    return (start + timedelta(seconds=random_seconds)).strftime("%Y-%m-%d %H:%M:%S")

def random_enum(choices: List[str]) -> str:
    """从枚举中随机选择"""
    return random.choice(choices)

def random_address() -> Dict[str, str]:
    """生成随机地址"""
    province = random.choice(PROVINCES)
    city = random.choice(CITIES.get(province, [""]))
    district = random.choice(DISTRICTS)
    street = random.choice(STREETS) + str(random.randint(1, 999)) + "号"
    return {
        "province": province,
        "city": city,
        "district": district,
        "detail": f"{district}{street}"
    }

def random_product_name() -> str:
    """生成随机产品名称"""
    brands = ["华为","小米","苹果","三星","OPPO","vivo","联想","戴尔","惠普","索尼"]
    categories = ["手机","电脑","平板","耳机","手表","相机","电视","冰箱","洗衣机","空调"]
    return f"{random.choice(brands)} {random.choice(categories)} {random.randint(1,9)}代"

def random_uuid() -> str:
    """生成随机UUID-like ID"""
    return f"{random.randint(100000,999999)}-{random.randint(1000,9999)}"

# ============= 模板定义 =============

DEFAULT_TEMPLATES = {
    "users": {
        "fields": {
            "id": {"type": "auto_increment", "start": 1},
            "name": {"type": "name"},
            "email": {"type": "email"},
            "phone": {"type": "phone"},
            "age": {"type": "age"},
            "gender": {"type": "gender"},
            "created_at": {"type": "datetime"}
        }
    },
    "orders": {
        "fields": {
            "id": {"type": "uuid"},
            "user_id": {"type": "auto_increment", "start": 1},
            "product": {"type": "product_name"},
            "amount": {"type": "amount"},
            "quantity": {"type": "int", "min": 1, "max": 10},
            "status": {"type": "enum", "values": ORDER_STATUSES},
            "payment_method": {"type": "enum", "values": PAYMENT_METHODS},
            "created_at": {"type": "datetime"}
        }
    },
    "products": {
        "fields": {
            "id": {"type": "auto_increment", "start": 1},
            "name": {"type": "product_name"},
            "category": {"type": "enum", "values": PRODUCT_CATEGORIES},
            "price": {"type": "amount", "min": 9.9, "max": 9999.9},
            "stock": {"type": "int", "min": 0, "max": 1000},
            "created_at": {"type": "datetime"}
        }
    },
    "reviews": {
        "fields": {
            "id": {"type": "uuid"},
            "user_id": {"type": "auto_increment", "start": 1},
            "product_id": {"type": "auto_increment", "start": 1},
            "rating": {"type": "int", "min": 1, "max": 5},
            "comment": {"type": "text", "max_length": 200},
            "created_at": {"type": "datetime"}
        }
    },
    "addresses": {
        "fields": {
            "id": {"type": "auto_increment", "start": 1},
            "user_id": {"type": "auto_increment", "start": 1},
            "province": {"type": "province"},
            "city": {"type": "city"},
            "district": {"type": "enum", "values": DISTRICTS},
            "detail": {"type": "text", "max_length": 100},
            "phone": {"type": "phone"},
            "is_default": {"type": "bool"}
        }
    }
}

COMMENTS = [
    "质量很好，满意！","发货速度快","包装严实","和描述一致","性价比高",
    "服务态度好","值得购买","推荐！","一般般","有点失望","还行吧",
    "很好用","不错不错","超级喜欢","下次还来","非常好！"
]

# ============= 核心生成器 =============

class DataGenerator:
    """测试数据生成器"""

    def __init__(self, template: str = "users"):
        self.template_name = template
        self.template = DEFAULT_TEMPLATES.get(template, DEFAULT_TEMPLATES["users"])
        self._auto_increment_values: Dict[str, int] = {}
        for field_name, field_def in self.template["fields"].items():
            if field_def.get("type") == "auto_increment":
                self._auto_increment_values[field_name] = field_def.get("start", 1)

    def _generate_field(self, field_name: str, field_def: Dict) -> Any:
        """根据字段定义生成单个字段值"""
        ftype = field_def.get("type", "text")

        if ftype == "name":
            return random_name()
        elif ftype == "email":
            return random_email()
        elif ftype == "phone":
            return random_phone()
        elif ftype == "age":
            return random_age(field_def.get("min", 18), field_def.get("max", 80))
        elif ftype == "gender":
            return random_gender()
        elif ftype == "amount":
            return random_amount(field_def.get("min", 0.01), field_def.get("max", 9999.99))
        elif ftype == "date":
            return random_date(field_def.get("start_year", 2020))
        elif ftype == "datetime":
            return random_datetime(field_def.get("start_year", 2020))
        elif ftype == "enum":
            return random_enum(field_def.get("values", ["A","B"]))
        elif ftype == "uuid":
            return random_uuid()
        elif ftype == "auto_increment":
            val = self._auto_increment_values[field_name]
            self._auto_increment_values[field_name] += 1
            return val
        elif ftype == "product_name":
            return random_product_name()
        elif ftype == "province":
            return random.choice(PROVINCES)
        elif ftype == "city":
            return random.choice(list(CITIES.keys()))
        elif ftype == "bool":
            return random.choice([True, False])
        elif ftype == "int":
            return random.randint(field_def.get("min", 0), field_def.get("max", 100))
        elif ftype == "text":
            max_len = field_def.get("max_length", 100)
            if "评论" in field_name or "comment" in field_name.lower():
                return random.choice(COMMENTS)
            return f"测试文本{random.randint(1,999)}"
        else:
            return f"value_{random.randint(1,100)}"

    def generate(self, count: int = 1) -> List[Dict[str, Any]]:
        """生成指定数量的数据"""
        results = []
        for _ in range(count):
            record = {}
            for field_name, field_def in self.template["fields"].items():
                record[field_name] = self._generate_field(field_name, field_def)
            results.append(record)
        return results

    def to_json(self, data: List[Dict[str, Any]]) -> str:
        """转换为JSON格式"""
        return json.dumps(data, ensure_ascii=False, indent=2)

    def to_csv(self, data: List[Dict[str, Any]]) -> str:
        """转换为CSV格式"""
        if not data:
            return ""
        output = StringIO()
        writer = csv.DictWriter(output, fieldnames=data[0].keys())
        writer.writeheader()
        writer.writerows(data)
        return output.getvalue()

    def to_sql(self, data: List[Dict[str, Any]], db_type: str = "mysql", table_name: str = None) -> str:
        """转换为SQL INSERT语句"""
        if not data:
            return ""
        if table_name is None:
            table_name = self.template_name

        sql_statements = []
        for record in data:
            columns = ", ".join(record.keys())
            values = []
            for v in record.values():
                if v is None:
                    values.append("NULL")
                elif isinstance(v, bool):
                    values.append("1" if v else "0")
                elif isinstance(v, (int, float)):
                    values.append(str(v))
                elif isinstance(v, str):
                    # 转义单引号
                    escaped = v.replace("'", "''")
                    values.append(f"'{escaped}'")
                else:
                    values.append(f"'{str(v)}'")
            values_str = ", ".join(values)
            sql_statements.append(f"INSERT INTO {table_name} ({columns}) VALUES ({values_str});")

        # PostgreSQL使用双引号
        if db_type == "pg":
            sql_statements = [s.replace('"', '"') for s in sql_statements]

        return "\n".join(sql_statements)

    @staticmethod
    def get_available_templates() -> List[str]:
        """获取可用模板列表"""
        return list(DEFAULT_TEMPLATES.keys())


def load_template(path: str) -> Dict:
    """从文件加载自定义模板"""
    with open(path, "r", encoding="utf-8") as f:
        return json.load(f)


def generate_from_template(template_name: str, count: int, output_format: str,
                           db_type: str = "mysql", custom_template: str = None) -> str:
    """根据模板生成数据"""
    if custom_template and os.path.exists(custom_template):
        template_data = load_template(custom_template)
        # 临时覆盖
        saved = DEFAULT_TEMPLATES.get(template_name, {})
        DEFAULT_TEMPLATES[template_name] = template_data

    gen = DataGenerator(template_name)
    data = gen.generate(count)

    if output_format == "json":
        result = gen.to_json(data)
    elif output_format == "csv":
        result = gen.to_csv(data)
    elif output_format == "sql":
        result = gen.to_sql(data, db_type)
    else:
        raise ValueError(f"不支持的格式: {output_format}")

    # 恢复原模板
    if custom_template and os.path.exists(custom_template):
        if saved:
            DEFAULT_TEMPLATES[template_name] = saved

    return result


path: requirements.txt
content: pytest>=7.0.0


path: tests/test_generator.py
content: """
测试数据生成器 - 单元测试
"""

import pytest
import json
import csv
import sys
import os

sys.path.insert(0, os.path.join(os.path.dirname(__file__), ".."))
from generator import (
    DataGenerator, random_name, random_email, random_phone,
    random_age, random_gender, random_amount, random_date,
    random_address, random_product_name, DEFAULT_TEMPLATES
)


class TestRandomFunctions:
    """测试随机数据生成函数"""

    def test_random_name(self):
        """测试姓名生成"""
        for _ in range(100):
            name = random_name()
            assert len(name) >= 2
            assert len(name) <= 4

    def test_random_email(self):
        """测试邮箱生成"""
        for _ in range(100):
            email = random_email()
            assert "@" in email
            assert "." in email.split("@")[1]

    def test_random_phone(self):
        """测试手机号生成"""
        for _ in range(100):
            phone = random_phone()
            assert len(phone) == 11
            assert phone.startswith("1")

    def test_random_age(self):
        """测试年龄生成"""
        for _ in range(100):
            age = random_age(18, 60)
            assert 18 <= age <= 60

    def test_random_gender(self):
        """测试性别生成"""
        genders = [random_gender() for _ in range(100)]
        assert all(g in ["男", "女"] for g in genders)

    def test_random_amount(self):
        """测试金额生成"""
        for _ in range(100):
            amount = random_amount(1.0, 100.0)
            assert 1.0 <= amount <= 100.0

    def test_random_date(self):
        """测试日期生成"""
        for _ in range(100):
            date_str = random_date(2020)
            assert len(date_str) == 10
            parts = date_str.split("-")
            assert len(parts) == 3
            assert 2020 <= int(parts[0]) <= 2030

    def test_random_address(self):
        """测试地址生成"""
        addr = random_address()
        assert "province" in addr
        assert "city" in addr
        assert "district" in addr
        assert "detail" in addr

    def test_random_product_name(self):
        """测试产品名称生成"""
        for _ in range(100):
            name = random_product_name()
            assert len(name) > 0


class TestDataGenerator:
    """测试数据生成器类"""

    def test_generator_init(self):
        """测试生成器初始化"""
        gen = DataGenerator("users")
        assert gen.template_name == "users"

    def test_generate_users(self):
        """测试生成用户数据"""
        gen = DataGenerator("users")
        data = gen.generate(10)
        assert len(data) == 10
        assert "name" in data[0]
        assert "email" in data[0]
        assert "phone" in data[0]

    def test_generate_orders(self):
        """测试生成订单数据"""
        gen = DataGenerator("orders")
        data = gen.generate(5)
        assert len(data) == 5
        assert "product" in data[0]
        assert "amount" in data[0]
        assert "status" in data[0]

    def test_generate_products(self):
        """测试生成产品数据"""
        gen = DataGenerator("products")
        data = gen.generate(20)
        assert len(data) == 20
        assert "name" in data[0]
        assert "price" in data[0]
        assert "stock" in data[0]

    def test_auto_increment(self):
        """测试自增ID"""
        gen = DataGenerator("users")
        data = gen.generate(10)
        ids = [r["id"] for r in data]
        assert ids == list(range(1, 11))

    def test_to_json(self):
        """测试JSON输出"""
        gen = DataGenerator("users")
        data = gen.generate(2)
        json_str = gen.to_json(data)
        parsed = json.loads(json_str)
        assert len(parsed) == 2

    def test_to_csv(self):
        """测试CSV输出"""
        gen = DataGenerator("users")
        data = gen.generate(2)
        csv_str = gen.to_csv(data)
        lines = csv_str.strip().split("\n")
        assert len(lines) == 3  # 1 header + 2 data

    def test_to_sql_mysql(self):
        """测试MySQL SQL输出"""
        gen = DataGenerator("users")
        data = gen.generate(1)
        sql = gen.to_sql(data, "mysql")
        assert "INSERT INTO users" in sql
        assert "VALUES" in sql

    def test_to_sql_pg(self):
        """测试PostgreSQL SQL输出"""
        gen = DataGenerator("users")
        data = gen.generate(1)
        sql = gen.to_sql(data, "pg")
        assert "INSERT INTO users" in sql

    def test_get_available_templates(self):
        """测试获取模板列表"""
        templates = DataGenerator.get_available_templates()
        assert "users" in templates
        assert "orders" in templates
        assert "products" in templates


class TestEdgeCases:
    """边界情况测试"""

    def test_generate_zero(self):
        """测试生成0条数据"""
        gen = DataGenerator("users")
        data = gen.generate(0)
        assert len(data) == 0

    def test_empty_to_csv(self):
        """测试空数据CSV"""
        gen = DataGenerator("users")
        csv_str = gen.to_csv([])
        assert csv_str == ""

    def test_empty_to_sql(self):
        """测试空数据SQL"""
        gen = DataGenerator("users")
        sql = gen.to_sql([])
        assert sql == ""

    def test_special_characters(self):
        """测试特殊字符转义"""
        gen = DataGenerator("users")
        data = [{"name": "O'Brien", "email": "[REDACTED_SECRET]"}]
        sql = gen.to_sql(data)
        assert "O''Brien" in sql  # 单引号应该被转义


if __name__ == "__main__":
    pytest.main([__file__, "-v"])


Security scan verdicts — ClawScan: clean | static analysis: clean | VirusTotal: clean | SkillSpector: suspicious (MEDIUM)