package api import ( "bytes" "encoding/json" "fmt" "net/http" "net/http/httptest" "testing" "github.com/alicebob/miniredis/v2" "github.com/ez-api/ez-api/internal/middleware" "github.com/ez-api/ez-api/internal/model" "github.com/ez-api/ez-api/internal/service" "github.com/gin-gonic/gin" "github.com/redis/go-redis/v9" "gorm.io/driver/sqlite" "gorm.io/gorm" ) func newTestHandlerWithWebhook(t *testing.T) (*Handler, *miniredis.Miniredis) { t.Helper() gin.SetMode(gin.TestMode) dsn := fmt.Sprintf("file:%s?mode=memory&cache=shared", t.Name()) db, err := gorm.Open(sqlite.Open(dsn), &gorm.Config{}) if err != nil { t.Fatalf("open sqlite: %v", err) } if err := db.AutoMigrate(&model.LogRecord{}); err != nil { t.Fatalf("migrate: %v", err) } mr := miniredis.RunT(t) rdb := redis.NewClient(&redis.Options{Addr: mr.Addr()}) sync := service.NewSyncService(rdb) return NewHandler(db, db, sync, nil, rdb, nil), mr } func TestLogWebhookConfigCRUD(t *testing.T) { h, _ := newTestHandlerWithWebhook(t) r := gin.New() r.Use(middleware.ResponseEnvelope()) r.GET("/admin/logs/webhook", h.GetLogWebhookConfig) r.PUT("/admin/logs/webhook", h.UpdateLogWebhookConfig) reqBody := service.LogWebhookConfig{ Enabled: true, URL: "https://example.com/webhook", Secret: "s1", Threshold: 3, WindowSeconds: 60, CooldownSeconds: 120, StatusCodeThreshold: 500, } payload, _ := json.Marshal(reqBody) req := httptest.NewRequest(http.MethodPut, "/admin/logs/webhook", bytes.NewReader(payload)) req.Header.Set("Content-Type", "application/json") rr := httptest.NewRecorder() r.ServeHTTP(rr, req) if rr.Code != http.StatusOK { t.Fatalf("expected 200, got %d body=%s", rr.Code, rr.Body.String()) } getReq := httptest.NewRequest(http.MethodGet, "/admin/logs/webhook", nil) getRR := httptest.NewRecorder() r.ServeHTTP(getRR, getReq) if getRR.Code != http.StatusOK { t.Fatalf("expected 200, got %d body=%s", getRR.Code, getRR.Body.String()) } var got service.LogWebhookConfig env := decodeEnvelope(t, getRR, &got) if env.Code != "ok" { t.Fatalf("expected code=ok, got %q", env.Code) } if !got.Enabled || got.URL == "" || got.Threshold != 3 { t.Fatalf("unexpected webhook config: %+v", got) } if got.Secret != "s1" { t.Fatalf("expected secret s1, got %q", got.Secret) } } func TestLogWebhookConfigDisableClears(t *testing.T) { h, mr := newTestHandlerWithWebhook(t) r := gin.New() r.Use(middleware.ResponseEnvelope()) r.PUT("/admin/logs/webhook", h.UpdateLogWebhookConfig) seed := service.LogWebhookConfig{Enabled: true, URL: "https://example.com"} payload, _ := json.Marshal(seed) req := httptest.NewRequest(http.MethodPut, "/admin/logs/webhook", bytes.NewReader(payload)) req.Header.Set("Content-Type", "application/json") rr := httptest.NewRecorder() r.ServeHTTP(rr, req) if rr.Code != http.StatusOK { t.Fatalf("expected 200, got %d body=%s", rr.Code, rr.Body.String()) } clear := service.LogWebhookConfig{Enabled: false} clearPayload, _ := json.Marshal(clear) clearReq := httptest.NewRequest(http.MethodPut, "/admin/logs/webhook", bytes.NewReader(clearPayload)) clearReq.Header.Set("Content-Type", "application/json") clearRR := httptest.NewRecorder() r.ServeHTTP(clearRR, clearReq) if clearRR.Code != http.StatusOK { t.Fatalf("expected 200, got %d body=%s", clearRR.Code, clearRR.Body.String()) } if mr.Exists("meta:log:webhook") { t.Fatalf("expected webhook config to be cleared") } }