Commit 72e7256ae49c8422d6008aed15677168cb705cc9

Authored by tangwang
1 parent f1505d1b

清理文件

scripts/generate_test_data.py deleted
... ... @@ -1,421 +0,0 @@
1   -#!/usr/bin/env python3
2   -"""
3   -Generate test data for Shoplazza SPU and SKU tables.
4   -
5   -Generates 100 SPU records with 1-5 SKUs each.
6   -"""
7   -
8   -import sys
9   -import os
10   -import random
11   -import argparse
12   -from pathlib import Path
13   -from datetime import datetime, timedelta
14   -
15   -# Add parent directory to path
16   -sys.path.insert(0, str(Path(__file__).parent.parent))
17   -
18   -
19   -def generate_spu_data(num_spus: int = 100, tenant_id: str = "1", start_id: int = 1):
20   - """
21   - Generate SPU test data.
22   -
23   - Args:
24   - num_spus: Number of SPUs to generate
25   - tenant_id: Tenant ID
26   - start_id: Starting ID for SPUs
27   -
28   - Returns:
29   - List of SPU data dictionaries
30   - """
31   - categories = ["电子产品", "服装", "家居用品", "美妆", "食品", "运动用品", "图书", "玩具"]
32   - vendors = ["Sony", "Nike", "Apple", "Samsung", "华为", "小米", "美的", "海尔"]
33   -
34   - products = [
35   - ("蓝牙耳机", "Bluetooth Headphone", "高品质无线蓝牙耳机", "High-quality wireless Bluetooth headphone"),
36   - ("运动鞋", "Running Shoes", "舒适透气的运动鞋", "Comfortable and breathable running shoes"),
37   - ("智能手机", "Smartphone", "高性能智能手机", "High-performance smartphone"),
38   - ("笔记本电脑", "Laptop", "轻薄便携笔记本电脑", "Lightweight and portable laptop"),
39   - ("智能手表", "Smart Watch", "多功能智能手表", "Multi-function smart watch"),
40   - ("平板电脑", "Tablet", "高清平板电脑", "High-definition tablet"),
41   - ("无线鼠标", "Wireless Mouse", "人体工学无线鼠标", "Ergonomic wireless mouse"),
42   - ("机械键盘", "Mechanical Keyboard", "RGB背光机械键盘", "RGB backlit mechanical keyboard"),
43   - ("显示器", "Monitor", "4K高清显示器", "4K high-definition monitor"),
44   - ("音响", "Speaker", "蓝牙无线音响", "Bluetooth wireless speaker"),
45   - ]
46   -
47   - spus = []
48   - for i in range(num_spus):
49   - spu_id = start_id + i
50   - product = random.choice(products)
51   - category = random.choice(categories)
52   - vendor = random.choice(vendors)
53   -
54   - # Generate handle
55   - handle = f"product-{spu_id}"
56   -
57   - # Generate title (Chinese)
58   - title_zh = f"{product[0]} {vendor}"
59   -
60   - # Generate brief
61   - brief_zh = product[2]
62   -
63   - # Generate description
64   - description_zh = f"<p>{product[2]},来自{vendor}品牌。{product[3]}</p>"
65   -
66   - # Generate SEO fields
67   - seo_title = f"{title_zh} - {category}"
68   - seo_description = f"购买{vendor}{product[0]},{product[2]}"
69   - seo_keywords = f"{product[0]},{vendor},{category}"
70   -
71   - # Generate tags
72   - tags = f"{category},{vendor},{product[0]}"
73   -
74   - # Generate image
75   - image_src = f"//cdn.example.com/products/{spu_id}.jpg"
76   -
77   - # Generate dates
78   - created_at = datetime.now() - timedelta(days=random.randint(1, 365))
79   - updated_at = created_at + timedelta(days=random.randint(0, 30))
80   -
81   - spu = {
82   - 'id': spu_id,
83   - 'shop_id': 1,
84   - 'shoplazza_id': f"spu-{spu_id}",
85   - 'handle': handle,
86   - 'title': title_zh,
87   - 'brief': brief_zh,
88   - 'description': description_zh,
89   - 'spu': '',
90   - 'vendor': vendor,
91   - 'vendor_url': f"https://{vendor.lower()}.com",
92   - 'seo_title': seo_title,
93   - 'seo_description': seo_description,
94   - 'seo_keywords': seo_keywords,
95   - 'image_src': image_src,
96   - 'image_width': 800,
97   - 'image_height': 600,
98   - 'image_path': f"products/{spu_id}.jpg",
99   - 'image_alt': title_zh,
100   - 'inventory_policy': '',
101   - 'inventory_quantity': 0,
102   - 'inventory_tracking': '0',
103   - 'published': 1,
104   - 'published_at': created_at.strftime('%Y-%m-%d %H:%M:%S'),
105   - 'requires_shipping': 1,
106   - 'taxable': 0,
107   - 'fake_sales': 0,
108   - 'display_fake_sales': 0,
109   - 'mixed_wholesale': 0,
110   - 'need_variant_image': 0,
111   - 'has_only_default_variant': 0,
112   - 'tags': tags,
113   - 'note': '',
114   - 'category': category,
115   - 'shoplazza_created_at': created_at.strftime('%Y-%m-%d %H:%M:%S'),
116   - 'shoplazza_updated_at': updated_at.strftime('%Y-%m-%d %H:%M:%S'),
117   - 'tenant_id': tenant_id,
118   - 'creator': '1',
119   - 'create_time': created_at.strftime('%Y-%m-%d %H:%M:%S'),
120   - 'updater': '1',
121   - 'update_time': updated_at.strftime('%Y-%m-%d %H:%M:%S'),
122   - 'deleted': 0
123   - }
124   - spus.append(spu)
125   -
126   - return spus
127   -
128   -
129   -def generate_sku_data(spus: list, start_sku_id: int = 1):
130   - """
131   - Generate SKU test data for SPUs.
132   -
133   - Args:
134   - spus: List of SPU data
135   - start_sku_id: Starting ID for SKUs
136   -
137   - Returns:
138   - List of SKU data dictionaries
139   - """
140   - colors = ["黑色", "白色", "红色", "蓝色", "绿色", "灰色"]
141   - sizes = ["S", "M", "L", "XL", "XXL"]
142   -
143   - skus = []
144   - sku_id = start_sku_id
145   -
146   - for spu in spus:
147   - spu_id = spu['id']
148   - num_skus = random.randint(1, 5)
149   -
150   - # Base price
151   - base_price = random.uniform(50, 500)
152   -
153   - for i in range(num_skus):
154   - # Generate variant options
155   - color = random.choice(colors) if num_skus > 1 else None
156   - size = random.choice(sizes) if num_skus > 2 else None
157   -
158   - # Generate title
159   - title_parts = []
160   - if color:
161   - title_parts.append(color)
162   - if size:
163   - title_parts.append(size)
164   - title = " / ".join(title_parts) if title_parts else ""
165   -
166   - # Generate SKU
167   - sku_code = f"SKU-{spu_id}-{i+1}"
168   -
169   - # Generate price (variation from base)
170   - price = base_price + random.uniform(-20, 50)
171   - compare_at_price = price * random.uniform(1.2, 1.5)
172   -
173   - # Generate stock
174   - stock = random.randint(0, 100)
175   -
176   - # Generate dates
177   - created_at = datetime.now() - timedelta(days=random.randint(1, 365))
178   - updated_at = created_at + timedelta(days=random.randint(0, 30))
179   -
180   - sku = {
181   - 'id': sku_id,
182   - 'spu_id': spu_id,
183   - 'shop_id': 1,
184   - 'shoplazza_id': f"sku-{sku_id}",
185   - 'shoplazza_product_id': spu['shoplazza_id'],
186   - 'shoplazza_image_id': '',
187   - 'title': title,
188   - 'sku': sku_code,
189   - 'barcode': f"BAR{sku_id:08d}",
190   - 'position': i + 1,
191   - 'price': round(price, 2),
192   - 'compare_at_price': round(compare_at_price, 2),
193   - 'cost_price': round(price * 0.6, 2),
194   - 'option1': color if color else '',
195   - 'option2': size if size else '',
196   - 'option3': '',
197   - 'inventory_quantity': stock,
198   - 'weight': round(random.uniform(0.1, 5.0), 2),
199   - 'weight_unit': 'kg',
200   - 'image_src': '',
201   - 'wholesale_price': '[{"price": ' + str(round(price * 0.8, 2)) + ', "minQuantity": 10}]',
202   - 'note': '',
203   - 'extend': None, # JSON field, use NULL instead of empty string
204   - 'shoplazza_created_at': created_at.strftime('%Y-%m-%d %H:%M:%S'),
205   - 'shoplazza_updated_at': updated_at.strftime('%Y-%m-%d %H:%M:%S'),
206   - 'tenant_id': spu['tenant_id'],
207   - 'creator': '1',
208   - 'create_time': created_at.strftime('%Y-%m-%d %H:%M:%S'),
209   - 'updater': '1',
210   - 'update_time': updated_at.strftime('%Y-%m-%d %H:%M:%S'),
211   - 'deleted': 0
212   - }
213   - skus.append(sku)
214   - sku_id += 1
215   -
216   - return skus
217   -
218   -
219   -def escape_sql_string(value: str) -> str:
220   - """
221   - Escape SQL string value (replace single quotes with doubled quotes).
222   -
223   - Args:
224   - value: String value to escape
225   -
226   - Returns:
227   - Escaped string
228   - """
229   - if value is None:
230   - return ''
231   - return str(value).replace("'", "''").replace("\\", "\\\\")
232   -
233   -
234   -def generate_sql_inserts(spus: list, skus: list, output_file: str):
235   - """
236   - Generate SQL INSERT statements.
237   -
238   - Args:
239   - spus: List of SPU data
240   - skus: List of SKU data
241   - output_file: Output file path
242   - """
243   - with open(output_file, 'w', encoding='utf-8') as f:
244   - f.write("-- SPU Test Data\n")
245   - f.write("INSERT INTO shoplazza_product_spu (\n")
246   - f.write(" id, shop_id, shoplazza_id, handle, title, brief, description, spu,\n")
247   - f.write(" vendor, vendor_url, seo_title, seo_description, seo_keywords,\n")
248   - f.write(" image_src, image_width, image_height, image_path, image_alt,\n")
249   - f.write(" inventory_policy, inventory_quantity, inventory_tracking,\n")
250   - f.write(" published, published_at, requires_shipping, taxable,\n")
251   - f.write(" fake_sales, display_fake_sales, mixed_wholesale, need_variant_image,\n")
252   - f.write(" has_only_default_variant, tags, note, category,\n")
253   - f.write(" shoplazza_created_at, shoplazza_updated_at, tenant_id,\n")
254   - f.write(" creator, create_time, updater, update_time, deleted\n")
255   - f.write(") VALUES\n")
256   -
257   - for i, spu in enumerate(spus):
258   - values = (
259   - f"({spu['id']}, {spu['shop_id']}, '{escape_sql_string(spu['shoplazza_id'])}', "
260   - f"'{escape_sql_string(spu['handle'])}', '{escape_sql_string(spu['title'])}', "
261   - f"'{escape_sql_string(spu['brief'])}', '{escape_sql_string(spu['description'])}', "
262   - f"'{escape_sql_string(spu['spu'])}', '{escape_sql_string(spu['vendor'])}', "
263   - f"'{escape_sql_string(spu['vendor_url'])}', '{escape_sql_string(spu['seo_title'])}', "
264   - f"'{escape_sql_string(spu['seo_description'])}', '{escape_sql_string(spu['seo_keywords'])}', "
265   - f"'{escape_sql_string(spu['image_src'])}', {spu['image_width']}, "
266   - f"{spu['image_height']}, '{escape_sql_string(spu['image_path'])}', "
267   - f"'{escape_sql_string(spu['image_alt'])}', '{escape_sql_string(spu['inventory_policy'])}', "
268   - f"{spu['inventory_quantity']}, '{escape_sql_string(spu['inventory_tracking'])}', "
269   - f"{spu['published']}, '{escape_sql_string(spu['published_at'])}', "
270   - f"{spu['requires_shipping']}, {spu['taxable']}, "
271   - f"{spu['fake_sales']}, {spu['display_fake_sales']}, {spu['mixed_wholesale']}, "
272   - f"{spu['need_variant_image']}, {spu['has_only_default_variant']}, "
273   - f"'{escape_sql_string(spu['tags'])}', '{escape_sql_string(spu['note'])}', "
274   - f"'{escape_sql_string(spu['category'])}', '{escape_sql_string(spu['shoplazza_created_at'])}', "
275   - f"'{escape_sql_string(spu['shoplazza_updated_at'])}', '{escape_sql_string(spu['tenant_id'])}', "
276   - f"'{escape_sql_string(spu['creator'])}', '{escape_sql_string(spu['create_time'])}', "
277   - f"'{escape_sql_string(spu['updater'])}', '{escape_sql_string(spu['update_time'])}', "
278   - f"{spu['deleted']})"
279   - )
280   - f.write(values)
281   - if i < len(spus) - 1:
282   - f.write(",\n")
283   - else:
284   - f.write(";\n\n")
285   -
286   - f.write("-- SKU Test Data\n")
287   - f.write("INSERT INTO shoplazza_product_sku (\n")
288   - f.write(" id, spu_id, shop_id, shoplazza_id, shoplazza_product_id, shoplazza_image_id,\n")
289   - f.write(" title, sku, barcode, position, price, compare_at_price, cost_price,\n")
290   - f.write(" option1, option2, option3, inventory_quantity, weight, weight_unit,\n")
291   - f.write(" image_src, wholesale_price, note, extend,\n")
292   - f.write(" shoplazza_created_at, shoplazza_updated_at, tenant_id,\n")
293   - f.write(" creator, create_time, updater, update_time, deleted\n")
294   - f.write(") VALUES\n")
295   -
296   - for i, sku in enumerate(skus):
297   - # Handle extend field (JSON, can be NULL)
298   - extend_value = 'NULL' if sku['extend'] is None else f"'{escape_sql_string(sku['extend'])}'"
299   -
300   - values = (
301   - f"({sku['id']}, {sku['spu_id']}, {sku['shop_id']}, '{escape_sql_string(sku['shoplazza_id'])}', "
302   - f"'{escape_sql_string(sku['shoplazza_product_id'])}', '{escape_sql_string(sku['shoplazza_image_id'])}', "
303   - f"'{escape_sql_string(sku['title'])}', '{escape_sql_string(sku['sku'])}', "
304   - f"'{escape_sql_string(sku['barcode'])}', {sku['position']}, "
305   - f"{sku['price']}, {sku['compare_at_price']}, {sku['cost_price']}, "
306   - f"'{escape_sql_string(sku['option1'])}', '{escape_sql_string(sku['option2'])}', "
307   - f"'{escape_sql_string(sku['option3'])}', {sku['inventory_quantity']}, {sku['weight']}, "
308   - f"'{escape_sql_string(sku['weight_unit'])}', '{escape_sql_string(sku['image_src'])}', "
309   - f"'{escape_sql_string(sku['wholesale_price'])}', '{escape_sql_string(sku['note'])}', "
310   - f"{extend_value}, '{escape_sql_string(sku['shoplazza_created_at'])}', "
311   - f"'{escape_sql_string(sku['shoplazza_updated_at'])}', '{escape_sql_string(sku['tenant_id'])}', "
312   - f"'{escape_sql_string(sku['creator'])}', '{escape_sql_string(sku['create_time'])}', "
313   - f"'{escape_sql_string(sku['updater'])}', '{escape_sql_string(sku['update_time'])}', "
314   - f"{sku['deleted']})"
315   - )
316   - f.write(values)
317   - if i < len(skus) - 1:
318   - f.write(",\n")
319   - else:
320   - f.write(";\n")
321   -
322   -
323   -def get_max_ids_from_db(db_config=None):
324   - """
325   - Get maximum IDs from database to avoid primary key conflicts.
326   -
327   - Args:
328   - db_config: Optional database config dict with keys: host, port, database, username, password
329   -
330   - Returns:
331   - tuple: (max_spu_id, max_sku_id) or (0, 0) if cannot connect
332   - """
333   - if not db_config:
334   - return 0, 0
335   -
336   - try:
337   - from utils.db_connector import create_db_connection
338   - from sqlalchemy import text
339   -
340   - db_engine = create_db_connection(
341   - host=db_config['host'],
342   - port=db_config['port'],
343   - database=db_config['database'],
344   - username=db_config['username'],
345   - password=db_config['password']
346   - )
347   -
348   - with db_engine.connect() as conn:
349   - result = conn.execute(text('SELECT MAX(id) FROM shoplazza_product_spu'))
350   - max_spu_id = result.scalar() or 0
351   -
352   - result = conn.execute(text('SELECT MAX(id) FROM shoplazza_product_sku'))
353   - max_sku_id = result.scalar() or 0
354   -
355   - return max_spu_id, max_sku_id
356   - except Exception as e:
357   - print(f"Warning: Could not get max IDs from database: {e}")
358   - return 0, 0
359   -
360   -
361   -def main():
362   - parser = argparse.ArgumentParser(description='Generate test data for Shoplazza tables')
363   - parser.add_argument('--num-spus', type=int, default=100, help='Number of SPUs to generate')
364   - parser.add_argument('--tenant-id', default='1', help='Tenant ID')
365   - parser.add_argument('--start-spu-id', type=int, default=None, help='Starting SPU ID (default: auto-calculate from DB)')
366   - parser.add_argument('--start-sku-id', type=int, default=None, help='Starting SKU ID (default: auto-calculate from DB)')
367   - parser.add_argument('--output', default='test_data.sql', help='Output SQL file')
368   - parser.add_argument('--db-host', help='Database host (for auto-calculating start IDs)')
369   - parser.add_argument('--db-port', type=int, default=3306, help='Database port (default: 3306)')
370   - parser.add_argument('--db-database', help='Database name (for auto-calculating start IDs)')
371   - parser.add_argument('--db-username', help='Database username (for auto-calculating start IDs)')
372   - parser.add_argument('--db-password', help='Database password (for auto-calculating start IDs)')
373   -
374   - args = parser.parse_args()
375   -
376   - # Auto-calculate start IDs if not provided and DB config available
377   - start_spu_id = args.start_spu_id
378   - start_sku_id = args.start_sku_id
379   -
380   - if (start_spu_id is None or start_sku_id is None) and args.db_host and args.db_database and args.db_username and args.db_password:
381   - print("Auto-calculating start IDs from database...")
382   - db_config = {
383   - 'host': args.db_host,
384   - 'port': args.db_port,
385   - 'database': args.db_database,
386   - 'username': args.db_username,
387   - 'password': args.db_password
388   - }
389   - max_spu_id, max_sku_id = get_max_ids_from_db(db_config)
390   - if start_spu_id is None:
391   - start_spu_id = max_spu_id + 1
392   - if start_sku_id is None:
393   - start_sku_id = max_sku_id + 1
394   - print(f" Max SPU ID in DB: {max_spu_id}, using start SPU ID: {start_spu_id}")
395   - print(f" Max SKU ID in DB: {max_sku_id}, using start SKU ID: {start_sku_id}")
396   - else:
397   - if start_spu_id is None:
398   - start_spu_id = 1
399   - if start_sku_id is None:
400   - start_sku_id = 1
401   - print(f"Using start SPU ID: {start_spu_id}, start SKU ID: {start_sku_id}")
402   -
403   - print(f"Generating {args.num_spus} SPUs with skus...")
404   -
405   - # Generate SPU data
406   - spus = generate_spu_data(args.num_spus, args.tenant_id, start_spu_id)
407   - print(f"Generated {len(spus)} SPUs")
408   -
409   - # Generate SKU data
410   - skus = generate_sku_data(spus, start_sku_id)
411   - print(f"Generated {len(skus)} SKUs")
412   -
413   - # Generate SQL file
414   - generate_sql_inserts(spus, skus, args.output)
415   - print(f"SQL file generated: {args.output}")
416   -
417   -
418   -if __name__ == '__main__':
419   - import json
420   - main()
421   -
scripts/generate_test_summary.py deleted
... ... @@ -1,179 +0,0 @@
1   -#!/usr/bin/env python3
2   -"""
3   -生成测试摘要脚本
4   -
5   -用于CI/CD流水线中汇总所有测试结果
6   -"""
7   -
8   -import json
9   -import os
10   -import sys
11   -import glob
12   -from pathlib import Path
13   -from datetime import datetime
14   -from typing import Dict, Any, List
15   -
16   -
17   -def collect_test_results() -> Dict[str, Any]:
18   - """收集所有测试结果"""
19   - results = {
20   - 'timestamp': datetime.now().isoformat(),
21   - 'suites': {},
22   - 'summary': {
23   - 'total_tests': 0,
24   - 'passed': 0,
25   - 'failed': 0,
26   - 'skipped': 0,
27   - 'errors': 0,
28   - 'total_duration': 0.0
29   - }
30   - }
31   -
32   - # 查找所有测试结果文件
33   - test_files = glob.glob('*_test_results.json')
34   -
35   - for test_file in test_files:
36   - try:
37   - with open(test_file, 'r', encoding='utf-8') as f:
38   - test_data = json.load(f)
39   -
40   - suite_name = test_file.replace('_test_results.json', '')
41   -
42   - if 'summary' in test_data:
43   - summary = test_data['summary']
44   - results['suites'][suite_name] = {
45   - 'total': summary.get('total', 0),
46   - 'passed': summary.get('passed', 0),
47   - 'failed': summary.get('failed', 0),
48   - 'skipped': summary.get('skipped', 0),
49   - 'errors': summary.get('error', 0),
50   - 'duration': summary.get('duration', 0.0)
51   - }
52   -
53   - # 更新总体统计
54   - results['summary']['total_tests'] += summary.get('total', 0)
55   - results['summary']['passed'] += summary.get('passed', 0)
56   - results['summary']['failed'] += summary.get('failed', 0)
57   - results['summary']['skipped'] += summary.get('skipped', 0)
58   - results['summary']['errors'] += summary.get('error', 0)
59   - results['summary']['total_duration'] += summary.get('duration', 0.0)
60   -
61   - except Exception as e:
62   - print(f"Error reading {test_file}: {e}")
63   - continue
64   -
65   - # 计算成功率
66   - if results['summary']['total_tests'] > 0:
67   - results['summary']['success_rate'] = (
68   - results['summary']['passed'] / results['summary']['total_tests'] * 100
69   - )
70   - else:
71   - results['summary']['success_rate'] = 0.0
72   -
73   - return results
74   -
75   -
76   -def generate_text_report(results: Dict[str, Any]) -> str:
77   - """生成文本格式的测试报告"""
78   - lines = []
79   -
80   - # 标题
81   - lines.append("=" * 60)
82   - lines.append("搜索引擎自动化测试报告")
83   - lines.append("=" * 60)
84   - lines.append(f"时间: {results['timestamp']}")
85   - lines.append("")
86   -
87   - # 摘要
88   - summary = results['summary']
89   - lines.append("📊 测试摘要")
90   - lines.append("-" * 30)
91   - lines.append(f"总测试数: {summary['total_tests']}")
92   - lines.append(f"✅ 通过: {summary['passed']}")
93   - lines.append(f"❌ 失败: {summary['failed']}")
94   - lines.append(f"⏭️ 跳过: {summary['skipped']}")
95   - lines.append(f"🚨 错误: {summary['errors']}")
96   - lines.append(f"📈 成功率: {summary['success_rate']:.1f}%")
97   - lines.append(f"⏱️ 总耗时: {summary['total_duration']:.2f}秒")
98   - lines.append("")
99   -
100   - # 状态判断
101   - if summary['failed'] == 0 and summary['errors'] == 0:
102   - lines.append("🎉 所有测试都通过了!")
103   - else:
104   - lines.append("⚠️ 存在失败的测试,请查看详细日志。")
105   - lines.append("")
106   -
107   - # 各测试套件详情
108   - if results['suites']:
109   - lines.append("📋 测试套件详情")
110   - lines.append("-" * 30)
111   -
112   - for suite_name, suite_data in results['suites'].items():
113   - lines.append(f"\n{suite_name.upper()}:")
114   - lines.append(f" 总数: {suite_data['total']}")
115   - lines.append(f" ✅ 通过: {suite_data['passed']}")
116   - lines.append(f" ❌ 失败: {suite_data['failed']}")
117   - lines.append(f" ⏭️ 跳过: {suite_data['skipped']}")
118   - lines.append(f" 🚨 错误: {suite_data['errors']}")
119   - lines.append(f" ⏱️ 耗时: {suite_data['duration']:.2f}秒")
120   -
121   - # 添加状态图标
122   - if suite_data['failed'] == 0 and suite_data['errors'] == 0:
123   - lines.append(f" 状态: ✅ 全部通过")
124   - else:
125   - lines.append(f" 状态: ❌ 存在问题")
126   -
127   - lines.append("")
128   - lines.append("=" * 60)
129   -
130   - return "\n".join(lines)
131   -
132   -
133   -def generate_json_report(results: Dict[str, Any]) -> str:
134   - """生成JSON格式的测试报告"""
135   - return json.dumps(results, indent=2, ensure_ascii=False)
136   -
137   -
138   -def main():
139   - """主函数"""
140   - # 收集测试结果
141   - print("收集测试结果...")
142   - results = collect_test_results()
143   -
144   - # 生成报告
145   - print("生成测试报告...")
146   - text_report = generate_text_report(results)
147   - json_report = generate_json_report(results)
148   -
149   - # 保存报告
150   - timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
151   -
152   - # 文本报告
153   - text_file = f"final_test_report.txt"
154   - with open(text_file, 'w', encoding='utf-8') as f:
155   - f.write(text_report)
156   -
157   - # JSON报告
158   - json_file = f"final_test_report.json"
159   - with open(json_file, 'w', encoding='utf-8') as f:
160   - f.write(json_report)
161   -
162   - print(f"测试报告已生成:")
163   - print(f" 文本报告: {text_file}")
164   - print(f" JSON报告: {json_file}")
165   -
166   - # 输出摘要到控制台
167   - print("\n" + "=" * 60)
168   - print(text_report)
169   -
170   - # 返回退出码
171   - summary = results['summary']
172   - if summary['failed'] > 0 or summary['errors'] > 0:
173   - return 1
174   - else:
175   - return 0
176   -
177   -
178   -if __name__ == "__main__":
179   - sys.exit(main())
180 0 \ No newline at end of file
scripts/import_tenant2_csv.py renamed to scripts/indexer__old_2025_11/import_tenant2_csv.py
scripts/import_test_data.py renamed to scripts/indexer__old_2025_11/import_test_data.py
scripts/ingest.sh renamed to scripts/indexer__old_2025_11/ingest.sh
scripts/ingest_shoplazza.py renamed to scripts/indexer__old_2025_11/ingest_shoplazza.py
scripts/recreate_and_import.py renamed to scripts/indexer__old_2025_11/recreate_and_import.py
scripts/run_tests.py deleted
... ... @@ -1,705 +0,0 @@
1   -#!/usr/bin/env python3
2   -"""
3   -测试执行脚本
4   -
5   -运行完整的测试流水线,包括:
6   -- 环境检查
7   -- 单元测试
8   -- 集成测试
9   -- 性能测试
10   -- 测试报告生成
11   -"""
12   -
13   -import os
14   -import sys
15   -import subprocess
16   -import time
17   -import json
18   -import argparse
19   -import logging
20   -from pathlib import Path
21   -from typing import Dict, List, Optional, Any
22   -from dataclasses import dataclass, asdict
23   -from datetime import datetime
24   -
25   -
26   -# 添加项目根目录到Python路径
27   -project_root = Path(__file__).parent.parent
28   -sys.path.insert(0, str(project_root))
29   -
30   -
31   -@dataclass
32   -class TestResult:
33   - """测试结果数据结构"""
34   - name: str
35   - status: str # "passed", "failed", "skipped", "error"
36   - duration: float
37   - details: Optional[Dict[str, Any]] = None
38   - output: Optional[str] = None
39   - error: Optional[str] = None
40   -
41   -
42   -@dataclass
43   -class TestSuiteResult:
44   - """测试套件结果"""
45   - name: str
46   - total_tests: int
47   - passed: int
48   - failed: int
49   - skipped: int
50   - errors: int
51   - duration: float
52   - results: List[TestResult]
53   -
54   -
55   -class TestRunner:
56   - """测试运行器"""
57   -
58   - def __init__(self, config: Dict[str, Any]):
59   - self.config = config
60   - self.logger = self._setup_logger()
61   - self.results: List[TestSuiteResult] = []
62   - self.start_time = time.time()
63   -
64   - def _setup_logger(self) -> logging.Logger:
65   - """设置日志记录器"""
66   - log_level = getattr(logging, self.config.get('log_level', 'INFO').upper())
67   - logging.basicConfig(
68   - level=log_level,
69   - format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
70   - handlers=[
71   - logging.StreamHandler(),
72   - logging.FileHandler(
73   - project_root / 'test_logs' / f'test_run_{datetime.now().strftime("%Y%m%d_%H%M%S")}.log'
74   - )
75   - ]
76   - )
77   - return logging.getLogger(__name__)
78   -
79   - def _run_command(self, cmd: List[str], cwd: Optional[Path] = None, env: Optional[Dict[str, str]] = None) -> subprocess.CompletedProcess:
80   - """运行命令"""
81   - try:
82   - self.logger.info(f"执行命令: {' '.join(cmd)}")
83   -
84   - # 设置环境变量
85   - process_env = os.environ.copy()
86   - if env:
87   - process_env.update(env)
88   -
89   - result = subprocess.run(
90   - cmd,
91   - cwd=cwd or project_root,
92   - env=process_env,
93   - capture_output=True,
94   - text=True,
95   - timeout=self.config.get('test_timeout', 300)
96   - )
97   -
98   - self.logger.debug(f"命令返回码: {result.returncode}")
99   - if result.stdout:
100   - self.logger.debug(f"标准输出: {result.stdout[:500]}...")
101   - if result.stderr:
102   - self.logger.debug(f"标准错误: {result.stderr[:500]}...")
103   -
104   - return result
105   -
106   - except subprocess.TimeoutExpired:
107   - self.logger.error(f"命令执行超时: {' '.join(cmd)}")
108   - raise
109   - except Exception as e:
110   - self.logger.error(f"命令执行失败: {e}")
111   - raise
112   -
113   - def check_environment(self) -> bool:
114   - """检查测试环境"""
115   - self.logger.info("检查测试环境...")
116   -
117   - checks = []
118   -
119   - # 检查Python环境
120   - try:
121   - python_version = sys.version
122   - self.logger.info(f"Python版本: {python_version}")
123   - checks.append(("Python", True, f"版本 {python_version}"))
124   - except Exception as e:
125   - checks.append(("Python", False, str(e)))
126   -
127   - # 检查conda环境
128   - try:
129   - result = self._run_command(['conda', '--version'])
130   - if result.returncode == 0:
131   - conda_version = result.stdout.strip()
132   - self.logger.info(f"Conda版本: {conda_version}")
133   - checks.append(("Conda", True, conda_version))
134   - else:
135   - checks.append(("Conda", False, "未找到conda"))
136   - except Exception as e:
137   - checks.append(("Conda", False, str(e)))
138   -
139   - # 检查依赖包
140   - required_packages = [
141   - 'pytest', 'fastapi', 'elasticsearch', 'numpy',
142   - 'torch', 'transformers', 'pyyaml'
143   - ]
144   -
145   - for package in required_packages:
146   - try:
147   - result = self._run_command(['python', '-c', f'import {package}'])
148   - if result.returncode == 0:
149   - checks.append((package, True, "已安装"))
150   - else:
151   - checks.append((package, False, "导入失败"))
152   - except Exception as e:
153   - checks.append((package, False, str(e)))
154   -
155   - # 检查Elasticsearch
156   - try:
157   - es_host = os.getenv('ES_HOST', 'http://localhost:9200')
158   - result = self._run_command(['curl', '-s', f'{es_host}/_cluster/health'])
159   - if result.returncode == 0:
160   - health_data = json.loads(result.stdout)
161   - status = health_data.get('status', 'unknown')
162   - self.logger.info(f"Elasticsearch状态: {status}")
163   - checks.append(("Elasticsearch", True, f"状态: {status}"))
164   - else:
165   - checks.append(("Elasticsearch", False, "连接失败"))
166   - except Exception as e:
167   - checks.append(("Elasticsearch", False, str(e)))
168   -
169   - # 检查API服务
170   - try:
171   - api_host = os.getenv('API_HOST', '127.0.0.1')
172   - api_port = os.getenv('API_PORT', '6003')
173   - result = self._run_command(['curl', '-s', f'http://{api_host}:{api_port}/health'])
174   - if result.returncode == 0:
175   - health_data = json.loads(result.stdout)
176   - status = health_data.get('status', 'unknown')
177   - self.logger.info(f"API服务状态: {status}")
178   - checks.append(("API服务", True, f"状态: {status}"))
179   - else:
180   - checks.append(("API服务", False, "连接失败"))
181   - except Exception as e:
182   - checks.append(("API服务", False, str(e)))
183   -
184   - # 输出检查结果
185   - self.logger.info("环境检查结果:")
186   - all_passed = True
187   - for name, passed, details in checks:
188   - status = "✓" if passed else "✗"
189   - self.logger.info(f" {status} {name}: {details}")
190   - if not passed:
191   - all_passed = False
192   -
193   - return all_passed
194   -
195   - def run_unit_tests(self) -> TestSuiteResult:
196   - """运行单元测试"""
197   - self.logger.info("运行单元测试...")
198   -
199   - start_time = time.time()
200   - cmd = [
201   - 'python', '-m', 'pytest',
202   - 'tests/unit/',
203   - '-v',
204   - '--tb=short',
205   - '--json-report',
206   - '--json-report-file=test_logs/unit_test_results.json'
207   - ]
208   -
209   - try:
210   - result = self._run_command(cmd)
211   - duration = time.time() - start_time
212   -
213   - # 解析测试结果
214   - if result.returncode == 0:
215   - status = "passed"
216   - else:
217   - status = "failed"
218   -
219   - # 尝试解析JSON报告
220   - test_results = []
221   - passed = failed = skipped = errors = 0
222   -
223   - try:
224   - with open(project_root / 'test_logs' / 'unit_test_results.json', 'r') as f:
225   - report_data = json.load(f)
226   -
227   - summary = report_data.get('summary', {})
228   - total = summary.get('total', 0)
229   - passed = summary.get('passed', 0)
230   - failed = summary.get('failed', 0)
231   - skipped = summary.get('skipped', 0)
232   - errors = summary.get('error', 0)
233   -
234   - # 获取详细结果
235   - for test in report_data.get('tests', []):
236   - test_results.append(TestResult(
237   - name=test.get('nodeid', ''),
238   - status=test.get('outcome', 'unknown'),
239   - duration=test.get('duration', 0.0),
240   - details=test
241   - ))
242   -
243   - except Exception as e:
244   - self.logger.warning(f"无法解析单元测试JSON报告: {e}")
245   -
246   - suite_result = TestSuiteResult(
247   - name="单元测试",
248   - total_tests=passed + failed + skipped + errors,
249   - passed=passed,
250   - failed=failed,
251   - skipped=skipped,
252   - errors=errors,
253   - duration=duration,
254   - results=test_results
255   - )
256   -
257   - self.results.append(suite_result)
258   - self.logger.info(f"单元测试完成: {suite_result.total_tests}个测试, "
259   - f"{suite_result.passed}通过, {suite_result.failed}失败, "
260   - f"{suite_result.skipped}跳过, {suite_result.errors}错误")
261   -
262   - return suite_result
263   -
264   - except Exception as e:
265   - self.logger.error(f"单元测试执行失败: {e}")
266   - raise
267   -
268   - def run_integration_tests(self) -> TestSuiteResult:
269   - """运行集成测试"""
270   - self.logger.info("运行集成测试...")
271   -
272   - start_time = time.time()
273   - cmd = [
274   - 'python', '-m', 'pytest',
275   - 'tests/integration/',
276   - '-v',
277   - '--tb=short',
278   - '-m', 'not slow', # 排除慢速测试
279   - '--json-report',
280   - '--json-report-file=test_logs/integration_test_results.json'
281   - ]
282   -
283   - try:
284   - result = self._run_command(cmd)
285   - duration = time.time() - start_time
286   -
287   - # 解析测试结果
288   - if result.returncode == 0:
289   - status = "passed"
290   - else:
291   - status = "failed"
292   -
293   - # 尝试解析JSON报告
294   - test_results = []
295   - passed = failed = skipped = errors = 0
296   -
297   - try:
298   - with open(project_root / 'test_logs' / 'integration_test_results.json', 'r') as f:
299   - report_data = json.load(f)
300   -
301   - summary = report_data.get('summary', {})
302   - total = summary.get('total', 0)
303   - passed = summary.get('passed', 0)
304   - failed = summary.get('failed', 0)
305   - skipped = summary.get('skipped', 0)
306   - errors = summary.get('error', 0)
307   -
308   - for test in report_data.get('tests', []):
309   - test_results.append(TestResult(
310   - name=test.get('nodeid', ''),
311   - status=test.get('outcome', 'unknown'),
312   - duration=test.get('duration', 0.0),
313   - details=test
314   - ))
315   -
316   - except Exception as e:
317   - self.logger.warning(f"无法解析集成测试JSON报告: {e}")
318   -
319   - suite_result = TestSuiteResult(
320   - name="集成测试",
321   - total_tests=passed + failed + skipped + errors,
322   - passed=passed,
323   - failed=failed,
324   - skipped=skipped,
325   - errors=errors,
326   - duration=duration,
327   - results=test_results
328   - )
329   -
330   - self.results.append(suite_result)
331   - self.logger.info(f"集成测试完成: {suite_result.total_tests}个测试, "
332   - f"{suite_result.passed}通过, {suite_result.failed}失败, "
333   - f"{suite_result.skipped}跳过, {suite_result.errors}错误")
334   -
335   - return suite_result
336   -
337   - except Exception as e:
338   - self.logger.error(f"集成测试执行失败: {e}")
339   - raise
340   -
341   - def run_api_tests(self) -> TestSuiteResult:
342   - """运行API测试"""
343   - self.logger.info("运行API测试...")
344   -
345   - start_time = time.time()
346   - cmd = [
347   - 'python', '-m', 'pytest',
348   - 'tests/integration/test_api_integration.py',
349   - '-v',
350   - '--tb=short',
351   - '--json-report',
352   - '--json-report-file=test_logs/api_test_results.json'
353   - ]
354   -
355   - try:
356   - result = self._run_command(cmd)
357   - duration = time.time() - start_time
358   -
359   - # 解析测试结果
360   - if result.returncode == 0:
361   - status = "passed"
362   - else:
363   - status = "failed"
364   -
365   - # 尝试解析JSON报告
366   - test_results = []
367   - passed = failed = skipped = errors = 0
368   -
369   - try:
370   - with open(project_root / 'test_logs' / 'api_test_results.json', 'r') as f:
371   - report_data = json.load(f)
372   -
373   - summary = report_data.get('summary', {})
374   - total = summary.get('total', 0)
375   - passed = summary.get('passed', 0)
376   - failed = summary.get('failed', 0)
377   - skipped = summary.get('skipped', 0)
378   - errors = summary.get('error', 0)
379   -
380   - for test in report_data.get('tests', []):
381   - test_results.append(TestResult(
382   - name=test.get('nodeid', ''),
383   - status=test.get('outcome', 'unknown'),
384   - duration=test.get('duration', 0.0),
385   - details=test
386   - ))
387   -
388   - except Exception as e:
389   - self.logger.warning(f"无法解析API测试JSON报告: {e}")
390   -
391   - suite_result = TestSuiteResult(
392   - name="API测试",
393   - total_tests=passed + failed + skipped + errors,
394   - passed=passed,
395   - failed=failed,
396   - skipped=skipped,
397   - errors=errors,
398   - duration=duration,
399   - results=test_results
400   - )
401   -
402   - self.results.append(suite_result)
403   - self.logger.info(f"API测试完成: {suite_result.total_tests}个测试, "
404   - f"{suite_result.passed}通过, {suite_result.failed}失败, "
405   - f"{suite_result.skipped}跳过, {suite_result.errors}错误")
406   -
407   - return suite_result
408   -
409   - except Exception as e:
410   - self.logger.error(f"API测试执行失败: {e}")
411   - raise
412   -
413   - def run_performance_tests(self) -> TestSuiteResult:
414   - """运行性能测试"""
415   - self.logger.info("运行性能测试...")
416   -
417   - start_time = time.time()
418   -
419   - # 简单的性能测试 - 测试搜索响应时间
420   - test_queries = [
421   - "红色连衣裙",
422   - "智能手机",
423   - "笔记本电脑 AND (游戏 OR 办公)",
424   - "无线蓝牙耳机"
425   - ]
426   -
427   - test_results = []
428   - passed = failed = 0
429   -
430   - for query in test_queries:
431   - try:
432   - query_start = time.time()
433   - result = self._run_command([
434   - 'curl', '-s',
435   - f'http://{os.getenv("API_HOST", "127.0.0.1")}:{os.getenv("API_PORT", "6003")}/search',
436   - '-d', f'q={query}'
437   - ])
438   - query_duration = time.time() - query_start
439   -
440   - if result.returncode == 0:
441   - response_data = json.loads(result.stdout)
442   - took_ms = response_data.get('took_ms', 0)
443   -
444   - # 性能阈值:响应时间不超过2秒
445   - if took_ms <= 2000:
446   - test_results.append(TestResult(
447   - name=f"搜索性能测试: {query}",
448   - status="passed",
449   - duration=query_duration,
450   - details={"took_ms": took_ms, "response_size": len(result.stdout)}
451   - ))
452   - passed += 1
453   - else:
454   - test_results.append(TestResult(
455   - name=f"搜索性能测试: {query}",
456   - status="failed",
457   - duration=query_duration,
458   - details={"took_ms": took_ms, "threshold": 2000}
459   - ))
460   - failed += 1
461   - else:
462   - test_results.append(TestResult(
463   - name=f"搜索性能测试: {query}",
464   - status="failed",
465   - duration=query_duration,
466   - error=result.stderr
467   - ))
468   - failed += 1
469   -
470   - except Exception as e:
471   - test_results.append(TestResult(
472   - name=f"搜索性能测试: {query}",
473   - status="error",
474   - duration=0.0,
475   - error=str(e)
476   - ))
477   - failed += 1
478   -
479   - duration = time.time() - start_time
480   -
481   - suite_result = TestSuiteResult(
482   - name="性能测试",
483   - total_tests=len(test_results),
484   - passed=passed,
485   - failed=failed,
486   - skipped=0,
487   - errors=0,
488   - duration=duration,
489   - results=test_results
490   - )
491   -
492   - self.results.append(suite_result)
493   - self.logger.info(f"性能测试完成: {suite_result.total_tests}个测试, "
494   - f"{suite_result.passed}通过, {suite_result.failed}失败")
495   -
496   - return suite_result
497   -
498   - def generate_report(self) -> str:
499   - """生成测试报告"""
500   - self.logger.info("生成测试报告...")
501   -
502   - # 计算总体统计
503   - total_tests = sum(suite.total_tests for suite in self.results)
504   - total_passed = sum(suite.passed for suite in self.results)
505   - total_failed = sum(suite.failed for suite in self.results)
506   - total_skipped = sum(suite.skipped for suite in self.results)
507   - total_errors = sum(suite.errors for suite in self.results)
508   - total_duration = sum(suite.duration for suite in self.results)
509   -
510   - # 生成报告数据
511   - report_data = {
512   - "timestamp": datetime.now().isoformat(),
513   - "summary": {
514   - "total_tests": total_tests,
515   - "passed": total_passed,
516   - "failed": total_failed,
517   - "skipped": total_skipped,
518   - "errors": total_errors,
519   - "success_rate": (total_passed / total_tests * 100) if total_tests > 0 else 0,
520   - "total_duration": total_duration
521   - },
522   - "suites": [asdict(suite) for suite in self.results]
523   - }
524   -
525   - # 保存JSON报告
526   - report_file = project_root / 'test_logs' / f'test_report_{datetime.now().strftime("%Y%m%d_%H%M%S")}.json'
527   - with open(report_file, 'w', encoding='utf-8') as f:
528   - json.dump(report_data, f, indent=2, ensure_ascii=False)
529   -
530   - # 生成文本报告
531   - text_report = self._generate_text_report(report_data)
532   -
533   - report_file_text = project_root / 'test_logs' / f'test_report_{datetime.now().strftime("%Y%m%d_%H%M%S")}.txt'
534   - with open(report_file_text, 'w', encoding='utf-8') as f:
535   - f.write(text_report)
536   -
537   - self.logger.info(f"测试报告已保存: {report_file}")
538   - self.logger.info(f"文本报告已保存: {report_file_text}")
539   -
540   - return text_report
541   -
542   - def _generate_text_report(self, report_data: Dict[str, Any]) -> str:
543   - """生成文本格式的测试报告"""
544   - lines = []
545   -
546   - # 标题
547   - lines.append("=" * 60)
548   - lines.append("搜索引擎测试报告")
549   - lines.append("=" * 60)
550   - lines.append(f"时间: {report_data['timestamp']}")
551   - lines.append("")
552   -
553   - # 摘要
554   - summary = report_data['summary']
555   - lines.append("测试摘要")
556   - lines.append("-" * 30)
557   - lines.append(f"总测试数: {summary['total_tests']}")
558   - lines.append(f"通过: {summary['passed']}")
559   - lines.append(f"失败: {summary['failed']}")
560   - lines.append(f"跳过: {summary['skipped']}")
561   - lines.append(f"错误: {summary['errors']}")
562   - lines.append(f"成功率: {summary['success_rate']:.1f}%")
563   - lines.append(f"总耗时: {summary['total_duration']:.2f}秒")
564   - lines.append("")
565   -
566   - # 各测试套件详情
567   - lines.append("测试套件详情")
568   - lines.append("-" * 30)
569   -
570   - for suite in report_data['suites']:
571   - lines.append(f"\n{suite['name']}:")
572   - lines.append(f" 总数: {suite['total_tests']}, 通过: {suite['passed']}, "
573   - f"失败: {suite['failed']}, 跳过: {suite['skipped']}, 错误: {suite['errors']}")
574   - lines.append(f" 耗时: {suite['duration']:.2f}秒")
575   -
576   - # 显示失败的测试
577   - failed_tests = [r for r in suite['results'] if r['status'] in ['failed', 'error']]
578   - if failed_tests:
579   - lines.append(" 失败的测试:")
580   - for test in failed_tests[:5]: # 只显示前5个
581   - lines.append(f" - {test['name']}: {test['status']}")
582   - if test.get('error'):
583   - lines.append(f" 错误: {test['error'][:100]}...")
584   - if len(failed_tests) > 5:
585   - lines.append(f" ... 还有 {len(failed_tests) - 5} 个失败的测试")
586   -
587   - return "\n".join(lines)
588   -
589   - def run_all_tests(self) -> bool:
590   - """运行所有测试"""
591   - try:
592   - # 确保日志目录存在
593   - (project_root / 'test_logs').mkdir(exist_ok=True)
594   -
595   - # 加载环境变量
596   - env_file = project_root / 'test_env.sh'
597   - if env_file.exists():
598   - self.logger.info("加载测试环境变量...")
599   - result = self._run_command(['bash', str(env_file)])
600   - if result.returncode != 0:
601   - self.logger.warning("环境变量加载失败,继续使用默认配置")
602   -
603   - # 检查环境
604   - if not self.check_environment():
605   - self.logger.error("环境检查失败,请先启动测试环境")
606   - return False
607   -
608   - # 运行各类测试
609   - test_suites = [
610   - ("unit", self.run_unit_tests),
611   - ("integration", self.run_integration_tests),
612   - ("api", self.run_api_tests),
613   - ("performance", self.run_performance_tests)
614   - ]
615   -
616   - failed_suites = []
617   -
618   - for suite_name, suite_func in test_suites:
619   - if suite_name in self.config.get('skip_suites', []):
620   - self.logger.info(f"跳过 {suite_name} 测试")
621   - continue
622   -
623   - try:
624   - suite_result = suite_func()
625   - if suite_result.failed > 0 or suite_result.errors > 0:
626   - failed_suites.append(suite_name)
627   - except Exception as e:
628   - self.logger.error(f"{suite_name} 测试执行失败: {e}")
629   - failed_suites.append(suite_name)
630   -
631   - # 生成报告
632   - report = self.generate_report()
633   - print(report)
634   -
635   - # 返回测试结果
636   - return len(failed_suites) == 0
637   -
638   - except Exception as e:
639   - self.logger.error(f"测试执行失败: {e}")
640   - return False
641   -
642   -
643   -def main():
644   - """主函数"""
645   - parser = argparse.ArgumentParser(description="运行搜索引擎测试流水线")
646   - parser.add_argument('--skip-suites', nargs='+',
647   - choices=['unit', 'integration', 'api', 'performance'],
648   - help='跳过指定的测试套件')
649   - parser.add_argument('--log-level', choices=['DEBUG', 'INFO', 'WARNING', 'ERROR'],
650   - default='INFO', help='日志级别')
651   - parser.add_argument('--test-timeout', type=int, default=300,
652   - help='单个测试超时时间(秒)')
653   - parser.add_argument('--start-env', action='store_true',
654   - help='启动测试环境后运行测试')
655   - parser.add_argument('--stop-env', action='store_true',
656   - help='测试完成后停止测试环境')
657   -
658   - args = parser.parse_args()
659   -
660   - # 配置
661   - config = {
662   - 'skip_suites': args.skip_suites or [],
663   - 'log_level': args.log_level,
664   - 'test_timeout': args.test_timeout
665   - }
666   -
667   - # 启动环境
668   - if args.start_env:
669   - print("启动测试环境...")
670   - result = subprocess.run([
671   - 'bash', str(project_root / 'scripts' / 'start_test_environment.sh')
672   - ], capture_output=True, text=True)
673   -
674   - if result.returncode != 0:
675   - print(f"测试环境启动失败: {result.stderr}")
676   - return 1
677   -
678   - print("测试环境启动成功")
679   - time.sleep(5) # 等待服务完全启动
680   -
681   - try:
682   - # 运行测试
683   - runner = TestRunner(config)
684   - success = runner.run_all_tests()
685   -
686   - if success:
687   - print("\n🎉 所有测试通过!")
688   - return_code = 0
689   - else:
690   - print("\n❌ 部分测试失败,请查看日志")
691   - return_code = 1
692   -
693   - finally:
694   - # 停止环境
695   - if args.stop_env:
696   - print("\n停止测试环境...")
697   - subprocess.run([
698   - 'bash', str(project_root / 'scripts' / 'stop_test_environment.sh')
699   - ])
700   -
701   - return return_code
702   -
703   -
704   -if __name__ == "__main__":
705   - sys.exit(main())
706 0 \ No newline at end of file
scripts/start_test_environment.sh deleted
... ... @@ -1,275 +0,0 @@
1   -#!/bin/bash
2   -
3   -# 启动测试环境脚本
4   -# 用于在commit前自动化测试时启动必要的依赖服务
5   -
6   -set -e
7   -
8   -# 颜色定义
9   -RED='\033[0;31m'
10   -GREEN='\033[0;32m'
11   -YELLOW='\033[1;33m'
12   -BLUE='\033[0;34m'
13   -NC='\033[0m' # No Color
14   -
15   -# 配置
16   -SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
17   -PROJECT_ROOT="$(dirname "$SCRIPT_DIR")"
18   -TEST_LOG_DIR="$PROJECT_ROOT/test_logs"
19   -PID_FILE="$PROJECT_ROOT/test_environment.pid"
20   -
21   -# 日志文件
22   -LOG_FILE="$TEST_LOG_DIR/test_environment.log"
23   -ES_LOG_FILE="$TEST_LOG_DIR/elasticsearch.log"
24   -API_LOG_FILE="$TEST_LOG_DIR/api_test.log"
25   -
26   -echo -e "${GREEN}========================================${NC}"
27   -echo -e "${GREEN}启动测试环境${NC}"
28   -echo -e "${GREEN}========================================${NC}"
29   -
30   -# 创建日志目录
31   -mkdir -p "$TEST_LOG_DIR"
32   -
33   -# 检查是否已经运行
34   -if [ -f "$PID_FILE" ]; then
35   - OLD_PID=$(cat "$PID_FILE")
36   - if ps -p $OLD_PID > /dev/null 2>&1; then
37   - echo -e "${YELLOW}测试环境已在运行 (PID: $OLD_PID)${NC}"
38   - echo -e "${BLUE}如需重启,请先运行: ./scripts/stop_test_environment.sh${NC}"
39   - exit 0
40   - else
41   - rm -f "$PID_FILE"
42   - fi
43   -fi
44   -
45   -# 激活conda环境
46   -echo -e "${BLUE}激活conda环境...${NC}"
47   -source /home/tw/miniconda3/etc/profile.d/conda.sh
48   -conda activate searchengine
49   -
50   -# 设置环境变量
51   -echo -e "${BLUE}设置测试环境变量...${NC}"
52   -export PYTHONPATH="$PROJECT_ROOT:$PYTHONPATH"
53   -export TESTING_MODE=true
54   -export LOG_LEVEL=DEBUG
55   -
56   -# Elasticsearch配置
57   -export ES_HOST="http://localhost:9200"
58   -export ES_USERNAME="elastic"
59   -export ES_PASSWORD="changeme"
60   -
61   -# API配置
62   -export API_HOST="127.0.0.1"
63   -export API_PORT="6003" # 使用不同的端口避免冲突
64   -export TENANT_ID="test_tenant"
65   -
66   -# 测试配置
67   -export TEST_TIMEOUT=60
68   -export TEST_RETRY_COUNT=3
69   -
70   -echo -e "${BLUE}环境配置:${NC}"
71   -echo " ES_HOST: $ES_HOST"
72   -echo " API_HOST: $API_HOST:$API_PORT"
73   -echo " TENANT_ID: $TENANT_ID"
74   -echo " LOG_LEVEL: $LOG_LEVEL"
75   -echo " TESTING_MODE: $TESTING_MODE"
76   -
77   -# 检查Elasticsearch是否运行
78   -echo -e "${BLUE}检查Elasticsearch状态...${NC}"
79   -if curl -s "$ES_HOST/_cluster/health" > /dev/null; then
80   - echo -e "${GREEN}✓ Elasticsearch正在运行${NC}"
81   -else
82   - echo -e "${YELLOW}⚠ Elasticsearch未运行,尝试启动...${NC}"
83   -
84   - # 尝试启动Elasticsearch(如果安装了本地版本)
85   - if command -v elasticsearch &> /dev/null; then
86   - echo -e "${BLUE}启动本地Elasticsearch...${NC}"
87   - elasticsearch -d -p "$TEST_LOG_DIR/es.pid"
88   - sleep 10
89   -
90   - # 再次检查
91   - if curl -s "$ES_HOST/_cluster/health" > /dev/null; then
92   - echo -e "${GREEN}✓ Elasticsearch启动成功${NC}"
93   - else
94   - echo -e "${RED}✗ Elasticsearch启动失败${NC}"
95   - echo -e "${YELLOW}请手动启动Elasticsearch或配置远程ES地址${NC}"
96   - exit 1
97   - fi
98   - else
99   - echo -e "${RED}✗ 未找到本地Elasticsearch${NC}"
100   - echo -e "${YELLOW}请启动Elasticsearch服务或修改ES_HOST配置${NC}"
101   - exit 1
102   - fi
103   -fi
104   -
105   -# 等待Elasticsearch就绪
106   -echo -e "${BLUE}等待Elasticsearch就绪...${NC}"
107   -for i in {1..30}; do
108   - if curl -s "$ES_HOST/_cluster/health?wait_for_status=yellow&timeout=1s" | grep -q '"status":"green\|yellow"'; then
109   - echo -e "${GREEN}✓ Elasticsearch已就绪${NC}"
110   - break
111   - fi
112   - if [ $i -eq 30 ]; then
113   - echo -e "${RED}✗ Elasticsearch就绪超时${NC}"
114   - exit 1
115   - fi
116   - sleep 1
117   -done
118   -
119   -# 创建测试索引(如果需要)
120   -echo -e "${BLUE}准备测试数据索引...${NC}"
121   -curl -X PUT "$ES_HOST/test_products" -H 'Content-Type: application/json' -d'
122   -{
123   - "settings": {
124   - "number_of_shards": 1,
125   - "number_of_replicas": 0,
126   - "analysis": {
127   - "analyzer": {
128   - "ansj": {
129   - "type": "custom",
130   - "tokenizer": "keyword"
131   - }
132   - }
133   - }
134   - },
135   - "mappings": {
136   - "properties": {
137   - "name": {
138   - "type": "text",
139   - "analyzer": "ansj"
140   - },
141   - "brand_name": {
142   - "type": "text",
143   - "analyzer": "ansj"
144   - },
145   - "tags": {
146   - "type": "text",
147   - "analyzer": "ansj"
148   - },
149   - "price": {
150   - "type": "double"
151   - },
152   - "category_id": {
153   - "type": "integer"
154   - },
155   - "spu_id": {
156   - "type": "keyword"
157   - },
158   - "text_embedding": {
159   - "type": "dense_vector",
160   - "dims": 1024
161   - }
162   - }
163   - }
164   -}' > /dev/null 2>&1 || echo -e "${YELLOW}索引可能已存在${NC}"
165   -
166   -# 插入测试数据
167   -echo -e "${BLUE}插入测试数据...${NC}"
168   -curl -X POST "$ES_HOST/test_products/_bulk" -H 'Content-Type: application/json' -d'
169   -{"index": {"_id": "1"}}
170   -{"name": "红色连衣裙", "brand_name": "测试品牌", "tags": ["红色", "连衣裙", "女装"], "price": 299.0, "category_id": 1, "spu_id": "dress_001"}
171   -{"index": {"_id": "2"}}
172   -{"name": "蓝色连衣裙", "brand_name": "测试品牌", "tags": ["蓝色", "连衣裙", "女装"], "price": 399.0, "category_id": 1, "spu_id": "dress_002"}
173   -{"index": {"_id": "3"}}
174   -{"name": "智能手机", "brand_name": "科技品牌", "tags": ["智能", "手机", "数码"], "price": 2999.0, "category_id": 2, "spu_id": "phone_001"}
175   -{"index": {"_id": "4"}}
176   -{"name": "笔记本电脑", "brand_name": "科技品牌", "tags": ["笔记本", "电脑", "办公"], "price": 5999.0, "category_id": 3, "spu_id": "laptop_001"}
177   -' > /dev/null 2>&1 || echo -e "${YELLOW}测试数据可能已存在${NC}"
178   -
179   -# 启动测试API服务
180   -echo -e "${BLUE}启动测试API服务...${NC}"
181   -cd "$PROJECT_ROOT"
182   -
183   -# 使用后台模式启动API
184   -python -m api.app \
185   - --host $API_HOST \
186   - --port $API_PORT \
187   - --tenant $TENANT_ID \
188   - --es-host $ES_HOST \
189   - > "$API_LOG_FILE" 2>&1 &
190   -
191   -API_PID=$!
192   -echo $API_PID > "$PID_FILE"
193   -
194   -# 等待API服务启动
195   -echo -e "${BLUE}等待API服务启动...${NC}"
196   -for i in {1..30}; do
197   - if curl -s "http://$API_HOST:$API_PORT/health" > /dev/null; then
198   - echo -e "${GREEN}✓ API服务已就绪 (PID: $API_PID)${NC}"
199   - break
200   - fi
201   - if [ $i -eq 30 ]; then
202   - echo -e "${RED}✗ API服务启动超时${NC}"
203   - kill $API_PID 2>/dev/null || true
204   - rm -f "$PID_FILE"
205   - exit 1
206   - fi
207   - sleep 1
208   -done
209   -
210   -# 验证测试环境
211   -echo -e "${BLUE}验证测试环境...${NC}"
212   -
213   -# 测试Elasticsearch连接
214   -if curl -s "$ES_HOST/_cluster/health" | grep -q '"status":"green\|yellow"'; then
215   - echo -e "${GREEN}✓ Elasticsearch连接正常${NC}"
216   -else
217   - echo -e "${RED}✗ Elasticsearch连接失败${NC}"
218   - exit 1
219   -fi
220   -
221   -# 测试API健康检查
222   -if curl -s "http://$API_HOST:$API_PORT/health" | grep -q '"status"'; then
223   - echo -e "${GREEN}✓ API服务健康检查通过${NC}"
224   -else
225   - echo -e "${RED}✗ API服务健康检查失败${NC}"
226   - exit 1
227   -fi
228   -
229   -# 测试基本搜索功能
230   -if curl -s "http://$API_HOST:$API_PORT/search?q=红色连衣裙" | grep -q '"hits"'; then
231   - echo -e "${GREEN}✓ 基本搜索功能正常${NC}"
232   -else
233   - echo -e "${YELLOW}⚠ 基本搜索功能可能有问题,但继续进行${NC}"
234   -fi
235   -
236   -# 输出环境信息
237   -echo -e "${GREEN}========================================${NC}"
238   -echo -e "${GREEN}测试环境启动完成!${NC}"
239   -echo -e "${GREEN}========================================${NC}"
240   -echo -e "${BLUE}服务信息:${NC}"
241   -echo " Elasticsearch: $ES_HOST"
242   -echo " API服务: http://$API_HOST:$API_PORT"
243   -echo " 测试客户: $TENANT_ID"
244   -echo -e "${BLUE}进程信息:${NC}"
245   -echo " API PID: $API_PID"
246   -echo " PID文件: $PID_FILE"
247   -echo -e "${BLUE}日志文件:${NC}"
248   -echo " 环境日志: $LOG_FILE"
249   -echo " API日志: $API_LOG_FILE"
250   -echo " ES日志: $ES_LOG_FILE"
251   -echo -e "${BLUE}测试命令:${NC}"
252   -echo " 运行所有测试: python scripts/run_tests.py"
253   -echo " 单元测试: pytest tests/unit/ -v"
254   -echo " 集成测试: pytest tests/integration/ -v"
255   -echo " API测试: pytest tests/integration/test_api_integration.py -v"
256   -echo "e${NC}"
257   -echo -e "${BLUE}停止环境: ./scripts/stop_test_environment.sh${NC}"
258   -
259   -# 保存环境变量到文件供测试脚本使用
260   -cat > "$PROJECT_ROOT/test_env.sh" << EOF
261   -#!/bin/bash
262   -export ES_HOST="$ES_HOST"
263   -export ES_USERNAME="$ES_USERNAME"
264   -export ES_PASSWORD="$ES_PASSWORD"
265   -export API_HOST="$API_HOST"
266   -export API_PORT="$API_PORT"
267   -export TENANT_ID="$TENANT_ID"
268   -export TESTING_MODE="$TESTING_MODE"
269   -export LOG_LEVEL="$LOG_LEVEL"
270   -export PYTHONPATH="$PROJECT_ROOT:\$PYTHONPATH"
271   -EOF
272   -
273   -chmod +x "$PROJECT_ROOT/test_env.sh"
274   -
275   -echo -e "${GREEN}测试环境已准备就绪!${NC}"
276 0 \ No newline at end of file
scripts/stop_test_environment.sh deleted
... ... @@ -1,82 +0,0 @@
1   -#!/bin/bash
2   -
3   -# 停止测试环境脚本
4   -
5   -set -e
6   -
7   -# 颜色定义
8   -RED='\033[0;31m'
9   -GREEN='\033[0;32m'
10   -YELLOW='\033[1;33m'
11   -BLUE='\033[0;34m'
12   -NC='\033[0m' # No Color
13   -
14   -# 配置
15   -SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
16   -PROJECT_ROOT="$(dirname "$SCRIPT_DIR")"
17   -PID_FILE="$PROJECT_ROOT/test_environment.pid"
18   -ES_PID_FILE="$PROJECT_ROOT/test_logs/es.pid"
19   -
20   -echo -e "${BLUE}========================================${NC}"
21   -echo -e "${BLUE}停止测试环境${NC}"
22   -echo -e "${BLUE}========================================${NC}"
23   -
24   -# 停止API服务
25   -if [ -f "$PID_FILE" ]; then
26   - API_PID=$(cat "$PID_FILE")
27   - if ps -p $API_PID > /dev/null 2>&1; then
28   - echo -e "${BLUE}停止API服务 (PID: $API_PID)...${NC}"
29   - kill $API_PID
30   -
31   - # 等待进程结束
32   - for i in {1..10}; do
33   - if ! ps -p $API_PID > /dev/null 2>&1; then
34   - echo -e "${GREEN}✓ API服务已停止${NC}"
35   - break
36   - fi
37   - if [ $i -eq 10 ]; then
38   - echo -e "${YELLOW}强制停止API服务...${NC}"
39   - kill -9 $API_PID 2>/dev/null || true
40   - fi
41   - sleep 1
42   - done
43   - else
44   - echo -e "${YELLOW}API服务进程不存在${NC}"
45   - fi
46   - rm -f "$PID_FILE"
47   -else
48   - echo -e "${YELLOW}未找到API服务PID文件${NC}"
49   -fi
50   -
51   -# 停止Elasticsearch(如果是本地启动的)
52   -if [ -f "$ES_PID_FILE" ]; then
53   - ES_PID=$(cat "$ES_PID_FILE")
54   - if ps -p $ES_PID > /dev/null 2>&1; then
55   - echo -e "${BLUE}停止本地Elasticsearch (PID: $ES_PID)...${NC}"
56   - kill $ES_PID
57   - rm -f "$ES_PID_FILE"
58   - echo -e "${GREEN}✓ Elasticsearch已停止${NC}"
59   - else
60   - echo -e "${YELLOW}Elasticsearch进程不存在${NC}"
61   - rm -f "$ES_PID_FILE"
62   - fi
63   -else
64   - echo -e "${BLUE}跳过本地Elasticsearch停止(未找到PID文件)${NC}"
65   -fi
66   -
67   -# 清理测试环境文件
68   -echo -e "${BLUE}清理测试环境文件...${NC}"
69   -rm -f "$PROJECT_ROOT/test_env.sh"
70   -
71   -# 清理测试索引(可选)
72   -read -p "是否删除测试索引? (y/N): " -n 1 -r
73   -echo
74   -if [[ $REPLY =~ ^[Yy]$ ]]; then
75   - echo -e "${BLUE}删除测试索引...${NC}"
76   - curl -X DELETE "http://localhost:9200/test_products" 2>/dev/null || true
77   - echo -e "${GREEN}✓ 测试索引已删除${NC}"
78   -fi
79   -
80   -echo -e "${GREEN}========================================${NC}"
81   -echo -e "${GREEN}测试环境已停止!${NC}"
82   -echo -e "${GREEN}========================================${NC}"
83 0 \ No newline at end of file
scripts/test_base.py deleted
... ... @@ -1,242 +0,0 @@
1   -#!/usr/bin/env python3
2   -"""
3   -Test script for base configuration.
4   -
5   -Tests data ingestion, search API, response format, and tenant isolation.
6   -"""
7   -
8   -import sys
9   -import os
10   -import argparse
11   -import requests
12   -import json
13   -from pathlib import Path
14   -
15   -# Add parent directory to path
16   -sys.path.insert(0, str(Path(__file__).parent.parent))
17   -
18   -
19   -def test_search_api(base_url: str, tenant_id: str, query: str = "耳机"):
20   - """
21   - Test search API.
22   -
23   - Args:
24   - base_url: API base URL
25   - tenant_id: Tenant ID
26   - query: Search query
27   -
28   - Returns:
29   - Response JSON or None if failed
30   - """
31   - url = f"{base_url}/search/"
32   - headers = {
33   - "X-Tenant-ID": tenant_id,
34   - "Content-Type": "application/json"
35   - }
36   - payload = {
37   - "query": query,
38   - "size": 10,
39   - "from": 0
40   - }
41   -
42   - print(f"\nTesting search API:")
43   - print(f" URL: {url}")
44   - print(f" Query: {query}")
45   - print(f" Tenant ID: {tenant_id}")
46   -
47   - try:
48   - response = requests.post(url, json=payload, headers=headers, timeout=30)
49   - response.raise_for_status()
50   - data = response.json()
51   -
52   - print(f" Status: {response.status_code}")
53   - print(f" Total: {data.get('total', 0)}")
54   - print(f" Results: {len(data.get('results', []))}")
55   -
56   - return data
57   - except Exception as e:
58   - print(f" ERROR: {e}")
59   - return None
60   -
61   -
62   -def validate_response_format(data: dict):
63   - """
64   - Validate response format.
65   -
66   - Args:
67   - data: Response data
68   -
69   - Returns:
70   - List of validation errors (empty if valid)
71   - """
72   - errors = []
73   -
74   - # Check for results field (not hits)
75   - if 'hits' in data:
76   - errors.append("Response contains 'hits' field (should be 'results')")
77   -
78   - if 'results' not in data:
79   - errors.append("Response missing 'results' field")
80   - else:
81   - results = data['results']
82   - if not isinstance(results, list):
83   - errors.append("'results' should be a list")
84   - else:
85   - # Validate first result structure
86   - if results:
87   - result = results[0]
88   - required_fields = ['spu_id', 'title', 'skus', 'relevance_score']
89   - for field in required_fields:
90   - if field not in result:
91   - errors.append(f"Result missing required field: {field}")
92   -
93   - # Check for ES internal fields
94   - es_internal_fields = ['_id', '_score', '_source']
95   - for field in es_internal_fields:
96   - if field in result:
97   - errors.append(f"Result contains ES internal field: {field}")
98   -
99   - # Validate skus
100   - if 'skus' in result:
101   - skus = result['skus']
102   - if not isinstance(skus, list):
103   - errors.append("'skus' should be a list")
104   - elif skus:
105   - sku = skus[0]
106   - sku_required = ['sku_id', 'price', 'sku', 'stock']
107   - for field in sku_required:
108   - if field not in sku:
109   - errors.append(f"SKU missing required field: {field}")
110   -
111   - # Check for suggestions and related_searches
112   - if 'suggestions' not in data:
113   - errors.append("Response missing 'suggestions' field")
114   - if 'related_searches' not in data:
115   - errors.append("Response missing 'related_searches' field")
116   -
117   - return errors
118   -
119   -
120   -def test_facets(base_url: str, tenant_id: str):
121   - """
122   - Test facets aggregation.
123   -
124   - Args:
125   - base_url: API base URL
126   - tenant_id: Tenant ID
127   -
128   - Returns:
129   - Response JSON or None if failed
130   - """
131   - url = f"{base_url}/search/"
132   - headers = {
133   - "X-Tenant-ID": tenant_id,
134   - "Content-Type": "application/json"
135   - }
136   - payload = {
137   - "query": "商品",
138   - "size": 10,
139   - "facets": ["category.keyword", "vendor.keyword"]
140   - }
141   -
142   - print(f"\nTesting facets:")
143   - print(f" Facets: {payload['facets']}")
144   -
145   - try:
146   - response = requests.post(url, json=payload, headers=headers, timeout=30)
147   - response.raise_for_status()
148   - data = response.json()
149   -
150   - if 'facets' in data and data['facets']:
151   - print(f" Facets returned: {len(data['facets'])}")
152   - for facet in data['facets']:
153   - print(f" - {facet.get('field')}: {len(facet.get('values', []))} values")
154   - else:
155   - print(" WARNING: No facets returned")
156   -
157   - return data
158   - except Exception as e:
159   - print(f" ERROR: {e}")
160   - return None
161   -
162   -
163   -def test_tenant_isolation(base_url: str, tenant_id_1: str, tenant_id_2: str):
164   - """
165   - Test tenant isolation.
166   -
167   - Args:
168   - base_url: API base URL
169   - tenant_id_1: First tenant ID
170   - tenant_id_2: Second tenant ID
171   - """
172   - print(f"\nTesting tenant isolation:")
173   - print(f" Tenant 1: {tenant_id_1}")
174   - print(f" Tenant 2: {tenant_id_2}")
175   -
176   - # Search for tenant 1
177   - data1 = test_search_api(base_url, tenant_id_1, "商品")
178   - # Search for tenant 2
179   - data2 = test_search_api(base_url, tenant_id_2, "商品")
180   -
181   - if data1 and data2:
182   - results1 = set(r.get('spu_id') for r in data1.get('results', []))
183   - results2 = set(r.get('spu_id') for r in data2.get('results', []))
184   -
185   - overlap = results1 & results2
186   - if overlap:
187   - print(f" WARNING: Found {len(overlap)} overlapping results between tenants")
188   - else:
189   - print(f" OK: No overlapping results (tenant isolation working)")
190   -
191   -
192   -def main():
193   - parser = argparse.ArgumentParser(description='Test base configuration')
194   - parser.add_argument('--api-url', default='http://localhost:8000', help='API base URL')
195   - parser.add_argument('--tenant-id', default='1', help='Tenant ID for testing')
196   - parser.add_argument('--test-tenant-2', help='Second tenant ID for isolation test')
197   -
198   - args = parser.parse_args()
199   -
200   - print("=" * 60)
201   - print("Base Configuration Test Suite")
202   - print("=" * 60)
203   -
204   - # Test 1: Basic search
205   - print("\n[Test 1] Basic Search")
206   - data = test_search_api(args.api_url, args.tenant_id)
207   - if not data:
208   - print("FAILED: Basic search test")
209   - return 1
210   -
211   - # Test 2: Response format validation
212   - print("\n[Test 2] Response Format Validation")
213   - errors = validate_response_format(data)
214   - if errors:
215   - print("FAILED: Response format validation")
216   - for error in errors:
217   - print(f" - {error}")
218   - return 1
219   - else:
220   - print("PASSED: Response format is correct")
221   -
222   - # Test 3: Facets
223   - print("\n[Test 3] Facets Aggregation")
224   - facet_data = test_facets(args.api_url, args.tenant_id)
225   - if not facet_data:
226   - print("WARNING: Facets test failed (may be expected if no data)")
227   -
228   - # Test 4: Tenant isolation (if second tenant provided)
229   - if args.test_tenant_2:
230   - print("\n[Test 4] Tenant Isolation")
231   - test_tenant_isolation(args.api_url, args.tenant_id, args.test_tenant_2)
232   -
233   - print("\n" + "=" * 60)
234   - print("All tests completed")
235   - print("=" * 60)
236   -
237   - return 0
238   -
239   -
240   -if __name__ == '__main__':
241   - sys.exit(main())
242   -
scripts/test_frontend.sh deleted
... ... @@ -1,94 +0,0 @@
1   -#!/bin/bash
2   -
3   -# Test Frontend - Quick verification script
4   -
5   -set -e
6   -
7   -GREEN='\033[0;32m'
8   -YELLOW='\033[1;33m'
9   -RED='\033[0;31m'
10   -NC='\033[0m'
11   -
12   -API_URL="http://120.76.41.98:6002"
13   -
14   -echo -e "${GREEN}========================================${NC}"
15   -echo -e "${GREEN}Frontend Test Script${NC}"
16   -echo -e "${GREEN}========================================${NC}"
17   -
18   -echo -e "\n${YELLOW}Testing API endpoints...${NC}"
19   -
20   -# Test 1: Health check
21   -echo -e "\n1. Testing health endpoint..."
22   -if curl -s "${API_URL}/health" > /dev/null; then
23   - echo -e "${GREEN}✓ Health check passed${NC}"
24   -else
25   - echo -e "${RED}✗ Health check failed${NC}"
26   - exit 1
27   -fi
28   -
29   -# Test 2: Frontend HTML
30   -echo -e "\n2. Testing frontend HTML..."
31   -if curl -s "${API_URL}/" | grep -q "Product Search"; then
32   - echo -e "${GREEN}✓ Frontend HTML accessible${NC}"
33   -else
34   - echo -e "${RED}✗ Frontend HTML not found${NC}"
35   - exit 1
36   -fi
37   -
38   -# Test 3: Static CSS
39   -echo -e "\n3. Testing static CSS..."
40   -if curl -s "${API_URL}/static/css/style.css" | grep -q "page-container"; then
41   - echo -e "${GREEN}✓ CSS file accessible${NC}"
42   -else
43   - echo -e "${RED}✗ CSS file not found${NC}"
44   - exit 1
45   -fi
46   -
47   -# Test 4: Static JS
48   -echo -e "\n4. Testing static JavaScript..."
49   -if curl -s "${API_URL}/static/js/app.js" | grep -q "performSearch"; then
50   - echo -e "${GREEN}✓ JavaScript file accessible${NC}"
51   -else
52   - echo -e "${RED}✗ JavaScript file not found${NC}"
53   - exit 1
54   -fi
55   -
56   -# Test 5: Search API
57   -echo -e "\n5. Testing search API..."
58   -SEARCH_RESULT=$(curl -s -X POST "${API_URL}/search/" \
59   - -H "Content-Type: application/json" \
60   - -d '{"query":"玩具","size":5}')
61   -
62   -if echo "$SEARCH_RESULT" | grep -q "hits"; then
63   - echo -e "${GREEN}✓ Search API working${NC}"
64   - TOTAL=$(echo "$SEARCH_RESULT" | grep -o '"total":[0-9]*' | cut -d: -f2)
65   - echo -e " Found ${YELLOW}${TOTAL}${NC} results"
66   -else
67   - echo -e "${RED}✗ Search API failed${NC}"
68   - exit 1
69   -fi
70   -
71   -echo -e "\n${GREEN}========================================${NC}"
72   -echo -e "${GREEN}All tests passed! ✓${NC}"
73   -echo -e "${GREEN}========================================${NC}"
74   -
75   -echo -e "\n${YELLOW}Frontend is ready!${NC}"
76   -echo -e "Open in browser: ${GREEN}${API_URL}/${NC}"
77   -
78   -echo -e "\n${YELLOW}Quick Start Guide:${NC}"
79   -echo "1. Open browser and go to: ${API_URL}/"
80   -echo "2. Enter a search query (e.g., '玩具')"
81   -echo "3. Click on filter tags to refine results"
82   -echo "4. Use sort buttons with arrows to sort"
83   -echo "5. Use pagination at the bottom to browse"
84   -
85   -echo -e "\n${YELLOW}Key Features:${NC}"
86   -echo "- Clean white background design"
87   -echo "- Horizontal filter tags (categories, brands, suppliers)"
88   -echo "- Sort buttons with up/down arrows for ascending/descending"
89   -echo "- Product grid with images, prices, MOQ info"
90   -echo "- Full pagination support"
91   -echo "- Responsive design for mobile and desktop"
92   -
93   -echo -e "\n${GREEN}Enjoy your new frontend! 🎉${NC}"
94   -
scripts/test_cloud_embedding.py renamed to tests/test_cloud_embedding.py
scripts/test_cnclip_service.py renamed to tests/test_cnclip_service.py
scripts/test_facet_api.py renamed to tests/test_facet_api.py