Skip to content
Snippets Groups Projects
expr_test.go 3.72 KiB
package expr

import (
	"context"
	"fmt"
	"testing"
	"time"

	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
)

func TestIsExpression(t *testing.T) {
	now := time.Now()

	tests := []struct {
		name string
		eval string
		want bool
	}{
		{"equal", "i == 3", true},
		{"in array", "i in [1,2,3]", true},
		{"contains", "value contains 'some'", true},
		{"contains with . + () $ {} ^", "value contains 'something with . + () $ {} ^'", true},
		{"startsWith", "value startsWith 'some'", true},
		{"startsWith . + () $ {} ^", "value startsWith '. + () $ {} ^'", true},
		{"endsWith", "value endsWith 'some'", true},
		{"endsWith . + () $ {} ^", "value endsWith '. + () $ {} ^'", true},
		{"icontains", "icontains(value, 'some')", true},
		{"icontains with . + () $ {} ^", "icontains (value, 'something with . + () $ {} ^')", true},
		{"istartsWith", "istartsWith(value, 'Some')", true},
		{"istartsWith . + () $ {} ^ . + () $ {} ^", "istartsWith(value, '. + () $ {} ^')", true},
		{"iendsWith", "iendsWith(value, 'some')", true},
		{"iendsWith . + () $ {} ^", "iendsWith(value,'. + () $ {} ^')", true},
		{"or", "i == 2 || i > 10", true},
		{"search", "search('some') || i > 10", true},
		{"vars:or", "i == a + 2 || i > a + 10", true},
		{"near", "near(a, [55.5, 37.5], 1000)", true},
		{"within", "within(a, 'box', [[54.54, 36.36], [55.55, 37.37]])", true},
		{"time", "d > Time.Date('2021-08-31')", true},
		{"time", fmt.Sprintf("d > Time.Time('%s')", now.Format(time.RFC3339)), true},
		{"in", "In(s, [1,2,3])", true},
		{"in", "In(s, 1)", true},
		{"exists#1", "exists(s)", true},
		{"exists#2", "exists", false},
		{"len#1", "len(s) == 1", true},
		{"len#2", "len(s) != 1", true},
		{"len#3", "len(s) > 1", true},
		{"len#4", "len(s) >= 1", true},
		{"len#5", "len(s) < 1", true},
		{"len#6", "len(s) <= 1", true},
		{"len#7", "len", false},
		{"text search or id", "id", false},
		{"numbers", "3", false},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			got := IsExpression(tt.eval)
			assert.Equal(t, tt.want, got)
		})
	}
}

type testEnvStruct struct {
	ID   string      `expr:"id"`
	Size int         `expr:"size"`
	Data interface{} `expr:"data"`
}

func (s *testEnvStruct) Equal(other *testEnvStruct) bool {
	return s.ID == other.ID
}

func TestExpr_Example(t *testing.T) {
	ctx := context.Background()

	tests := []struct {
		name       string
		exp        string
		env        map[string]interface{}
		wantErr    bool
		wantResult interface{}
	}{
		{
			name:       "get field by expr tag",
			exp:        "s.id",
			env:        map[string]interface{}{"s": &testEnvStruct{ID: "id1"}},
			wantResult: "id1",
		},
		{
			name:       "get field by field name",
			exp:        "s.ID",
			env:        map[string]interface{}{"s": &testEnvStruct{ID: "id1"}},
			wantResult: "id1",
		},
		{
			name:       "get nested field",
			exp:        "m.s.size",
			env:        map[string]interface{}{"m": map[string]interface{}{"s": &testEnvStruct{Size: 1}}},
			wantResult: 1,
		},
		{
			name:       "check field",
			exp:        "s.data.size < 100",
			env:        map[string]interface{}{"s": &testEnvStruct{Data: &testEnvStruct{Size: 0}}},
			wantResult: true,
		},
		{
			name:       "use method",
			exp:        "s1.Equal(s2)",
			env:        map[string]interface{}{"s1": &testEnvStruct{ID: "id1"}, "s2": &testEnvStruct{ID: "id2"}},
			wantResult: false,
		},
		{
			name:    "field not exists",
			exp:     "s.not_exists",
			env:     map[string]interface{}{"s": &testEnvStruct{}},
			wantErr: true,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			result, err := Eval(ctx, tt.exp, tt.env)
			if tt.wantErr {
				require.Error(t, err)
				return
			}

			require.NoError(t, err)
			require.Equal(t, tt.wantResult, result)
		})
	}
}