package http import ( "context" "errors" "net/http" "net/http/httptest" "strings" "testing" "gitea.nik4nao.com/nik/home-services/alert-bridge/internal/core/domain" ) type fakeAlertHandler struct { handleFn func(ctx context.Context, alert domain.Alert) error got domain.Alert called bool } func (f *fakeAlertHandler) Handle(ctx context.Context, alert domain.Alert) error { f.called = true f.got = alert if f.handleFn != nil { return f.handleFn(ctx, alert) } return nil } func doRequest(h *Handler, apiKey, body string) *httptest.ResponseRecorder { req := httptest.NewRequest(http.MethodPost, "/alerts", strings.NewReader(body)) if apiKey != "" { req.Header.Set("Authorization", "Bearer "+apiKey) } rec := httptest.NewRecorder() h.ServeHTTP(rec, req) return rec } func TestHandler_Unauthorized(t *testing.T) { fake := &fakeAlertHandler{} h := NewHandler(fake, "secret") rec := doRequest(h, "wrong-key", `{"source":"x","message":"y"}`) if rec.Code != http.StatusUnauthorized { t.Fatalf("expected 401, got %d", rec.Code) } if fake.called { t.Fatal("app should not be called when unauthorized") } } func TestHandler_MissingAuthHeader(t *testing.T) { fake := &fakeAlertHandler{} h := NewHandler(fake, "secret") req := httptest.NewRequest(http.MethodPost, "/alerts", strings.NewReader(`{"source":"x","message":"y"}`)) rec := httptest.NewRecorder() h.ServeHTTP(rec, req) if rec.Code != http.StatusUnauthorized { t.Fatalf("expected 401, got %d", rec.Code) } } func TestHandler_BadJSON(t *testing.T) { fake := &fakeAlertHandler{} h := NewHandler(fake, "secret") rec := doRequest(h, "secret", `not json`) if rec.Code != http.StatusBadRequest { t.Fatalf("expected 400, got %d", rec.Code) } } func TestHandler_MissingFields(t *testing.T) { fake := &fakeAlertHandler{} h := NewHandler(fake, "secret") rec := doRequest(h, "secret", `{"source":"","message":""}`) if rec.Code != http.StatusBadRequest { t.Fatalf("expected 400, got %d", rec.Code) } } func TestHandler_InvalidLevel(t *testing.T) { fake := &fakeAlertHandler{} h := NewHandler(fake, "secret") rec := doRequest(h, "secret", `{"source":"x","message":"y","level":"critical"}`) if rec.Code != http.StatusBadRequest { t.Fatalf("expected 400, got %d", rec.Code) } } func TestHandler_DefaultsLevelToInfo(t *testing.T) { fake := &fakeAlertHandler{} h := NewHandler(fake, "secret") rec := doRequest(h, "secret", `{"source":"ba-cronjob","message":"Started"}`) if rec.Code != http.StatusAccepted { t.Fatalf("expected 202, got %d", rec.Code) } if fake.got.Level != domain.LevelInfo { t.Fatalf("expected default level info, got %q", fake.got.Level) } if fake.got.Source != "ba-cronjob" || fake.got.Message != "Started" { t.Fatalf("unexpected alert passed through: %+v", fake.got) } } func TestHandler_NotifierFailure(t *testing.T) { fake := &fakeAlertHandler{handleFn: func(ctx context.Context, alert domain.Alert) error { return errors.New("discord unreachable") }} h := NewHandler(fake, "secret") rec := doRequest(h, "secret", `{"source":"x","message":"y","level":"error"}`) if rec.Code != http.StatusBadGateway { t.Fatalf("expected 502, got %d", rec.Code) } }