Files
picoclaw/pkg/channels/dynamic_mux_test.go
T
Cytown f1cb7cc8f5 fix gateway reload will cause pico stop working issue (#2082)
* fix gateway reload will cause pico stop working issue

* fix for review
2026-03-28 11:30:31 +08:00

163 lines
4.3 KiB
Go

package channels
import (
"net/http"
"net/http/httptest"
"sync"
"testing"
)
func TestDynamicServeMuxExactMatch(t *testing.T) {
dm := newDynamicServeMux()
dm.HandleFunc("/health", func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)
})
rec := httptest.NewRecorder()
dm.ServeHTTP(rec, httptest.NewRequest(http.MethodGet, "/health", nil))
if rec.Code != http.StatusOK {
t.Fatalf("expected 200, got %d", rec.Code)
}
}
func TestDynamicServeMuxSubtreePrefixMatch(t *testing.T) {
dm := newDynamicServeMux()
dm.HandleFunc("/api/", func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusCreated)
})
for _, path := range []string{"/api/", "/api/v1", "/api/v1/resource"} {
rec := httptest.NewRecorder()
dm.ServeHTTP(rec, httptest.NewRequest(http.MethodGet, path, nil))
if rec.Code != http.StatusCreated {
t.Fatalf("path %q: expected 201, got %d", path, rec.Code)
}
}
}
func TestDynamicServeMuxExactOverPrefix(t *testing.T) {
dm := newDynamicServeMux()
dm.HandleFunc("/api", func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)
})
dm.HandleFunc("/api/", func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusCreated)
})
// Exact match wins
rec := httptest.NewRecorder()
dm.ServeHTTP(rec, httptest.NewRequest(http.MethodGet, "/api", nil))
if rec.Code != http.StatusOK {
t.Fatalf("exact match: expected 200, got %d", rec.Code)
}
// Prefix match for sub-paths
rec = httptest.NewRecorder()
dm.ServeHTTP(rec, httptest.NewRequest(http.MethodGet, "/api/v1", nil))
if rec.Code != http.StatusCreated {
t.Fatalf("prefix match: expected 201, got %d", rec.Code)
}
}
func TestDynamicServeMuxLongestPrefixWins(t *testing.T) {
dm := newDynamicServeMux()
dm.HandleFunc("/a/", func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)
})
dm.HandleFunc("/a/b/", func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusAccepted)
})
rec := httptest.NewRecorder()
dm.ServeHTTP(rec, httptest.NewRequest(http.MethodGet, "/a/b/c", nil))
if rec.Code != http.StatusAccepted {
t.Fatalf("longest prefix: expected 202, got %d", rec.Code)
}
}
func TestDynamicServeMuxNotFound(t *testing.T) {
dm := newDynamicServeMux()
rec := httptest.NewRecorder()
dm.ServeHTTP(rec, httptest.NewRequest(http.MethodGet, "/nonexistent", nil))
if rec.Code != http.StatusNotFound {
t.Fatalf("expected 404, got %d", rec.Code)
}
}
func TestDynamicServeMuxUnhandle(t *testing.T) {
dm := newDynamicServeMux()
dm.HandleFunc("/test", func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)
})
// Verify it works before removal
rec := httptest.NewRecorder()
dm.ServeHTTP(rec, httptest.NewRequest(http.MethodGet, "/test", nil))
if rec.Code != http.StatusOK {
t.Fatalf("before unhandle: expected 200, got %d", rec.Code)
}
// Remove and verify 404
dm.Unhandle("/test")
rec = httptest.NewRecorder()
dm.ServeHTTP(rec, httptest.NewRequest(http.MethodGet, "/test", nil))
if rec.Code != http.StatusNotFound {
t.Fatalf("after unhandle: expected 404, got %d", rec.Code)
}
}
func TestDynamicServeMuxConcurrent(t *testing.T) {
dm := newDynamicServeMux()
dm.HandleFunc("/static", func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)
})
var wg sync.WaitGroup
const goroutines = 50
// Concurrent Handle/Unhandle
for i := 0; i < goroutines; i++ {
wg.Add(1)
go func(i int) {
defer wg.Done()
pattern := "/concurrent"
if i%2 == 0 {
dm.HandleFunc(pattern, func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusAccepted)
})
} else {
dm.Unhandle(pattern)
}
}(i)
}
// Concurrent ServeHTTP
for i := 0; i < goroutines; i++ {
wg.Add(1)
go func() {
defer wg.Done()
rec := httptest.NewRecorder()
dm.ServeHTTP(rec, httptest.NewRequest(http.MethodGet, "/static", nil))
// Should not panic; result is either 200 or 404
_ = rec.Code
}()
}
wg.Wait()
}
func TestDynamicServeMuxHandleUsesHandler(t *testing.T) {
dm := newDynamicServeMux()
var called bool
dm.Handle("/handler", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
called = true
}))
rec := httptest.NewRecorder()
dm.ServeHTTP(rec, httptest.NewRequest(http.MethodGet, "/handler", nil))
if !called {
t.Fatal("handler was not called")
}
}