Repository: gavv/httpexpect (go)

Issue:
Switch from net.Error.Temporary to net.Error.Timeout
Currently httpexpect.Request has 4 retry policies:

* DontRetry
* RetryTemporaryNetworkErrors
* RetryTemporaryNetworkAndServerErrors
* RetryAllErrors

RetryTemporaryNetworkErrors and RetryTemporaryNetworkAndServerErrors use net.Error.Temporary to identify temporary network errors (see Request.shouldRetry). This method were deprecated in recent go versions:

```go
// An Error represents a network error.
type Error interface {
	error
	Timeout() bool // Is the error a timeout?

	// Deprecated: Temporary errors are not well-defined.
	// Most "temporary" errors are timeouts, and the few exceptions are surprising.
	// Do not use this method.
	Temporary() bool
}
```

Hence, we should migrate from Temporary to Timeout, but without breaking compatibility.

Steps:

* introduce two new retry policies: RetryTimeoutErrors, RetryTimeoutAndServerErrors
* RetryTimeoutErrors should work like RetryTemporaryNetworkErrors, but it should use net.Error.Timeout
* RetryTimeoutAndServerErrors should work like RetryTemporaryNetworkAndServerErrors, but it should use net.Error.Timeout
* mark RetryTemporaryNetworkErrors and RetryTemporaryNetworkAndServerErrors as deprecated and suggest to use corresponding new policies instead
* update tests to work with new policies; exclude deprecated policies from tests because these tests are huge already

We will remove deprecated policies in v3.

Pull request:
Feature: Switch from net.Error.Temporary to net.Error.Timeout
Closes #270 

Patch:
diff --git a/request.go b/request.go
index 0872a6a..98ccbef 100644
--- a/request.go
+++ b/request.go
@@ -118,7 +118,7 @@ func newRequest(
 		redirectPolicy: defaultRedirectPolicy,
 		maxRedirects:   -1,
 
-		retryPolicy:   RetryTemporaryNetworkAndServerErrors,
+		retryPolicy:   RetryTimeoutAndServerErrors,
 		maxRetries:    0,
 		minRetryDelay: time.Millisecond * 50,
 		maxRetryDelay: time.Second * 5,
@@ -578,15 +578,21 @@ const (
 	// DontRetry disables retrying at all.
 	DontRetry RetryPolicy = iota
 
-	// RetryTemporaryNetworkErrors enables retrying only temporary network errors.
-	// Retry happens if Client returns net.Error and its Temporary() method
-	// returns true.
+	// Deprecated: use RetryTimeoutErrors instead.
 	RetryTemporaryNetworkErrors
 
-	// RetryTemporaryNetworkAndServerErrors enables retrying of temporary network
-	// errors, as well as 5xx status codes.
+	// Deprecated: use RetryTimeoutAndServerErrors instead.
 	RetryTemporaryNetworkAndServerErrors
 
+	// RetryTimeoutErrors enables retrying of timeout errors.
+	// Retry happens if Client returns net.Error and its Timeout() method
+	// returns true.
+	RetryTimeoutErrors
+
+	// RetryTimeoutAndServerErrors enables retrying of network timeout errors,
+	// as well as 5xx status codes.
+	RetryTimeoutAndServerErrors
+
 	// RetryAllErrors enables retrying of any error or 4xx/5xx status code.
 	RetryAllErrors
 )
@@ -599,7 +605,7 @@ const (
 // How much retry attempts happens is defined by WithMaxRetries().
 // How much to wait between attempts is defined by WithRetryDelay().
 //
-// Default retry policy is RetryTemporaryNetworkAndServerErrors, but
+// Default retry policy is RetryTimeoutAndServerErrors, but
 // default maximum number of retries is zero, so no retries happen
 // unless WithMaxRetries() is called.
 //
@@ -2128,18 +2134,20 @@ func (r *Request) retryRequest(reqFunc func() (*http.Response, error)) (
 
 func (r *Request) shouldRetry(resp *http.Response, err error) bool {
 	var (
-		isTemporaryNetworkError bool
-		isTemporaryServerError  bool
+		isTemporaryNetworkError bool // Deprecated
+		isTimeoutError          bool
+		isServerError           bool
 		isHTTPError             bool
 	)
 
 	if netErr, ok := err.(net.Error); ok {
 		//nolint
 		isTemporaryNetworkError = netErr.Temporary()
+		isTimeoutError = netErr.Timeout()
 	}
 
 	if resp != nil {
-		isTemporaryServerError = resp.StatusCode >= 500 && resp.StatusCode <= 599
+		isServerError = resp.StatusCode >= 500 && resp.StatusCode <= 599
 		isHTTPError = resp.StatusCode >= 400 && resp.StatusCode <= 599
 	}
 
@@ -2151,7 +2159,13 @@ func (r *Request) shouldRetry(resp *http.Response, err error) bool {
 		return isTemporaryNetworkError
 
 	case RetryTemporaryNetworkAndServerErrors:
-		return isTemporaryNetworkError || isTemporaryServerError
+		return isTemporaryNetworkError || isServerError
+
+	case RetryTimeoutErrors:
+		return isTimeoutError
+
+	case RetryTimeoutAndServerErrors:
+		return isTimeoutError || isServerError
 
 	case RetryAllErrors:
 		return err != nil || isHTTPError
diff --git a/string.go b/string.go
index 1603f83..3e8e151 100644
--- a/string.go
+++ b/string.go
@@ -408,6 +408,106 @@ func (s *String) NotInList(values ...string) *String {
 	return s
 }
 
+// InListFold succeeds if the string is equal to one of the values from given
+// list of strings after applying Unicode case-folding (so it's a case-insensitive match).
+//
+// Example:
+//
+//	str := NewString(t, "Hello")
+//	str.InListFold("hEllo", "Goodbye")
+func (s *String) InListFold(values ...string) *String {
+	opChain := s.chain.enter("InListFold()")
+	defer opChain.leave()
+
+	if opChain.failed() {
+		return s
+	}
+
+	if len(values) == 0 {
+		opChain.fail(AssertionFailure{
+			Type: AssertUsage,
+			Errors: []error{
+				errors.New("unexpected empty list argument"),
+			},
+		})
+		return s
+	}
+
+	var isListed bool
+	for _, v := range values {
+		if strings.EqualFold(s.value, v) {
+			isListed = true
+			break
+		}
+	}
+
+	if !isListed {
+		valueList := make([]interface{}, 0, len(values))
+		for _, v := range values {
+			valueList = append(valueList, v)
+		}
+
+		opChain.fail(AssertionFailure{
+			Type:     AssertBelongs,
+			Actual:   &AssertionValue{s.value},
+			Expected: &AssertionValue{AssertionList(valueList)},
+			Errors: []error{
+				errors.New("expected: string is equal to one of the values (if folded)"),
+			},
+		})
+	}
+
+	return s
+}
+
+// NotInListFold succeeds if the string is not equal to any of the values from given
+// list of strings after applying Unicode case-folding (so it's a case-insensitive match).
+//
+// Example:
+//
+//	str := NewString(t, "Hello")
+//	str.NotInListFold("Bye", "Goodbye")
+func (s *String) NotInListFold(values ...string) *String {
+	opChain := s.chain.enter("NotInListFold()")
+	defer opChain.leave()
+
+	if opChain.failed() {
+		return s
+	}
+
+	if len(values) == 0 {
+		opChain.fail(AssertionFailure{
+			Type: AssertUsage,
+			Errors: []error{
+				errors.New("unexpected empty list argument"),
+			},
+		})
+		return s
+	}
+
+	for _, v := range values {
+		if strings.EqualFold(s.value, v) {
+			valueList := make([]interface{}, 0, len(values))
+			for _, v := range values {
+				valueList = append(valueList, v)
+			}
+
+			opChain.fail(AssertionFailure{
+				Type:     AssertNotBelongs,
+				Actual:   &AssertionValue{s.value},
+				Expected: &AssertionValue{AssertionList(valueList)},
+				Errors: []error{
+					errors.New("expected: string is not equal to any of the values (if folded)"),
+				},
+			})
+
+			return s
+		}
+	}
+
+	return s
+}
+
 // Contains succeeds if string contains given Go string as a substring.
 //
 // Example:


Test patch:
diff --git a/array_test.go b/array_test.go
index b0376e4..66f17c3 100644
--- a/array_test.go
+++ b/array_test.go
@@ -200,19 +200,19 @@ func TestArray_Decode(t *testing.T) {
 func TestArray_Alias(t *testing.T) {
 	reporter := newMockReporter(t)
 
-	value1 := NewArray(reporter, []interface{}{1, 2})
-	assert.Equal(t, []string{"Array()"}, value1.chain.context.Path)
-	assert.Equal(t, []string{"Array()"}, value1.chain.context.AliasedPath)
+	value := NewArray(reporter, []interface{}{1, 2})
+	assert.Equal(t, []string{"Array()"}, value.chain.context.Path)
+	assert.Equal(t, []string{"Array()"}, value.chain.context.AliasedPath)
 
-	value2 := value1.Alias("foo")
-	assert.Equal(t, []string{"Array()"}, value2.chain.context.Path)
-	assert.Equal(t, []string{"foo"}, value2.chain.context.AliasedPath)
+	value.Alias("foo")
+	assert.Equal(t, []string{"Array()"}, value.chain.context.Path)
+	assert.Equal(t, []string{"foo"}, value.chain.context.AliasedPath)
 
-	value3 := value2.Filter(func(index int, value *Value) bool {
+	childValue := value.Filter(func(index int, value *Value) bool {
 		return value.Number().Raw() > 1
 	})
-	assert.Equal(t, []string{"Array()", "Filter()"}, value3.chain.context.Path)
-	assert.Equal(t, []string{"foo", "Filter()"}, value3.chain.context.AliasedPath)
+	assert.Equal(t, []string{"Array()", "Filter()"}, childValue.chain.context.Path)
+	assert.Equal(t, []string{"foo", "Filter()"}, childValue.chain.context.AliasedPath)
 }
 
 func TestArray_Getters(t *testing.T) {
diff --git a/boolean_test.go b/boolean_test.go
index 90e383e..c95b6bc 100644
--- a/boolean_test.go
+++ b/boolean_test.go
@@ -102,13 +102,13 @@ func TestBoolean_Decode(t *testing.T) {
 func TestBoolean_Alias(t *testing.T) {
 	reporter := newMockReporter(t)
 
-	value1 := NewBoolean(reporter, true)
-	assert.Equal(t, []string{"Boolean()"}, value1.chain.context.Path)
-	assert.Equal(t, []string{"Boolean()"}, value1.chain.context.AliasedPath)
+	value := NewBoolean(reporter, true)
+	assert.Equal(t, []string{"Boolean()"}, value.chain.context.Path)
+	assert.Equal(t, []string{"Boolean()"}, value.chain.context.AliasedPath)
 
-	value2 := value1.Alias("foo")
-	assert.Equal(t, []string{"Boolean()"}, value2.chain.context.Path)
-	assert.Equal(t, []string{"foo"}, value2.chain.context.AliasedPath)
+	value.Alias("foo")
+	assert.Equal(t, []string{"Boolean()"}, value.chain.context.Path)
+	assert.Equal(t, []string{"foo"}, value.chain.context.AliasedPath)
 }
 
 func TestBoolean_Getters(t *testing.T) {
diff --git a/cookie_test.go b/cookie_test.go
index 9e20d10..739da75 100644
--- a/cookie_test.go
+++ b/cookie_test.go
@@ -104,20 +104,19 @@ func TestCookie_Constructors(t *testing.T) {
 func TestCookie_Alias(t *testing.T) {
 	reporter := newMockReporter(t)
 
-	value1 := NewCookie(reporter, &http.Cookie{
+	value := NewCookie(reporter, &http.Cookie{
 		MaxAge: 0,
 	})
-	assert.Equal(t, []string{"Cookie()"}, value1.chain.context.Path)
-	assert.Equal(t, []string{"Cookie()"}, value1.chain.context.AliasedPath)
+	assert.Equal(t, []string{"Cookie()"}, value.chain.context.Path)
+	assert.Equal(t, []string{"Cookie()"}, value.chain.context.AliasedPath)
 
-	value2 := value1.Alias("foo")
-	assert.Equal(t, []string{"Cookie()"}, value2.chain.context.Path)
-	assert.Equal(t, []string{"foo"}, value2.chain.context.AliasedPath)
+	value.Alias("foo")
+	assert.Equal(t, []string{"Cookie()"}, value.chain.context.Path)
+	assert.Equal(t, []string{"foo"}, value.chain.context.AliasedPath)
 
-	value3 := value2.Domain()
-	assert.Equal(t, []string{"Cookie()", "Domain()"},
-		value3.chain.context.Path)
-	assert.Equal(t, []string{"foo", "Domain()"}, value3.chain.context.AliasedPath)
+	childValue := value.Domain()
+	assert.Equal(t, []string{"Cookie()", "Domain()"}, childValue.chain.context.Path)
+	assert.Equal(t, []string{"foo", "Domain()"}, childValue.chain.context.AliasedPath)
 }
 
 func TestCookie_Getters(t *testing.T) {
diff --git a/datetime_test.go b/datetime_test.go
index e0dac50..2a856c3 100644
--- a/datetime_test.go
+++ b/datetime_test.go
@@ -73,13 +73,13 @@ func TestDateTime_Constructors(t *testing.T) {
 func TestDateTime_Alias(t *testing.T) {
 	reporter := newMockReporter(t)
 
-	value1 := NewDateTime(reporter, time.Unix(0, 1234))
-	assert.Equal(t, []string{"DateTime()"}, value1.chain.context.Path)
-	assert.Equal(t, []string{"DateTime()"}, value1.chain.context.AliasedPath)
+	value := NewDateTime(reporter, time.Unix(0, 1234))
+	assert.Equal(t, []string{"DateTime()"}, value.chain.context.Path)
+	assert.Equal(t, []string{"DateTime()"}, value.chain.context.AliasedPath)
 
-	value2 := value1.Alias("foo")
-	assert.Equal(t, []string{"DateTime()"}, value2.chain.context.Path)
-	assert.Equal(t, []string{"foo"}, value2.chain.context.AliasedPath)
+	value.Alias("foo")
+	assert.Equal(t, []string{"DateTime()"}, value.chain.context.Path)
+	assert.Equal(t, []string{"foo"}, value.chain.context.AliasedPath)
 }
 
 func TestDateTime_Getters(t *testing.T) {
diff --git a/duration_test.go b/duration_test.go
index fc0d88c..3e42461 100644
--- a/duration_test.go
+++ b/duration_test.go
@@ -59,13 +59,13 @@ func TestDuration_Constructors(t *testing.T) {
 func TestDuration_Alias(t *testing.T) {
 	reporter := newMockReporter(t)
 
-	value1 := NewDuration(reporter, time.Second)
-	assert.Equal(t, []string{"Duration()"}, value1.chain.context.Path)
-	assert.Equal(t, []string{"Duration()"}, value1.chain.context.AliasedPath)
+	value := NewDuration(reporter, time.Second)
+	assert.Equal(t, []string{"Duration()"}, value.chain.context.Path)
+	assert.Equal(t, []string{"Duration()"}, value.chain.context.AliasedPath)
 
-	value2 := value1.Alias("foo")
-	assert.Equal(t, []string{"Duration()"}, value2.chain.context.Path)
-	assert.Equal(t, []string{"foo"}, value2.chain.context.AliasedPath)
+	value.Alias("foo")
+	assert.Equal(t, []string{"Duration()"}, value.chain.context.Path)
+	assert.Equal(t, []string{"foo"}, value.chain.context.AliasedPath)
 }
 
 func TestDuration_Set(t *testing.T) {
diff --git a/match_test.go b/match_test.go
index 68c7857..d066991 100644
--- a/match_test.go
+++ b/match_test.go
@@ -59,17 +59,17 @@ func TestMatch_Alias(t *testing.T) {
 	matches := []string{"m0", "m1", "m2"}
 	names := []string{"", "n1", "n2"}
 
-	value1 := NewMatch(reporter, matches, names)
-	assert.Equal(t, []string{"Match()"}, value1.chain.context.Path)
-	assert.Equal(t, []string{"Match()"}, value1.chain.context.AliasedPath)
+	value := NewMatch(reporter, matches, names)
+	assert.Equal(t, []string{"Match()"}, value.chain.context.Path)
+	assert.Equal(t, []string{"Match()"}, value.chain.context.AliasedPath)
 
-	value2 := value1.Alias("foo")
-	assert.Equal(t, []string{"Match()"}, value2.chain.context.Path)
-	assert.Equal(t, []string{"foo"}, value2.chain.context.AliasedPath)
+	value.Alias("foo")
+	assert.Equal(t, []string{"Match()"}, value.chain.context.Path)
+	assert.Equal(t, []string{"foo"}, value.chain.context.AliasedPath)
 
-	value3 := value2.Index(0)
-	assert.Equal(t, []string{"Match()", "Index(0)"}, value3.chain.context.Path)
-	assert.Equal(t, []string{"foo", "Index(0)"}, value3.chain.context.AliasedPath)
+	childValue := value.Index(0)
+	assert.Equal(t, []string{"Match()", "Index(0)"}, childValue.chain.context.Path)
+	assert.Equal(t, []string{"foo", "Index(0)"}, childValue.chain.context.AliasedPath)
 }
 
 func TestMatch_Getters(t *testing.T) {
diff --git a/number_test.go b/number_test.go
index 34687f5..8018b92 100644
--- a/number_test.go
+++ b/number_test.go
@@ -121,13 +121,13 @@ func TestNumber_Decode(t *testing.T) {
 func TestNumber_Alias(t *testing.T) {
 	reporter := newMockReporter(t)
 
-	value1 := NewNumber(reporter, 123)
-	assert.Equal(t, []string{"Number()"}, value1.chain.context.Path)
-	assert.Equal(t, []string{"Number()"}, value1.chain.context.AliasedPath)
+	value := NewNumber(reporter, 123)
+	assert.Equal(t, []string{"Number()"}, value.chain.context.Path)
+	assert.Equal(t, []string{"Number()"}, value.chain.context.AliasedPath)
 
-	value2 := value1.Alias("foo")
-	assert.Equal(t, []string{"Number()"}, value2.chain.context.Path)
-	assert.Equal(t, []string{"foo"}, value2.chain.context.AliasedPath)
+	value.Alias("foo")
+	assert.Equal(t, []string{"Number()"}, value.chain.context.Path)
+	assert.Equal(t, []string{"foo"}, value.chain.context.AliasedPath)
 }
 
 func TestNumber_Getters(t *testing.T) {
diff --git a/object_test.go b/object_test.go
index c186400..662912b 100644
--- a/object_test.go
+++ b/object_test.go
@@ -235,20 +235,19 @@ func TestObject_Decode(t *testing.T) {
 func TestObject_Alias(t *testing.T) {
 	reporter := newMockReporter(t)
 
-	value1 := NewObject(reporter, map[string]interface{}{
+	value := NewObject(reporter, map[string]interface{}{
 		"foo": 100.0,
 	})
-	assert.Equal(t, []string{"Object()"}, value1.chain.context.Path)
-	assert.Equal(t, []string{"Object()"}, value1.chain.context.AliasedPath)
+	assert.Equal(t, []string{"Object()"}, value.chain.context.Path)
+	assert.Equal(t, []string{"Object()"}, value.chain.context.AliasedPath)
 
-	value2 := value1.Alias("bar")
-	assert.Equal(t, []string{"Object()"}, value2.chain.context.Path)
-	assert.Equal(t, []string{"bar"}, value2.chain.context.AliasedPath)
+	value.Alias("bar")
+	assert.Equal(t, []string{"Object()"}, value.chain.context.Path)
+	assert.Equal(t, []string{"bar"}, value.chain.context.AliasedPath)
 
-	value3 := value2.Values()
-	assert.Equal(t, []string{"Object()", "Values()"},
-		value3.chain.context.Path)
-	assert.Equal(t, []string{"bar", "Values()"}, value3.chain.context.AliasedPath)
+	childValue := value.Values()
+	assert.Equal(t, []string{"Object()", "Values()"}, childValue.chain.context.Path)
+	assert.Equal(t, []string{"bar", "Values()"}, childValue.chain.context.AliasedPath)
 }
 
 func TestObject_Getters(t *testing.T) {
diff --git a/request_test.go b/request_test.go
index 1a214e1..d940a02 100644
--- a/request_test.go
+++ b/request_test.go
@@ -106,13 +106,13 @@ func TestRequest_Alias(t *testing.T) {
 		Reporter:       reporter,
 	}
 
-	value1 := NewRequestC(config, "GET", "")
-	assert.Equal(t, []string{`Request("GET")`}, value1.chain.context.Path)
-	assert.Equal(t, []string{`Request("GET")`}, value1.chain.context.AliasedPath)
+	value := NewRequestC(config, "GET", "")
+	assert.Equal(t, []string{`Request("GET")`}, value.chain.context.Path)
+	assert.Equal(t, []string{`Request("GET")`}, value.chain.context.AliasedPath)
 
-	value2 := value1.Alias("foo")
-	assert.Equal(t, []string{`Request("GET")`}, value2.chain.context.Path)
-	assert.Equal(t, []string{"foo"}, value2.chain.context.AliasedPath)
+	value.Alias("foo")
+	assert.Equal(t, []string{`Request("GET")`}, value.chain.context.Path)
+	assert.Equal(t, []string{"foo"}, value.chain.context.AliasedPath)
 }
 
 func TestRequest_Empty(t *testing.T) {
@@ -2586,16 +2586,16 @@ func TestRequest_Retries(t *testing.T) {
 		}
 	}
 
-	newTempNetErrClient := func(cb func(req *http.Request)) *mockClient {
+	newTimeoutErrClient := func(cb func(req *http.Request)) *mockClient {
 		return &mockClient{
 			err: &mockNetError{
-				isTemporary: true,
+				isTimeout: true,
 			},
 			cb: cb,
 		}
 	}
 
-	newTempServerErrClient := func(cb func(req *http.Request)) *mockClient {
+	newServerErrClient := func(cb func(req *http.Request)) *mockClient {
 		return &mockClient{
 			resp: http.Response{
 				StatusCode: http.StatusInternalServerError,
@@ -2647,10 +2647,10 @@ func TestRequest_Retries(t *testing.T) {
 			assert.Equal(t, 1, callCount)
 		})
 
-		t.Run("temporary network error", func(t *testing.T) {
+		t.Run("timeout error", func(t *testing.T) {
 			callCount := 0
 
-			client := newTempNetErrClient(func(req *http.Request) {
+			client := newTimeoutErrClient(func(req *http.Request) {
 				callCount++
 
 				b, err := ioutil.ReadAll(req.Body)
@@ -2677,10 +2677,10 @@ func TestRequest_Retries(t *testing.T) {
 			assert.Equal(t, 1, callCount)
 		})
 
-		t.Run("temporary server error", func(t *testing.T) {
+		t.Run("server error", func(t *testing.T) {
 			callCount := 0
 
-			client := newTempServerErrClient(func(req *http.Request) {
+			client := newServerErrClient(func(req *http.Request) {
 				callCount++
 
 				b, err := ioutil.ReadAll(req.Body)
@@ -2741,7 +2741,7 @@ func TestRequest_Retries(t *testing.T) {
 
 	})
 
-	t.Run("retry temporary network errors policy", func(t *testing.T) {
+	t.Run("retry timeout errors policy", func(t *testing.T) {
 		t.Run("no error", func(t *testing.T) {
 			callCount := 0
 
@@ -2760,7 +2760,7 @@ func TestRequest_Retries(t *testing.T) {
 
 			req := NewRequestC(config, http.MethodPost, "/url").
 				WithText("test body").
-				WithRetryPolicy(RetryTemporaryNetworkErrors)
+				WithRetryPolicy(RetryTimeoutErrors)
 			req.sleepFn = noopSleepFn
 			req.chain.assertNotFailed(t)
 
@@ -2771,10 +2771,10 @@ func TestRequest_Retries(t *testing.T) {
 			assert.Equal(t, 1, callCount)
 		})
 
-		t.Run("temporary network error", func(t *testing.T) {
+		t.Run("timeout error", func(t *testing.T) {
 			callCount := 0
 
-			client := newTempNetErrClient(func(req *http.Request) {
+			client := newTimeoutErrClient(func(req *http.Request) {
 				callCount++
 
 				b, err := ioutil.ReadAll(req.Body)
@@ -2789,7 +2789,7 @@ func TestRequest_Retries(t *testing.T) {
 
 			req := NewRequestC(config, http.MethodPost, "/url").
 				WithText("test body").
-				WithRetryPolicy(RetryTemporaryNetworkErrors).
+				WithRetryPolicy(RetryTimeoutErrors).
 				WithMaxRetries(1).
 				WithRetryDelay(0, 0)
 			req.sleepFn = noopSleepFn
@@ -2802,10 +2802,10 @@ func TestRequest_Retries(t *testing.T) {
 			assert.Equal(t, 2, callCount)
 		})
 
-		t.Run("temporary server error", func(t *testing.T) {
+		t.Run("server error", func(t *testing.T) {
 			callCount := 0
 
-			client := newTempServerErrClient(func(req *http.Request) {
+			client := newServerErrClient(func(req *http.Request) {
 				callCount++
 
 				b, err := ioutil.ReadAll(req.Body)
@@ -2820,7 +2820,7 @@ func TestRequest_Retries(t *testing.T) {
 
 			req := NewRequestC(config, http.MethodPost, "/url").
 				WithText("test body").
-				WithRetryPolicy(RetryTemporaryNetworkErrors).
+				WithRetryPolicy(RetryTimeoutErrors).
 				WithMaxRetries(1)
 			req.sleepFn = noopSleepFn
 			req.chain.assertNotFailed(t)
@@ -2851,7 +2851,7 @@ func TestRequest_Retries(t *testing.T) {
 
 			req := NewRequestC(config, http.MethodPost, "/url").
 				WithText("test body").
-				WithRetryPolicy(RetryTemporaryNetworkErrors).
+				WithRetryPolicy(RetryTimeoutErrors).
 				WithMaxRetries(1)
 			req.sleepFn = noopSleepFn
 			req.chain.assertNotFailed(t)
@@ -2865,7 +2865,7 @@ func TestRequest_Retries(t *testing.T) {
 		})
 	})
 
-	t.Run("retry temporary network and server errors policy", func(t *testing.T) {
+	t.Run("retry timeout and server errors policy", func(t *testing.T) {
 		t.Run("no error", func(t *testing.T) {
 			callCount := 0
 
@@ -2884,7 +2884,7 @@ func TestRequest_Retries(t *testing.T) {
 
 			req := NewRequestC(config, http.MethodPost, "/url").
 				WithText("test body").
-				WithRetryPolicy(RetryTemporaryNetworkAndServerErrors)
+				WithRetryPolicy(RetryTimeoutAndServerErrors)
 			req.sleepFn = noopSleepFn
 			req.chain.assertNotFailed(t)
 
@@ -2895,10 +2895,10 @@ func TestRequest_Retries(t *testing.T) {
 			assert.Equal(t, 1, callCount)
 		})
 
-		t.Run("temporary network error", func(t *testing.T) {
+		t.Run("timeout error", func(t *testing.T) {
 			callCount := 0
 
-			client := newTempNetErrClient(func(req *http.Request) {
+			client := newTimeoutErrClient(func(req *http.Request) {
 				callCount++
 
 				b, err := ioutil.ReadAll(req.Body)
@@ -2913,7 +2913,7 @@ func TestRequest_Retries(t *testing.T) {
 
 			req := NewRequestC(config, http.MethodPost, "/url").
 				WithText("test body").
-				WithRetryPolicy(RetryTemporaryNetworkAndServerErrors).
+				WithRetryPolicy(RetryTimeoutAndServerErrors).
 				WithMaxRetries(1).
 				WithRetryDelay(0, 0)
 			req.sleepFn = noopSleepFn
@@ -2926,10 +2926,10 @@ func TestRequest_Retries(t *testing.T) {
 			assert.Equal(t, 2, callCount)
 		})
 
-		t.Run("temporary server error", func(t *testing.T) {
+		t.Run("server error", func(t *testing.T) {
 			callCount := 0
 
-			client := newTempServerErrClient(func(req *http.Request) {
+			client := newServerErrClient(func(req *http.Request) {
 				callCount++
 
 				b, err := ioutil.ReadAll(req.Body)
@@ -2944,7 +2944,7 @@ func TestRequest_Retries(t *testing.T) {
 
 			req := NewRequestC(config, http.MethodPost, "/url").
 				WithText("test body").
-				WithRetryPolicy(RetryTemporaryNetworkAndServerErrors).
+				WithRetryPolicy(RetryTimeoutAndServerErrors).
 				WithMaxRetries(1).
 				WithRetryDelay(0, 0)
 			req.sleepFn = noopSleepFn
@@ -2976,7 +2976,7 @@ func TestRequest_Retries(t *testing.T) {
 
 			req := NewRequestC(config, http.MethodPost, "/url").
 				WithText("test body").
-				WithRetryPolicy(RetryTemporaryNetworkAndServerErrors).
+				WithRetryPolicy(RetryTimeoutAndServerErrors).
 				WithMaxRetries(1)
 			req.sleepFn = noopSleepFn
 			req.chain.assertNotFailed(t)
@@ -3020,10 +3020,10 @@ func TestRequest_Retries(t *testing.T) {
 			assert.Equal(t, 1, callCount)
 		})
 
-		t.Run("temporary network error", func(t *testing.T) {
+		t.Run("timeout error", func(t *testing.T) {
 			callCount := 0
 
-			client := newTempNetErrClient(func(req *http.Request) {
+			client := newTimeoutErrClient(func(req *http.Request) {
 				callCount++
 
 				b, err := ioutil.ReadAll(req.Body)
@@ -3051,10 +3051,10 @@ func TestRequest_Retries(t *testing.T) {
 			assert.Equal(t, 2, callCount)
 		})
 
-		t.Run("temporary server error", func(t *testing.T) {
+		t.Run("server error", func(t *testing.T) {
 			callCount := 0
 
-			client := newTempServerErrClient(func(req *http.Request) {
+			client := newServerErrClient(func(req *http.Request) {
 				callCount++
 
 				b, err := ioutil.ReadAll(req.Body)
diff --git a/response_test.go b/response_test.go
index 25568cb..c19c4d1 100644
--- a/response_test.go
+++ b/response_test.go
@@ -119,14 +119,13 @@ func TestResponse_Constructors(t *testing.T) {
 func TestResponse_Alias(t *testing.T) {
 	reporter := newMockReporter(t)
 
-	duration := time.Second
-	value1 := NewResponse(reporter, &http.Response{}, duration)
-	assert.Equal(t, []string{"Response()"}, value1.chain.context.Path)
-	assert.Equal(t, []string{"Response()"}, value1.chain.context.AliasedPath)
-
-	value2 := value1.Alias("foo")
-	assert.Equal(t, []string{"Response()"}, value2.chain.context.Path)
-	assert.Equal(t, []string{"foo"}, value2.chain.context.AliasedPath)
+	value := NewResponse(reporter, &http.Response{}, time.Second)
+	assert.Equal(t, []string{"Response()"}, value.chain.context.Path)
+	assert.Equal(t, []string{"Response()"}, value.chain.context.AliasedPath)
+
+	value.Alias("foo")
+	assert.Equal(t, []string{"Response()"}, value.chain.context.Path)
+	assert.Equal(t, []string{"foo"}, value.chain.context.AliasedPath)
 }
 
 func TestResponse_RoundTripTime(t *testing.T) {
diff --git a/string_test.go b/string_test.go
index fdcddf7..c839ae6 100644
--- a/string_test.go
+++ b/string_test.go
@@ -32,6 +32,8 @@ func TestString_FailedChain(t *testing.T) {
 	value.NotEqualFold("")
 	value.InList("")
 	value.NotInList("")
+	value.InListFold("")
+	value.NotInListFold("")
 	value.Contains("")
 	value.NotContains("")
 	value.ContainsFold("")
@@ -130,18 +132,18 @@ func TestString_Decode(t *testing.T) {
 
 func TestString_Alias(t *testing.T) {
 	reporter := newMockReporter(t)
-	value1 := NewString(reporter, "123")
-	assert.Equal(t, []string{"String()"}, value1.chain.context.Path)
-	assert.Equal(t, []string{"String()"}, value1.chain.context.AliasedPath)
-
-	value2 := value1.Alias("foo")
-	assert.Equal(t, []string{"String()"}, value2.chain.context.Path)
-	assert.Equal(t, []string{"foo"}, value2.chain.context.AliasedPath)
-
-	value3 := value2.AsNumber(10)
-	assert.Equal(t, []string{"String()", "AsNumber()"},
-		value3.chain.context.Path)
-	assert.Equal(t, []string{"foo", "AsNumber()"}, value3.chain.context.AliasedPath)
+
+	value := NewString(reporter, "123")
+	assert.Equal(t, []string{"String()"}, value.chain.context.Path)
+	assert.Equal(t, []string{"String()"}, value.chain.context.AliasedPath)
+
+	value.Alias("foo")
+	assert.Equal(t, []string{"String()"}, value.chain.context.Path)
+	assert.Equal(t, []string{"foo"}, value.chain.context.AliasedPath)
+
+	childValue := value.AsNumber()
+	assert.Equal(t, []string{"String()", "AsNumber()"}, childValue.chain.context.Path)
+	assert.Equal(t, []string{"foo", "AsNumber()"}, childValue.chain.context.AliasedPath)
 }
 
 func TestString_Getters(t *testing.T) {
@@ -287,6 +289,46 @@ func TestString_InList(t *testing.T) {
 	value.chain.clearFailed()
 }
 
+func TestString_InListFold(t *testing.T) {
+	reporter := newMockReporter(t)
+
+	value := NewString(reporter, "Foo")
+
+	assert.Equal(t, "Foo", value.Raw())
+
+	value.InListFold()
+	value.chain.assertFailed(t)
+	value.chain.clearFailed()
+
+	value.NotInListFold()
+	value.chain.assertFailed(t)
+	value.chain.clearFailed()
+
+	value.InListFold("foo", "bar")
+	value.chain.assertNotFailed(t)
+	value.chain.clearFailed()
+
+	value.InListFold("FOO", "BAR")
+	value.chain.assertNotFailed(t)
+	value.chain.clearFailed()
+
+	value.InListFold("BAR", "BAZ")
+	value.chain.assertFailed(t)
+	value.chain.clearFailed()
+
+	value.NotInListFold("foo", "bar")
+	value.chain.assertFailed(t)
+	value.chain.clearFailed()
+
+	value.NotInListFold("FOO", "BAR")
+	value.chain.assertFailed(t)
+	value.chain.clearFailed()
+
+	value.NotInListFold("BAR", "BAZ")
+	value.chain.assertNotFailed(t)
+	value.chain.clearFailed()
+}
+
 func TestString_Contains(t *testing.T) {
 	reporter := newMockReporter(t)
 
@@ -618,23 +660,25 @@ func TestString_AsBoolean(t *testing.T) {
 func TestString_AsDateTime(t *testing.T) {
 	reporter := newMockReporter(t)
 
-	value1 := NewString(reporter, "Tue, 15 Nov 1994 08:12:31 GMT")
-	dt1 := value1.AsDateTime()
-	value1.chain.assertNotFailed(t)
-	dt1.chain.assertNotFailed(t)
-	assert.True(t, time.Date(1994, 11, 15, 8, 12, 31, 0, time.UTC).Equal(dt1.Raw()))
+	t.Run("basic", func(t *testing.T) {
+		value1 := NewString(reporter, "Tue, 15 Nov 1994 08:12:31 GMT")
+		dt1 := value1.AsDateTime()
+		value1.chain.assertNotFailed(t)
+		dt1.chain.assertNotFailed(t)
+		assert.True(t, time.Date(1994, 11, 15, 8, 12, 31, 0, time.UTC).Equal(dt1.Raw()))
 
-	value2 := NewString(reporter, "15 Nov 94 08:12 GMT")
-	dt2 := value2.AsDateTime(time.RFC822)
-	value2.chain.assertNotFailed(t)
-	dt2.chain.assertNotFailed(t)
-	assert.True(t, time.Date(1994, 11, 15, 8, 12, 0, 0, time.UTC).Equal(dt2.Raw()))
+		value2 := NewString(reporter, "15 Nov 94 08:12 GMT")
+		dt2 := value2.AsDateTime(time.RFC822)
+		value2.chain.assertNotFailed(t)
+		dt2.chain.assertNotFailed(t)
+		assert.True(t, time.Date(1994, 11, 15, 8, 12, 0, 0, time.UTC).Equal(dt2.Raw()))
 
-	value3 := NewString(reporter, "bad")
-	dt3 := value3.AsDateTime()
-	value3.chain.assertFailed(t)
-	dt3.chain.assertFailed(t)
-	assert.True(t, time.Unix(0, 0).Equal(dt3.Raw()))
+		value3 := NewString(reporter, "bad")
+		dt3 := value3.AsDateTime()
+		value3.chain.assertFailed(t)
+		dt3.chain.assertFailed(t)
+		assert.True(t, time.Unix(0, 0).Equal(dt3.Raw()))
+	})
 
 	formats := []string{
 		http.TimeFormat,
@@ -651,25 +695,27 @@ func TestString_AsDateTime(t *testing.T) {
 	}
 
 	for n, f := range formats {
-		str := time.Now().Format(f)
-
-		value1 := NewString(reporter, str)
-		dt1 := value1.AsDateTime()
-		dt1.chain.assertNotFailed(t)
-
-		value2 := NewString(reporter, str)
-		dt2 := value2.AsDateTime(formats...)
-		dt2.chain.assertNotFailed(t)
-
-		value3 := NewString(reporter, str)
-		dt3 := value3.AsDateTime(f)
-		dt3.chain.assertNotFailed(t)
-
-		if n != 0 {
-			value4 := NewString(reporter, str)
-			dt4 := value4.AsDateTime(formats[0])
-			dt4.chain.assertFailed(t)
-		}
+		t.Run(f, func(t *testing.T) {
+			str := time.Now().Format(f)
+
+			value1 := NewString(reporter, str)
+			dt1 := value1.AsDateTime()
+			dt1.chain.assertNotFailed(t)
+
+			value2 := NewString(reporter, str)
+			dt2 := value2.AsDateTime(formats...)
+			dt2.chain.assertNotFailed(t)
+
+			value3 := NewString(reporter, str)
+			dt3 := value3.AsDateTime(f)
+			dt3.chain.assertNotFailed(t)
+
+			if n != 0 {
+				value4 := NewString(reporter, str)
+				dt4 := value4.AsDateTime(formats[0])
+				dt4.chain.assertFailed(t)
+			}
+		})
 	}
 }
 
diff --git a/value_test.go b/value_test.go
index 759348d..5259ddf 100644
--- a/value_test.go
+++ b/value_test.go
@@ -142,17 +142,17 @@ func TestValue_Decode(t *testing.T) {
 func TestValue_Alias(t *testing.T) {
 	reporter := newMockReporter(t)
 
-	value1 := NewValue(reporter, 123)
-	assert.Equal(t, []string{"Value()"}, value1.chain.context.Path)
-	assert.Equal(t, []string{"Value()"}, value1.chain.context.AliasedPath)
+	value := NewValue(reporter, 123)
+	assert.Equal(t, []string{"Value()"}, value.chain.context.Path)
+	assert.Equal(t, []string{"Value()"}, value.chain.context.AliasedPath)
 
-	value2 := value1.Alias("foo")
-	assert.Equal(t, []string{"Value()"}, value2.chain.context.Path)
-	assert.Equal(t, []string{"foo"}, value2.chain.context.AliasedPath)
+	value.Alias("foo")
+	assert.Equal(t, []string{"Value()"}, value.chain.context.Path)
+	assert.Equal(t, []string{"foo"}, value.chain.context.AliasedPath)
 
-	value3 := value2.Number()
-	assert.Equal(t, []string{"Value()", "Number()"}, value3.chain.context.Path)
-	assert.Equal(t, []string{"foo", "Number()"}, value3.chain.context.AliasedPath)
+	childValue := value.Number()
+	assert.Equal(t, []string{"Value()", "Number()"}, childValue.chain.context.Path)
+	assert.Equal(t, []string{"foo", "Number()"}, childValue.chain.context.AliasedPath)
 }
 
 func TestValue_CastNull(t *testing.T) {
diff --git a/websocket_test.go b/websocket_test.go
index 8ff17c4..789f3e7 100644
--- a/websocket_test.go
+++ b/websocket_test.go
@@ -49,13 +49,13 @@ func TestWebsocket_FailedChain(t *testing.T) {
 func TestWebsocket_Alias(t *testing.T) {
 	reporter := newMockReporter(t)
 
-	value1 := NewWebsocketC(Config{Reporter: reporter}, newMockWebsocketConn())
-	assert.Equal(t, []string{"Websocket()"}, value1.chain.context.Path)
-	assert.Equal(t, []string{"Websocket()"}, value1.chain.context.AliasedPath)
+	value := NewWebsocketC(Config{Reporter: reporter}, newMockWebsocketConn())
+	assert.Equal(t, []string{"Websocket()"}, value.chain.context.Path)
+	assert.Equal(t, []string{"Websocket()"}, value.chain.context.AliasedPath)
 
-	value2 := value1.Alias("foo")
-	assert.Equal(t, []string{"Websocket()"}, value2.chain.context.Path)
-	assert.Equal(t, []string{"foo"}, value2.chain.context.AliasedPath)
+	value.Alias("foo")
+	assert.Equal(t, []string{"Websocket()"}, value.chain.context.Path)
+	assert.Equal(t, []string{"foo"}, value.chain.context.AliasedPath)
 }
 
 func TestWebsocket_NilConn(t *testing.T) {