Files
observer/server/biz/service/app_version_test.go
T
2026-08-25 17:17:06 +08:00

229 lines
7.4 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package service
// App 版本管理白盒测试:下发(APK 上传 + 记录)、最新版本查询、重复版本拒绝、
// APK 固定文件覆盖(目录永远只有一个文件)。
// 运行方式同 admin_test.gocd server && GF_GCFG_FILE=biz/service/testdata/config.yml go test ./biz/service/
import (
"bytes"
"mime/multipart"
"os"
"path/filepath"
"strconv"
"testing"
"time"
"github.com/gogf/gf/v2/net/ghttp"
"observer-server/biz/dao"
"observer-server/biz/model/dto"
"observer-server/common"
)
// uniqueVersion 每次运行生成唯一版本号,避免测试库残留数据冲突
func uniqueVersion() string {
return "9." + strconv.FormatInt(time.Now().UnixNano()%100000000, 10) + ".0"
}
// newApkUpload 构造真实 multipart 上传文件(Save 需要可读的文件内容)
func newApkUpload(t *testing.T, filename, content string) *ghttp.UploadFile {
t.Helper()
var buf bytes.Buffer
w := multipart.NewWriter(&buf)
fw, err := w.CreateFormFile("file", filename)
if err != nil {
t.Fatal(err)
}
if _, err := fw.Write([]byte(content)); err != nil {
t.Fatal(err)
}
w.Close()
form, err := multipart.NewReader(&buf, w.Boundary()).ReadForm(1 << 20)
if err != nil {
t.Fatal(err)
}
return &ghttp.UploadFile{FileHeader: form.File["file"][0]}
}
// countApk 目录下 apk 文件数(不含临时文件)
func countApk(t *testing.T) int {
t.Helper()
entries, err := os.ReadDir(common.ApkDir(ctx()))
if err != nil {
t.Fatal(err)
}
n := 0
for _, e := range entries {
if filepath.Ext(e.Name()) == ".apk" {
n++
}
}
return n
}
// TestAppVersionUpdate 下发:落库 + APK 保存固定文件,更新检查返回最新记录
func TestAppVersionUpdate(t *testing.T) {
ver := uniqueVersion()
if _, err := AppVersion.AdminAddVersion(ctx(), &dto.AdminAppVersionAddReq{
Version: ver, Notes: "修复识别准确率", File: newApkUpload(t, "v1.apk", "apk-v1"),
}); err != nil {
t.Fatalf("add version: %v", err)
}
res, err := AppVersion.GetUpdate(ctx(), &dto.AppUpdateReq{})
if err != nil {
t.Fatalf("get update: %v", err)
}
if res.Version != ver || res.Notes != "修复识别准确率" {
t.Fatalf("update = %+v", res)
}
// APK 已保存为固定文件名
content, err := os.ReadFile(common.ApkFilePath(ctx()))
if err != nil || string(content) != "apk-v1" {
t.Fatalf("apk file = %q, %v", content, err)
}
if n := countApk(t); n != 1 {
t.Fatalf("apk count = %d, want 1", n)
}
}
// TestAppVersionOverwrite 再次下发:固定文件被新内容覆盖,目录仍只有一个文件
func TestAppVersionOverwrite(t *testing.T) {
ver1 := uniqueVersion()
ver2 := uniqueVersion() + "1"
req1 := &dto.AdminAppVersionAddReq{Version: ver1, File: newApkUpload(t, "a.apk", "apk-old")}
req2 := &dto.AdminAppVersionAddReq{Version: ver2, File: newApkUpload(t, "b.apk", "apk-new")}
if _, err := AppVersion.AdminAddVersion(ctx(), req1); err != nil {
t.Fatalf("add v1: %v", err)
}
if _, err := AppVersion.AdminAddVersion(ctx(), req2); err != nil {
t.Fatalf("add v2: %v", err)
}
content, err := os.ReadFile(common.ApkFilePath(ctx()))
if err != nil || string(content) != "apk-new" {
t.Fatalf("apk file = %q, %v; want new content", content, err)
}
if n := countApk(t); n != 1 {
t.Fatalf("apk count = %d, want 1", n)
}
}
// TestAppVersionInvalidFile 无文件 / 非 .apk 文件拒绝
func TestAppVersionInvalidFile(t *testing.T) {
ver := uniqueVersion()
if _, err := AppVersion.AdminAddVersion(ctx(), &dto.AdminAppVersionAddReq{Version: ver}); err == nil {
t.Fatal("missing file should fail")
}
if _, err := AppVersion.AdminAddVersion(ctx(), &dto.AdminAppVersionAddReq{
Version: ver, File: newApkUpload(t, "app.txt", "not an apk"),
}); err == nil {
t.Fatal("non-apk file should fail")
}
}
// TestAppVersionDuplicate 同版本号重复下发报错
func TestAppVersionDuplicate(t *testing.T) {
ver := uniqueVersion()
req := &dto.AdminAppVersionAddReq{Version: ver, File: newApkUpload(t, "v.apk", "apk")}
if _, err := AppVersion.AdminAddVersion(ctx(), req); err != nil {
t.Fatalf("add version: %v", err)
}
if _, err := AppVersion.AdminAddVersion(ctx(), req); err == nil {
t.Fatal("duplicate version should fail")
}
}
// TestAppVersionList 分页列表:新增多条后按下发时间倒序
func TestAppVersionList(t *testing.T) {
base := uniqueVersion()
for _, v := range []string{base, base + "1"} {
if _, err := AppVersion.AdminAddVersion(ctx(), &dto.AdminAppVersionAddReq{
Version: v, File: newApkUpload(t, "v.apk", "apk"),
}); err != nil {
t.Fatalf("add version %s: %v", v, err)
}
}
res, err := AppVersion.AdminListVersions(ctx(), &dto.AdminAppVersionListReq{Size: 10})
if err != nil {
t.Fatalf("list versions: %v", err)
}
if res.Total < 2 {
t.Fatalf("total = %d, want >= 2", res.Total)
}
// 倒序:最新一条为 base+1
if res.List[0].Version != base+"1" {
t.Fatalf("latest = %s, want %s", res.List[0].Version, base+"1")
}
}
// TestAppVersionDeleteLatest 删除最新版本:记录删除 + APK 文件联动删除
func TestAppVersionDeleteLatest(t *testing.T) {
ver := uniqueVersion()
if _, err := AppVersion.AdminAddVersion(ctx(), &dto.AdminAppVersionAddReq{
Version: ver, File: newApkUpload(t, "v.apk", "apk-v1"),
}); err != nil {
t.Fatalf("add version: %v", err)
}
latest, err := dao.AppVersion.Latest(ctx())
if err != nil || latest == nil {
t.Fatalf("latest = %+v, %v", latest, err)
}
if _, err := AppVersion.AdminDeleteVersion(ctx(), &dto.AdminAppVersionDeleteReq{Id: latest.Id}); err != nil {
t.Fatalf("delete version: %v", err)
}
// 记录已删(测试库有历史残留记录,update 不再返回被删版本即可)
gone, err := dao.AppVersion.GetById(ctx(), latest.Id)
if err != nil || gone != nil {
t.Fatalf("record should be deleted: %+v, %v", gone, err)
}
res, err := AppVersion.GetUpdate(ctx(), &dto.AppUpdateReq{})
if err != nil {
t.Fatalf("get update: %v", err)
}
if res.Version == ver {
t.Fatalf("update still returns deleted version %s", ver)
}
// APK 文件联动删除(下载 404)
if _, err := os.Stat(common.ApkFilePath(ctx())); !os.IsNotExist(err) {
t.Fatalf("apk file should be removed, stat err = %v", err)
}
}
// TestAppVersionDeleteHistoric 删除历史版本:仅删记录,APK 文件保留(仍对应最新版本)
func TestAppVersionDeleteHistoric(t *testing.T) {
ver1 := uniqueVersion()
ver2 := uniqueVersion() + "1"
for _, v := range []string{ver1, ver2} {
if _, err := AppVersion.AdminAddVersion(ctx(), &dto.AdminAppVersionAddReq{
Version: v, File: newApkUpload(t, "v.apk", "apk"),
}); err != nil {
t.Fatalf("add version %s: %v", v, err)
}
}
old, err := dao.AppVersion.GetByVersion(ctx(), ver1)
if err != nil || old == nil {
t.Fatalf("get by version: %+v, %v", old, err)
}
if _, err := AppVersion.AdminDeleteVersion(ctx(), &dto.AdminAppVersionDeleteReq{Id: old.Id}); err != nil {
t.Fatalf("delete version: %v", err)
}
// 最新记录仍是 ver2
res, err := AppVersion.GetUpdate(ctx(), &dto.AppUpdateReq{})
if err != nil {
t.Fatalf("get update: %v", err)
}
if res.Version != ver2 {
t.Fatalf("update = %+v, want %s", res, ver2)
}
// APK 文件保留
if _, err := os.Stat(common.ApkFilePath(ctx())); err != nil {
t.Fatalf("apk file should remain: %v", err)
}
}
// TestAppVersionDeleteNotFound 删除不存在的版本报错
func TestAppVersionDeleteNotFound(t *testing.T) {
if _, err := AppVersion.AdminDeleteVersion(ctx(), &dto.AdminAppVersionDeleteReq{Id: 999999999}); err == nil {
t.Fatal("delete missing version should fail")
}
}