-
Notifications
You must be signed in to change notification settings - Fork 6
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Unit tests for the util package (#14)
Co-authored-by: Nestor Oprysk <noprysk-ua@singlestore.com>
- Loading branch information
1 parent
148043b
commit 5d91e78
Showing
10 changed files
with
361 additions
and
9 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,79 @@ | ||
package util_test | ||
|
||
import ( | ||
"testing" | ||
|
||
"github.com/google/uuid" | ||
"github.com/hashicorp/terraform-plugin-framework/types" | ||
"github.com/singlestore-labs/singlestore-go/management" | ||
"github.com/singlestore-labs/terraform-provider-singlestoredb/internal/provider/util" | ||
"github.com/stretchr/testify/require" | ||
) | ||
|
||
func TestMaybeString(t *testing.T) { | ||
require.Nil(t, util.MaybeString(types.StringNull())) | ||
require.Nil(t, util.MaybeString(types.StringUnknown())) | ||
s := "bar" | ||
require.Equal(t, &s, util.MaybeString(types.StringValue(s))) | ||
} | ||
|
||
func TestToString(t *testing.T) { | ||
require.Empty(t, util.ToString(types.StringNull())) | ||
require.Empty(t, util.ToString(types.StringUnknown())) | ||
s := "buzz" | ||
require.Equal(t, s, util.ToString(types.StringValue(s))) | ||
} | ||
|
||
func TestMaybeStringValue(t *testing.T) { | ||
require.Equal(t, types.StringNull(), util.MaybeStringValue(nil)) | ||
s := "fizz" | ||
require.Equal(t, types.StringValue(s), util.MaybeStringValue(&s)) | ||
} | ||
|
||
func TestMaybeBool(t *testing.T) { | ||
require.Nil(t, util.MaybeBool(types.BoolNull())) | ||
require.Nil(t, util.MaybeBool(types.BoolUnknown())) | ||
require.True(t, util.Deref(util.MaybeBool(types.BoolValue(true)))) | ||
} | ||
|
||
func TestMaybeBoolValue(t *testing.T) { | ||
require.Equal(t, types.BoolNull(), util.MaybeBoolValue(nil)) | ||
require.Equal(t, types.BoolValue(true), util.MaybeBoolValue(util.Ptr(true))) | ||
} | ||
|
||
func TestUUIDStringValue(t *testing.T) { | ||
id := "9966fccf-5116-437e-a34f-008ee32e8d94" | ||
require.Equal(t, types.StringValue(id), util.UUIDStringValue(uuid.MustParse(id))) | ||
} | ||
|
||
func TestStringFirewallRanges(t *testing.T) { | ||
a := "192.168.5.10/24" | ||
b := "192.168.5.10/32" | ||
result := util.StringFirewallRanges([]types.String{types.StringValue(a), types.StringValue(b)}) | ||
require.Equal(t, []string{a, b}, result) | ||
} | ||
|
||
func TestFirewallRanges(t *testing.T) { | ||
a := "192.168.5.10/24" | ||
b := "192.168.5.10/32" | ||
result := util.FirewallRanges(nil) | ||
require.Empty(t, result) | ||
result = util.FirewallRanges(util.Ptr([]string{a, b})) | ||
require.Equal(t, []types.String{types.StringValue(a), types.StringValue(b)}, result) | ||
} | ||
|
||
func TestWorkspaceGroupStateStringValue(t *testing.T) { | ||
state := management.ACTIVE | ||
require.Equal(t, string(state), util.WorkspaceGroupStateStringValue(state).ValueString()) | ||
} | ||
|
||
func TestWorkspaceStateString(t *testing.T) { | ||
require.Nil(t, util.WorkspaceStateString(types.StringValue("something"))) | ||
active := string(management.WorkspaceStateACTIVE) | ||
require.Equal(t, management.WorkspaceStateACTIVE, util.Deref(util.WorkspaceStateString(types.StringValue(active)))) | ||
} | ||
|
||
func TestWorkspaceStateStringValue(t *testing.T) { | ||
state := management.WorkspaceStateACTIVE | ||
require.Equal(t, string(state), util.WorkspaceStateStringValue(state).ValueString()) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,14 +1,54 @@ | ||
package util | ||
|
||
import ( | ||
"errors" | ||
"fmt" | ||
"io" | ||
"net/http" | ||
|
||
"github.com/hashicorp/go-retryablehttp" | ||
) | ||
|
||
const respReadLimit = int64(4096) | ||
|
||
// NewHTTPClient creates an HTTP client for the Terraform provider. | ||
func NewHTTPClient() *http.Client { | ||
result := retryablehttp.NewClient() | ||
result.ErrorHandler = HandleError | ||
|
||
return result.StandardClient() | ||
} | ||
|
||
var _ retryablehttp.ErrorHandler = HandleError | ||
|
||
// HandleError overrides the default behavior of the library | ||
// by exposing the underlying issue because the underlying issue may be useful, e.g., | ||
// a customer running out of credits and still closing the body. | ||
// | ||
// This function is called if retries are expired, containing the last status | ||
// from the http library. If not specified, default behavior for the library is | ||
// to close the body and return an error indicating how many tries were attempted. | ||
// | ||
// The function is called only when server returns 500s. | ||
func HandleError(resp *http.Response, ierr error, numTries int) (*http.Response, error) { | ||
defer resp.Body.Close() | ||
|
||
body, err := io.ReadAll(io.LimitReader(resp.Body, respReadLimit)) | ||
if err != nil { | ||
result := fmt.Sprintf("giving up after %d attempts, unable to read response body, status code: %s, error: %s", numTries, http.StatusText(resp.StatusCode), err) | ||
|
||
return nil, maybeWithExtraError(result, ierr) | ||
} | ||
|
||
result := fmt.Sprintf("giving up after %d attempts, unexpected status code: %s, response: %s", numTries, http.StatusText(resp.StatusCode), body) | ||
|
||
return nil, maybeWithExtraError(result, ierr) | ||
} | ||
|
||
func maybeWithExtraError(main string, extra error) error { | ||
if extra == nil { | ||
return errors.New(main) | ||
} | ||
|
||
return fmt.Errorf("%s: %w", main, extra) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,85 @@ | ||
package util_test | ||
|
||
import ( | ||
"context" | ||
"errors" | ||
"io" | ||
"net/http" | ||
"net/http/httptest" | ||
"strconv" | ||
"testing" | ||
"testing/iotest" | ||
|
||
"github.com/singlestore-labs/terraform-provider-singlestoredb/internal/provider/util" | ||
"github.com/stretchr/testify/require" | ||
) | ||
|
||
func TestHTTPClientStatusOK(t *testing.T) { | ||
body := []byte("fizz") | ||
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { | ||
_, err := w.Write(body) | ||
require.NoError(t, err) | ||
})) | ||
t.Cleanup(server.Close) | ||
|
||
client := util.NewHTTPClient() | ||
req, err := http.NewRequestWithContext(context.Background(), http.MethodGet, server.URL, nil) | ||
require.NoError(t, err) | ||
resp, err := client.Do(req) | ||
require.NoError(t, err) | ||
defer resp.Body.Close() | ||
require.Equal(t, http.StatusOK, resp.StatusCode) | ||
result, err := io.ReadAll(resp.Body) | ||
require.NoError(t, err) | ||
require.Equal(t, body, result) | ||
} | ||
|
||
func TestHTTPClientStatusInternalServerError(t *testing.T) { | ||
body := []byte("fizz") | ||
attempts := 0 | ||
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { | ||
attempts++ | ||
w.WriteHeader(http.StatusInternalServerError) | ||
_, err := w.Write(body) | ||
require.NoError(t, err) | ||
})) | ||
t.Cleanup(server.Close) | ||
|
||
client := util.NewHTTPClient() | ||
req, err := http.NewRequestWithContext(context.Background(), http.MethodGet, server.URL, nil) | ||
require.NoError(t, err) | ||
_, err = client.Do(req) //nolint: bodyclose | ||
require.ErrorContains(t, err, string(body), "returns an error on 500s") | ||
require.ErrorContains(t, err, http.StatusText(http.StatusInternalServerError)) | ||
require.Greater(t, attempts, 1, "retries 500s") | ||
} | ||
|
||
func TestHTTPClientStatusConflict(t *testing.T) { | ||
body := []byte("insufficient credits") | ||
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { | ||
w.WriteHeader(http.StatusConflict) | ||
_, err := w.Write(body) | ||
require.NoError(t, err) | ||
})) | ||
t.Cleanup(server.Close) | ||
|
||
client := util.NewHTTPClient() | ||
req, err := http.NewRequestWithContext(context.Background(), http.MethodGet, server.URL, nil) | ||
require.NoError(t, err) | ||
resp, err := client.Do(req) | ||
require.NoError(t, err, "returns no error and a body on not 500s") | ||
defer resp.Body.Close() | ||
result, err := io.ReadAll(resp.Body) | ||
require.NoError(t, err) | ||
require.Equal(t, body, result) | ||
} | ||
|
||
func TestHandleError(t *testing.T) { | ||
readErr := errors.New("failed to read") | ||
extra := errors.New("extra") | ||
numTries := 3 | ||
_, err := util.HandleError(&http.Response{Body: io.NopCloser(iotest.ErrReader(readErr))}, extra, numTries) //nolint: bodyclose | ||
require.ErrorContains(t, err, readErr.Error()) | ||
require.ErrorContains(t, err, extra.Error()) | ||
require.ErrorContains(t, err, strconv.Itoa(numTries)) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.