Metadata-Version: 2.4
Name: pandatest
Version: 0.1.0
Summary: PandaTest SDK & CLI — 移动端自动化测试平台的 Python 客户端
Author-email: PandaTest <contact@pandatest.net>
License: MIT
Project-URL: Homepage, https://www.pandatest.net
Project-URL: Documentation, https://docs.pandatest.net
Project-URL: Repository, https://github.com/PandaTestGrid/pandatest
Keywords: testing,automation,mobile,android,device-farm
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Software Development :: Testing
Requires-Python: >=3.11
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: httpx>=0.27
Requires-Dist: click>=8.1
Requires-Dist: rich>=13.0
Provides-Extra: dev
Requires-Dist: pytest>=7.0; extra == "dev"
Requires-Dist: pytest-httpx>=0.30; extra == "dev"
Dynamic: license-file

# PandaTest SDK & CLI

PandaTest 移动端自动化测试平台的 Python SDK 和命令行工具。

## 安装

```bash
pip install pandatest
```

开发模式安装：

```bash
cd packages/pandatest
pip install -e .
```

## 快速开始

### SDK 使用

```python
from pandatest import PandaTest

client = PandaTest(api_key="pk_xxx")

# 项目作用域
scope = client.project(2)

# 列出构建
builds = scope.builds.list()
for build in builds.items:
    print(f"{build.id}: {build.name}")

# 触发构建
result = scope.builds.trigger("build-14-xxx")
print(f"Job created: {result.job_id}")

# 等待 Job 完成
job = scope.jobs.wait(result.job_id, timeout=600)
print(f"Status: {job.status}, Pass rate: {job.pass_rate}%")
```

### CLI 使用

```bash
# 配置
pt config set --api-key pk_xxx --url https://your-domain.com
pt config show

# 触发构建并等待
pt run --build build-14-xxx --wait --timeout 600

# 管理构建
pt builds list
pt builds get build-14-xxx
pt builds create --name "回归测试" --app 1 --parallel --concurrency 3
pt builds trigger build-14-xxx
pt builds delete build-14-xxx

# 查看 Job
pt jobs list --build build-14-xxx
pt jobs get job-xxx
pt jobs cancel job-xxx
pt jobs rerun job-xxx

# 查看 Task
pt tasks list --job job-xxx
pt tasks get task-xxx
pt tasks logs task-xxx

# 管理调度
pt schedules list
pt schedules create --name "每日回归" --build build-14-xxx --frequency daily --time 02:00
pt schedules enable schedule-xxx
pt schedules disable schedule-xxx
pt schedules trigger schedule-xxx
pt schedules delete schedule-xxx

# 设备与自动化锁（OpenAPI）
pt devices list
pt devices list --platform android --status online
pt devices acquire R58N30ABCDE
pt devices status R58N30ABCDE
pt devices release R58N30ABCDE --lease-token <from-acquire>
```

SDK 示例（acquire 自动带心跳，release 自动停心跳）：

```python
from pandatest import PandaTest

client = PandaTest(api_key="pk_xxx", base_url="http://localhost:8000")
client.devices.acquire("R58N30ABCDE")
try:
    # 在这里执行自动化任务，心跳自动保活
    pass
finally:
    client.devices.release("R58N30ABCDE")  # 无需传 token，SDK 自动记住
    client.close()
```

### CI/CD (GitHub Actions)

```yaml
- name: Install PandaTest CLI
  run: pip install pandatest

- name: Run Tests
  env:
    PANDA_API_KEY: ${{ secrets.PANDA_API_KEY }}
    PANDA_PROJECT_ID: "2"
  run: pt run --build build-14-xxx --wait --timeout 600
```

## 配置

配置按优先级加载（高到低）：

1. **构造函数参数** / CLI 参数
2. **环境变量**: `PANDA_API_KEY`, `PANDA_API_URL`, `PANDA_PROJECT_ID`
3. **配置文件**: `~/.pandatest/config.toml`

## SDK API

### 项目作用域

SDK 采用项目作用域模式，项目级 API 需要先绑定 `project_id`：

```python
client = PandaTest(api_key="pk_xxx")

# 风格 A: ProjectScope（推荐）
scope = client.project(123)
scope.builds.list()
scope.jobs.list()
scope.tasks.list()
scope.schedules.list()

# 风格 B: 每模块单独绑定
client.builds(123).list()
client.jobs(123).list()
```

### Projects（全局，无需项目作用域）

| 方法 | 说明 |
|------|------|
| `client.projects.list()` | 获取当前用户可访问的项目列表 |
| `client.projects.get(id)` | 获取项目详情 |

### Builds

| 方法 | 说明 |
|------|------|
| `scope.builds.list()` | 列出构建 |
| `scope.builds.get(id)` | 获取构建详情 |
| `scope.builds.create(name=, app=)` | 创建构建 |
| `scope.builds.update(id, **fields)` | 更新构建 |
| `scope.builds.delete(id)` | 删除构建 |
| `scope.builds.trigger(id)` | 触发构建 |
| `scope.builds.duplicate(id)` | 复制构建 |
| `scope.builds.toggle_pin(id)` | 切换置顶状态 |
| `scope.builds.recent_jobs(id, limit=10)` | 获取最近的 Job 列表 |
| `scope.builds.trigger_image_build(id)` | 触发 Runner 镜像构建 |
| `scope.builds.image_status(id)` | 获取 Runner 镜像状态 |
| `scope.builds.logs(id, tail=500)` | 获取构建日志 |

### Jobs

| 方法 | 说明 |
|------|------|
| `scope.jobs.list(build_id=)` | 列出 Job |
| `scope.jobs.get(id)` | 获取 Job 详情 |
| `scope.jobs.cancel(id)` | 取消 Job |
| `scope.jobs.rerun(id)` | 重新运行 Job |
| `scope.jobs.tasks(id)` | 获取 Job 下的 Task |
| `scope.jobs.wait(id, timeout=600)` | 等待 Job 完成 |
| `scope.jobs.report_summary(id)` | 获取 Job 报告摘要 |
| `scope.jobs.share(id, action=, expires_days=)` | 创建/禁用分享链接 |
| `scope.jobs.share_status(id)` | 获取分享状态 |

### Tasks

| 方法 | 说明 |
|------|------|
| `scope.tasks.list(job_id=)` | 列出 Task |
| `scope.tasks.get(id)` | 获取 Task 详情 |
| `scope.tasks.logs(id)` | 获取 Task 日志 |
| `scope.tasks.running_stats()` | 获取运行中的 Task 统计 |

### Schedules

| 方法 | 说明 |
|------|------|
| `scope.schedules.list()` | 列出调度 |
| `scope.schedules.get(id)` | 获取调度详情 |
| `scope.schedules.create(name=, build=, frequency=)` | 创建调度 |
| `scope.schedules.update(id, **fields)` | 更新调度 |
| `scope.schedules.delete(id)` | 删除调度 |
| `scope.schedules.enable(id)` | 启用调度 |
| `scope.schedules.disable(id)` | 禁用调度 |
| `scope.schedules.trigger(id)` | 立即触发调度 |

### Devices（全局，无需项目作用域）

| 方法 | 说明 |
|------|------|
| `client.devices.list()` | 获取设备列表（含锁状态） |
| `client.devices.get(device_id)` | 获取单个设备详情 |
| `client.devices.acquire(device_id)` | 占用设备（自动启动心跳） |
| `client.devices.release(device_id)` | 释放设备（自动停止心跳） |
| `client.devices.lock_status(device_id)` | 查询设备锁状态 |
| `client.devices.remote_debug_start(device_id)` | 启动 ADB 远程调试代理 |
| `client.devices.remote_debug_stop(device_id)` | 停止 ADB 远程调试代理 |
| `client.devices.close()` | 释放所有已占用设备 |
