Files
observer/server/biz/service/app_version_test.go
T
admin a0b115d954 训练体系整合与标注单阶段化
- 标注:AI 预标注直写 labels_json(去候选确认两阶段);重叠去重(minIoU);全量标注按钮
- 训练:脚本迁移入 server/training/(Go 化 prepare_yolo/analyze_rfdetr,保留 train_server.py);tflite 产物自检并入训练流程(check_tflite)
- 数据目录/权重不进 git;.gitignore 迁移至仓库根
2026-08-26 18:22:56 +08:00

230 lines
7.5 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{
Notes: "修复识别准确率", File: newApkUpload(t, "observer-"+ver+".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{File: newApkUpload(t, "observer-"+ver1+".apk", "apk-old")}
req2 := &dto.AdminAppVersionAddReq{File: newApkUpload(t, "observer-"+ver2+".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 无文件 / 文件名不含版本号(格式不符)拒绝
func TestAppVersionInvalidFile(t *testing.T) {
if _, err := AppVersion.AdminAddVersion(ctx(), &dto.AdminAppVersionAddReq{}); err == nil {
t.Fatal("missing file should fail")
}
for _, name := range []string{"app.txt", "myapp-1.0.apk", "observer-1.0.apk", "observer-abc.apk"} {
if _, err := AppVersion.AdminAddVersion(ctx(), &dto.AdminAppVersionAddReq{
File: newApkUpload(t, name, "not an apk"),
}); err == nil {
t.Fatalf("file %s should fail (version not parseable)", name)
}
}
}
// TestAppVersionDuplicate 同版本号重复下发报错
func TestAppVersionDuplicate(t *testing.T) {
ver := uniqueVersion()
req := &dto.AdminAppVersionAddReq{File: newApkUpload(t, "observer-"+ver+".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{
File: newApkUpload(t, "observer-"+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{
File: newApkUpload(t, "observer-"+ver+".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{
File: newApkUpload(t, "observer-"+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")
}
}