Repository: gavv/httpexpect
Pull request #183 (2022-12-01 19:33:18)

177 array and object transform
Issue #177 

Hey @gavv checkout this PR. Let me know if something needs to be changed in it. 

coveralls: 
[![Coverage Status](https://coveralls.io/builds/54841218/badge)](https://coveralls.io/builds/54841218)

Coverage decreased (-0.2%) to 92.5% when pulling **ab8886fd63304610dfabd79c1a2de7e4338afff1 on angshumanHalder:177-array-and-object-Transform** into **67cdcd4bfa69ab1c707405ad475e8175cd5eea2b on gavv:master**.

gavv: > [![Coverage Status](https://camo.githubusercontent.com/4fc151e935d37a664e9d795aab59fa8c3b150037bf1883f716126b7993525797/68747470733a2f2f636f766572616c6c732e696f2f6275696c64732f35343834313234372f6261646765)](https://coveralls.io/builds/54841247)
> 
> Coverage decreased (-0.2%) to 92.5% when pulling **[ab8886f](https://github.com/gavv/httpexpect/commit/ab8886fd63304610dfabd79c1a2de7e4338afff1) on angshumanHalder:177-array-and-object-Transform** into **[67cdcd4](https://github.com/gavv/httpexpect/commit/67cdcd4bfa69ab1c707405ad475e8175cd5eea2b) on gavv:master**.

@angshumanHalder this comment indicates that test coverage decreased in this PR.

If you follow the link to coveralls, you'll see that in array.go and object.go there are two new branches which are not covered (in Transform).

Could you cover those branches in unit tests?

Patch:
diff --git a/.gitignore b/.gitignore
index 0bf55fd..8a1c41d 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,1 +1,2 @@
 TODO.org
+vendor/
\ No newline at end of file
diff --git a/array.go b/array.go
index 61964e3..9efd047 100644
--- a/array.go
+++ b/array.go
@@ -967,3 +967,46 @@ func countElement(array []interface{}, element interface{}) int {
 	}
 	return count
 }
+
+// Transform runs the passed function on all the Elements in the array
+// and returns a new array without effeecting original array.
+//
+// Example:
+//
+//	array := NewArray(t, []interface{}{"foo", "bar"})
+//	transformedArray := array.Transform(
+//		func(index int, value interface{}) interface{} {
+//		  if val, ok := value.(float64); ok {
+//		    return int(val) * int(val)
+//		  }
+//	    return nil
+//		},
+//	)
+//	transformedArray.Equals([]interface{}{....})
+func (a *Array) Transform(fn func(index int, value interface{}) interface{}) *Array {
+	a.chain.enter("Transform()")
+	defer a.chain.leave()
+
+	if a.chain.failed() {
+		return newArray(a.chain, nil)
+	}
+
+	if fn == nil {
+		a.chain.fail(AssertionFailure{
+			Type: AssertUsage,
+			Errors: []error{
+				errors.New("unexpected nil function argument"),
+			},
+		})
+		return newArray(a.chain, nil)
+	}
+
+	array := []interface{}{}
+
+	for index, val := range a.value {
+		transformedValue := fn(index, val)
+		array = append(array, transformedValue)
+	}
+
+	return newArray(a.chain, array)
+}
diff --git a/object.go b/object.go
index 7ce1d2e..1061ddc 100644
--- a/object.go
+++ b/object.go
@@ -768,3 +768,46 @@ func checkSubset(outer, inner map[string]interface{}) bool {
 	}
 	return true
 }
+
+// Transform runs the passed function on all the Elements in the Object
+// and returns a new object without effecting original object.
+//
+// Example:
+//
+//	object := NewObject(t, []interface{}{"foo": 12, "bar": 13})
+//	transformedObject := object.Transform(
+//		func(key string, value interface{}) interface{} {
+//		  if val, ok := value.(float64); ok {
+//		    return int(val) * int(val)
+//		  }
+//	    return nil
+//		},
+//	)
+//	transformedObject.Equals([]interface{}{"foo": 144, "bar": 169 })
+func (o *Object) Transform(fn func(key string, value interface{}) interface{}) *Object {
+	o.chain.enter("Transform()")
+	defer o.chain.leave()
+
+	if o.chain.failed() {
+		return newObject(o.chain, nil)
+	}
+
+	if fn == nil {
+		o.chain.fail(AssertionFailure{
+			Type: AssertUsage,
+			Errors: []error{
+				errors.New("unexpected nil function argument"),
+			},
+		})
+		return newObject(o.chain, nil)
+	}
+
+	object := map[string]interface{}{}
+
+	for key, val := range o.value {
+		transformedValue := fn(key, val)
+		object[key] = transformedValue
+	}
+
+	return newObject(o.chain, object)
+}
diff --git a/string.go b/string.go
index 363a2bc..fd6f401 100644
--- a/string.go
+++ b/string.go
@@ -359,6 +359,234 @@ func (s *String) NotContainsFold(value string) *String {
 	return s
 }
 
+// HasPrefix succeeds if string has given Go string as prefix
+//
+// Example:
+//
+//	str := NewString(t, "Hello World")
+//	str.HasPrefix("Hello")
+func (s *String) HasPrefix(value string) *String {
+	s.chain.enter("HasPrefix()")
+	defer s.chain.leave()
+
+	if s.chain.failed() {
+		return s
+	}
+
+	if !strings.HasPrefix(s.value, value) {
+		s.chain.fail(AssertionFailure{
+			Type:     AssertContainsSubset,
+			Actual:   &AssertionValue{s.value},
+			Expected: &AssertionValue{value},
+			Errors: []error{
+				errors.New("expected: string has prefix"),
+			},
+		})
+	}
+
+	return s
+}
+
+// NotHasPrefix succeeds if string doesn't have given Go string as prefix
+//
+// Example:
+//
+//	str := NewString(t, "Hello World")
+//	str.NotHasPrefix("Bye")
+func (s *String) NotHasPrefix(value string) *String {
+	s.chain.enter("NotHasPrefix()")
+	defer s.chain.leave()
+
+	if s.chain.failed() {
+		return s
+	}
+
+	if strings.HasPrefix(s.value, value) {
+		s.chain.fail(AssertionFailure{
+			Type:     AssertNotContainsSubset,
+			Actual:   &AssertionValue{s.value},
+			Expected: &AssertionValue{value},
+			Errors: []error{
+				errors.New("expected: string doesn't have prefix"),
+			},
+		})
+	}
+
+	return s
+}
+
+// HasSuffix succeeds if string has given Go string as suffix
+//
+// Example:
+//
+//	str := NewString(t, "Hello World")
+//	str.HasSuffix("World")
+func (s *String) HasSuffix(value string) *String {
+	s.chain.enter("HasSuffix()")
+	defer s.chain.leave()
+
+	if s.chain.failed() {
+		return s
+	}
+
+	if !strings.HasSuffix(s.value, value) {
+		s.chain.fail(AssertionFailure{
+			Type:     AssertContainsSubset,
+			Actual:   &AssertionValue{s.value},
+			Expected: &AssertionValue{value},
+			Errors: []error{
+				errors.New("expected: string has suffix"),
+			},
+		})
+	}
+
+	return s
+}
+
+// NotHasSuffix succeeds if string doesn't have given Go string as suffix
+//
+// Example:
+//
+//	str := NewString(t, "Hello World")
+//	str.NotHasSuffix("Hello")
+func (s *String) NotHasSuffix(value string) *String {
+	s.chain.enter("NotHasSuffix()")
+	defer s.chain.leave()
+
+	if s.chain.failed() {
+		return s
+	}
+
+	if strings.HasSuffix(s.value, value) {
+		s.chain.fail(AssertionFailure{
+			Type:     AssertNotContainsSubset,
+			Actual:   &AssertionValue{s.value},
+			Expected: &AssertionValue{value},
+			Errors: []error{
+				errors.New("expected: string doesn't have suffix"),
+			},
+		})
+	}
+
+	return s
+}
+
+// HasPrefixFold succeeds if string has given Go string as prefix
+// after applying Unicode case-folding (so it's a case-insensitive match).
+//
+// Example:
+//
+//	str := NewString(t, "Hello World")
+//	str.HasPrefixFold("hello")
+func (s *String) HasPrefixFold(value string) *String {
+	s.chain.enter("HasPrefixFold()")
+	defer s.chain.leave()
+
+	if s.chain.failed() {
+		return s
+	}
+
+	if !strings.HasPrefix(strings.ToLower(s.value), strings.ToLower(value)) {
+		s.chain.fail(AssertionFailure{
+			Type:     AssertContainsSubset,
+			Actual:   &AssertionValue{s.value},
+			Expected: &AssertionValue{value},
+			Errors: []error{
+				errors.New("expected: string has prefix (if folded)"),
+			},
+		})
+	}
+
+	return s
+}
+
+// NotHasPrefixFold succeeds if string doesn't have given Go string as prefix
+// after applying Unicode case-folding (so it's a case-insensitive match).
+//
+// Example:
+//
+//	str := NewString(t, "Hello World")
+//	str.NotHasPrefixFold("Bye")
+func (s *String) NotHasPrefixFold(value string) *String {
+	s.chain.enter("NotHasPrefixFold()")
+	defer s.chain.leave()
+
+	if s.chain.failed() {
+		return s
+	}
+
+	if strings.HasPrefix(strings.ToLower(s.value), strings.ToLower(value)) {
+		s.chain.fail(AssertionFailure{
+			Type:     AssertNotContainsSubset,
+			Actual:   &AssertionValue{s.value},
+			Expected: &AssertionValue{value},
+			Errors: []error{
+				errors.New("expected: string doesn't have prefix (if folded)"),
+			},
+		})
+	}
+
+	return s
+}
+
+// HasSuffixFold succeeds if string has given Go string as suffix
+// after applying Unicode case-folding (so it's a case-insensitive match).
+//
+// Example:
+//
+//	str := NewString(t, "Hello World")
+//	str.HasSuffixFold("world")
+func (s *String) HasSuffixFold(value string) *String {
+	s.chain.enter("HasSuffixFold()")
+	defer s.chain.leave()
+
+	if s.chain.failed() {
+		return s
+	}
+
+	if !strings.HasSuffix(strings.ToLower(s.value), strings.ToLower(value)) {
+		s.chain.fail(AssertionFailure{
+			Type:     AssertContainsSubset,
+			Actual:   &AssertionValue{s.value},
+			Expected: &AssertionValue{value},
+			Errors: []error{
+				errors.New("expected: string has suffix (if folded)"),
+			},
+		})
+	}
+
+	return s
+}
+
+// NotHasSuffixFold succeeds if string doesn't have given Go string as suffix
+// after applying Unicode case-folding (so it's a case-insensitive match).
+//
+// Example:
+//
+//	str := NewString(t, "Hello World")
+//	str.NotHasSuffixFold("Bye")
+func (s *String) NotHasSuffixFold(value string) *String {
+	s.chain.enter("NotHasSuffix()")
+	defer s.chain.leave()
+
+	if s.chain.failed() {
+		return s
+	}
+
+	if strings.HasSuffix(strings.ToLower(s.value), strings.ToLower(value)) {
+		s.chain.fail(AssertionFailure{
+			Type:     AssertNotContainsSubset,
+			Actual:   &AssertionValue{s.value},
+			Expected: &AssertionValue{value},
+			Errors: []error{
+				errors.New("expected: string doesn't have suffix (if folded)"),
+			},
+		})
+	}
+
+	return s
+}
+
 // Match matches the string with given regexp and returns a new Match instance
 // with found submatches.
 //


Test patch:
diff --git a/array_test.go b/array_test.go
index 3b145e2..2465b2d 100644
--- a/array_test.go
+++ b/array_test.go
@@ -44,6 +44,9 @@ func TestArrayFailed(t *testing.T) {
 			val.String().NotEmpty()
 			return true
 		})
+		value.Transform(func(index int, value interface{}) interface{} {
+			return nil
+		})
 	}
 
 	t.Run("failed_chain", func(t *testing.T) {
@@ -813,6 +816,54 @@ func TestArrayEvery(t *testing.T) {
 	})
 }
 
+func TestArrayTransform(t *testing.T) {
+	t.Run("Square Integers", func(ts *testing.T) {
+		reporter := newMockReporter(ts)
+		array := NewArray(reporter, []interface{}{2, 4, 6})
+		newArray := array.Transform(func(_ int, val interface{}) interface{} {
+			if v, ok := val.(float64); ok {
+				return int(v) * int(v)
+			}
+			ts.Errorf("failed transformation")
+			return nil
+		})
+		assert.Equal(t, []interface{}{float64(4), float64(16), float64(36)}, newArray.value)
+		newArray.chain.assertOK(ts)
+	})
+
+	t.Run("Chain fail on nil function value", func(ts *testing.T) {
+		reporter := newMockReporter(ts)
+		array := NewArray(reporter, []interface{}{2, 4, 6})
+		newArray := array.Transform(nil)
+		newArray.chain.assertFailed(reporter)
+	})
+
+	t.Run("Empty array", func(ts *testing.T) {
+		reporter := newMockReporter(ts)
+		array := NewArray(reporter, []interface{}{})
+		newArray := array.Transform(func(_ int, _ interface{}) interface{} {
+			ts.Errorf("failed transformation")
+			return nil
+		})
+		newArray.chain.assertOK(ts)
+	})
+
+	t.Run("Test correct index", func(ts *testing.T) {
+		reporter := newMockReporter(ts)
+		array := NewArray(reporter, []interface{}{1, 2, 3})
+		newArray := array.Transform(
+			func(idx int, val interface{}) interface{} {
+				if v, ok := val.(float64); ok {
+					assert.Equal(ts, idx, int(v)-1)
+				}
+				return val
+			},
+		)
+		assert.Equal(t, []interface{}{float64(1), float64(2), float64(3)}, newArray.value)
+		newArray.chain.assertOK(ts)
+	})
+}
+
 func TestArrayFilter(t *testing.T) {
 	t.Run("Filter an array of elements of the same type and validate", func(ts *testing.T) {
 		reporter := newMockReporter(t)
diff --git a/object_test.go b/object_test.go
index f2ecc9c..2da61cb 100644
--- a/object_test.go
+++ b/object_test.go
@@ -32,6 +32,9 @@ func TestObjectFailed(t *testing.T) {
 		value.Every(func(_ string, value *Value) {
 			value.String().NotEmpty()
 		})
+		value.Transform(func(key string, value interface{}) interface{} {
+			return nil
+		})
 		value.Filter(func(_ string, value *Value) bool {
 			value.String().NotEmpty()
 			return true
@@ -831,17 +834,92 @@ func TestObjectEvery(t *testing.T) {
 	})
 }
 
+func TestObjectTransform(t *testing.T) {
+	t.Run("Add Hello", func(ts *testing.T) {
+		reporter := newMockReporter(ts)
+		object := NewObject(reporter, map[string]interface{}{
+			"foo": "123",
+			"bar": "456",
+			"baz": "b",
+		})
+		newObject := object.Transform(func(_ string, value interface{}) interface{} {
+			if v, ok := value.(string); ok {
+				return "Hello " + v
+			}
+			return nil
+		})
+		assert.Equal(t,
+			map[string]interface{}{"foo": "Hello 123", "bar": "Hello 456", "baz": "Hello b"},
+			newObject.value,
+		)
+	})
+
+	t.Run("Chain fail on nil function value", func(ts *testing.T) {
+		reporter := newMockReporter(ts)
+		object := NewObject(reporter, map[string]interface{}{
+			"foo": "123",
+			"bar": "456",
+			"baz": "b",
+		})
+		newObject := object.Transform(nil)
+		newObject.chain.assertFailed(reporter)
+	})
+
+	t.Run("Empty object", func(ts *testing.T) {
+		reporter := newMockReporter(ts)
+		object := NewObject(reporter, map[string]interface{}{
+			"foo": "123",
+			"bar": "456",
+			"baz": "b",
+		})
+		newObject := object.Transform(func(_ string, value interface{}) interface{} {
+			if v, ok := value.(string); ok {
+				return "Hello " + v
+			}
+			return nil
+		})
+		newObject.chain.assertOK(ts)
+	})
+
+	t.Run("Test correct index", func(ts *testing.T) {
+		reporter := newMockReporter(ts)
+		object := NewObject(reporter, map[string]interface{}{
+			"foo": "123",
+			"bar": "456",
+			"baz": "baz",
+		})
+		newObject := object.Transform(func(key string, value interface{}) interface{} {
+			if v, ok := value.(string); ok {
+				switch v {
+				case "123":
+					assert.Equal(ts, "foo", key)
+				case "456":
+					assert.Equal(ts, "bar", key)
+				case "baz":
+					assert.Equal(ts, "baz", key)
+				}
+			}
+			return value
+		})
+		newObject.chain.assertOK(ts)
+	})
+}
+
 func TestObjectFilter(t *testing.T) {
 	t.Run("Filter an object of elements of the same type and validate", func(ts *testing.T) {
 		reporter := newMockReporter(t)
-		object := NewObject(reporter, map[string]interface{}{"foo": "bar",
-			"baz": "qux", "quux": "corge"})
+		object := NewObject(reporter, map[string]interface{}{
+			"foo": "bar",
+			"baz": "qux", "quux": "corge",
+		})
 		filteredObject := object.Filter(func(key string, value *Value) bool {
 			return value.Raw() != "qux" && key != "quux"
 		})
 		assert.Equal(t, map[string]interface{}{"foo": "bar"}, filteredObject.Raw())
-		assert.Equal(t, object.Raw(), map[string]interface{}{"foo": "bar",
-			"baz": "qux", "quux": "corge"})
+		assert.Equal(t, object.Raw(), map[string]interface{}{
+			"foo": "bar",
+			"baz": "qux", "quux": "corge",
+		})
 
 		filteredObject.chain.assertOK(t)
 		object.chain.assertOK(t)
@@ -849,15 +927,19 @@ func TestObjectFilter(t *testing.T) {
 
 	t.Run("Filter throws when an assertion within predicate fails", func(ts *testing.T) {
 		reporter := newMockReporter(t)
-		object := NewObject(reporter, map[string]interface{}{"foo": "bar", "baz": 6.0,
-			"qux": "quux"})
+		object := NewObject(reporter, map[string]interface{}{
+			"foo": "bar", "baz": 6.0,
+			"qux": "quux",
+		})
 		filteredObject := object.Filter(func(key string, value *Value) bool {
 			stringifiedValue := value.String().NotEmpty().Raw()
 			return stringifiedValue != "bar"
 		})
 		assert.Equal(t, map[string]interface{}{"qux": "quux"}, filteredObject.Raw())
-		assert.Equal(t, object.Raw(), map[string]interface{}{"foo": "bar", "baz": 6.0,
-			"qux": "quux"})
+		assert.Equal(t, object.Raw(), map[string]interface{}{
+			"foo": "bar", "baz": 6.0,
+			"qux": "quux",
+		})
 
 		filteredObject.chain.assertOK(t)
 		object.chain.assertOK(t)
@@ -865,14 +947,18 @@ func TestObjectFilter(t *testing.T) {
 
 	t.Run("Filter an object of different types and validate", func(ts *testing.T) {
 		reporter := newMockReporter(t)
-		object := NewObject(reporter, map[string]interface{}{"foo": "bar",
-			"baz": 3.0, "qux": false})
+		object := NewObject(reporter, map[string]interface{}{
+			"foo": "bar",
+			"baz": 3.0, "qux": false,
+		})
 		filteredObject := object.Filter(func(key string, value *Value) bool {
 			return value.Raw() != "bar" && value.Raw() != 3.0
 		})
 		assert.Equal(t, map[string]interface{}{"qux": false}, filteredObject.Raw())
-		assert.Equal(t, object.Raw(), map[string]interface{}{"foo": "bar",
-			"baz": 3.0, "qux": false})
+		assert.Equal(t, object.Raw(), map[string]interface{}{
+			"foo": "bar",
+			"baz": 3.0, "qux": false,
+		})
 
 		filteredObject.chain.assertOK(t)
 		object.chain.assertOK(t)
@@ -893,17 +979,20 @@ func TestObjectFilter(t *testing.T) {
 	t.Run("Filter should return an empty non-nil object if no items pass",
 		func(ts *testing.T) {
 			reporter := newMockReporter(t)
-			object := NewObject(reporter, map[string]interface{}{"foo": "bar",
-				"baz": "qux", "quux": "corge"})
+			object := NewObject(reporter, map[string]interface{}{
+				"foo": "bar",
+				"baz": "qux", "quux": "corge",
+			})
 			filteredObject := object.Filter(func(key string, value *Value) bool {
 				return false
 			})
 			assert.Equal(t, map[string]interface{}{}, filteredObject.Raw())
-			assert.Equal(t, object.Raw(), map[string]interface{}{"foo": "bar",
-				"baz": "qux", "quux": "corge"})
+			assert.Equal(t, object.Raw(), map[string]interface{}{
+				"foo": "bar",
+				"baz": "qux", "quux": "corge",
+			})
 
 			filteredObject.chain.assertOK(t)
 			object.chain.assertOK(t)
 		})
-
 }
diff --git a/string_test.go b/string_test.go
index 14de6e1..b122ba1 100644
--- a/string_test.go
+++ b/string_test.go
@@ -31,6 +31,14 @@ func TestStringFailed(t *testing.T) {
 	value.NotContains("")
 	value.ContainsFold("")
 	value.NotContainsFold("")
+	value.HasPrefix("")
+	value.NotHasPrefix("")
+	value.HasSuffix("")
+	value.NotHasSuffix("")
+	value.HasPrefixFold("")
+	value.NotHasPrefixFold("")
+	value.HasSuffixFold("")
+	value.NotHasSuffixFold("")
 	value.Match("")
 	value.NotMatch("")
 	value.MatchAll("")
@@ -474,3 +482,159 @@ func TestStringAsDateTime(t *testing.T) {
 		}
 	}
 }
+
+func TestStringHasPrefix(t *testing.T) {
+	reporter := newMockReporter(t)
+
+	value := NewString(reporter, "Hello World")
+
+	value.HasPrefix("Hello")
+	value.chain.assertOK(t)
+	value.chain.reset()
+
+	value.HasPrefix("Hello World")
+	value.chain.assertOK(t)
+	value.chain.reset()
+
+	value.HasPrefix("")
+	value.chain.assertOK(t)
+	value.chain.reset()
+
+	value.HasPrefix("Hello!")
+	value.chain.assertFailed(t)
+	value.chain.reset()
+
+	value.HasPrefix("hello")
+	value.chain.assertFailed(t)
+	value.chain.reset()
+
+	value.HasPrefix("World")
+	value.chain.assertFailed(t)
+	value.chain.reset()
+
+	value.NotHasPrefix("Bye")
+	value.chain.assertOK(t)
+	value.chain.reset()
+
+	value.NotHasPrefix("Hello")
+	value.chain.assertFailed(t)
+	value.chain.reset()
+
+	value.NotHasPrefix("hello")
+	value.chain.assertOK(t)
+	value.chain.reset()
+}
+
+func TestStringHasSuffix(t *testing.T) {
+	reporter := newMockReporter(t)
+
+	value := NewString(reporter, "Hello World")
+
+	value.HasSuffix("World")
+	value.chain.assertOK(t)
+	value.chain.reset()
+
+	value.HasSuffix("Hello World")
+	value.chain.assertOK(t)
+	value.chain.reset()
+
+	value.HasSuffix("")
+	value.chain.assertOK(t)
+	value.chain.reset()
+
+	value.HasPrefix("World!")
+	value.chain.assertFailed(t)
+	value.chain.reset()
+
+	value.HasSuffix("world")
+	value.chain.assertFailed(t)
+	value.chain.reset()
+
+	value.HasSuffix("Hello")
+	value.chain.assertFailed(t)
+	value.chain.reset()
+
+	value.NotHasSuffix("Bye")
+	value.chain.assertOK(t)
+	value.chain.reset()
+
+	value.NotHasSuffix("World")
+	value.chain.assertFailed(t)
+	value.chain.reset()
+
+	value.NotHasSuffix("world")
+	value.chain.assertOK(t)
+	value.chain.reset()
+}
+
+func TestStringHasPrefixFold(t *testing.T) {
+	reporter := newMockReporter(t)
+
+	value := NewString(reporter, "Hello World")
+
+	value.HasPrefixFold("hello")
+	value.chain.assertOK(t)
+	value.chain.reset()
+
+	value.HasPrefixFold("HeLlO wOrLd")
+	value.chain.assertOK(t)
+	value.chain.reset()
+
+	value.HasPrefixFold("")
+	value.chain.assertOK(t)
+	value.chain.reset()
+
+	value.HasPrefixFold("World")
+	value.chain.assertFailed(t)
+	value.chain.reset()
+
+	value.NotHasPrefixFold("Bye")
+	value.chain.assertOK(t)
+	value.chain.reset()
+
+	value.NotHasPrefixFold("world")
+	value.chain.assertOK(t)
+	value.chain.reset()
+
+	value.NotHasPrefixFold("world!")
+	value.chain.assertOK(t)
+	value.chain.reset()
+}
+
+func TestStringHasSuffixFold(t *testing.T) {
+	reporter := newMockReporter(t)
+
+	value := NewString(reporter, "Hello World")
+
+	value.HasSuffixFold("world")
+	value.chain.assertOK(t)
+	value.chain.reset()
+
+	value.HasSuffixFold("hElLo WoRlD")
+	value.chain.assertOK(t)
+	value.chain.reset()
+
+	value.HasSuffixFold("")
+	value.chain.assertOK(t)
+	value.chain.reset()
+
+	value.HasSuffixFold("hello")
+	value.chain.assertFailed(t)
+	value.chain.reset()
+
+	value.HasSuffixFold("world!")
+	value.chain.assertFailed(t)
+	value.chain.reset()
+
+	value.NotHasSuffixFold("Bye")
+	value.chain.assertOK(t)
+	value.chain.reset()
+
+	value.NotHasSuffixFold("world")
+	value.chain.assertFailed(t)
+	value.chain.reset()
+
+	value.NotHasSuffixFold("world!")
+	value.chain.assertOK(t)
+	value.chain.reset()
+}