package router import ( "net/http" "net/http/httptest" "os" "path/filepath" "testing" "github.com/gin-gonic/gin" ) func newSPAEngine(t *testing.T, withDist bool) *gin.Engine { t.Helper() gin.SetMode(gin.TestMode) dist := filepath.Join(t.TempDir(), "dist") if withDist { if err := os.MkdirAll(filepath.Join(dist, "assets"), 0o755); err != nil { t.Fatalf("建目录失败: %v", err) } if err := os.WriteFile(filepath.Join(dist, "index.html"), []byte("SPA"), 0o644); err != nil { t.Fatalf("写 index.html 失败: %v", err) } } t.Setenv(SPADirEnv, dist) engine := gin.New() engine.GET("/api/admin/v1/ping", func(c *gin.Context) { c.String(http.StatusOK, "pong") }) InitSPARouter(engine) return engine } func do(engine *gin.Engine, method, path string) *httptest.ResponseRecorder { recorder := httptest.NewRecorder() engine.ServeHTTP(recorder, httptest.NewRequest(method, path, nil)) return recorder } // history 模式:前端路由必须回退到 index.html,否则刷新页面就是 404。 func TestUnmatchedGetFallsBackToIndex(t *testing.T) { engine := newSPAEngine(t, true) response := do(engine, http.MethodGet, "/goauto/syb-products") if response.Code != http.StatusOK { t.Fatalf("前端路由应回退到 index.html,实际 %d", response.Code) } if body := response.Body.String(); body != "SPA" { t.Fatalf("返回的不是 index.html: %q", body) } } // `[必须]` 写错的 API 路径必须还是 404。回一个 200 + HTML,客户端看到的会是 // JSON 解析错误,而不是"这个接口不存在"。 func TestUnknownAPIPathStaysA404(t *testing.T) { engine := newSPAEngine(t, true) for _, path := range []string{ "/api/admin/v1/does-not-exist", "/api/v1/nope", "/swagger/admin/index.html", "/static/missing.png", "/ws/1/2", "/info", } { if response := do(engine, http.MethodGet, path); response.Code != http.StatusNotFound { t.Fatalf("%s 应返回 404,实际 %d", path, response.Code) } } } // 真实存在的接口不能被 SPA 回退影响。 func TestRegisteredAPIStillWorks(t *testing.T) { engine := newSPAEngine(t, true) if response := do(engine, http.MethodGet, "/api/admin/v1/ping"); response.Code != http.StatusOK { t.Fatalf("已注册的接口被影响了: %d", response.Code) } } // 非 GET 请求不该拿到 HTML——POST 到不存在的路径就是 404。 func TestNonGetDoesNotFallBack(t *testing.T) { engine := newSPAEngine(t, true) if response := do(engine, http.MethodPost, "/whatever"); response.Code != http.StatusNotFound { t.Fatalf("POST 不应回退到 index.html,实际 %d", response.Code) } } // 开发环境没有 dist,绝不能注册 NoRoute——否则每个真 404 都变成一张 HTML。 func TestWithoutDistNoFallbackIsInstalled(t *testing.T) { engine := newSPAEngine(t, false) if response := do(engine, http.MethodGet, "/goauto/syb-products"); response.Code != http.StatusNotFound { t.Fatalf("没有 dist 时不应回退,实际 %d", response.Code) } if response := do(engine, http.MethodGet, "/api/admin/v1/ping"); response.Code != http.StatusOK { t.Fatalf("没有 dist 时接口仍应正常: %d", response.Code) } }