diff --git a/.github/workflows/test-package.yml b/.github/workflows/test-package.yml index 0feee70e..0566d344 100644 --- a/.github/workflows/test-package.yml +++ b/.github/workflows/test-package.yml @@ -4,7 +4,6 @@ on: [ push, pull_request ] jobs: test: - runs-on: ${{ matrix.os }}-latest defaults: run: @@ -13,7 +12,8 @@ jobs: fail-fast: false matrix: os: [ubuntu, windows] - go-version: ['1.11', '1.12', '1.13', '1.14', '1.15', '1.16', '1.17', '1.18', '1.19', '1.20', '1.21', '1.22'] + #go-version: ['1.11', '1.12', '1.13', '1.14', '1.15', '1.16', '1.17', '1.18', '1.19', '1.20', '1.21', '1.22'] + go-version: ['1.11'] steps: - uses: actions/checkout@v2 @@ -31,27 +31,29 @@ jobs: with: go-version: ${{ matrix.go-version }} - name: install dependencies - run: go get -v -d ./... + run: GO111MODULE="on" go get -v -d ./... - name: run tests - run: go test ./... + run: go test $(go list ./... | grep -v /features/) - name: vet package # go1.12 vet shows spurious 'unknown identifier' issues if: matrix.go-version != '1.12' - run: go vet ./... + run: go vet $(go list ./... | grep -v /features/) + - name: install integration dependencies + if: matrix.os == 'ubuntu' + run: | + sudo apt-get update + sudo apt-get install libcurl4-openssl-dev - name: install Ruby if: matrix.os == 'ubuntu' uses: ruby/setup-ruby@v1 with: - ruby-version: 2.7 + ruby-version: '3.2' bundler-cache: true working-directory: go/src/github.com/bugsnag/bugsnag-go # relative to $GITHUB_WORKSPACE - - name: install integration dependencies - if: matrix.os == 'ubuntu' - run: sudo apt-get install docker-compose - name: maze tests working-directory: go/src/github.com/bugsnag/bugsnag-go if: matrix.os == 'ubuntu' env: GO_VERSION: ${{ matrix.go-version }} - run: bundle exec bugsnag-maze-runner --color --format progress + run: bundle exec maze-runner ./features/user.feature --color --format progress \ No newline at end of file diff --git a/features/apptype.feature b/features/apptype.feature index f03d3fd0..4b9f1826 100644 --- a/features/apptype.feature +++ b/features/apptype.feature @@ -6,13 +6,13 @@ Background: Scenario: An error report contains the configured app type when running a go app Given I set environment variable "AUTO_CAPTURE_SESSIONS" to "false" When I start the service "app" - And I run HandledScenario + And I run "HandledScenario" And I wait to receive an error And the event "app.type" equals "background-queue" Scenario: An session report contains the configured app type when running a go app Given I set environment variable "AUTO_CAPTURE_SESSIONS" to "true" When I start the service "app" - And I run SendSessionScenario + And I run "SendSessionScenario" And I wait to receive a session And the session payload field "app.type" equals "background-queue" diff --git a/features/appversion.feature b/features/appversion.feature index ca552806..032a4e4d 100644 --- a/features/appversion.feature +++ b/features/appversion.feature @@ -6,13 +6,13 @@ Background: Scenario: An error report contains the configured app type when running a go app Given I set environment variable "AUTO_CAPTURE_SESSIONS" to "false" When I start the service "app" - And I run HandledScenario + And I run "HandledScenario" And I wait to receive an error And the event "app.version" equals "3.1.2" Scenario: A session report contains the configured app type when running a go app Given I set environment variable "AUTO_CAPTURE_SESSIONS" to "true" When I start the service "app" - And I run SendSessionScenario + And I run "SendSessionScenario" And I wait to receive a session And the session payload field "app.version" equals "3.1.2" diff --git a/features/autonotify.feature b/features/autonotify.feature index 34eb39a6..cc11dd70 100644 --- a/features/autonotify.feature +++ b/features/autonotify.feature @@ -2,7 +2,7 @@ Feature: Using auto notify Scenario: An error report is sent when an AutoNotified crash occurs which later gets recovered When I start the service "app" - And I run AutonotifyPanicScenario + And I run "AutonotifyPanicScenario" And I wait to receive an error - And the exception "errorClass" equals "Error" + And the exception "errorClass" equals "*errors.errorString" And the exception "message" equals "Go routine killed with auto notify" diff --git a/features/fixtures/app/Dockerfile b/features/fixtures/app/Dockerfile index ae79fd90..42d2f860 100644 --- a/features/fixtures/app/Dockerfile +++ b/features/fixtures/app/Dockerfile @@ -1,13 +1,14 @@ ARG GO_VERSION FROM golang:${GO_VERSION}-alpine -RUN apk update && apk upgrade && apk add git bash +RUN apk update && apk upgrade && apk add git bash build-base ENV GOPATH /app +ENV GO111MODULE="on" COPY features /app/src/features -COPY v2 /app/src/v2 -WORKDIR /app/src/v2 +COPY v2 /app/src/github.com/bugsnag/bugsnag-go/v2 +WORKDIR /app/src/github.com/bugsnag/bugsnag-go/v2 # Ensure subsequent steps are re-run if the GO_VERSION variable changes ARG GO_VERSION @@ -25,8 +26,6 @@ WORKDIR /app/src/features/fixtures/app # Create app module - avoid locking bugsnag dep by not checking it in # Skip on old versions of Go which pre-date modules -RUN if [[ $GO_VERSION != '1.11' && $GO_VERSION != '1.12' ]]; then \ - go mod init && go mod tidy; \ - echo "replace github.com/bugsnag/bugsnag-go/v2 => /app/src/v2" >> go.mod; \ - go mod tidy; \ - fi +RUN go mod init && go mod tidy && \ + echo "replace github.com/bugsnag/bugsnag-go/v2 => /app/src/github.com/bugsnag/bugsnag-go/v2" >> go.mod && \ + go mod tidy diff --git a/features/fixtures/app/handled_scenario.go b/features/fixtures/app/handled_scenario.go index 81fad8b6..b9dd8d22 100644 --- a/features/fixtures/app/handled_scenario.go +++ b/features/fixtures/app/handled_scenario.go @@ -9,8 +9,12 @@ import ( "github.com/bugsnag/bugsnag-go/v2" ) -func HandledErrorScenario() (bugsnag.Configuration, func()) { - config := bugsnag.Configuration{} +func HandledErrorScenario(command Command) (bugsnag.Configuration, func()) { + config := ConfigureBugsnag() + config.APIKey = command.APIKey + config.Endpoints.Sessions = command.SessionsEndpoint + config.Endpoints.Notify = command.NotifyEndpoint + scenarioFunc := func() { if _, err := os.Open("nonexistent_file.txt"); err != nil { if errClass := os.Getenv("ERROR_CLASS"); errClass != "" { @@ -23,9 +27,13 @@ func HandledErrorScenario() (bugsnag.Configuration, func()) { return config, scenarioFunc } -func MultipleHandledErrorsScenario() (bugsnag.Configuration, func()) { +func MultipleHandledErrorsScenario(command Command) (bugsnag.Configuration, func()) { //Make the order of the below predictable - config := bugsnag.Configuration{Synchronous: true} + config := ConfigureBugsnag() + config.APIKey = command.APIKey + config.Endpoints.Sessions = command.SessionsEndpoint + config.Endpoints.Notify = command.NotifyEndpoint + config.Synchronous = true scenarioFunc := func() { ctx := bugsnag.StartSession(context.Background()) @@ -35,8 +43,12 @@ func MultipleHandledErrorsScenario() (bugsnag.Configuration, func()) { return config, scenarioFunc } -func NestedHandledErrorScenario() (bugsnag.Configuration, func()) { - config := bugsnag.Configuration{} +func NestedHandledErrorScenario(command Command) (bugsnag.Configuration, func()) { + config := ConfigureBugsnag() + config.APIKey = command.APIKey + config.Endpoints.Sessions = command.SessionsEndpoint + config.Endpoints.Notify = command.NotifyEndpoint + scenarioFunc := func() { if err := Login("token " + os.Getenv("API_KEY")); err != nil { bugsnag.Notify(NewCustomErr("terminate process", err)) @@ -56,8 +68,12 @@ func NestedHandledErrorScenario() (bugsnag.Configuration, func()) { return config, scenarioFunc } -func HandledCallbackErrorScenario() (bugsnag.Configuration, func()) { - config := bugsnag.Configuration{} +func HandledCallbackErrorScenario(command Command) (bugsnag.Configuration, func()) { + config := ConfigureBugsnag() + config.APIKey = command.APIKey + config.Endpoints.Sessions = command.SessionsEndpoint + config.Endpoints.Notify = command.NotifyEndpoint + scenarioFunc := func() { bugsnag.Notify(fmt.Errorf("inadequent Prep Error"), func(event *bugsnag.Event) { event.Context = "nonfatal.go:14" @@ -70,8 +86,12 @@ func HandledCallbackErrorScenario() (bugsnag.Configuration, func()) { return config, scenarioFunc } -func HandledToUnhandledScenario() (bugsnag.Configuration, func()) { - config := bugsnag.Configuration{} +func HandledToUnhandledScenario(command Command) (bugsnag.Configuration, func()) { + config := ConfigureBugsnag() + config.APIKey = command.APIKey + config.Endpoints.Sessions = command.SessionsEndpoint + config.Endpoints.Notify = command.NotifyEndpoint + scenarioFunc := func() { bugsnag.Notify(fmt.Errorf("unknown event"), func(event *bugsnag.Event) { event.Unhandled = true @@ -81,8 +101,12 @@ func HandledToUnhandledScenario() (bugsnag.Configuration, func()) { return config, scenarioFunc } -func OnBeforeNotifyScenario() (bugsnag.Configuration, func()) { - config := bugsnag.Configuration{} +func OnBeforeNotifyScenario(command Command) (bugsnag.Configuration, func()) { + config := ConfigureBugsnag() + config.APIKey = command.APIKey + config.Endpoints.Sessions = command.SessionsEndpoint + config.Endpoints.Notify = command.NotifyEndpoint + scenarioFunc := func() { bugsnag.OnBeforeNotify( func(event *bugsnag.Event, config *bugsnag.Configuration) error { diff --git a/features/fixtures/app/main.go b/features/fixtures/app/main.go index c19f0714..4b49eaef 100644 --- a/features/fixtures/app/main.go +++ b/features/fixtures/app/main.go @@ -1,7 +1,6 @@ package main import ( - "context" "fmt" "os" "os/signal" @@ -11,7 +10,7 @@ import ( "github.com/bugsnag/bugsnag-go/v2" ) -var scenariosMap = map[string] func()(bugsnag.Configuration, func()){ +var scenariosMap = map[string] func(Command)(bugsnag.Configuration, func()){ "UnhandledScenario": UnhandledCrashScenario, "HandledScenario": HandledErrorScenario, "MultipleUnhandledScenario": MultipleUnhandledErrorsScenario, @@ -31,7 +30,8 @@ var scenariosMap = map[string] func()(bugsnag.Configuration, func()){ func main() { // Listening to the OS Signals - ctx, _ := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM) + signalsChan := make(chan os.Signal, 1) + signal.Notify(signalsChan, syscall.SIGINT, syscall.SIGTERM) ticker := time.NewTicker(1 * time.Second) addr := os.Getenv("DEFAULT_MAZE_ADDRESS") @@ -42,19 +42,20 @@ func main() { for { select { case <-ticker.C: - fmt.Println("[Bugsnag] Get command") - command := GetCommand(DEFAULT_MAZE_ADDRESS) + command := GetCommand(addr) fmt.Printf("[Bugsnag] Received command: %+v\n", command) if command.Action == "run-scenario" { prepareScenarioFunc, ok := scenariosMap[command.ScenarioName] if ok { - config, scenarioFunc := prepareScenarioFunc() + config, scenarioFunc := prepareScenarioFunc(command) bugsnag.Configure(config) + time.Sleep(200 * time.Millisecond) scenarioFunc() + time.Sleep(200 * time.Millisecond) } } - case <-ctx.Done(): + case <-signalsChan: fmt.Println("[Bugsnag] Context is done, closing") ticker.Stop() return diff --git a/features/fixtures/app/metadata_scenario.go b/features/fixtures/app/metadata_scenario.go index f5c191c6..a4e468fc 100644 --- a/features/fixtures/app/metadata_scenario.go +++ b/features/fixtures/app/metadata_scenario.go @@ -6,8 +6,12 @@ import ( "github.com/bugsnag/bugsnag-go/v2" ) -func MetadataScenario() (bugsnag.Configuration, func()) { - config := bugsnag.Configuration{} +func MetadataScenario(command Command) (bugsnag.Configuration, func()) { + config := ConfigureBugsnag() + config.APIKey = command.APIKey + config.Endpoints.Sessions = command.SessionsEndpoint + config.Endpoints.Notify = command.NotifyEndpoint + scenarioFunc := func() { customerData := map[string]string{"Name": "Joe Bloggs", "Age": "21"} bugsnag.Notify(fmt.Errorf("oops"), bugsnag.MetaData{ @@ -20,8 +24,12 @@ func MetadataScenario() (bugsnag.Configuration, func()) { return config, scenarioFunc } -func FilteredMetadataScenario() (bugsnag.Configuration, func()) { - config := bugsnag.Configuration{} +func FilteredMetadataScenario(command Command) (bugsnag.Configuration, func()) { + config := ConfigureBugsnag() + config.APIKey = command.APIKey + config.Endpoints.Sessions = command.SessionsEndpoint + config.Endpoints.Notify = command.NotifyEndpoint + scenarioFunc := func() { bugsnag.Notify(fmt.Errorf("oops"), bugsnag.MetaData{ "Account": { diff --git a/features/fixtures/app/panic_scenario.go b/features/fixtures/app/panic_scenario.go index 6f278a8b..0c049fda 100644 --- a/features/fixtures/app/panic_scenario.go +++ b/features/fixtures/app/panic_scenario.go @@ -4,8 +4,12 @@ import ( "github.com/bugsnag/bugsnag-go/v2" ) -func AutonotifyPanicScenario()(bugsnag.Configuration, func()) { - config := bugsnag.Configuration{} +func AutonotifyPanicScenario(command Command) (bugsnag.Configuration, func()) { + config := ConfigureBugsnag() + config.APIKey = command.APIKey + config.Endpoints.Sessions = command.SessionsEndpoint + config.Endpoints.Notify = command.NotifyEndpoint + scenarioFunc := func() { defer bugsnag.AutoNotify() panic("Go routine killed with auto notify") @@ -14,11 +18,15 @@ func AutonotifyPanicScenario()(bugsnag.Configuration, func()) { return config, scenarioFunc } -func RecoverAfterPanicScenario() (bugsnag.Configuration, func()) { - config := bugsnag.Configuration{} +func RecoverAfterPanicScenario(command Command) (bugsnag.Configuration, func()) { + config := ConfigureBugsnag() + config.APIKey = command.APIKey + config.Endpoints.Sessions = command.SessionsEndpoint + config.Endpoints.Notify = command.NotifyEndpoint + scenarioFunc := func() { defer bugsnag.Recover() panic("Go routine killed but recovered") } return config, scenarioFunc -} \ No newline at end of file +} diff --git a/features/fixtures/app/session_scenario.go b/features/fixtures/app/session_scenario.go index 1bf08b5d..b7282e43 100644 --- a/features/fixtures/app/session_scenario.go +++ b/features/fixtures/app/session_scenario.go @@ -7,16 +7,24 @@ import ( "github.com/bugsnag/bugsnag-go/v2" ) -func SendSessionScenario() (bugsnag.Configuration, func()) { - config := bugsnag.Configuration{} +func SendSessionScenario(command Command) (bugsnag.Configuration, func()) { + config := ConfigureBugsnag() + config.APIKey = command.APIKey + config.Endpoints.Sessions = command.SessionsEndpoint + config.Endpoints.Notify = command.NotifyEndpoint + scenarioFunc := func() { bugsnag.StartSession(context.Background()) } return config, scenarioFunc } -func SessionAndErrorScenario() (bugsnag.Configuration, func()) { - config := bugsnag.Configuration{} +func SessionAndErrorScenario(command Command) (bugsnag.Configuration, func()) { + config := ConfigureBugsnag() + config.APIKey = command.APIKey + config.Endpoints.Sessions = command.SessionsEndpoint + config.Endpoints.Notify = command.NotifyEndpoint + scenarioFunc := func() { ctx := bugsnag.StartSession(context.Background()) bugsnag.Notify(fmt.Errorf("oops"), ctx) diff --git a/features/fixtures/app/unhandled_scenario.go b/features/fixtures/app/unhandled_scenario.go index eb6ba7dd..4eb1b4b3 100644 --- a/features/fixtures/app/unhandled_scenario.go +++ b/features/fixtures/app/unhandled_scenario.go @@ -7,8 +7,12 @@ import ( ) //go:noinline -func UnhandledCrashScenario() (bugsnag.Configuration, func()) { - config := bugsnag.Configuration{} +func UnhandledCrashScenario(command Command) (bugsnag.Configuration, func()) { + config := ConfigureBugsnag() + config.APIKey = command.APIKey + config.Endpoints.Sessions = command.SessionsEndpoint + config.Endpoints.Notify = command.NotifyEndpoint + scenarioFunc := func() { // Invalid type assertion, will panic func(a interface{}) string { @@ -18,8 +22,12 @@ func UnhandledCrashScenario() (bugsnag.Configuration, func()) { return config, scenarioFunc } -func MultipleUnhandledErrorsScenario() (bugsnag.Configuration, func()) { - config := bugsnag.Configuration{} +func MultipleUnhandledErrorsScenario(command Command) (bugsnag.Configuration, func()) { + config := ConfigureBugsnag() + config.APIKey = command.APIKey + config.Endpoints.Sessions = command.SessionsEndpoint + config.Endpoints.Notify = command.NotifyEndpoint + scenarioFunc := func() { //Make the order of the below predictable notifier := bugsnag.New(bugsnag.Configuration{Synchronous: true}) diff --git a/features/fixtures/app/user_scenario.go b/features/fixtures/app/user_scenario.go index d4e3422d..bd4a491d 100644 --- a/features/fixtures/app/user_scenario.go +++ b/features/fixtures/app/user_scenario.go @@ -6,8 +6,12 @@ import ( "github.com/bugsnag/bugsnag-go/v2" ) -func SetUserScenario() (bugsnag.Configuration, func()) { - config := bugsnag.Configuration{} +func SetUserScenario(command Command) (bugsnag.Configuration, func()) { + config := ConfigureBugsnag() + config.APIKey = command.APIKey + config.Endpoints.Sessions = command.SessionsEndpoint + config.Endpoints.Notify = command.NotifyEndpoint + scenarioFunc := func() { bugsnag.Notify(fmt.Errorf("oops"), bugsnag.User{ Id: "test-user-id", diff --git a/features/handled.feature b/features/handled.feature index b114c4b5..844391fe 100644 --- a/features/handled.feature +++ b/features/handled.feature @@ -1,67 +1,67 @@ Feature: Plain handled errors Background: - Given I set environment variable "BUGSNAG_SOURCE_ROOT" to "app/src/test/" + Given I set environment variable "BUGSNAG_SOURCE_ROOT" to "/app/src/features/fixtures/app/" And I set environment variable "AUTO_CAPTURE_SESSIONS" to "false" Scenario: A handled error sends a report When I start the service "app" - And I run HandledScenario + And I run "HandledScenario" And I wait to receive an error And the event "unhandled" is false And the event "severity" equals "warning" - And the event "severityReason.type" equals "handledException" - And the exception "errorClass" equals "Error" - And the "file" of stack frame 0 equals "main.go" + And the event "severityReason.type" equals "handledError" + And the exception "errorClass" equals "*os.PathError" + And the "file" of stack frame 0 equals "handled_scenario.go" Scenario: A handled error sends a report with a custom name Given I set environment variable "ERROR_CLASS" to "MyCustomErrorClass" When I start the service "app" - And I run HandledScenario + And I run "HandledScenario" And I wait to receive an error And the event "unhandled" is false And the event "severity" equals "warning" - And the event "severityReason.type" equals "handledException" + And the event "severityReason.type" equals "handledError" And the exception "errorClass" equals "MyCustomErrorClass" - And the "file" of stack frame 0 equals "main.go" + And the "file" of stack frame 0 equals "handled_scenario.go" Scenario: Sending an event using a callback to modify report contents When I start the service "app" - And I run HandledCallbackErrorScenario + And I run "HandledCallbackErrorScenario" And I wait to receive an error And the event "unhandled" is false And the event "severity" equals "info" And the event "severityReason.type" equals "userCallbackSetSeverity" And the event "context" equals "nonfatal.go:14" - And the "file" of stack frame 0 equals "main.go" - And the "lineNumber" of stack frame 0 equals 242 + And the "file" of stack frame 0 equals "handled_scenario.go" + And the "lineNumber" of stack frame 0 equals 78 And the "file" of stack frame 1 equals ">insertion<" And the "lineNumber" of stack frame 1 equals 0 Scenario: Marking an error as unhandled in a callback When I start the service "app" - And I run HandledToUnhandledScenario + And I run "HandledToUnhandledScenario" And I wait to receive an error And the event "unhandled" is true And the event "severity" equals "error" And the event "severityReason.type" equals "userCallbackSetSeverity" And the event "severityReason.unhandledOverridden" is true - And the "file" of stack frame 0 equals "main.go" - And the "lineNumber" of stack frame 0 equals 254 + And the "file" of stack frame 0 equals "handled_scenario.go" + And the "lineNumber" of stack frame 0 equals 96 Scenario: Unwrapping the causes of a handled error When I start the service "app" - And I run NestedErrorScenario + And I run "NestedErrorScenario" And I wait to receive an error And the event "unhandled" is false And the event "severity" equals "warning" And the event "exceptions.0.message" equals "terminate process" - And the "lineNumber" of stack frame 0 equals 292 - And the "file" of stack frame 0 equals "main.go" - And the "method" of stack frame 0 equals "nestedHandledError" + And the "lineNumber" of stack frame 0 equals 54 + And the "file" of stack frame 0 equals "handled_scenario.go" + And the "method" of stack frame 0 equals "NestedHandledErrorScenario.func1" And the event "exceptions.1.message" equals "login failed" - And the event "exceptions.1.stacktrace.0.file" equals "main.go" - And the event "exceptions.1.stacktrace.0.lineNumber" equals 312 + And the event "exceptions.1.stacktrace.0.file" equals "utils.go" + And the event "exceptions.1.stacktrace.0.lineNumber" equals 39 And the event "exceptions.2.message" equals "invalid token" - And the event "exceptions.2.stacktrace.0.file" equals "main.go" - And the event "exceptions.2.stacktrace.0.lineNumber" equals 320 + And the event "exceptions.2.stacktrace.0.file" equals "utils.go" + And the event "exceptions.2.stacktrace.0.lineNumber" equals 47 diff --git a/features/hostname.feature b/features/hostname.feature index 658f4be0..a32af02e 100644 --- a/features/hostname.feature +++ b/features/hostname.feature @@ -4,7 +4,7 @@ Scenario: An error report contains the configured hostname Given I set environment variable "HOSTNAME" to "server-1a" And I set environment variable "AUTO_CAPTURE_SESSIONS" to "false" When I start the service "app" - And I run HandledScenario + And I run "HandledScenario" And I wait to receive an error And the event "device.hostname" equals "server-1a" @@ -12,6 +12,6 @@ Scenario: An session report contains the configured hostname Given I set environment variable "HOSTNAME" to "server-1a" And I set environment variable "AUTO_CAPTURE_SESSIONS" to "true" When I start the service "app" - And I run SendSessionScenario + And I run "SendSessionScenario" And I wait to receive a session And the session payload field "device.hostname" equals "server-1a" diff --git a/features/metadata.feature b/features/metadata.feature index 42d13750..a6205fda 100644 --- a/features/metadata.feature +++ b/features/metadata.feature @@ -3,7 +3,7 @@ Feature: Sending meta data Scenario: An error report contains custom meta data When I set environment variable "AUTO_CAPTURE_SESSIONS" to "false" And I start the service "app" - And I run MetadataScenario + And I run "MetadataScenario" And I wait to receive an error And the event "metaData.Scheme.Customer.Name" equals "Joe Bloggs" And the event "metaData.Scheme.Customer.Age" equals "21" diff --git a/features/multieventsession.feature b/features/multieventsession.feature index 141ffd65..0ea0d6f4 100644 --- a/features/multieventsession.feature +++ b/features/multieventsession.feature @@ -5,14 +5,14 @@ Background: Scenario: Handled errors know about previous reported handled errors When I start the service "app" - And I run MultipleHandledScenario + And I run "MultipleHandledScenario" And I wait to receive 2 errors And the event handled sessions count equals 1 for request 0 And the event handled sessions count equals 2 for request 1 Scenario: Unhandled errors know about previous reported handled errors When I start the service "app" - And I run MultipleUnhandledScenario + And I run "MultipleUnhandledScenario" And I wait to receive 2 errors And the event unhandled sessions count equals 1 for request 0 And the event unhandled sessions count equals 2 for request 1 diff --git a/features/onbeforenotify.feature b/features/onbeforenotify.feature index a3886615..0f7681ba 100644 --- a/features/onbeforenotify.feature +++ b/features/onbeforenotify.feature @@ -2,7 +2,7 @@ Feature: Configuring on before notify Scenario: Send three bugsnags and use on before notify to drop one and modify the message of another When I start the service "app" - And I run OnBeforeNotifyScenario + And I run "OnBeforeNotifyScenario" And I wait to receive 2 errors And the exception "message" equals "Don't ignore this error" And I discard the oldest error diff --git a/features/paramfilters.feature b/features/paramfilters.feature index 68ebe550..7d91f69f 100644 --- a/features/paramfilters.feature +++ b/features/paramfilters.feature @@ -3,21 +3,21 @@ Feature: Configuring param filters Scenario: An error report containing meta data is not filtered when the param filters are set but do not match Given I set environment variable "PARAMS_FILTERS" to "Name" When I start the service "app" - And I run FilteredMetadataScenario + And I run "FilteredMetadataScenario" And I wait to receive an error And the event "metaData.Account.Price(dollars)" equals "1 Million" Scenario: An error report containing meta data is filtered when the param filters are set and completely match Given I set environment variable "PARAMS_FILTERS" to "Price(dollars)" When I start the service "app" - And I run FilteredMetadataScenario + And I run "FilteredMetadataScenario" And I wait to receive an error And the event "metaData.Account.Price(dollars)" equals "[FILTERED]" Scenario: An error report containing meta data is filtered when the param filters are set and partially match Given I set environment variable "PARAMS_FILTERS" to "Price" When I start the service "app" - And I run FilteredMetadataScenario + And I run "FilteredMetadataScenario" And I wait to receive an error And the event "metaData.Account.Price(dollars)" equals "[FILTERED]" diff --git a/features/recover.feature b/features/recover.feature index 35bbf850..618fe8b2 100644 --- a/features/recover.feature +++ b/features/recover.feature @@ -2,7 +2,7 @@ Feature: Using recover Scenario: An error report is sent when a go routine crashes but recovers When I start the service "app" - And I run RecoverAfterPanicScenario + And I run "RecoverAfterPanicScenario" And I wait to receive an error - And the exception "errorClass" equals "Error" + And the exception "errorClass" equals "*errors.errorString" And the exception "message" equals "Go routine killed but recovered" diff --git a/features/releasestage.feature b/features/releasestage.feature index e856e771..59edd8a4 100644 --- a/features/releasestage.feature +++ b/features/releasestage.feature @@ -5,7 +5,7 @@ Scenario: An error report is sent when release stage matches notify release stag And I set environment variable "AUTO_CAPTURE_SESSIONS" to "false" And I set environment variable "RELEASE_STAGE" to "stage2" When I start the service "app" - And I run HandledScenario + And I run "HandledScenario" And I wait to receive an error And the event "app.releaseStage" equals "stage2" @@ -13,7 +13,7 @@ Scenario: An error report is sent when no notify release stages are specified Given I set environment variable "RELEASE_STAGE" to "stage2" And I set environment variable "AUTO_CAPTURE_SESSIONS" to "false" When I start the service "app" - And I run HandledScenario + And I run "HandledScenario" And I wait to receive an error And the event "app.releaseStage" equals "stage2" @@ -21,7 +21,7 @@ Scenario: An error report is sent regardless of notify release stages if release Given I set environment variable "NOTIFY_RELEASE_STAGES" to "stage1,stage2,stage3" And I set environment variable "AUTO_CAPTURE_SESSIONS" to "false" When I start the service "app" - And I run HandledScenario + And I run "HandledScenario" And I wait to receive an error Scenario: An error report is not sent if the release stage does not match the notify release stages @@ -29,7 +29,7 @@ Scenario: An error report is not sent if the release stage does not match the no And I set environment variable "AUTO_CAPTURE_SESSIONS" to "false" And I set environment variable "RELEASE_STAGE" to "stage4" When I start the service "app" - And I run HandledScenario + And I run "HandledScenario" And I should receive no errors Scenario: An session report is sent when release stage matches notify release stages @@ -37,7 +37,7 @@ Scenario: An session report is sent when release stage matches notify release st And I set environment variable "AUTO_CAPTURE_SESSIONS" to "true" And I set environment variable "RELEASE_STAGE" to "stage2" When I start the service "app" - And I run SendSessionScenario + And I run "SendSessionScenario" And I wait to receive a session And the session payload field "app.releaseStage" equals "stage2" @@ -45,7 +45,7 @@ Scenario: An session report is sent when no notify release stages are specified Given I set environment variable "RELEASE_STAGE" to "stage2" And I set environment variable "AUTO_CAPTURE_SESSIONS" to "true" When I start the service "app" - And I run SendSessionScenario + And I run "SendSessionScenario" And I wait to receive a session And the session payload field "app.releaseStage" equals "stage2" @@ -53,7 +53,7 @@ Scenario: An session report is sent regardless of notify release stages if relea Given I set environment variable "NOTIFY_RELEASE_STAGES" to "stage1,stage2,stage3" And I set environment variable "AUTO_CAPTURE_SESSIONS" to "true" When I start the service "app" - And I run SendSessionScenario + And I run "SendSessionScenario" And I wait to receive a session Scenario: An session report is not sent if the release stage does not match the notify release stages @@ -61,6 +61,6 @@ Scenario: An session report is not sent if the release stage does not match the And I set environment variable "AUTO_CAPTURE_SESSIONS" to "true" And I set environment variable "RELEASE_STAGE" to "stage4" When I start the service "app" - And I run SendSessionScenario + And I run "SendSessionScenario" And I should receive no sessions diff --git a/features/sessioncontext.feature b/features/sessioncontext.feature index c0d276ee..87583c9d 100644 --- a/features/sessioncontext.feature +++ b/features/sessioncontext.feature @@ -2,7 +2,7 @@ Feature: Session data inside an error report using a session context Scenario: An error report contains a session count when part of a session When I start the service "app" - And I run SessionAndErrorScenario + And I run "SessionAndErrorScenario" Then I wait to receive 2 errors And the event handled sessions count equals 1 for request 0 And the event unhandled sessions count equals 0 for request 0 diff --git a/features/support/env.rb b/features/support/env.rb index 8c279b6d..7ba266b1 100644 --- a/features/support/env.rb +++ b/features/support/env.rb @@ -1,5 +1,6 @@ Before do $address = nil + $api_key = "166f5ad3590596f9aa8d601ea89af845" steps %( When I configure the maze endpoint ) diff --git a/features/user.feature b/features/user.feature index e9a41424..99aab721 100644 --- a/features/user.feature +++ b/features/user.feature @@ -5,7 +5,7 @@ Scenario: An error report contains custom user data And I set environment variable "USER_NAME" to "test-user-name" And I set environment variable "USER_EMAIL" to "test-user-email" When I start the service "app" - And I run SetUserScenario + And I run "SetUserScenario" And I wait to receive an error And the event "user.id" equals "test-user-id" And the event "user.name" equals "test-user-name" diff --git a/v2/headers/prefixed.go b/v2/headers/prefixed.go index 099d7947..8a518e80 100644 --- a/v2/headers/prefixed.go +++ b/v2/headers/prefixed.go @@ -1,14 +1,20 @@ package headers -import "time" +import ( + "fmt" + "time" +) + +// PrefixedHeaders returns a map of Content-Type and the 'Bugsnag-' headers for +// API key, payload version, and the time at which the request is being sent. +func PrefixedHeaders(apiKey, payloadVersion, sha1 string) map[string]string { + integrityHeader := fmt.Sprintf("sha1 %v", sha1) -//PrefixedHeaders returns a map of Content-Type and the 'Bugsnag-' headers for -//API key, payload version, and the time at which the request is being sent. -func PrefixedHeaders(apiKey, payloadVersion string) map[string]string { return map[string]string{ "Content-Type": "application/json", "Bugsnag-Api-Key": apiKey, "Bugsnag-Payload-Version": payloadVersion, "Bugsnag-Sent-At": time.Now().UTC().Format(time.RFC3339), + "Bugsnag-Integrity": integrityHeader, } } diff --git a/v2/headers/prefixed_test.go b/v2/headers/prefixed_test.go index 739d9e4f..97e85986 100644 --- a/v2/headers/prefixed_test.go +++ b/v2/headers/prefixed_test.go @@ -1,6 +1,7 @@ package headers import ( + "fmt" "strings" "testing" "time" @@ -8,9 +9,10 @@ import ( const APIKey = "abcd1234abcd1234" const testPayloadVersion = "3" +const testSHA = "5e13ae4640ae4ae0e09c05b7bb060f544dabd042" func TestConstantBugsnagPrefixedHeaders(t *testing.T) { - headers := PrefixedHeaders(APIKey, testPayloadVersion) + headers := PrefixedHeaders(APIKey, testPayloadVersion, testSHA) testCases := []struct { header string expected string @@ -18,6 +20,7 @@ func TestConstantBugsnagPrefixedHeaders(t *testing.T) { {header: "Content-Type", expected: "application/json"}, {header: "Bugsnag-Api-Key", expected: APIKey}, {header: "Bugsnag-Payload-Version", expected: testPayloadVersion}, + {header: "Bugsnag-Integrity", expected: fmt.Sprintf("sha1 %v", testSHA)}, } for _, tc := range testCases { t.Run(tc.header, func(st *testing.T) { @@ -29,7 +32,7 @@ func TestConstantBugsnagPrefixedHeaders(t *testing.T) { } func TestTimeDependentBugsnagPrefixedHeaders(t *testing.T) { - headers := PrefixedHeaders(APIKey, testPayloadVersion) + headers := PrefixedHeaders(APIKey, testPayloadVersion ,testSHA) sentAtString := headers["Bugsnag-Sent-At"] if !strings.HasSuffix(sentAtString, "Z") { t.Errorf("Error when setting Bugsnag-Sent-At header: %s, doesn't end with a Z", sentAtString) diff --git a/v2/payload.go b/v2/payload.go index be2234ce..2ab427a9 100644 --- a/v2/payload.go +++ b/v2/payload.go @@ -2,6 +2,8 @@ package bugsnag import ( "bytes" + "crypto/sha1" + "encoding/hex" "encoding/json" "fmt" "net/http" @@ -32,10 +34,16 @@ func (p *payload) deliver() error { } buf, err := p.MarshalJSON() + if err != nil { + return fmt.Errorf("bugsnag/payload.deliver: %v", err) + } + hasher := sha1.New() + _, err = hasher.Write(buf) if err != nil { return fmt.Errorf("bugsnag/payload.deliver: %v", err) } + sha1_hash := hex.EncodeToString(hasher.Sum(nil)) client := http.Client{ Transport: p.Transport, @@ -44,7 +52,7 @@ func (p *payload) deliver() error { if err != nil { return fmt.Errorf("bugsnag/payload.deliver unable to create request: %v", err) } - for k, v := range headers.PrefixedHeaders(p.APIKey, notifyPayloadVersion) { + for k, v := range headers.PrefixedHeaders(p.APIKey, notifyPayloadVersion, sha1_hash) { req.Header.Add(k, v) } resp, err := client.Do(req) diff --git a/v2/sessions/publisher.go b/v2/sessions/publisher.go index b06985fa..895cd2f1 100644 --- a/v2/sessions/publisher.go +++ b/v2/sessions/publisher.go @@ -2,6 +2,8 @@ package sessions import ( "bytes" + "crypto/sha1" + "encoding/hex" "encoding/json" "fmt" "net/http" @@ -55,11 +57,18 @@ func (p *publisher) publish(sessions []*Session) error { if err != nil { return fmt.Errorf("bugsnag/sessions/publisher.publish unable to marshal json: %v", err) } + hasher := sha1.New() + _, err = hasher.Write(buf) + if err != nil { + return fmt.Errorf("bugsnag/payload.deliver: %v", err) + } + sha1_hash := hex.EncodeToString(hasher.Sum(nil)) + req, err := http.NewRequest("POST", p.config.Endpoint, bytes.NewBuffer(buf)) if err != nil { return fmt.Errorf("bugsnag/sessions/publisher.publish unable to create request: %v", err) } - for k, v := range headers.PrefixedHeaders(p.config.APIKey, sessionPayloadVersion) { + for k, v := range headers.PrefixedHeaders(p.config.APIKey, sessionPayloadVersion, sha1_hash) { req.Header.Add(k, v) } res, err := p.client.Do(req)