github.com/bingoohuang/gg@v0.0.0-20240325092523-45da7dee9335/pkg/yaml/validate_test.go (about)

     1  package yaml_test
     2  
     3  import (
     4  	"strings"
     5  	"testing"
     6  
     7  	"github.com/bingoohuang/gg/pkg/yaml"
     8  	"github.com/go-playground/validator/v10"
     9  )
    10  
    11  func TestStructValidator(t *testing.T) {
    12  	type Inner struct {
    13  		Required string `validate:"required"`
    14  		Lt10     int    `validate:"lt=10"`
    15  	}
    16  	cases := []struct {
    17  		TestName    string
    18  		YAMLContent string
    19  		ExpectedErr string
    20  		Instance    interface{}
    21  	}{
    22  		{
    23  			TestName: "Test Simple Validation",
    24  			YAMLContent: `---
    25  - name: john
    26    age: 20
    27  - name: tom
    28    age: -1
    29  - name: ken
    30    age: 10`,
    31  			ExpectedErr: `[5:8] Key: 'Age' Error:Field validation for 'Age' failed on the 'gte' tag
    32     2 | - name: john
    33     3 |   age: 20
    34     4 | - name: tom
    35  >  5 |   age: -1
    36                ^
    37     6 | - name: ken
    38     7 |   age: 10`,
    39  			Instance: &[]struct {
    40  				Name string `yaml:"name" validate:"required"`
    41  				Age  int    `yaml:"age" validate:"gte=0,lt=120"`
    42  			}{},
    43  		},
    44  		{
    45  			TestName: "Test Missing Required Field",
    46  			YAMLContent: `---
    47  - name: john
    48    age: 20
    49  - age: 10`,
    50  			ExpectedErr: `[4:1] Key: 'Name' Error:Field validation for 'Name' failed on the 'required' tag
    51     1 | ---
    52     2 | - name: john
    53     3 |   age: 20
    54  >  4 | - age: 10
    55         ^
    56  `,
    57  			Instance: &[]struct {
    58  				Name string `yaml:"name" validate:"required"`
    59  				Age  int    `yaml:"age" validate:"gte=0,lt=120"`
    60  			}{},
    61  		},
    62  		{
    63  			TestName: "Test Nested Validation Missing Internal Required",
    64  			YAMLContent: `---
    65  name: john
    66  age: 10
    67  addr:
    68    number: seven`,
    69  			ExpectedErr: `[4:5] Key: 'State' Error:Field validation for 'State' failed on the 'required' tag
    70     1 | ---
    71     2 | name: john
    72     3 | age: 10
    73  >  4 | addr:
    74             ^
    75     5 |   number: seven`,
    76  			Instance: &struct {
    77  				Name string `yaml:"name" validate:"required"`
    78  				Age  int    `yaml:"age" validate:"gte=0,lt=120"`
    79  				Addr struct {
    80  					Number string `yaml:"number" validate:"required"`
    81  					State  string `yaml:"state" validate:"required"`
    82  				} `yaml:"addr"`
    83  			}{},
    84  		},
    85  		{
    86  			TestName: "Test nested Validation with unknown field",
    87  			YAMLContent: `---
    88  name: john
    89  age: 20
    90  addr:
    91    number: seven
    92    state: washington
    93    error: error
    94  `,
    95  			ExpectedErr: `[7:3] unknown field "error"
    96     4 | addr:
    97     5 |   number: seven
    98     6 |   state: washington
    99  >  7 |   error: error
   100           ^
   101  `,
   102  			Instance: &struct {
   103  				Name string `yaml:"name" validate:"required"`
   104  				Age  int    `yaml:"age" validate:"gte=0,lt=120"`
   105  				Addr *struct {
   106  					Number string `yaml:"number" validate:"required"`
   107  					State  string `yaml:"state" validate:"required"`
   108  				} `yaml:"addr" validate:"required"`
   109  			}{},
   110  		},
   111  		{
   112  			TestName: "Test Validation with wrong field type",
   113  			YAMLContent: `---
   114  name: myDocument
   115  roles:
   116    name: myRole
   117    permissions:
   118  	- hello
   119  	- how
   120  	- are
   121  	- you
   122  	`,
   123  			ExpectedErr: `[4:7] mapping was used where sequence is expected
   124     1 | ---
   125     2 | name: myDocument
   126     3 | roles:
   127  >  4 |   name: myRole
   128               ^
   129     5 |   permissions:
   130     6 | 	- hello
   131     7 | 	- how
   132     8 | `,
   133  			Instance: &struct {
   134  				Name  string `yaml:"name"`
   135  				Roles []struct {
   136  					Name        string   `yaml:"name"`
   137  					Permissions []string `yaml:"permissions"`
   138  				} `yaml:"roles"`
   139  			}{},
   140  		},
   141  		{
   142  			TestName: "Test inline validation missing required",
   143  			YAMLContent: `---
   144  name: john
   145  age: 20
   146  `,
   147  			ExpectedErr: `Key: 'Inner.Required' Error:Field validation for 'Required' failed on the 'required' tag`,
   148  			Instance: &struct {
   149  				Name  string `yaml:"name" validate:"required"`
   150  				Age   int    `yaml:"age" validate:"gte=0,lt=120"`
   151  				Inner `yaml:",inline"`
   152  			}{},
   153  		},
   154  		{
   155  			TestName: "Test inline validation field error",
   156  			YAMLContent: `---
   157  name: john
   158  age: 20
   159  required: present
   160  lt10: 20
   161  `,
   162  			ExpectedErr: `[5:7] Key: 'Inner.Lt10' Error:Field validation for 'Lt10' failed on the 'lt' tag
   163     2 | name: john
   164     3 | age: 20
   165     4 | required: present
   166  >  5 | lt10: 20
   167               ^
   168  `,
   169  			Instance: &struct {
   170  				Name  string `yaml:"name" validate:"required"`
   171  				Age   int    `yaml:"age" validate:"gte=0,lt=120"`
   172  				Inner `yaml:",inline"`
   173  			}{},
   174  		},
   175  	}
   176  
   177  	for _, tc := range cases {
   178  		tc := tc // NOTE: https://github.com/golang/go/wiki/CommonMistakes#using-goroutines-on-loop-iterator-variables
   179  		t.Run(tc.TestName, func(t *testing.T) {
   180  			validate := validator.New()
   181  			dec := yaml.NewDecoder(
   182  				strings.NewReader(tc.YAMLContent),
   183  				yaml.Validator(validate),
   184  				yaml.Strict(),
   185  			)
   186  			err := dec.Decode(tc.Instance)
   187  			switch {
   188  			case tc.ExpectedErr != "" && err == nil:
   189  				t.Fatal("expected error")
   190  			case tc.ExpectedErr == "" && err != nil:
   191  				t.Fatalf("unexpected error: %v", err)
   192  			case tc.ExpectedErr != "" && tc.ExpectedErr != err.Error():
   193  				t.Fatalf("expected `%s` but actual `%s`", tc.ExpectedErr, err.Error())
   194  			}
   195  		})
   196  	}
   197  }