Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commitac2f9bf

Browse files
authored
test: unit test to document validation behavior of parameters (#387)
Documenting validation behavior with a unit test
1 parentf871a43 commitac2f9bf

File tree

2 files changed

+302
-0
lines changed

2 files changed

+302
-0
lines changed

‎provider/parameter_test.go

Lines changed: 232 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,9 @@ package provider_test
22

33
import (
44
"fmt"
5+
"os"
56
"regexp"
7+
"strconv"
68
"strings"
79
"testing"
810

@@ -686,6 +688,217 @@ data "coder_parameter" "region" {
686688
}
687689
}
688690

691+
// TestParameterValidationEnforcement tests various parameter states and the
692+
// validation enforcement that should be applied to them. The table is described
693+
// by a markdown table. This is done so that the test cases can be more easily
694+
// edited and read.
695+
//
696+
// Copy and paste the table to https://www.tablesgenerator.com/markdown_tables for easier editing
697+
//
698+
//nolint:paralleltest,tparallel // Parameters load values from env vars
699+
funcTestParameterValidationEnforcement(t*testing.T) {
700+
// Some interesting observations:
701+
// - Validation logic does not apply to the value of 'options'
702+
//- [NumDefInvOpt] So an invalid option can be present and selected, but would fail
703+
// - Validation logic does not apply to the default if a value is given
704+
//- [NumIns/DefInv] So the default can be invalid if an input value is valid.
705+
// The value is therefore not really optional, but it is marked as such.
706+
// - [NumInsNotOptsVal | NumsInsNotOpts] values do not need to be in the option set?
707+
table,err:=os.ReadFile("testdata/parameter_table.md")
708+
require.NoError(t,err)
709+
710+
typerowstruct {
711+
Namestring
712+
Types []string
713+
InputValuestring
714+
Defaultstring
715+
Options []string
716+
Validation*provider.Validation
717+
OutputValuestring
718+
Optionalbool
719+
Error*regexp.Regexp
720+
}
721+
722+
rows:=make([]row,0)
723+
lines:=strings.Split(string(table),"\n")
724+
validMinMax:=regexp.MustCompile("^[0-9]*-[0-9]*$")
725+
for_,line:=rangelines[2:] {
726+
columns:=strings.Split(line,"|")
727+
columns=columns[1 :len(columns)-1]
728+
fori:=rangecolumns {
729+
// Trim the whitespace from all columns
730+
columns[i]=strings.TrimSpace(columns[i])
731+
}
732+
733+
ifcolumns[0]=="" {
734+
continue// Skip rows with empty names
735+
}
736+
737+
optional,err:=strconv.ParseBool(columns[8])
738+
ifcolumns[8]!="" {
739+
// Value does not matter if not specified
740+
require.NoError(t,err)
741+
}
742+
743+
varrerr*regexp.Regexp
744+
ifcolumns[9]!="" {
745+
rerr,err=regexp.Compile(columns[9])
746+
iferr!=nil {
747+
t.Fatalf("failed to parse error column %q: %v",columns[9],err)
748+
}
749+
}
750+
varoptions []string
751+
ifcolumns[4]!="" {
752+
options=strings.Split(columns[4],",")
753+
}
754+
755+
varvalidation*provider.Validation
756+
ifcolumns[5]!="" {
757+
// Min-Max validation should look like:
758+
//1-10 :: min=1, max=10
759+
//-10 :: max=10
760+
//1- :: min=1
761+
ifvalidMinMax.MatchString(columns[5]) {
762+
parts:=strings.Split(columns[5],"-")
763+
min,_:=strconv.ParseInt(parts[0],10,64)
764+
max,_:=strconv.ParseInt(parts[1],10,64)
765+
validation=&provider.Validation{
766+
Min:int(min),
767+
MinDisabled:parts[0]=="",
768+
Max:int(max),
769+
MaxDisabled:parts[1]=="",
770+
Monotonic:"",
771+
Regex:"",
772+
Error:"{min} < {value} < {max}",
773+
}
774+
}else {
775+
validation=&provider.Validation{
776+
Min:0,
777+
MinDisabled:true,
778+
Max:0,
779+
MaxDisabled:true,
780+
Monotonic:"",
781+
Regex:columns[5],
782+
Error:"regex error",
783+
}
784+
}
785+
}
786+
787+
rows=append(rows,row{
788+
Name:columns[0],
789+
Types:strings.Split(columns[1],","),
790+
InputValue:columns[2],
791+
Default:columns[3],
792+
Options:options,
793+
Validation:validation,
794+
OutputValue:columns[7],
795+
Optional:optional,
796+
Error:rerr,
797+
})
798+
}
799+
800+
stringLiteral:=func(sstring)string {
801+
ifs=="" {
802+
return`""`
803+
}
804+
returnfmt.Sprintf("%q",s)
805+
}
806+
807+
forrowIndex,row:=rangerows {
808+
for_,rt:=rangerow.Types {
809+
//nolint:paralleltest,tparallel // Parameters load values from env vars
810+
t.Run(fmt.Sprintf("%d|%s:%s",rowIndex,row.Name,rt),func(t*testing.T) {
811+
ifrow.InputValue!="" {
812+
t.Setenv(provider.ParameterEnvironmentVariable("parameter"),row.InputValue)
813+
}
814+
815+
ifrow.Error!=nil {
816+
ifrow.OutputValue!="" {
817+
t.Errorf("output value %q should not be set if error is set",row.OutputValue)
818+
}
819+
}
820+
821+
varcfg strings.Builder
822+
cfg.WriteString("data\"coder_parameter\"\"parameter\" {\n")
823+
cfg.WriteString("\tname =\"parameter\"\n")
824+
ifrt=="multi-select"||rt=="tag-select" {
825+
cfg.WriteString(fmt.Sprintf("\ttype =\"%s\"\n","list(string)"))
826+
cfg.WriteString(fmt.Sprintf("\tform_type =\"%s\"\n",rt))
827+
}else {
828+
cfg.WriteString(fmt.Sprintf("\ttype =\"%s\"\n",rt))
829+
}
830+
ifrow.Default!="" {
831+
cfg.WriteString(fmt.Sprintf("\tdefault = %s\n",stringLiteral(row.Default)))
832+
}
833+
834+
for_,opt:=rangerow.Options {
835+
cfg.WriteString("\toption {\n")
836+
cfg.WriteString(fmt.Sprintf("\t\tname = %s\n",stringLiteral(opt)))
837+
cfg.WriteString(fmt.Sprintf("\t\tvalue = %s\n",stringLiteral(opt)))
838+
cfg.WriteString("\t}\n")
839+
}
840+
841+
ifrow.Validation!=nil {
842+
cfg.WriteString("\tvalidation {\n")
843+
if!row.Validation.MinDisabled {
844+
cfg.WriteString(fmt.Sprintf("\t\tmin = %d\n",row.Validation.Min))
845+
}
846+
if!row.Validation.MaxDisabled {
847+
cfg.WriteString(fmt.Sprintf("\t\tmax = %d\n",row.Validation.Max))
848+
}
849+
ifrow.Validation.Monotonic!="" {
850+
cfg.WriteString(fmt.Sprintf("\t\tmonotonic =\"%s\"\n",row.Validation.Monotonic))
851+
}
852+
ifrow.Validation.Regex!="" {
853+
cfg.WriteString(fmt.Sprintf("\t\tregex = %q\n",row.Validation.Regex))
854+
}
855+
cfg.WriteString(fmt.Sprintf("\t\terror = %q\n",row.Validation.Error))
856+
cfg.WriteString("\t}\n")
857+
}
858+
859+
cfg.WriteString("}\n")
860+
861+
resource.Test(t, resource.TestCase{
862+
ProviderFactories:coderFactory(),
863+
IsUnitTest:true,
864+
Steps: []resource.TestStep{{
865+
Config:cfg.String(),
866+
ExpectError:row.Error,
867+
Check:func(state*terraform.State)error {
868+
require.Len(t,state.Modules,1)
869+
require.Len(t,state.Modules[0].Resources,1)
870+
param:=state.Modules[0].Resources["data.coder_parameter.parameter"]
871+
require.NotNil(t,param)
872+
873+
ifrow.Default=="" {
874+
_,ok:=param.Primary.Attributes["default"]
875+
require.False(t,ok,"default should not be set")
876+
}else {
877+
require.Equal(t,strings.Trim(row.Default,`"`),param.Primary.Attributes["default"])
878+
}
879+
880+
ifrow.OutputValue=="" {
881+
_,ok:=param.Primary.Attributes["value"]
882+
require.False(t,ok,"output value should not be set")
883+
}else {
884+
require.Equal(t,strings.Trim(row.OutputValue,`"`),param.Primary.Attributes["value"])
885+
}
886+
887+
forkey,expected:=rangemap[string]string{
888+
"optional":strconv.FormatBool(row.Optional),
889+
} {
890+
require.Equal(t,expected,param.Primary.Attributes[key],"optional")
891+
}
892+
893+
returnnil
894+
},
895+
}},
896+
})
897+
})
898+
}
899+
}
900+
}
901+
689902
funcTestValueValidatesType(t*testing.T) {
690903
t.Parallel()
691904
for_,tc:=range []struct {
@@ -798,6 +1011,25 @@ func TestValueValidatesType(t *testing.T) {
7981011
Value:`[]`,
7991012
MinDisabled:true,
8001013
MaxDisabled:true,
1014+
}, {
1015+
Name:"ValidListOfStrings",
1016+
Type:"list(string)",
1017+
Value:`["first","second","third"]`,
1018+
MinDisabled:true,
1019+
MaxDisabled:true,
1020+
}, {
1021+
Name:"InvalidListOfStrings",
1022+
Type:"list(string)",
1023+
Value:`["first","second","third"`,
1024+
MinDisabled:true,
1025+
MaxDisabled:true,
1026+
Error:regexp.MustCompile("is not valid list of strings"),
1027+
}, {
1028+
Name:"EmptyListOfStrings",
1029+
Type:"list(string)",
1030+
Value:`[]`,
1031+
MinDisabled:true,
1032+
MaxDisabled:true,
8011033
}} {
8021034
tc:=tc
8031035
t.Run(tc.Name,func(t*testing.T) {

‎provider/testdata/parameter_table.md

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
| Name| Type| Input| Default| Options| Validation| ->| Output| Optional| Error|
2+
|----------------------|---------------|-----------|---------|-------------------|------------|----|--------|----------|--------------|
3+
|| Empty Vals|||||||||
4+
| Empty| string,number|||||| ""| false||
5+
| EmptyList| list(string)|||||| ""| false||
6+
| EmptyMulti| tag-select|||||| ""| false||
7+
| EmptyOpts| string,number||| 1,2,3||| ""| false||
8+
| EmptyRegex| string|||| world|||| regex error|
9+
| EmptyMin| number|||| 1-10|||| 1 < < 10|
10+
| EmptyMinOpt| number||| 1,2,3| 2-5|||| 2 < < 5|
11+
| EmptyRegexOpt| string||| "hello","goodbye"| goodbye|||| regex error|
12+
| EmptyRegexOk| string|||| .*|| ""| false||
13+
|||||||||||
14+
|| Default Set| No inputs||||||||
15+
| NumDef| number|| 5|||| 5| true||
16+
| NumDefVal| number|| 5|| 3-7|| 5| true||
17+
| NumDefInv| number|| 5|| 10-|||| 10 < 5 < 0|
18+
| NumDefOpts| number|| 5| 1,3,5,7| 2-6|| 5| true||
19+
| NumDefNotOpts| number|| 5| 1,3,7,9| 2-6|||| valid option|
20+
| NumDefInvOpt| number|| 5| 1,3,5,7| 6-10|||| 6 < 5 < 10|
21+
|||||||||||
22+
| StrDef| string|| hello|||| hello| true||
23+
| StrDefInv| string|| hello|| world|||| regex error|
24+
| StrDefOpts| string|| a| a,b,c||| a| true||
25+
| StrDefNotOpts| string|| a| b,c,d||||| valid option|
26+
| StrDefValOpts| string|| a| a,b,c,d,e,f|[a-c]|| a| true||
27+
| StrDefInvOpt| string|| d| a,b,c,d,e,f|[a-c]|||| regex error|
28+
|||||||||||
29+
| LStrDef| list(string)||["a"]||||["a"]| true||
30+
| LStrDefOpts| list(string)||["a"]|["a"],["b"]|||["a"]| true||
31+
| LStrDefNotOpts| list(string)||["a"]|["b"],["c"]||||| valid option|
32+
|||||||||||
33+
| MulDef| tag-select||["a"]||||["a"]| true||
34+
| MulDefOpts| multi-select||["a"]| a,b|||["a"]| true||
35+
| MulDefNotOpts| multi-select||["a"]| b,c||||| valid option|
36+
|||||||||||
37+
|| Input Vals|||||||||
38+
| NumIns| number| 3||||| 3| false||
39+
| NumInsDef| number| 3| 5|||| 3| true||
40+
| NumIns/DefInv| number| 3| 5|| 1-3|| 3| true||
41+
| NumIns=DefInv| number| 5| 5|| 1-3|||| 1 < 5 < 3|
42+
| NumInsOpts| number| 3| 5| 1,2,3,4,5| 1-3|| 3| true||
43+
| NumInsNotOptsVal| number| 3| 5| 1,2,4,5| 1-3|| 3| true||
44+
| NumInsNotOptsInv| number| 3| 5| 1,2,4,5| 1-2||| true| 1 < 3 < 2|
45+
| NumInsNotOpts| number| 3| 5| 1,2,4,5||| 3| true||
46+
| NumInsNotOpts/NoDef| number| 3|| 1,2,4,5||| 3| false||
47+
|||||||||||
48+
| StrIns| string| c||||| c| false||
49+
| StrInsDef| string| c| e|||| c| true||
50+
| StrIns/DefInv| string| c| e||[a-c]|| c| true||
51+
| StrIns=DefInv| string| e| e||[a-c]|||| regex error|
52+
| StrInsOpts| string| c| e| a,b,c,d,e|[a-c]|| c| true||
53+
| StrInsNotOptsVal| string| c| e| a,b,d,e|[a-c]|| c| true||
54+
| StrInsNotOptsInv| string| c| e| a,b,d,e|[a-b]|||| regex error|
55+
| StrInsNotOpts| string| c| e| a,b,d,e||| c| true||
56+
| StrInsNotOpts/NoDef| string| c|| a,b,d,e||| c| false||
57+
| StrInsBadVal| string| c|| a,b,c,d,e| 1-10|||| min cannot|
58+
|||||||||||
59+
|| list(string)|||||||||
60+
| LStrIns| list(string)|["c"]|||||["c"]| false||
61+
| LStrInsDef| list(string)|["c"]|["e"]||||["c"]| true||
62+
| LStrIns/DefInv| list(string)|["c"]|["e"]||[a-c]|||| regex cannot|
63+
| LStrInsOpts| list(string)|["c"]|["e"]|["c"],["d"],["e"]|||["c"]| true||
64+
| LStrInsNotOpts| list(string)|["c"]|["e"]|["d"],["e"]|||["c"]| true||
65+
| LStrInsNotOpts/NoDef| list(string)|["c"]||["d"],["e"]|||["c"]| false||
66+
|||||||||||
67+
| MulInsOpts| multi-select|["c"]|["e"]| c,d,e|||["c"]| true||
68+
| MulInsNotOpts| multi-select|["c"]|["e"]| d,e|||["c"]| true||
69+
| MulInsNotOpts/NoDef| multi-select|["c"]|| d,e|||["c"]| false||
70+
| MulInsInvOpts| multi-select|["c"]|["e"]| c,d,e|[a-c]|||| regex cannot|

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp