feat: add errors.Is support to all errors

Add functionality to support errors.Is on all generated errors to keep
in line with best practice on checking whether an error is of the
specified type as per changes to error handling in go1.13.
This commit is contained in:
Gerwin van de Steeg 2024-01-09 17:50:25 +13:00
parent e36638d878
commit d6e5a55f3c
No known key found for this signature in database
GPG key ID: DFD9320BFB453A70
5 changed files with 109 additions and 0 deletions

View file

@ -0,0 +1,17 @@
package encoding
import (
"fmt"
"testing"
"github.com/stretchr/testify/assert"
)
func Test_encodingError(t *testing.T) {
err1 := fmt.Errorf("test error")
err2 := encodingError("encoding error")
assert.NotErrorIs(t, err1, err2)
assert.NotErrorIs(t, err2, err1)
assert.ErrorIs(t, err2, encodingError("encoding error"))
assert.NotErrorIs(t, err2, encodingError("other encodingerror"))
}

View file

@ -37,6 +37,12 @@ func (pe ConfigParseError) Unwrap() error {
return pe.err return pe.err
} }
// Is adds a check to see if the specified target is an error of this type, see [errors.Is]
func (pe ConfigParseError) Is(err error) bool {
_, ok := err.(*ConfigParseError)
return ok
}
// toCaseInsensitiveValue checks if the value is a map; // toCaseInsensitiveValue checks if the value is a map;
// if so, create a copy and lower-case the keys recursively. // if so, create a copy and lower-case the keys recursively.
func toCaseInsensitiveValue(value any) any { func toCaseInsensitiveValue(value any) any {

View file

@ -11,6 +11,7 @@
package viper package viper
import ( import (
"fmt"
"os" "os"
"path/filepath" "path/filepath"
"testing" "testing"
@ -84,3 +85,13 @@ func TestAbsPathify(t *testing.T) {
assert.Equal(t, test.output, got) assert.Equal(t, test.output, got)
} }
} }
func TestConfigParseError(t *testing.T) {
// test a generic error
err1 := fmt.Errorf("test error")
assert.NotErrorIs(t, err1, &ConfigParseError{})
// test the wrapped generic error
err2 := ConfigParseError{err: err1}
assert.ErrorIs(t, err2, &ConfigParseError{})
assert.ErrorIs(t, err2.Unwrap(), err1)
}

View file

@ -61,6 +61,17 @@ func (e ConfigMarshalError) Error() string {
return fmt.Sprintf("While marshaling config: %s", e.err.Error()) return fmt.Sprintf("While marshaling config: %s", e.err.Error())
} }
// Unwrap returns the wrapped error.
func (e ConfigMarshalError) Unwrap() error {
return e.err
}
// Is adds a check to see if the specified target is an error of this type, see [errors.Is]
func (e ConfigMarshalError) Is(err error) bool {
_, ok := err.(*ConfigMarshalError)
return ok
}
var v *Viper var v *Viper
type RemoteResponse struct { type RemoteResponse struct {
@ -118,6 +129,12 @@ func (fnfe ConfigFileNotFoundError) Error() string {
return fmt.Sprintf("Config File %q Not Found in %q", fnfe.name, fnfe.locations) return fmt.Sprintf("Config File %q Not Found in %q", fnfe.name, fnfe.locations)
} }
// Is adds a check to see if the specified target is an error of this type, see [errors.Is]
func (e ConfigFileNotFoundError) Is(err error) bool {
_, ok := err.(*ConfigFileNotFoundError)
return ok
}
// ConfigFileAlreadyExistsError denotes failure to write new configuration file. // ConfigFileAlreadyExistsError denotes failure to write new configuration file.
type ConfigFileAlreadyExistsError string type ConfigFileAlreadyExistsError string

View file

@ -8,6 +8,7 @@ package viper
import ( import (
"bytes" "bytes"
"encoding/json" "encoding/json"
"fmt"
"io" "io"
"os" "os"
"os/exec" "os/exec"
@ -2696,3 +2697,60 @@ func skipWindows(t *testing.T) {
t.Skip("Skip test on Windows") t.Skip("Skip test on Windows")
} }
} }
// Test the ConfigMarshalError
func TestConfigMarshalError(t *testing.T) {
// test a generic error
err1 := fmt.Errorf("test error")
assert.NotErrorIs(t, err1, &ConfigMarshalError{})
// test the wrapped generic error
err2 := ConfigMarshalError{err: err1}
assert.ErrorIs(t, err2, &ConfigMarshalError{})
assert.ErrorIs(t, err2.Unwrap(), err1)
}
func TestUnsupportedConfigError(t *testing.T) {
err1 := fmt.Errorf("test error")
err2 := UnsupportedConfigError("some string")
assert.NotErrorIs(t, err2, err1)
assert.NotErrorIs(t, err1, err2)
assert.ErrorIs(t, err2, UnsupportedConfigError("some string"))
assert.NotErrorIs(t, err2, UnsupportedConfigError("other string"))
}
func TestUnsupportedRemoteProviderError(t *testing.T) {
err1 := fmt.Errorf("test error")
err2 := UnsupportedRemoteProviderError("some string")
assert.NotErrorIs(t, err1, err2)
assert.NotErrorIs(t, err2, err1)
assert.ErrorIs(t, err2, UnsupportedRemoteProviderError("some string"))
assert.NotErrorIs(t, err2, UnsupportedRemoteProviderError("other string"))
}
func TestRemoteConfigError(t *testing.T) {
err1 := fmt.Errorf("test error")
err2 := RemoteConfigError("some string")
assert.NotErrorIs(t, err1, err2)
assert.NotErrorIs(t, err2, err1)
assert.ErrorIs(t, err2, RemoteConfigError("some string"))
assert.NotErrorIs(t, err2, RemoteConfigError("other string"))
}
func TestConfigFileNotFoundError(t *testing.T) {
err1 := fmt.Errorf("test error")
err2 := ConfigFileNotFoundError{name: "name", locations: "locations"}
assert.NotErrorIs(t, err1, err2)
assert.NotErrorIs(t, err2, err1)
assert.ErrorIs(t, err2, &ConfigFileNotFoundError{})
assert.ErrorIs(t, err2, &ConfigFileNotFoundError{name: "name", locations: "locations"})
assert.ErrorIs(t, err2, &ConfigFileNotFoundError{name: "other name", locations: "other locations"})
}
func TestConfigFileAlreadyExistsError(t *testing.T) {
err1 := fmt.Errorf("test error")
err2 := ConfigFileAlreadyExistsError("some string")
assert.NotErrorIs(t, err1, err2)
assert.NotErrorIs(t, err2, err1)
assert.ErrorIs(t, err2, ConfigFileAlreadyExistsError("some string"))
assert.NotErrorIs(t, err2, ConfigFileAlreadyExistsError("other string"))
}