@@ -3,6 +3,7 @@ package provider_test
3
3
import (
4
4
"fmt"
5
5
"regexp"
6
+ "strconv"
6
7
"strings"
7
8
"testing"
8
9
@@ -686,180 +687,224 @@ data "coder_parameter" "region" {
686
687
}
687
688
}
688
689
690
+ // TestParameterValidationEnforcement tests various parameter states and the
691
+ // validation enforcement that should be applied to them. The table is described
692
+ // by a markdown table. This is done so that the test cases can be more easily
693
+ // edited and read.
694
+ //
695
+ // Copy and paste the table to https://www.tablesgenerator.com/markdown_tables for easier editing
696
+ //
689
697
//nolint:paralleltest,tparallel // Parameters load values from env vars
690
698
func TestParameterValidationEnforcement (t * testing.T ) {
691
- for _ , tc := range []struct {
699
+ table := strings .TrimSpace (`
700
+ | Name | Type | Input Value | Default | Options | Validation | -> | Output Value | Optional | Error |
701
+ |---------------|---------------|-------------|---------|-------------------|------------|----|--------------|----------|--------------|
702
+ | | Empty Vals | | | | | | | | |
703
+ | Emty | string,number | | | | | | "" | false | |
704
+ | EmtyOpts | string,number | | | 1,2,3 | | | "" | false | |
705
+ | EmtyRegex | string | | | | world | | | | regex error |
706
+ | EmtyMin | number | | | | 1-10 | | | | 1 < < 10 |
707
+ | EmtyMinOpt | number | | | 1,2,3 | 2-5 | | | | 2 < < 5 |
708
+ | EmtyRegexOpt | string | | | "hello","goodbye" | goodbye | | | | regex error |
709
+ | EmtyRegexOk | string | | | | .* | | "" | false | |
710
+ | | | | | | | | | | |
711
+ | | Default Set | No inputs | | | | | | | |
712
+ | NumDef | number | | 5 | | | | 5 | true | |
713
+ | NumDefVal | number | | 5 | | 3-7 | | 5 | true | |
714
+ | NumDefInv | number | | 5 | | 10- | | 5 | | 10 < 5 < 0 |
715
+ | NumDefOpts | number | | 5 | 1,3,5,7 | 2-6 | | 5 | true | |
716
+ | NumDefNotOpts | number | | 5 | 1,3,7,9 | 2-6 | | | | valid option |
717
+ | | | | | | | | | | |
718
+ | StrDef | string | | hello | | | | hello | true | |
719
+ | StrDefInv | string | | hello | | world | | | | regex error |
720
+ | StrDefOpts | string | | a | a,b,c | | | a | true | |
721
+ | StrDefNotOpts | string | | a | b,c,d | | | | | valid option |
722
+ | StrDefOpts | string | | a | a,b,c,d,e,f | [a-c] | | a | true | |
723
+ | | | | | | | | | | |
724
+ | | Input Vals | | | | | | | | |
725
+ | NumIns | number | 3 | 5 | | | | 3 | true | |
726
+ | | | | | | | | | | |
727
+ | | | | | | | | | | |
728
+ ` )
729
+
730
+ type row struct {
692
731
Name string
693
- Config string
694
- Value string
695
- ExpectError * regexp.Regexp
696
- Check func (state * terraform.ResourceState )
697
- }{
698
- // Empty
699
- {
700
- Name : "EmptyString" ,
701
- Config : `
702
- data "coder_parameter" "parameter" {
703
- name = "parameter"
704
- type = "string"
705
- }
706
- ` ,
707
- ExpectError : nil ,
708
- Check : func (state * terraform.ResourceState ) {
709
- attrs := state .Primary .Attributes
710
- for key , value := range map [string ]interface {}{
711
- "default" : "" ,
712
- "value" : "" ,
713
- "optional" : "false" ,
714
- } {
715
- require .Equal (t , value , attrs [key ])
716
- }
717
- },
718
- },
719
- {
720
- Name : "EmptyNumber" ,
721
- Config : `
722
- data "coder_parameter" "parameter" {
723
- name = "parameter"
724
- type = "number"
732
+ Types []string
733
+ InputValue string
734
+ Default string
735
+ Options []string
736
+ Validation * provider.Validation
737
+ OutputValue string
738
+ Optional bool
739
+ Error * regexp.Regexp
740
+ }
741
+
742
+ rows := make ([]row , 0 )
743
+ lines := strings .Split (table , "\n " )
744
+ validMinMax := regexp .MustCompile ("^[0-9]*-[0-9]*$" )
745
+ for _ , line := range lines [2 :] {
746
+ columns := strings .Split (line , "|" )
747
+ columns = columns [1 : len (columns )- 1 ]
748
+ for i := range columns {
749
+ // Trim the whitespace from all columns
750
+ columns [i ] = strings .TrimSpace (columns [i ])
751
+ }
752
+
753
+ if columns [0 ] == "" {
754
+ continue // Skip rows with empty names
755
+ }
756
+
757
+ optional , err := strconv .ParseBool (columns [8 ])
758
+ if columns [8 ] != "" {
759
+ // Value does not matter if not specified
760
+ require .NoError (t , err )
761
+ }
762
+
763
+ var rerr * regexp.Regexp
764
+ if columns [9 ] != "" {
765
+ rerr , err = regexp .Compile (columns [9 ])
766
+ if err != nil {
767
+ t .Fatalf ("failed to parse error column %q: %v" , columns [9 ], err )
725
768
}
726
- ` ,
727
- ExpectError : nil ,
728
- Check : func ( state * terraform. ResourceState ) {
729
- attrs := state . Primary . Attributes
730
- for key , value := range map [ string ] interface {}{
731
- "default" : "" ,
732
- "value" : "" ,
733
- "optional" : "false" ,
734
- } {
735
- require . Equal ( t , value , attrs [ key ])
736
- }
737
- },
738
- },
739
- // EmptyWithOption
740
- {
741
- Name : "EmptyWithOption" ,
742
- Config : `
743
- data "coder_parameter" "parameter" {
744
- name = "parameter"
745
- type = "number"
746
-
747
- option {
748
- name = "option"
749
- value = "5"
769
+ }
770
+ var options [] string
771
+ if columns [ 4 ] != "" {
772
+ options = strings . Split ( columns [ 4 ], "," )
773
+ }
774
+
775
+ var validation * provider. Validation
776
+ if columns [ 5 ] != "" {
777
+ // Min-Max validation should look like:
778
+ // 1-10 :: min=1, max=10
779
+ // -10 :: max=10
780
+ // 1- :: min=1
781
+ if validMinMax . MatchString ( columns [ 5 ]) {
782
+ parts := strings . Split ( columns [ 5 ], "-" )
783
+ min , _ := strconv . ParseInt ( parts [ 0 ], 10 , 64 )
784
+ max , _ := strconv . ParseInt ( parts [ 1 ], 10 , 64 )
785
+ validation = & provider. Validation {
786
+ Min : int ( min ),
787
+ MinDisabled : parts [ 0 ] == "" ,
788
+ Max : int ( max ),
789
+ MaxDisabled : parts [ 1 ] == "" ,
790
+ Monotonic : "" ,
791
+ Regex : "" ,
792
+ Error : "{min} < { value} < {max}" ,
750
793
}
751
- }
752
- ` ,
753
- ExpectError : nil ,
754
- Check : func (state * terraform.ResourceState ) {
755
- attrs := state .Primary .Attributes
756
- for key , value := range map [string ]interface {}{
757
- "default" : "" ,
758
- "value" : "" ,
759
- "optional" : "false" ,
760
- } {
761
- require .Equal (t , value , attrs [key ])
794
+ } else {
795
+ validation = & provider.Validation {
796
+ Min : 0 ,
797
+ MinDisabled : true ,
798
+ Max : 0 ,
799
+ MaxDisabled : true ,
800
+ Monotonic : "" ,
801
+ Regex : columns [5 ],
802
+ Error : "regex error" ,
762
803
}
763
- },
764
- },
765
- // DefaultSet
766
- {
767
- Name : "DefaultSet" ,
768
- Config : `
769
- data "coder_parameter" "parameter" {
770
- name = "parameter"
771
- type = "number"
772
- default = "5"
773
804
}
774
- ` ,
775
- ExpectError : nil ,
776
- Check : func (state * terraform.ResourceState ) {
777
- attrs := state .Primary .Attributes
778
- for key , value := range map [string ]interface {}{
779
- "default" : "5" ,
780
- "value" : "5" ,
781
- "optional" : "true" ,
782
- } {
783
- require .Equal (t , value , attrs [key ])
784
- }
785
- },
786
- },
787
- {
788
- Name : "DefaultSetInOption" ,
789
- Config : `
790
- data "coder_parameter" "parameter" {
791
- name = "parameter"
792
- type = "number"
793
- default = "5"
794
- option {
795
- name = "option"
796
- value = "5"
805
+ }
806
+
807
+ rows = append (rows , row {
808
+ Name : columns [0 ],
809
+ Types : strings .Split (columns [1 ], "," ),
810
+ InputValue : columns [2 ],
811
+ Default : columns [3 ],
812
+ Options : options ,
813
+ Validation : validation ,
814
+ OutputValue : columns [7 ],
815
+ Optional : optional ,
816
+ Error : rerr ,
817
+ })
818
+ }
819
+
820
+ stringLiteral := func (s string ) string {
821
+ if s == "" {
822
+ return `""`
823
+ }
824
+ return fmt .Sprintf ("%q" , s )
825
+ }
826
+
827
+ for rowIndex , row := range rows {
828
+ for _ , rt := range row .Types {
829
+ //nolint:paralleltest,tparallel // Parameters load values from env vars
830
+ t .Run (fmt .Sprintf ("%d|%s:%s" , rowIndex , row .Name , rt ), func (t * testing.T ) {
831
+ if row .InputValue != "" {
832
+ t .Setenv (provider .ParameterEnvironmentVariable ("parameter" ), row .InputValue )
797
833
}
798
- }
799
- ` ,
800
- ExpectError : nil ,
801
- Check : func (state * terraform.ResourceState ) {
802
- attrs := state .Primary .Attributes
803
- for key , value := range map [string ]interface {}{
804
- "default" : "5" ,
805
- "value" : "5" ,
806
- "optional" : "true" ,
807
- } {
808
- require .Equal (t , value , attrs [key ])
834
+
835
+ var cfg strings.Builder
836
+ cfg .WriteString ("data \" coder_parameter\" \" parameter\" {\n " )
837
+ cfg .WriteString ("\t name = \" parameter\" \n " )
838
+ cfg .WriteString (fmt .Sprintf ("\t type = \" %s\" \n " , rt ))
839
+ if row .Default != "" {
840
+ cfg .WriteString (fmt .Sprintf ("\t default = %s\n " , stringLiteral (row .Default )))
809
841
}
810
- },
811
- },
812
- {
813
- Name : "DefaultSetOutOption" ,
814
- Config : `
815
- data "coder_parameter" "parameter" {
816
- name = "parameter"
817
- type = "number"
818
- default = "2"
819
- option {
820
- name = "option"
821
- value = "5"
842
+
843
+ for _ , opt := range row .Options {
844
+ cfg .WriteString ("\t option {\n " )
845
+ cfg .WriteString (fmt .Sprintf ("\t \t name = %s\n " , stringLiteral (opt )))
846
+ cfg .WriteString (fmt .Sprintf ("\t \t value = %s\n " , stringLiteral (opt )))
847
+ cfg .WriteString ("\t }\n " )
822
848
}
823
- }
824
- ` ,
825
- ExpectError : nil ,
826
- Check : func (state * terraform.ResourceState ) {
827
- attrs := state .Primary .Attributes
828
- for key , value := range map [string ]interface {}{
829
- "default" : "5" ,
830
- "value" : "5" ,
831
- "optional" : "true" ,
832
- } {
833
- require .Equal (t , value , attrs [key ])
849
+
850
+ if row .Validation != nil {
851
+ cfg .WriteString ("\t validation {\n " )
852
+ if ! row .Validation .MinDisabled {
853
+ cfg .WriteString (fmt .Sprintf ("\t \t min = %d\n " , row .Validation .Min ))
854
+ }
855
+ if ! row .Validation .MaxDisabled {
856
+ cfg .WriteString (fmt .Sprintf ("\t \t max = %d\n " , row .Validation .Max ))
857
+ }
858
+ if row .Validation .Monotonic != "" {
859
+ cfg .WriteString (fmt .Sprintf ("\t \t monotonic = \" %s\" \n " , row .Validation .Monotonic ))
860
+ }
861
+ if row .Validation .Regex != "" {
862
+ cfg .WriteString (fmt .Sprintf ("\t \t regex = %q\n " , row .Validation .Regex ))
863
+ }
864
+ cfg .WriteString (fmt .Sprintf ("\t \t error = %q\n " , row .Validation .Error ))
865
+ cfg .WriteString ("\t }\n " )
834
866
}
835
- },
836
- },
837
- } {
838
- tc := tc
839
- //nolint:paralleltest,tparallel // Parameters load values from env vars
840
- t .Run (tc .Name , func (t * testing.T ) {
841
- if tc .Value != "" {
842
- t .Setenv (provider .ParameterEnvironmentVariable ("parameter" ), tc .Value )
843
- }
844
- resource .Test (t , resource.TestCase {
845
- ProviderFactories : coderFactory (),
846
- IsUnitTest : true ,
847
- Steps : []resource.TestStep {{
848
- Config : tc .Config ,
849
- ExpectError : tc .ExpectError ,
850
- Check : func (state * terraform.State ) error {
851
- require .Len (t , state .Modules , 1 )
852
- require .Len (t , state .Modules [0 ].Resources , 1 )
853
- param := state .Modules [0 ].Resources ["data.coder_parameter.parameter" ]
854
- require .NotNil (t , param )
855
- if tc .Check != nil {
856
- tc .Check (param )
857
- }
858
- return nil
859
- },
860
- }},
867
+
868
+ cfg .WriteString ("}\n " )
869
+
870
+ resource .Test (t , resource.TestCase {
871
+ ProviderFactories : coderFactory (),
872
+ IsUnitTest : true ,
873
+ Steps : []resource.TestStep {{
874
+ Config : cfg .String (),
875
+ ExpectError : row .Error ,
876
+ Check : func (state * terraform.State ) error {
877
+ require .Len (t , state .Modules , 1 )
878
+ require .Len (t , state .Modules [0 ].Resources , 1 )
879
+ param := state .Modules [0 ].Resources ["data.coder_parameter.parameter" ]
880
+ require .NotNil (t , param )
881
+
882
+ if row .Default == "" {
883
+ _ , ok := param .Primary .Attributes ["default" ]
884
+ require .False (t , ok , "default should not be set" )
885
+ } else {
886
+ require .Equal (t , strings .Trim (row .Default , `"` ), param .Primary .Attributes ["default" ])
887
+ }
888
+
889
+ if row .OutputValue == "" {
890
+ _ , ok := param .Primary .Attributes ["value" ]
891
+ require .False (t , ok , "output value should not be set" )
892
+ } else {
893
+ require .Equal (t , strings .Trim (row .OutputValue , `"` ), param .Primary .Attributes ["value" ])
894
+ }
895
+
896
+ for key , expected := range map [string ]string {
897
+ "optional" : strconv .FormatBool (row .Optional ),
898
+ } {
899
+ require .Equal (t , expected , param .Primary .Attributes [key ])
900
+ }
901
+
902
+ return nil
903
+ },
904
+ }},
905
+ })
861
906
})
862
- })
907
+ }
863
908
}
864
909
}
865
910
0 commit comments