Skip to content

Commit

Permalink
[chore] Replace test server in favor of httptest
Browse files Browse the repository at this point in the history
- Cleaned up the package integration tests to not have global vars
- Make tests independent
  • Loading branch information
kinbiko committed Sep 7, 2018
1 parent d979aa1 commit cc08d45
Show file tree
Hide file tree
Showing 3 changed files with 105 additions and 96 deletions.
140 changes: 66 additions & 74 deletions bugsnag_test.go
Original file line number Diff line number Diff line change
@@ -1,27 +1,47 @@
package bugsnag

import (
"context"
"fmt"
"io/ioutil"
"log"
"net"
"net/http"
"net/http/httptest"
"strings"
"sync"
"testing"

"github.com/bitly/go-simplejson"
"github.com/bugsnag/bugsnag-go/sessions"
)

type _recurse struct {
Recurse *_recurse
}

var postedJSON = make(chan []byte, 10)
var testOnce sync.Once
var testEndpoint string
var testAPIKey = "166f5ad3590596f9aa8d601ea89af845"

// setup sets up a simple sessionTracker and returns a test event server for receiving the event payloads.
// report payloads published to ts.URL will be put on the returned channel
func setup() (*httptest.Server, chan []byte) {
reports := make(chan []byte, 10)
sessionTracker = &testSessionTracker{}
return httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
body, _ := ioutil.ReadAll(r.Body)
reports <- body
})), reports
}

type testSessionTracker struct{}

func (t *testSessionTracker) StartSession(context.Context) context.Context {
return context.Background()
}

func (t *testSessionTracker) GetSession(context.Context) *sessions.Session {
return &sessions.Session{}
}

func TestConfigure(t *testing.T) {
Configure(Configuration{
APIKey: testAPIKey,
Expand All @@ -37,7 +57,8 @@ func TestConfigure(t *testing.T) {
}

func TestNotify(t *testing.T) {
startTestServer()
ts, reports := setup()
defer ts.Close()

recurse := _recurse{}
recurse.Recurse = &recurse
Expand All @@ -50,15 +71,7 @@ func TestNotify(t *testing.T) {
})

Notify(fmt.Errorf("hello world"),
Configuration{
APIKey: testAPIKey,
Endpoints: Endpoints{Notify: testEndpoint},
ReleaseStage: "test",
AppType: "foo",
AppVersion: "1.2.3",
Hostname: "web1",
ProjectPackages: []string{"github.com/bugsnag/bugsnag-go"},
},
generateSampleConfig(ts.URL),
User{Id: "123", Name: "Conrad", Email: "[email protected]"},
Context{"testing"},
MetaData{"test": {
Expand All @@ -69,7 +82,7 @@ func TestNotify(t *testing.T) {
}},
)

json, err := simplejson.NewJson(<-postedJSON)
json, err := simplejson.NewJson(<-reports)

if err != nil {
t.Fatal(err)
Expand Down Expand Up @@ -137,21 +150,32 @@ func TestNotify(t *testing.T) {
}

func TestHandler(t *testing.T) {
startTestServer()
ts, reports := setup()
defer ts.Close()

l, err := runCrashyServer(Configuration{
APIKey: testAPIKey,
Endpoints: Endpoints{Notify: testEndpoint},
ProjectPackages: []string{"github.com/bugsnag/bugsnag-go"},
Logger: log.New(ioutil.Discard, log.Prefix(), log.Flags()),
}, SeverityInfo)
l, err := net.Listen("tcp", "127.0.0.1:0")
if err != nil {
t.Fatal(err)
}
mux := http.NewServeMux()
mux.HandleFunc("/", crashyHandler)
srv := http.Server{
Addr: l.Addr().String(),
Handler: Handler(mux, Configuration{
APIKey: testAPIKey,
Endpoints: Endpoints{Notify: ts.URL},
ProjectPackages: []string{"github.com/bugsnag/bugsnag-go"},
Logger: log.New(ioutil.Discard, log.Prefix(), log.Flags()),
}, SeverityInfo),
ErrorLog: log.New(ioutil.Discard, log.Prefix(), 0),
}

go srv.Serve(l)

http.Get("http://" + l.Addr().String() + "/ok?foo=bar")
l.Close()

json, err := simplejson.NewJson(<-postedJSON)
json, err := simplejson.NewJson(<-reports)
if err != nil {
t.Fatal(err)
}
Expand Down Expand Up @@ -219,14 +243,16 @@ func TestHandler(t *testing.T) {
}

func TestAutoNotify(t *testing.T) {
ts, reports := setup()
defer ts.Close()

var panicked interface{}

func() {
defer func() {
panicked = recover()
}()
defer AutoNotify(Configuration{Endpoints: Endpoints{Notify: testEndpoint}, APIKey: testAPIKey})
defer AutoNotify(Configuration{Endpoints: Endpoints{Notify: ts.URL}, APIKey: testAPIKey})

panic("eggs")
}()
Expand All @@ -238,7 +264,7 @@ func TestAutoNotify(t *testing.T) {
t.Errorf("didn't re-panic")
}

json, err := simplejson.NewJson(<-postedJSON)
json, err := simplejson.NewJson(<-reports)
if err != nil {
t.Fatal(err)
}
Expand All @@ -257,13 +283,16 @@ func TestAutoNotify(t *testing.T) {
}

func TestRecover(t *testing.T) {
ts, reports := setup()
defer ts.Close()

var panicked interface{}

func() {
defer func() {
panicked = recover()
}()
defer Recover(Configuration{Endpoints: Endpoints{Notify: testEndpoint}, APIKey: testAPIKey})
defer Recover(Configuration{Endpoints: Endpoints{Notify: ts.URL}, APIKey: testAPIKey})

panic("ham")
}()
Expand All @@ -272,7 +301,7 @@ func TestRecover(t *testing.T) {
t.Errorf("re-panick'd")
}

json, err := simplejson.NewJson(<-postedJSON)
json, err := simplejson.NewJson(<-reports)
if err != nil {
t.Fatal(err)
}
Expand All @@ -291,25 +320,27 @@ func TestRecover(t *testing.T) {
}

func TestSeverityReasonNotifyErr(t *testing.T) {
startTestServer()
ts, reports := setup()
defer ts.Close()

Notify(fmt.Errorf("hello world"), generateSampleConfig())
Notify(fmt.Errorf("hello world"), generateSampleConfig(ts.URL))

json, _ := simplejson.NewJson(<-postedJSON)
json, _ := simplejson.NewJson(<-reports)
assertSeverityReasonEqual(t, json, "warning", "handledError", false)
}

func TestSeverityReasonNotifyCallback(t *testing.T) {
startTestServer()
ts, reports := setup()
defer ts.Close()

OnBeforeNotify(func(event *Event, config *Configuration) error {
event.Severity = SeverityInfo
return nil
})

Notify(fmt.Errorf("hello world"), generateSampleConfig())
Notify(fmt.Errorf("hello world"), generateSampleConfig(ts.URL))

json, _ := simplejson.NewJson(<-postedJSON)
json, _ := simplejson.NewJson(<-reports)
assertSeverityReasonEqual(t, json, "info", "userCallbackSetSeverity", false)
}

Expand Down Expand Up @@ -350,10 +381,10 @@ func assertSeverityReasonEqual(t *testing.T, json *simplejson.Json, expSeverity
}
}

func generateSampleConfig() Configuration {
func generateSampleConfig(endpoint string) Configuration {
return Configuration{
APIKey: testAPIKey,
Endpoints: Endpoints{Notify: testEndpoint},
Endpoints: Endpoints{Notify: endpoint},
ReleaseStage: "test",
AppType: "foo",
AppVersion: "1.2.3",
Expand All @@ -367,42 +398,3 @@ func crashyHandler(w http.ResponseWriter, r *http.Request) {
close(c)
c <- 1
}

func runCrashyServer(rawData ...interface{}) (net.Listener, error) {
l, err := net.Listen("tcp", "127.0.0.1:0")
if err != nil {
return nil, err
}

mux := http.NewServeMux()
mux.HandleFunc("/", crashyHandler)
srv := http.Server{
Addr: l.Addr().String(),
Handler: Handler(mux, rawData...),
ErrorLog: log.New(ioutil.Discard, log.Prefix(), 0),
}

go srv.Serve(l)
return l, err
}

func startTestServer() {
testOnce.Do(func() {
mux := http.NewServeMux()
mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
body, err := ioutil.ReadAll(r.Body)
if err != nil {
panic(err)
}
postedJSON <- body
})

l, err := net.Listen("tcp", "127.0.0.1:0")
if err != nil {
panic(err)
}
testEndpoint = "http://" + l.Addr().String() + "/"

go http.Serve(l, mux)
})
}
38 changes: 25 additions & 13 deletions panicwrap_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,22 +3,24 @@
package bugsnag

import (
"github.com/bitly/go-simplejson"
"github.com/kardianos/osext"
"os"
"os/exec"
"testing"
"time"

"github.com/bitly/go-simplejson"
"github.com/kardianos/osext"
)

// Test the panic handler by launching a new process which runs the init()
// method in this file and causing a handled panic
func TestPanicHandlerHandledPanic(t *testing.T) {
t.Skip()
startTestServer()
startPanickingProcess(t, "handled")
ts, reports := setup()
defer ts.Close()

startPanickingProcess(t, "handled", ts.URL)

json, err := simplejson.NewJson(<-postedJSON)
json, err := simplejson.NewJson(<-reports)
if err != nil {
t.Fatal(err)
}
Expand Down Expand Up @@ -50,19 +52,20 @@ func TestPanicHandlerHandledPanic(t *testing.T) {
}

// Test the panic handler by launching a new process which runs the init()
// method in this file and causing a handled panic
// method in this file and causing an unhandled panic
func TestPanicHandlerUnhandledPanic(t *testing.T) {
t.Skip()
startTestServer()
startPanickingProcess(t, "unhandled")
json, err := simplejson.NewJson(<-postedJSON)
ts, reports := setup()
defer ts.Close()

startPanickingProcess(t, "unhandled", ts.URL)
json, err := simplejson.NewJson(<-reports)
if err != nil {
t.Fatal(err)
}
assertSeverityReasonEqual(t, json, "error", "unhandledPanic", true)
}

func startPanickingProcess(t *testing.T, variant string) {
func startPanickingProcess(t *testing.T, variant string, endpoint string) {
exePath, err := osext.Executable()
if err != nil {
t.Fatal(err)
Expand All @@ -71,7 +74,16 @@ func startPanickingProcess(t *testing.T, variant string) {
// Use the same trick as panicwrap() to re-run ourselves.
// In the init() block below, we will then panic.
cmd := exec.Command(exePath, os.Args[1:]...)
cmd.Env = append(os.Environ(), "BUGSNAG_API_KEY="+testAPIKey, "BUGSNAG_NOTIFY_ENDPOINT="+testEndpoint, "please_panic="+variant)
cmd.Env = append(os.Environ(), "BUGSNAG_API_KEY="+testAPIKey, "BUGSNAG_NOTIFY_ENDPOINT="+endpoint, "please_panic="+variant)

// Gift for the debugging developer:
// As these tests shell out we don't see, or even want to see, the output
// of these tests by default. The following two lines may be uncommented
// in order to see what this command would print to stdout and stderr.
/*
bytes, _ := cmd.CombinedOutput()
fmt.Println(string(bytes))
*/

if err = cmd.Start(); err != nil {
t.Fatal(err)
Expand Down
23 changes: 14 additions & 9 deletions payload.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ func (p *payload) deliver() error {
return fmt.Errorf("bugsnag/payload.deliver: invalid api key")
}

buf, err := json.Marshal(p)
buf, err := p.MarshalJSON()

if err != nil {
return fmt.Errorf("bugsnag/payload.deliver: %v", err)
Expand Down Expand Up @@ -91,21 +91,26 @@ func (p *payload) MarshalJSON() ([]byte, error) {
}

func (p *payload) makeSession() *sessionJSON {
session := sessionTracker.GetSession(p.Ctx)
if p.Ctx == nil {
return nil
}
handled, unhandled := 1, 0
if p.handledState.Unhandled {
handled, unhandled = unhandled, handled
}

// In the case of an immediate crash on startup, the sessionTracker may
// not have been set up just yet. We therefore have to fall back to a
// payload without a 'session' property
if sessionTracker == nil {
return nil
}

session := sessionTracker.GetSession(p.Ctx)
if p.Ctx == nil {
return nil
}
return &sessionJSON{
ID: session.ID,
StartedAt: session.StartedAt,
Events: eventCountsJSON{
Handled: handled,
Unhandled: unhandled,
},
Events: eventCountsJSON{Handled: handled, Unhandled: unhandled},
}
}

Expand Down

0 comments on commit cc08d45

Please sign in to comment.