- SSE subscription to ntfy with auto-reconnect - Discord webhook integration with embed formatting - Priority to color mapping, tag to emoji conversion - Native HashiCorp Vault support (Kubernetes + token auth) - Hot reload secrets via fsnotify or Vault polling - Prometheus metrics (/metrics endpoint) - Health/ready endpoints for Kubernetes probes - Comprehensive unit tests and fuzz tests - Multi-stage Docker build (~10MB scratch image) - CI/CD pipeline for Gitea Actions
92 lines
2.2 KiB
Go
92 lines
2.2 KiB
Go
package server
|
|
|
|
import (
|
|
"context"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"testing"
|
|
"time"
|
|
)
|
|
|
|
func TestServer_HealthEndpoint_StatusCodes(t *testing.T) {
|
|
// Test health endpoint returns JSON
|
|
mux := http.NewServeMux()
|
|
mux.HandleFunc("/health", func(w http.ResponseWriter, r *http.Request) {
|
|
w.Header().Set("Content-Type", "application/json")
|
|
w.Write([]byte(`{"status":"ok","healthy":true}`))
|
|
})
|
|
|
|
req := httptest.NewRequest(http.MethodGet, "/health", nil)
|
|
w := httptest.NewRecorder()
|
|
|
|
mux.ServeHTTP(w, req)
|
|
|
|
if w.Code != http.StatusOK {
|
|
t.Errorf("status = %d, want %d", w.Code, http.StatusOK)
|
|
}
|
|
|
|
if w.Header().Get("Content-Type") != "application/json" {
|
|
t.Error("expected Content-Type application/json")
|
|
}
|
|
}
|
|
|
|
func TestServer_ReadyEndpoint_StatusCodes(t *testing.T) {
|
|
// Test ready endpoint returns JSON
|
|
mux := http.NewServeMux()
|
|
mux.HandleFunc("/ready", func(w http.ResponseWriter, r *http.Request) {
|
|
w.Header().Set("Content-Type", "application/json")
|
|
w.Write([]byte(`{"status":"ready","ready":true}`))
|
|
})
|
|
|
|
req := httptest.NewRequest(http.MethodGet, "/ready", nil)
|
|
w := httptest.NewRecorder()
|
|
|
|
mux.ServeHTTP(w, req)
|
|
|
|
if w.Code != http.StatusOK {
|
|
t.Errorf("status = %d, want %d", w.Code, http.StatusOK)
|
|
}
|
|
}
|
|
|
|
func TestServer_Shutdown(t *testing.T) {
|
|
// Create a minimal server for shutdown testing
|
|
srv := &http.Server{
|
|
Addr: ":0",
|
|
Handler: http.NewServeMux(),
|
|
}
|
|
|
|
// Start in background
|
|
go srv.ListenAndServe()
|
|
|
|
// Give it a moment to start
|
|
time.Sleep(10 * time.Millisecond)
|
|
|
|
// Shutdown should complete without error
|
|
ctx, cancel := context.WithTimeout(context.Background(), time.Second)
|
|
defer cancel()
|
|
|
|
err := srv.Shutdown(ctx)
|
|
if err != nil {
|
|
t.Errorf("Shutdown() error = %v", err)
|
|
}
|
|
}
|
|
|
|
func TestServer_MetricsEndpoint(t *testing.T) {
|
|
// Verify /metrics endpoint can be created
|
|
// The actual promhttp.Handler() is tested by Prometheus library
|
|
mux := http.NewServeMux()
|
|
mux.HandleFunc("/metrics", func(w http.ResponseWriter, r *http.Request) {
|
|
w.WriteHeader(http.StatusOK)
|
|
w.Write([]byte("# metrics here"))
|
|
})
|
|
|
|
req := httptest.NewRequest(http.MethodGet, "/metrics", nil)
|
|
w := httptest.NewRecorder()
|
|
|
|
mux.ServeHTTP(w, req)
|
|
|
|
if w.Code != http.StatusOK {
|
|
t.Errorf("status = %d, want %d", w.Code, http.StatusOK)
|
|
}
|
|
}
|