Skip to content

Commit 98bbcc4

Browse files
added error aggregation (#39)
* added error aggregation * Update errutils/errutils.go Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> Signed-off-by: Kashif Khan <[email protected]> --------- Signed-off-by: Kashif Khan <[email protected]> Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
1 parent 987c698 commit 98bbcc4

File tree

2 files changed

+190
-0
lines changed

2 files changed

+190
-0
lines changed

errutils/errutils.go

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
// Package errutils provides utilities for error aggregation and handling.
2+
package errutils
3+
4+
import (
5+
"errors"
6+
"strings"
7+
)
8+
9+
// ErrorAggregator aggregates multiple errors into a single error.
10+
type ErrorAggregator struct {
11+
errors []error
12+
}
13+
14+
// NewErrorAggregator creates a new instance of ErrorAggregator.
15+
func NewErrorAggregator() *ErrorAggregator {
16+
return &ErrorAggregator{}
17+
}
18+
19+
// Add adds a new error to the aggregator. If err is nil, it is ignored.
20+
func (e *ErrorAggregator) Add(err error) {
21+
if err != nil {
22+
e.errors = append(e.errors, err)
23+
}
24+
}
25+
26+
// HasErrors returns true if there are any aggregated errors.
27+
func (e *ErrorAggregator) HasErrors() bool {
28+
return len(e.errors) > 0
29+
}
30+
31+
// Error returns the aggregated errors as a single error message.
32+
// If there are no errors, it returns nil.
33+
func (e *ErrorAggregator) Error() error {
34+
if !e.HasErrors() {
35+
return nil
36+
}
37+
38+
var sb strings.Builder
39+
40+
// loop over all the errors in the list and write a string
41+
for i, err := range e.ErrorList() {
42+
sb.WriteString(err.Error())
43+
44+
// for each error add `;` in the end unless it's the last one
45+
if i < len(e.errors)-1 {
46+
sb.WriteString("; ")
47+
}
48+
}
49+
50+
return errors.New(sb.String())
51+
}
52+
53+
// ErrorList returns the list of aggregated errors as a slice.
54+
func (e *ErrorAggregator) ErrorList() []error {
55+
result := make([]error, len(e.errors))
56+
copy(result, e.errors)
57+
return result
58+
}

errutils/errutils_test.go

Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
// Package errutils provides utilities for error aggregation and handling.
2+
package errutils
3+
4+
import (
5+
"errors"
6+
"reflect"
7+
"testing"
8+
)
9+
10+
func TestErrorAggregator_Add(t *testing.T) {
11+
// create one instance on top to use in test
12+
e := NewErrorAggregator()
13+
14+
type args struct {
15+
err error
16+
}
17+
tests := []struct {
18+
name string
19+
args args
20+
total int
21+
}{
22+
{
23+
name: "success - add a new error",
24+
args: args{err: errors.New("a new error")},
25+
total: 1,
26+
},
27+
{
28+
name: "success - add a another new error",
29+
args: args{err: errors.New("a new different error")},
30+
total: 2,
31+
},
32+
}
33+
for _, tt := range tests {
34+
t.Run(tt.name, func(t *testing.T) {
35+
e.Add(tt.args.err)
36+
37+
if len(e.ErrorList()) != tt.total {
38+
t.Errorf("add error failed: got total: %d - want total: %d", len(e.ErrorList()), tt.total)
39+
}
40+
})
41+
}
42+
}
43+
44+
func TestErrorAggregator_HasErrors(t *testing.T) {
45+
e := NewErrorAggregator()
46+
47+
e1 := NewErrorAggregator()
48+
e1.Add(errors.New("a new error for test"))
49+
50+
type args struct {
51+
errs *ErrorAggregator
52+
}
53+
54+
tests := []struct {
55+
name string
56+
args args
57+
want bool
58+
}{
59+
{
60+
name: "no error",
61+
args: args{errs: e},
62+
want: false,
63+
},
64+
{
65+
name: "has error",
66+
args: args{errs: e1},
67+
want: true,
68+
},
69+
}
70+
for _, tt := range tests {
71+
t.Run(tt.name, func(t *testing.T) {
72+
if got := tt.args.errs.HasErrors(); got != tt.want {
73+
t.Errorf("ErrorAggregator.HasErrors() = %v, want %v", got, tt.want)
74+
}
75+
})
76+
}
77+
}
78+
79+
func TestErrorAggregator_Error(t *testing.T) {
80+
e := NewErrorAggregator()
81+
e.Add(errors.New("a new error"))
82+
e.Add(errors.New("a new another error"))
83+
84+
tests := []struct {
85+
name string
86+
errs *ErrorAggregator
87+
want string
88+
}{
89+
{
90+
name: "two errors",
91+
errs: e,
92+
want: "a new error; a new another error",
93+
},
94+
}
95+
96+
for _, tt := range tests {
97+
t.Run(tt.name, func(t *testing.T) {
98+
// calling the Error() method to get the aggregated error
99+
if err := tt.errs.Error(); err != nil {
100+
// Comparing the error message
101+
if err.Error() != tt.want {
102+
t.Errorf("ErrorAggregator.Error() = %v, want %v", err.Error(), tt.want)
103+
}
104+
} else {
105+
t.Errorf("ErrorAggregator.Error() = nil, want %v", tt.want)
106+
}
107+
})
108+
}
109+
}
110+
111+
func TestErrorAggregator_ErrorList(t *testing.T) {
112+
e := NewErrorAggregator()
113+
e.Add(errors.New("a new error"))
114+
e.Add(errors.New("a new another error"))
115+
116+
tests := []struct {
117+
name string
118+
want []error
119+
}{
120+
{
121+
name: "two errors",
122+
want: []error{errors.New("a new error"), errors.New("a new another error")},
123+
},
124+
}
125+
for _, tt := range tests {
126+
t.Run(tt.name, func(t *testing.T) {
127+
if got := e.ErrorList(); !reflect.DeepEqual(got, tt.want) {
128+
t.Errorf("ErrorAggregator.ErrorList() = %v, want %v", got, tt.want)
129+
}
130+
})
131+
}
132+
}

0 commit comments

Comments
 (0)