package airbrake import ( "encoding/xml" "net/http" "net/http/httptest" "testing" "time" "github.com/Sirupsen/logrus" ) type notice struct { Error NoticeError `xml:"error"` } type NoticeError struct { Class string `xml:"class"` Message string `xml:"message"` } type customErr struct { msg string } func (e *customErr) Error() string { return e.msg } const ( testAPIKey = "abcxyz" testEnv = "development" expectedClass = "*airbrake.customErr" expectedMsg = "foo" unintendedMsg = "Airbrake will not see this string" ) var ( noticeError = make(chan NoticeError, 1) ) // TestLogEntryMessageReceived checks if invoking Logrus' log.Error // method causes an XML payload containing the log entry message is received // by a HTTP server emulating an Airbrake-compatible endpoint. func TestLogEntryMessageReceived(t *testing.T) { log := logrus.New() ts := startAirbrakeServer(t) defer ts.Close() hook := NewHook(ts.URL, testAPIKey, "production") log.Hooks.Add(hook) log.Error(expectedMsg) select { case received := <-noticeError: if received.Message != expectedMsg { t.Errorf("Unexpected message received: %s", received.Message) } case <-time.After(time.Second): t.Error("Timed out; no notice received by Airbrake API") } } // TestLogEntryMessageReceived confirms that, when passing an error type using // logrus.Fields, a HTTP server emulating an Airbrake endpoint receives the // error message returned by the Error() method on the error interface // rather than the logrus.Entry.Message string. func TestLogEntryWithErrorReceived(t *testing.T) { log := logrus.New() ts := startAirbrakeServer(t) defer ts.Close() hook := NewHook(ts.URL, testAPIKey, "production") log.Hooks.Add(hook) log.WithFields(logrus.Fields{ "error": &customErr{expectedMsg}, }).Error(unintendedMsg) select { case received := <-noticeError: if received.Message != expectedMsg { t.Errorf("Unexpected message received: %s", received.Message) } if received.Class != expectedClass { t.Errorf("Unexpected error class: %s", received.Class) } case <-time.After(time.Second): t.Error("Timed out; no notice received by Airbrake API") } } // TestLogEntryWithNonErrorTypeNotReceived confirms that, when passing a // non-error type using logrus.Fields, a HTTP server emulating an Airbrake // endpoint receives the logrus.Entry.Message string. // // Only error types are supported when setting the 'error' field using // logrus.WithFields(). func TestLogEntryWithNonErrorTypeNotReceived(t *testing.T) { log := logrus.New() ts := startAirbrakeServer(t) defer ts.Close() hook := NewHook(ts.URL, testAPIKey, "production") log.Hooks.Add(hook) log.WithFields(logrus.Fields{ "error": expectedMsg, }).Error(unintendedMsg) select { case received := <-noticeError: if received.Message != unintendedMsg { t.Errorf("Unexpected message received: %s", received.Message) } case <-time.After(time.Second): t.Error("Timed out; no notice received by Airbrake API") } } func startAirbrakeServer(t *testing.T) *httptest.Server { ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { var notice notice if err := xml.NewDecoder(r.Body).Decode(¬ice); err != nil { t.Error(err) } r.Body.Close() noticeError <- notice.Error })) return ts }