Skip to content

Commit 6330964

Browse files
Fix empty string credentials validation issue, increase test coverage of credential validation (#7690) (#14279)
Signed-off-by: Modular Magician <[email protected]>
1 parent 43bfd88 commit 6330964

File tree

4 files changed

+131
-60
lines changed

4 files changed

+131
-60
lines changed

.changelog/7690.txt

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
```release-note:bug
2+
provider: fixed bug where `credentials` field could not be set as an empty string
3+
```

google/framework_provider_test.go

+61-40
Original file line numberDiff line numberDiff line change
@@ -57,48 +57,69 @@ func TestFrameworkProvider_impl(t *testing.T) {
5757
var _ provider.ProviderWithMetaSchema = New("test")
5858
}
5959

60-
func TestFrameworkProvider_loadCredentialsFromFile(t *testing.T) {
61-
cv := CredentialsValidator()
62-
63-
req := validator.StringRequest{
64-
ConfigValue: types.StringValue(testFakeCredentialsPath),
65-
}
66-
67-
resp := validator.StringResponse{
68-
Diagnostics: diag.Diagnostics{},
69-
}
70-
71-
cv.ValidateString(context.Background(), req, &resp)
72-
73-
if resp.Diagnostics.WarningsCount() > 0 {
74-
t.Errorf("Expected 0 warnings, got %d", resp.Diagnostics.WarningsCount())
75-
}
76-
if resp.Diagnostics.HasError() {
77-
t.Errorf("Expected 0 errors, got %d", resp.Diagnostics.ErrorsCount())
78-
}
79-
}
80-
81-
func TestFrameworkProvider_loadCredentialsFromJSON(t *testing.T) {
82-
contents, err := ioutil.ReadFile(testFakeCredentialsPath)
83-
if err != nil {
84-
t.Fatalf("Unexpected error: %s", err)
85-
}
86-
cv := CredentialsValidator()
87-
88-
req := validator.StringRequest{
89-
ConfigValue: types.StringValue(string(contents)),
90-
}
91-
92-
resp := validator.StringResponse{
93-
Diagnostics: diag.Diagnostics{},
60+
func TestFrameworkProvider_CredentialsValidator(t *testing.T) {
61+
cases := map[string]struct {
62+
ConfigValue func(t *testing.T) types.String
63+
ExpectedWarningCount int
64+
ExpectedErrorCount int
65+
}{
66+
"configuring credentials as a path to a credentials JSON file is valid": {
67+
ConfigValue: func(t *testing.T) types.String {
68+
return types.StringValue(testFakeCredentialsPath) // Path to a test fixture
69+
},
70+
},
71+
"configuring credentials as a path to a non-existant file is NOT valid": {
72+
ConfigValue: func(t *testing.T) types.String {
73+
return types.StringValue("./this/path/doesnt/exist.json") // Doesn't exist
74+
},
75+
ExpectedErrorCount: 1,
76+
},
77+
"configuring credentials as a credentials JSON string is valid": {
78+
ConfigValue: func(t *testing.T) types.String {
79+
contents, err := ioutil.ReadFile(testFakeCredentialsPath)
80+
if err != nil {
81+
t.Fatalf("Unexpected error: %s", err)
82+
}
83+
stringContents := string(contents)
84+
return types.StringValue(stringContents)
85+
},
86+
},
87+
"configuring credentials as an empty string is valid": {
88+
ConfigValue: func(t *testing.T) types.String {
89+
return types.StringValue("")
90+
},
91+
},
92+
"leaving credentials unconfigured is valid": {
93+
ConfigValue: func(t *testing.T) types.String {
94+
return types.StringNull()
95+
},
96+
},
9497
}
9598

96-
cv.ValidateString(context.Background(), req, &resp)
97-
if resp.Diagnostics.WarningsCount() > 0 {
98-
t.Errorf("Expected 0 warnings, got %d", resp.Diagnostics.WarningsCount())
99-
}
100-
if resp.Diagnostics.HasError() {
101-
t.Errorf("Expected 0 errors, got %d", resp.Diagnostics.ErrorsCount())
99+
for tn, tc := range cases {
100+
t.Run(tn, func(t *testing.T) {
101+
// Arrange
102+
req := validator.StringRequest{
103+
ConfigValue: tc.ConfigValue(t),
104+
}
105+
106+
resp := validator.StringResponse{
107+
Diagnostics: diag.Diagnostics{},
108+
}
109+
110+
cv := CredentialsValidator()
111+
112+
// Act
113+
cv.ValidateString(context.Background(), req, &resp)
114+
115+
// Assert
116+
if resp.Diagnostics.WarningsCount() > tc.ExpectedWarningCount {
117+
t.Errorf("Expected %d warnings, got %d", tc.ExpectedWarningCount, resp.Diagnostics.WarningsCount())
118+
}
119+
if resp.Diagnostics.ErrorsCount() > tc.ExpectedErrorCount {
120+
t.Errorf("Expected %d errors, got %d", tc.ExpectedErrorCount, resp.Diagnostics.ErrorsCount())
121+
}
122+
})
102123
}
103124
}
104125

google/framework_validators.go

+2-1
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import (
99

1010
"github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator"
1111
"github.com/hashicorp/terraform-plugin-framework/schema/validator"
12+
"github.com/hashicorp/terraform-plugin-framework/types"
1213

1314
googleoauth "golang.org/x/oauth2/google"
1415
)
@@ -36,7 +37,7 @@ func (v credentialsValidator) MarkdownDescription(ctx context.Context) string {
3637

3738
// ValidateString performs the validation.
3839
func (v credentialsValidator) ValidateString(ctx context.Context, request validator.StringRequest, response *validator.StringResponse) {
39-
if request.ConfigValue.IsNull() || request.ConfigValue.IsUnknown() {
40+
if request.ConfigValue.IsNull() || request.ConfigValue.IsUnknown() || request.ConfigValue.Equal(types.StringValue("")) {
4041
return
4142
}
4243

google/provider_test.go

+65-19
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package google
22

33
import (
44
"context"
5+
"errors"
56
"fmt"
67
"io/ioutil"
78
"log"
@@ -165,27 +166,72 @@ func AccTestPreCheck(t *testing.T) {
165166
}
166167
}
167168

168-
func TestProvider_loadCredentialsFromFile(t *testing.T) {
169-
ws, es := validateCredentials(testFakeCredentialsPath, "")
170-
if len(ws) != 0 {
171-
t.Errorf("Expected %d warnings, got %v", len(ws), ws)
172-
}
173-
if len(es) != 0 {
174-
t.Errorf("Expected %d errors, got %v", len(es), es)
169+
func TestProvider_validateCredentials(t *testing.T) {
170+
cases := map[string]struct {
171+
ConfigValue func(t *testing.T) interface{}
172+
ValueNotProvided bool
173+
ExpectedWarnings []string
174+
ExpectedErrors []error
175+
}{
176+
"configuring credentials as a path to a credentials JSON file is valid": {
177+
ConfigValue: func(t *testing.T) interface{} {
178+
return testFakeCredentialsPath // Path to a test fixture
179+
},
180+
},
181+
"configuring credentials as a path to a non-existant file is NOT valid": {
182+
ConfigValue: func(t *testing.T) interface{} {
183+
return "./this/path/doesnt/exist.json" // Doesn't exist
184+
},
185+
ExpectedErrors: []error{
186+
// As the file doesn't exist, so the function attempts to parse it as a JSON
187+
errors.New("JSON credentials are not valid: invalid character '.' looking for beginning of value"),
188+
},
189+
},
190+
"configuring credentials as a credentials JSON string is valid": {
191+
ConfigValue: func(t *testing.T) interface{} {
192+
contents, err := ioutil.ReadFile(testFakeCredentialsPath)
193+
if err != nil {
194+
t.Fatalf("Unexpected error: %s", err)
195+
}
196+
return string(contents)
197+
},
198+
},
199+
"configuring credentials as an empty string is valid": {
200+
ConfigValue: func(t *testing.T) interface{} {
201+
return ""
202+
},
203+
},
204+
"leaving credentials unconfigured is valid": {
205+
ValueNotProvided: true,
206+
},
175207
}
176-
}
177208

178-
func TestProvider_loadCredentialsFromJSON(t *testing.T) {
179-
contents, err := ioutil.ReadFile(testFakeCredentialsPath)
180-
if err != nil {
181-
t.Fatalf("Unexpected error: %s", err)
182-
}
183-
ws, es := validateCredentials(string(contents), "")
184-
if len(ws) != 0 {
185-
t.Errorf("Expected %d warnings, got %v", len(ws), ws)
186-
}
187-
if len(es) != 0 {
188-
t.Errorf("Expected %d errors, got %v", len(es), es)
209+
for tn, tc := range cases {
210+
t.Run(tn, func(t *testing.T) {
211+
// Arrange
212+
var configValue interface{}
213+
if !tc.ValueNotProvided {
214+
configValue = tc.ConfigValue(t)
215+
}
216+
217+
// Act
218+
// Note: second argument is currently unused by the function but is necessary to fulfill the SchemaValidateFunc type's function signature
219+
ws, es := validateCredentials(configValue, "")
220+
221+
// Assert
222+
if len(ws) != len(tc.ExpectedWarnings) {
223+
t.Errorf("Expected %d warnings, got %d: %v", len(tc.ExpectedWarnings), len(ws), ws)
224+
}
225+
if len(es) != len(tc.ExpectedErrors) {
226+
t.Errorf("Expected %d errors, got %d: %v", len(tc.ExpectedErrors), len(es), es)
227+
}
228+
229+
if len(tc.ExpectedErrors) > 0 {
230+
if es[0].Error() != tc.ExpectedErrors[0].Error() {
231+
t.Errorf("Expected first error to be \"%s\", got \"%s\"", tc.ExpectedErrors[0], es[0])
232+
}
233+
}
234+
})
189235
}
190236
}
191237

0 commit comments

Comments
 (0)