# OCDP 第二次测试报告

**测试日期:** 2026-05-11
**测试环境:** http://10.6.80.114:18080

---

## 测试1: 资源配额限额

### 测试方法
使用 test-user-b（quota: cpu=2, mem=4Gi, gpu=0, gpumem=0）在 k3s 集群部署 nginx chart

### 测试结果

| 测试 | 操作 | 预期 | 实际 | 结论 |
|------|------|------|------|------|
| Test A | 部署 nginx（默认值，在配额内） | 成功 | 部署完成，状态 deployed | ✅ |
| Test B | 部署 nginx（requests.cpu=2, mem=4Gi, replica=5，超配额） | 被配额阻止 | Helm release 创建成功，所有 Pod 被 ResourceQuota 阻塞，状态永远 stuck 在 pending-install | ⚠️ 部分通过 |
| Test C | 部署 vllm-serve（gpuLimit=1，gpu配额=0） | 被配额阻止 | Helm release 创建成功，Pod 被 ResourceQuota 阻塞，状态 pending-install | ⚠️ 部分通过 |

### 关键发现

**1. 没有 API 层的预检查配额验证**
- 后端 API 无条件接受所有部署请求，不检查是否超配额
- 所有超配额请求返回 HTTP 200 + status: pending-install
- 后端日志中**没有任何配额相关的条目**

**2. K8s ResourceQuota 在 Pod 级别执行**
- `tenant-quota` ResourceQuota 对象确实存在并执行限制
- 当 Pod 超配额时，K8s 明确拒绝创建并给出错误消息
- 但 Helm release、Service、Deployment、ReplicaSet **仍然被创建**

**3. 实例永远 stuck 在 "pending-install"**
- 超配额的实例永远不会转换到 deployed/failed/error
- OCDP 平台不检测 ResourceQuota 拒绝事件
- 唯一知道失败的方式是直接查 K8s events

**4. GPU 配额绕过**
- gpu=0 的用户可以提交需要 GPU 的 chart 部署
- K8s ResourceQuota 最终会阻止，但 Helm release 等资源已被创建

**5. 有效的 ResourceQuota 配置**
```yaml
apiVersion: v1
kind: ResourceQuota
metadata:
  name: tenant-quota
  namespace: ocdp-u-test-b
spec:
  hard:
    requests.cpu: "2"
    requests.memory: 4Gi
    requests.nvidia.com/gpu: "0"
    requests.nvidia.com/gpumem: "0"
```

### 建议
1. **添加 API 层预检查配额验证** — 在接受部署前检查请求资源是否超过用户配额
2. **处理 pending-install 超时** — 监控 Helm release 创建后 Pod 是否 stuck，更新状态为 failed
3. **GPU 配额预检查** — 如果 chart 需要 GPU 而用户 gpu=0，在 API 层拒绝
4. **UI 配额指示器** — 在部署表单上显示剩余配额

---

## 测试2: values.yaml 覆盖优先级

### 测试方法
使用 test-user-c（quota: cpu=4, mem=8Gi, gpu=1, gpumem=5000）部署 vllm-serve:0.6.0 chart

### 测试结果

| 方法 | 提交方式 | 是否部署成功 | 存储的值 | 结论 |
|------|----------|-------------|---------|------|
| 方法1 | `values` JSON 字段 | ✅ | cpuRequest=2, gpuMem=10000 | JSON 值被准确接受和存储 |
| 方法2 | `valuesYaml` 字符串 | ✅ | cpuRequest=4, gpuMem=10000 | YAML 被正确解析为结构化 values |
| 方法3 | 同时提供 `values` + `valuesYaml`（冲突） | ✅ 无任何错误/警告 | **values JSON 全胜** | `values` JSON 静默覆盖 `valuesYaml` |
| 方法4 | 不提供任何 values（使用 chart 默认） | ✅ | 仅 namespace | chart 默认值不存储在 API 响应中 |

### 优先级最终结论

| 优先级 | 来源 | 说明 |
|--------|------|------|
| **最高** | `values` JSON 字段 | 请求体中的结构化 JSON |
| **中** | `valuesYaml` 字符串 | 请求体中的 YAML 字符串 |
| **最低** | Chart 内置 values.yaml | Helm chart 打包的默认值 |

### 冲突测试详细结果

当同时提供 `values` 和 `valuesYaml` 且值冲突时：
- `values` JSON 字段**完全覆盖** `valuesYaml`
- **没有任何错误或警告**返回给用户
- 两者被合并到统一的 DB `values` 字段

### gpuMem=10000 行为
- 整数值 `10000` 在 `values` JSON 和 `valuesYaml` 中都被**正确接受**
- 无单位转换（作为整数 MB 标量存储）
- 符合项目规范

### 建议
1. **记录优先级顺序** — 用户需知道同时提供两者时 values JSON 优先
2. **添加冲突警告** — 当两个字段存在冲突值时应返回警告
3. **考虑废弃一个字段** — values 和 valuesYaml 语义重复易混淆

---

## 测试3: 前端 UI 溢出/滚动/刷新

### 测试方法
Playwright + 源码分析，测试 1920/768/375 三个视口

### 测试结果
**总体结论: PASS** — 没有导致功能问题的关键溢出问题

| 测试项 | 结果 | 详情 |
|--------|------|------|
| 水平溢出 | ✅ 无问题 | 所有视口均无水平溢出 |
| 文本截断 | ⚠️ 1 个低风险 | InstanceCard h3 标题 truncate 无 title tooltip |
| 响应式设计 | ✅ 正确 | sm/md/lg/xl 断点覆盖完整 |
| 滚动行为 | ✅ 流畅 | Sidebar 和内容区独立滚动，overscroll-contain 防滚动穿透 |
| 模态框布局 | ✅ 正确 | body scroll lock + 内容独立滚动 |
| 页面刷新 | ✅ 正常 | 受保护路由正确重定向到登录页 |
| 颜色对比度 | ⚠️ 1 个中风险 | 登录页错误文本 red-400 在白色背景上仅 2.5:1 (WCAG AA 要求 4.5:1) |

### 通过的细分项
- Chart Browser 全高 + overflow-y-auto 布局 ✅
- InstanceCard 操作按钮网格 grid-cols-2/3/5 响应正确 ✅
- Tabs 支持 overflow-x-auto 水平滚动 ✅
- 用户管理表格 overflow-x-auto ✅
- iOS 触摸滚动 (`-webkit-overflow-scrolling: touch`) 已配置 ✅

### 建议
1. 将登录页错误文本从 text-red-400 改为 text-red-600/700
2. InstanceCard h3 标题添加 title 属性

---

## 综合建议
1. 添加 API 层配额预检查
2. 处理 pending-install 超时 + 状态更新
3. 记录 values 覆盖优先级并添加冲突警告
4. 统一 values JSON/YAML 的 API 设计
