Skip to content

Commit bf2b673

Browse files
committed
testing framework for form_type
1 parent 0b28b64 commit bf2b673

File tree

3 files changed

+230
-6
lines changed

3 files changed

+230
-6
lines changed

provider/formtype.go

+9-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,10 @@ import (
77
)
88

99
// OptionType is a type of option that can be used in the 'type' argument of
10-
// a parameter.
10+
// a parameter. These should match types as defined in terraform:
11+
// https://developer.hashicorp.com/terraform/language/expressions/types
12+
// The value have to be string literals, as type constraint keywords are not
13+
// supported in providers. :'(
1114
type OptionType string
1215

1316
const (
@@ -26,6 +29,11 @@ func OptionTypes() []OptionType {
2629
}
2730
}
2831

32+
// ParameterFormType is the list of supported form types for display in
33+
// the Coder "create workspace" form. These form types are functional as well
34+
// as cosmetic.
35+
// For example, "multi-select" has the type "list(string)" but the option
36+
// values are "string".
2937
type ParameterFormType string
3038

3139
const (

provider/formtype_test.go

+215
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,215 @@
1+
package provider_test
2+
3+
import (
4+
"fmt"
5+
"regexp"
6+
"strings"
7+
"testing"
8+
9+
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
10+
"github.com/hashicorp/terraform-plugin-sdk/v2/terraform"
11+
"github.com/stretchr/testify/assert"
12+
"github.com/stretchr/testify/require"
13+
14+
"github.com/coder/terraform-provider-coder/v2/provider"
15+
)
16+
17+
type formTypeTestCase struct {
18+
name string
19+
config string
20+
assert paramAssert
21+
expectError *regexp.Regexp
22+
}
23+
24+
type paramAssert struct {
25+
Default string
26+
FormType string
27+
Type string
28+
Styling string
29+
}
30+
31+
type formTypeCheck struct {
32+
formType provider.ParameterFormType
33+
optionType provider.OptionType
34+
optionsExist bool
35+
}
36+
37+
func TestValidateFormType(t *testing.T) {
38+
t.Parallel()
39+
40+
//formTypesChecked := make(map[provider.ParameterFormType]map[provider.OptionType]map[bool]struct{})
41+
formTypesChecked := make(map[formTypeCheck]struct{})
42+
43+
const paramName = "test_me"
44+
45+
cases := []formTypeTestCase{
46+
{
47+
// When nothing is specified
48+
name: "defaults",
49+
config: ezconfig(paramName, ezconfigOpts{}),
50+
assert: paramAssert{
51+
Default: "",
52+
FormType: "input",
53+
Type: "string",
54+
Styling: "",
55+
},
56+
},
57+
{
58+
name: "string radio",
59+
config: ezconfig(paramName, ezconfigOpts{Options: []string{"foo"}}),
60+
assert: paramAssert{
61+
Default: "",
62+
FormType: "radio",
63+
Type: "string",
64+
Styling: "",
65+
},
66+
},
67+
}
68+
69+
for _, c := range cases {
70+
t.Run(c.name, func(t *testing.T) {
71+
t.Parallel()
72+
if c.assert.Styling == "" {
73+
c.assert.Styling = "{}"
74+
}
75+
76+
formTypesChecked[formTypeTest(t, paramName, c)] = struct{}{}
77+
})
78+
}
79+
80+
// TODO: assume uncovered cases should fail
81+
t.Run("AssumeErrorCases", func(t *testing.T) {
82+
t.Skip()
83+
// requiredChecks loops through all possible form_type and option_type
84+
// combinations.
85+
requiredChecks := make([]formTypeCheck, 0)
86+
//requiredChecks := make(map[provider.ParameterFormType][]provider.OptionType)
87+
for _, ft := range provider.ParameterFormTypes() {
88+
//requiredChecks[ft] = make([]provider.OptionType, 0)
89+
for _, ot := range provider.OptionTypes() {
90+
requiredChecks = append(requiredChecks, formTypeCheck{
91+
formType: ft,
92+
optionType: ot,
93+
optionsExist: false,
94+
})
95+
requiredChecks = append(requiredChecks, formTypeCheck{
96+
formType: ft,
97+
optionType: ot,
98+
optionsExist: true,
99+
})
100+
//requiredChecks[ft] = append(requiredChecks[ft], ot)
101+
}
102+
}
103+
104+
for _, check := range requiredChecks {
105+
_, alreadyChecked := formTypesChecked[check]
106+
if alreadyChecked {
107+
continue
108+
}
109+
110+
// Assume it will fail
111+
112+
}
113+
114+
//checkedFormTypes := make([]provider.ParameterFormType, 0)
115+
//for ft, ot := range formTypesChecked {
116+
// checkedFormTypes = append(checkedFormTypes, ft)
117+
// var _ = ot
118+
//}
119+
//
120+
//// Fist check all form types have at least 1 test.
121+
//require.ElementsMatch(t, provider.ParameterFormTypes(), checkedFormTypes, "some form types are missing tests")
122+
//
123+
//// Then check each form type has tried with each option type
124+
//for expectedFt, expectedOptionTypes := range requiredChecks {
125+
// actual := formTypesChecked[expectedFt]
126+
//
127+
// assert.Equalf(t, expectedOptionTypes, maps.Keys(actual), "some option types are missing for form type %s", expectedFt)
128+
//
129+
// // Also check for a true/false
130+
// for _, expectedOptionType := range expectedOptionTypes {
131+
// assert.Equalf(t, []bool{true, false}, maps.Keys(actual[expectedOptionType]), "options should be both present and absent for form type %q, option type %q", expectedFt, expectedOptionType)
132+
// }
133+
//}
134+
})
135+
}
136+
137+
type ezconfigOpts struct {
138+
Options []string
139+
FormType string
140+
OptionType string
141+
Default string
142+
}
143+
144+
func ezconfig(paramName string, cfg ezconfigOpts) string {
145+
var body strings.Builder
146+
if cfg.Default != "" {
147+
body.WriteString(fmt.Sprintf("default = %q\n", cfg.Default))
148+
}
149+
if cfg.FormType != "" {
150+
body.WriteString(fmt.Sprintf("form_type = %q\n", cfg.FormType))
151+
}
152+
if cfg.OptionType != "" {
153+
body.WriteString(fmt.Sprintf("type = %q\n", cfg.OptionType))
154+
}
155+
156+
for i, opt := range cfg.Options {
157+
body.WriteString("option {\n")
158+
body.WriteString(fmt.Sprintf("name = \"val_%d\"\n", i))
159+
body.WriteString(fmt.Sprintf("value = %q\n", opt))
160+
body.WriteString("}\n")
161+
}
162+
163+
return coderParamHCL(paramName, body.String())
164+
}
165+
166+
func coderParamHCL(paramName string, body string) string {
167+
return fmt.Sprintf(`
168+
provider "coder" {
169+
}
170+
data "coder_parameter" "%s" {
171+
name = "%s"
172+
%s
173+
}
174+
`, paramName, paramName, body)
175+
}
176+
177+
func formTypeTest(t *testing.T, paramName string, c formTypeTestCase) formTypeCheck {
178+
var check formTypeCheck
179+
resource.Test(t, resource.TestCase{
180+
IsUnitTest: true,
181+
ProviderFactories: coderFactory(),
182+
Steps: []resource.TestStep{
183+
{
184+
Config: c.config,
185+
Check: func(state *terraform.State) error {
186+
require.Len(t, state.Modules, 1)
187+
require.Len(t, state.Modules[0].Resources, 1)
188+
189+
key := strings.Join([]string{"data", "coder_parameter", paramName}, ".")
190+
param := state.Modules[0].Resources[key]
191+
192+
assert.Equal(t, c.assert.Default, param.Primary.Attributes["default"], "default value")
193+
assert.Equal(t, c.assert.FormType, param.Primary.Attributes["form_type"], "form_type")
194+
assert.Equal(t, c.assert.Type, param.Primary.Attributes["type"], "type")
195+
assert.JSONEq(t, c.assert.Styling, param.Primary.Attributes["styling"], "styling")
196+
197+
ft := provider.ParameterFormType(param.Primary.Attributes["form_type"])
198+
ot := provider.OptionType(param.Primary.Attributes["type"])
199+
200+
// Option blocks are not stored in a very friendly format
201+
// here.
202+
optionsExist := param.Primary.Attributes["option.0.name"] != ""
203+
check = formTypeCheck{
204+
formType: ft,
205+
optionType: ot,
206+
optionsExist: optionsExist,
207+
}
208+
209+
return nil
210+
},
211+
},
212+
},
213+
})
214+
return check
215+
}

provider/parameter.go

+6-5
Original file line numberDiff line numberDiff line change
@@ -252,11 +252,12 @@ func parameterDataSource() *schema.Resource {
252252
ValidateFunc: validation.StringInSlice(toStrings(ParameterFormTypes()), false),
253253
Description: fmt.Sprintf("The type of this parameter. Must be one of: [%s].", strings.Join(toStrings(ParameterFormTypes()), ", ")),
254254
},
255-
"form_type_metadata": {
256-
Type: schema.TypeString,
257-
Default: `{}`,
258-
Description: "JSON encoded string containing the metadata for controlling the appearance of this parameter in the UI.",
259-
Optional: true,
255+
"styling": {
256+
Type: schema.TypeString,
257+
Default: `{}`,
258+
Description: "JSON encoded string containing the metadata for controlling the appearance of this parameter in the UI. " +
259+
"This option is purely cosmetic and does not affect the function of the parameter in terraform.",
260+
Optional: true,
260261
},
261262
"mutable": {
262263
Type: schema.TypeBool,

0 commit comments

Comments
 (0)