feat: implement ntfy-discord bridge in Go
Some checks failed
Build and Push / build (push) Failing after 4m36s
Build and Push / test (push) Has been cancelled

- 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
This commit is contained in:
2026-02-02 18:13:55 -05:00
parent b325d9bfec
commit f97ad0e7cb
22 changed files with 2678 additions and 0 deletions

View File

@@ -0,0 +1,91 @@
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)
}
}