sophhub 0.4.21 → 0.4.22
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/skills/insurance-customer-policy/skill.json +12 -0
- package/skills/insurance-customer-policy/src/SKILL.md +121 -0
- package/skills/insurance-customer-policy/src/pyproject.toml +9 -0
- package/skills/insurance-customer-policy/src/scripts/cli.py +16 -0
- package/skills/insurance-customer-policy/src/scripts/cloud_insurance_full_test.py +785 -0
- package/skills/insurance-customer-policy/src/scripts/dashboard_all.py +205 -0
- package/skills/insurance-customer-policy/src/scripts/insurance_customer_cli/__init__.py +0 -0
- package/skills/insurance-customer-policy/src/scripts/insurance_customer_cli/__main__.py +4 -0
- package/skills/insurance-customer-policy/src/scripts/insurance_customer_cli/cli.py +816 -0
- package/skills/insurance-customer-policy/src/scripts/insurance_customer_cli/db.py +181 -0
- package/skills/insurance-customer-policy/src/scripts/insurance_customer_cli/insurance_paths.py +184 -0
- package/skills/insurance-customer-policy/src/scripts/run_e2e_smoke.py +164 -0
- package/skills/insurance-customer-policy/src/scripts/test_cloud_zero_config.py +217 -0
- package/skills/insurance-customer-policy/src/scripts/test_dashboard_all.py +113 -0
- package/skills/insurance-customer-policy/src/src/insurance_customer_cli/__init__.py +0 -0
- package/skills/insurance-customer-policy/src/src/insurance_customer_cli/__main__.py +4 -0
- package/skills/insurance-customer-policy/src/src/insurance_customer_cli/cli.py +816 -0
- package/skills/insurance-customer-policy/src/src/insurance_customer_cli/db.py +181 -0
- package/skills/insurance-customer-policy/src/src/insurance_customer_cli/insurance_paths.py +184 -0
- package/skills/insurance-product-analysis/skill.json +12 -0
- package/skills/insurance-product-analysis/src/SKILL.md +99 -0
- package/skills/insurance-product-analysis/src/pyproject.toml +9 -0
- package/skills/insurance-product-analysis/src/scripts/cli.py +16 -0
- package/skills/insurance-product-analysis/src/scripts/insurance_product_cli/__init__.py +0 -0
- package/skills/insurance-product-analysis/src/scripts/insurance_product_cli/__main__.py +4 -0
- package/skills/insurance-product-analysis/src/scripts/insurance_product_cli/cli.py +545 -0
- package/skills/insurance-product-analysis/src/scripts/insurance_product_cli/db.py +180 -0
- package/skills/insurance-product-analysis/src/scripts/insurance_product_cli/orchestrator.py +163 -0
- package/skills/insurance-product-analysis/src/scripts/run_e2e_smoke.py +202 -0
- package/skills/insurance-product-analysis/src/src/insurance_product_cli/__init__.py +0 -0
- package/skills/insurance-product-analysis/src/src/insurance_product_cli/__main__.py +4 -0
- package/skills/insurance-product-analysis/src/src/insurance_product_cli/cli.py +545 -0
- package/skills/insurance-product-analysis/src/src/insurance_product_cli/db.py +180 -0
- package/skills/insurance-product-analysis/src/src/insurance_product_cli/orchestrator.py +163 -0
- package/skills/insurance-sales-pipeline/skill.json +12 -0
- package/skills/insurance-sales-pipeline/src/SKILL.md +102 -0
- package/skills/insurance-sales-pipeline/src/pyproject.toml +9 -0
- package/skills/insurance-sales-pipeline/src/scripts/cli.py +16 -0
- package/skills/insurance-sales-pipeline/src/scripts/insurance_pipeline_cli/__init__.py +0 -0
- package/skills/insurance-sales-pipeline/src/scripts/insurance_pipeline_cli/__main__.py +4 -0
- package/skills/insurance-sales-pipeline/src/scripts/insurance_pipeline_cli/cli.py +496 -0
- package/skills/insurance-sales-pipeline/src/scripts/insurance_pipeline_cli/db.py +180 -0
- package/skills/insurance-sales-pipeline/src/scripts/insurance_pipeline_cli/orchestrator.py +36 -0
- package/skills/insurance-sales-pipeline/src/scripts/run_e2e_smoke.py +208 -0
- package/skills/insurance-sales-pipeline/src/src/insurance_pipeline_cli/__init__.py +0 -0
- package/skills/insurance-sales-pipeline/src/src/insurance_pipeline_cli/__main__.py +4 -0
- package/skills/insurance-sales-pipeline/src/src/insurance_pipeline_cli/cli.py +496 -0
- package/skills/insurance-sales-pipeline/src/src/insurance_pipeline_cli/db.py +180 -0
- package/skills/insurance-sales-pipeline/src/src/insurance_pipeline_cli/orchestrator.py +36 -0
- package/skills/insurance-schedule-renewal/skill.json +12 -0
- package/skills/insurance-schedule-renewal/src/SKILL.md +94 -0
- package/skills/insurance-schedule-renewal/src/pyproject.toml +9 -0
- package/skills/insurance-schedule-renewal/src/scripts/cli.py +16 -0
- package/skills/insurance-schedule-renewal/src/scripts/insurance_schedule_cli/__init__.py +0 -0
- package/skills/insurance-schedule-renewal/src/scripts/insurance_schedule_cli/__main__.py +4 -0
- package/skills/insurance-schedule-renewal/src/scripts/insurance_schedule_cli/cli.py +429 -0
- package/skills/insurance-schedule-renewal/src/scripts/insurance_schedule_cli/db.py +180 -0
- package/skills/insurance-schedule-renewal/src/scripts/insurance_schedule_cli/orchestrator.py +94 -0
- package/skills/insurance-schedule-renewal/src/scripts/run_e2e_smoke.py +218 -0
- package/skills/insurance-schedule-renewal/src/src/insurance_schedule_cli/__init__.py +0 -0
- package/skills/insurance-schedule-renewal/src/src/insurance_schedule_cli/__main__.py +4 -0
- package/skills/insurance-schedule-renewal/src/src/insurance_schedule_cli/cli.py +429 -0
- package/skills/insurance-schedule-renewal/src/src/insurance_schedule_cli/db.py +180 -0
- package/skills/insurance-schedule-renewal/src/src/insurance_schedule_cli/orchestrator.py +94 -0
- package/skills/insurance-shared-data/skill.json +20 -0
- package/skills/insurance-shared-data/src/SKILL.md +33 -0
- package/skills/insurance-shared-data/src/scripts/cloud_insurance_super_test.py +246 -0
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
"""Shared-db read helpers for insurance-schedule-renewal."""
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
|
|
4
|
+
from insurance_schedule_cli import db
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def policy_query(expire_before: str | None = None,
|
|
8
|
+
next_pay_before: str | None = None,
|
|
9
|
+
customer_id: str | None = None) -> dict:
|
|
10
|
+
conn = db.connect()
|
|
11
|
+
try:
|
|
12
|
+
sql = "SELECT * FROM policies WHERE status='active'"
|
|
13
|
+
vals: list[str] = []
|
|
14
|
+
if customer_id:
|
|
15
|
+
sql += " AND customer_id=?"
|
|
16
|
+
vals.append(customer_id)
|
|
17
|
+
if expire_before:
|
|
18
|
+
sql += " AND expire_date<>'' AND expire_date IS NOT NULL AND expire_date<=?"
|
|
19
|
+
vals.append(expire_before)
|
|
20
|
+
if next_pay_before:
|
|
21
|
+
sql += " AND next_pay_date<>'' AND next_pay_date IS NOT NULL AND next_pay_date<=?"
|
|
22
|
+
vals.append(next_pay_before)
|
|
23
|
+
sql += " ORDER BY effective_date DESC"
|
|
24
|
+
rows = list(conn.execute(sql, tuple(vals)))
|
|
25
|
+
return {"ok": True, "policies": [dict(r) for r in rows], "count": len(rows)}
|
|
26
|
+
finally:
|
|
27
|
+
conn.close()
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def customer_query(customer_id: str | None = None) -> dict:
|
|
31
|
+
conn = db.connect()
|
|
32
|
+
try:
|
|
33
|
+
if customer_id:
|
|
34
|
+
rows = list(
|
|
35
|
+
conn.execute("SELECT * FROM customers WHERE id=? AND status='active'", (customer_id,))
|
|
36
|
+
)
|
|
37
|
+
else:
|
|
38
|
+
rows = list(conn.execute("SELECT * FROM customers WHERE status='active' ORDER BY created_at DESC"))
|
|
39
|
+
return {"ok": True, "customers": [dict(r) for r in rows], "count": len(rows)}
|
|
40
|
+
finally:
|
|
41
|
+
conn.close()
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def followup_query(customer_id: str | None = None, days: int | None = None) -> dict:
|
|
45
|
+
conn = db.connect()
|
|
46
|
+
try:
|
|
47
|
+
if customer_id:
|
|
48
|
+
rows = list(
|
|
49
|
+
conn.execute(
|
|
50
|
+
"SELECT * FROM followups WHERE customer_id=? ORDER BY created_at DESC",
|
|
51
|
+
(customer_id,),
|
|
52
|
+
)
|
|
53
|
+
)
|
|
54
|
+
elif days is not None:
|
|
55
|
+
rows = list(
|
|
56
|
+
conn.execute(
|
|
57
|
+
"SELECT * FROM followups WHERE created_at >= datetime('now', ?) ORDER BY created_at DESC",
|
|
58
|
+
(f"-{int(days)} days",),
|
|
59
|
+
)
|
|
60
|
+
)
|
|
61
|
+
else:
|
|
62
|
+
rows = list(conn.execute("SELECT * FROM followups ORDER BY created_at DESC"))
|
|
63
|
+
return {"ok": True, "followups": [dict(r) for r in rows], "count": len(rows)}
|
|
64
|
+
finally:
|
|
65
|
+
conn.close()
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
def gap_analysis(customer_id: str) -> dict:
|
|
69
|
+
conn = db.connect()
|
|
70
|
+
try:
|
|
71
|
+
row = conn.execute(
|
|
72
|
+
"SELECT id, name FROM customers WHERE id=? AND status='active'",
|
|
73
|
+
(customer_id,),
|
|
74
|
+
).fetchone()
|
|
75
|
+
if not row:
|
|
76
|
+
return {"ok": False, "error": "customer_not_found"}
|
|
77
|
+
need_types = ["重疾险", "医疗险", "意外险", "寿险", "养老险", "教育金"]
|
|
78
|
+
owned = {
|
|
79
|
+
r["product_type"]
|
|
80
|
+
for r in conn.execute(
|
|
81
|
+
"SELECT DISTINCT product_type FROM policies WHERE customer_id=? AND status='active'",
|
|
82
|
+
(customer_id,),
|
|
83
|
+
)
|
|
84
|
+
if r["product_type"]
|
|
85
|
+
}
|
|
86
|
+
result = {
|
|
87
|
+
"customer_id": row["id"],
|
|
88
|
+
"name": row["name"],
|
|
89
|
+
"owned_types": sorted(owned),
|
|
90
|
+
"gaps": [t for t in need_types if t not in owned],
|
|
91
|
+
}
|
|
92
|
+
return {"ok": True, "result": result}
|
|
93
|
+
finally:
|
|
94
|
+
conn.close()
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "insurance-shared-data",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"types": [
|
|
5
|
+
"store"
|
|
6
|
+
],
|
|
7
|
+
"displayName": "保险统一数据底座",
|
|
8
|
+
"description": "保险系列套件入口:内置共享单库 insurance.sqlite3 的首次自动初始化,insurance-customer-policy / insurance-product-analysis / insurance-sales-pipeline / insurance-schedule-renewal 共用一套数据库。",
|
|
9
|
+
"changelog": [
|
|
10
|
+
{
|
|
11
|
+
"version": "1.0.0",
|
|
12
|
+
"date": "2026-05-12",
|
|
13
|
+
"changes": [
|
|
14
|
+
"初次提交:保险系列统一数据底座入口 skill(共享库自动初始化与路径约定说明)"
|
|
15
|
+
]
|
|
16
|
+
}
|
|
17
|
+
],
|
|
18
|
+
"createdAt": "2026-05-12",
|
|
19
|
+
"updatedAt": "2026-05-12"
|
|
20
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: insurance-shared-data
|
|
3
|
+
description: "保险套件入口:内置共享单库 insurance.sqlite3 的首次自动初始化。"
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# 保险统一数据底座入口
|
|
7
|
+
|
|
8
|
+
安装本 skill 后,`insurance-*` 系列会在首次运行时自动初始化共享单库 `insurance.sqlite3`(自动建库建表),用户无需手动配置环境变量,也不需要手动建表。
|
|
9
|
+
|
|
10
|
+
## 共享数据库位置
|
|
11
|
+
|
|
12
|
+
- Windows:`%USERPROFILE%\.config\insurance\insurance.sqlite3`
|
|
13
|
+
- Linux/macOS:`$HOME/.config/insurance/insurance.sqlite3`
|
|
14
|
+
|
|
15
|
+
## 共享范围
|
|
16
|
+
|
|
17
|
+
以下保险 skill 共用同一个数据库:
|
|
18
|
+
|
|
19
|
+
- `insurance-customer-policy`
|
|
20
|
+
- `insurance-product-analysis`
|
|
21
|
+
- `insurance-sales-pipeline`
|
|
22
|
+
- `insurance-schedule-renewal`
|
|
23
|
+
|
|
24
|
+
## 可选环境变量(一般无需配置)
|
|
25
|
+
|
|
26
|
+
- `INSURANCE_DB_PATH`:统一覆盖共享数据库文件路径。
|
|
27
|
+
|
|
28
|
+
兼容历史变量(已有旧部署时仍可生效):
|
|
29
|
+
|
|
30
|
+
- `INSURANCE_CUSTOMER_DB_PATH`
|
|
31
|
+
- `INSURANCE_PRODUCT_DB_PATH`
|
|
32
|
+
- `INSURANCE_PIPELINE_DB_PATH`
|
|
33
|
+
- `INSURANCE_SCHEDULE_DB_PATH`
|
|
@@ -0,0 +1,246 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
"""保险系列云端超级回归测试(失败不中断版)。
|
|
4
|
+
|
|
5
|
+
目标:
|
|
6
|
+
1) 尽可能覆盖所有保险 skill 功能;
|
|
7
|
+
2) 任一子测试失败不会阻塞后续测试;
|
|
8
|
+
3) 最终输出统一汇总报告与日志目录,方便一次定位多处问题。
|
|
9
|
+
|
|
10
|
+
推荐在云端执行:
|
|
11
|
+
python3 /app/skills/insurance-shared-data/src/scripts/cloud_insurance_super_test.py
|
|
12
|
+
或(扁平安装):
|
|
13
|
+
python3 /app/skills/insurance-shared-data/scripts/cloud_insurance_super_test.py
|
|
14
|
+
|
|
15
|
+
可选参数:
|
|
16
|
+
--skills-root /app/skills
|
|
17
|
+
--artifacts-dir /tmp/insurance-super-test
|
|
18
|
+
--skip-full
|
|
19
|
+
--always-zero
|
|
20
|
+
"""
|
|
21
|
+
from __future__ import annotations
|
|
22
|
+
|
|
23
|
+
import argparse
|
|
24
|
+
import json
|
|
25
|
+
import os
|
|
26
|
+
import subprocess
|
|
27
|
+
import sys
|
|
28
|
+
import time
|
|
29
|
+
from dataclasses import dataclass
|
|
30
|
+
from pathlib import Path
|
|
31
|
+
from typing import Any
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
@dataclass
|
|
35
|
+
class StepResult:
|
|
36
|
+
name: str
|
|
37
|
+
ok: bool
|
|
38
|
+
returncode: int
|
|
39
|
+
seconds: float
|
|
40
|
+
stdout_file: str
|
|
41
|
+
stderr_file: str
|
|
42
|
+
error: str = ""
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def _discover_skills_root(explicit: str | None) -> Path:
|
|
46
|
+
if explicit:
|
|
47
|
+
p = Path(explicit).expanduser().resolve()
|
|
48
|
+
if p.is_dir():
|
|
49
|
+
return p
|
|
50
|
+
raise SystemExit(f"[FAIL] --skills-root 不存在: {p}")
|
|
51
|
+
|
|
52
|
+
env = os.environ.get("INSURANCE_SKILLS_ROOT") or os.environ.get("SKILLS_ROOT")
|
|
53
|
+
if env:
|
|
54
|
+
p = Path(env).expanduser().resolve()
|
|
55
|
+
if p.is_dir():
|
|
56
|
+
return p
|
|
57
|
+
raise SystemExit(f"[FAIL] INSURANCE_SKILLS_ROOT/SKILLS_ROOT 不存在: {p}")
|
|
58
|
+
|
|
59
|
+
here = Path(__file__).resolve()
|
|
60
|
+
for c in [here.parents[3], Path("/app/skills"), Path.cwd() / "skills", Path.cwd()]:
|
|
61
|
+
try:
|
|
62
|
+
if (c / "insurance-customer-policy").is_dir():
|
|
63
|
+
return c.resolve()
|
|
64
|
+
except OSError:
|
|
65
|
+
continue
|
|
66
|
+
raise SystemExit("[FAIL] 无法定位 skills 根目录,请显式传入 --skills-root 或设置 INSURANCE_SKILLS_ROOT")
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def _must_file(p: Path) -> None:
|
|
70
|
+
if not p.is_file():
|
|
71
|
+
raise SystemExit(f"[FAIL] 文件不存在: {p}")
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def _resolve_script(skills_root: Path, skill: str, script_name: str) -> Path:
|
|
75
|
+
"""Resolve script path in both source and flattened layouts."""
|
|
76
|
+
candidates = [
|
|
77
|
+
skills_root / skill / "src" / "scripts" / script_name,
|
|
78
|
+
skills_root / skill / "scripts" / script_name,
|
|
79
|
+
Path("/app/skills") / skill / "src" / "scripts" / script_name,
|
|
80
|
+
Path("/app/skills") / skill / "scripts" / script_name,
|
|
81
|
+
]
|
|
82
|
+
for p in candidates:
|
|
83
|
+
try:
|
|
84
|
+
if p.is_file():
|
|
85
|
+
return p
|
|
86
|
+
except OSError:
|
|
87
|
+
continue
|
|
88
|
+
tried = "\n - ".join(str(x) for x in candidates)
|
|
89
|
+
raise SystemExit(f"[FAIL] 找不到脚本 {skill}/{script_name},已尝试:\n - {tried}")
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
def _write_text(path: Path, txt: str) -> None:
|
|
93
|
+
path.parent.mkdir(parents=True, exist_ok=True)
|
|
94
|
+
path.write_text(txt, encoding="utf-8")
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
def _run_step(
|
|
98
|
+
name: str,
|
|
99
|
+
cmd: list[str],
|
|
100
|
+
*,
|
|
101
|
+
env: dict[str, str],
|
|
102
|
+
cwd: Path,
|
|
103
|
+
logs_dir: Path,
|
|
104
|
+
timeout: int = 1800,
|
|
105
|
+
) -> StepResult:
|
|
106
|
+
safe = "".join(ch if ch.isalnum() or ch in ("-", "_") else "_" for ch in name).strip("_")
|
|
107
|
+
out_file = logs_dir / f"{safe}.stdout.log"
|
|
108
|
+
err_file = logs_dir / f"{safe}.stderr.log"
|
|
109
|
+
t0 = time.time()
|
|
110
|
+
try:
|
|
111
|
+
r = subprocess.run(
|
|
112
|
+
cmd,
|
|
113
|
+
env=env,
|
|
114
|
+
cwd=str(cwd),
|
|
115
|
+
capture_output=True,
|
|
116
|
+
text=True,
|
|
117
|
+
encoding="utf-8",
|
|
118
|
+
timeout=timeout,
|
|
119
|
+
)
|
|
120
|
+
_write_text(out_file, r.stdout or "")
|
|
121
|
+
_write_text(err_file, r.stderr or "")
|
|
122
|
+
return StepResult(
|
|
123
|
+
name=name,
|
|
124
|
+
ok=(r.returncode == 0),
|
|
125
|
+
returncode=r.returncode,
|
|
126
|
+
seconds=round(time.time() - t0, 2),
|
|
127
|
+
stdout_file=str(out_file),
|
|
128
|
+
stderr_file=str(err_file),
|
|
129
|
+
)
|
|
130
|
+
except Exception as e: # noqa: BLE001
|
|
131
|
+
_write_text(out_file, "")
|
|
132
|
+
_write_text(err_file, str(e))
|
|
133
|
+
return StepResult(
|
|
134
|
+
name=name,
|
|
135
|
+
ok=False,
|
|
136
|
+
returncode=999,
|
|
137
|
+
seconds=round(time.time() - t0, 2),
|
|
138
|
+
stdout_file=str(out_file),
|
|
139
|
+
stderr_file=str(err_file),
|
|
140
|
+
error=str(e),
|
|
141
|
+
)
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
def main() -> int:
|
|
145
|
+
parser = argparse.ArgumentParser(description="保险 skill 云端超级回归(失败不中断)")
|
|
146
|
+
parser.add_argument("--skills-root", help="skills 目录路径(包含 insurance-* 子目录)")
|
|
147
|
+
parser.add_argument("--artifacts-dir", help="日志与报告输出目录")
|
|
148
|
+
parser.add_argument("--skip-full", action="store_true", help="跳过 cloud_insurance_full_test.py 全量集成阶段")
|
|
149
|
+
parser.add_argument("--always-zero", action="store_true", help="即使失败也返回 0(适合人工巡检场景)")
|
|
150
|
+
args = parser.parse_args()
|
|
151
|
+
|
|
152
|
+
try:
|
|
153
|
+
sys.stdout.reconfigure(encoding="utf-8")
|
|
154
|
+
sys.stderr.reconfigure(encoding="utf-8")
|
|
155
|
+
except (AttributeError, OSError):
|
|
156
|
+
pass
|
|
157
|
+
|
|
158
|
+
skills_root = _discover_skills_root(args.skills_root)
|
|
159
|
+
scripts = {
|
|
160
|
+
"customer_smoke": _resolve_script(skills_root, "insurance-customer-policy", "run_e2e_smoke.py"),
|
|
161
|
+
"product_smoke": _resolve_script(skills_root, "insurance-product-analysis", "run_e2e_smoke.py"),
|
|
162
|
+
"pipeline_smoke": _resolve_script(skills_root, "insurance-sales-pipeline", "run_e2e_smoke.py"),
|
|
163
|
+
"schedule_smoke": _resolve_script(skills_root, "insurance-schedule-renewal", "run_e2e_smoke.py"),
|
|
164
|
+
"full": _resolve_script(skills_root, "insurance-customer-policy", "cloud_insurance_full_test.py"),
|
|
165
|
+
"dashboard_all": _resolve_script(skills_root, "insurance-customer-policy", "dashboard_all.py"),
|
|
166
|
+
"super_self": _resolve_script(skills_root, "insurance-shared-data", "cloud_insurance_super_test.py"),
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
ts = time.strftime("%Y%m%d-%H%M%S", time.localtime())
|
|
170
|
+
artifacts = Path(args.artifacts_dir).expanduser().resolve() if args.artifacts_dir else Path(f"/tmp/insurance-super-test-{ts}")
|
|
171
|
+
logs_dir = artifacts / "logs"
|
|
172
|
+
artifacts.mkdir(parents=True, exist_ok=True)
|
|
173
|
+
logs_dir.mkdir(parents=True, exist_ok=True)
|
|
174
|
+
|
|
175
|
+
# 每个子脚本自己会再创建隔离数据库,统一给定父目录便于回溯。
|
|
176
|
+
env = dict(os.environ)
|
|
177
|
+
env["INSURANCE_SKILLS_ROOT"] = str(skills_root)
|
|
178
|
+
env["INSURANCE_CLOUD_TEST_DIR"] = str(artifacts / "db")
|
|
179
|
+
env["PYTHONIOENCODING"] = "utf-8"
|
|
180
|
+
|
|
181
|
+
steps: list[tuple[str, list[str], int]] = [
|
|
182
|
+
("customer_smoke", [sys.executable, str(scripts["customer_smoke"])], 1200),
|
|
183
|
+
("product_smoke", [sys.executable, str(scripts["product_smoke"])], 1200),
|
|
184
|
+
("pipeline_smoke", [sys.executable, str(scripts["pipeline_smoke"])], 1200),
|
|
185
|
+
("schedule_smoke", [sys.executable, str(scripts["schedule_smoke"])], 1200),
|
|
186
|
+
("dashboard_all_json", [sys.executable, str(scripts["dashboard_all"]), "--json"], 300),
|
|
187
|
+
("dashboard_all_md", [sys.executable, str(scripts["dashboard_all"]), "--md"], 300),
|
|
188
|
+
]
|
|
189
|
+
if not args.skip_full:
|
|
190
|
+
steps.extend(
|
|
191
|
+
[
|
|
192
|
+
("cloud_full_smoke", [sys.executable, str(scripts["full"]), "--smoke"], 1800),
|
|
193
|
+
("cloud_full_all", [sys.executable, str(scripts["full"]), "-v"], 3600),
|
|
194
|
+
]
|
|
195
|
+
)
|
|
196
|
+
|
|
197
|
+
print("====== 保险技能超级回归(失败不中断)======")
|
|
198
|
+
print(f"skills_root : {skills_root}")
|
|
199
|
+
print(f"artifacts_dir : {artifacts}")
|
|
200
|
+
print(f"steps : {len(steps)}")
|
|
201
|
+
print("------------------------------------------")
|
|
202
|
+
|
|
203
|
+
results: list[StepResult] = []
|
|
204
|
+
for idx, (name, cmd, timeout) in enumerate(steps, start=1):
|
|
205
|
+
print(f"[{idx}/{len(steps)}] RUN {name}")
|
|
206
|
+
r = _run_step(name, cmd, env=env, cwd=skills_root, logs_dir=logs_dir, timeout=timeout)
|
|
207
|
+
results.append(r)
|
|
208
|
+
if r.ok:
|
|
209
|
+
print(f" -> PASS ({r.seconds}s)")
|
|
210
|
+
else:
|
|
211
|
+
print(f" -> FAIL rc={r.returncode} ({r.seconds}s)")
|
|
212
|
+
|
|
213
|
+
passed = sum(1 for r in results if r.ok)
|
|
214
|
+
failed = len(results) - passed
|
|
215
|
+
summary: dict[str, Any] = {
|
|
216
|
+
"ok": failed == 0,
|
|
217
|
+
"skills_root": str(skills_root),
|
|
218
|
+
"artifacts_dir": str(artifacts),
|
|
219
|
+
"stats": {"total": len(results), "passed": passed, "failed": failed},
|
|
220
|
+
"results": [r.__dict__ for r in results],
|
|
221
|
+
}
|
|
222
|
+
summary_file = artifacts / "summary.json"
|
|
223
|
+
_write_text(summary_file, json.dumps(summary, ensure_ascii=False, indent=2))
|
|
224
|
+
|
|
225
|
+
print("\n============== 汇总 ==============")
|
|
226
|
+
print(f"PASS: {passed}")
|
|
227
|
+
print(f"FAIL: {failed}")
|
|
228
|
+
print(f"SUMMARY: {summary_file}")
|
|
229
|
+
print(f"LOG DIR: {logs_dir}")
|
|
230
|
+
|
|
231
|
+
if failed > 0:
|
|
232
|
+
print("\n失败项:")
|
|
233
|
+
for r in results:
|
|
234
|
+
if not r.ok:
|
|
235
|
+
print(f"- {r.name}: rc={r.returncode}")
|
|
236
|
+
print(f" stderr: {r.stderr_file}")
|
|
237
|
+
print(f" stdout: {r.stdout_file}")
|
|
238
|
+
|
|
239
|
+
if failed > 0 and not args.always_zero:
|
|
240
|
+
return 1
|
|
241
|
+
return 0
|
|
242
|
+
|
|
243
|
+
|
|
244
|
+
if __name__ == "__main__":
|
|
245
|
+
raise SystemExit(main())
|
|
246
|
+
|