@@ -3,6 +3,7 @@ package provider_test
33import (
44 "fmt"
55 "regexp"
6+ "strconv"
67 "strings"
78 "testing"
89
@@ -686,180 +687,224 @@ data "coder_parameter" "region" {
686687 }
687688}
688689
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+ //
689697//nolint:paralleltest,tparallel // Parameters load values from env vars
690698func 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 {
692731 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 )
725768 }
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}" ,
750793 }
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" ,
762803 }
763- },
764- },
765- // DefaultSet
766- {
767- Name : "DefaultSet" ,
768- Config : `
769- data "coder_parameter" "parameter" {
770- name = "parameter"
771- type = "number"
772- default = "5"
773804 }
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 )
797833 }
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 )))
809841 }
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 " )
822848 }
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 " )
834866 }
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+ })
861906 })
862- })
907+ }
863908 }
864909}
865910
0 commit comments