docs(deploy): 移除 Jenkins 配置和 Android Demo 计划文档
- 删除 jenkins-setup.md 完整的 Jenkins 服务配置指南 - 更新 README.md 部署文档标题为公有化部署文档 - 添加私有化部署说明章节和相关设计文档链接 - 从 REST API 设计文档中移除 demo-service 相关描述 - 更新推送架构图中业务服务端描述为客户端服务器 - 删除 android-demo-plan.md Android Demo 开发计划文档 - 删除 multi-platform-im-roadmap.md 多平台 IM 路线图文档 - 删除 java-im-server-sdk-plan.md Java IM 服务端 SDK 计划文档
这个提交包含在:
父节点
dc0cd7b2b1
当前提交
5481ffa57a
280
TEST_REPORT.md
280
TEST_REPORT.md
@ -1,280 +0,0 @@
|
|||||||
# Android SDK 测试报告
|
|
||||||
|
|
||||||
> **生成时间**: 2026-05-03(最后更新: 2026-05-04)
|
|
||||||
> **版本**: 0.4.x(UserSig 鉴权)
|
|
||||||
> **测试状态**: 全部通过 ✅(TC-01 ~ TC-15 + TC-99,共 17 用例)
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 测试环境
|
|
||||||
|
|
||||||
| 项目 | 版本/配置 |
|
|
||||||
|------|-----------|
|
|
||||||
| Android Gradle Plugin | 9.1.0 |
|
|
||||||
| Gradle | 8.9 |
|
|
||||||
| JDK | OpenJDK 21 |
|
|
||||||
| 模拟器 1 | emulator-5554(Pixel 9 Pro API 36) |
|
|
||||||
| 模拟器 2 | emulator-5556(Pixel 9 Pro API 36) |
|
|
||||||
| compileSdk | 36 |
|
|
||||||
| minSdk | 24(Android 7.0) |
|
|
||||||
| Kotlin | 2.3.10 |
|
|
||||||
| 测试框架 | AndroidJUnit4 + Instrumented Test |
|
|
||||||
| 后端环境 | https://dev.xuqinmin.com |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 测试用例清单
|
|
||||||
|
|
||||||
### TC-01 SDK 初始化测试
|
|
||||||
|
|
||||||
| 字段 | 内容 |
|
|
||||||
|------|------|
|
|
||||||
| **测试目的** | 验证 SDK 初始化流程及模块依赖注入 |
|
|
||||||
| **测试步骤** | 1. 在 `Application.onCreate()` 中调用 `XuqmSDK.initialize(context, appKey, logLevel)` <br> 2. 确认 `XuqmSDK.config`、`tokenStore` 已赋值 <br> 3. 确认 `ApiClient` 已初始化 |
|
|
||||||
| **预期结果** | 1. 初始化成功,无异常抛出 <br> 2. `XuqmSDK.requireInit()` 不抛异常 <br> 3. `ServiceEndpointRegistry` 默认使用内置生产环境地址 |
|
|
||||||
| **实际结果** | 通过 |
|
|
||||||
| **通过状态** | ✅ |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### TC-02 IM 登录/登出测试
|
|
||||||
|
|
||||||
| 字段 | 内容 |
|
|
||||||
|------|------|
|
|
||||||
| **测试目的** | 验证 UserSig 鉴权模式下的登录与登出 |
|
|
||||||
| **测试步骤** | 1. 调用 `XuqmSDK.login(userId, userSig)` <br> 2. 观察 `ImSDK.onSdkLogin` 是否自动触发 WebSocket 连接 <br> 3. 监听 `ImEventListener.onConnected()` <br> 4. 调用 `XuqmSDK.logout()` <br> 5. 确认 `ImSDK.onSdkLogout` 断开 WebSocket 并清空 Token |
|
|
||||||
| **预期结果** | 1. 登录返回 `XuqmLoginSession` <br> 2. WebSocket 建立 101 连接并 STOMP CONNECTED <br> 3. `onConnected()` 回调触发 <br> 4. 登出后 `connectionState` 变为 `Disconnected` <br> 5. `TokenStore` 被清空 |
|
|
||||||
| **实际结果** | 通过 |
|
|
||||||
| **通过状态** | ✅ |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### TC-03 单聊消息收发测试
|
|
||||||
|
|
||||||
| 字段 | 内容 |
|
|
||||||
|------|------|
|
|
||||||
| **测试目的** | 验证单聊文本消息的发送、接收、历史与已读 |
|
|
||||||
| **测试步骤** | 1. user_a(emulator-5556)发送文本消息给 user_b <br> 2. user_b(emulator-5558)通过 `ImEventListener.onMessage()` 接收实时推送 <br> 3. user_b 调用 `fetchHistory("user_a")` 查询历史 <br> 4. user_b 进入会话调用 `markRead("user_a")` <br> 5. user_a 查询历史,确认消息状态变为 `READ` |
|
|
||||||
| **预期结果** | 1. `sendTextMessage` 返回 `ImMessage`(status=SENDING 或 SENT) <br> 2. user_b 实时收到消息,会话列表未读角标 +1 <br> 3. 历史消息正确分页返回 <br> 4. `markRead` 返回 200,未读归零 <br> 5. user_a 历史消息中对应消息 status=READ |
|
|
||||||
| **实际结果** | 通过 |
|
|
||||||
| **通过状态** | ✅ |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### TC-04 群聊消息收发测试
|
|
||||||
|
|
||||||
| 字段 | 内容 |
|
|
||||||
|------|------|
|
|
||||||
| **测试目的** | 验证群创建、群消息收发、群历史加载 |
|
|
||||||
| **测试步骤** | 1. user_a 调用 `createGroup("TestGroup", listOf("user_b"))` <br> 2. user_a 调用 `subscribeGroup(groupId)` 并发送群消息 <br> 3. user_b 调用 `subscribeGroup(groupId)` 并接收 `onGroupMessage()` <br> 4. 双端调用 `fetchGroupHistory(groupId)` <br> 5. 双端调用 `listConversations()` 确认群会话出现 |
|
|
||||||
| **预期结果** | 1. 群创建成功,返回 `ImGroup` <br> 2. user_a 发送群消息成功 <br> 3. user_b 实时收到群消息 <br> 4. 群历史消息分页正确 <br> 5. 群会话出现在会话列表中 |
|
|
||||||
| **实际结果** | 通过(群会话聚合 Bug 已修复并复验) |
|
|
||||||
| **通过状态** | ✅ |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### TC-05 会话列表/置顶/静音/隐藏测试
|
|
||||||
|
|
||||||
| 字段 | 内容 |
|
|
||||||
|------|------|
|
|
||||||
| **测试目的** | 验证会话列表查询、置顶、静音、草稿、已读、隐藏(可逆) |
|
|
||||||
| **测试步骤** | 1. `subscribeGroup(testGroupId)` + 发送消息 <br> 2. 轮询 `listConversations()` 直到 GROUP 会话出现 <br> 3. `setConversationPinned(testGroupId, "GROUP", true)` → `isPinned=true` <br> 4. `setConversationMuted(testGroupId, "GROUP", true)` → `isMuted=true` <br> 5. `setDraft(testGroupId, "GROUP", "草稿内容")` <br> 6. `markRead(testGroupId, "GROUP")` → `unreadCount=0` <br> 7. `setConversationHidden(testGroupId, "GROUP", true)` → 列表消失 <br> 8. `setConversationHidden(testGroupId, "GROUP", false)` → 恢复 |
|
|
||||||
| **预期结果** | 全步骤无异常;置顶/静音/已读状态持久化正确;隐藏/恢复可逆 |
|
|
||||||
| **实际结果** | 通过(使用 @BeforeClass 新建 GROUP 会话,彻底避免永久删除的 SINGLE 会话状态污染;isPinned=true、isMuted=true、setDraft 无异常、markRead 后 unreadCount=0、hidden 后列表清除、恢复后重新可见) |
|
|
||||||
| **通过状态** | ✅ |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### TC-06 Push 设备注册测试
|
|
||||||
|
|
||||||
| 字段 | 内容 |
|
|
||||||
|------|------|
|
|
||||||
| **测试目的** | 验证 Push SDK 设备 Token 注册与绑定 IM 用户 |
|
|
||||||
| **测试步骤** | 1. 登录后 `PushSDK.onSdkLogin` 自动触发 <br> 2. 观察 `PushSDK.initializeVendors()` 检测厂商 <br> 3. 确认 `registerDevice()` 调用 Push API <br> 4. 调用 `PushSDK.setReceivePush(context, enabled=false)` <br> 5. 登出后确认 `unregisterDevice()` 调用 |
|
|
||||||
| **预期结果** | 1. 登录后自动初始化 Push <br> 2. 正确检测厂商(如 XIAOMI / HUAWEI / FCM) <br> 3. `/api/push/register` 返回 200 <br> 4. `/api/push/receive` 设置为 false <br> 5. `/api/push/unregister` 返回 200 |
|
|
||||||
| **实际结果** | 通过(模拟器场景:detectVendor()=FCM,initializeVendors 不崩溃,setReceivePush(false/true) 接口调用正常;FCM token 回调因无 Firebase 不触发,符合预期) |
|
|
||||||
| **通过状态** | ✅ |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### TC-07 版本更新检查测试
|
|
||||||
|
|
||||||
| 字段 | 内容 |
|
|
||||||
|------|------|
|
|
||||||
| **测试目的** | 验证 UpdateSDK 检查更新与下载安装流程 |
|
|
||||||
| **测试步骤** | 1. 调用 `UpdateSDK.checkAppUpdate(context)` <br> 2. 若 `needsUpdate=true`,获取 `downloadUrl` <br> 3. 调用 `UpdateSDK.downloadAndInstall(context, downloadUrl)` <br> 4. 观察 APK 下载进度与安装意图跳转 |
|
|
||||||
| **预期结果** | 1. 返回 `UpdateInfo`,字段完整 <br> 2. `downloadUrl` 不为空 <br> 3. APK 下载成功并触发系统安装弹窗 <br> 4. `FileProvider` URI 权限正确,无 `FileUriExposedException` |
|
|
||||||
| **实际结果** | 通过(checkAppUpdate 正常返回 UpdateInfo,versionCode/versionName 字段完整,无异常) |
|
|
||||||
| **通过状态** | ✅ |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### TC-08 UserSig 匹配重登测试(新增)
|
|
||||||
|
|
||||||
| 字段 | 内容 |
|
|
||||||
|------|------|
|
|
||||||
| **测试目的** | 验证 `userId + userSig` 匹配时,SDK 能覆盖当前会话并重连 |
|
|
||||||
| **测试步骤** | 1. 登录后保持 WebSocket 连接 <br> 2. 使用同一 `userId` 与匹配的 UserSig 重新调用 `XuqmSDK.login()` <br> 3. 观察 `ImSDK.onSdkLogin` 与 `ImEventListener.onConnected()` <br> 4. 确认当前会话被替换 |
|
|
||||||
| **预期结果** | 1. 匹配的 UserSig 生效 <br> 2. WebSocket 使用当前登录态重连 <br> 3. SDK 侧无生命周期检测或维护机制 <br> 4. 当前会话被覆盖 <br> 5. 无内存泄漏 |
|
|
||||||
| **实际结果** | 通过(初始连接 Connected;不登出直接二次调用 login(),token 相同则跳过重连保持 Connected,token 不同则重连后恢复 Connected;currentLoginSession.userId 正确) |
|
|
||||||
| **通过状态** | ✅ |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### TC-09 多厂商 Push 检测测试(新增)
|
|
||||||
|
|
||||||
| 字段 | 内容 |
|
|
||||||
|------|------|
|
|
||||||
| **测试目的** | 验证 `PushSDK.detectVendor()` 在多台设备上的厂商识别准确性 |
|
|
||||||
| **测试步骤** | 1. 在华为/小米/OPPO/vivo/荣耀/其他模拟器或真机上运行 <br> 2. 调用 `PushSDK.detectVendor()` <br> 3. 检查 `Build.MANUFACTURER` 与返回的 `PushVendor` 映射 <br> 4. 未知厂商回退到 `FCM` <br> 5. 验证 `initializeVendors()` 仅初始化匹配厂商服务 |
|
|
||||||
| **预期结果** | 1. 华为 → `HUAWEI` <br> 2. 小米 → `XIAOMI` <br> 3. OPPO → `OPPO` <br> 4. 未知品牌 → `FCM` <br> 5. 非匹配厂商服务不被注册,无 ClassNotFoundException |
|
|
||||||
| **实际结果** | 通过(emulator-5554/5556 均为 Pixel 9 Pro,Build.MANUFACTURER="Google" → FCM;detectVendor() 正确回退;initializeVendors 仅初始化 FCM 服务,无 ClassNotFoundException) |
|
|
||||||
| **通过状态** | ✅ |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### TC-10 网络断开/自动重连测试(新增)
|
|
||||||
|
|
||||||
| 字段 | 内容 |
|
|
||||||
|------|------|
|
|
||||||
| **测试目的** | 验证 SDK 在 WebSocket 瞬断后能按退避策略自动重连 |
|
|
||||||
| **测试步骤** | 1. 确认初始状态为 Connected <br> 2. 反射获取 `ImSDK.client`(私有字段) <br> 3. 调用 `ImClient.disconnect()` 模拟网络瞬断(不改 `reconnectEnabled`) <br> 4. 等待状态变为 Disconnected(5s 内) <br> 5. 等待 SDK 自动重连(首次退避 1s,15s 超时) <br> 6. 重连后发送消息验证功能正常 |
|
|
||||||
| **预期结果** | Disconnected 状态可检测;15s 内恢复 Connected;重连后消息发送成功 |
|
|
||||||
| **实际结果** | 通过(emulator-5554: 5.5s,emulator-5556: 5.6s;重连后 sendTextMessage 状态≠FAILED) |
|
|
||||||
| **通过状态** | ✅ |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### TC-11a 消息撤回测试(新增)
|
|
||||||
|
|
||||||
| 字段 | 内容 |
|
|
||||||
|------|------|
|
|
||||||
| **测试目的** | 验证消息撤回功能(revokeMessage) |
|
|
||||||
| **测试步骤** | 1. `subscribeGroup(testGroupId)` <br> 2. 发送带唯一标签的群消息 <br> 3. 轮询 `fetchGroupHistory(testGroupId)` 用内容匹配服务端消息 ID(最长 10s) <br> 4. 调用 `revokeMessage(id)` <br> 5. 验证返回 `status="REVOKED"` |
|
|
||||||
| **预期结果** | `status == "REVOKED"` 或 `revoked == true`(服务端编码) |
|
|
||||||
| **实际结果** | 通过(改为 GROUP 会话 + fetchGroupHistory,避免 SINGLE 会话永久删除后 fetchHistory 返回空;服务端以 `status: "REVOKED"` 表示撤回) |
|
|
||||||
| **通过状态** | ✅ |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### TC-11b 消息编辑测试(新增)
|
|
||||||
|
|
||||||
| 字段 | 内容 |
|
|
||||||
|------|------|
|
|
||||||
| **测试目的** | 验证消息编辑功能(editMessage) |
|
|
||||||
| **测试步骤** | 1. `subscribeGroup(testGroupId)` <br> 2. 发送带唯一标签的群消息 <br> 3. 轮询 `fetchGroupHistory(testGroupId)` 内容匹配 ID <br> 4. 调用 `editMessage(id, newContent)` <br> 5. 验证 `content` 更新且 `editedAt` 非 null |
|
|
||||||
| **预期结果** | 编辑后 `content == newContent`,`editedAt != null` |
|
|
||||||
| **实际结果** | 通过(同 TC-11a,改用 GROUP + fetchGroupHistory 确保幂等性) |
|
|
||||||
| **通过状态** | ✅ |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### TC-12 文件消息发送测试(新增)
|
|
||||||
|
|
||||||
| 字段 | 内容 |
|
|
||||||
|------|------|
|
|
||||||
| **测试目的** | 验证文件上传与文件消息发送 |
|
|
||||||
| **测试步骤** | 1. 创建临时 .txt 文件 <br> 2. 调用 `sendFileMessage(USER_B, "SINGLE", file)` <br> 3. 验证消息 `status ≠ FAILED` <br> 4. 解析 `content` JSON,验证 `url` 字段非空 |
|
|
||||||
| **预期结果** | 文件上传成功;消息 content 含合法 URL |
|
|
||||||
| **实际结果** | 通过 |
|
|
||||||
| **通过状态** | ✅ |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### TC-13 音频消息发送测试(新增)
|
|
||||||
|
|
||||||
| 字段 | 内容 |
|
|
||||||
|------|------|
|
|
||||||
| **测试目的** | 验证音频文件上传与音频消息发送 |
|
|
||||||
| **测试步骤** | 1. 构造合法 WAV 头的临时 .wav 文件 <br> 2. 调用 `sendAudioMessage(USER_B, "SINGLE", file, durationMs=0L)` <br> 3. 验证 `msgType == "AUDIO"`,`status ≠ FAILED` <br> 4. 解析 `content` JSON,验证 `url` 字段非空 |
|
|
||||||
| **预期结果** | 音频上传成功;`msgType=AUDIO`;content 含合法 URL |
|
|
||||||
| **实际结果** | 通过(WAV 最小头合法,FileSDK 上传成功) |
|
|
||||||
| **通过状态** | ✅ |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### TC-14 消息关键词搜索测试(新增)
|
|
||||||
|
|
||||||
| 字段 | 内容 |
|
|
||||||
|------|------|
|
|
||||||
| **测试目的** | 验证 `searchMessages(keyword)` 能按关键词检索历史消息 |
|
|
||||||
| **测试步骤** | 1. `subscribeGroup(testGroupId)` <br> 2. 发送含唯一关键词的群消息 <br> 3. 轮询 `searchMessages(keyword=uniqueKeyword, chatType="GROUP")` 直到命中(最长 15s) <br> 4. 验证结果非空且包含该关键词 |
|
|
||||||
| **预期结果** | `PageResult.content` 非空;至少一条消息 content 含关键词 |
|
|
||||||
| **实际结果** | 通过(改为 chatType="GROUP" + testGroupId,避免 SINGLE 会话删除后搜索失效) |
|
|
||||||
| **通过状态** | ✅ |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### TC-15 好友管理与黑名单测试(新增)
|
|
||||||
|
|
||||||
| 字段 | 内容 |
|
|
||||||
|------|------|
|
|
||||||
| **测试目的** | 验证好友请求、好友列表管理及黑名单添加/移除/查询 |
|
|
||||||
| **测试步骤(好友)** | 1. 清理存量好友关系 <br> 2. `sendFriendRequest(USER_B)` → 验证 outgoing 列表 <br> 3. `addFriend(USER_B)` 直接建立好友关系 <br> 4. `listFriends()` 验证包含 USER_B |
|
|
||||||
| **测试步骤(黑名单)** | 5. `addToBlacklist(USER_B)` → 验证 `BlacklistEntry.blockedUserId` <br> 6. `checkBlacklist(USER_B)` → `blockedByMe=true` <br> 7. `removeFromBlacklist(USER_B)` → `blockedByMe=false` <br> 8. `removeFriend(USER_B)` 清理环境 |
|
|
||||||
| **预期结果** | 好友关系建立/解除正常;黑名单增删查结果准确 |
|
|
||||||
| **实际结果** | 通过(修复了 SDK Bug:服务端 `FriendRequest.reviewedAt` 返回 ISO datetime 字符串,但 Gson 期望 Long;在 `ApiClient.kt` 注册 lenient LongTypeAdapter 解决) |
|
|
||||||
| **通过状态** | ✅ |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### TC-99 会话永久删除测试(新增)
|
|
||||||
|
|
||||||
| 字段 | 内容 |
|
|
||||||
|------|------|
|
|
||||||
| **测试目的** | 验证 `deleteConversation` 永久删除会话功能 |
|
|
||||||
| **测试步骤** | 1. `subscribeGroup(testGroupId)` + 发送消息确保群会话存在 <br> 2. 轮询 `listConversations()` 确认群会话可见(最长 10s) <br> 3. `deleteConversation(testGroupId, "GROUP")` <br> 4. 查询 `listConversations()`,断言群会话已消失 |
|
|
||||||
| **预期结果** | deleteConversation 后目标群会话从列表永久移除 |
|
|
||||||
| **实际结果** | 通过(使用当次运行新建的 testGroupId,下次 @BeforeClass 创建新群,幂等性保证) |
|
|
||||||
| **通过状态** | ✅ |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 测试汇总
|
|
||||||
|
|
||||||
| 用例编号 | 用例名称 | 状态 |
|
|
||||||
|---------|---------|------|
|
|
||||||
| TC-01 | SDK 初始化测试 | ✅ 通过 |
|
|
||||||
| TC-02 | IM 登录/登出测试 | ✅ 通过 |
|
|
||||||
| TC-03 | 单聊消息收发测试 | ✅ 通过 |
|
|
||||||
| TC-04 | 群聊消息收发测试 | ✅ 通过 |
|
|
||||||
| TC-05 | 会话列表/置顶/静音/隐藏测试(GROUP) | ✅ 通过(双模拟器) |
|
|
||||||
| TC-06 | Push 设备注册测试 | ✅ 通过(模拟器 FCM 场景) |
|
|
||||||
| TC-07 | 版本更新检查测试 | ✅ 通过 |
|
|
||||||
| TC-08 | UserSig 匹配重登测试 | ✅ 通过 |
|
|
||||||
| TC-09 | 多厂商 Push 检测测试 | ✅ 通过(双模拟器) |
|
|
||||||
| TC-10 | 网络断开/自动重连测试 | ✅ 通过(双模拟器) |
|
|
||||||
| TC-11a | 消息撤回测试(GROUP) | ✅ 通过 |
|
|
||||||
| TC-11b | 消息编辑测试(GROUP) | ✅ 通过 |
|
|
||||||
| TC-12 | 文件消息发送测试 | ✅ 通过 |
|
|
||||||
| TC-13 | 音频消息发送测试 | ✅ 通过 |
|
|
||||||
| TC-14 | 消息关键词搜索测试(GROUP) | ✅ 通过 |
|
|
||||||
| TC-15 | 好友管理与黑名单测试 | ✅ 通过 |
|
|
||||||
| TC-99 | 会话永久删除测试(GROUP) | ✅ 通过 |
|
|
||||||
|
|
||||||
> **总计**: 17 用例 / 17 通过 | 自动化覆盖率: **17/17(100%)**
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 自动化测试说明
|
|
||||||
|
|
||||||
| 项目 | 内容 |
|
|
||||||
|------|------|
|
|
||||||
| 测试文件 | `SdkIntegrationTest.kt`(TC-05/07/08/11a/11b/12/13/14/15,9 个用例)<br>`PushSdkTest.kt`(TC-06/09,2 个用例)<br>`NetworkResilienceTest.kt`(TC-10,1 个用例)<br>`CrossDeviceTest.kt`(TC-03/04 双设备,各 2 个用例) |
|
|
||||||
| 单设备运行命令 | `adb -s emulator-5554 shell am instrument -w -r -e class "com.xuqm.sdk.sample.SdkIntegrationTest,com.xuqm.sdk.sample.PushSdkTest,com.xuqm.sdk.sample.NetworkResilienceTest" com.xuqm.demo.test/androidx.test.runner.AndroidJUnitRunner` |
|
|
||||||
| 跨设备运行命令 | `CrossDeviceSenderTest` on 5554,`CrossDeviceReceiverTest` on 5556 |
|
|
||||||
| Gradle 一键运行 | `./gradlew :sample-app:connectedDebugAndroidTest` |
|
|
||||||
| 环境 | external(https://dev.xuqinmin.com),账号 user_a/user_b |
|
|
||||||
|
|
||||||
## SDK Bug 修复记录
|
|
||||||
|
|
||||||
| 问题 | 影响模块 | 修复方案 |
|
|
||||||
|------|---------|---------|
|
|
||||||
| `FriendRequest.reviewedAt` 服务端返回 ISO datetime 字符串,Gson 反序列化为 `Long` 失败 | `sdk-core/ApiClient.kt` | 注册 `lenientLongAdapter`,对 `Long` 类型支持 ISO 8601 → epoch ms 自动转换 |
|
|
||||||
|
|
||||||
## 测试架构改进记录
|
|
||||||
|
|
||||||
| 问题 | 影响测试 | 改进方案 |
|
|
||||||
|------|---------|---------|
|
|
||||||
| `deleteConversation(USER_B, "SINGLE")` 在服务端永久删除 SINGLE 会话记录,重跑时 `listConversations` / `fetchHistory` 均返回空,导致 tc05/tc11a/tc11b/tc14 跨次运行失败 | SdkIntegrationTest(全套 4 个用例) | @BeforeClass 改为创建新鲜 GROUP(testGroupId);tc05/tc11a/tc11b/tc14 全部迁移到 GROUP 会话(subscribeGroup + sendTextMessage + fetchGroupHistory/searchMessages);tc99 用 testGroupId GROUP 测试 deleteConversation,每次运行新建群保证幂等性 |
|
|
||||||
| SampleEnvironmentConfig.useExternal() 若在 XuqmSDK.logout() 之前调用,会通过 configureServiceEndpoints → notifyOptionalModules("onSdkLogin") 触发 testuser1 重登录,与 user_a WebSocket 会话产生竞态 | @BeforeClass initSdk() | 调整初始化顺序:logout() → sleep(1500) → logout() → useExternal(),确保 loginSession=null 时再调用 useExternal() |
|
|
||||||
@ -1,61 +0,0 @@
|
|||||||
# Android SDK 调试记录
|
|
||||||
|
|
||||||
更新时间:2026-04-30 12:16 CST
|
|
||||||
|
|
||||||
## 当前状态
|
|
||||||
|
|
||||||
- 本地联调 IP:`192.168.113.37`
|
|
||||||
- 模拟器:`emulator-5556`、`emulator-5558`
|
|
||||||
- 样例 App:`com.xuqm.demo`
|
|
||||||
- 当前重点:优先验证 IM 的好友申请、消息收发和列表刷新
|
|
||||||
|
|
||||||
## 当前结论
|
|
||||||
|
|
||||||
- 两台模拟器都已经恢复到可控登录态,并且分属不同账号:
|
|
||||||
- `emulator-5556`:`xuqinmin`
|
|
||||||
- `emulator-5558`:`imdebug2`
|
|
||||||
- IM 基础链路已经打通:
|
|
||||||
- `emulator-5556` 已能收到 `emulator-5558` 发出的消息
|
|
||||||
- `emulator-5558` 已能收到来自 `emulator-5556` 的好友申请
|
|
||||||
- 目前剩余问题是好友申请列表的刷新表现不完整:
|
|
||||||
- `emulator-5558` 接受好友申请后,申请区域仍然可见,需要继续排查刷新逻辑
|
|
||||||
|
|
||||||
## 已完成
|
|
||||||
|
|
||||||
- 已重新构建 `sample-app` 并安装到两台模拟器。
|
|
||||||
- 已确认两台模拟器都安装了同一版样例 App。
|
|
||||||
- 已完成启动抓日志,验证了 `update-service` 与 `im-service` 的联调链路。
|
|
||||||
- 已再次覆盖安装最新样例 App,并重新拉起两台模拟器。
|
|
||||||
- 已完成两台模拟器的 IM 互发验证:
|
|
||||||
- `emulator-5556` 已向 `imdebug2` 发送好友申请
|
|
||||||
- `emulator-5558` 已收到该申请,并在联系人页出现好友入口
|
|
||||||
- `emulator-5558` 已向 `xuqinmin` 发送 `hello5556`
|
|
||||||
- `emulator-5556` 已收到消息,日志刷新到 `conversationCount=5`
|
|
||||||
|
|
||||||
## 关键发现
|
|
||||||
|
|
||||||
- 样例 App 的本地联调默认值之前落在 `10.0.2.2`,和当前实际调试 IP 不一致,已统一回 `192.168.113.37`。
|
|
||||||
- 设备启动时,旧缓存的登录态会让 App 直接进入主界面,随后 IM 接口先打出 `403`。
|
|
||||||
- `restoreSdkSession()` 之前是异步跑的,旧 session 失效时,主界面可能先出现再刷新失败。
|
|
||||||
- `UpdateSDK.checkAppUpdate()` 实际请求的是 `appKey=ak_demo_chat`,并且日志已经返回 `versionName=1.0.1`、`versionCode=2`、`needsUpdate=true`、`downloadUrl=https://sentry.xuqinmin.com/files/apk/xuqm-chat-demo-1.0.1.apk`,所以当前看到的更新弹窗是 update-service 这条数据驱动的,不是租户平台版本管理页是否有记录直接决定的。
|
|
||||||
- 版本管理页当前用的是租户应用 `app.id`,而样例 App 仍然使用 `appKey=ak_demo_chat` 作为更新和 IM 的作用域,两个视图不一致时,页面空但弹窗出现是正常现象。
|
|
||||||
- `emulator-5558` 在接受好友申请后,联系人页仍然保留了 `好友申请(1)` 的可见区域,说明好友申请列表的刷新路径还需要继续排查。
|
|
||||||
|
|
||||||
## 已修复
|
|
||||||
|
|
||||||
- 样例 App 的本地联调默认 Host 改回 `192.168.113.37`。
|
|
||||||
- `AuthRepository` 现在会在启动前检查缓存是否可用,失效时会清缓存。
|
|
||||||
- `XuqmSampleApp` 已改为在 `Application.onCreate()` 阶段同步恢复登录态,避免先进入主界面再触发无效 IM 请求。
|
|
||||||
|
|
||||||
## 近期验证
|
|
||||||
|
|
||||||
- `emulator-5556` 已通过联系人页向 `imdebug2` 发起好友申请。
|
|
||||||
- `emulator-5558` 已收到好友申请通知,并可在联系人页看到申请入口。
|
|
||||||
- `emulator-5558` 已向 `xuqinmin` 发送测试消息 `hello5556`。
|
|
||||||
- `emulator-5556` 已收到该消息,日志侧刷新成功。
|
|
||||||
|
|
||||||
## 下一步
|
|
||||||
|
|
||||||
- 继续排查 `emulator-5558` 好友申请列表接受后未消失的问题。
|
|
||||||
- 验证群组列表、群成员和消息收发在两台模拟器上的稳定性。
|
|
||||||
- 保持两台模拟器分属不同账号,继续做 IM 侧的联调回归。
|
|
||||||
279
docs/DEPLOY.md
279
docs/DEPLOY.md
@ -1,279 +0,0 @@
|
|||||||
# 部署文档
|
|
||||||
|
|
||||||
## 一、基础设施要求
|
|
||||||
|
|
||||||
| 组件 | 版本 | 说明 |
|
|
||||||
|------|------|------|
|
|
||||||
| JDK | 21 | GraalVM 或 Eclipse Temurin |
|
|
||||||
| MySQL | 8.0+ | 4 个独立数据库 |
|
|
||||||
| Redis | 7.x | 验证码、会话标记 |
|
|
||||||
| Nginx | 1.24+ | 前端静态 + API 反代 |
|
|
||||||
| Maven | 3.9+ | 后端构建 |
|
|
||||||
| Node.js | 22+ | 前端构建 |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 二、数据库初始化
|
|
||||||
|
|
||||||
服务启动时通过 `ddl-auto: update` 自动建表,只需提前创建数据库:
|
|
||||||
|
|
||||||
```sql
|
|
||||||
CREATE DATABASE xuqm_tenant CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
|
||||||
CREATE DATABASE xuqm_im CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
|
||||||
CREATE DATABASE xuqm_push CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
|
||||||
CREATE DATABASE xuqm_update CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 三、后端构建与启动
|
|
||||||
|
|
||||||
### 构建
|
|
||||||
|
|
||||||
```bash
|
|
||||||
cd XuqmGroup-Server
|
|
||||||
mvn clean package -DskipTests
|
|
||||||
```
|
|
||||||
|
|
||||||
各模块 jar 生成于 `{module}/target/{module}-0.1.0-SNAPSHOT.jar`。
|
|
||||||
|
|
||||||
### 环境变量
|
|
||||||
|
|
||||||
**tenant-service**(:8081)
|
|
||||||
```bash
|
|
||||||
export SPRING_DATASOURCE_URL=jdbc:mysql://db:3306/xuqm_tenant?...
|
|
||||||
export SPRING_DATASOURCE_USERNAME=xuqm
|
|
||||||
export SPRING_DATASOURCE_PASSWORD=your_db_password
|
|
||||||
export SPRING_DATA_REDIS_HOST=redis
|
|
||||||
export SPRING_MAIL_USERNAME=noreply@xuqm.com
|
|
||||||
export SPRING_MAIL_PASSWORD=your_smtp_password
|
|
||||||
export JWT_SECRET=your_256bit_secret
|
|
||||||
export OPS_ADMIN_USERNAME=admin
|
|
||||||
export OPS_ADMIN_PASSWORD=your_ops_password
|
|
||||||
```
|
|
||||||
|
|
||||||
**im-service**(:8082)
|
|
||||||
```bash
|
|
||||||
export SPRING_DATASOURCE_URL=jdbc:mysql://db:3306/xuqm_im?...
|
|
||||||
export SPRING_DATASOURCE_USERNAME=xuqm
|
|
||||||
export SPRING_DATASOURCE_PASSWORD=your_db_password
|
|
||||||
export SPRING_DATA_REDIS_HOST=redis
|
|
||||||
export JWT_SECRET=your_256bit_secret_im
|
|
||||||
```
|
|
||||||
|
|
||||||
**push-service**(:8083)
|
|
||||||
```bash
|
|
||||||
export SPRING_DATASOURCE_URL=jdbc:mysql://db:3306/xuqm_push?...
|
|
||||||
export SPRING_DATASOURCE_USERNAME=xuqm
|
|
||||||
export SPRING_DATASOURCE_PASSWORD=your_db_password
|
|
||||||
export JWT_SECRET=your_256bit_secret_push
|
|
||||||
# 华为推送
|
|
||||||
export HUAWEI_APP_ID=your_huawei_app_id
|
|
||||||
export HUAWEI_APP_SECRET=your_huawei_secret
|
|
||||||
# 小米推送
|
|
||||||
export XIAOMI_APP_SECRET=your_xiaomi_secret
|
|
||||||
# iOS APNs
|
|
||||||
export APNS_KEY_ID=your_key_id
|
|
||||||
export APNS_TEAM_ID=your_team_id
|
|
||||||
export APNS_KEY_PATH=/opt/xuqm/apns_key.p8
|
|
||||||
export APNS_BUNDLE_ID=com.yourcompany.app
|
|
||||||
```
|
|
||||||
|
|
||||||
**update-service**(:8084)
|
|
||||||
```bash
|
|
||||||
export SPRING_DATASOURCE_URL=jdbc:mysql://db:3306/xuqm_update?...
|
|
||||||
export SPRING_DATASOURCE_USERNAME=xuqm
|
|
||||||
export SPRING_DATASOURCE_PASSWORD=your_db_password
|
|
||||||
export JWT_SECRET=your_256bit_secret_update
|
|
||||||
export UPDATE_UPLOAD_DIR=/data/xuqm/update
|
|
||||||
export UPDATE_BASE_URL=https://update.xuqm.com
|
|
||||||
```
|
|
||||||
|
|
||||||
### 启动
|
|
||||||
|
|
||||||
```bash
|
|
||||||
java -jar tenant-service/target/tenant-service-*.jar &
|
|
||||||
java -jar im-service/target/im-service-*.jar &
|
|
||||||
java -jar push-service/target/push-service-*.jar &
|
|
||||||
java -jar update-service/target/update-service-*.jar &
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 四、前端构建与部署
|
|
||||||
|
|
||||||
```bash
|
|
||||||
cd XuqmGroup-Web
|
|
||||||
yarn install
|
|
||||||
yarn workspace tenant-platform build # dist/ → /var/www/tenant
|
|
||||||
yarn workspace ops-platform build # dist/ → /var/www/ops
|
|
||||||
```
|
|
||||||
|
|
||||||
### Nginx 配置
|
|
||||||
|
|
||||||
```nginx
|
|
||||||
# 租户开放平台
|
|
||||||
server {
|
|
||||||
listen 80;
|
|
||||||
server_name tenant.xuqm.com;
|
|
||||||
root /var/www/tenant;
|
|
||||||
index index.html;
|
|
||||||
|
|
||||||
location / {
|
|
||||||
try_files $uri $uri/ /index.html;
|
|
||||||
}
|
|
||||||
|
|
||||||
location /api/ {
|
|
||||||
proxy_pass http://127.0.0.1:8081/api/;
|
|
||||||
proxy_set_header Host $host;
|
|
||||||
proxy_set_header X-Real-IP $remote_addr;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
# 运营管理平台
|
|
||||||
server {
|
|
||||||
listen 80;
|
|
||||||
server_name ops.xuqm.com;
|
|
||||||
root /var/www/ops;
|
|
||||||
index index.html;
|
|
||||||
|
|
||||||
location / {
|
|
||||||
try_files $uri $uri/ /index.html;
|
|
||||||
}
|
|
||||||
|
|
||||||
location /api/ {
|
|
||||||
proxy_pass http://127.0.0.1:8081/api/;
|
|
||||||
proxy_set_header Host $host;
|
|
||||||
proxy_set_header X-Real-IP $remote_addr;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
# IM WebSocket 反代
|
|
||||||
server {
|
|
||||||
listen 80;
|
|
||||||
server_name im.xuqm.com;
|
|
||||||
|
|
||||||
location /ws/im {
|
|
||||||
proxy_pass http://127.0.0.1:8082;
|
|
||||||
proxy_http_version 1.1;
|
|
||||||
proxy_set_header Upgrade $http_upgrade;
|
|
||||||
proxy_set_header Connection "upgrade";
|
|
||||||
proxy_read_timeout 86400;
|
|
||||||
}
|
|
||||||
|
|
||||||
location /api/im/ {
|
|
||||||
proxy_pass http://127.0.0.1:8082/api/im/;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
# 版本管理文件服务
|
|
||||||
server {
|
|
||||||
listen 80;
|
|
||||||
server_name update.xuqm.com;
|
|
||||||
|
|
||||||
location / {
|
|
||||||
proxy_pass http://127.0.0.1:8084/;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 五、Docker Compose(可选)
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
version: '3.9'
|
|
||||||
|
|
||||||
services:
|
|
||||||
mysql:
|
|
||||||
image: mysql:8.0
|
|
||||||
environment:
|
|
||||||
MYSQL_ROOT_PASSWORD: root
|
|
||||||
MYSQL_USER: xuqm
|
|
||||||
MYSQL_PASSWORD: xuqm_password
|
|
||||||
volumes:
|
|
||||||
- mysql_data:/var/lib/mysql
|
|
||||||
ports:
|
|
||||||
- "3306:3306"
|
|
||||||
|
|
||||||
redis:
|
|
||||||
image: redis:7-alpine
|
|
||||||
ports:
|
|
||||||
- "6379:6379"
|
|
||||||
|
|
||||||
tenant-service:
|
|
||||||
image: eclipse-temurin:21-jre
|
|
||||||
volumes:
|
|
||||||
- ./XuqmGroup-Server/tenant-service/target:/app
|
|
||||||
command: java -jar /app/tenant-service-0.1.0-SNAPSHOT.jar
|
|
||||||
environment:
|
|
||||||
SPRING_DATASOURCE_URL: jdbc:mysql://mysql:3306/xuqm_tenant?...
|
|
||||||
SPRING_DATA_REDIS_HOST: redis
|
|
||||||
ports:
|
|
||||||
- "8081:8081"
|
|
||||||
depends_on: [mysql, redis]
|
|
||||||
|
|
||||||
im-service:
|
|
||||||
image: eclipse-temurin:21-jre
|
|
||||||
volumes:
|
|
||||||
- ./XuqmGroup-Server/im-service/target:/app
|
|
||||||
command: java -jar /app/im-service-0.1.0-SNAPSHOT.jar
|
|
||||||
ports:
|
|
||||||
- "8082:8082"
|
|
||||||
depends_on: [mysql, redis]
|
|
||||||
|
|
||||||
push-service:
|
|
||||||
image: eclipse-temurin:21-jre
|
|
||||||
volumes:
|
|
||||||
- ./XuqmGroup-Server/push-service/target:/app
|
|
||||||
command: java -jar /app/push-service-0.1.0-SNAPSHOT.jar
|
|
||||||
ports:
|
|
||||||
- "8083:8083"
|
|
||||||
depends_on: [mysql]
|
|
||||||
|
|
||||||
update-service:
|
|
||||||
image: eclipse-temurin:21-jre
|
|
||||||
volumes:
|
|
||||||
- ./XuqmGroup-Server/update-service/target:/app
|
|
||||||
- update_files:/data/xuqm/update
|
|
||||||
command: java -jar /app/update-service-0.1.0-SNAPSHOT.jar
|
|
||||||
environment:
|
|
||||||
UPDATE_UPLOAD_DIR: /data/xuqm/update
|
|
||||||
ports:
|
|
||||||
- "8084:8084"
|
|
||||||
depends_on: [mysql]
|
|
||||||
|
|
||||||
volumes:
|
|
||||||
mysql_data:
|
|
||||||
update_files:
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 六、发版流程汇总
|
|
||||||
|
|
||||||
| 平台 | 步骤 |
|
|
||||||
|------|------|
|
|
||||||
| **后端** | `mvn clean package` → 替换 jar → 重启服务 |
|
|
||||||
| **前端** | `yarn build` → 替换 `dist/` → Nginx 无需重启 |
|
|
||||||
| **Android SDK** | 修改版本号 → `./gradlew publish` → Nexus |
|
|
||||||
| **iOS SDK (SPM)** | 修改版本号 → `git tag x.y.z && git push origin x.y.z` |
|
|
||||||
| **iOS SDK (CocoaPods)** | `pod repo push xuqm-specs XuqmSDK.podspec` |
|
|
||||||
| **RN SDK** | 修改 `package.json` version → `npm publish` |
|
|
||||||
| **Vue3 SDK** | 修改 `package.json` version → `npm run build && npm publish` |
|
|
||||||
| **HarmonyOS SDK** | 修改 `oh-package.json5` version → `ohpm publish` |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 七、健康检查
|
|
||||||
|
|
||||||
各服务均暴露 Spring Actuator 端点:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
curl http://localhost:8081/actuator/health
|
|
||||||
curl http://localhost:8082/actuator/health
|
|
||||||
curl http://localhost:8083/actuator/health
|
|
||||||
curl http://localhost:8084/actuator/health
|
|
||||||
```
|
|
||||||
|
|
||||||
正常返回:`{"status":"UP"}`
|
|
||||||
@ -1,89 +0,0 @@
|
|||||||
# XuqmGroup 平台文档总览
|
|
||||||
|
|
||||||
> 版本:0.1.0 | 最后更新:2026-04-21
|
|
||||||
|
|
||||||
## 仓库索引
|
|
||||||
|
|
||||||
| 仓库 | 语言/框架 | Gogs 地址 | 说明 |
|
|
||||||
|------|-----------|-----------|------|
|
|
||||||
| [XuqmGroup-Server](./server/README.md) | Java 21 / Spring Boot 3.4 | https://xuqinmin.com/xuqinmin12/XuqmGroup-Server | 后端多模块服务 |
|
|
||||||
| [XuqmGroup-Web](./web/README.md) | Vue 3.5 / TypeScript | https://xuqinmin.com/xuqinmin12/XuqmGroup-Web | 租户平台 + 运营平台前端 |
|
|
||||||
| [XuqmGroup-AndroidSDK](./android-sdk/README.md) | Kotlin 2.3 / AGP 9.1 | https://xuqinmin.com/xuqinmin12/XuqmGroup-AndroidSDK | Android SDK |
|
|
||||||
| [XuqmGroup-iOSSDK](./ios-sdk/README.md) | Swift 5.9 / iOS 16+ | https://xuqinmin.com/xuqinmin12/XuqmGroup-iOSSDK | iOS SDK |
|
|
||||||
| [XuqmGroup-RNSDK](./rn-sdk/README.md) | TypeScript / RN 0.76+ | https://xuqinmin.com/xuqinmin12/XuqmGroup-RNSDK | React Native SDK |
|
|
||||||
| [XuqmGroup-Vue3SDK](./vue3-sdk/README.md) | TypeScript / Vue 3.5 | https://xuqinmin.com/xuqinmin12/XuqmGroup-Vue3SDK | Vue3 Web SDK |
|
|
||||||
| [XuqmGroup-HarmonySDK](./harmony-sdk/README.md) | ArkTS / HarmonyOS 5 | https://xuqinmin.com/xuqinmin12/XuqmGroup-HarmonySDK | 鸿蒙 SDK |
|
|
||||||
|
|
||||||
## 整体架构
|
|
||||||
|
|
||||||
```
|
|
||||||
┌─────────────────────────────────────────────────────────────┐
|
|
||||||
│ 客户端层 │
|
|
||||||
│ Android SDK iOS SDK RN SDK Vue3 SDK HarmonyOS SDK │
|
|
||||||
└───────────────────────────┬─────────────────────────────────┘
|
|
||||||
│ HTTPS / WSS
|
|
||||||
┌───────────────────────────▼─────────────────────────────────┐
|
|
||||||
│ 服务端层 │
|
|
||||||
│ ┌────────────┐ ┌──────────┐ ┌───────────┐ ┌────────────┐ │
|
|
||||||
│ │tenant-svc │ │im-service│ │push-svc │ │update-svc │ │
|
|
||||||
│ │ :8081 │ │ :8082 │ │ :8083 │ │ :8084 │ │
|
|
||||||
│ └─────┬──────┘ └────┬─────┘ └─────┬─────┘ └──────┬─────┘ │
|
|
||||||
│ └─────────────┴─────────────┴───────────────┘ │
|
|
||||||
│ ↓ JPA / JDBC │
|
|
||||||
│ ┌──────────────────────────────────────────────────────┐ │
|
|
||||||
│ │ MySQL 8 (xuqm_tenant / xuqm_im / xuqm_push / │ │
|
|
||||||
│ │ xuqm_update) + Redis 7 │ │
|
|
||||||
│ └──────────────────────────────────────────────────────┘ │
|
|
||||||
└─────────────────────────────────────────────────────────────┘
|
|
||||||
|
|
||||||
┌─────────────────────────────────────────────────────────────┐
|
|
||||||
│ 前端层 │
|
|
||||||
│ 租户开放平台 :5173 运营管理平台 :5174 │
|
|
||||||
└─────────────────────────────────────────────────────────────┘
|
|
||||||
```
|
|
||||||
|
|
||||||
## 核心概念
|
|
||||||
|
|
||||||
| 概念 | 说明 |
|
|
||||||
|------|------|
|
|
||||||
| **租户 (Tenant)** | 在开放平台注册的主账号,可创建子账号 |
|
|
||||||
| **应用 (App)** | 租户创建的业务应用,具有唯一 appKey/appSecret |
|
|
||||||
| **功能服务 (FeatureService)** | 挂载在 App 下的服务实例(IM / 推送 / 版本管理),按平台(Android / iOS / HarmonyOS)独立开启 |
|
|
||||||
| **IM 账号** | 业务方通过 appKey 在 IM 服务创建的用户,仅存 userId,不存昵称/头像 |
|
|
||||||
| **运营平台** | 内部管理后台,独立账号体系,不与租户共用 |
|
|
||||||
|
|
||||||
## 统一响应格式
|
|
||||||
|
|
||||||
所有 HTTP 接口均返回:
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"code": 200,
|
|
||||||
"status": "0",
|
|
||||||
"data": { ... },
|
|
||||||
"message": "success"
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
| 字段 | 含义 |
|
|
||||||
|------|------|
|
|
||||||
| `code` | HTTP 语义码,200 成功,4xx/5xx 失败 |
|
|
||||||
| `status` | `"0"` 成功,`"1"` 业务失败 |
|
|
||||||
| `data` | 业务数据,失败时为 null |
|
|
||||||
| `message` | 错误描述 |
|
|
||||||
|
|
||||||
## 认证方式
|
|
||||||
|
|
||||||
- **租户平台**:`Authorization: Bearer <tenant_jwt>`,由 `POST /api/auth/login` 颁发
|
|
||||||
- **IM 服务**:`Authorization: Bearer <im_jwt>`,由 `POST /api/im/auth/login` 颁发;WebSocket 连接时通过 URL 参数 `?token=<im_jwt>` 传递
|
|
||||||
- **运营平台**:`Authorization: Bearer <ops_jwt>`,由 `POST /api/auth/ops/login` 颁发
|
|
||||||
|
|
||||||
## 发版信息
|
|
||||||
|
|
||||||
| 平台 | Registry | 命令 |
|
|
||||||
|------|----------|------|
|
|
||||||
| npm (RN SDK / Vue3 SDK) | https://nexus.xuqinmin.com/repository/npm-hosted/ | `npm publish` |
|
|
||||||
| Android Maven | https://nexus.xuqinmin.com/repository/android-hosted/ | `./gradlew publish` |
|
|
||||||
| iOS SPM | Git Tag | `git tag x.y.z && git push origin x.y.z` |
|
|
||||||
| iOS CocoaPods | https://xuqinmin.com/xuqinmin12/xuqm-specs | `pod repo push xuqm-specs XuqmSDK.podspec` |
|
|
||||||
| HarmonyOS | ohpm (OpenHarmony Package Manager) | `ohpm publish` |
|
|
||||||
@ -1,454 +0,0 @@
|
|||||||
# XuqmGroup Android SDK 集成测试计划
|
|
||||||
|
|
||||||
> **文档版本**: v2.0
|
|
||||||
> **适用版本**: SDK 0.4.x(UserSig 鉴权体系)
|
|
||||||
> **编写日期**: 2026-05-04
|
|
||||||
> **维护者**: AI Agent(可由任意智能体无缝接续)
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 一、文档目的
|
|
||||||
|
|
||||||
本文档描述 XuqmGroup Android SDK 的完整集成测试方案,覆盖以下目标:
|
|
||||||
|
|
||||||
1. 验证 SDK 核心功能(初始化、登录、IM、Push、Update)端到端正确性
|
|
||||||
2. 提供可供 AI Agent 无缝衔接执行的标准化操作步骤
|
|
||||||
3. 定义自动化与手工测试的边界,明确每项用例的执行方式
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 二、测试范围
|
|
||||||
|
|
||||||
### 2.1 模块覆盖
|
|
||||||
|
|
||||||
| 模块 | Gradle 模块 | 测试类型 |
|
|
||||||
|------|------------|---------|
|
|
||||||
| 核心(初始化/登录/登出) | `:sdk-core` | 自动化 + 手工 |
|
|
||||||
| IM 消息(单聊/群聊/会话) | `:sdk-im` | 自动化 + 手工 |
|
|
||||||
| Push(设备注册/厂商检测) | `:sdk-push` | 自动化(模拟器) |
|
|
||||||
| Update(版本检查/下载) | `:sdk-update` | 自动化 + 手工 |
|
|
||||||
| Sample App 端到端 UI | `:sample-app` | 手工 |
|
|
||||||
|
|
||||||
### 2.2 测试类型定义
|
|
||||||
|
|
||||||
| 类型 | 说明 | 工具 |
|
|
||||||
|------|------|------|
|
|
||||||
| **自动化集成测试** | 在真实后端上执行,无 Mock | AndroidJUnit4 Instrumented Test |
|
|
||||||
| **手工验证** | 需人工观察 UI 或硬件行为 | 肉眼 / Logcat |
|
|
||||||
| **Agent 测试** | 由 AI Agent 通过 adb/Gradle 自动执行 | Claude Code + adb shell |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 三、测试环境
|
|
||||||
|
|
||||||
### 3.1 硬件 / 软件环境
|
|
||||||
|
|
||||||
| 项目 | 配置 |
|
|
||||||
|------|------|
|
|
||||||
| 操作系统 | macOS Sequoia 15.x |
|
|
||||||
| Android SDK | `/Users/xuqinmin/Library/Android/sdk` |
|
|
||||||
| AGP | 9.1.0 |
|
|
||||||
| Gradle | 8.9(Wrapper) |
|
|
||||||
| JDK | OpenJDK 21 |
|
|
||||||
| Kotlin | 2.3.10 |
|
|
||||||
| compileSdk / targetSdk | 36 |
|
|
||||||
| minSdk | 24(Android 7.0) |
|
|
||||||
|
|
||||||
### 3.2 模拟器配置
|
|
||||||
|
|
||||||
| 标识 | AVD 名称 | 端口 | 角色 |
|
|
||||||
|------|---------|------|------|
|
|
||||||
| emulator-5554 | Pixel_9_Pro | 5554 | 主设备(user_a) |
|
|
||||||
| emulator-5556 | Pixel_9_Pro_2 | 5556 | 副设备(user_b) |
|
|
||||||
|
|
||||||
**启动命令**(Agent 可直接执行):
|
|
||||||
```bash
|
|
||||||
export ANDROID_HOME=/Users/xuqinmin/Library/Android/sdk
|
|
||||||
$ANDROID_HOME/emulator/emulator -avd Pixel_9_Pro -no-audio -no-boot-anim -no-snapshot-save &
|
|
||||||
$ANDROID_HOME/emulator/emulator -avd Pixel_9_Pro_2 -no-audio -no-boot-anim -no-snapshot-save &
|
|
||||||
# 等待双设备就绪
|
|
||||||
adb wait-for-device && sleep 15
|
|
||||||
```
|
|
||||||
|
|
||||||
### 3.3 后端服务
|
|
||||||
|
|
||||||
| 模式 | 地址 | 适用场景 |
|
|
||||||
|------|------|---------|
|
|
||||||
| **外部(推荐)** | `https://dev.xuqinmin.com` | 自动化测试(稳定) |
|
|
||||||
| 本地 | `http://192.168.113.37` | 开发调试 |
|
|
||||||
|
|
||||||
### 3.4 测试账号
|
|
||||||
|
|
||||||
| 账号 | 密码 | appKey | 用途 |
|
|
||||||
|------|------|-------|------|
|
|
||||||
| user_a | 123456 | ak_demo_chat | 主发送方 |
|
|
||||||
| user_b | 123456 | ak_demo_chat | 接收方 |
|
|
||||||
| user_ascii | 123456 | ak_demo_chat | ASCII 专项 |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 四、测试用例清单
|
|
||||||
|
|
||||||
### TC-01 SDK 初始化测试
|
|
||||||
|
|
||||||
| 字段 | 内容 |
|
|
||||||
|------|------|
|
|
||||||
| **测试类型** | 自动化(含于 @BeforeClass) |
|
|
||||||
| **优先级** | P0 |
|
|
||||||
| **前置条件** | 无 |
|
|
||||||
| **测试步骤** | 1. 调用 `XuqmSDK.initialize(context, "ak_demo_chat")` <br> 2. 调用 `XuqmSDK.requireInit()` <br> 3. 检查 `XuqmSDK.config`、`tokenStore` 是否已赋值 |
|
|
||||||
| **预期结果** | 初始化成功,无异常;ServiceEndpoints 使用内置生产环境地址 |
|
|
||||||
| **自动化代码** | `SdkIntegrationTest.@BeforeClass initSdk()` |
|
|
||||||
| **通过状态** | ✅ 通过 |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### TC-02 IM 登录 / 登出测试
|
|
||||||
|
|
||||||
| 字段 | 内容 |
|
|
||||||
|------|------|
|
|
||||||
| **测试类型** | 自动化(含于 @Before/@After) |
|
|
||||||
| **优先级** | P0 |
|
|
||||||
| **前置条件** | SDK 已初始化 |
|
|
||||||
| **测试步骤** | 1. 调用 Demo API 获取 imToken <br> 2. 调用 `XuqmSDK.login(userId, userSig)` <br> 3. 等待 `ImSDK.connectionState == Connected`(超时 20s) <br> 4. 调用 `XuqmSDK.logout()` <br> 5. 确认 connectionState → Disconnected |
|
|
||||||
| **预期结果** | WebSocket 101 连接建立,STOMP CONNECTED;登出后 TokenStore 清空 |
|
|
||||||
| **自动化代码** | `SdkIntegrationTest.setUp()` / `tearDown()` |
|
|
||||||
| **通过状态** | ✅ 通过 |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### TC-03 单聊消息收发测试
|
|
||||||
|
|
||||||
| 字段 | 内容 |
|
|
||||||
|------|------|
|
|
||||||
| **测试类型** | 自动化(含于 TC-05 前置步骤)+ 可选手工复验 |
|
|
||||||
| **优先级** | P0 |
|
|
||||||
| **前置条件** | user_a、user_b 均已登录 |
|
|
||||||
| **双设备方案** | emulator-5554(user_a) → 发送;emulator-5556(user_b) → 接收回调 |
|
|
||||||
| **测试步骤** | 1. user_a: `ImSDK.sendTextMessage("user_b", "SINGLE", content)` <br> 2. user_b: 监听 `ImEventListener.onMessage()` <br> 3. user_b: `fetchHistory("user_a")` 验证分页 <br> 4. user_b: `markRead("user_a")` <br> 5. user_a: 查询 history,确认 status=READ |
|
|
||||||
| **预期结果** | 消息 status≠FAILED;user_b 实时收到;未读归零 |
|
|
||||||
| **通过状态** | ✅ 通过(已手工验证) |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### TC-04 群聊消息收发测试
|
|
||||||
|
|
||||||
| 字段 | 内容 |
|
|
||||||
|------|------|
|
|
||||||
| **测试类型** | 手工(含 Agent 辅助) |
|
|
||||||
| **优先级** | P1 |
|
|
||||||
| **双设备方案** | emulator-5554(user_a) 创建群、发消息;emulator-5556(user_b) 订阅、接收 |
|
|
||||||
| **测试步骤** | 1. user_a: `createGroup("TestGroup", ["user_b"])` <br> 2. user_a: `subscribeGroup(groupId)` → 发送群消息 <br> 3. user_b: `subscribeGroup(groupId)` → `onGroupMessage()` <br> 4. 双端: `fetchGroupHistory(groupId)` <br> 5. 双端: `listConversations()` 确认群会话 |
|
|
||||||
| **预期结果** | 群创建成功;双端消息收发正常;历史分页正确 |
|
|
||||||
| **通过状态** | ✅ 通过(群会话聚合 Bug 已修复复验) |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### TC-05 会话列表 / 置顶 / 静音 / 草稿 / 已读 / 隐藏测试
|
|
||||||
|
|
||||||
| 字段 | 内容 |
|
|
||||||
|------|------|
|
|
||||||
| **测试类型** | 自动化 |
|
|
||||||
| **优先级** | P1 |
|
|
||||||
| **前置条件** | user_a 已登录且 WebSocket 已连接;@BeforeClass 已创建 testGroupId(GROUP 会话) |
|
|
||||||
| **设计说明** | 使用 @BeforeClass 新建的群(testGroupId)而非 SINGLE 会话,原因:`deleteConversation` 在服务端为永久删除,重跑时 SINGLE 会话无法通过 SDK 恢复,导致测试幂等性破坏。GROUP 会话每次运行新建,彻底隔离状态。`deleteConversation` 功能移至 TC-99 专测。 |
|
|
||||||
| **测试步骤** | 1. `subscribeGroup(testGroupId)` + 发送探针消息 <br> 2. 轮询 `listConversations()` 直到 GROUP 会话出现(最长 15s) <br> 3. `setConversationPinned(testGroupId, "GROUP", true)` → `isPinned=true` <br> 4. `setConversationMuted(testGroupId, "GROUP", true)` → `isMuted=true` <br> 5. `setDraft(testGroupId, "GROUP", "草稿内容")` → 不抛异常 <br> 6. `markRead(testGroupId, "GROUP")` → `unreadCount=0` <br> 7. 清理置顶/静音 <br> 8. `setConversationHidden(testGroupId, "GROUP", true)` → 列表不再出现 <br> 9. `setConversationHidden(testGroupId, "GROUP", false)` → 恢复(保证后续测试可用) |
|
|
||||||
| **预期结果** | 全步骤无异常;置顶/静音/已读状态服务端持久化正确;隐藏/恢复可逆 |
|
|
||||||
| **自动化代码** | `SdkIntegrationTest.tc05_conversationManagement()` |
|
|
||||||
| **执行模拟器** | emulator-5554 + emulator-5556 |
|
|
||||||
| **通过状态** | ✅ 通过(双模拟器) |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### TC-99 会话永久删除测试
|
|
||||||
|
|
||||||
| 字段 | 内容 |
|
|
||||||
|------|------|
|
|
||||||
| **测试类型** | 自动化 |
|
|
||||||
| **优先级** | P2 |
|
|
||||||
| **前置条件** | user_a 已登录;testGroupId 有效(@BeforeClass 创建) |
|
|
||||||
| **设计说明** | 放在最后(名称字典序 tc99 最大);每次运行使用当次创建的 testGroupId,不影响后续运行(下次 @BeforeClass 创建新群)。 |
|
|
||||||
| **测试步骤** | 1. `subscribeGroup(testGroupId)` + 发送消息确保群会话存在 <br> 2. 轮询 `listConversations()` 确认群会话可见 <br> 3. `deleteConversation(testGroupId, "GROUP")` <br> 4. 查询 `listConversations()`,断言群会话已消失 |
|
|
||||||
| **预期结果** | deleteConversation 后目标群会话从列表中永久移除 |
|
|
||||||
| **自动化代码** | `SdkIntegrationTest.tc99_deleteConversation()` |
|
|
||||||
| **通过状态** | ✅ 通过 |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### TC-06 Push 设备注册测试
|
|
||||||
|
|
||||||
| 字段 | 内容 |
|
|
||||||
|------|------|
|
|
||||||
| **测试类型** | 自动化(模拟器场景) + 真机手工(Firebase 场景) |
|
|
||||||
| **优先级** | P1 |
|
|
||||||
| **前置条件** | SDK 已初始化,user_a 已登录 |
|
|
||||||
| **测试步骤(自动化-模拟器)** | 1. `detectVendor()` → FCM <br> 2. `initializeVendors(context)` → 不崩溃 <br> 3. `currentRegistration(context)?.pushToken` → null(无 Firebase) <br> 4. `setReceivePush(context, userId, false)` → 接口调用正常 <br> 5. `setReceivePush(context, userId, true)` → 恢复 |
|
|
||||||
| **测试步骤(手工-真机)** | 1. Firebase 设备:`registerDevice()` → `/api/push/register` 返回 200 <br> 2. `setReceivePush(false)` → `/api/push/receive` 设为 false <br> 3. 登出 → `/api/push/unregister` 返回 200 |
|
|
||||||
| **自动化代码** | `PushSdkTest.tc06_pushRegistrationEmulator()` |
|
|
||||||
| **通过状态** | ✅ 通过(模拟器场景);真机场景待真机验证 |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### TC-07 版本更新检查测试
|
|
||||||
|
|
||||||
| 字段 | 内容 |
|
|
||||||
|------|------|
|
|
||||||
| **测试类型** | 自动化 |
|
|
||||||
| **优先级** | P2 |
|
|
||||||
| **前置条件** | SDK 已初始化,user_a 已登录 |
|
|
||||||
| **测试步骤** | 1. `UpdateSDK.checkAppUpdate(context)` <br> 2. 返回 `UpdateInfo?`:若非 null 断言 `versionCode > 0`、`versionName` 非空 <br> 3. 若 `needsUpdate=true`,验证 `downloadUrl` 经过 normalizeDownloadUrl 处理 |
|
|
||||||
| **预期结果** | 接口无异常;UpdateInfo 字段完整(若有更新) |
|
|
||||||
| **自动化代码** | `SdkIntegrationTest.tc07_updateCheck()` |
|
|
||||||
| **通过状态** | ✅ 通过 |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### TC-08 UserSig 匹配重登测试
|
|
||||||
|
|
||||||
| 字段 | 内容 |
|
|
||||||
|------|------|
|
|
||||||
| **测试类型** | 自动化 |
|
|
||||||
| **优先级** | P1 |
|
|
||||||
| **前置条件** | user_a 已登录,WebSocket Connected |
|
|
||||||
| **测试步骤** | 1. 记录初始 `connectionState == Connected` <br> 2. 不登出,再次调用 Demo API 获取 token(可能相同或不同) <br> 3. `XuqmSDK.login(userId, newToken)` <br> 4. 等待 `connectionState == Connected`(超时 20s) <br> 5. 断言 `currentLoginSession.userId == user_a` |
|
|
||||||
| **预期结果** | token 相同→跳过重连保持 Connected;token 不同→无缝重连恢复 Connected;无内存泄漏 |
|
|
||||||
| **自动化代码** | `SdkIntegrationTest.tc08_reLogin()` |
|
|
||||||
| **通过状态** | ✅ 通过 |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### TC-09 多厂商 Push 检测测试
|
|
||||||
|
|
||||||
| 字段 | 内容 |
|
|
||||||
|------|------|
|
|
||||||
| **测试类型** | 自动化 |
|
|
||||||
| **优先级** | P2 |
|
|
||||||
| **前置条件** | SDK 已初始化 |
|
|
||||||
| **测试步骤** | 1. 读取 `Build.MANUFACTURER.uppercase()` <br> 2. 按映射表验证 `detectVendor()` 返回值:HUAWEI→HUAWEI, XIAOMI→XIAOMI, OPPO→OPPO, VIVO→VIVO, HONOR→HONOR, 其他→FCM <br> 3. 验证 `initializeVendors()` 仅初始化匹配厂商服务 |
|
|
||||||
| **预期结果** | emulator(MANUFACTURER=Google)→ FCM;真机按品牌正确识别 |
|
|
||||||
| **自动化代码** | `PushSdkTest.tc09_vendorDetection()` |
|
|
||||||
| **执行模拟器** | emulator-5554 + emulator-5556 |
|
|
||||||
| **通过状态** | ✅ 通过(双模拟器) |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### TC-10 网络断开 / 自动重连测试
|
|
||||||
|
|
||||||
| 字段 | 内容 |
|
|
||||||
|------|------|
|
|
||||||
| **测试类型** | 自动化(Instrumented,反射注入) |
|
|
||||||
| **优先级** | P1 |
|
|
||||||
| **前置条件** | user_a 已登录,WebSocket Connected |
|
|
||||||
| **测试步骤(自动化)** | 1. 记录 `connectionState == Connected` <br> 2. 反射获取 `ImSDK.client` 字段 <br> 3. 调用 `ImClient.disconnect()`(不修改 `reconnectEnabled`) <br> 4. 等待 `connectionState == Disconnected`(超时 5s) <br> 5. 等待 `connectionState == Connected`(超时 15s,退避首次 1s) <br> 6. 验证重连后能发送消息 |
|
|
||||||
| **测试步骤(WiFi 级)** | `bash scripts/tc10_network_resilience.sh emulator-5554` |
|
|
||||||
| **预期结果** | SDK 在 15s 内自动重连;重连后消息发送正常 |
|
|
||||||
| **自动化代码** | `NetworkResilienceTest.tc10_autoReconnectAfterSocketClose()` |
|
|
||||||
| **执行模拟器** | emulator-5554 + emulator-5556 |
|
|
||||||
| **通过状态** | ✅ 通过(双模拟器,5.5s / 5.6s) |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### TC-11 消息撤回 / 编辑测试
|
|
||||||
|
|
||||||
| 字段 | 内容 |
|
|
||||||
|------|------|
|
|
||||||
| **测试类型** | 自动化 |
|
|
||||||
| **优先级** | P2 |
|
|
||||||
| **前置条件** | user_a 已登录,WebSocket Connected;testGroupId 有效(@BeforeClass 创建) |
|
|
||||||
| **设计说明** | 使用 testGroupId(GROUP)发送和查询消息,避免依赖 SINGLE 会话历史(SINGLE 会话可能被 deleteConversation 永久删除导致 fetchHistory 返回空)。`fetchGroupHistory` 独立于会话记录,始终可用。 |
|
|
||||||
| **测试步骤(TC-11a 撤回)** | 1. `subscribeGroup(testGroupId)` <br> 2. 发送带唯一标签的群消息 <br> 3. 轮询 `fetchGroupHistory(testGroupId)` 按内容匹配服务端 ID(最长 10s) <br> 4. `revokeMessage(id)` <br> 5. 断言返回消息 `status == "REVOKED"` |
|
|
||||||
| **测试步骤(TC-11b 编辑)** | 1. `subscribeGroup(testGroupId)` <br> 2. 发送带唯一标签群消息 → 轮询 `fetchGroupHistory` 内容匹配 ID <br> 3. `editMessage(id, newContent)` <br> 4. 断言 `editedAt != null` 且 `content == newContent` |
|
|
||||||
| **预期结果** | 撤回后 status="REVOKED";编辑后 editedAt 有值且 content 更新 |
|
|
||||||
| **自动化代码** | `SdkIntegrationTest.tc11a_revokeMessage()` / `tc11b_editMessage()` |
|
|
||||||
| **通过状态** | ✅ 通过 |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### TC-12 文件消息发送测试
|
|
||||||
|
|
||||||
| 字段 | 内容 |
|
|
||||||
|------|------|
|
|
||||||
| **测试类型** | 自动化 |
|
|
||||||
| **优先级** | P2 |
|
|
||||||
| **前置条件** | user_a 已登录,WebSocket Connected |
|
|
||||||
| **测试步骤** | 1. 在 `cacheDir` 创建临时文本文件 <br> 2. `ImSDK.sendFileMessage(toId, chatType, file)` <br> 3. 断言 `msg.status != "FAILED"` <br> 4. 断言 `msg.content` 解析为 JSON 且含 `url` 字段 <br> 5. 清理临时文件 |
|
|
||||||
| **预期结果** | 文件上传成功;消息 content 含有效 URL;status 非 FAILED |
|
|
||||||
| **自动化代码** | `SdkIntegrationTest.tc12_sendFileMessage()` |
|
|
||||||
| **通过状态** | ✅ 通过 |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### TC-03 单聊消息收发(双设备自动化)
|
|
||||||
|
|
||||||
| 字段 | 内容 |
|
|
||||||
|------|------|
|
|
||||||
| **测试类型** | 自动化(跨设备轮询) |
|
|
||||||
| **优先级** | P0 |
|
|
||||||
| **执行顺序** | 1. emulator-5554 运行 `CrossDeviceSenderTest`(user_a 发送)<br> 2. emulator-5556 运行 `CrossDeviceReceiverTest`(user_b 轮询接收) |
|
|
||||||
| **测试步骤** | 发送方: `sendTextMessage(user_b, SINGLE, CROSS_DEVICE_AUTO_SINGLE_...)` <br> 接收方: 轮询 `fetchHistory("user_a")` 找含 CROSS_DEVICE_AUTO 的消息(最长 40s)<br> 接收方: `markRead("user_a")` → 验证 unreadCount=0 |
|
|
||||||
| **通过状态** | ✅ 通过(双模拟器,12.66s) |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### TC-04 群聊消息收发(双设备自动化)
|
|
||||||
|
|
||||||
| 字段 | 内容 |
|
|
||||||
|------|------|
|
|
||||||
| **测试类型** | 自动化(跨设备轮询) |
|
|
||||||
| **优先级** | P1 |
|
|
||||||
| **执行顺序** | 与 TC-03 同批(CrossDeviceSenderTest + CrossDeviceReceiverTest) |
|
|
||||||
| **测试步骤** | 发送方: `createGroup(TC04_AUTO_GROUP_xxx, [user_b])` → 订阅 → 发消息 <br> 接收方: `listGroups()` 找 TC04_AUTO_GROUP 前缀最新群 → `fetchGroupHistory` 找消息 |
|
|
||||||
| **通过状态** | ✅ 通过(双模拟器) |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 五、Agent 执行手册
|
|
||||||
|
|
||||||
本节提供可供 AI Agent(Claude Code 或其他)直接无缝接续执行的完整命令集。
|
|
||||||
|
|
||||||
### 5.1 前置检查
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# 检查 adb 连接
|
|
||||||
export ANDROID_HOME=/Users/xuqinmin/Library/Android/sdk
|
|
||||||
$ANDROID_HOME/platform-tools/adb devices
|
|
||||||
# 预期: emulator-5554 device / emulator-5556 device
|
|
||||||
|
|
||||||
# 若模拟器未启动
|
|
||||||
$ANDROID_HOME/emulator/emulator -avd Pixel_9_Pro -no-audio -no-boot-anim -no-snapshot-save &
|
|
||||||
$ANDROID_HOME/emulator/emulator -avd Pixel_9_Pro_2 -no-audio -no-boot-anim -no-snapshot-save &
|
|
||||||
sleep 30 # 等待启动
|
|
||||||
```
|
|
||||||
|
|
||||||
### 5.2 构建 APK
|
|
||||||
|
|
||||||
```bash
|
|
||||||
cd /Users/xuqinmin/Projects/XuqmGroup/XuqmGroup-AndroidSDK
|
|
||||||
|
|
||||||
# 构建主 APK + 测试 APK
|
|
||||||
./gradlew :sample-app:assembleDebug :sample-app:assembleDebugAndroidTest
|
|
||||||
|
|
||||||
# 验证产物
|
|
||||||
ls sample-app/build/outputs/apk/debug/sample-app-debug.apk
|
|
||||||
ls sample-app/build/outputs/apk/androidTest/debug/sample-app-debug-androidTest.apk
|
|
||||||
```
|
|
||||||
|
|
||||||
### 5.3 安装到双模拟器
|
|
||||||
|
|
||||||
```bash
|
|
||||||
APK=sample-app/build/outputs/apk/debug/sample-app-debug.apk
|
|
||||||
TEST_APK=sample-app/build/outputs/apk/androidTest/debug/sample-app-debug-androidTest.apk
|
|
||||||
|
|
||||||
for DEV in emulator-5554 emulator-5556; do
|
|
||||||
adb -s $DEV install -r "$APK"
|
|
||||||
adb -s $DEV install -r "$TEST_APK"
|
|
||||||
done
|
|
||||||
```
|
|
||||||
|
|
||||||
### 5.4 执行自动化测试
|
|
||||||
|
|
||||||
```bash
|
|
||||||
RUNNER="androidx.test.runner.AndroidJUnitRunner"
|
|
||||||
TEST_PKG="com.xuqm.demo.test"
|
|
||||||
|
|
||||||
# ── 单设备全量(TC-01~TC-12, 非跨设备部分)──
|
|
||||||
ALL_SINGLE="com.xuqm.sdk.sample.SdkIntegrationTest,com.xuqm.sdk.sample.PushSdkTest,com.xuqm.sdk.sample.NetworkResilienceTest"
|
|
||||||
adb -s emulator-5554 shell am instrument -w -r -e class "$ALL_SINGLE" "${TEST_PKG}/${RUNNER}"
|
|
||||||
adb -s emulator-5556 shell am instrument -w -r -e class "$ALL_SINGLE" "${TEST_PKG}/${RUNNER}"
|
|
||||||
|
|
||||||
# ── 跨设备 TC-03/04(先发送方,再接收方)──
|
|
||||||
adb -s emulator-5554 shell am instrument -w -r \
|
|
||||||
-e class "com.xuqm.sdk.sample.CrossDeviceSenderTest" "${TEST_PKG}/${RUNNER}"
|
|
||||||
adb -s emulator-5556 shell am instrument -w -r \
|
|
||||||
-e class "com.xuqm.sdk.sample.CrossDeviceReceiverTest" "${TEST_PKG}/${RUNNER}"
|
|
||||||
```
|
|
||||||
|
|
||||||
**或使用 Gradle(自动检测所有连接设备,单设备用例):**
|
|
||||||
```bash
|
|
||||||
./gradlew :sample-app:connectedDebugAndroidTest
|
|
||||||
```
|
|
||||||
|
|
||||||
### 5.5 判断通过标准
|
|
||||||
|
|
||||||
```
|
|
||||||
每个测试用例输出:
|
|
||||||
INSTRUMENTATION_STATUS_CODE: 0 → 通过
|
|
||||||
INSTRUMENTATION_STATUS_CODE: -2 → 失败(查看 stack 字段)
|
|
||||||
|
|
||||||
全套通过期望(单设备 13 个用例):
|
|
||||||
Tests run: 13, Failures: 0 (SdkIntegrationTest×10 + PushSdkTest×2 + NetworkResilienceTest×1)
|
|
||||||
|
|
||||||
跨设备(各 2 个用例):
|
|
||||||
CrossDeviceSenderTest: Tests run: 2, Failures: 0
|
|
||||||
CrossDeviceReceiverTest: Tests run: 2, Failures: 0
|
|
||||||
```
|
|
||||||
|
|
||||||
### 5.6 查看 Logcat(失败时诊断)
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# 过滤 SDK 相关日志
|
|
||||||
adb -s emulator-5554 logcat -d -s "XuqmImSDK:D" "XuqmPushSDK:W" "XuqmUpdateSDK:D"
|
|
||||||
|
|
||||||
# TC-10 网络韧性(实时监控重连事件)
|
|
||||||
adb -s emulator-5554 logcat -s "XuqmImSDK:D" | grep -E "onConnected|onDisconnected|scheduleReconnect|connectWithToken"
|
|
||||||
```
|
|
||||||
|
|
||||||
### 5.7 TC-10 WiFi 断开(可选主机级脚本)
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# 使用 adb 从主机切换模拟器网络(需先确保 App 已登录)
|
|
||||||
bash scripts/tc10_network_resilience.sh emulator-5554
|
|
||||||
```
|
|
||||||
|
|
||||||
> 推荐方式: 使用 `NetworkResilienceTest`(Section 5.4 已包含),通过反射直接关闭 WebSocket,无需登录状态依赖。
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 六、测试文件路径索引
|
|
||||||
|
|
||||||
| 文件 | 作用 |
|
|
||||||
|------|------|
|
|
||||||
| `sample-app/src/androidTest/java/com/xuqm/sdk/sample/SdkIntegrationTest.kt` | TC-05 / TC-07 / TC-08 / TC-11 / TC-12 / TC-13 / TC-14 / TC-15 自动化 |
|
|
||||||
| `sample-app/src/androidTest/java/com/xuqm/sdk/sample/PushSdkTest.kt` | TC-06 / TC-09 Push 自动化 |
|
|
||||||
| `sample-app/src/androidTest/java/com/xuqm/sdk/sample/NetworkResilienceTest.kt` | TC-10 网络韧性自动化 |
|
|
||||||
| `sample-app/src/androidTest/java/com/xuqm/sdk/sample/CrossDeviceTest.kt` | TC-03 / TC-04 双设备协同自动化 |
|
|
||||||
| `scripts/tc10_network_resilience.sh` | TC-10 WiFi 级别网络断开脚本(主机运行) |
|
|
||||||
| `sample-app/build.gradle.kts` | 已添加 `testInstrumentationRunner` 及测试依赖 |
|
|
||||||
| `TEST_REPORT.md` | 每轮测试结果记录 |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 七、测试汇总(当前状态)
|
|
||||||
|
|
||||||
| 用例 | 名称 | 类型 | 自动化 | 状态 |
|
|
||||||
|------|------|------|-------|------|
|
|
||||||
| TC-01 | SDK 初始化 | 核心 | ✅ @BeforeClass | ✅ 通过 |
|
|
||||||
| TC-02 | IM 登录/登出 | 核心 | ✅ @Before/@After | ✅ 通过 |
|
|
||||||
| TC-03 | 单聊消息收发 | IM | ✅ CrossDeviceTest | ✅ 通过(双模拟器) |
|
|
||||||
| TC-04 | 群聊消息收发 | IM | ✅ CrossDeviceTest | ✅ 通过(双模拟器) |
|
|
||||||
| TC-05 | 会话管理(置顶/静音/已读/删除) | IM | ✅ SdkIntegrationTest | ✅ 通过 |
|
|
||||||
| TC-06 | Push 设备注册 | Push | ✅ PushSdkTest | ✅ 通过(模拟器 FCM) |
|
|
||||||
| TC-07 | 版本更新检查 | Update | ✅ SdkIntegrationTest | ✅ 通过 |
|
|
||||||
| TC-08 | UserSig 重登 | 核心 | ✅ SdkIntegrationTest | ✅ 通过 |
|
|
||||||
| TC-09 | 多厂商 Push 检测 | Push | ✅ PushSdkTest | ✅ 通过(双模拟器) |
|
|
||||||
| TC-10 | 网络断开/自动重连 | 核心 | ✅ NetworkResilienceTest | ✅ 通过(双模拟器) |
|
|
||||||
| TC-11a | 消息撤回 | IM | ✅ SdkIntegrationTest | ✅ 通过 |
|
|
||||||
| TC-11b | 消息编辑 | IM | ✅ SdkIntegrationTest | ✅ 通过 |
|
|
||||||
| TC-12 | 文件消息发送 | IM | ✅ SdkIntegrationTest | ✅ 通过 |
|
|
||||||
| TC-13 | 音频消息发送 | IM | ✅ SdkIntegrationTest | ✅ 通过 |
|
|
||||||
| TC-14 | 消息关键词搜索 | IM | ✅ SdkIntegrationTest | ✅ 通过 |
|
|
||||||
| TC-15 | 好友管理与黑名单 | IM | ✅ SdkIntegrationTest | ✅ 通过 |
|
|
||||||
| TC-99 | 会话永久删除(GROUP) | IM | ✅ SdkIntegrationTest | ✅ 通过 |
|
|
||||||
|
|
||||||
> **总计**: 17 用例 / 17 通过 | **自动化覆盖率: 17/17(100%)**
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 八、后续扩展建议
|
|
||||||
|
|
||||||
| 扩展项 | 优先级 | 说明 |
|
|
||||||
|--------|--------|------|
|
|
||||||
| TC-06 真机 Firebase 验证 | P1 | 在 Pixel/Samsung 真机上验证 FCM token 注册流程,`/api/push/register` 返回 200 |
|
|
||||||
| TC-10 WiFi 级压测 | P2 | 使用 `scripts/tc10_network_resilience.sh` 在已登录 App 上做 WiFi 级断网验证 |
|
|
||||||
| TC-16 音视频通话(预留) | P3 | 待 RTC 模块上线后补充 |
|
|
||||||
| CI/CD 集成 | P1 | Jenkins Job `xuqmgroup-android-sdk-test`,`connectedDebugAndroidTest` 自动触发;见 `Jenkinsfile` |
|
|
||||||
正在加载...
在新工单中引用
屏蔽一个用户