THRIFT-5423: IDL parameter validation for Go
Closes https://github.com/apache/thrift/pull/2469.
diff --git a/lib/go/test/tests/validate_test.go b/lib/go/test/tests/validate_test.go
new file mode 100644
index 0000000..957a8df
--- /dev/null
+++ b/lib/go/test/tests/validate_test.go
@@ -0,0 +1,494 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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 tests
+
+import (
+ "encoding/json"
+ "errors"
+ "strconv"
+ "testing"
+
+ "github.com/apache/thrift/lib/go/test/gopath/src/validatetest"
+ thrift "github.com/apache/thrift/lib/go/thrift"
+)
+
+func TestBasicValidator(t *testing.T) {
+ bt := validatetest.NewBasicTest()
+ if err := bt.Validate(); err != nil {
+ t.Error(err)
+ }
+ var ve *thrift.ValidationError
+ bt = validatetest.NewBasicTest()
+ bt.Bool1 = thrift.BoolPtr(false)
+ if err := bt.Validate(); err == nil {
+ t.Error("Expected vt.const error for Bool1")
+ } else if errors.As(err, &ve) {
+ if ve.Check() != "vt.const" {
+ t.Errorf("Expected vt.const check error, but got %v", ve.Check())
+ }
+ if ve.Field() != "Bool1" {
+ t.Errorf("Expected error for Bool1, but got %v", ve.Field())
+ }
+ } else {
+ t.Errorf("Error cannot be unwrapped into *ValidationError: %v", err)
+ }
+ bt = validatetest.NewBasicTest()
+ bt.Byte1 = thrift.Int8Ptr(3)
+ if err := bt.Validate(); err == nil {
+ t.Errorf("Expected vt.lt error for Byte1")
+ } else if errors.As(err, &ve) {
+ if ve.Check() != "vt.lt" {
+ t.Errorf("Expected vt.lt check error, but got %v", ve.Check())
+ }
+ if ve.Field() != "Byte1" {
+ t.Errorf("Expected error for Byte1, but got %v", ve.Field())
+ }
+ } else {
+ t.Errorf("Error cannot be unwrapped into *ValidationError: %v", err)
+ }
+ bt = validatetest.NewBasicTest()
+ bt.Double1 = thrift.Float64Ptr(3.0)
+ if err := bt.Validate(); err == nil {
+ t.Errorf("Expected vt.lt error for Double1")
+ } else if errors.As(err, &ve) {
+ if ve.Check() != "vt.lt" {
+ t.Errorf("Expected vt.lt check error, but got %v", ve.Check())
+ }
+ if ve.Field() != "Double1" {
+ t.Errorf("Expected error for Double1, but got %v", ve.Field())
+ }
+ } else {
+ t.Errorf("Error cannot be unwrapped into *ValidationError: %v", err)
+ }
+ bt = validatetest.NewBasicTest()
+ bt.String1 = thrift.StringPtr("other string")
+ if err := bt.Validate(); err == nil {
+ t.Errorf("Expected vt.const error for String1")
+ } else if errors.As(err, &ve) {
+ if ve.Check() != "vt.const" {
+ t.Errorf("Expected vt.const check error, but got %v", ve.Check())
+ }
+ if ve.Field() != "String1" {
+ t.Errorf("Expected error for String1, but got %v", ve.Field())
+ }
+ } else {
+ t.Errorf("Error cannot be unwrapped into *ValidationError: %v", err)
+ }
+ bt = validatetest.NewBasicTest()
+ bt.Binary1 = []byte("other binary")
+ if err := bt.Validate(); err == nil {
+ t.Errorf("Expected vt.const error for Binary1")
+ } else if errors.As(err, &ve) {
+ if ve.Check() != "vt.const" {
+ t.Errorf("Expected vt.const check error, but got %v", ve.Check())
+ }
+ if ve.Field() != "Binary1" {
+ t.Errorf("Expected error for Binary1, but got %v", ve.Field())
+ }
+ } else {
+ t.Errorf("Error cannot be unwrapped into *ValidationError: %v", err)
+ }
+ bt = validatetest.NewBasicTest()
+ bt.Map1 = make(map[string]string)
+ for i := 0; i < 11; i++ {
+ bt.Map1[strconv.Itoa(i)] = strconv.Itoa(i)
+ }
+ if err := bt.Validate(); err == nil {
+ t.Errorf("Expected vt.max_size error for Map1")
+ } else if errors.As(err, &ve) {
+ if ve.Check() != "vt.max_size" {
+ t.Errorf("Expected vt.max_size check error, but got %v", ve.Check())
+ }
+ if ve.Field() != "Map1" {
+ t.Errorf("Expected error for Map1, but got %v", ve.Field())
+ }
+ } else {
+ t.Errorf("Error cannot be unwrapped into *ValidationError: %v", err)
+ }
+ bt.Map1 = map[string]string{"012345678910": "0"}
+ if err := bt.Validate(); err == nil {
+ t.Errorf("Expected vt.max_size error for Map1")
+ } else if errors.As(err, &ve) {
+ if ve.Check() != "vt.max_size" {
+ t.Errorf("Expected vt.max_size check error, but got %v", ve.Check())
+ }
+ if ve.Field() != "Map1" {
+ t.Errorf("Expected error for Map1, but got %v", ve.Field())
+ }
+ } else {
+ t.Errorf("Error cannot be unwrapped into *ValidationError: %v", err)
+ }
+ bt.Map1 = map[string]string{"0": "012345678910"}
+ if err := bt.Validate(); err == nil {
+ t.Errorf("Expected vt.max_size error for Map1")
+ } else if errors.As(err, &ve) {
+ if ve.Check() != "vt.max_size" {
+ t.Errorf("Expected vt.max_size check error, but got %v", ve.Check())
+ }
+ if ve.Field() != "Map1" {
+ t.Errorf("Expected error for Map1, but got %v", ve.Field())
+ }
+ } else {
+ t.Errorf("Error cannot be unwrapped into *ValidationError: %v", err)
+ }
+ bt = validatetest.NewBasicTest()
+ for i := 0; i < 11; i++ {
+ bt.Set1 = append(bt.Set1, "0")
+ }
+ if err := bt.Validate(); err == nil {
+ t.Errorf("Expected vt.max_size error for Set1")
+ } else if errors.As(err, &ve) {
+ if ve.Check() != "vt.max_size" {
+ t.Errorf("Expected vt.max_size check error, but got %v", ve.Check())
+ }
+ if ve.Field() != "Set1" {
+ t.Errorf("Expected error for Set1, but got %v", ve.Field())
+ }
+ } else {
+ t.Errorf("Error cannot be unwrapped into *ValidationError: %v", err)
+ }
+ bt.Set1 = []string{"0"}
+ if err := bt.Validate(); err == nil {
+ t.Errorf("Expected vt.min_size error for Set1")
+ } else if errors.As(err, &ve) {
+ if ve.Check() != "vt.min_size" {
+ t.Errorf("Expected vt.min_size check error, but got %v", ve.Check())
+ }
+ if ve.Field() != "Set1" {
+ t.Errorf("Expected error for Set1, but got %v", ve.Field())
+ }
+ } else {
+ t.Errorf("Error cannot be unwrapped into *ValidationError: %v", err)
+ }
+ bt = validatetest.NewBasicTest()
+ bt.Enum1 = (*validatetest.EnumFoo)(thrift.Int64Ptr(int64(validatetest.EnumFoo_e2)))
+ if err := bt.Validate(); err == nil {
+ t.Errorf("Expected vt.in error for Enum1")
+ } else if errors.As(err, &ve) {
+ if ve.Check() != "vt.in" {
+ t.Errorf("Expected vt.in check error, but got %v", ve.Check())
+ }
+ if ve.Field() != "Enum1" {
+ t.Errorf("Expected error for Enum1, but got %v", ve.Field())
+ }
+ } else {
+ t.Errorf("Error cannot be unwrapped into *ValidationError: %v", err)
+ }
+}
+
+func TestFieldReference(t *testing.T) {
+ frt := validatetest.NewFieldReferenceTest()
+ if err := frt.Validate(); err != nil {
+ t.Error(err)
+ }
+ var ve *thrift.ValidationError
+ frt = validatetest.NewFieldReferenceTest()
+ frt.Bool2 = true
+ if err := frt.Validate(); err == nil {
+ t.Errorf("Expected vt.const error for Bool0")
+ } else if errors.As(err, &ve) {
+ if ve.Check() != "vt.const" {
+ t.Errorf("Expected vt.const check error, but got %v", ve.Check())
+ }
+ if ve.Field() != "Bool0" {
+ t.Errorf("Expected error for Bool0, but got %v", ve.Field())
+ }
+ } else {
+ t.Errorf("Error cannot be unwrapped into *ValidationError: %v", err)
+ }
+ frt = validatetest.NewFieldReferenceTest()
+ frt.Byte4 = 9
+ if err := frt.Validate(); err == nil {
+ t.Errorf("Expected vt.lt error for Byte0")
+ } else if errors.As(err, &ve) {
+ if ve.Check() != "vt.lt" {
+ t.Errorf("Expected vt.lt check error, but got %v", ve.Check())
+ }
+ if ve.Field() != "Byte0" {
+ t.Errorf("Expected error for Byte0, but got %v", ve.Field())
+ }
+ } else {
+ t.Errorf("Error cannot be unwrapped into *ValidationError: %v", err)
+ }
+ frt = validatetest.NewFieldReferenceTest()
+ frt.Double4 = 9
+ if err := frt.Validate(); err == nil {
+ t.Errorf("Expected vt.lt error for Double0")
+ } else if errors.As(err, &ve) {
+ if ve.Check() != "vt.lt" {
+ t.Errorf("Expected vt.lt check error, but got %v", ve.Check())
+ }
+ if ve.Field() != "Double0" {
+ t.Errorf("Expected error for Double0, but got %v", ve.Field())
+ }
+ } else {
+ t.Errorf("Error cannot be unwrapped into *ValidationError: %v", err)
+ }
+ frt = validatetest.NewFieldReferenceTest()
+ frt.String2 = "other string"
+ if err := frt.Validate(); err == nil {
+ t.Errorf("Expected vt.const error for String0")
+ } else if errors.As(err, &ve) {
+ if ve.Check() != "vt.const" {
+ t.Errorf("Expected vt.const check error, but got %v", ve.Check())
+ }
+ if ve.Field() != "String0" {
+ t.Errorf("Expected error for String0, but got %v", ve.Field())
+ }
+ } else {
+ t.Errorf("Error cannot be unwrapped into *ValidationError: %v", err)
+ }
+ frt = validatetest.NewFieldReferenceTest()
+ frt.Binary2 = []byte("other string")
+ if err := frt.Validate(); err == nil {
+ t.Errorf("Expected vt.const error for Binary0")
+ } else if errors.As(err, &ve) {
+ if ve.Check() != "vt.const" {
+ t.Errorf("Expected vt.const check error, but got %v", ve.Check())
+ }
+ if ve.Field() != "Binary0" {
+ t.Errorf("Expected error for Binary0, but got %v", ve.Field())
+ }
+ } else {
+ t.Errorf("Error cannot be unwrapped into *ValidationError: %v", err)
+ }
+ frt = validatetest.NewFieldReferenceTest()
+ frt.MaxSize = 8
+ frt.Map0 = make(map[string]string)
+ for i := 0; i < 9; i++ {
+ frt.Map0[strconv.Itoa(i)] = strconv.Itoa(i)
+ }
+ if err := frt.Validate(); err == nil {
+ t.Errorf("Expected vt.max_size error for Map0")
+ } else if errors.As(err, &ve) {
+ if ve.Check() != "vt.max_size" {
+ t.Errorf("Expected vt.max_size check error, but got %v", ve.Check())
+ }
+ if ve.Field() != "Map0" {
+ t.Errorf("Expected error for Map0, but got %v", ve.Field())
+ }
+ } else {
+ t.Errorf("Error cannot be unwrapped into *ValidationError: %v", err)
+ }
+ frt = validatetest.NewFieldReferenceTest()
+ frt.MaxSize = 8
+ for i := 0; i < 9; i++ {
+ frt.List0 = append(frt.List0, "0")
+ }
+ if err := frt.Validate(); err == nil {
+ t.Errorf("Expected vt.max_size error for List0")
+ } else if errors.As(err, &ve) {
+ if ve.Check() != "vt.max_size" {
+ t.Errorf("Expected vt.max_size check error, but got %v", ve.Check())
+ }
+ if ve.Field() != "List0" {
+ t.Errorf("Expected error for List0, but got %v", ve.Field())
+ }
+ } else {
+ t.Errorf("Error cannot be unwrapped into *ValidationError: %v", err)
+ }
+ frt = validatetest.NewFieldReferenceTest()
+ frt.MaxSize = 8
+ for i := 0; i < 9; i++ {
+ frt.Set0 = append(frt.Set0, "0")
+ }
+ if err := frt.Validate(); err == nil {
+ t.Errorf("Expected vt.max_size error for Set0")
+ } else if errors.As(err, &ve) {
+ if ve.Check() != "vt.max_size" {
+ t.Errorf("Expected vt.max_size check error, but got %v", ve.Check())
+ }
+ if ve.Field() != "Set0" {
+ t.Errorf("Expected error for Set0, but got %v", ve.Field())
+ }
+ } else {
+ t.Errorf("Error cannot be unwrapped into *ValidationError: %v", err)
+ }
+}
+
+func TestValidationFunction(t *testing.T) {
+ vft := validatetest.NewValidationFunctionTest()
+ if err := vft.Validate(); err != nil {
+ t.Error(err)
+ }
+ var ve *thrift.ValidationError
+ vft = validatetest.NewValidationFunctionTest()
+ vft.StringFoo = "some string"
+ if err := vft.Validate(); err == nil {
+ t.Errorf("Expected vt.in error for StringLength")
+ } else if errors.As(err, &ve) {
+ if ve.Check() != "vt.in" {
+ t.Errorf("Expected vt.in check error, but got %v", ve.Check())
+ }
+ if ve.Field() != "StringLength" {
+ t.Errorf("Expected error for StringLength, but got %v", ve.Field())
+ }
+ } else {
+ t.Errorf("Error cannot be unwrapped into *ValidationError: %v", err)
+ }
+}
+
+func TestAnnotationCompatibleTest(t *testing.T) {
+ act := validatetest.NewAnnotationCompatibleTest()
+ if err := act.Validate(); err != nil {
+ t.Error(err)
+ }
+ var ve *thrift.ValidationError
+ act = validatetest.NewAnnotationCompatibleTest()
+ act.Bool0 = false
+ if err := act.Validate(); err == nil {
+ t.Errorf("Expected vt.const error for Bool0")
+ } else if errors.As(err, &ve) {
+ if ve.Check() != "vt.const" {
+ t.Errorf("Expected vt.const check error, but got %v", ve.Check())
+ }
+ if ve.Field() != "Bool0" {
+ t.Errorf("Expected error for Bool0, but got %v", ve.Field())
+ }
+ } else {
+ t.Errorf("Error cannot be unwrapped into *ValidationError: %v", err)
+ }
+ act = validatetest.NewAnnotationCompatibleTest()
+ act.Byte0 = 3
+ if err := act.Validate(); err == nil {
+ t.Errorf("Expected vt.lt error for Byte0")
+ } else if errors.As(err, &ve) {
+ if ve.Check() != "vt.lt" {
+ t.Errorf("Expected vt.lt check error, but got %v", ve.Check())
+ }
+ if ve.Field() != "Byte0" {
+ t.Errorf("Expected error for Byte0, but got %v", ve.Field())
+ }
+ } else {
+ t.Errorf("Error cannot be unwrapped into *ValidationError: %v", err)
+ }
+ act = validatetest.NewAnnotationCompatibleTest()
+ act.Double0 = 3
+ if err := act.Validate(); err == nil {
+ t.Errorf("Expected vt.lt error for Double0")
+ } else if errors.As(err, &ve) {
+ if ve.Check() != "vt.lt" {
+ t.Errorf("Expected vt.lt check error, but got %v", ve.Check())
+ }
+ if ve.Field() != "Double0" {
+ t.Errorf("Expected error for Double0, but got %v", ve.Field())
+ }
+ } else {
+ t.Errorf("Error cannot be unwrapped into *ValidationError: %v", err)
+ }
+ act = validatetest.NewAnnotationCompatibleTest()
+ act.String0 = "other string"
+ if err := act.Validate(); err == nil {
+ t.Errorf("Expected vt.const error for String0")
+ } else if errors.As(err, &ve) {
+ if ve.Check() != "vt.const" {
+ t.Errorf("Expected vt.const check error, but got %v", ve.Check())
+ }
+ if ve.Field() != "String0" {
+ t.Errorf("Expected error for String0, but got %v", ve.Field())
+ }
+ } else {
+ t.Errorf("Error cannot be unwrapped into *ValidationError: %v", err)
+ }
+ act = validatetest.NewAnnotationCompatibleTest()
+ act.Binary0 = []byte("other string")
+ if err := act.Validate(); err == nil {
+ t.Errorf("Expected vt.const error for Binary0")
+ } else if errors.As(err, &ve) {
+ if ve.Check() != "vt.const" {
+ t.Errorf("Expected vt.const check error, but got %v", ve.Check())
+ }
+ if ve.Field() != "Binary0" {
+ t.Errorf("Expected error for Binary0, but got %v", ve.Field())
+ }
+ } else {
+ t.Errorf("Error cannot be unwrapped into *ValidationError: %v", err)
+ }
+ act = validatetest.NewAnnotationCompatibleTest()
+ act.Map0 = map[string]string{"0": "0", "1": "1", "2": "2"}
+ if err := act.Validate(); err == nil {
+ t.Errorf("Expected vt.max_size error for Map0")
+ } else if errors.As(err, &ve) {
+ if ve.Check() != "vt.max_size" {
+ t.Errorf("Expected vt.max_size check error, but got %v", ve.Check())
+ }
+ if ve.Field() != "Map0" {
+ t.Errorf("Expected error for Map0, but got %v", ve.Field())
+ }
+ } else {
+ t.Errorf("Error cannot be unwrapped into *ValidationError: %v", err)
+ }
+ act = validatetest.NewAnnotationCompatibleTest()
+ act.Set0 = []string{"0", "1", "2"}
+ if err := act.Validate(); err == nil {
+ t.Errorf("Expected vt.max_size error for Set0")
+ } else if errors.As(err, &ve) {
+ if ve.Check() != "vt.max_size" {
+ t.Errorf("Expected vt.max_size check error, but got %v", ve.Check())
+ }
+ if ve.Field() != "Set0" {
+ t.Errorf("Expected error for Set0, but got %v", ve.Field())
+ }
+ } else {
+ t.Errorf("Error cannot be unwrapped into *ValidationError: %v", err)
+ }
+ act = validatetest.NewAnnotationCompatibleTest()
+ act.List0 = []string{"0", "1", "2"}
+ if err := act.Validate(); err == nil {
+ t.Errorf("Expected vt.max_size error for List0")
+ } else if errors.As(err, &ve) {
+ if ve.Check() != "vt.max_size" {
+ t.Errorf("Expected vt.max_size check error, but got %v", ve.Check())
+ }
+ if ve.Field() != "List0" {
+ t.Errorf("Expected error for List0, but got %v", ve.Field())
+ }
+ } else {
+ t.Errorf("Error cannot be unwrapped into *ValidationError: %v", err)
+ }
+ act = validatetest.NewAnnotationCompatibleTest()
+ act.Enum0 = validatetest.EnumFoo_e1
+ if err := act.Validate(); err == nil {
+ t.Errorf("Expected vt.in error for Enum0")
+ } else if errors.As(err, &ve) {
+ if ve.Check() != "vt.in" {
+ t.Errorf("Expected vt.in check error, but got %v", ve.Check())
+ }
+ if ve.Field() != "Enum0" {
+ t.Errorf("Expected error for Enum0, but got %v", ve.Field())
+ }
+ } else {
+ t.Errorf("Error cannot be unwrapped into *ValidationError: %v", err)
+ }
+ fields := []string{"bool1", "byte1", "double1", "string1", "binary1", "enum1", "struct1", "list1", "set1", "map1"}
+ b, err := json.Marshal(act)
+ if err != nil {
+ t.Error(err)
+ }
+ jsonMap := make(map[string]interface{})
+ if err = json.Unmarshal(b, &jsonMap); err != nil {
+ t.Error(err)
+ }
+ for _, field := range fields {
+ if _, ok := jsonMap[field]; !ok {
+ t.Errorf("Expected field %s in JSON, but not found", field)
+ }
+ }
+}