Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit c640b02

Browse files
committedApr 14, 2025
begin adding invalid unit tests
1 parent d7fbec9 commit c640b02

File tree

2 files changed

+174
-33
lines changed

2 files changed

+174
-33
lines changed
 

‎provider/parameter.go

+52-33
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,6 @@ const (
5050
)
5151

5252
type Parameter struct {
53-
Value string
5453
Name string
5554
DisplayName string `mapstructure:"display_name"`
5655
Description string
@@ -86,7 +85,6 @@ func parameterDataSource() *schema.Resource {
8685

8786
var parameter Parameter
8887
err = mapstructure.Decode(struct {
89-
Value interface{}
9088
Name interface{}
9189
DisplayName interface{}
9290
Description interface{}
@@ -101,7 +99,6 @@ func parameterDataSource() *schema.Resource {
10199
Order interface{}
102100
Ephemeral interface{}
103101
}{
104-
Value: rd.Get("value"),
105102
Name: rd.Get("name"),
106103
DisplayName: rd.Get("display_name"),
107104
Description: rd.Get("description"),
@@ -126,14 +123,7 @@ func parameterDataSource() *schema.Resource {
126123
if err != nil {
127124
return diag.Errorf("decode parameter: %s", err)
128125
}
129-
var value string
130-
if parameter.Default != "" {
131-
err := valueIsType(parameter.Type, parameter.Default, defaultValuePath)
132-
if err != nil {
133-
return err
134-
}
135-
value = parameter.Default
136-
}
126+
value := parameter.Default
137127
envValue, ok := os.LookupEnv(ParameterEnvironmentVariable(parameter.Name))
138128
if ok {
139129
value = envValue
@@ -381,27 +371,27 @@ func fixValidationResourceData(rawConfig cty.Value, validation interface{}) (int
381371
return vArr, nil
382372
}
383373

384-
func valueIsType(typ OptionType, value string, attrPath cty.Path) diag.Diagnostics {
374+
func valueIsType(typ OptionType, value string) error {
385375
switch typ {
386376
case OptionTypeNumber:
387377
_, err := strconv.ParseFloat(value, 64)
388378
if err != nil {
389-
return diag.Errorf("%q is not a number", value)
379+
return fmt.Errorf("%q is not a number", value)
390380
}
391381
case OptionTypeBoolean:
392382
_, err := strconv.ParseBool(value)
393383
if err != nil {
394-
return diag.Errorf("%q is not a bool", value)
384+
return fmt.Errorf("%q is not a bool", value)
395385
}
396386
case OptionTypeListString:
397-
_, diags := valueIsListString(value, attrPath)
398-
if diags.HasError() {
399-
return diags
387+
_, err := valueIsListString(value)
388+
if err != nil {
389+
return err
400390
}
401391
case OptionTypeString:
402392
// Anything is a string!
403393
default:
404-
return diag.Errorf("invalid type %q", typ)
394+
return fmt.Errorf("invalid type %q", typ)
405395
}
406396
return nil
407397
}
@@ -447,9 +437,15 @@ func (v *Parameter) Valid(value string) diag.Diagnostics {
447437
},
448438
}
449439
}
450-
diags := valueIsType(optionType, option.Value, cty.Path{})
451-
if diags.HasError() {
452-
return diags
440+
err = valueIsType(optionType, option.Value)
441+
if err != nil {
442+
return diag.Diagnostics{
443+
{
444+
Severity: diag.Error,
445+
Summary: fmt.Sprintf("Option %q with value=%q is not of type %q", option.Name, option.Value, optionType),
446+
Detail: err.Error(),
447+
},
448+
}
453449
}
454450
optionValues[option.Value] = nil
455451
optionNames[option.Name] = nil
@@ -461,6 +457,18 @@ func (v *Parameter) Valid(value string) diag.Diagnostics {
461457

462458
// Validate the default value
463459
if v.Default != "" {
460+
err := valueIsType(v.Type, v.Default)
461+
if err != nil {
462+
return diag.Diagnostics{
463+
{
464+
Severity: diag.Error,
465+
Summary: fmt.Sprintf("Default value is not of type %q", v.Type),
466+
Detail: err.Error(),
467+
AttributePath: defaultValuePath,
468+
},
469+
}
470+
}
471+
464472
d := v.validValue(v.Default, optionType, optionValues, defaultValuePath)
465473
if d.HasError() {
466474
return d
@@ -473,6 +481,17 @@ func (v *Parameter) Valid(value string) diag.Diagnostics {
473481
return d
474482
}
475483

484+
err = valueIsType(v.Type, value)
485+
if err != nil {
486+
return diag.Diagnostics{
487+
{
488+
Severity: diag.Error,
489+
Summary: fmt.Sprintf("Parameter value is not of type %q", v.Type),
490+
Detail: err.Error(),
491+
},
492+
}
493+
}
494+
476495
return nil
477496
}
478497

@@ -488,9 +507,16 @@ func (v *Parameter) validValue(value string, optionType OptionType, optionValues
488507
if v.Type == OptionTypeListString && optionType == OptionTypeString {
489508
// If the type is list(string) and optionType is string, we have
490509
// to ensure all elements of the default exist as options.
491-
listValues, diags := valueIsListString(value, defaultValuePath)
492-
if diags.HasError() {
493-
return diags
510+
listValues, err := valueIsListString(value)
511+
if err != nil {
512+
return diag.Diagnostics{
513+
{
514+
Severity: diag.Error,
515+
Summary: "When using list(string) type, value must be a json encoded list of strings",
516+
Detail: err.Error(),
517+
AttributePath: defaultValuePath,
518+
},
519+
}
494520
}
495521

496522
// missing is used to construct a more helpful error message
@@ -608,18 +634,11 @@ func (v *Validation) Valid(typ OptionType, value string) error {
608634
return nil
609635
}
610636

611-
func valueIsListString(value string, path cty.Path) ([]string, diag.Diagnostics) {
637+
func valueIsListString(value string) ([]string, error) {
612638
var items []string
613639
err := json.Unmarshal([]byte(value), &items)
614640
if err != nil {
615-
return nil, diag.Diagnostics{
616-
{
617-
Severity: diag.Error,
618-
Summary: "When using list(string) type, value must be a json encoded list of strings",
619-
Detail: fmt.Sprintf("value %q is not a valid list of strings", value),
620-
AttributePath: path,
621-
},
622-
}
641+
return nil, fmt.Errorf("value %q is not a valid list of strings", value)
623642
}
624643
return items, nil
625644
}

‎provider/parameter_test.go

+122
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import (
88

99
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
1010
"github.com/hashicorp/terraform-plugin-sdk/v2/terraform"
11+
"github.com/stretchr/testify/assert"
1112
"github.com/stretchr/testify/require"
1213

1314
"github.com/coder/terraform-provider-coder/v2/provider"
@@ -686,6 +687,108 @@ data "coder_parameter" "region" {
686687
}
687688
}
688689

690+
func TestParameterValidation(t *testing.T) {
691+
t.Parallel()
692+
opts := func(vals ...string) []provider.Option {
693+
options := make([]provider.Option, 0, len(vals))
694+
for _, val := range vals {
695+
options = append(options, provider.Option{
696+
Name: val,
697+
Value: val,
698+
})
699+
}
700+
return options
701+
}
702+
703+
for _, tc := range []struct {
704+
Name string
705+
Parameter provider.Parameter
706+
Value string
707+
ExpectError *regexp.Regexp
708+
}{
709+
{
710+
Name: "ValidStringParameter",
711+
Parameter: provider.Parameter{
712+
Type: "string",
713+
},
714+
Value: "alpha",
715+
},
716+
// Test invalid states
717+
{
718+
Name: "InvalidFormType",
719+
Parameter: provider.Parameter{
720+
Type: "string",
721+
Option: opts("alpha", "bravo", "charlie"),
722+
FormType: provider.ParameterFormTypeSlider,
723+
},
724+
Value: "alpha",
725+
ExpectError: regexp.MustCompile("Invalid form_type for parameter"),
726+
},
727+
{
728+
Name: "NotInOptions",
729+
Parameter: provider.Parameter{
730+
Type: "string",
731+
Option: opts("alpha", "bravo", "charlie"),
732+
},
733+
Value: "delta", // not in option set
734+
ExpectError: regexp.MustCompile("Value must be a valid option"),
735+
},
736+
{
737+
Name: "NonUniqueOptionNames",
738+
Parameter: provider.Parameter{
739+
Type: "string",
740+
Option: opts("alpha", "alpha"),
741+
},
742+
Value: "alpha",
743+
ExpectError: regexp.MustCompile("Option names must be unique"),
744+
},
745+
{
746+
Name: "NonUniqueOptionValues",
747+
Parameter: provider.Parameter{
748+
Type: "string",
749+
Option: []provider.Option{
750+
{Name: "Alpha", Value: "alpha"},
751+
{Name: "AlphaAgain", Value: "alpha"},
752+
},
753+
},
754+
Value: "alpha",
755+
ExpectError: regexp.MustCompile("Option values must be unique"),
756+
},
757+
{
758+
Name: "IncorrectValueTypeOption",
759+
Parameter: provider.Parameter{
760+
Type: "number",
761+
Option: opts("not-a-number"),
762+
},
763+
Value: "5",
764+
ExpectError: regexp.MustCompile("is not a number"),
765+
},
766+
{
767+
Name: "IncorrectValueType",
768+
Parameter: provider.Parameter{
769+
Type: "number",
770+
},
771+
Value: "not-a-number",
772+
ExpectError: regexp.MustCompile("Parameter value is not of type \"number\""),
773+
},
774+
} {
775+
tc := tc
776+
t.Run(tc.Name, func(t *testing.T) {
777+
t.Parallel()
778+
diags := tc.Parameter.Valid(tc.Value)
779+
if tc.ExpectError != nil {
780+
require.True(t, diags.HasError())
781+
errMsg := fmt.Sprintf("%+v", diags[0]) // close enough
782+
require.Truef(t, tc.ExpectError.MatchString(errMsg), "got: %s", errMsg)
783+
} else {
784+
if !assert.False(t, diags.HasError()) {
785+
t.Logf("got: %+v", diags[0])
786+
}
787+
}
788+
})
789+
}
790+
}
791+
689792
func TestValueValidatesType(t *testing.T) {
690793
t.Parallel()
691794
for _, tc := range []struct {
@@ -798,6 +901,25 @@ func TestValueValidatesType(t *testing.T) {
798901
Value: `[]`,
799902
MinDisabled: true,
800903
MaxDisabled: true,
904+
}, {
905+
Name: "ValidListOfStrings",
906+
Type: "list(string)",
907+
Value: `["first","second","third"]`,
908+
MinDisabled: true,
909+
MaxDisabled: true,
910+
}, {
911+
Name: "InvalidListOfStrings",
912+
Type: "list(string)",
913+
Value: `["first","second","third"`,
914+
MinDisabled: true,
915+
MaxDisabled: true,
916+
Error: regexp.MustCompile("is not valid list of strings"),
917+
}, {
918+
Name: "EmptyListOfStrings",
919+
Type: "list(string)",
920+
Value: `[]`,
921+
MinDisabled: true,
922+
MaxDisabled: true,
801923
}} {
802924
tc := tc
803925
t.Run(tc.Name, func(t *testing.T) {

0 commit comments

Comments
 (0)
Please sign in to comment.