diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 04284d6..dce5a48 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -5,7 +5,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - go: ['1.14', '1.15', '1.16', '1.18', '1.19', '1.20', '1.21'] + go: ['1.16', '1.18', '1.19', '1.20', '1.21'] name: Running with Go ${{ matrix.go }} steps: - name: Install Go diff --git a/arrays.go b/arrays.go index 559c166..7523a29 100644 --- a/arrays.go +++ b/arrays.go @@ -1,6 +1,8 @@ package jsonlogic -import "fmt" +import ( + "fmt" +) type ErrReduceDataType struct { dataType string @@ -95,15 +97,21 @@ func reduce(values, data interface{}) interface{} { accumulator interface{} valueType string ) + { - if isBool(parsed[2]) { - accumulator = isTrue(parsed[2]) + initialValue := parsed[2] + if isMap(initialValue) { + initialValue = apply(initialValue, data) + } + + if isBool(initialValue) { + accumulator = isTrue(initialValue) valueType = "bool" - } else if isNumber(parsed[2]) { - accumulator = toNumber(parsed[2]) + } else if isNumber(initialValue) { + accumulator = toNumber(initialValue) valueType = "number" - } else if isString(parsed[2]) { - accumulator = toString(parsed[2]) + } else if isString(initialValue) { + accumulator = toString(initialValue) valueType = "string" } else { panic(ErrReduceDataType{ diff --git a/arrays_test.go b/arrays_test.go index 6441b40..9500820 100644 --- a/arrays_test.go +++ b/arrays_test.go @@ -1,111 +1,79 @@ -package jsonlogic +package jsonlogic_test import ( - "encoding/json" + "bytes" + "strings" "testing" "github.com/stretchr/testify/assert" + + "github.com/diegoholiveira/jsonlogic/v3" ) func TestFilterParseTheSubjectFromFirstPosition(t *testing.T) { - var parsed interface{} - - err := json.Unmarshal([]byte(`[ + rule := strings.NewReader(`{"filter": [ [1,2,3,4,5], {"%":[{"var":""},2]} - ]`), &parsed) - if err != nil { - panic(err) - } - - result := filter(parsed, nil) - - var expected interface{} + ]}`) - json.Unmarshal([]byte(`[1,3,5]`), &expected) // nolint:errcheck + var result bytes.Buffer - assert.Equal(t, expected, result) + err := jsonlogic.Apply(rule, nil, &result) + assert.Nil(t, err) + assert.JSONEq(t, `[1,3,5]`, result.String()) } func TestFilterParseTheSubjectFromNullValue(t *testing.T) { - var parsed interface{} - - err := json.Unmarshal([]byte(`[ + rule := strings.NewReader(`{"filter": [ null, {"%":[{"var":""},2]} - ]`), &parsed) - if err != nil { - panic(err) - } - - result := filter(parsed, nil) + ]}`) - var expected interface{} + var result bytes.Buffer - json.Unmarshal([]byte(`[]`), &expected) // nolint:errcheck - - assert.Equal(t, expected, result) + err := jsonlogic.Apply(rule, nil, &result) + assert.Nil(t, err) + assert.JSONEq(t, `[]`, result.String()) } func TestReduceSkipNullValues(t *testing.T) { - var parsed interface{} - - err := json.Unmarshal([]byte(`[ + rule := strings.NewReader(`{"reduce": [ [1,2,null,4,5], {"+":[{"var":"current"}, {"var":"accumulator"}]}, 0 - ]`), &parsed) - if err != nil { - panic(err) - } + ]}`) - result := reduce(parsed, nil) + var result bytes.Buffer - var expected interface{} - - json.Unmarshal([]byte(`12`), &expected) // nolint:errcheck - - assert.Equal(t, expected, result) + err := jsonlogic.Apply(rule, nil, &result) + assert.Nil(t, err) + assert.JSONEq(t, `12`, result.String()) } func TestReduceBoolValues(t *testing.T) { - var parsed interface{} - - err := json.Unmarshal([]byte(`[ + rule := strings.NewReader(`{"reduce": [ [true,false,true,null], {"or":[{"var":"current"}, {"var":"accumulator"}]}, false - ]`), &parsed) - if err != nil { - panic(err) - } - - result := reduce(parsed, nil) + ]}`) - var expected interface{} + var result bytes.Buffer - json.Unmarshal([]byte(`true`), &expected) // nolint:errcheck - - assert.Equal(t, expected, result) + err := jsonlogic.Apply(rule, nil, &result) + assert.Nil(t, err) + assert.JSONEq(t, `true`, result.String()) } func TestReduceStringValues(t *testing.T) { - var parsed interface{} - - err := json.Unmarshal([]byte(`[ + rule := strings.NewReader(`{"reduce": [ ["a",null,"b"], {"cat":[{"var":"current"}, {"var":"accumulator"}]}, "" - ]`), &parsed) - if err != nil { - panic(err) - } - - result := reduce(parsed, nil) - - var expected interface{} + ]}`) - json.Unmarshal([]byte(`"ba"`), &expected) // nolint:errcheck + var result bytes.Buffer - assert.Equal(t, expected, result) + err := jsonlogic.Apply(rule, nil, &result) + assert.Nil(t, err) + assert.JSONEq(t, `"ba"`, result.String()) } diff --git a/comp.go b/comp.go index ad2343f..84c3d7b 100644 --- a/comp.go +++ b/comp.go @@ -75,7 +75,7 @@ func equals(a, b interface{}) bool { } if isNumber(a) { - return toNumber(a) == toNumber(b) + return isPrimitive(b) && toNumber(a) == toNumber(b) } if isBool(a) { @@ -85,5 +85,9 @@ func equals(a, b interface{}) bool { return isTrue(a) == isTrue(b) } + if !isString(a) || !isString(b) { + return false + } + return toString(a) == toString(b) } diff --git a/internal/issues/0083_test.go b/internal/issues/0083_test.go deleted file mode 100644 index 01fcc1e..0000000 --- a/internal/issues/0083_test.go +++ /dev/null @@ -1,37 +0,0 @@ -package issues_test - -import ( - "bytes" - "strings" - "testing" - - "github.com/stretchr/testify/assert" - - "github.com/diegoholiveira/jsonlogic/v3" -) - -func TestIssue83(t *testing.T) { - rule := `{ - "map": [ - {"var": "listOfLists"}, - {"in": ["item_a", {"var": ""}]} - ] - }` - - data := `{ - "listOfLists": [ - ["item_a", "item_b", "item_c"], - ["item_b", "item_c"], - ["item_a", "item_c"] - ] - }` - - var result bytes.Buffer - - err := jsonlogic.Apply(strings.NewReader(rule), strings.NewReader(data), &result) - - if assert.Nil(t, err) { - expected := `[true,false,true]` - assert.JSONEq(t, expected, result.String()) - } -} diff --git a/internal/testing.go b/internal/testing.go index d60b29e..7014a87 100644 --- a/internal/testing.go +++ b/internal/testing.go @@ -4,7 +4,6 @@ import ( "bytes" "encoding/json" "io" - "io/ioutil" "log" "net/http" "reflect" @@ -42,7 +41,7 @@ func GetScenariosFromOfficialTestSuite() Tests { return tests } - buffer, _ := ioutil.ReadAll(response.Body) + buffer, _ := io.ReadAll(response.Body) response.Body.Close() diff --git a/issues_test.go b/issues_test.go new file mode 100644 index 0000000..b61fdcf --- /dev/null +++ b/issues_test.go @@ -0,0 +1,316 @@ +package jsonlogic_test + +import ( + "bytes" + "encoding/json" + "strings" + "testing" + + "github.com/stretchr/testify/assert" + + "github.com/diegoholiveira/jsonlogic/v3" +) + +func TestIssue50(t *testing.T) { + logic := strings.NewReader(`{"<": ["abc", 3]}`) + data := strings.NewReader(`{}`) + + var result bytes.Buffer + err := jsonlogic.Apply(logic, data, &result) + if err != nil { + t.Fatal(err) + } + + expected := `false` + assert.JSONEq(t, expected, result.String()) +} + +func TestIssue51_example1(t *testing.T) { + logic := strings.NewReader(`{"==":[{"var":"test"},true]}`) + data := strings.NewReader(`{}`) + + var result bytes.Buffer + err := jsonlogic.Apply(logic, data, &result) + if err != nil { + t.Fatal(err) + } + + expected := `false` + assert.JSONEq(t, expected, result.String()) +} + +func TestIssue51_example2(t *testing.T) { + logic := strings.NewReader(`{"==":[{"var":"test"},"true"]}`) + data := strings.NewReader(`{"test": true}`) + + var result bytes.Buffer + err := jsonlogic.Apply(logic, data, &result) + if err != nil { + t.Fatal(err) + } + + expected := `false` + assert.JSONEq(t, expected, result.String()) +} + +func TestIssue52_example1(t *testing.T) { + data := strings.NewReader(`{}`) + logic := strings.NewReader(`{"substr": ["jsonlogic", -10]}`) + + var result bytes.Buffer + err := jsonlogic.Apply(logic, data, &result) + if err != nil { + t.Fatal(err) + } + + expected := `"jsonlogic"` + assert.JSONEq(t, expected, result.String()) +} + +func TestIssue52_example2(t *testing.T) { + data := strings.NewReader(`{}`) + logic := strings.NewReader(`{"substr": ["jsonlogic", 10]}`) + + var result bytes.Buffer + err := jsonlogic.Apply(logic, data, &result) + if err != nil { + t.Fatal(err) + } + + expected := `"jsonlogic"` + assert.JSONEq(t, expected, result.String()) +} + +func TestIssue58_example(t *testing.T) { + data := strings.NewReader(`{"foo": "bar"}`) + logic := strings.NewReader(`{"if":[ + {"==":[{"var":"foo"},"bar"]},{"foo":"is_bar","path":"foo_is_bar"}, + {"foo":"not_bar","path":"default_object"} + ]}`) + + var result bytes.Buffer + err := jsonlogic.Apply(logic, data, &result) + if err != nil { + t.Fatal(err) + } + + expected := `{"foo":"is_bar","path":"foo_is_bar"}` + assert.JSONEq(t, expected, result.String()) +} + +func TestIssue70(t *testing.T) { + data := strings.NewReader(`{"people": [ + {"age":18, "name":"John"}, + {"age":20, "name":"Luke"}, + {"age":18, "name":"Mark"} +]}`) + logic := strings.NewReader(`{"filter": [ + {"var": ["people"]}, + {"==": [{"var": ["age"]}, 18]} +]}`) + + var result bytes.Buffer + err := jsonlogic.Apply(logic, data, &result) + if err != nil { + t.Fatal(err) + } + + expected := `[ + {"age": 18, "name": "John"}, + {"age": 18, "name": "Mark"} +]` + assert.JSONEq(t, expected, result.String()) +} + +func TestIssue71_example_empty_min(t *testing.T) { + data := strings.NewReader(`{}`) + logic := strings.NewReader(`{"min":[]}`) + + var result bytes.Buffer + err := jsonlogic.Apply(logic, data, &result) + if err != nil { + t.Fatal(err) + } + + expected := `null` + assert.JSONEq(t, expected, result.String()) +} + +func TestIssue71_example_empty_max(t *testing.T) { + data := strings.NewReader(`{}`) + logic := strings.NewReader(`{"max":[]}`) + + var result bytes.Buffer + err := jsonlogic.Apply(logic, data, &result) + if err != nil { + t.Fatal(err) + } + + expected := `null` + assert.JSONEq(t, expected, result.String()) +} + +func TestIssue71_example_max(t *testing.T) { + data := strings.NewReader(`{}`) + logic := strings.NewReader(`{"max":[-3, -2]}`) + + var result bytes.Buffer + err := jsonlogic.Apply(logic, data, &result) + if err != nil { + t.Fatal(err) + } + + expected := `-2` + assert.JSONEq(t, expected, result.String()) +} + +func TestIssue74(t *testing.T) { + logic := strings.NewReader(`{"if":[ false, {"var":"values.0.categories"}, "else" ]}`) + data := strings.NewReader(`{ "values": [] }`) + + var result bytes.Buffer + _ = jsonlogic.Apply(logic, data, &result) + expected := `"else"` + assert.JSONEq(t, expected, result.String()) +} + +func TestJsonLogicWithSolvedVars(t *testing.T) { + rule := json.RawMessage(`{ + "or":[ + { + "and":[ + {"==": [{ "var":"is_foo" }, true ]}, + {"==": [{ "var":"is_bar" }, true ]}, + {">=": [{ "var":"foo" }, 17179869184 ]}, + {"==": [{ "var":"bar" }, 0 ]} + ] + }, + { + "and":[ + {"==": [{ "var":"is_bar" }, true ]}, + {"==": [{ "var":"is_foo" }, false ]}, + {"==": [{ "var":"foo" }, 34359738368 ]}, + {"==": [{ "var":"bar" }, 0 ]} + ] + }] + }`) + + data := json.RawMessage(`{"foo": 34359738368, "bar": 10, "is_foo": false, "is_bar": true}`) + + output, err := jsonlogic.GetJsonLogicWithSolvedVars(rule, data) + + if err != nil { + t.Fatal(err) + } + + expected := `{ + "or":[ + { + "and":[ + { "==":[ false, true ] }, + { "==":[ true, true ] }, + { ">=":[ 34359738368, 17179869184 ] }, + { "==":[ 10, 0 ] } + ] + }, + { + "and":[ + { "==":[ true, true ] }, + { "==":[ false, false ] }, + { "==":[ 34359738368, 34359738368 ] }, + { "==":[ 10, 0 ] } + ] + }] + }` + + assert.JSONEq(t, expected, string(output)) +} + +func TestIssue79(t *testing.T) { + rule := strings.NewReader( + `{"and": [ + {"in": [ + {"var": "flow"}, + ["BRAND"] + ]}, + {"or": [ + {"if": [ + {"missing": ["gender"]}, + true, + false + ]}, + {"some": [ + {"var": "gender"}, + {"==": [ + {"var": null}, + "men" + ]} + ]} + ]} + ]}`, + ) + + data := strings.NewReader(`{"category":["sneakers"],"flow":"BRAND","gender":["men"],"market":"US"}`) + + var result bytes.Buffer + + err := jsonlogic.Apply(rule, data, &result) + + if err != nil { + t.Fatal(err) + } + + expected := `true` + assert.JSONEq(t, expected, result.String()) +} + +func TestIssue83(t *testing.T) { + rule := `{ + "map": [ + {"var": "listOfLists"}, + {"in": ["item_a", {"var": ""}]} + ] + }` + + data := `{ + "listOfLists": [ + ["item_a", "item_b", "item_c"], + ["item_b", "item_c"], + ["item_a", "item_c"] + ] + }` + + var result bytes.Buffer + + err := jsonlogic.Apply(strings.NewReader(rule), strings.NewReader(data), &result) + + if assert.Nil(t, err) { + expected := `[true,false,true]` + assert.JSONEq(t, expected, result.String()) + } +} + +func TestIssue81(t *testing.T) { + rule := `{ + "some": [ + {"var": "A"}, + {"!=": [ + {"var": ".B"}, + {"var": "B"} + ]} + ]} + ` + + data := `{"A":[{"B":1}], "B":2}` + + var result bytes.Buffer + + err := jsonlogic.Apply(strings.NewReader(rule), strings.NewReader(data), &result) + + if err != nil { + t.Fatal(err) + } + + expected := `true` + assert.JSONEq(t, expected, result.String()) +} diff --git a/jsonlogic.go b/jsonlogic.go index 6968101..edcd9eb 100644 --- a/jsonlogic.go +++ b/jsonlogic.go @@ -4,7 +4,6 @@ import ( "encoding/json" "fmt" "io" - "sort" "strings" "github.com/barkimedes/go-deepcopy" @@ -125,40 +124,6 @@ func _inRange(value interface{}, values interface{}) bool { return toString(value) >= toString(i) && toString(j) >= toString(value) } -// Expect values to be in alphabetical ascending order -func _inSorted(value interface{}, values interface{}) bool { - valuesSlice := values.([]interface{}) - - findElement := func(i int) bool { - element := valuesSlice[i] - - if isSlice(valuesSlice[i]) { - sliceElement := valuesSlice[i].([]interface{}) - start := sliceElement[0] - end := sliceElement[1] - - return (toString(start) <= toString(value) && toString(end) >= toString(value)) || toString(end) > toString(value) - } - - return toString(element) >= toString(value) - } - - i := sort.Search(len(valuesSlice), findElement) - if i >= len(valuesSlice) { - return false - } - - if isSlice(valuesSlice[i]) { - sliceElement := valuesSlice[i].([]interface{}) - start := sliceElement[0] - end := sliceElement[1] - - return toString(start) <= toString(value) && toString(end) >= toString(value) - } - - return toString(valuesSlice[i]) == toString(value) -} - func _in(value interface{}, values interface{}) bool { if value == nil || values == nil { return false @@ -168,6 +133,10 @@ func _in(value interface{}, values interface{}) bool { return strings.Contains(values.(string), value.(string)) } + if !isSlice(values) { + return false + } + for _, element := range values.([]interface{}) { if isSlice(element) { if _inRange(value, element) { @@ -522,7 +491,7 @@ func Apply(rule, data io.Reader, result io.Writer) error { return err } - output, err := ApplyInterface(_rule, _data) + output, err := applyInterface(_rule, _data) if err != nil { return err } @@ -572,7 +541,7 @@ func ApplyRaw(rule, data json.RawMessage) (json.RawMessage, error) { return nil, err } - result, err := ApplyInterface(_rule, _data) + result, err := applyInterface(_rule, _data) if err != nil { return nil, err } @@ -580,11 +549,9 @@ func ApplyRaw(rule, data json.RawMessage) (json.RawMessage, error) { return json.Marshal(&result) } -// ApplyInterface receives a rule and data as interface{} and returns the result +// applyInterface receives a rule and data as interface{} and returns the result // of the rule applied to the data. -// -// Deprecated: Use Apply instead because ApplyInterface will be private in the next version. -func ApplyInterface(rule, data interface{}) (output interface{}, err error) { +func applyInterface(rule, data interface{}) (output interface{}, err error) { defer func() { if e := recover(); e != nil { // fmt.Println("stacktrace from panic: \n" + string(debug.Stack())) diff --git a/jsonlogic_test.go b/jsonlogic_test.go index 5bda383..5e50f59 100644 --- a/jsonlogic_test.go +++ b/jsonlogic_test.go @@ -4,12 +4,13 @@ import ( "bytes" "encoding/json" "fmt" - "io/ioutil" + "io" "strings" "testing" - "github.com/diegoholiveira/jsonlogic/v3/internal" "github.com/stretchr/testify/assert" + + "github.com/diegoholiveira/jsonlogic/v3/internal" ) func TestRulesFromJsonLogic(t *testing.T) { @@ -24,7 +25,7 @@ func TestRulesFromJsonLogic(t *testing.T) { t.Fatal(err) } - if b, err := ioutil.ReadAll(test.Expected); err == nil { + if b, err := io.ReadAll(test.Expected); err == nil { assert.JSONEq(t, string(b), result.String()) } }) @@ -184,59 +185,6 @@ func TestListOfRanges(t *testing.T) { assert.JSONEq(t, expected, result.String()) } -func TestInSortedOperator(t *testing.T) { - rule := strings.NewReader(`{ - "filter": [ - {"var": "people"}, - {"in_sorted": [ - {"var": ".age"}, - [ - 11.00, - [12, 14], - [13, 18], - 2, - "20", - [32, 38], - "a", - ["b", "d"] - ] - ]} - ] - }`) - - data := strings.NewReader(`{ - "people": [ - {"age":"18", "name":"John"}, - {"age":20, "name":"Luke"}, - {"age":18, "name":"Mark"}, - {"age":40, "name":"Donald"}, - {"age":11, "name":"Mickey"}, - {"age":"1", "name":"Minnie"}, - {"age":2, "name":"Mario"}, - {"age":"a", "name":"Mario"}, - {"age":"c", "name":"Princess"} - ] - }`) - - var result bytes.Buffer - err := Apply(rule, data, &result) - if err != nil { - t.Fatal(err) - } - - expected := `[ - {"age":"18", "name": "John"}, - {"age":20, "name":"Luke"}, - {"age":18, "name": "Mark"}, - {"age":11, "name":"Mickey"}, - {"age":2, "name":"Mario"}, - {"age":"a", "name":"Mario"}, - {"age":"c", "name":"Princess"} - ]` - - assert.JSONEq(t, expected, result.String()) -} - func TestSomeWithLists(t *testing.T) { rule := strings.NewReader(`{ "some": [ @@ -440,7 +388,7 @@ func TestDataWithDefaultValueWithApplyInterface(t *testing.T) { } expected := float64(3) - output, err := ApplyInterface(rule, nil) + output, err := applyInterface(rule, nil) if err != nil { t.Fatal(err) } @@ -456,7 +404,7 @@ func TestMissingOperators(t *testing.T) { }, } - _, err := ApplyInterface(rule, nil) + _, err := applyInterface(rule, nil) assert.EqualError(t, err, "The operator \"sum\" is not supported") } @@ -628,7 +576,7 @@ func TestReduceWithUnsupportedValue(t *testing.T) { "data": []interface{}{"str"}, } - _, err := ApplyInterface(rule, data) + _, err := applyInterface(rule, data) assert.EqualError(t, err, "The type \"\" is not supported") } @@ -655,280 +603,30 @@ func TestAddOperator(t *testing.T) { assert.JSONEq(t, expected, result.String()) } -func TestIssue50(t *testing.T) { - logic := strings.NewReader(`{"<": ["abc", 3]}`) - data := strings.NewReader(`{}`) - - var result bytes.Buffer - err := Apply(logic, data, &result) - if err != nil { - t.Fatal(err) - } - - expected := `false` - assert.JSONEq(t, expected, result.String()) -} - -func TestIssue51_example1(t *testing.T) { - logic := strings.NewReader(`{"==":[{"var":"test"},true]}`) - data := strings.NewReader(`{}`) - - var result bytes.Buffer - err := Apply(logic, data, &result) - if err != nil { - t.Fatal(err) - } - - expected := `false` - assert.JSONEq(t, expected, result.String()) -} - -func TestIssue51_example2(t *testing.T) { - logic := strings.NewReader(`{"==":[{"var":"test"},"true"]}`) - data := strings.NewReader(`{"test": true}`) - - var result bytes.Buffer - err := Apply(logic, data, &result) - if err != nil { - t.Fatal(err) - } - - expected := `false` - assert.JSONEq(t, expected, result.String()) -} - -func TestIssue52_example1(t *testing.T) { - data := strings.NewReader(`{}`) - logic := strings.NewReader(`{"substr": ["jsonlogic", -10]}`) - - var result bytes.Buffer - err := Apply(logic, data, &result) - if err != nil { - t.Fatal(err) - } - - expected := `"jsonlogic"` - assert.JSONEq(t, expected, result.String()) -} - -func TestIssue52_example2(t *testing.T) { - data := strings.NewReader(`{}`) - logic := strings.NewReader(`{"substr": ["jsonlogic", 10]}`) - - var result bytes.Buffer - err := Apply(logic, data, &result) - if err != nil { - t.Fatal(err) - } - - expected := `"jsonlogic"` - assert.JSONEq(t, expected, result.String()) -} - -func TestIssue58_example(t *testing.T) { - data := strings.NewReader(`{"foo": "bar"}`) - logic := strings.NewReader(`{"if":[ - {"==":[{"var":"foo"},"bar"]},{"foo":"is_bar","path":"foo_is_bar"}, - {"foo":"not_bar","path":"default_object"} - ]}`) - - var result bytes.Buffer - err := Apply(logic, data, &result) - if err != nil { - t.Fatal(err) - } - - expected := `{"foo":"is_bar","path":"foo_is_bar"}` - assert.JSONEq(t, expected, result.String()) -} - -func TestIssue70(t *testing.T) { - data := strings.NewReader(`{"people": [ - {"age":18, "name":"John"}, - {"age":20, "name":"Luke"}, - {"age":18, "name":"Mark"} -]}`) - logic := strings.NewReader(`{"filter": [ - {"var": ["people"]}, - {"==": [{"var": ["age"]}, 18]} -]}`) - - var result bytes.Buffer - err := Apply(logic, data, &result) - if err != nil { - t.Fatal(err) - } - - expected := `[ - {"age": 18, "name": "John"}, - {"age": 18, "name": "Mark"} -]` - assert.JSONEq(t, expected, result.String()) -} - -func TestIssue71_example_empty_min(t *testing.T) { - data := strings.NewReader(`{}`) - logic := strings.NewReader(`{"min":[]}`) - - var result bytes.Buffer - err := Apply(logic, data, &result) - if err != nil { - t.Fatal(err) - } - - expected := `null` - assert.JSONEq(t, expected, result.String()) -} - -func TestIssue71_example_empty_max(t *testing.T) { - data := strings.NewReader(`{}`) - logic := strings.NewReader(`{"max":[]}`) - - var result bytes.Buffer - err := Apply(logic, data, &result) - if err != nil { - t.Fatal(err) - } - - expected := `null` - assert.JSONEq(t, expected, result.String()) -} - -func TestIssue71_example_max(t *testing.T) { - data := strings.NewReader(`{}`) - logic := strings.NewReader(`{"max":[-3, -2]}`) - - var result bytes.Buffer - err := Apply(logic, data, &result) - if err != nil { - t.Fatal(err) - } - - expected := `-2` - assert.JSONEq(t, expected, result.String()) -} - -func TestIssue74(t *testing.T) { - logic := strings.NewReader(`{"if":[ false, {"var":"values.0.categories"}, "else" ]}`) - data := strings.NewReader(`{ "values": [] }`) - - var result bytes.Buffer - _ = Apply(logic, data, &result) - expected := `"else"` - assert.JSONEq(t, expected, result.String()) -} - -func TestJsonLogicWithSolvedVars(t *testing.T) { - rule := json.RawMessage(`{ - "or":[ - { - "and":[ - {"==": [{ "var":"is_foo" }, true ]}, - {"==": [{ "var":"is_bar" }, true ]}, - {">=": [{ "var":"foo" }, 17179869184 ]}, - {"==": [{ "var":"bar" }, 0 ]} - ] - }, - { - "and":[ - {"==": [{ "var":"is_bar" }, true ]}, - {"==": [{ "var":"is_foo" }, false ]}, - {"==": [{ "var":"foo" }, 34359738368 ]}, - {"==": [{ "var":"bar" }, 0 ]} - ] - }] - }`) - - data := json.RawMessage(`{"foo": 34359738368, "bar": 10, "is_foo": false, "is_bar": true}`) - - output, err := GetJsonLogicWithSolvedVars(rule, data) - - if err != nil { - t.Fatal(err) - } - - expected := `{ - "or":[ - { - "and":[ - { "==":[ false, true ] }, - { "==":[ true, true ] }, - { ">=":[ 34359738368, 17179869184 ] }, - { "==":[ 10, 0 ] } - ] - }, - { - "and":[ - { "==":[ true, true ] }, - { "==":[ false, false ] }, - { "==":[ 34359738368, 34359738368 ] }, - { "==":[ 10, 0 ] } - ] - }] - }` - - assert.JSONEq(t, expected, string(output)) -} - -func TestIssue79(t *testing.T) { - rule := strings.NewReader( - `{"and": [ - {"in": [ - {"var": "flow"}, - ["BRAND"] - ]}, - {"or": [ - {"if": [ - {"missing": ["gender"]}, - true, - false - ]}, - {"some": [ - {"var": "gender"}, - {"==": [ - {"var": null}, - "men" - ]} - ]} - ]} - ]}`, - ) - - data := strings.NewReader(`{"category":["sneakers"],"flow":"BRAND","gender":["men"],"market":"US"}`) +func TestInWithOneParam(t *testing.T) { + rule := strings.NewReader(`{"in": [ "Ringo" ]}`) + data := strings.NewReader(`null`) var result bytes.Buffer err := Apply(rule, data, &result) - if err != nil { t.Fatal(err) } - expected := `true` - assert.JSONEq(t, expected, result.String()) + assert.JSONEq(t, `false`, result.String()) } -func TestIssue81(t *testing.T) { - rule := `{ - "some": [ - {"var": "A"}, - {"!=": [ - {"var": ".B"}, - {"var": "B"} - ]} - ]} - ` - - data := `{"A":[{"B":1}], "B":2}` +func TestEqualWithList(t *testing.T) { + rule := strings.NewReader(`{"==": [ 2, [3, 2, 1] ]}`) + data := strings.NewReader(`null`) var result bytes.Buffer - err := Apply(strings.NewReader(rule), strings.NewReader(data), &result) - + err := Apply(rule, data, &result) if err != nil { t.Fatal(err) } - expected := `true` - assert.JSONEq(t, expected, result.String()) + assert.JSONEq(t, `false`, result.String()) } diff --git a/math_test.go b/math_test.go index 27ccb65..b6dd3cf 100644 --- a/math_test.go +++ b/math_test.go @@ -7,20 +7,6 @@ import ( "github.com/stretchr/testify/assert" ) -func TestMinusWithOneNumber(t *testing.T) { - json_parsed := []interface{}{"-10.0"} - input := interface{}(json_parsed) - expected := -10.0 - assert.Equal(t, expected, minus(input)) -} - -func TestDivWithOneNumber(t *testing.T) { - json_parsed := []interface{}{"2.0"} - input := interface{}(json_parsed) - expected := 2.0 - assert.Equal(t, expected, minus(input)) -} - func TestSubOperation(t *testing.T) { var rule json.RawMessage = json.RawMessage(`{ "-": [ diff --git a/operation.go b/operation.go index f67c3a2..392a012 100644 --- a/operation.go +++ b/operation.go @@ -74,7 +74,7 @@ func operation(operator string, values, data interface{}) interface{} { return _or(parsed) } - if len(parsed) == 1 { + if operator != "in" && len(parsed) == 1 { return unary(operator, parsed[0]) } @@ -103,11 +103,10 @@ func operation(operator string, values, data interface{}) interface{} { } if operator == "in" { - return _in(parsed[0], parsed[1]) - } - - if operator == "in_sorted" { - return _inSorted(parsed[0], parsed[1]) + if len(parsed) > 1 { + return _in(parsed[0], parsed[1]) + } + return _in(parsed[0], nil) } if operator == "%" { diff --git a/validator.go b/validator.go index 62e97ff..72ae2ae 100644 --- a/validator.go +++ b/validator.go @@ -69,7 +69,6 @@ func isOperator(op string) bool { "and", "?:", "in", - "in_sorted", "cat", "%", "abs",