Переглянути джерело

test(pkg): add test cases (#1913)

* test(pkg): add test cases

Signed-off-by: xjasonlyu <xjasonlyu@gmail.com>

* add statement tests

Signed-off-by: xjasonlyu <xjasonlyu@gmail.com>

* add test cases for cast

Signed-off-by: xjasonlyu <xjasonlyu@gmail.com>

* ignore expr.go

Signed-off-by: xjasonlyu <xjasonlyu@gmail.com>

* separate expr codes

Signed-off-by: xjasonlyu <xjasonlyu@gmail.com>

---------

Signed-off-by: xjasonlyu <xjasonlyu@gmail.com>
Jason Lyu 1 рік тому
батько
коміт
6219a8d72a
7 змінених файлів з 357 додано та 118 видалено
  1. 1 1
      codecov.yaml
  2. 0 117
      pkg/ast/expr.go
  3. 132 0
      pkg/ast/expr_ref.go
  4. 0 0
      pkg/ast/expr_ref_test.go
  5. 71 0
      pkg/ast/statement_test.go
  6. 117 0
      pkg/cast/cast_test.go
  7. 36 0
      pkg/cast/time_test.go

+ 1 - 1
codecov.yaml

@@ -9,4 +9,4 @@ coverage:
         threshold: 1%
 
 ignore:
-  - "**/main.go"
+  - "**/main.go"

+ 0 - 117
pkg/ast/expr.go

@@ -14,12 +14,6 @@
 
 package ast
 
-import (
-	"fmt"
-	"regexp"
-	"strings"
-)
-
 type Node interface {
 	node()
 }
@@ -202,28 +196,6 @@ type BetweenExpr struct {
 func (b *BetweenExpr) expr() {}
 func (b *BetweenExpr) node() {}
 
-type LikePattern struct {
-	Expr    Expr
-	Pattern *regexp.Regexp
-}
-
-func (l *LikePattern) expr() {}
-func (l *LikePattern) node() {}
-
-func (l *LikePattern) Compile(likestr string) (*regexp.Regexp, error) {
-	regstr := strings.ReplaceAll(strings.NewReplacer(
-		`\%`, `\%`,
-		`\_`, `\_`,
-		`%`, `.*`,
-		`_`, `.`,
-	).Replace(likestr), `\`, `\\`)
-	re, err := regexp.Compile("^" + regstr + "$")
-	if err != nil {
-		return nil, err
-	}
-	return re, nil
-}
-
 type StreamName string
 
 func (sn *StreamName) node() {}
@@ -233,95 +205,6 @@ const (
 	AliasStream   = StreamName("$$alias")
 )
 
-// FieldRef could be
-//  1. SQL Field
-//     1.1 Explicit field "stream.col"
-//     1.2 Implicit field "col"  -> only exist in schemaless stream. Otherwise, explicit stream name will be bound
-//     1.3 Alias field "expr as c" -> refer to an Expression or column
-type FieldRef struct {
-	// optional, bind in analyzer, empty means alias, default means not set
-	// MUST have after binding for SQL fields. For 1.2,1.3 and 1.4, use special constant as stream name
-	StreamName StreamName
-	// optional, set only once. For selections, empty name will be assigned a default name
-	// MUST have after binding, assign a name for 1.4
-	Name string
-	// Only for alias
-	*AliasRef
-}
-
-func (fr *FieldRef) expr() {}
-func (fr *FieldRef) node() {}
-func (fr *FieldRef) IsColumn() bool {
-	return fr.StreamName != AliasStream && fr.StreamName != ""
-}
-
-func (fr *FieldRef) IsAlias() bool {
-	return fr.StreamName == AliasStream
-}
-
-func (fr *FieldRef) RefSelection(a *AliasRef) {
-	fr.AliasRef = a
-}
-
-// RefSources Must call after binding or will get empty
-func (fr *FieldRef) RefSources() []StreamName {
-	if fr.StreamName == AliasStream {
-		return fr.refSources
-	} else if fr.StreamName != "" {
-		return []StreamName{fr.StreamName}
-	} else {
-		return nil
-	}
-}
-
-// SetRefSource Only call this for alias field ref
-func (fr *FieldRef) SetRefSource(names []StreamName) {
-	fr.refSources = names
-}
-
-type AliasRef struct {
-	// MUST have, It is used for evaluation
-	Expression Expr
-	// MUST have after binding, calculate once in initializer. Could be 0 when alias an Expression without col like "1+2"
-	refSources []StreamName
-	// optional, lazy set when calculating isAggregate
-	IsAggregate *bool
-}
-
-func NewAliasRef(e Expr) (*AliasRef, error) {
-	r := make(map[StreamName]bool)
-	var walkErr error
-	WalkFunc(e, func(n Node) bool {
-		switch f := n.(type) {
-		case *FieldRef:
-			switch f.StreamName {
-			case AliasStream:
-				walkErr = fmt.Errorf("cannot use alias %s inside another alias %v", f.Name, e)
-				return false
-			default:
-				r[f.StreamName] = true
-			}
-		}
-		return true
-	})
-	if walkErr != nil {
-		return nil, walkErr
-	}
-	rs := make([]StreamName, 0)
-	for k := range r {
-		rs = append(rs, k)
-	}
-	return &AliasRef{
-		Expression: e,
-		refSources: rs,
-	}, nil
-}
-
-// MockAliasRef is for testing only.
-func MockAliasRef(e Expr, r []StreamName, a *bool) *AliasRef {
-	return &AliasRef{e, r, a}
-}
-
 type MetaRef struct {
 	StreamName StreamName
 	Name       string

+ 132 - 0
pkg/ast/expr_ref.go

@@ -0,0 +1,132 @@
+// Copyright 2023 EMQ Technologies Co., Ltd.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package ast
+
+import (
+	"fmt"
+	"regexp"
+	"strings"
+)
+
+type LikePattern struct {
+	Expr    Expr
+	Pattern *regexp.Regexp
+}
+
+func (l *LikePattern) expr() {}
+func (l *LikePattern) node() {}
+
+func (l *LikePattern) Compile(likestr string) (*regexp.Regexp, error) {
+	regstr := strings.ReplaceAll(strings.NewReplacer(
+		`\%`, `\%`,
+		`\_`, `\_`,
+		`%`, `.*`,
+		`_`, `.`,
+	).Replace(likestr), `\`, `\\`)
+	re, err := regexp.Compile("^" + regstr + "$")
+	if err != nil {
+		return nil, err
+	}
+	return re, nil
+}
+
+// FieldRef could be
+//  1. SQL Field
+//     1.1 Explicit field "stream.col"
+//     1.2 Implicit field "col"  -> only exist in schemaless stream. Otherwise, explicit stream name will be bound
+//     1.3 Alias field "expr as c" -> refer to an Expression or column
+type FieldRef struct {
+	// optional, bind in analyzer, empty means alias, default means not set
+	// MUST have after binding for SQL fields. For 1.2,1.3 and 1.4, use special constant as stream name
+	StreamName StreamName
+	// optional, set only once. For selections, empty name will be assigned a default name
+	// MUST have after binding, assign a name for 1.4
+	Name string
+	// Only for alias
+	*AliasRef
+}
+
+func (fr *FieldRef) expr() {}
+func (fr *FieldRef) node() {}
+func (fr *FieldRef) IsColumn() bool {
+	return fr.StreamName != AliasStream && fr.StreamName != ""
+}
+
+func (fr *FieldRef) IsAlias() bool {
+	return fr.StreamName == AliasStream
+}
+
+func (fr *FieldRef) RefSelection(a *AliasRef) {
+	fr.AliasRef = a
+}
+
+// RefSources Must call after binding or will get empty
+func (fr *FieldRef) RefSources() []StreamName {
+	if fr.StreamName == AliasStream {
+		return fr.refSources
+	} else if fr.StreamName != "" {
+		return []StreamName{fr.StreamName}
+	} else {
+		return nil
+	}
+}
+
+// SetRefSource Only call this for alias field ref
+func (fr *FieldRef) SetRefSource(names []StreamName) {
+	fr.refSources = names
+}
+
+type AliasRef struct {
+	// MUST have, It is used for evaluation
+	Expression Expr
+	// MUST have after binding, calculate once in initializer. Could be 0 when alias an Expression without col like "1+2"
+	refSources []StreamName
+	// optional, lazy set when calculating isAggregate
+	IsAggregate *bool
+}
+
+func NewAliasRef(e Expr) (*AliasRef, error) {
+	r := make(map[StreamName]bool)
+	var walkErr error
+	WalkFunc(e, func(n Node) bool {
+		switch f := n.(type) {
+		case *FieldRef:
+			switch f.StreamName {
+			case AliasStream:
+				walkErr = fmt.Errorf("cannot use alias %s inside another alias %v", f.Name, e)
+				return false
+			default:
+				r[f.StreamName] = true
+			}
+		}
+		return true
+	})
+	if walkErr != nil {
+		return nil, walkErr
+	}
+	rs := make([]StreamName, 0)
+	for k := range r {
+		rs = append(rs, k)
+	}
+	return &AliasRef{
+		Expression: e,
+		refSources: rs,
+	}, nil
+}
+
+// MockAliasRef is for testing only.
+func MockAliasRef(e Expr, r []StreamName, a *bool) *AliasRef {
+	return &AliasRef{e, r, a}
+}

pkg/ast/expr_test.go → pkg/ast/expr_ref_test.go


+ 71 - 0
pkg/ast/statement_test.go

@@ -0,0 +1,71 @@
+// Copyright 2023 EMQ Technologies Co., Ltd.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package ast
+
+import (
+	"testing"
+
+	"github.com/stretchr/testify/assert"
+)
+
+func TestFieldResult(t *testing.T) {
+	fields := Fields{
+		{
+			Name:  "Name",
+			AName: "AName",
+			Expr:  &IndexExpr{},
+		},
+		{
+			Name:  "Name",
+			AName: "AName",
+			Expr:  &FieldRef{},
+		},
+		{
+			Name:  "Name",
+			AName: "",
+			Expr:  nil,
+		},
+		{
+			Name:  "",
+			AName: "AName",
+			Expr:  nil,
+		},
+	}
+	fields.node()
+	fields.Swap(0, 1)
+	assert.Equal(t, Field{
+		Name:  "Name",
+		AName: "AName",
+		Expr:  &FieldRef{},
+	}, fields[0])
+	assert.Equal(t, 4, fields.Len())
+
+	for _, f := range fields {
+		if f.AName != "" {
+			assert.Equal(t, f.AName, f.GetName())
+			assert.False(t, f.IsColumn())
+			assert.True(t, f.IsSelectionField())
+		} else {
+			if _, ok := f.Expr.(*FieldRef); ok {
+				assert.True(t, f.IsColumn())
+				assert.True(t, f.IsSelectionField())
+			} else {
+				assert.False(t, f.IsColumn())
+				assert.False(t, f.IsSelectionField())
+			}
+			assert.Equal(t, f.Name, f.GetName())
+		}
+	}
+}

+ 117 - 0
pkg/cast/cast_test.go

@@ -60,6 +60,11 @@ func TestToString(t *testing.T) {
 			"test",
 		},
 		{
+			"test",
+			CONVERT_ALL,
+			"test",
+		},
+		{
 			[]byte("test"),
 			CONVERT_SAMEKIND,
 			"test",
@@ -218,6 +223,10 @@ func TestToIntResult(t *testing.T) {
 			0,
 		},
 		{
+			true,
+			1,
+		},
+		{
 			nil,
 			0,
 		},
@@ -271,6 +280,110 @@ func TestToIntResult(t *testing.T) {
 	}
 }
 
+func TestToFloatResult(t *testing.T) {
+	tests := []struct {
+		input any
+		want  float64
+	}{
+		{
+			100,
+			100,
+		},
+		{
+			int8(100),
+			100,
+		},
+		{
+			int16(100),
+			100,
+		},
+		{
+			int32(100),
+			100,
+		},
+		{
+			int64(100),
+			100,
+		},
+		{
+			uint(100),
+			100,
+		},
+		{
+			uint8(100),
+			100,
+		},
+		{
+			uint16(100),
+			100,
+		},
+		{
+			uint32(100),
+			100,
+		},
+		{
+			uint64(100),
+			100,
+		},
+		{
+			float32(100),
+			100,
+		},
+		{
+			float64(100),
+			100,
+		},
+		{
+			"100",
+			100,
+		},
+		{
+			false,
+			0,
+		},
+		{
+			true,
+			1,
+		},
+	}
+	for _, tt := range tests {
+		var (
+			got any
+			err error
+		)
+		got, err = ToFloat32(tt.input, CONVERT_ALL)
+		assert.NoError(t, err)
+		assert.Equal(t, float32(tt.want), got)
+
+		got, err = ToFloat64(tt.input, CONVERT_ALL)
+		assert.NoError(t, err)
+		assert.Equal(t, tt.want, got)
+	}
+
+	errTests := []any{
+		1,
+		int8(1),
+		int16(1),
+		int32(1),
+		int64(1),
+		uint(1),
+		uint8(1),
+		uint16(1),
+		uint32(1),
+		uint64(1),
+		true,
+		nil,
+		"1",
+	}
+	for _, input := range errTests {
+		_, err := ToFloat32(input, STRICT)
+		assert.Error(t, err)
+
+		_, err = ToFloat64(input, STRICT)
+		assert.Error(t, err)
+	}
+}
+
 func TestToUintResult(t *testing.T) {
 	tests := []struct {
 		input any
@@ -333,6 +446,10 @@ func TestToUintResult(t *testing.T) {
 			0,
 		},
 		{
+			true,
+			1,
+		},
+		{
 			nil,
 			0,
 		},

+ 36 - 0
pkg/cast/time_test.go

@@ -125,6 +125,30 @@ func TestInterfaceToTime(t *testing.T) {
 			false,
 		},
 		{
+			"2022-04-13 6:22:32.2",
+			"YYYY-MM-dd h:m:sS",
+			time.Date(2022, time.April, 13, 6, 22, 32, 200000000, time.UTC),
+			false,
+		},
+		{
+			"2022-04-13 6:22:32.23",
+			"YYYY-MM-dd h:m:sSS",
+			time.Date(2022, time.April, 13, 6, 22, 32, 230000000, time.UTC),
+			false,
+		},
+		{
+			"2022-04-13 Wed 06:22:32.233",
+			"YYYY-MM-dd EEE HH:m:ssSSS",
+			time.Date(2022, time.April, 13, 6, 22, 32, 233000000, time.UTC),
+			false,
+		},
+		{
+			"2022-04-13 Wednesday 06:22:32.233",
+			"YYYY-MM-dd EEEE HH:m:ssSSS",
+			time.Date(2022, time.April, 13, 6, 22, 32, 233000000, time.UTC),
+			false,
+		},
+		{
 			1649830952233,
 			"YYYY-MM-dd HH:mm:ssSSS",
 			time.Date(2022, time.April, 13, 6, 22, 32, 233000000, time.UTC),
@@ -149,6 +173,12 @@ func TestInterfaceToTime(t *testing.T) {
 			false,
 		},
 		{
+			"2022-04-13 06:22:32.233",
+			"YYYy-MM-dd HH:mm:ssSSS",
+			time.Date(2022, time.April, 13, 6, 22, 32, 233000000, time.UTC),
+			true,
+		},
+		{
 			struct{}{},
 			"YYYY-MM-dd HH:mm:ssSSS",
 			time.Date(2022, time.April, 13, 6, 22, 32, 233000000, time.UTC),
@@ -204,6 +234,12 @@ func TestInterfaceToUnixMilli(t *testing.T) {
 			false,
 		},
 		{
+			"2022-04-13 06:22:32.233",
+			"YYYy-MM-dd HH:mm:ssSSS",
+			1649830952233,
+			true,
+		},
+		{
 			struct{}{},
 			"YYYY-MM-dd HH:mm:ssSSS",
 			1649830952233,