- 新增 desktop 应用入口,将后端与前端打包为单一可执行文件 - 集成系统托盘功能(getlantern/systray) - 支持单实例锁和端口冲突检测 - 启动时自动打开浏览器显示管理界面 - 新增 embedfs 模块嵌入静态资源 - 新增跨平台构建脚本(macOS/Windows/Linux) - 新增 macOS .app 打包脚本 - 统一 Makefile,移除 backend/Makefile - 更新 README 添加桌面应用使用说明
124 lines
2.8 KiB
Go
124 lines
2.8 KiB
Go
package main
|
|
|
|
import (
|
|
"io/fs"
|
|
"net/http/httptest"
|
|
"strings"
|
|
"testing"
|
|
|
|
"github.com/gin-gonic/gin"
|
|
|
|
"nex/embedfs"
|
|
)
|
|
|
|
func TestSetupStaticFiles(t *testing.T) {
|
|
gin.SetMode(gin.TestMode)
|
|
|
|
distFS, err := fs.Sub(embedfs.FrontendDist, "frontend-dist")
|
|
if err != nil {
|
|
t.Skipf("跳过测试: 前端资源未构建: %v", err)
|
|
return
|
|
}
|
|
|
|
getContentType := func(path string) string {
|
|
if strings.HasSuffix(path, ".js") {
|
|
return "application/javascript"
|
|
}
|
|
if strings.HasSuffix(path, ".css") {
|
|
return "text/css"
|
|
}
|
|
if strings.HasSuffix(path, ".svg") {
|
|
return "image/svg+xml"
|
|
}
|
|
return "application/octet-stream"
|
|
}
|
|
|
|
r := gin.New()
|
|
r.GET("/assets/*filepath", func(c *gin.Context) {
|
|
filepath := c.Param("filepath")
|
|
data, err := fs.ReadFile(distFS, "assets"+filepath)
|
|
if err != nil {
|
|
c.Status(404)
|
|
return
|
|
}
|
|
c.Data(200, getContentType(filepath), data)
|
|
})
|
|
|
|
r.GET("/favicon.svg", func(c *gin.Context) {
|
|
data, err := fs.ReadFile(distFS, "favicon.svg")
|
|
if err != nil {
|
|
c.Status(404)
|
|
return
|
|
}
|
|
c.Data(200, "image/svg+xml", data)
|
|
})
|
|
|
|
r.NoRoute(func(c *gin.Context) {
|
|
path := c.Request.URL.Path
|
|
if strings.HasPrefix(path, "/api/") ||
|
|
strings.HasPrefix(path, "/v1/") ||
|
|
strings.HasPrefix(path, "/health") {
|
|
c.JSON(404, gin.H{"error": "not found"})
|
|
return
|
|
}
|
|
data, err := fs.ReadFile(distFS, "index.html")
|
|
if err != nil {
|
|
c.Status(500)
|
|
return
|
|
}
|
|
c.Data(200, "text/html; charset=utf-8", data)
|
|
})
|
|
|
|
t.Run("API 404", func(t *testing.T) {
|
|
req := httptest.NewRequest("GET", "/api/test", nil)
|
|
w := httptest.NewRecorder()
|
|
r.ServeHTTP(w, req)
|
|
|
|
if w.Code != 404 {
|
|
t.Errorf("期望状态码 404, 实际 %d", w.Code)
|
|
}
|
|
})
|
|
|
|
t.Run("SPA fallback", func(t *testing.T) {
|
|
req := httptest.NewRequest("GET", "/providers", nil)
|
|
w := httptest.NewRecorder()
|
|
r.ServeHTTP(w, req)
|
|
|
|
if w.Code != 200 {
|
|
t.Errorf("期望状态码 200, 实际 %d", w.Code)
|
|
}
|
|
})
|
|
|
|
t.Run("MIME type for JS", func(t *testing.T) {
|
|
req := httptest.NewRequest("GET", "/assets/test.js", nil)
|
|
w := httptest.NewRecorder()
|
|
r.ServeHTTP(w, req)
|
|
|
|
if w.Code == 200 {
|
|
expected := "application/javascript"
|
|
if w.Header().Get("Content-Type") != expected {
|
|
t.Errorf("期望 Content-Type %s, 实际 %s", expected, w.Header().Get("Content-Type"))
|
|
}
|
|
} else {
|
|
t.Log("文件不存在,跳过 MIME 类型验证")
|
|
}
|
|
})
|
|
|
|
t.Run("MIME type for CSS", func(t *testing.T) {
|
|
req := httptest.NewRequest("GET", "/assets/test.css", nil)
|
|
w := httptest.NewRecorder()
|
|
r.ServeHTTP(w, req)
|
|
|
|
if w.Code == 200 {
|
|
expected := "text/css"
|
|
if w.Header().Get("Content-Type") != expected {
|
|
t.Errorf("期望 Content-Type %s, 实际 %s", expected, w.Header().Get("Content-Type"))
|
|
}
|
|
} else {
|
|
t.Log("文件不存在,跳过 MIME 类型验证")
|
|
}
|
|
})
|
|
|
|
t.Log("静态文件服务测试通过")
|
|
}
|