1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174
|
package failures_test
import (
"errors"
"fmt"
"testing"
"github.com/onsi/ginkgo/v2"
"github.com/onsi/gomega"
"github.com/onsi/gomega/format"
"github.com/nicholas-fedor/shoutrrr/internal/failures"
"github.com/nicholas-fedor/shoutrrr/internal/testutils"
)
// TestFailures runs the Ginkgo test suite for the failures package.
func TestFailures(t *testing.T) {
format.CharactersAroundMismatchToInclude = 20 // Show more context in failure output
gomega.RegisterFailHandler(ginkgo.Fail)
ginkgo.RunSpecs(t, "Failure Suite")
}
var _ = ginkgo.Describe("the failure package", func() {
// Common test fixtures
var (
testID failures.FailureID = 42 // Consistent ID for testing
testMessage = "test failure occurred" // Sample error message
wrappedErr = errors.New("underlying error") // Sample wrapped error
)
ginkgo.Describe("Wrap function", func() {
ginkgo.When("creating a basic failure", func() {
ginkgo.It("returns a failure with the provided message and ID", func() {
failure := failures.Wrap(testMessage, testID, nil)
gomega.Expect(failure.Error()).To(gomega.Equal(testMessage))
gomega.Expect(failure.ID()).To(gomega.Equal(testID))
gomega.Expect(failure.Unwrap()).To(gomega.Succeed())
})
})
ginkgo.When("wrapping an existing error", func() {
ginkgo.It("combines the message and wrapped error", func() {
failure := failures.Wrap(testMessage, testID, wrappedErr)
expectedError := fmt.Sprintf("%s: %v", testMessage, wrappedErr)
gomega.Expect(failure.Error()).To(gomega.Equal(expectedError))
gomega.Expect(failure.ID()).To(gomega.Equal(testID))
gomega.Expect(failure.Unwrap()).To(gomega.Equal(wrappedErr))
})
})
ginkgo.When("using formatted message with arguments", func() {
ginkgo.It("formats the message correctly", func() {
formatMessage := "test failure %d"
failure := failures.Wrap(formatMessage, testID, nil, 123)
gomega.Expect(failure.Error()).To(gomega.Equal("test failure 123"))
gomega.Expect(failure.ID()).To(gomega.Equal(testID))
})
})
})
ginkgo.Describe("Failure interface methods", func() {
var failure failures.Failure
// Setup a failure with a wrapped error before each test
ginkgo.BeforeEach(func() {
failure = failures.Wrap(testMessage, testID, wrappedErr)
})
ginkgo.Describe("Error method", func() {
ginkgo.It("returns only the message when no wrapped error exists", func() {
failureNoWrap := failures.Wrap(testMessage, testID, nil)
gomega.Expect(failureNoWrap.Error()).To(gomega.Equal(testMessage))
})
ginkgo.It("combines message with wrapped error", func() {
expected := fmt.Sprintf("%s: %v", testMessage, wrappedErr)
gomega.Expect(failure.Error()).To(gomega.Equal(expected))
})
})
ginkgo.Describe("ID method", func() {
ginkgo.It("returns the assigned ID", func() {
gomega.Expect(failure.ID()).To(gomega.Equal(testID))
})
})
ginkgo.Describe("Unwrap method", func() {
ginkgo.It("returns the wrapped error", func() {
gomega.Expect(failure.Unwrap()).To(gomega.Equal(wrappedErr))
})
ginkgo.It("returns nil when no wrapped error exists", func() {
failureNoWrap := failures.Wrap(testMessage, testID, nil)
gomega.Expect(failureNoWrap.Unwrap()).To(gomega.Succeed())
})
})
ginkgo.Describe("Is method", func() {
ginkgo.It("returns true for failures with the same ID", func() {
f1 := failures.Wrap("first", testID, nil)
f2 := failures.Wrap("second", testID, nil)
gomega.Expect(f1.Is(f2)).To(gomega.BeTrue())
gomega.Expect(f2.Is(f1)).To(gomega.BeTrue())
})
ginkgo.It("returns false for failures with different IDs", func() {
f1 := failures.Wrap("first", testID, nil)
f2 := failures.Wrap("second", testID+1, nil)
gomega.Expect(f1.Is(f2)).To(gomega.BeFalse())
gomega.Expect(f2.Is(f1)).To(gomega.BeFalse())
})
ginkgo.It("returns false when comparing with a non-failure error", func() {
f1 := failures.Wrap("first", testID, nil)
gomega.Expect(f1.Is(wrappedErr)).To(gomega.BeFalse())
})
})
})
ginkgo.Describe("edge cases", func() {
ginkgo.When("wrapping with an empty message", func() {
ginkgo.It("handles an empty message gracefully", func() {
failure := failures.Wrap("", testID, wrappedErr)
gomega.Expect(failure.Error()).To(gomega.Equal(": " + wrappedErr.Error()))
gomega.Expect(failure.ID()).To(gomega.Equal(testID))
gomega.Expect(failure.Unwrap()).To(gomega.Equal(wrappedErr))
})
})
ginkgo.When("wrapping with nil error and no args", func() {
ginkgo.It("returns a valid failure with just message and ID", func() {
failure := failures.Wrap(testMessage, testID, nil)
gomega.Expect(failure.Error()).To(gomega.Equal(testMessage))
gomega.Expect(failure.ID()).To(gomega.Equal(testID))
gomega.Expect(failure.Unwrap()).To(gomega.Succeed())
})
})
ginkgo.When("using multiple wrapped failures", func() {
ginkgo.It("correctly chains and unwraps multiple errors", func() {
innerErr := errors.New("inner error")
middleErr := failures.Wrap("middle", testID+1, innerErr)
outerErr := failures.Wrap("outer", testID, middleErr)
gomega.Expect(outerErr.Error()).To(gomega.Equal("outer: middle: inner error"))
gomega.Expect(outerErr.ID()).To(gomega.Equal(testID))
gomega.Expect(outerErr.Unwrap()).To(gomega.Equal(middleErr))
gomega.Expect(middleErr.Unwrap()).To(gomega.Equal(innerErr))
})
})
})
ginkgo.Describe("integration-like scenarios", func() {
ginkgo.It("works with standard error wrapping utilities", func() {
innerErr := errors.New("inner error")
failure := failures.Wrap("wrapped failure", testID, innerErr)
gomega.Expect(errors.Is(failure, innerErr)).To(gomega.BeTrue()) // Matches wrapped error
gomega.Expect(errors.Unwrap(failure)).To(gomega.Equal(innerErr))
})
ginkgo.It("handles fmt.Errorf wrapping", func() {
failure := failures.Wrap("failure", testID, nil)
wrapped := fmt.Errorf("additional context: %w", failure)
gomega.Expect(wrapped.Error()).To(gomega.Equal("additional context: failure"))
gomega.Expect(errors.Unwrap(wrapped)).To(gomega.Equal(failure))
})
})
ginkgo.Describe("testutils integration", func() {
ginkgo.It("can use TestLogger for logging failures", func() {
// Demonstrate compatibility with testutils logger
failure := failures.Wrap("logged failure", testID, nil)
logger := testutils.TestLogger()
logger.Printf("Error occurred: %v", failure)
// No assertion needed; ensures no panic during logging
})
})
})
|