From 531eea3ce77fa0e84f9c9696124c94072a9c25ec Mon Sep 17 00:00:00 2001 From: Fahim Bagar Date: Mon, 30 Jan 2023 23:03:53 +0700 Subject: [PATCH 01/38] Add InList and NotInList for String --- string.go | 73 ++++++++++++++++++++++++++++++++++++++++++++++++++ string_test.go | 26 ++++++++++++++++++ 2 files changed, 99 insertions(+) diff --git a/string.go b/string.go index f6b97575d..a394f3335 100644 --- a/string.go +++ b/string.go @@ -245,6 +245,70 @@ func (s *String) Equal(value string) *String { return s.IsEqual(value) } +// InList succeeds if string is listed by given [values...]. +// +// Example: +// +// str := NewString(t, "Hello") +// str.InList("Hello", "Goodbye") +func (s *String) InList(values ...string) *String { + opChain := s.chain.enter("InList()") + defer opChain.leave() + + if opChain.failed() { + return s + } + + for _, v := range values { + if s.value == v { + return s + } + } + + opChain.fail(AssertionFailure{ + Type: AssertBelongs, + Actual: &AssertionValue{s.value}, + Expected: &AssertionValue{AssertionList(stringList(values))}, + Errors: []error{ + errors.New("expected: string is listed"), + }, + }) + + return s +} + +// NotInList succeeds if string is not listed by given [values...]. +// +// Example: +// +// str := NewString(t, "Hello") +// str.NotInList("NotInList", "Goodbye") +func (s *String) NotInList(values ...string) *String { + opChain := s.chain.enter("NotInList()") + defer opChain.leave() + + if opChain.failed() { + return s + } + + for _, v := range values { + if s.value == v { + opChain.fail(AssertionFailure{ + Type: AssertNotBelongs, + Actual: &AssertionValue{s.value}, + Expected: &AssertionValue{AssertionList(stringList(values))}, + Errors: []error{ + errors.New("expected: string is not listed"), + }, + }) + + return s + } + } + + return s +} + // IsEqualFold succeeds if string is equal to given Go string after applying Unicode // case-folding (so it's a case-insensitive match). // @@ -1142,3 +1206,12 @@ func (s *String) Number() *Number { func (s *String) DateTime(layout ...string) *DateTime { return s.AsDateTime(layout...) } + +func stringList(values []string) []interface{} { + s := make([]interface{}, 0, len(values)) + for _, v := range values { + s = append(s, v) + } + + return s +} diff --git a/string_test.go b/string_test.go index 03d7df258..2907cae43 100644 --- a/string_test.go +++ b/string_test.go @@ -29,6 +29,8 @@ func TestString_Failed(t *testing.T) { value.NotEmpty() value.IsEqual("") value.NotEqual("") + value.InList("") + value.NotInList("") value.IsEqualFold("") value.NotEqualFold("") value.Contains("") @@ -218,6 +220,30 @@ func TestString_Equal(t *testing.T) { value.chain.clearFailed() } +func TestString_List(t *testing.T) { + reporter := newMockReporter(t) + + value := NewString(reporter, "foo") + + assert.Equal(t, "foo", value.Raw()) + + value.InList("foo", "bar") + value.chain.assertNotFailed(t) + value.chain.clearFailed() + + value.InList("FOO", "BAR") + value.chain.assertFailed(t) + value.chain.clearFailed() + + value.NotInList("FOO", "bar") + value.chain.assertNotFailed(t) + value.chain.clearFailed() + + value.NotInList("foo", "BAR") + value.chain.assertFailed(t) + value.chain.clearFailed() +} + func TestString_EqualFold(t *testing.T) { reporter := newMockReporter(t) From 3fedfb2c7a8b1b975bc1bfb7343963c67f9c37a9 Mon Sep 17 00:00:00 2001 From: Fahim Bagar Date: Tue, 31 Jan 2023 00:07:02 +0700 Subject: [PATCH 02/38] Add InList and NotInList for Object Since NotInList receives interface type, it must iterates all the given values before deciding whether it not listed. --- object.go | 90 ++++++++++++++++++++++++++++++++++++++++++++++++++ object_test.go | 62 ++++++++++++++++++++++++++++++++++ 2 files changed, 152 insertions(+) diff --git a/object.go b/object.go index df80dc25f..5114fb4e0 100644 --- a/object.go +++ b/object.go @@ -744,6 +744,96 @@ func (o *Object) Equal(value interface{}) *Object { return o.IsEqual(value) } +// InList succeeds if object is listed by given [values...]. +// Before comparison, both object and value are converted to canonical form. +// +// values should be an array of map[string]interface{} or struct. +// +// Example: +// +// object := NewObject(t, map[string]interface{}{"foo": 123}) +// object.InList(map[string]interface{}{"foo": 123}) +func (o *Object) InList(values ...interface{}) *Object { + opChain := o.chain.enter("InList()") + defer opChain.leave() + + if opChain.failed() { + return o + } + + arr, _ := canonArray(opChain, values) + + var isListed bool + for _, v := range arr { + expected, ok := canonMap(opChain, v) + if !ok { + return o + } + + if reflect.DeepEqual(expected, o.value) { + isListed = true + } + } + + if !isListed { + opChain.fail(AssertionFailure{ + Type: AssertBelongs, + Actual: &AssertionValue{o.value}, + Expected: &AssertionValue{AssertionList(values)}, + Errors: []error{ + errors.New("expected: map is listed"), + }, + }) + } + + return o +} + +// NotInList succeeds if object is not listed by given [values...]. +// Before comparison, both object and value are converted to canonical form. +// +// values should be an array of map[string]interface{} or struct. +// +// Example: +// +// object := NewObject(t, map[string]interface{}{"foo": 123}) +// object.InList(map[string]interface{}{"bar": 456}) +func (o *Object) NotInList(values ...interface{}) *Object { + opChain := o.chain.enter("NotInList()") + defer opChain.leave() + + if opChain.failed() { + return o + } + + arr, _ := canonArray(opChain, values) + + var isListed bool + for _, v := range arr { + expected, ok := canonMap(opChain, v) + if !ok { + return o + } + + if reflect.DeepEqual(expected, o.value) { + isListed = true + } + } + + if isListed { + opChain.fail(AssertionFailure{ + Type: AssertNotBelongs, + Actual: &AssertionValue{o.value}, + Expected: &AssertionValue{AssertionList(values)}, + Errors: []error{ + errors.New("expected: map is not listed"), + }, + }) + } + + return o +} + // ContainsKey succeeds if object contains given key. // // Example: diff --git a/object_test.go b/object_test.go index 3e790a76f..42417f900 100644 --- a/object_test.go +++ b/object_test.go @@ -26,6 +26,8 @@ func TestObject_Failed(t *testing.T) { value.NotEmpty() value.IsEqual(nil) value.NotEqual(nil) + value.InList(nil) + value.NotInList(nil) value.ContainsKey("foo") value.NotContainsKey("foo") value.ContainsValue("foo") @@ -421,6 +423,66 @@ func TestObject_Equal(t *testing.T) { value.chain.clearFailed() } +func TestObject_InList(t *testing.T) { + reporter := newMockReporter(t) + + value := NewObject(reporter, map[string]interface{}{"foo": 123.0}) + + assert.Equal(t, map[string]interface{}{"foo": 123.0}, value.Raw()) + + value.InList(map[string]interface{}{}) + value.chain.assertFailed(t) + value.chain.clearFailed() + + value.NotInList(map[string]interface{}{}) + value.chain.assertNotFailed(t) + value.chain.clearFailed() + + value.InList(map[string]interface{}{"FOO": 123.0}, map[string]interface{}{"BAR": 456.0}) + value.chain.assertFailed(t) + value.chain.clearFailed() + + value.NotInList(map[string]interface{}{"FOO": 123.0}, map[string]interface{}{"BAR": 456.0}) + value.chain.assertNotFailed(t) + value.chain.clearFailed() + + value.InList(map[string]interface{}{"foo": 456.0}, map[string]interface{}{"bar": 123.0}) + value.chain.assertFailed(t) + value.chain.clearFailed() + + value.NotInList(map[string]interface{}{"foo": 456.0}, map[string]interface{}{"bar": 123.0}) + value.chain.assertNotFailed(t) + value.chain.clearFailed() + + value.InList(map[string]interface{}{"foo": 123.0}, map[string]interface{}{"bar": 456.0}) + value.chain.assertNotFailed(t) + value.chain.clearFailed() + + value.NotInList(map[string]interface{}{"foo": 123.0}, map[string]interface{}{"bar": 456.0}) + value.chain.assertFailed(t) + value.chain.clearFailed() + + value.InList(struct { + Foo float64 `json:"foo"` + }{Foo: 123.00}) + value.chain.assertNotFailed(t) + value.chain.clearFailed() + + value.NotInList(struct { + Foo float64 `json:"foo"` + }{Foo: 123.00}) + value.chain.assertFailed(t) + value.chain.clearFailed() + + value.InList(nil) + value.chain.assertFailed(t) + value.chain.clearFailed() + + value.NotInList(nil) + value.chain.assertFailed(t) + value.chain.clearFailed() +} + func TestObject_EqualStruct(t *testing.T) { reporter := newMockReporter(t) From 7df2ded2e473b6ed18d25b9a10cbc1a0679f3c18 Mon Sep 17 00:00:00 2001 From: Fahim Bagar Date: Tue, 31 Jan 2023 00:51:41 +0700 Subject: [PATCH 03/38] Add InList and NotInList for Number --- number.go | 90 ++++++++++++++++++++++++++++++++++++++++++++++++++ number_test.go | 40 ++++++++++++++++++++++ 2 files changed, 130 insertions(+) diff --git a/number.go b/number.go index 0ab4a0fbc..7e6310330 100644 --- a/number.go +++ b/number.go @@ -366,6 +366,96 @@ func (n *Number) NotInRange(min, max interface{}) *Number { return n } +// InList succeeds if number is listed by given [values...]. +// +// values should have array of numeric type convertible to float64. Before +// comparison, it is converted to float64. +// +// Example: +// +// number := NewNumber(t, 123) +// number.InList(float64(123), int32(123)) +func (n *Number) InList(values ...interface{}) *Number { + opChain := n.chain.enter("IsList()") + defer opChain.leave() + + if opChain.failed() { + return n + } + + arr, _ := canonArray(opChain, values) + + var isListed bool + for _, v := range arr { + num, ok := canonNumber(opChain, v) + if !ok { + return n + } + + if n.value == num { + isListed = true + } + } + + if !isListed { + opChain.fail(AssertionFailure{ + Type: AssertBelongs, + Actual: &AssertionValue{n.value}, + Expected: &AssertionValue{AssertionList(values)}, + Errors: []error{ + errors.New("expected: number is listed"), + }, + }) + } + + return n +} + +// NotInList succeeds if number is listed by given [values...]. +// +// values should have array of numeric type convertible to float64. Before +// comparison, it is converted to float64. +// +// Example: +// +// number := NewNumber(t, 123) +// number.NotInList(float64(456), int32(456)) +func (n *Number) NotInList(values ...interface{}) *Number { + opChain := n.chain.enter("NotInList()") + defer opChain.leave() + + if opChain.failed() { + return n + } + + arr, _ := canonArray(opChain, values) + + var isListed bool + for _, v := range arr { + num, ok := canonNumber(opChain, v) + if !ok { + return n + } + + if n.value == num { + isListed = true + } + } + + if isListed { + opChain.fail(AssertionFailure{ + Type: AssertNotBelongs, + Actual: &AssertionValue{n.value}, + Expected: &AssertionValue{AssertionList(values)}, + Errors: []error{ + errors.New("expected: number is not listed"), + }, + }) + } + + return n +} + // Gt succeeds if number is greater than given value. // // value should have numeric type convertible to float64. Before comparison, diff --git a/number_test.go b/number_test.go index 7c2a83b88..522fa992e 100644 --- a/number_test.go +++ b/number_test.go @@ -22,6 +22,8 @@ func TestNumber_Failed(t *testing.T) { value.IsEqual(0) value.NotEqual(0) + value.InList(0) + value.NotInList(0) value.InDelta(0, 0) value.NotInDelta(0, 0) value.Gt(0) @@ -309,6 +311,44 @@ func TestNumber_InRange(t *testing.T) { value.chain.clearFailed() } +func TestNumber_InList(t *testing.T) { + reporter := newMockReporter(t) + + value := NewNumber(reporter, 1234) + + value.InList(1234, 4567) + value.chain.assertNotFailed(t) + value.chain.clearFailed() + + value.NotInList(1234, 4567) + value.chain.assertFailed(t) + value.chain.clearFailed() + + value.InList(1234.00, 4567.00) + value.chain.assertNotFailed(t) + value.chain.clearFailed() + + value.NotInList(1234.00, 4567.00) + value.chain.assertFailed(t) + value.chain.clearFailed() + + value.InList(4567.00, 1234.01) + value.chain.assertFailed(t) + value.chain.clearFailed() + + value.NotInList(4567.00, 1234.01) + value.chain.assertNotFailed(t) + value.chain.clearFailed() + + value.InList(1234+1, "1234") + value.chain.assertFailed(t) + value.chain.clearFailed() + + value.NotInList("1234+1", 1234+2) + value.chain.assertFailed(t) + value.chain.clearFailed() +} + func TestNumber_Greater(t *testing.T) { reporter := newMockReporter(t) From c64c0cf037f015fdfdf5145089999fc822be85f3 Mon Sep 17 00:00:00 2001 From: Fahim Bagar Date: Tue, 31 Jan 2023 02:01:27 +0700 Subject: [PATCH 04/38] Add InList and NotInList for Duration --- duration.go | 94 ++++++++++++++++++++++++++++++++++++++++++++++++ duration_test.go | 43 ++++++++++++++++++++++ 2 files changed, 137 insertions(+) diff --git a/duration.go b/duration.go index 35e64d8ca..145e2b7f5 100644 --- a/duration.go +++ b/duration.go @@ -429,3 +429,97 @@ func (d *Duration) NotInRange(min, max time.Duration) *Duration { return d } + +// InList succeeds if Duration is listed by given duration [values...]. +// +// Example: +// +// d := NewDuration(t, time.Minute) +// d.InList(time.Minute, time.Hour) +func (d *Duration) InList(values ...time.Duration) *Duration { + opChain := d.chain.enter("InList()") + defer opChain.leave() + + if opChain.failed() { + return d + } + + if d.value == nil { + opChain.fail(AssertionFailure{ + Type: AssertNotNil, + Actual: &AssertionValue{d.value}, + Errors: []error{ + errors.New("expected: duration is present"), + }, + }) + return d + } + + for _, v := range values { + if *d.value == v { + return d + } + } + + opChain.fail(AssertionFailure{ + Type: AssertBelongs, + Actual: &AssertionValue{d.value}, + Expected: &AssertionValue{AssertionList(durationList(values))}, + Errors: []error{ + errors.New("expected: duration is listed"), + }, + }) + + return d +} + +// NotInList succeeds if Duration is not listed by given duration [values...]. +// +// Example: +// +// d := NewDuration(t, time.Minute) +// d.NotInList(time.Second, time.Hour) +func (d *Duration) NotInList(values ...time.Duration) *Duration { + opChain := d.chain.enter("NotInList()") + defer opChain.leave() + + if opChain.failed() { + return d + } + + if d.value == nil { + opChain.fail(AssertionFailure{ + Type: AssertNotNil, + Actual: &AssertionValue{d.value}, + Errors: []error{ + errors.New("expected: duration is present"), + }, + }) + + return d + } + + for _, v := range values { + if *d.value == v { + opChain.fail(AssertionFailure{ + Type: AssertNotBelongs, + Actual: &AssertionValue{d.value}, + Expected: &AssertionValue{AssertionList(durationList(values))}, + Errors: []error{ + errors.New("expected: duration is not listed"), + }, + }) + } + } + + return d +} + +func durationList(values []time.Duration) []interface{} { + l := make([]interface{}, 0, len(values)) + for _, v := range values { + l = append(l, v) + } + + return l +} diff --git a/duration_test.go b/duration_test.go index add4e745e..7c64ad26a 100644 --- a/duration_test.go +++ b/duration_test.go @@ -16,6 +16,8 @@ func TestDuration_Failed(t *testing.T) { value.IsEqual(tm) value.NotEqual(tm) + value.InList(tm) + value.NotInList(tm) value.Gt(tm) value.Ge(tm) value.Lt(tm) @@ -223,3 +225,44 @@ func TestDuration_InRange(t *testing.T) { value.chain.assertNotFailed(t) value.chain.clearFailed() } + +func TestDuration_InList(t *testing.T) { + reporter := newMockReporter(t) + + newDuration(newMockChain(t), nil).InList().chain.assertFailed(t) + newDuration(newMockChain(t), nil).NotInList().chain.assertFailed(t) + + value := NewDuration(reporter, time.Second) + + value.InList(time.Second, time.Minute) + value.chain.assertNotFailed(t) + value.chain.clearFailed() + + value.NotInList(time.Second, time.Minute) + value.chain.assertFailed(t) + value.chain.clearFailed() + + value.InList(time.Second-1, time.Minute) + value.chain.assertFailed(t) + value.chain.clearFailed() + + value.NotInList(time.Second-1, time.Minute) + value.chain.assertNotFailed(t) + value.chain.clearFailed() + + value.InList(time.Second, time.Second+1) + value.chain.assertNotFailed(t) + value.chain.clearFailed() + + value.NotInList(time.Second, time.Second+1) + value.chain.assertFailed(t) + value.chain.clearFailed() + + value.InList(time.Second+1, time.Second-1) + value.chain.assertFailed(t) + value.chain.clearFailed() + + value.NotInList(time.Second+1, time.Second-1) + value.chain.assertNotFailed(t) + value.chain.clearFailed() +} From 0dcf755f807be32370ebd4f54fb74fd41a7c82a3 Mon Sep 17 00:00:00 2001 From: Fahim Bagar Date: Tue, 31 Jan 2023 02:14:19 +0700 Subject: [PATCH 05/38] Add InList and NotInList for DateTime --- datetime.go | 71 ++++++++++++++++++++++++++++++++++++++++++++++++ datetime_test.go | 56 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 127 insertions(+) diff --git a/datetime.go b/datetime.go index 5de349e9d..1794aa1e5 100644 --- a/datetime.go +++ b/datetime.go @@ -422,6 +422,68 @@ func (dt *DateTime) NotInRange(min, max time.Time) *DateTime { return dt } +// InList succeeds if DateTime is listed by given [values...]. +// +// Example: +// +// dt := NewDateTime(t, time.Unix(0, 2)) +// dt.InRange(time.Unix(0, 1), time.Unix(0, 2)) +func (dt *DateTime) InList(values ...time.Time) *DateTime { + opChain := dt.chain.enter("InRange()") + defer opChain.leave() + + if opChain.failed() { + return dt + } + + for _, v := range values { + if dt.value.Equal(v) { + return dt + } + } + + opChain.fail(AssertionFailure{ + Type: AssertBelongs, + Actual: &AssertionValue{dt.value}, + Expected: &AssertionValue{AssertionList(timeList(values))}, + Errors: []error{ + errors.New("expected: time point is listed"), + }, + }) + + return dt +} + +// NotInList succeeds if DateTime is not listed by given [values...]. +// +// Example: +// +// dt := NewDateTime(t, time.Unix(0, 2)) +// dt.InRange(time.Unix(0, 1), time.Unix(0, 3)) +func (dt *DateTime) NotInList(values ...time.Time) *DateTime { + opChain := dt.chain.enter("NotInList()") + defer opChain.leave() + + if opChain.failed() { + return dt + } + + for _, v := range values { + if dt.value.Equal(v) { + opChain.fail(AssertionFailure{ + Type: AssertNotBelongs, + Actual: &AssertionValue{dt.value}, + Expected: &AssertionValue{AssertionList(timeList(values))}, + Errors: []error{ + errors.New("expected: time point is not listed"), + }, + }) + } + } + + return dt +} + // Gt succeeds if DateTime is greater than given value. // // Example: @@ -569,3 +631,12 @@ func (dt *DateTime) AsLocal() *DateTime { return newDateTime(opChain, dt.value.Local()) } + +func timeList(values []time.Time) []interface{} { + l := make([]interface{}, 0, len(values)) + for _, v := range values { + l = append(l, v) + } + + return l +} diff --git a/datetime_test.go b/datetime_test.go index fb511bc77..fe6a419bc 100644 --- a/datetime_test.go +++ b/datetime_test.go @@ -25,6 +25,8 @@ func TestDateTime_Failed(t *testing.T) { value.Le(tm) value.InRange(tm, tm) value.NotInRange(tm, tm) + value.InList(tm, tm) + value.NotInList(tm, tm) value.Zone() value.Year() value.Month() @@ -244,3 +246,57 @@ func TestDateTime_InRange(t *testing.T) { value.chain.assertNotFailed(t) value.chain.clearFailed() } + +func TestDateTime_InList(t *testing.T) { + reporter := newMockReporter(t) + + value := NewDateTime(reporter, time.Unix(0, 1234)) + + value.InList(time.Unix(0, 1234), time.Unix(0, 1234)) + value.chain.assertNotFailed(t) + value.chain.clearFailed() + + value.NotInList(time.Unix(0, 1234), time.Unix(0, 1234)) + value.chain.assertFailed(t) + value.chain.clearFailed() + + value.InList(time.Unix(0, 1234-1), time.Unix(0, 1234)) + value.chain.assertNotFailed(t) + value.chain.clearFailed() + + value.NotInList(time.Unix(0, 1234-1), time.Unix(0, 1234)) + value.chain.assertFailed(t) + value.chain.clearFailed() + + value.InList(time.Unix(0, 1234), time.Unix(0, 1234+1)) + value.chain.assertNotFailed(t) + value.chain.clearFailed() + + value.NotInList(time.Unix(0, 1234), time.Unix(0, 1234+1)) + value.chain.assertFailed(t) + value.chain.clearFailed() + + value.InList(time.Unix(0, 1234+1), time.Unix(0, 1234+2)) + value.chain.assertFailed(t) + value.chain.clearFailed() + + value.NotInList(time.Unix(0, 1234+1), time.Unix(0, 1234+2)) + value.chain.assertNotFailed(t) + value.chain.clearFailed() + + value.InList(time.Unix(0, 1234-2), time.Unix(0, 1234-1)) + value.chain.assertFailed(t) + value.chain.clearFailed() + + value.NotInList(time.Unix(0, 1234-2), time.Unix(0, 1234-1)) + value.chain.assertNotFailed(t) + value.chain.clearFailed() + + value.InList(time.Unix(0, 1234+1), time.Unix(0, 1234-1)) + value.chain.assertFailed(t) + value.chain.clearFailed() + + value.NotInList(time.Unix(0, 1234+1), time.Unix(0, 1234-1)) + value.chain.assertNotFailed(t) + value.chain.clearFailed() +} From ac54f6f1efc65f2716583dae61a1c7abf20ee26a Mon Sep 17 00:00:00 2001 From: Fahim Bagar Date: Tue, 31 Jan 2023 02:24:33 +0700 Subject: [PATCH 06/38] Add InList and NotInList for Boolean --- boolean.go | 71 +++++++++++++++++++++++++++++++++++++++++++++++++ boolean_test.go | 18 +++++++++++++ 2 files changed, 89 insertions(+) diff --git a/boolean.go b/boolean.go index 86120b15d..f74799a0b 100644 --- a/boolean.go +++ b/boolean.go @@ -162,6 +162,77 @@ func (b *Boolean) Equal(value bool) *Boolean { return b.IsEqual(value) } +// InList succeeds if boolean is listed by given [values...]. +// +// Example: +// +// boolean := NewBoolean(t, true) +// boolean.InList(true, false) +func (b *Boolean) InList(values ...bool) *Boolean { + opChain := b.chain.enter("InList()") + defer opChain.leave() + + if opChain.failed() { + return b + } + + for _, v := range values { + if b.value == v { + return b + } + } + + opChain.fail(AssertionFailure{ + Type: AssertBelongs, + Actual: &AssertionValue{b.value}, + Expected: &AssertionValue{AssertionList(boolList(values))}, + Errors: []error{ + errors.New("expected: boolean is listed"), + }, + }) + + return b +} + +// NotInList succeeds if boolean is not listed by given [values...]. +// +// Example: +// +// boolean := NewBoolean(t, true) +// boolean.NotInList(true, false) +func (b *Boolean) NotInList(values ...bool) *Boolean { + opChain := b.chain.enter("NotInList()") + defer opChain.leave() + + if opChain.failed() { + return b + } + + for _, v := range values { + if b.value == v { + opChain.fail(AssertionFailure{ + Type: AssertNotBelongs, + Actual: &AssertionValue{b.value}, + Expected: &AssertionValue{AssertionList(boolList(values))}, + Errors: []error{ + errors.New("expected: boolean is not listed"), + }, + }) + } + } + + return b +} + +func boolList(values []bool) []interface{} { + l := make([]interface{}, 0, len(values)) + for _, v := range values { + l = append(l, v) + } + + return l +} + // True succeeds if boolean is true. // // Example: diff --git a/boolean_test.go b/boolean_test.go index 4d52c2cb1..5a1ac4d05 100644 --- a/boolean_test.go +++ b/boolean_test.go @@ -21,6 +21,8 @@ func TestBoolean_Failed(t *testing.T) { value.IsEqual(false) value.NotEqual(false) + value.InList(false) + value.NotInList(false) value.True() value.False() } @@ -160,6 +162,14 @@ func TestBoolean_True(t *testing.T) { value.False() value.chain.assertFailed(t) value.chain.clearFailed() + + value.InList(true, true) + value.chain.assertNotFailed(t) + value.chain.clearFailed() + + value.NotInList(true, false) + value.chain.assertFailed(t) + value.chain.clearFailed() } func TestBoolean_False(t *testing.T) { @@ -192,4 +202,12 @@ func TestBoolean_False(t *testing.T) { value.False() value.chain.assertNotFailed(t) value.chain.clearFailed() + + value.InList(true, true) + value.chain.assertFailed(t) + value.chain.clearFailed() + + value.NotInList(true, true) + value.chain.assertNotFailed(t) + value.chain.clearFailed() } From ae61ab3573ac211acd32d35ebca9a516998d2ae5 Mon Sep 17 00:00:00 2001 From: Fahim Bagar Date: Tue, 31 Jan 2023 02:49:11 +0700 Subject: [PATCH 07/38] Add InList and NotInList for Value --- value.go | 76 +++++++++++++++++++++++++++++++++++++++++++++++++++ value_test.go | 33 ++++++++++++++++++++++ 2 files changed, 109 insertions(+) diff --git a/value.go b/value.go index 19632d569..24a9160fb 100644 --- a/value.go +++ b/value.go @@ -523,3 +523,79 @@ func (v *Value) NotEqual(value interface{}) *Value { func (v *Value) Equal(value interface{}) *Value { return v.IsEqual(value) } + +// InList succeeds if value is listed by given [values....] +// (e.g. map, slice, string, etc). +// Before comparison, both values are converted to canonical form. +// +// Example: +// +// value := NewValue(t, "foo") +// value.InList("foo", map[string]interface{}{"bar": true}) +func (v *Value) InList(values ...interface{}) *Value { + opChain := v.chain.enter("InList()") + defer opChain.leave() + + if opChain.failed() { + return v + } + + for _, val := range values { + expected, ok := canonValue(opChain, val) + if !ok { + return v + } + + if reflect.DeepEqual(expected, v.value) { + return v + } + } + + opChain.fail(AssertionFailure{ + Type: AssertBelongs, + Actual: &AssertionValue{v.value}, + Expected: &AssertionValue{AssertionList(values)}, + Errors: []error{ + errors.New("expected: value is listed"), + }, + }) + + return v +} + +// NotInList succeeds if value is not listed by given [values....] +// (e.g. map, slice, string, etc). +// Before comparison, both values are converted to canonical form. +// +// Example: +// +// value := NewValue(t, "foo") +// value.NotInList("bar", map[string]interface{}{"bar": true}) +func (v *Value) NotInList(values ...interface{}) *Value { + opChain := v.chain.enter("NotInList()") + defer opChain.leave() + + if opChain.failed() { + return v + } + + for _, val := range values { + expected, ok := canonValue(opChain, val) + if !ok { + return v + } + + if reflect.DeepEqual(expected, v.value) { + opChain.fail(AssertionFailure{ + Type: AssertNotBelongs, + Actual: &AssertionValue{v.value}, + Expected: &AssertionValue{AssertionList(values)}, + Errors: []error{ + errors.New("expected: value is not listed"), + }, + }) + } + } + + return v +} diff --git a/value_test.go b/value_test.go index eb684bb7a..4e9e10ff1 100644 --- a/value_test.go +++ b/value_test.go @@ -42,6 +42,9 @@ func TestValue_Failed(t *testing.T) { value.IsEqual(nil) value.NotEqual(nil) + + value.InList(nil) + value.NotInList(nil) } func TestValue_Constructors(t *testing.T) { @@ -406,6 +409,36 @@ func TestValue_Equal(t *testing.T) { NewValue(reporter, data1).NotEqual(func() {}).chain.assertFailed(t) } +func TestValue_InList(t *testing.T) { + reporter := newMockReporter(t) + + data1 := map[string]interface{}{"foo": "bar"} + data2 := "baz" + data3 := struct { + Data []int `json:"data"` + }{ + Data: []int{1, 2, 3, 4}, + } + + NewValue(reporter, data1).InList(data1, data3).chain.assertNotFailed(t) + NewValue(reporter, data2).NotInList(data1, data3).chain.assertNotFailed(t) + + NewValue(reporter, data1).InList(data2, data3).chain.assertFailed(t) + NewValue(reporter, data2).NotInList(data2, data3).chain.assertFailed(t) + + NewValue(reporter, data1).InList(data2).chain.assertFailed(t) + NewValue(reporter, data2).NotInList(data2).chain.assertFailed(t) + + NewValue(reporter, data1).InList(data1).chain.assertNotFailed(t) + NewValue(reporter, data2).NotInList(data1).chain.assertNotFailed(t) + + NewValue(reporter, nil).InList(map[string]interface{}(nil)).chain.assertNotFailed(t) + NewValue(reporter, nil).NotInList(map[string]interface{}{}).chain.assertNotFailed(t) + + NewValue(reporter, data1).InList(func() {}).chain.assertFailed(t) + NewValue(reporter, data1).NotInList(func() {}).chain.assertFailed(t) +} + func TestValue_PathObject(t *testing.T) { reporter := newMockReporter(t) From 31ba96d55d3e510839f2a747cf2d04bbd230b877 Mon Sep 17 00:00:00 2001 From: Fahim Bagar Date: Tue, 31 Jan 2023 01:48:09 +0700 Subject: [PATCH 08/38] Add InList and NotInList for Array --- array.go | 108 ++++++++++++++++++++++++++++++++++++++++++++++++++ array_test.go | 58 +++++++++++++++++++++++++++ 2 files changed, 166 insertions(+) diff --git a/array.go b/array.go index 4b9a33a98..6ea8d8754 100644 --- a/array.go +++ b/array.go @@ -746,6 +746,114 @@ func (a *Array) Equal(value interface{}) *Array { return a.IsEqual(value) } +// InList succeeds if array is listed by given [values...]. +// Before comparison, both array and value are converted to canonical form. +// +// values should be an array of slice of any type. This comparison adheres +// element order. +// +// Example: +// +// array := NewArray(t, []interface{}{"foo", 123}) +// array.InList([]interface{}{"foo", 123}) +// +// array := NewArray(t, []interface{}{"foo", "bar"}) +// array.InList([]string{}{"foo", "bar"}) +// +// array := NewArray(t, []interface{}{123, 456}) +// array.InList([]int{}{123, 456}) +func (a *Array) InList(values ...interface{}) *Array { + opChain := a.chain.enter("InList()") + defer opChain.leave() + + if opChain.failed() { + return a + } + + if len(values) <= 1 { + return a.IsEqual(values[0]) + } + + var isListed bool + for _, v := range values { + expected, ok := canonArray(opChain, v) + if !ok { + return a + } + + if reflect.DeepEqual(expected, a.value) { + isListed = true + } + } + + if !isListed { + opChain.fail(AssertionFailure{ + Type: AssertBelongs, + Actual: &AssertionValue{a.value}, + Expected: &AssertionValue{AssertionList(values)}, + Errors: []error{ + errors.New("expected: arrays are listed"), + }, + }) + } + + return a +} + +// NotInList succeeds if array is not listed by given [values...]. +// Before comparison, both array and value are converted to canonical form. +// +// values should be an array of slice of any type. This comparison adheres +// element order. +// +// Example: +// +// array := NewArray(t, []interface{}{"foo", 123}) +// array.NotInList([]interface{}{"bar", 456}) +// +// array := NewArray(t, []interface{}{"foo", "bar"}) +// array.NotInList([]string{}{"bar", "foo"}) +// +// array := NewArray(t, []interface{}{123, 456}) +// array.NotInList([]int{}{789, 901}) +func (a *Array) NotInList(values ...interface{}) *Array { + opChain := a.chain.enter("NotInList()") + defer opChain.leave() + + if opChain.failed() { + return a + } + + if len(values) <= 1 { + return a.NotEqual(values[0]) + } + + var isListed bool + for _, v := range values { + expected, ok := canonArray(opChain, v) + if !ok { + return a + } + + if reflect.DeepEqual(expected, a.value) { + isListed = true + } + } + + if isListed { + opChain.fail(AssertionFailure{ + Type: AssertNotBelongs, + Actual: &AssertionValue{a.value}, + Expected: &AssertionValue{AssertionList(values)}, + Errors: []error{ + errors.New("expected: arrays are not listed"), + }, + }) + } + + return a +} + // IsEqualUnordered succeeds if array is equal to another array, ignoring element // order. Before comparison, both arrays are converted to canonical form. // diff --git a/array_test.go b/array_test.go index 020622933..d73f742aa 100644 --- a/array_test.go +++ b/array_test.go @@ -31,6 +31,8 @@ func TestArray_Failed(t *testing.T) { value.NotEmpty() value.IsEqual([]interface{}{}) value.NotEqual([]interface{}{}) + value.InList([]interface{}{}) + value.NotInList([]interface{}{}) value.IsEqualUnordered([]interface{}{}) value.NotEqualUnordered([]interface{}{}) value.ConsistsOf("foo") @@ -377,6 +379,62 @@ func TestArray_EqualNotEmpty(t *testing.T) { value.chain.clearFailed() } +func TestArray_InList(t *testing.T) { + reporter := newMockReporter(t) + + value := NewArray(reporter, []interface{}{"foo", "bar"}) + + assert.Equal(t, []interface{}{"foo", "bar"}, value.Raw()) + + value.InList([]interface{}{}) + value.chain.assertFailed(t) + value.chain.clearFailed() + + value.NotInList([]interface{}{}) + value.chain.assertNotFailed(t) + value.chain.clearFailed() + + value.InList([]interface{}{"foo"}) + value.chain.assertFailed(t) + value.chain.clearFailed() + + value.NotInList([]interface{}{"foo"}) + value.chain.assertNotFailed(t) + value.chain.clearFailed() + + value.InList([]interface{}{"bar", "foo"}) + value.chain.assertFailed(t) + value.chain.clearFailed() + + value.NotInList([]interface{}{"bar", "foo"}) + value.chain.assertNotFailed(t) + value.chain.clearFailed() + + value.InList([]interface{}{"bar", "foo"}, []interface{}{"foo", "bar"}) + value.chain.assertNotFailed(t) + value.chain.clearFailed() + + value.NotInList([]interface{}{"bar", "foo"}, []interface{}{"foo", "bar"}) + value.chain.assertFailed(t) + value.chain.clearFailed() + + value.InList([]interface{}{"bar", "foo"}, []interface{}{"FOO", "BAR"}) + value.chain.assertFailed(t) + value.chain.clearFailed() + + value.NotInList([]interface{}{"bar", "foo"}, []interface{}{"FOO", "BAR"}) + value.chain.assertNotFailed(t) + value.chain.clearFailed() + + value.InList([]interface{}{"foo", "bar"}, "foo") + value.chain.assertFailed(t) + value.chain.clearFailed() + + value.NotInList([]interface{}{"foo", "bar"}, "foo") + value.chain.assertFailed(t) + value.chain.clearFailed() +} + func TestArray_EqualTypes(t *testing.T) { reporter := newMockReporter(t) From 649ee8caf0e8c918aa9a2de8a8d6f6c5c3ec1cf7 Mon Sep 17 00:00:00 2001 From: Fahim Bagar Date: Tue, 31 Jan 2023 03:07:34 +0700 Subject: [PATCH 09/38] Safeguard InList and NotInList for Array --- array.go | 30 +++++++++++++++++++++--------- array_test.go | 10 +++++----- 2 files changed, 26 insertions(+), 14 deletions(-) diff --git a/array.go b/array.go index 6ea8d8754..d7f393b12 100644 --- a/array.go +++ b/array.go @@ -750,7 +750,8 @@ func (a *Array) Equal(value interface{}) *Array { // Before comparison, both array and value are converted to canonical form. // // values should be an array of slice of any type. This comparison adheres -// element order. +// element order. For values with length 1, it behaves the same way with +// IsEqual. // // Example: // @@ -770,8 +771,13 @@ func (a *Array) InList(values ...interface{}) *Array { return a } - if len(values) <= 1 { - return a.IsEqual(values[0]) + arr, ok := canonArray(opChain, values) + if !ok { + return a + } + + if len(arr) == 1 { + return a.IsEqual(arr[0]) } var isListed bool @@ -790,7 +796,7 @@ func (a *Array) InList(values ...interface{}) *Array { opChain.fail(AssertionFailure{ Type: AssertBelongs, Actual: &AssertionValue{a.value}, - Expected: &AssertionValue{AssertionList(values)}, + Expected: &AssertionValue{AssertionList(arr)}, Errors: []error{ errors.New("expected: arrays are listed"), }, @@ -804,7 +810,8 @@ func (a *Array) InList(values ...interface{}) *Array { // Before comparison, both array and value are converted to canonical form. // // values should be an array of slice of any type. This comparison adheres -// element order. +// element order. For values with length 1, it behaves the same way with +// NotEqual. // // Example: // @@ -824,12 +831,17 @@ func (a *Array) NotInList(values ...interface{}) *Array { return a } - if len(values) <= 1 { - return a.NotEqual(values[0]) + arr, ok := canonArray(opChain, values) + if !ok { + return a + } + + if len(arr) == 1 { + return a.NotEqual(arr[0]) } var isListed bool - for _, v := range values { + for _, v := range arr { expected, ok := canonArray(opChain, v) if !ok { return a @@ -844,7 +856,7 @@ func (a *Array) NotInList(values ...interface{}) *Array { opChain.fail(AssertionFailure{ Type: AssertNotBelongs, Actual: &AssertionValue{a.value}, - Expected: &AssertionValue{AssertionList(values)}, + Expected: &AssertionValue{AssertionList(arr)}, Errors: []error{ errors.New("expected: arrays are not listed"), }, diff --git a/array_test.go b/array_test.go index d73f742aa..975bbf328 100644 --- a/array_test.go +++ b/array_test.go @@ -386,19 +386,19 @@ func TestArray_InList(t *testing.T) { assert.Equal(t, []interface{}{"foo", "bar"}, value.Raw()) - value.InList([]interface{}{}) + value.InList() value.chain.assertFailed(t) value.chain.clearFailed() - value.NotInList([]interface{}{}) - value.chain.assertNotFailed(t) + value.NotInList() + value.chain.assertFailed(t) value.chain.clearFailed() - value.InList([]interface{}{"foo"}) + value.InList([]interface{}{}) value.chain.assertFailed(t) value.chain.clearFailed() - value.NotInList([]interface{}{"foo"}) + value.NotInList([]interface{}{}) value.chain.assertNotFailed(t) value.chain.clearFailed() From b06cca5905d0d5af35101b4722bd37f3b6d8ef52 Mon Sep 17 00:00:00 2001 From: Fahim Bagar Date: Tue, 31 Jan 2023 03:38:39 +0700 Subject: [PATCH 10/38] Fix linter issues --- object_test.go | 30 ++++++++++++++++++++++++------ 1 file changed, 24 insertions(+), 6 deletions(-) diff --git a/object_test.go b/object_test.go index 42417f900..30bd5d866 100644 --- a/object_test.go +++ b/object_test.go @@ -438,27 +438,45 @@ func TestObject_InList(t *testing.T) { value.chain.assertNotFailed(t) value.chain.clearFailed() - value.InList(map[string]interface{}{"FOO": 123.0}, map[string]interface{}{"BAR": 456.0}) + value.InList( + map[string]interface{}{"FOO": 123.0}, + map[string]interface{}{"BAR": 456.0}, + ) value.chain.assertFailed(t) value.chain.clearFailed() - value.NotInList(map[string]interface{}{"FOO": 123.0}, map[string]interface{}{"BAR": 456.0}) + value.NotInList( + map[string]interface{}{"FOO": 123.0}, + map[string]interface{}{"BAR": 456.0}, + ) value.chain.assertNotFailed(t) value.chain.clearFailed() - value.InList(map[string]interface{}{"foo": 456.0}, map[string]interface{}{"bar": 123.0}) + value.InList( + map[string]interface{}{"foo": 456.0}, + map[string]interface{}{"bar": 123.0}, + ) value.chain.assertFailed(t) value.chain.clearFailed() - value.NotInList(map[string]interface{}{"foo": 456.0}, map[string]interface{}{"bar": 123.0}) + value.NotInList( + map[string]interface{}{"foo": 456.0}, + map[string]interface{}{"bar": 123.0}, + ) value.chain.assertNotFailed(t) value.chain.clearFailed() - value.InList(map[string]interface{}{"foo": 123.0}, map[string]interface{}{"bar": 456.0}) + value.InList( + map[string]interface{}{"foo": 123.0}, + map[string]interface{}{"bar": 456.0}, + ) value.chain.assertNotFailed(t) value.chain.clearFailed() - value.NotInList(map[string]interface{}{"foo": 123.0}, map[string]interface{}{"bar": 456.0}) + value.NotInList( + map[string]interface{}{"foo": 123.0}, + map[string]interface{}{"bar": 456.0}, + ) value.chain.assertFailed(t) value.chain.clearFailed() From 40c6e50cc826924a245632406c46278e2c0f9a98 Mon Sep 17 00:00:00 2001 From: Fahim Bagar Date: Tue, 31 Jan 2023 03:56:47 +0700 Subject: [PATCH 11/38] Remove single slice checker from AssertionList --- assertion_test.go | 24 ------------------------ assertion_validation.go | 7 ------- 2 files changed, 31 deletions(-) diff --git a/assertion_test.go b/assertion_test.go index 765160b3b..e7d59433b 100644 --- a/assertion_test.go +++ b/assertion_test.go @@ -336,18 +336,6 @@ func TestAssertion_ValidateTraits(t *testing.T) { List: fieldRequired, }, }, - { - testName: "AssertionList should not be slice of single element", - errorContainsText: "AssertionList", - failure: AssertionFailure{ - Expected: &AssertionValue{ - Value: AssertionList{[]int{1}}, - }, - }, - traits: fieldTraits{ - List: fieldRequired, - }, - }, } for _, test := range tests { @@ -627,18 +615,6 @@ func TestAssertion_ValidateAssertion(t *testing.T) { Expected: &AssertionValue{AssertionList{}}, }, }, - { - testName: "List has one element and it's list", - errorContainsText: "AssertionList", - input: AssertionFailure{ - Type: AssertBelongs, - Errors: []error{ - errors.New("test"), - }, - Actual: &AssertionValue{}, - Expected: &AssertionValue{AssertionList{[]string{"test"}}}, - }, - }, } for _, test := range tests { diff --git a/assertion_validation.go b/assertion_validation.go index 3edcb4f09..2a9ae069b 100644 --- a/assertion_validation.go +++ b/assertion_validation.go @@ -3,7 +3,6 @@ package httpexpect import ( "errors" "fmt" - "reflect" ) func validateAssertion(failure *AssertionFailure) error { @@ -182,12 +181,6 @@ func validateTraits(failure *AssertionFailure, traits fieldTraits) error { if len(lst) == 0 { return errors.New("AssertionList should be non-empty") } - - if len(lst) == 1 && reflect.ValueOf(lst[0]).Kind() == reflect.Slice { - return errors.New( - "AssertionList should contain a list of values," + - " but it contains a single element which itself is a list") - } } } } From 1a3dd7a621fb6927e977caa6ce289f6a46e654a7 Mon Sep 17 00:00:00 2001 From: Fahim Bagar Date: Tue, 31 Jan 2023 03:57:41 +0700 Subject: [PATCH 12/38] Revert treating single array as param with IsEqual/NotEqual --- array.go | 8 -------- 1 file changed, 8 deletions(-) diff --git a/array.go b/array.go index d7f393b12..6b83639ea 100644 --- a/array.go +++ b/array.go @@ -776,10 +776,6 @@ func (a *Array) InList(values ...interface{}) *Array { return a } - if len(arr) == 1 { - return a.IsEqual(arr[0]) - } - var isListed bool for _, v := range values { expected, ok := canonArray(opChain, v) @@ -836,10 +832,6 @@ func (a *Array) NotInList(values ...interface{}) *Array { return a } - if len(arr) == 1 { - return a.NotEqual(arr[0]) - } - var isListed bool for _, v := range arr { expected, ok := canonArray(opChain, v) From 60b72adfa2ee92e70247ed70f625458f9b438afd Mon Sep 17 00:00:00 2001 From: Fahim Bagar Date: Tue, 31 Jan 2023 10:32:59 +0700 Subject: [PATCH 13/38] Zero length for Array InList NotInList --- array.go | 26 +++++++++++++++++++------- array_test.go | 8 ++++++++ 2 files changed, 27 insertions(+), 7 deletions(-) diff --git a/array.go b/array.go index 6b83639ea..05276f0a6 100644 --- a/array.go +++ b/array.go @@ -771,8 +771,14 @@ func (a *Array) InList(values ...interface{}) *Array { return a } - arr, ok := canonArray(opChain, values) - if !ok { + if len(values) == 0 { + opChain.fail(AssertionFailure{ + Type: AssertUsage, + Errors: []error{ + errors.New("unexpected empty list argument"), + }, + }) + return a } @@ -792,7 +798,7 @@ func (a *Array) InList(values ...interface{}) *Array { opChain.fail(AssertionFailure{ Type: AssertBelongs, Actual: &AssertionValue{a.value}, - Expected: &AssertionValue{AssertionList(arr)}, + Expected: &AssertionValue{AssertionList(values)}, Errors: []error{ errors.New("expected: arrays are listed"), }, @@ -827,13 +833,19 @@ func (a *Array) NotInList(values ...interface{}) *Array { return a } - arr, ok := canonArray(opChain, values) - if !ok { + if len(values) == 0 { + opChain.fail(AssertionFailure{ + Type: AssertUsage, + Errors: []error{ + errors.New("unexpected empty list argument"), + }, + }) + return a } var isListed bool - for _, v := range arr { + for _, v := range values { expected, ok := canonArray(opChain, v) if !ok { return a @@ -848,7 +860,7 @@ func (a *Array) NotInList(values ...interface{}) *Array { opChain.fail(AssertionFailure{ Type: AssertNotBelongs, Actual: &AssertionValue{a.value}, - Expected: &AssertionValue{AssertionList(arr)}, + Expected: &AssertionValue{AssertionList(values)}, Errors: []error{ errors.New("expected: arrays are not listed"), }, diff --git a/array_test.go b/array_test.go index 975bbf328..612481f63 100644 --- a/array_test.go +++ b/array_test.go @@ -394,6 +394,14 @@ func TestArray_InList(t *testing.T) { value.chain.assertFailed(t) value.chain.clearFailed() + value.InList("foo", "bar") + value.chain.assertFailed(t) + value.chain.clearFailed() + + value.NotInList("foo", "bar") + value.chain.assertFailed(t) + value.chain.clearFailed() + value.InList([]interface{}{}) value.chain.assertFailed(t) value.chain.clearFailed() From d34acb1b9d65e5a4c75a195d2cd0024f31221d3f Mon Sep 17 00:00:00 2001 From: Fahim Bagar Date: Tue, 31 Jan 2023 10:36:15 +0700 Subject: [PATCH 14/38] Zero length for Boolean InList NotInList --- boolean.go | 22 ++++++++++++++++++++++ boolean_test.go | 14 ++++++++++++++ 2 files changed, 36 insertions(+) diff --git a/boolean.go b/boolean.go index f74799a0b..7e3524ca9 100644 --- a/boolean.go +++ b/boolean.go @@ -176,6 +176,17 @@ func (b *Boolean) InList(values ...bool) *Boolean { return b } + if len(values) == 0 { + opChain.fail(AssertionFailure{ + Type: AssertUsage, + Errors: []error{ + errors.New("unexpected empty list argument"), + }, + }) + + return b + } + for _, v := range values { if b.value == v { return b @@ -208,6 +219,17 @@ func (b *Boolean) NotInList(values ...bool) *Boolean { return b } + if len(values) == 0 { + opChain.fail(AssertionFailure{ + Type: AssertUsage, + Errors: []error{ + errors.New("unexpected empty list argument"), + }, + }) + + return b + } + for _, v := range values { if b.value == v { opChain.fail(AssertionFailure{ diff --git a/boolean_test.go b/boolean_test.go index 5a1ac4d05..11e7ddba9 100644 --- a/boolean_test.go +++ b/boolean_test.go @@ -211,3 +211,17 @@ func TestBoolean_False(t *testing.T) { value.chain.assertNotFailed(t) value.chain.clearFailed() } + +func TestBoolean_ZeroLengthInList(t *testing.T) { + reporter := newMockReporter(t) + + value := NewBoolean(reporter, true) + + value.InList() + value.chain.assertFailed(t) + value.chain.clearFailed() + + value.NotInList() + value.chain.assertFailed(t) + value.chain.clearFailed() +} From bfd5e5f378f6be4fd980790c60b13a33b5ef1988 Mon Sep 17 00:00:00 2001 From: Fahim Bagar Date: Tue, 31 Jan 2023 10:43:25 +0700 Subject: [PATCH 15/38] Zero length for Duration InList NotInList --- duration.go | 22 ++++++++++++++++++++++ duration_test.go | 12 ++++++++++-- 2 files changed, 32 insertions(+), 2 deletions(-) diff --git a/duration.go b/duration.go index 145e2b7f5..fe0d554f7 100644 --- a/duration.go +++ b/duration.go @@ -444,6 +444,17 @@ func (d *Duration) InList(values ...time.Duration) *Duration { return d } + if len(values) == 0 { + opChain.fail(AssertionFailure{ + Type: AssertUsage, + Errors: []error{ + errors.New("unexpected empty list argument"), + }, + }) + + return d + } + if d.value == nil { opChain.fail(AssertionFailure{ Type: AssertNotNil, @@ -487,6 +498,17 @@ func (d *Duration) NotInList(values ...time.Duration) *Duration { return d } + if len(values) == 0 { + opChain.fail(AssertionFailure{ + Type: AssertUsage, + Errors: []error{ + errors.New("unexpected empty list argument"), + }, + }) + + return d + } + if d.value == nil { opChain.fail(AssertionFailure{ Type: AssertNotNil, diff --git a/duration_test.go b/duration_test.go index 7c64ad26a..4144736be 100644 --- a/duration_test.go +++ b/duration_test.go @@ -229,11 +229,19 @@ func TestDuration_InRange(t *testing.T) { func TestDuration_InList(t *testing.T) { reporter := newMockReporter(t) - newDuration(newMockChain(t), nil).InList().chain.assertFailed(t) - newDuration(newMockChain(t), nil).NotInList().chain.assertFailed(t) + newDuration(newMockChain(t), nil).InList(time.Second).chain.assertFailed(t) + newDuration(newMockChain(t), nil).NotInList(time.Second).chain.assertFailed(t) value := NewDuration(reporter, time.Second) + value.InList() + value.chain.assertFailed(t) + value.chain.clearFailed() + + value.NotInList() + value.chain.assertFailed(t) + value.chain.clearFailed() + value.InList(time.Second, time.Minute) value.chain.assertNotFailed(t) value.chain.clearFailed() From 97342686c73b09485d45d842d828faaaf4db61da Mon Sep 17 00:00:00 2001 From: Fahim Bagar Date: Tue, 31 Jan 2023 10:48:38 +0700 Subject: [PATCH 16/38] Zero length for DateTime InList NotInList --- datetime.go | 22 ++++++++++++++++++++++ datetime_test.go | 8 ++++++++ 2 files changed, 30 insertions(+) diff --git a/datetime.go b/datetime.go index 1794aa1e5..bc9d0190a 100644 --- a/datetime.go +++ b/datetime.go @@ -436,6 +436,17 @@ func (dt *DateTime) InList(values ...time.Time) *DateTime { return dt } + if len(values) == 0 { + opChain.fail(AssertionFailure{ + Type: AssertUsage, + Errors: []error{ + errors.New("unexpected empty list argument"), + }, + }) + + return dt + } + for _, v := range values { if dt.value.Equal(v) { return dt @@ -468,6 +479,17 @@ func (dt *DateTime) NotInList(values ...time.Time) *DateTime { return dt } + if len(values) == 0 { + opChain.fail(AssertionFailure{ + Type: AssertUsage, + Errors: []error{ + errors.New("unexpected empty list argument"), + }, + }) + + return dt + } + for _, v := range values { if dt.value.Equal(v) { opChain.fail(AssertionFailure{ diff --git a/datetime_test.go b/datetime_test.go index fe6a419bc..8da670225 100644 --- a/datetime_test.go +++ b/datetime_test.go @@ -252,6 +252,14 @@ func TestDateTime_InList(t *testing.T) { value := NewDateTime(reporter, time.Unix(0, 1234)) + value.InList() + value.chain.assertFailed(t) + value.chain.clearFailed() + + value.NotInList() + value.chain.assertFailed(t) + value.chain.clearFailed() + value.InList(time.Unix(0, 1234), time.Unix(0, 1234)) value.chain.assertNotFailed(t) value.chain.clearFailed() From b885435c99ef42ba2d7c82b34021ef29f6de229c Mon Sep 17 00:00:00 2001 From: Fahim Bagar Date: Tue, 31 Jan 2023 10:52:14 +0700 Subject: [PATCH 17/38] Zero length for Number InList NotInList --- number.go | 26 ++++++++++++++++++++++---- number_test.go | 8 ++++++++ 2 files changed, 30 insertions(+), 4 deletions(-) diff --git a/number.go b/number.go index 7e6310330..7972b4a5c 100644 --- a/number.go +++ b/number.go @@ -383,10 +383,19 @@ func (n *Number) InList(values ...interface{}) *Number { return n } - arr, _ := canonArray(opChain, values) + if len(values) == 0 { + opChain.fail(AssertionFailure{ + Type: AssertUsage, + Errors: []error{ + errors.New("unexpected empty list argument"), + }, + }) + + return n + } var isListed bool - for _, v := range arr { + for _, v := range values { num, ok := canonNumber(opChain, v) if !ok { return n @@ -428,10 +437,19 @@ func (n *Number) NotInList(values ...interface{}) *Number { return n } - arr, _ := canonArray(opChain, values) + if len(values) == 0 { + opChain.fail(AssertionFailure{ + Type: AssertUsage, + Errors: []error{ + errors.New("unexpected empty list argument"), + }, + }) + + return n + } var isListed bool - for _, v := range arr { + for _, v := range values { num, ok := canonNumber(opChain, v) if !ok { return n diff --git a/number_test.go b/number_test.go index 522fa992e..6f7f0ec6d 100644 --- a/number_test.go +++ b/number_test.go @@ -316,6 +316,14 @@ func TestNumber_InList(t *testing.T) { value := NewNumber(reporter, 1234) + value.InList() + value.chain.assertFailed(t) + value.chain.clearFailed() + + value.NotInList() + value.chain.assertFailed(t) + value.chain.clearFailed() + value.InList(1234, 4567) value.chain.assertNotFailed(t) value.chain.clearFailed() From b7fc213ce722a152f674015a898290fe3475e965 Mon Sep 17 00:00:00 2001 From: Fahim Bagar Date: Tue, 31 Jan 2023 10:55:47 +0700 Subject: [PATCH 18/38] Zero length for Object InList NotInList --- object.go | 26 ++++++++++++++++++++++---- object_test.go | 8 ++++++++ 2 files changed, 30 insertions(+), 4 deletions(-) diff --git a/object.go b/object.go index 5114fb4e0..bc9e4fa44 100644 --- a/object.go +++ b/object.go @@ -761,10 +761,19 @@ func (o *Object) InList(values ...interface{}) *Object { return o } - arr, _ := canonArray(opChain, values) + if len(values) == 0 { + opChain.fail(AssertionFailure{ + Type: AssertUsage, + Errors: []error{ + errors.New("unexpected empty list argument"), + }, + }) + + return o + } var isListed bool - for _, v := range arr { + for _, v := range values { expected, ok := canonMap(opChain, v) if !ok { return o @@ -806,10 +815,19 @@ func (o *Object) NotInList(values ...interface{}) *Object { return o } - arr, _ := canonArray(opChain, values) + if len(values) == 0 { + opChain.fail(AssertionFailure{ + Type: AssertUsage, + Errors: []error{ + errors.New("unexpected empty list argument"), + }, + }) + + return o + } var isListed bool - for _, v := range arr { + for _, v := range values { expected, ok := canonMap(opChain, v) if !ok { return o diff --git a/object_test.go b/object_test.go index 30bd5d866..aa73b98b5 100644 --- a/object_test.go +++ b/object_test.go @@ -430,6 +430,14 @@ func TestObject_InList(t *testing.T) { assert.Equal(t, map[string]interface{}{"foo": 123.0}, value.Raw()) + value.InList() + value.chain.assertFailed(t) + value.chain.clearFailed() + + value.NotInList() + value.chain.assertFailed(t) + value.chain.clearFailed() + value.InList(map[string]interface{}{}) value.chain.assertFailed(t) value.chain.clearFailed() From 36a20f9fe17698eea5af589e92e19f3c7acf358b Mon Sep 17 00:00:00 2001 From: Fahim Bagar Date: Tue, 31 Jan 2023 10:57:11 +0700 Subject: [PATCH 19/38] Zero length for String InList NotInList --- string.go | 22 ++++++++++++++++++++++ string_test.go | 8 ++++++++ 2 files changed, 30 insertions(+) diff --git a/string.go b/string.go index a394f3335..a0cefb37a 100644 --- a/string.go +++ b/string.go @@ -259,6 +259,17 @@ func (s *String) InList(values ...string) *String { 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 s.value == v { return s @@ -291,6 +302,17 @@ func (s *String) NotInList(values ...string) *String { 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 s.value == v { opChain.fail(AssertionFailure{ diff --git a/string_test.go b/string_test.go index 2907cae43..e2a43d6dd 100644 --- a/string_test.go +++ b/string_test.go @@ -227,6 +227,14 @@ func TestString_List(t *testing.T) { assert.Equal(t, "foo", value.Raw()) + value.InList() + value.chain.assertFailed(t) + value.chain.clearFailed() + + value.InList() + value.chain.assertFailed(t) + value.chain.clearFailed() + value.InList("foo", "bar") value.chain.assertNotFailed(t) value.chain.clearFailed() From f0cc4aa033e29f31d30997ba2a94d0eb5f5e089f Mon Sep 17 00:00:00 2001 From: Fahim Bagar Date: Tue, 31 Jan 2023 10:59:22 +0700 Subject: [PATCH 20/38] Zero length for Value InList NotInList --- value.go | 22 ++++++++++++++++++++++ value_test.go | 3 +++ 2 files changed, 25 insertions(+) diff --git a/value.go b/value.go index 24a9160fb..9ab121657 100644 --- a/value.go +++ b/value.go @@ -540,6 +540,17 @@ func (v *Value) InList(values ...interface{}) *Value { return v } + if len(values) == 0 { + opChain.fail(AssertionFailure{ + Type: AssertUsage, + Errors: []error{ + errors.New("unexpected empty list argument"), + }, + }) + + return v + } + for _, val := range values { expected, ok := canonValue(opChain, val) if !ok { @@ -579,6 +590,17 @@ func (v *Value) NotInList(values ...interface{}) *Value { return v } + if len(values) == 0 { + opChain.fail(AssertionFailure{ + Type: AssertUsage, + Errors: []error{ + errors.New("unexpected empty list argument"), + }, + }) + + return v + } + for _, val := range values { expected, ok := canonValue(opChain, val) if !ok { diff --git a/value_test.go b/value_test.go index 4e9e10ff1..5d0e6106f 100644 --- a/value_test.go +++ b/value_test.go @@ -420,6 +420,9 @@ func TestValue_InList(t *testing.T) { Data: []int{1, 2, 3, 4}, } + NewValue(reporter, data1).InList().chain.assertFailed(t) + NewValue(reporter, data2).NotInList().chain.assertFailed(t) + NewValue(reporter, data1).InList(data1, data3).chain.assertNotFailed(t) NewValue(reporter, data2).NotInList(data1, data3).chain.assertNotFailed(t) From 4d9d21bfc2a37a5488f2644bc4804b684b5bb815 Mon Sep 17 00:00:00 2001 From: Fahim Bagar Date: Tue, 31 Jan 2023 11:11:43 +0700 Subject: [PATCH 21/38] Remove unneeded comment in Array --- array.go | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/array.go b/array.go index 05276f0a6..98355e1df 100644 --- a/array.go +++ b/array.go @@ -750,8 +750,7 @@ func (a *Array) Equal(value interface{}) *Array { // Before comparison, both array and value are converted to canonical form. // // values should be an array of slice of any type. This comparison adheres -// element order. For values with length 1, it behaves the same way with -// IsEqual. +// element order. // // Example: // @@ -812,8 +811,7 @@ func (a *Array) InList(values ...interface{}) *Array { // Before comparison, both array and value are converted to canonical form. // // values should be an array of slice of any type. This comparison adheres -// element order. For values with length 1, it behaves the same way with -// NotEqual. +// element order. // // Example: // From d5f93d24d655b9c3c3717a8871ab0e6cad9eeabc Mon Sep 17 00:00:00 2001 From: Fahim Bagar Date: Wed, 1 Feb 2023 17:58:24 +0700 Subject: [PATCH 22/38] Fix comment and error message in Array --- array.go | 38 ++++++++++++++------------------------ 1 file changed, 14 insertions(+), 24 deletions(-) diff --git a/array.go b/array.go index 98355e1df..13e14fc02 100644 --- a/array.go +++ b/array.go @@ -746,22 +746,17 @@ func (a *Array) Equal(value interface{}) *Array { return a.IsEqual(value) } -// InList succeeds if array is listed by given [values...]. -// Before comparison, both array and value are converted to canonical form. +// InList succeeds if whole array is equal to one of the elements from given +// list of arrays. +// Before comparison, both array and each value are converted to canonical +// form. // -// values should be an array of slice of any type. This comparison adheres -// element order. +// Each value should be a slice of any type. // // Example: // // array := NewArray(t, []interface{}{"foo", 123}) -// array.InList([]interface{}{"foo", 123}) -// -// array := NewArray(t, []interface{}{"foo", "bar"}) -// array.InList([]string{}{"foo", "bar"}) -// -// array := NewArray(t, []interface{}{123, 456}) -// array.InList([]int{}{123, 456}) +// array.InList([]interface{}{"foo", 123}, []interface{}{"bar", "456"}) func (a *Array) InList(values ...interface{}) *Array { opChain := a.chain.enter("InList()") defer opChain.leave() @@ -799,7 +794,7 @@ func (a *Array) InList(values ...interface{}) *Array { Actual: &AssertionValue{a.value}, Expected: &AssertionValue{AssertionList(values)}, Errors: []error{ - errors.New("expected: arrays are listed"), + errors.New("expected: arrays is equal to one of the values"), }, }) } @@ -807,22 +802,17 @@ func (a *Array) InList(values ...interface{}) *Array { return a } -// NotInList succeeds if array is not listed by given [values...]. -// Before comparison, both array and value are converted to canonical form. +// NotInList succeeds if whole array is not equal to any of the elements +// from given list of arrays. +// Before comparison, both array and each value are converted to canonical +// form. // -// values should be an array of slice of any type. This comparison adheres -// element order. +// Each value should be a slice of any type. // // Example: // // array := NewArray(t, []interface{}{"foo", 123}) -// array.NotInList([]interface{}{"bar", 456}) -// -// array := NewArray(t, []interface{}{"foo", "bar"}) -// array.NotInList([]string{}{"bar", "foo"}) -// -// array := NewArray(t, []interface{}{123, 456}) -// array.NotInList([]int{}{789, 901}) +// array.NotInList([]interface{}{"bar", 456}, []interface{}{"baz", "foo"}) func (a *Array) NotInList(values ...interface{}) *Array { opChain := a.chain.enter("NotInList()") defer opChain.leave() @@ -860,7 +850,7 @@ func (a *Array) NotInList(values ...interface{}) *Array { Actual: &AssertionValue{a.value}, Expected: &AssertionValue{AssertionList(values)}, Errors: []error{ - errors.New("expected: arrays are not listed"), + errors.New("expected: arrays is not equal to any of the values"), }, }) } From 826d0cd3b7f1fa2e6a49c3c0dd544ae23e9b44cf Mon Sep 17 00:00:00 2001 From: Fahim Bagar Date: Wed, 1 Feb 2023 18:00:23 +0700 Subject: [PATCH 23/38] Early return for failed case in Array --- array.go | 23 ++++++++++------------- array_test.go | 8 -------- 2 files changed, 10 insertions(+), 21 deletions(-) diff --git a/array.go b/array.go index 13e14fc02..9d3d9463f 100644 --- a/array.go +++ b/array.go @@ -785,6 +785,7 @@ func (a *Array) InList(values ...interface{}) *Array { if reflect.DeepEqual(expected, a.value) { isListed = true + break } } @@ -832,7 +833,6 @@ func (a *Array) NotInList(values ...interface{}) *Array { return a } - var isListed bool for _, v := range values { expected, ok := canonArray(opChain, v) if !ok { @@ -840,21 +840,18 @@ func (a *Array) NotInList(values ...interface{}) *Array { } if reflect.DeepEqual(expected, a.value) { - isListed = true + opChain.fail(AssertionFailure{ + Type: AssertNotBelongs, + Actual: &AssertionValue{a.value}, + Expected: &AssertionValue{AssertionList(values)}, + Errors: []error{ + errors.New("expected: arrays is not equal to any of the values"), + }, + }) + break } } - if isListed { - opChain.fail(AssertionFailure{ - Type: AssertNotBelongs, - Actual: &AssertionValue{a.value}, - Expected: &AssertionValue{AssertionList(values)}, - Errors: []error{ - errors.New("expected: arrays is not equal to any of the values"), - }, - }) - } - return a } diff --git a/array_test.go b/array_test.go index 612481f63..dc58c00b8 100644 --- a/array_test.go +++ b/array_test.go @@ -433,14 +433,6 @@ func TestArray_InList(t *testing.T) { value.NotInList([]interface{}{"bar", "foo"}, []interface{}{"FOO", "BAR"}) value.chain.assertNotFailed(t) value.chain.clearFailed() - - value.InList([]interface{}{"foo", "bar"}, "foo") - value.chain.assertFailed(t) - value.chain.clearFailed() - - value.NotInList([]interface{}{"foo", "bar"}, "foo") - value.chain.assertFailed(t) - value.chain.clearFailed() } func TestArray_EqualTypes(t *testing.T) { From 9830403939848ef8d24410598b1b9fb3b23b9d41 Mon Sep 17 00:00:00 2001 From: Fahim Bagar Date: Wed, 1 Feb 2023 18:09:34 +0700 Subject: [PATCH 24/38] Fix comment and error message in Boolean --- boolean.go | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/boolean.go b/boolean.go index 7e3524ca9..c071f691a 100644 --- a/boolean.go +++ b/boolean.go @@ -162,7 +162,8 @@ func (b *Boolean) Equal(value bool) *Boolean { return b.IsEqual(value) } -// InList succeeds if boolean is listed by given [values...]. +// InList succeeds if boolean is equal to one of the elements from given +// list of booleans. // // Example: // @@ -198,14 +199,15 @@ func (b *Boolean) InList(values ...bool) *Boolean { Actual: &AssertionValue{b.value}, Expected: &AssertionValue{AssertionList(boolList(values))}, Errors: []error{ - errors.New("expected: boolean is listed"), + errors.New("expected: boolean is equal to one of the values"), }, }) return b } -// NotInList succeeds if boolean is not listed by given [values...]. +// NotInList succeeds if boolean is not equal to any of the elements from +// given list of booleans. // // Example: // @@ -237,7 +239,7 @@ func (b *Boolean) NotInList(values ...bool) *Boolean { Actual: &AssertionValue{b.value}, Expected: &AssertionValue{AssertionList(boolList(values))}, Errors: []error{ - errors.New("expected: boolean is not listed"), + errors.New("expected: boolean is not equal to any of the values"), }, }) } From a36fc2dd7149608a7dad77db41ebe3b733b9994d Mon Sep 17 00:00:00 2001 From: Fahim Bagar Date: Wed, 1 Feb 2023 18:17:16 +0700 Subject: [PATCH 25/38] Early return for failed case in Boolean --- boolean.go | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/boolean.go b/boolean.go index c071f691a..b13f85fbd 100644 --- a/boolean.go +++ b/boolean.go @@ -188,20 +188,24 @@ func (b *Boolean) InList(values ...bool) *Boolean { return b } + var isListed bool for _, v := range values { if b.value == v { - return b + isListed = true + break } } - opChain.fail(AssertionFailure{ - Type: AssertBelongs, - Actual: &AssertionValue{b.value}, - Expected: &AssertionValue{AssertionList(boolList(values))}, - Errors: []error{ - errors.New("expected: boolean is equal to one of the values"), - }, - }) + if !isListed { + opChain.fail(AssertionFailure{ + Type: AssertBelongs, + Actual: &AssertionValue{b.value}, + Expected: &AssertionValue{AssertionList(boolList(values))}, + Errors: []error{ + errors.New("expected: boolean is equal to one of the values"), + }, + }) + } return b } @@ -242,6 +246,7 @@ func (b *Boolean) NotInList(values ...bool) *Boolean { errors.New("expected: boolean is not equal to any of the values"), }, }) + break } } From 2994100a3e06b2d53cc369c265c769f7aa67d214 Mon Sep 17 00:00:00 2001 From: Fahim Bagar Date: Wed, 1 Feb 2023 18:21:57 +0700 Subject: [PATCH 26/38] Fix comment and error message in DateTime --- datetime.go | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/datetime.go b/datetime.go index bc9d0190a..fee553960 100644 --- a/datetime.go +++ b/datetime.go @@ -422,7 +422,8 @@ func (dt *DateTime) NotInRange(min, max time.Time) *DateTime { return dt } -// InList succeeds if DateTime is listed by given [values...]. +// InList succeeds if DateTime is equal to one of the elements from given +// list of time.Time. // // Example: // @@ -458,14 +459,15 @@ func (dt *DateTime) InList(values ...time.Time) *DateTime { Actual: &AssertionValue{dt.value}, Expected: &AssertionValue{AssertionList(timeList(values))}, Errors: []error{ - errors.New("expected: time point is listed"), + errors.New("expected: time point is equal to one of the values"), }, }) return dt } -// NotInList succeeds if DateTime is not listed by given [values...]. +// NotInList succeeds if DateTime is not equal to any of the elements from +// given list of time.Time. // // Example: // @@ -497,7 +499,7 @@ func (dt *DateTime) NotInList(values ...time.Time) *DateTime { Actual: &AssertionValue{dt.value}, Expected: &AssertionValue{AssertionList(timeList(values))}, Errors: []error{ - errors.New("expected: time point is not listed"), + errors.New("expected: time point is not equal to any of the values"), }, }) } From 892581104ad715ec40c03b4a4b3cfa76747cfeb7 Mon Sep 17 00:00:00 2001 From: Fahim Bagar Date: Wed, 1 Feb 2023 18:26:21 +0700 Subject: [PATCH 27/38] Early return for failed case in DateTime --- datetime.go | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/datetime.go b/datetime.go index fee553960..fa6af2e29 100644 --- a/datetime.go +++ b/datetime.go @@ -448,20 +448,24 @@ func (dt *DateTime) InList(values ...time.Time) *DateTime { return dt } + var isListed bool for _, v := range values { if dt.value.Equal(v) { - return dt + isListed = true + break } } - opChain.fail(AssertionFailure{ - Type: AssertBelongs, - Actual: &AssertionValue{dt.value}, - Expected: &AssertionValue{AssertionList(timeList(values))}, - Errors: []error{ - errors.New("expected: time point is equal to one of the values"), - }, - }) + if !isListed { + opChain.fail(AssertionFailure{ + Type: AssertBelongs, + Actual: &AssertionValue{dt.value}, + Expected: &AssertionValue{AssertionList(timeList(values))}, + Errors: []error{ + errors.New("expected: time point is equal to one of the values"), + }, + }) + } return dt } @@ -502,6 +506,7 @@ func (dt *DateTime) NotInList(values ...time.Time) *DateTime { errors.New("expected: time point is not equal to any of the values"), }, }) + break } } From 96ac9d577c50f0604bc3ad423bc61d2b91266ac7 Mon Sep 17 00:00:00 2001 From: Fahim Bagar Date: Wed, 1 Feb 2023 18:29:57 +0700 Subject: [PATCH 28/38] Fix comment and error message in Duration --- duration.go | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/duration.go b/duration.go index fe0d554f7..c15a87cd0 100644 --- a/duration.go +++ b/duration.go @@ -430,7 +430,8 @@ func (d *Duration) NotInRange(min, max time.Duration) *Duration { return d } -// InList succeeds if Duration is listed by given duration [values...]. +// InList succeeds if Duration is equal to one of the elements from given +// list of time.Duration. // // Example: // @@ -477,14 +478,15 @@ func (d *Duration) InList(values ...time.Duration) *Duration { Actual: &AssertionValue{d.value}, Expected: &AssertionValue{AssertionList(durationList(values))}, Errors: []error{ - errors.New("expected: duration is listed"), + errors.New("expected: duration is equal to one of the values"), }, }) return d } -// NotInList succeeds if Duration is not listed by given duration [values...]. +// NotInList succeeds if Duration is not equal to any of the elements from +// given list of time.Duration. // // Example: // @@ -528,7 +530,7 @@ func (d *Duration) NotInList(values ...time.Duration) *Duration { Actual: &AssertionValue{d.value}, Expected: &AssertionValue{AssertionList(durationList(values))}, Errors: []error{ - errors.New("expected: duration is not listed"), + errors.New("expected: duration is not equal to any of the values"), }, }) } From 353398419989a66fafd8c43df6e6d3a3931e2d28 Mon Sep 17 00:00:00 2001 From: Fahim Bagar Date: Wed, 1 Feb 2023 18:31:12 +0700 Subject: [PATCH 29/38] Early return for failed case in Duration --- duration.go | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/duration.go b/duration.go index c15a87cd0..78d02e60f 100644 --- a/duration.go +++ b/duration.go @@ -467,20 +467,24 @@ func (d *Duration) InList(values ...time.Duration) *Duration { return d } + var isListed bool for _, v := range values { if *d.value == v { - return d + isListed = true + break } } - opChain.fail(AssertionFailure{ - Type: AssertBelongs, - Actual: &AssertionValue{d.value}, - Expected: &AssertionValue{AssertionList(durationList(values))}, - Errors: []error{ - errors.New("expected: duration is equal to one of the values"), - }, - }) + if !isListed { + opChain.fail(AssertionFailure{ + Type: AssertBelongs, + Actual: &AssertionValue{d.value}, + Expected: &AssertionValue{AssertionList(durationList(values))}, + Errors: []error{ + errors.New("expected: duration is equal to one of the values"), + }, + }) + } return d } @@ -533,6 +537,7 @@ func (d *Duration) NotInList(values ...time.Duration) *Duration { errors.New("expected: duration is not equal to any of the values"), }, }) + break } } From fe31cbfa0137c2b6674195112fcfa63c92300723 Mon Sep 17 00:00:00 2001 From: Fahim Bagar Date: Wed, 1 Feb 2023 18:38:48 +0700 Subject: [PATCH 30/38] Fix comment and error message in Number --- number.go | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/number.go b/number.go index 7972b4a5c..34941d8cd 100644 --- a/number.go +++ b/number.go @@ -366,10 +366,11 @@ func (n *Number) NotInRange(min, max interface{}) *Number { return n } -// InList succeeds if number is listed by given [values...]. +// InList succeeds if number is equal to one of the elements from given +// list of numbers. +// Before comparison, each value is converted to canonical form. // -// values should have array of numeric type convertible to float64. Before -// comparison, it is converted to float64. +// Each value should be numeric type convertible to float64. // // Example: // @@ -412,7 +413,7 @@ func (n *Number) InList(values ...interface{}) *Number { Actual: &AssertionValue{n.value}, Expected: &AssertionValue{AssertionList(values)}, Errors: []error{ - errors.New("expected: number is listed"), + errors.New("expected: number is equal to one of the values"), }, }) } @@ -420,10 +421,11 @@ func (n *Number) InList(values ...interface{}) *Number { return n } -// NotInList succeeds if number is listed by given [values...]. +// NotInList succeeds if whole array is not equal to any of the elements +// from given list of numbers. +// Before comparison, each value is converted to canonical form. // -// values should have array of numeric type convertible to float64. Before -// comparison, it is converted to float64. +// Each value should be numeric type convertible to float64. // // Example: // @@ -466,7 +468,7 @@ func (n *Number) NotInList(values ...interface{}) *Number { Actual: &AssertionValue{n.value}, Expected: &AssertionValue{AssertionList(values)}, Errors: []error{ - errors.New("expected: number is not listed"), + errors.New("expected: number is not equal to any of the values"), }, }) } From 00b9337f5c87f1b521893e1c06e96f34fafbc0a9 Mon Sep 17 00:00:00 2001 From: Fahim Bagar Date: Wed, 1 Feb 2023 18:39:58 +0700 Subject: [PATCH 31/38] Early return for failed case in Number --- number.go | 23 ++++++++++------------- 1 file changed, 10 insertions(+), 13 deletions(-) diff --git a/number.go b/number.go index 34941d8cd..4606fd04e 100644 --- a/number.go +++ b/number.go @@ -404,6 +404,7 @@ func (n *Number) InList(values ...interface{}) *Number { if n.value == num { isListed = true + break } } @@ -450,7 +451,6 @@ func (n *Number) NotInList(values ...interface{}) *Number { return n } - var isListed bool for _, v := range values { num, ok := canonNumber(opChain, v) if !ok { @@ -458,21 +458,18 @@ func (n *Number) NotInList(values ...interface{}) *Number { } if n.value == num { - isListed = true + opChain.fail(AssertionFailure{ + Type: AssertNotBelongs, + Actual: &AssertionValue{n.value}, + Expected: &AssertionValue{AssertionList(values)}, + Errors: []error{ + errors.New("expected: number is not equal to any of the values"), + }, + }) + break } } - if isListed { - opChain.fail(AssertionFailure{ - Type: AssertNotBelongs, - Actual: &AssertionValue{n.value}, - Expected: &AssertionValue{AssertionList(values)}, - Errors: []error{ - errors.New("expected: number is not equal to any of the values"), - }, - }) - } - return n } From a8d07c229539d4c1ae6201268d6b5a8491d0ec25 Mon Sep 17 00:00:00 2001 From: Fahim Bagar Date: Wed, 1 Feb 2023 18:45:30 +0700 Subject: [PATCH 32/38] Fix comment and error message in Object --- object.go | 28 ++++++++++++++++++---------- 1 file changed, 18 insertions(+), 10 deletions(-) diff --git a/object.go b/object.go index bc9e4fa44..2d1bfb7e0 100644 --- a/object.go +++ b/object.go @@ -744,15 +744,19 @@ func (o *Object) Equal(value interface{}) *Object { return o.IsEqual(value) } -// InList succeeds if object is listed by given [values...]. -// Before comparison, both object and value are converted to canonical form. +// InList succeeds if whole object is equal to one of the elements from given +// list of objects. +// Before comparison, each object is converted to canonical form. // -// values should be an array of map[string]interface{} or struct. +// Each value should be map[string]interface{} or struct. // // Example: // // object := NewObject(t, map[string]interface{}{"foo": 123}) -// object.InList(map[string]interface{}{"foo": 123}) +// object.InList( +// map[string]interface{}{"foo": 123}, +// map[string]interface{}{"bar": 456}, +// ) func (o *Object) InList(values ...interface{}) *Object { opChain := o.chain.enter("InList()") defer opChain.leave() @@ -790,7 +794,7 @@ func (o *Object) InList(values ...interface{}) *Object { Actual: &AssertionValue{o.value}, Expected: &AssertionValue{AssertionList(values)}, Errors: []error{ - errors.New("expected: map is listed"), + errors.New("expected: map is equal to one of the values"), }, }) } @@ -798,15 +802,19 @@ func (o *Object) InList(values ...interface{}) *Object { return o } -// NotInList succeeds if object is not listed by given [values...]. -// Before comparison, both object and value are converted to canonical form. +// NotInList succeeds if whole object is equal to any of the elements from +// given list of objects. +// Before comparison, each object is converted to canonical form. // -// values should be an array of map[string]interface{} or struct. +// Each value should be map[string]interface{} or struct. // // Example: // // object := NewObject(t, map[string]interface{}{"foo": 123}) -// object.InList(map[string]interface{}{"bar": 456}) +// object.NotInList( +// map[string]interface{}{"bar": 456}, +// map[string]interface{}{"baz": 789}, +// ) func (o *Object) NotInList(values ...interface{}) *Object { opChain := o.chain.enter("NotInList()") defer opChain.leave() @@ -844,7 +852,7 @@ func (o *Object) NotInList(values ...interface{}) *Object { Actual: &AssertionValue{o.value}, Expected: &AssertionValue{AssertionList(values)}, Errors: []error{ - errors.New("expected: map is not listed"), + errors.New("expected: map is not equal to any of the values"), }, }) } From 76e9645b7925ef6da05e7d69d714b75924e43a3f Mon Sep 17 00:00:00 2001 From: Fahim Bagar Date: Wed, 1 Feb 2023 18:46:29 +0700 Subject: [PATCH 33/38] Early return for failed case in Object --- object.go | 23 ++++++++++------------- 1 file changed, 10 insertions(+), 13 deletions(-) diff --git a/object.go b/object.go index 2d1bfb7e0..cfe21afce 100644 --- a/object.go +++ b/object.go @@ -785,6 +785,7 @@ func (o *Object) InList(values ...interface{}) *Object { if reflect.DeepEqual(expected, o.value) { isListed = true + break } } @@ -834,7 +835,6 @@ func (o *Object) NotInList(values ...interface{}) *Object { return o } - var isListed bool for _, v := range values { expected, ok := canonMap(opChain, v) if !ok { @@ -842,21 +842,18 @@ func (o *Object) NotInList(values ...interface{}) *Object { } if reflect.DeepEqual(expected, o.value) { - isListed = true + opChain.fail(AssertionFailure{ + Type: AssertNotBelongs, + Actual: &AssertionValue{o.value}, + Expected: &AssertionValue{AssertionList(values)}, + Errors: []error{ + errors.New("expected: map is not equal to any of the values"), + }, + }) + break } } - if isListed { - opChain.fail(AssertionFailure{ - Type: AssertNotBelongs, - Actual: &AssertionValue{o.value}, - Expected: &AssertionValue{AssertionList(values)}, - Errors: []error{ - errors.New("expected: map is not equal to any of the values"), - }, - }) - } - return o } From 79bb6f0774ae8f0bd9b26f9b0b4bfc1f1e0c7331 Mon Sep 17 00:00:00 2001 From: Fahim Bagar Date: Wed, 1 Feb 2023 18:48:49 +0700 Subject: [PATCH 34/38] Fix comment and error message in String --- string.go | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/string.go b/string.go index a0cefb37a..a4a4ca512 100644 --- a/string.go +++ b/string.go @@ -245,7 +245,8 @@ func (s *String) Equal(value string) *String { return s.IsEqual(value) } -// InList succeeds if string is listed by given [values...]. +// InList succeeds if string is equal to one of the elements from given +// list of strings. // // Example: // @@ -281,14 +282,15 @@ func (s *String) InList(values ...string) *String { Actual: &AssertionValue{s.value}, Expected: &AssertionValue{AssertionList(stringList(values))}, Errors: []error{ - errors.New("expected: string is listed"), + errors.New("expected: string is equal to one of the values"), }, }) return s } -// NotInList succeeds if string is not listed by given [values...]. +// NotInList succeeds if string is not equal to any of the elements from +// given list of strings. // // Example: // @@ -320,7 +322,7 @@ func (s *String) NotInList(values ...string) *String { Actual: &AssertionValue{s.value}, Expected: &AssertionValue{AssertionList(stringList(values))}, Errors: []error{ - errors.New("expected: string is not listed"), + errors.New("expected: string is not equal to any of the values"), }, }) From 3f85b49e370b8aa8148858492b7c8143b6e0d15e Mon Sep 17 00:00:00 2001 From: Fahim Bagar Date: Wed, 1 Feb 2023 18:50:10 +0700 Subject: [PATCH 35/38] Early return for failed case in String --- string.go | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/string.go b/string.go index a4a4ca512..834c5ec9f 100644 --- a/string.go +++ b/string.go @@ -271,20 +271,23 @@ func (s *String) InList(values ...string) *String { return s } + var isListed bool for _, v := range values { if s.value == v { - return s + isListed = true } } - opChain.fail(AssertionFailure{ - Type: AssertBelongs, - Actual: &AssertionValue{s.value}, - Expected: &AssertionValue{AssertionList(stringList(values))}, - Errors: []error{ - errors.New("expected: string is equal to one of the values"), - }, - }) + if !isListed { + opChain.fail(AssertionFailure{ + Type: AssertBelongs, + Actual: &AssertionValue{s.value}, + Expected: &AssertionValue{AssertionList(stringList(values))}, + Errors: []error{ + errors.New("expected: string is equal to one of the values"), + }, + }) + } return s } @@ -325,8 +328,7 @@ func (s *String) NotInList(values ...string) *String { errors.New("expected: string is not equal to any of the values"), }, }) - - return s + break } } From be043ada51a5fc7360bdc922061c778c1bf5bad2 Mon Sep 17 00:00:00 2001 From: Fahim Bagar Date: Wed, 1 Feb 2023 18:55:10 +0700 Subject: [PATCH 36/38] Fix comment and error message in Value --- value.go | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/value.go b/value.go index 9ab121657..db1ab1209 100644 --- a/value.go +++ b/value.go @@ -524,9 +524,9 @@ func (v *Value) Equal(value interface{}) *Value { return v.IsEqual(value) } -// InList succeeds if value is listed by given [values....] -// (e.g. map, slice, string, etc). -// Before comparison, both values are converted to canonical form. +// InList succeeds if whole value is equal to one of the elements from given +// list of values (e.g. map, slice, string, etc). +// Before comparison, each value are converted to canonical form. // // Example: // @@ -567,16 +567,16 @@ func (v *Value) InList(values ...interface{}) *Value { Actual: &AssertionValue{v.value}, Expected: &AssertionValue{AssertionList(values)}, Errors: []error{ - errors.New("expected: value is listed"), + errors.New("expected: value is equal to one of the values"), }, }) return v } -// NotInList succeeds if value is not listed by given [values....] -// (e.g. map, slice, string, etc). -// Before comparison, both values are converted to canonical form. +// NotInList succeeds if whole value is not equal to any of the elements from +// given list of values (e.g. map, slice, string, etc). +// Before comparison, each value are converted to canonical form. // // Example: // @@ -613,7 +613,7 @@ func (v *Value) NotInList(values ...interface{}) *Value { Actual: &AssertionValue{v.value}, Expected: &AssertionValue{AssertionList(values)}, Errors: []error{ - errors.New("expected: value is not listed"), + errors.New("expected: value is not equal to any of the values"), }, }) } From 16b39f83c70a0a85843a18081d01e4ea6dbd020a Mon Sep 17 00:00:00 2001 From: Fahim Bagar Date: Wed, 1 Feb 2023 18:56:28 +0700 Subject: [PATCH 37/38] Early return for failed case in Value --- value.go | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/value.go b/value.go index db1ab1209..c32bd1d97 100644 --- a/value.go +++ b/value.go @@ -551,6 +551,7 @@ func (v *Value) InList(values ...interface{}) *Value { return v } + var isListed bool for _, val := range values { expected, ok := canonValue(opChain, val) if !ok { @@ -558,18 +559,21 @@ func (v *Value) InList(values ...interface{}) *Value { } if reflect.DeepEqual(expected, v.value) { - return v + isListed = true + break } } - opChain.fail(AssertionFailure{ - Type: AssertBelongs, - Actual: &AssertionValue{v.value}, - Expected: &AssertionValue{AssertionList(values)}, - Errors: []error{ - errors.New("expected: value is equal to one of the values"), - }, - }) + if !isListed { + opChain.fail(AssertionFailure{ + Type: AssertBelongs, + Actual: &AssertionValue{v.value}, + Expected: &AssertionValue{AssertionList(values)}, + Errors: []error{ + errors.New("expected: value is equal to one of the values"), + }, + }) + } return v } @@ -616,6 +620,7 @@ func (v *Value) NotInList(values ...interface{}) *Value { errors.New("expected: value is not equal to any of the values"), }, }) + break } } From 0d5cba174efafb67610d8f5eda37b1fa4ea8e193 Mon Sep 17 00:00:00 2001 From: Fahim Bagar Date: Wed, 1 Feb 2023 18:56:53 +0700 Subject: [PATCH 38/38] Moved unexposed to end of file --- boolean.go | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/boolean.go b/boolean.go index b13f85fbd..ac977d0f9 100644 --- a/boolean.go +++ b/boolean.go @@ -253,15 +253,6 @@ func (b *Boolean) NotInList(values ...bool) *Boolean { return b } -func boolList(values []bool) []interface{} { - l := make([]interface{}, 0, len(values)) - for _, v := range values { - l = append(l, v) - } - - return l -} - // True succeeds if boolean is true. // // Example: @@ -317,3 +308,12 @@ func (b *Boolean) False() *Boolean { return b } + +func boolList(values []bool) []interface{} { + l := make([]interface{}, 0, len(values)) + for _, v := range values { + l = append(l, v) + } + + return l +}