Skip to content

Commit

Permalink
Don't use reflect.DeepEqual for errors (#7311)
Browse files Browse the repository at this point in the history
I pushed most of these fixed previously but these ones required
more work. Probably more work that it was worth, lol, but now the
work is done... and at least we can add this to the list of enabled
checks.

Fixes #7238

Signed-off-by: Anders Eknert <anders@styra.com>
  • Loading branch information
anderseknert authored Jan 24, 2025
1 parent d20dd18 commit e682a67
Show file tree
Hide file tree
Showing 10 changed files with 102 additions and 31 deletions.
3 changes: 3 additions & 0 deletions .golangci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,9 @@ issues:
linters-settings:
lll:
line-length: 200
govet:
enable:
- deepequalerrors

linters:
disable-all: true
Expand Down
2 changes: 1 addition & 1 deletion v1/ast/parser_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1932,7 +1932,7 @@ func TestIsValidImportPath(t *testing.T) {
result := IsValidImportPath(path)
if tc.expected == nil && result != nil {
t.Errorf("Unexpected error for %v: %v", path, result)
} else if !reflect.DeepEqual(tc.expected, result) {
} else if tc.expected.Error() != result.Error() {
t.Errorf("For %v expected %v but got: %v", path, tc.expected, result)
}
}
Expand Down
3 changes: 1 addition & 2 deletions v1/dependencies/deps_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -511,8 +511,7 @@ func assertRefSliceEq(t *testing.T, exp, result []ast.Ref) {
}

for i, e := range exp {
r := result[i]
if e.Compare(r) != 0 {
if !e.Equal(result[i]) {
t.Fatalf("Expected refs %v, got %v", exp, result)
}
}
Expand Down
38 changes: 19 additions & 19 deletions v1/plugins/bundle/plugin_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ func TestPluginOneShot(t *testing.T) {

data, err := manager.Store.Read(ctx, txn, storage.Path{})
expData := util.MustUnmarshalJSON([]byte(`{
"foo": {"bar": 1, "baz": "qux"},
"foo": {"bar": 1, "baz": "qux"},
"system": {
"bundles": {"test-bundle": {"etag": "foo", "manifest": {"revision": "quickbrownfaux", "roots": [""]}}},
"modules": {"test-bundle/foo/bar": {"rego_version": 1}}
Expand Down Expand Up @@ -968,7 +968,7 @@ func TestPluginStartLazyLoadInMem(t *testing.T) {
}

expected := `{
"p": "x1", "q": "x2",
"p": "x1", "q": "x2",
"system": {
"bundles": {"test-1": {"etag": "", "manifest": {"revision": "", "roots": ["p", "authz"]}}, "test-2": {"etag": "", "manifest": {"revision": "", "roots": ["q"]}}},
"modules": {"test-1/bar/policy.rego": {"rego_version": 1}}
Expand Down Expand Up @@ -1087,7 +1087,7 @@ func TestPluginOneShotDiskStorageMetrics(t *testing.T) {

data, err := manager.Store.Read(ctx, txn, storage.Path{})
expData := util.MustUnmarshalJSON([]byte(`{
"foo": {"bar": 1, "baz": "qux"},
"foo": {"bar": 1, "baz": "qux"},
"system": {
"bundles": {"test-bundle": {"etag": "", "manifest": {"revision": "quickbrownfaux", "roots": [""]}}},
"modules": {"test-bundle/foo/bar": {"rego_version": 1}}
Expand Down Expand Up @@ -1194,7 +1194,7 @@ func TestPluginOneShotDeltaBundle(t *testing.T) {
t.Fatal(err)
}
expData := util.MustUnmarshalJSON([]byte(`{
"a": {"baz": "bux", "foo": ["hello", "world"]},
"a": {"baz": "bux", "foo": ["hello", "world"]},
"system": {
"bundles": {"test-bundle": {"etag": "foo", "manifest": {"revision": "delta", "roots": ["a"]}}},
"modules": {"test-bundle/a/policy.rego": {"rego_version": 1}}
Expand Down Expand Up @@ -1299,7 +1299,7 @@ func TestPluginOneShotDeltaBundleWithAstStore(t *testing.T) {
t.Fatal(err)
}
expData := ast.MustParseTerm(`{
"a": {"baz": "bux", "foo": ["hello", "world"]},
"a": {"baz": "bux", "foo": ["hello", "world"]},
"system": {
"bundles": {"test-bundle": {"etag": "foo", "manifest": {"revision": "delta", "roots": ["a"]}}},
"modules": {"test-bundle/a/policy.rego": {"rego_version": 1}}
Expand Down Expand Up @@ -1486,7 +1486,7 @@ func TestPluginOneShotBundlePersistence(t *testing.T) {

data, err := manager.Store.Read(ctx, txn, storage.Path{})
expData := util.MustUnmarshalJSON([]byte(`{
"foo": {"bar": 1, "baz": "qux"},
"foo": {"bar": 1, "baz": "qux"},
"system": {
"bundles": {"test-bundle": {"etag": "foo", "manifest": {"revision": "quickbrownfaux", "roots": [""]}}},
"modules": {"test-bundle/foo/bar.rego": {"rego_version": 1}}
Expand Down Expand Up @@ -1675,7 +1675,7 @@ corge contains 1 if {
}

expData := util.MustUnmarshalJSON([]byte(`{
"foo": {"bar": 1, "baz": "qux"},
"foo": {"bar": 1, "baz": "qux"},
"system": {
"bundles": {"test-bundle": {"etag": "foo", "manifest": {"revision": "quickbrownfaux", "roots": [""]}}}
}
Expand Down Expand Up @@ -1980,7 +1980,7 @@ corge contains 1 if {
}

expData := util.MustUnmarshalJSON([]byte(fmt.Sprintf(`{
"foo": {"bar": 1, "baz": "qux"},
"foo": {"bar": 1, "baz": "qux"},
"system": {
"bundles": {"test-bundle": {"etag": "foo", "manifest": {"revision": "quickbrownfaux"%s, "roots": [""]}}}%s
}
Expand Down Expand Up @@ -2169,7 +2169,7 @@ func TestLoadAndActivateBundlesFromDisk(t *testing.T) {

data, err := manager.Store.Read(ctx, txn, storage.Path{})
expData := util.MustUnmarshalJSON([]byte(`{
"foo": {"bar": 1, "baz": "qux"},
"foo": {"bar": 1, "baz": "qux"},
"system": {
"bundles": {"test-bundle": {"etag": "", "manifest": {"revision": "quickbrownfaux", "roots": [""]}}},
"modules": {"test-bundle/foo/bar.rego": {"rego_version": 1}}
Expand Down Expand Up @@ -2255,7 +2255,7 @@ func TestLoadAndActivateBundlesFromDiskReservedChars(t *testing.T) {

data, err := manager.Store.Read(ctx, txn, storage.Path{})
expData := util.MustUnmarshalJSON([]byte(`{
"foo": {"bar": 1, "baz": "qux"},
"foo": {"bar": 1, "baz": "qux"},
"system": {
"bundles": {"test?bundle=opa": {"etag": "", "manifest": {"revision": "quickbrownfaux", "roots": [""]}}},
"modules": {"test/foo/bar.rego": {"rego_version": 1}}
Expand Down Expand Up @@ -2504,7 +2504,7 @@ corge contains 2 if {
}

expData := util.MustUnmarshalJSON([]byte(`{
"foo": {"bar": 1, "baz": "qux"},
"foo": {"bar": 1, "baz": "qux"},
"system": {
"bundles": {"test-bundle": {"etag": "", "manifest": {"revision": "quickbrownfaux", "roots": [""]}}}
}
Expand Down Expand Up @@ -2739,7 +2739,7 @@ corge contains 1 if {
var expData any
if moduleRegoVersion != runtimeRegoVersion {
expData = util.MustUnmarshalJSON([]byte(fmt.Sprintf(`{
"foo": {"bar": 1, "baz": "qux"},
"foo": {"bar": 1, "baz": "qux"},
"system": {
"bundles": {"test-bundle": {"etag": "", "manifest": {"revision": "quickbrownfaux"%s, "roots": [""]}}},
"modules": {"test-bundle/foo/bar.rego": {"rego_version": %d}}
Expand All @@ -2748,7 +2748,7 @@ corge contains 1 if {
manifestRegoVersionStr, moduleRegoVersion)))
} else {
expData = util.MustUnmarshalJSON([]byte(fmt.Sprintf(`{
"foo": {"bar": 1, "baz": "qux"},
"foo": {"bar": 1, "baz": "qux"},
"system": {
"bundles": {"test-bundle": {"etag": "", "manifest": {"revision": "quickbrownfaux"%s, "roots": [""]}}}
}
Expand Down Expand Up @@ -3534,7 +3534,7 @@ p contains x if { x = 1 }`
t.Errorf("Expected to have bundle status for %q included in update, got: %+v", name, s1)
}
// they should be defaults at this point
if !reflect.DeepEqual(s, &Status{Name: name}) {
if !s.Equal(&Status{Name: name}) {
t.Errorf("Expected bundle %q to have an empty status, got: %+v", name, s1)
}
}
Expand Down Expand Up @@ -3567,7 +3567,7 @@ p contains x`
t.Errorf("Expected to have bundle status for %q included in update, got: %+v", name, s2)
}
// they should be still defaults
if !reflect.DeepEqual(s, &Status{Name: name}) {
if !s.Equal(&Status{Name: name}) {
t.Errorf("Expected bundle %q to have an empty status, got: %+v", name, s2)
}
}
Expand Down Expand Up @@ -3599,7 +3599,7 @@ p contains 1`
t.Errorf("Expected to have bundle status for %q included in update, got: %+v", name, s3)
}
// they should still be defaults
if !reflect.DeepEqual(s, &Status{Name: name}) {
if !s.Equal(&Status{Name: name}) {
t.Errorf("Expected bundle %q to have an empty status, got: %+v", name, s3)
}
}
Expand Down Expand Up @@ -3645,7 +3645,7 @@ p contains x if { x = 1 }`
t.Fatal("Unexpected status update, got:", s5)
}

if !reflect.DeepEqual(s5[bundleNames[0]], s4[bundleNames[0]]) {
if !s5[bundleNames[0]].Equal(s4[bundleNames[0]]) {
t.Fatalf("Expected bundle %q to have the same status as before updating bundle %q, got: %+v", bundleNames[0], bundleNames[1], s5)
}

Expand All @@ -3656,7 +3656,7 @@ p contains x if { x = 1 }`
t.Errorf("Expected to have bundle status for %q included in update, got: %+v", name, s5)
}
// they should still be defaults
if !reflect.DeepEqual(s, &Status{Name: name}) {
if !s.Equal(&Status{Name: name}) {
t.Errorf("Expected bundle %q to have an empty status, got: %+v", name, s5)
}
}
Expand Down Expand Up @@ -6858,7 +6858,7 @@ func TestPluginManualTriggerMultipleDiskStorage(t *testing.T) {

data, err := manager.Store.Read(ctx, txn, storage.Path{})
expData := util.MustUnmarshalJSON([]byte(`{
"p": "x1", "q": "x2",
"p": "x1", "q": "x2",
"system": {
"bundles": {"test-1": {"etag": "", "manifest": {"revision": "", "roots": ["p", "authz"]}}, "test-2": {"etag": "", "manifest": {"revision": "", "roots": ["q"]}}},
"modules": {"test-1/bar/policy.rego": {"rego_version": 1}}
Expand Down
38 changes: 38 additions & 0 deletions v1/plugins/bundle/status.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ package bundle
import (
"encoding/json"
"errors"
"reflect"
"strconv"
"time"

Expand Down Expand Up @@ -95,3 +96,40 @@ func (s *Status) SetError(err error) {
s.Errors = nil
}
}

func (s *Status) Equal(other *Status) bool {
if s == nil || other == nil {
return s == nil && other == nil
}

equal := s.Name == other.Name &&
s.Type == other.Type &&
s.Size == other.Size &&
s.Code == other.Code &&
s.Message == other.Message &&
s.HTTPCode == other.HTTPCode &&
s.ActiveRevision == other.ActiveRevision &&
s.LastSuccessfulActivation.Equal(other.LastSuccessfulActivation) &&
s.LastSuccessfulDownload.Equal(other.LastSuccessfulDownload) &&
s.LastSuccessfulRequest.Equal(other.LastSuccessfulRequest) &&
s.LastRequest.Equal(other.LastRequest)

if !equal {
return false
}

if len(s.Errors) != len(other.Errors) {
return false
}
for i := range s.Errors {
if s.Errors[i].Error() != other.Errors[i].Error() {
return false
}
}

if s.Metrics != nil && other.Metrics != nil && s.Metrics.All() != nil && other.Metrics.All() != nil {
return reflect.DeepEqual(s.Metrics.All(), other.Metrics.All())
}

return s.Metrics == nil && other.Metrics == nil
}
5 changes: 5 additions & 0 deletions v1/plugins/logs/status/status.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"errors"
"fmt"
"net/http"
"reflect"
"strconv"

"github.com/open-policy-agent/opa/v1/metrics"
Expand Down Expand Up @@ -49,6 +50,10 @@ func (s *Status) SetError(err error) {
}
}

func (s *Status) Equal(other *Status) bool {
return reflect.DeepEqual(s, other)
}

type HTTPError struct {
StatusCode int
}
Expand Down
8 changes: 8 additions & 0 deletions v1/plugins/plugins.go
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,14 @@ func (s *Status) String() string {
return fmt.Sprintf("{%v %q}", s.State, s.Message)
}

func (s *Status) Equal(other *Status) bool {
if s == nil || other == nil {
return s == nil && other == nil
}

return s.State == other.State && s.Message == other.Message
}

// StatusListener defines a handler to register for status updates.
type StatusListener func(status map[string]*Status)

Expand Down
18 changes: 18 additions & 0 deletions v1/plugins/status/plugin.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"context"
"encoding/json"
"fmt"
"maps"
"net/http"
"reflect"

Expand Down Expand Up @@ -529,3 +530,20 @@ func (p *Plugin) updatePrometheusMetrics(u *UpdateRequestV1) {
}
}
}

func (u UpdateRequestV1) Equal(other UpdateRequestV1) bool {
return maps.Equal(u.Labels, other.Labels) &&
maps.EqualFunc(u.Bundles, other.Bundles, (*bundle.Status).Equal) &&
maps.EqualFunc(u.Plugins, other.Plugins, (*plugins.Status).Equal) &&
u.Bundle.Equal(other.Bundle) &&
u.Discovery.Equal(other.Discovery) &&
u.DecisionLogs.Equal(other.DecisionLogs) &&
nullSafeDeepEqual(u.Metrics, other.Metrics)
}

func nullSafeDeepEqual(a, b interface{}) bool {
if a == nil && b == nil {
return true
}
return a != nil && b != nil && reflect.DeepEqual(a, b)
}
14 changes: 7 additions & 7 deletions v1/plugins/status/plugin_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -334,7 +334,7 @@ func TestPluginStart(t *testing.T) {
},
}

if !reflect.DeepEqual(result, exp) {
if !result.Equal(exp) {
t.Fatalf("Expected: %v but got: %v", exp, result)
}

Expand All @@ -345,7 +345,7 @@ func TestPluginStart(t *testing.T) {

exp.Bundles = map[string]*bundle.Status{"test": status}

if !reflect.DeepEqual(result, exp) {
if !result.Equal(exp) {
t.Fatalf("Expected: %v but got: %v", exp, result)
}
}
Expand Down Expand Up @@ -434,7 +434,7 @@ func TestPluginStartTriggerManual(t *testing.T) {

exp.Bundles = map[string]*bundle.Status{"test": status}

if !reflect.DeepEqual(result.Bundles, exp.Bundles) {
if !maps.EqualFunc(result.Bundles, exp.Bundles, (*bundle.Status).Equal) {
t.Fatalf("Expected: %v but got: %v", exp, result)
}
}
Expand Down Expand Up @@ -480,7 +480,7 @@ func TestPluginStartTriggerManualMultiple(t *testing.T) {
exp.Bundles = map[string]*bundle.Status{"test": status}
exp.Discovery = status

if !reflect.DeepEqual(result, exp) {
if !result.Equal(exp) {
t.Fatalf("Expected: %v but got: %v", exp, result)
}
}
Expand Down Expand Up @@ -632,7 +632,7 @@ func TestPluginStartBulkUpdate(t *testing.T) {

exp.Bundles = map[string]*bundle.Status{status.Name: status}

if !reflect.DeepEqual(result, exp) {
if !result.Equal(exp) {
t.Fatalf("Expected: %v but got: %v", exp, result)
}
}
Expand Down Expand Up @@ -729,7 +729,7 @@ func TestPluginStartDiscovery(t *testing.T) {
},
}

if !reflect.DeepEqual(result, exp) {
if !result.Equal(exp) {
t.Fatalf("Expected: %+v but got: %+v", exp, result)
}
}
Expand Down Expand Up @@ -772,7 +772,7 @@ func TestPluginStartDecisionLogs(t *testing.T) {
},
}

if !reflect.DeepEqual(result, exp) {
if !result.Equal(exp) {
t.Fatalf("Expected: %+v but got: %+v", exp, result)
}
}
Expand Down
4 changes: 2 additions & 2 deletions v1/storage/inmem/inmem_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -220,7 +220,7 @@ func TestInMemoryWrite(t *testing.T) {
t.Errorf("Test case %d (%v): expected patch error, but got nil instead", i+1, tc.note)
continue
}
if !reflect.DeepEqual(err, tc.expected) {
if err.Error() != tc.expected.Error() {
t.Errorf("Test case %d (%v): expected patch error %v but got: %v", i+1, tc.note, tc.expected, err)
continue
}
Expand All @@ -238,7 +238,7 @@ func TestInMemoryWrite(t *testing.T) {
t.Errorf("Test case %d (%v): expected get error but got: %v", i+1, tc.note, result)
continue
}
if !reflect.DeepEqual(err, expected) {
if err.Error() != expected.Error() {
t.Errorf("Test case %d (%v): expected get error %v but got: %v", i+1, tc.note, expected, err)
continue
}
Expand Down

0 comments on commit e682a67

Please sign in to comment.