package docs import ( "encoding/json" "net/http" "net/http/httptest" "strings" "testing" "github.com/gin-gonic/gin" ) func newTestRouter(t *testing.T) *gin.Engine { t.Helper() gin.SetMode(gin.TestMode) r := gin.New() New().Register(r) return r } func TestDocsIndexRedirect(t *testing.T) { r := newTestRouter(t) w := httptest.NewRecorder() r.ServeHTTP(w, httptest.NewRequest(http.MethodGet, "/docs", nil)) if w.Code != http.StatusFound { t.Fatalf("expect 302, got %d", w.Code) } if !strings.HasPrefix(w.Header().Get("Location"), "/docs/") { t.Fatalf("bad redirect: %s", w.Header().Get("Location")) } } func TestDocsAllPagesRender(t *testing.T) { h := New() r := newTestRouter(t) for _, slug := range h.order { w := httptest.NewRecorder() req := httptest.NewRequest(http.MethodGet, "/docs/"+slug, nil) r.ServeHTTP(w, req) if w.Code != http.StatusOK { t.Fatalf("page %s: expect 200, got %d", slug, w.Code) } body := w.Body.String() if !strings.Contains(body, "") || !strings.Contains(body, `class="md"`) { t.Fatalf("page %s: html structure incomplete", slug) } // 正文有渲染出的标题与段落 if !strings.Contains(body, "<h1") { t.Fatalf("page %s: missing h1", slug) } // 侧栏包含全部分组 for _, g := range h.manifest.Groups { if !strings.Contains(body, g.Name) { t.Fatalf("page %s: sidebar missing group %s", slug, g.Name) } } } } func TestDocsPageContainsTOCAndNav(t *testing.T) { r := newTestRouter(t) w := httptest.NewRecorder() r.ServeHTTP(w, httptest.NewRequest(http.MethodGet, "/docs/http", nil)) body := w.Body.String() if !strings.Contains(body, "sec-1") { t.Fatal("expect heading anchors") } if !strings.Contains(body, "上一页") { t.Fatal("expect prev link") } } func TestDocsSearchData(t *testing.T) { r := newTestRouter(t) w := httptest.NewRecorder() r.ServeHTTP(w, httptest.NewRequest(http.MethodGet, "/docs/search-data.json", nil)) if w.Code != http.StatusOK { t.Fatalf("expect 200, got %d", w.Code) } var entries []SearchEntry if err := json.Unmarshal(w.Body.Bytes(), &entries); err != nil { t.Fatalf("invalid json: %v", err) } if len(entries) < 10 { t.Fatalf("expect >= 10 search entries, got %d", len(entries)) } // MQTT 页应可被检索(标题或正文命中) found := false for _, e := range entries { if e.Slug == "mqtt" && (e.Title == "MQTT" || strings.Contains(e.Text, "MQTT")) { found = true } } if !found { t.Fatal("mqtt entry missing searchable text") } } func TestDocsAssetsAnd404(t *testing.T) { r := newTestRouter(t) w := httptest.NewRecorder() r.ServeHTTP(w, httptest.NewRequest(http.MethodGet, "/docs/assets/docs.css", nil)) if w.Code != http.StatusOK || !strings.Contains(w.Header().Get("Content-Type"), "text/css") { t.Fatalf("css asset: %d %s", w.Code, w.Header().Get("Content-Type")) } w = httptest.NewRecorder() r.ServeHTTP(w, httptest.NewRequest(http.MethodGet, "/docs/assets/docs.js", nil)) if w.Code != http.StatusOK { t.Fatalf("js asset: %d", w.Code) } w = httptest.NewRecorder() r.ServeHTTP(w, httptest.NewRequest(http.MethodGet, "/docs/not-exist-page", nil)) if w.Code != http.StatusNotFound { t.Fatalf("404 page: %d", w.Code) } if !strings.Contains(w.Body.String(), "返回文档首页") { t.Fatal("404 page missing home link") } }