Files
PriceForge/bible-local/demo/internal/web/server_test.go
2026-03-01 22:26:50 +03:00

293 lines
9.6 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 web
import (
"bytes"
"net/http"
"net/http/httptest"
"strings"
"testing"
)
func TestRoutes(t *testing.T) {
srv, err := NewServer()
if err != nil {
t.Fatalf("NewServer: %v", err)
}
t.Run("index", func(t *testing.T) {
rr := httptest.NewRecorder()
req := httptest.NewRequest(http.MethodGet, "/", nil)
srv.Handler().ServeHTTP(rr, req)
if rr.Code != http.StatusOK {
t.Fatalf("status=%d", rr.Code)
}
if !strings.Contains(rr.Body.String(), "UI Design Code") {
t.Fatalf("unexpected body: %s", rr.Body.String())
}
})
t.Run("healthz", func(t *testing.T) {
rr := httptest.NewRecorder()
req := httptest.NewRequest(http.MethodGet, "/healthz", nil)
srv.Handler().ServeHTTP(rr, req)
if rr.Code != http.StatusOK {
t.Fatalf("status=%d", rr.Code)
}
})
t.Run("static", func(t *testing.T) {
rr := httptest.NewRecorder()
req := httptest.NewRequest(http.MethodGet, "/static/css/app.css", nil)
srv.Handler().ServeHTTP(rr, req)
if rr.Code != http.StatusOK {
t.Fatalf("status=%d", rr.Code)
}
})
t.Run("table pattern page", func(t *testing.T) {
rr := httptest.NewRecorder()
req := httptest.NewRequest(http.MethodGet, "/patterns/table", nil)
srv.Handler().ServeHTTP(rr, req)
if rr.Code != http.StatusOK {
t.Fatalf("status=%d", rr.Code)
}
body := rr.Body.String()
if !strings.Contains(body, "Canonical List Page") {
t.Fatalf("missing table demo content in body")
}
if !strings.Contains(body, "Showing 110 of 36") {
t.Fatalf("missing summary: %s", body)
}
})
t.Run("controls pattern page", func(t *testing.T) {
rr := httptest.NewRecorder()
req := httptest.NewRequest(http.MethodGet, "/patterns/controls?segment=warning", nil)
srv.Handler().ServeHTTP(rr, req)
if rr.Code != http.StatusOK {
t.Fatalf("status=%d", rr.Code)
}
body := rr.Body.String()
if !strings.Contains(body, "Selection Table") {
t.Fatalf("missing controls page content")
}
})
t.Run("controls selection toggle persists in query links", func(t *testing.T) {
rr := httptest.NewRecorder()
req := httptest.NewRequest(http.MethodGet, "/patterns/controls?segment=all&selection_action=toggle&id=2", nil)
srv.Handler().ServeHTTP(rr, req)
if rr.Code != http.StatusOK {
t.Fatalf("status=%d", rr.Code)
}
body := rr.Body.String()
if !strings.Contains(body, "1 selected") {
t.Fatalf("expected selected counter, got body")
}
if !strings.Contains(body, "sel=2") {
t.Fatalf("expected selected id in follow-up links")
}
})
t.Run("controls select visible", func(t *testing.T) {
rr := httptest.NewRecorder()
req := httptest.NewRequest(http.MethodGet, "/patterns/controls?segment=warning&selection_action=select_visible", nil)
srv.Handler().ServeHTTP(rr, req)
if rr.Code != http.StatusOK {
t.Fatalf("status=%d", rr.Code)
}
body := rr.Body.String()
if !strings.Contains(body, "4 selected") {
t.Fatalf("expected all visible selected")
}
})
t.Run("modal pattern page", func(t *testing.T) {
rr := httptest.NewRecorder()
req := httptest.NewRequest(http.MethodGet, "/patterns/modals?open=edit&stage=confirm&sel=1", nil)
srv.Handler().ServeHTTP(rr, req)
if rr.Code != http.StatusOK {
t.Fatalf("status=%d", rr.Code)
}
body := rr.Body.String()
if !strings.Contains(body, "Confirmation Summary") || !strings.Contains(body, "Single-modal workflow progression") {
t.Fatalf("missing modal page markers")
}
})
t.Run("io pattern page", func(t *testing.T) {
rr := httptest.NewRecorder()
req := httptest.NewRequest(http.MethodGet, "/patterns/io?import_mode=confirm&export_ready=1", nil)
srv.Handler().ServeHTTP(rr, req)
if rr.Code != http.StatusOK {
t.Fatalf("status=%d", rr.Code)
}
body := rr.Body.String()
if !strings.Contains(body, "Import / Export Pattern") || !strings.Contains(body, "Download CSV") {
t.Fatalf("missing io page markers")
}
})
t.Run("forms pattern page", func(t *testing.T) {
rr := httptest.NewRecorder()
req := httptest.NewRequest(http.MethodGet, "/patterns/forms?mode=register&step=review", nil)
srv.Handler().ServeHTTP(rr, req)
if rr.Code != http.StatusOK {
t.Fatalf("status=%d", rr.Code)
}
body := rr.Body.String()
if !strings.Contains(body, "Forms") || !strings.Contains(body, "Validation Pattern") || !strings.Contains(body, "Review Summary") {
t.Fatalf("missing forms page markers")
}
if !strings.Contains(body, "Server serial is required") {
t.Fatalf("expected validation message")
}
})
t.Run("style playground pattern page", func(t *testing.T) {
rr := httptest.NewRecorder()
req := httptest.NewRequest(http.MethodGet, "/patterns/style-playground?style=slate", nil)
srv.Handler().ServeHTTP(rr, req)
if rr.Code != http.StatusOK {
t.Fatalf("status=%d", rr.Code)
}
body := rr.Body.String()
if !strings.Contains(body, "Style Playground") || !strings.Contains(body, "Theme Presets") {
t.Fatalf("missing style playground markers")
}
if !strings.Contains(body, "theme-slate") {
t.Fatalf("expected theme class in body")
}
})
t.Run("style playground extra presets", func(t *testing.T) {
for _, tc := range []struct {
name string
style string
expectMark string
}{
{name: "y2k silver", style: "y2k-silver", expectMark: "theme-y2k-silver"},
{name: "vaporwave alias", style: "vaporwave", expectMark: "theme-vaporwave"},
{name: "vaporwave soft", style: "vaporwave-soft", expectMark: "theme-vaporwave"},
{name: "vaporwave night", style: "vaporwave-night", expectMark: "theme-vaporwave-night"},
{name: "aqua", style: "aqua", expectMark: "theme-aqua"},
{name: "win9x", style: "win9x", expectMark: "theme-win9x"},
} {
t.Run(tc.name, func(t *testing.T) {
rr := httptest.NewRecorder()
req := httptest.NewRequest(http.MethodGet, "/patterns/style-playground?style="+tc.style, nil)
srv.Handler().ServeHTTP(rr, req)
if rr.Code != http.StatusOK {
t.Fatalf("status=%d", rr.Code)
}
body := rr.Body.String()
if !strings.Contains(body, tc.expectMark) {
t.Fatalf("expected theme marker %q", tc.expectMark)
}
if !strings.Contains(body, "Y2K Silver") || !strings.Contains(body, "Vapor Soft") || !strings.Contains(body, "Vapor Night") || !strings.Contains(body, "Aqua") || !strings.Contains(body, "Win9x") {
t.Fatalf("expected preset tabs in switcher")
}
})
}
})
t.Run("operator tools pattern page", func(t *testing.T) {
rr := httptest.NewRecorder()
req := httptest.NewRequest(http.MethodGet, "/patterns/operator-tools?scope=components&queue=failed", nil)
srv.Handler().ServeHTTP(rr, req)
if rr.Code != http.StatusOK {
t.Fatalf("status=%d", rr.Code)
}
body := rr.Body.String()
if !strings.Contains(body, "Operator Tools Pattern") || !strings.Contains(body, "Operations Queue") {
t.Fatalf("missing operator tools page markers")
}
})
t.Run("timeline pattern page", func(t *testing.T) {
rr := httptest.NewRecorder()
req := httptest.NewRequest(http.MethodGet, "/patterns/timeline?open=c1", nil)
srv.Handler().ServeHTTP(rr, req)
if rr.Code != http.StatusOK {
t.Fatalf("status=%d", rr.Code)
}
body := rr.Body.String()
if !strings.Contains(body, "Timeline Cards Pattern") || !strings.Contains(body, "Open details") {
t.Fatalf("missing timeline page markers")
}
if !strings.Contains(body, "Event Detail") {
t.Fatalf("expected drilldown panel when card is open")
}
})
t.Run("io export csv endpoint", func(t *testing.T) {
rr := httptest.NewRecorder()
req := httptest.NewRequest(http.MethodGet, "/patterns/io/export.csv?scope=selected", nil)
srv.Handler().ServeHTTP(rr, req)
if rr.Code != http.StatusOK {
t.Fatalf("status=%d", rr.Code)
}
if got := rr.Header().Get("Content-Type"); !strings.Contains(got, "text/csv") {
t.Fatalf("content-type=%q", got)
}
if got := rr.Header().Get("Content-Disposition"); !strings.Contains(got, "items.csv") {
t.Fatalf("content-disposition=%q", got)
}
body := rr.Body.Bytes()
if len(body) < 3 || !bytes.Equal(body[:3], []byte{0xEF, 0xBB, 0xBF}) {
t.Fatalf("missing UTF-8 BOM")
}
if !strings.Contains(string(body), "Code;Name;Category;Status;Qty") {
t.Fatalf("unexpected csv header")
}
})
t.Run("table pattern filters before pagination", func(t *testing.T) {
rr := httptest.NewRecorder()
req := httptest.NewRequest(http.MethodGet, "/patterns/table?q=Rack&page=99", nil)
srv.Handler().ServeHTTP(rr, req)
if rr.Code != http.StatusOK {
t.Fatalf("status=%d", rr.Code)
}
body := rr.Body.String()
if !strings.Contains(body, "Showing 13 of 3") {
t.Fatalf("expected filtered summary, got: %s", body)
}
if !strings.Contains(body, "Rack Controller Alpha") {
t.Fatalf("expected filtered rows")
}
if strings.Contains(body, "Component Spec 36") {
t.Fatalf("unexpected unrelated row present")
}
})
t.Run("table pattern keeps filter params in pager links", func(t *testing.T) {
rr := httptest.NewRecorder()
req := httptest.NewRequest(http.MethodGet, "/patterns/table?status=ready", nil)
srv.Handler().ServeHTTP(rr, req)
if rr.Code != http.StatusOK {
t.Fatalf("status=%d", rr.Code)
}
body := rr.Body.String()
if !strings.Contains(body, "status=ready") {
t.Fatalf("expected status filter in pagination links")
}
})
t.Run("table pattern empty result", func(t *testing.T) {
rr := httptest.NewRecorder()
req := httptest.NewRequest(http.MethodGet, "/patterns/table?q=does-not-exist", nil)
srv.Handler().ServeHTTP(rr, req)
if rr.Code != http.StatusOK {
t.Fatalf("status=%d", rr.Code)
}
body := rr.Body.String()
if !strings.Contains(body, "No rows match current filters.") {
t.Fatalf("expected empty-state message")
}
if !strings.Contains(body, "Showing 0 of 0") {
t.Fatalf("expected zero summary")
}
})
}