feat(admin): 添加APK管理功能
实现APK文件的上传、下载和列表功能 - 新增APKInfo数据结构 - 添加/admin/apk/upload、/admin/apk/download和/admin/apk/list路由 - 实现APK存储服务方法 - 添加相关测试用例
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
package handlers_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/rand"
|
||||
"crypto/rsa"
|
||||
"crypto/x509"
|
||||
@@ -324,3 +325,107 @@ func TestAssetsErrorCases(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func doReqWithBody(t *testing.T, h http.Handler, method string, target string, headers map[string]string, body []byte) *httptest.ResponseRecorder {
|
||||
t.Helper()
|
||||
req := httptest.NewRequest(method, target, bytes.NewReader(body))
|
||||
for k, v := range headers {
|
||||
req.Header.Set(k, v)
|
||||
}
|
||||
rec := httptest.NewRecorder()
|
||||
h.ServeHTTP(rec, req)
|
||||
return rec
|
||||
}
|
||||
|
||||
func TestAPKUploadRequiresAuth(t *testing.T) {
|
||||
h, _ := newTestServer(t)
|
||||
rec := doReqWithBody(t, h, http.MethodPost, "/admin/apk/upload?runtimeVersion=1.0.0", nil, []byte("fake-apk"))
|
||||
if rec.Code != http.StatusUnauthorized {
|
||||
t.Fatalf("expected 401, got %d", rec.Code)
|
||||
}
|
||||
}
|
||||
|
||||
func TestAPKUploadAndDownload(t *testing.T) {
|
||||
h, updatesRoot := newTestServer(t)
|
||||
authHeaders := map[string]string{"Authorization": "Bearer dev-token"}
|
||||
|
||||
fakeAPK := []byte("PK\x03\x04fake-apk-content-for-testing")
|
||||
uploadRec := doReqWithBody(t, h, http.MethodPost, "/admin/apk/upload?runtimeVersion=1.0.0", map[string]string{
|
||||
"Authorization": "Bearer dev-token",
|
||||
"Content-Type": "application/vnd.android.package-archive",
|
||||
}, fakeAPK)
|
||||
if uploadRec.Code != http.StatusOK {
|
||||
t.Fatalf("expected 200, got %d: %s", uploadRec.Code, mustBodyString(t, uploadRec))
|
||||
}
|
||||
|
||||
apkPath := filepath.Join(updatesRoot, "apk", "1.0.0.apk")
|
||||
if _, err := os.Stat(apkPath); os.IsNotExist(err) {
|
||||
t.Fatalf("APK file not created at %s", apkPath)
|
||||
}
|
||||
|
||||
downloadRec := doReq(t, h, http.MethodGet, "/admin/apk/download?runtimeVersion=1.0.0", authHeaders)
|
||||
if downloadRec.Code != http.StatusOK {
|
||||
t.Fatalf("expected 200, got %d", downloadRec.Code)
|
||||
}
|
||||
if downloadRec.Header().Get("Content-Type") != "application/vnd.android.package-archive" {
|
||||
t.Fatalf("expected application/vnd.android.package-archive, got %s", downloadRec.Header().Get("Content-Type"))
|
||||
}
|
||||
if downloadRec.Header().Get("Content-Disposition") != "attachment; filename=1.0.0.apk" {
|
||||
t.Fatalf("unexpected Content-Disposition: %s", downloadRec.Header().Get("Content-Disposition"))
|
||||
}
|
||||
body := mustBodyString(t, downloadRec)
|
||||
if body != string(fakeAPK) {
|
||||
t.Fatalf("downloaded content mismatch")
|
||||
}
|
||||
}
|
||||
|
||||
func TestAPKDownloadNotFound(t *testing.T) {
|
||||
h, _ := newTestServer(t)
|
||||
authHeaders := map[string]string{"Authorization": "Bearer dev-token"}
|
||||
rec := doReq(t, h, http.MethodGet, "/admin/apk/download?runtimeVersion=nonexistent", authHeaders)
|
||||
if rec.Code != http.StatusNotFound {
|
||||
t.Fatalf("expected 404, got %d", rec.Code)
|
||||
}
|
||||
}
|
||||
|
||||
func TestAPKList(t *testing.T) {
|
||||
h, _ := newTestServer(t)
|
||||
authHeaders := map[string]string{"Authorization": "Bearer dev-token"}
|
||||
|
||||
listRec := doReq(t, h, http.MethodGet, "/admin/apk/list", authHeaders)
|
||||
if listRec.Code != http.StatusOK {
|
||||
t.Fatalf("expected 200, got %d", listRec.Code)
|
||||
}
|
||||
body := mustBodyString(t, listRec)
|
||||
if !strings.Contains(body, `"apks"`) {
|
||||
t.Fatalf("expected apks array in response: %s", body)
|
||||
}
|
||||
|
||||
fakeAPK := []byte("PK\x03\x04fake-apk-content")
|
||||
uploadRec := doReqWithBody(t, h, http.MethodPost, "/admin/apk/upload?runtimeVersion=2.0.0", map[string]string{
|
||||
"Authorization": "Bearer dev-token",
|
||||
"Content-Type": "application/octet-stream",
|
||||
}, fakeAPK)
|
||||
if uploadRec.Code != http.StatusOK {
|
||||
t.Fatalf("expected 200, got %d", uploadRec.Code)
|
||||
}
|
||||
|
||||
listRec2 := doReq(t, h, http.MethodGet, "/admin/apk/list", authHeaders)
|
||||
if listRec2.Code != http.StatusOK {
|
||||
t.Fatalf("expected 200, got %d", listRec2.Code)
|
||||
}
|
||||
body2 := mustBodyString(t, listRec2)
|
||||
if !strings.Contains(body2, "2.0.0") {
|
||||
t.Fatalf("expected 2.0.0 in response: %s", body2)
|
||||
}
|
||||
}
|
||||
|
||||
func TestAPKUploadRequiresRuntimeVersion(t *testing.T) {
|
||||
h, _ := newTestServer(t)
|
||||
rec := doReqWithBody(t, h, http.MethodPost, "/admin/apk/upload", map[string]string{
|
||||
"Authorization": "Bearer dev-token",
|
||||
"Content-Type": "application/vnd.android.package-archive",
|
||||
}, []byte("fake"))
|
||||
if rec.Code != http.StatusBadRequest {
|
||||
t.Fatalf("expected 400, got %d", rec.Code)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user