Repository: diegoholiveira/jsonlogic
Pull request #55 (2022-07-22 06:22:32)

Fix less compare (#55)
close #55

codecov[bot]: # [Codecov](https://codecov.io/gh/diegoholiveira/jsonlogic/pull/55?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Diego+Oliveira) Report
> Merging [#55](https://codecov.io/gh/diegoholiveira/jsonlogic/pull/55?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Diego+Oliveira) (2ed013b) into [master](https://codecov.io/gh/diegoholiveira/jsonlogic/commit/d34d6a74a0b5c85309ef53ffe28682c96604cced?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Diego+Oliveira) (d34d6a7) will **decrease** coverage by `0.20%`.
> The diff coverage is `91.42%`.

```diff
@@            Coverage Diff             @@
##           master      #55      +/-   ##
==========================================
- Coverage   96.00%   95.79%   -0.21%     
==========================================
  Files           9        9              
  Lines         925      951      +26     
==========================================
+ Hits          888      911      +23     
- Misses         25       28       +3     
  Partials       12       12              
```


diegoholiveira: Hey @okhowang, can you please help me increase the code coverage? Thanks

Patch:
diff --git a/comp.go b/comp.go
index 678b119..34381d6 100644
--- a/comp.go
+++ b/comp.go
@@ -1,20 +1,59 @@
 package jsonlogic
 
 import (
+	"math"
 	"reflect"
+	"strconv"
 )
 
-func less(a, b interface{}) bool {
-	// comparison to a nil value is falsy
-	if a == nil || b == nil {
-		return false
+// at simulate undefined in javascript
+func at(values []interface{}, index int) interface{} {
+	if index >= 0 && index < len(values) {
+		return values[index]
+	}
+	return undefinedType{}
+}
+
+type undefinedType struct{}
+
+func toNumberForLess(v interface{}) float64 {
+	switch value := v.(type) {
+	case nil:
+		return 0
+	case undefinedType:
+		return math.NaN()
+	case float32, float64, int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64:
+		return reflect.ValueOf(value).Convert(reflect.TypeOf(float64(0))).Float()
+	case bool: // Boolean values true and false are converted to 1 and 0 respectively.
+		if value {
+			return 1
+		} else {
+			return 0
+		}
+	case string:
+		n, err := strconv.ParseFloat(value, 64)
+		switch err {
+		case strconv.ErrRange, nil:
+			return n
+		default:
+			return math.NaN()
+		}
+	default:
+		return math.NaN()
 	}
+}
 
-	if isString(a) || isString(b) {
+// less reference javascript implementation
+// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Less_than#description
+func less(a, b interface{}) bool {
+	// If both values are strings, they are compared as strings,
+	// based on the values of the Unicode code points they contain.
+	if isString(a) && isString(b) {
 		return toString(b) > toString(a)
 	}
 
-	return toNumber(b) > toNumber(a)
+	// Otherwise the values are compared as numeric values.
+	return toNumberForLess(b) > toNumberForLess(a)
 }
 
 func hardEquals(a, b interface{}) bool {
diff --git a/jsonlogic.go b/jsonlogic.go
index f8e6423..334738d 100644
--- a/jsonlogic.go
+++ b/jsonlogic.go
@@ -350,9 +350,10 @@ func all(values, data interface{}) interface{} {
 		return false
 	}
 
-	conditions := solveVars(parsed[1], data)
+	
 
 	for _, value := range subject.([]interface{}) {
+		conditions := solveVars(parsed[1], value)
 		v := apply(conditions, value)
 
 		if !isTrue(v) {
diff --git a/operation.go b/operation.go
index 016884f..f67c3a2 100644
--- a/operation.go
+++ b/operation.go
@@ -119,11 +119,11 @@ func operation(operator string, values, data interface{}) interface{} {
 	}
 
 	if operator == "<" {
-		return less(parsed[0], parsed[1])
+		return less(at(parsed, 0), at(parsed, 1))
 	}
 
 	if operator == ">" {
-		return less(parsed[1], parsed[0])
+		return less(at(parsed, 1), at(parsed, 0))
 	}
 
 	if operator == "<=" {


Test patch:
diff --git a/comp_test.go b/comp_test.go
index e406cff..69f6e83 100644
--- a/comp_test.go
+++ b/comp_test.go
@@ -1,13 +1,55 @@
 package jsonlogic
 
 import (
+	"math"
 	"testing"
 
 	"github.com/stretchr/testify/assert"
 )
 
-func TestLessCompareStrings(t *testing.T) {
+// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Less_than#string_to_string_comparison
+func TestLessCompareStringToString(t *testing.T) {
 	assert.True(t, less("a", "b"))
+	assert.False(t, less("a", "a"))
+	assert.False(t, less("a", "3"))
+}
+
+// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Less_than#string_to_number_comparison
+func TestLessCompareStringToNumber(t *testing.T) {
+	assert.False(t, less("5", 3))
+	assert.False(t, less("3", 3))
+	assert.True(t, less("3", 5))
+
+	assert.False(t, less("hello", 5))
+	assert.False(t, less(5, "hello"))
+
+	//console.log("5" < 3n);         // false
+	//console.log("3" < 5n);         // true
+}
+
+// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Less_than#number_to_number_comparison
+func TestLessCompareNumberToNumber(t *testing.T) {
+	assert.False(t, less(5, 3))
+	assert.False(t, less(3, 3))
+	assert.True(t, less(3, 5))
+}
+
+// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Less_than#comparing_boolean_null_undefined_nan
+func TestLessCompareBooleanNullUndefinedNan(t *testing.T) {
+	assert.False(t, less(true, false))
+	assert.True(t, less(false, true))
+
+	assert.True(t, less(0, true))
+	assert.False(t, less(true, 1))
+
+	assert.False(t, less(nil, 0))
+	assert.True(t, less(nil, 1))
+
+	assert.False(t, less(undefinedType{}, 3))
+	assert.False(t, less(3, undefinedType{}))
+
+	assert.False(t, less(3, math.NaN()))
+	assert.False(t, less(math.NaN(), 3))
 }
 
 func TestLessCompareStringAndNil(t *testing.T) {
diff --git a/jsonlogic_test.go b/jsonlogic_test.go
index ed2adfb..0a0fe95 100644
--- a/jsonlogic_test.go
+++ b/jsonlogic_test.go
@@ -281,7 +281,34 @@ func TestAllWithLists(t *testing.T) {
 
 	assert.JSONEq(t, "true", result.String())
 }
+func TestAllWithArrayOfMapData(t *testing.T) {
+	data := strings.NewReader(`[
+		{
+		  "P1": "A",
+		  "P2":"a"
+		},
+		
+		{
+		  "P1": "B",
+		  "P2":"b"
+		}
+	  ]`)
+	rule := strings.NewReader(`
+	  {
+		"all": [
+		  { "var": "" },
+		  { "in": [ {"var": "P1"} , ["A","B"]] }
+		]
+	  }
+	`)
+	var result bytes.Buffer
+	err := Apply(rule, data, &result)
+	if err != nil {
+		t.Fatal(err)
 
+	}
+	assert.JSONEq(t, "true", result.String())
+}
 func TestNoneWithLists(t *testing.T) {
 	rule := strings.NewReader(`{
 		"none": [