diff --git a/server/server_account_test.go b/server/server_account_test.go index 819c91e..147a129 100644 --- a/server/server_account_test.go +++ b/server/server_account_test.go @@ -22,7 +22,7 @@ func TestAccount_Signup_Success(t *testing.T) { "Authorization": util.BasicAuth("phil", "mypass"), }) require.Equal(t, 200, rr.Code) - token, _ := util.ReadJSON[apiAccountTokenResponse](io.NopCloser(rr.Body)) + token, _ := util.UnmarshalJSON[apiAccountTokenResponse](io.NopCloser(rr.Body)) require.NotEmpty(t, token.Token) require.True(t, time.Now().Add(71*time.Hour).Unix() < token.Expires) @@ -30,7 +30,7 @@ func TestAccount_Signup_Success(t *testing.T) { "Authorization": util.BearerAuth(token.Token), }) require.Equal(t, 200, rr.Code) - account, _ := util.ReadJSON[apiAccountResponse](io.NopCloser(rr.Body)) + account, _ := util.UnmarshalJSON[apiAccountResponse](io.NopCloser(rr.Body)) require.Equal(t, "phil", account.Username) require.Equal(t, "user", account.Role) } @@ -102,7 +102,7 @@ func TestAccount_Get_Anonymous(t *testing.T) { rr := request(t, s, "GET", "/v1/account", "", nil) require.Equal(t, 200, rr.Code) - account, _ := util.ReadJSON[apiAccountResponse](io.NopCloser(rr.Body)) + account, _ := util.UnmarshalJSON[apiAccountResponse](io.NopCloser(rr.Body)) require.Equal(t, "*", account.Username) require.Equal(t, string(user.RoleAnonymous), account.Role) require.Equal(t, "ip", account.Limits.Basis) @@ -124,7 +124,7 @@ func TestAccount_Get_Anonymous(t *testing.T) { rr = request(t, s, "GET", "/v1/account", "", nil) require.Equal(t, 200, rr.Code) - account, _ = util.ReadJSON[apiAccountResponse](io.NopCloser(rr.Body)) + account, _ = util.UnmarshalJSON[apiAccountResponse](io.NopCloser(rr.Body)) require.Equal(t, int64(2), account.Stats.Messages) require.Equal(t, int64(1002), account.Stats.MessagesRemaining) require.Equal(t, int64(1), account.Stats.Emails) @@ -151,7 +151,7 @@ func TestAccount_ChangeSettings(t *testing.T) { "Authorization": util.BearerAuth(token.Value), }) require.Equal(t, 200, rr.Code) - account, _ := util.ReadJSON[apiAccountResponse](io.NopCloser(rr.Body)) + account, _ := util.UnmarshalJSON[apiAccountResponse](io.NopCloser(rr.Body)) require.Equal(t, "de", account.Language) require.Equal(t, 86400, account.Notification.DeleteAfter) require.Equal(t, "juntos", account.Notification.Sound) @@ -171,7 +171,7 @@ func TestAccount_Subscription_AddUpdateDelete(t *testing.T) { "Authorization": util.BasicAuth("phil", "phil"), }) require.Equal(t, 200, rr.Code) - account, _ := util.ReadJSON[apiAccountResponse](io.NopCloser(rr.Body)) + account, _ := util.UnmarshalJSON[apiAccountResponse](io.NopCloser(rr.Body)) require.Equal(t, 1, len(account.Subscriptions)) require.NotEmpty(t, account.Subscriptions[0].ID) require.Equal(t, "http://abc.com", account.Subscriptions[0].BaseURL) @@ -188,7 +188,7 @@ func TestAccount_Subscription_AddUpdateDelete(t *testing.T) { "Authorization": util.BasicAuth("phil", "phil"), }) require.Equal(t, 200, rr.Code) - account, _ = util.ReadJSON[apiAccountResponse](io.NopCloser(rr.Body)) + account, _ = util.UnmarshalJSON[apiAccountResponse](io.NopCloser(rr.Body)) require.Equal(t, 1, len(account.Subscriptions)) require.Equal(t, subscriptionID, account.Subscriptions[0].ID) require.Equal(t, "http://abc.com", account.Subscriptions[0].BaseURL) @@ -204,7 +204,7 @@ func TestAccount_Subscription_AddUpdateDelete(t *testing.T) { "Authorization": util.BasicAuth("phil", "phil"), }) require.Equal(t, 200, rr.Code) - account, _ = util.ReadJSON[apiAccountResponse](io.NopCloser(rr.Body)) + account, _ = util.UnmarshalJSON[apiAccountResponse](io.NopCloser(rr.Body)) require.Equal(t, 0, len(account.Subscriptions)) } @@ -243,7 +243,7 @@ func TestAccount_ExtendToken(t *testing.T) { "Authorization": util.BasicAuth("phil", "phil"), }) require.Equal(t, 200, rr.Code) - token, err := util.ReadJSON[apiAccountTokenResponse](io.NopCloser(rr.Body)) + token, err := util.UnmarshalJSON[apiAccountTokenResponse](io.NopCloser(rr.Body)) require.Nil(t, err) time.Sleep(time.Second) @@ -252,7 +252,7 @@ func TestAccount_ExtendToken(t *testing.T) { "Authorization": util.BearerAuth(token.Token), }) require.Equal(t, 200, rr.Code) - extendedToken, err := util.ReadJSON[apiAccountTokenResponse](io.NopCloser(rr.Body)) + extendedToken, err := util.UnmarshalJSON[apiAccountTokenResponse](io.NopCloser(rr.Body)) require.Nil(t, err) require.Equal(t, token.Token, extendedToken.Token) require.True(t, token.Expires < extendedToken.Expires) @@ -277,7 +277,7 @@ func TestAccount_DeleteToken(t *testing.T) { "Authorization": util.BasicAuth("phil", "phil"), }) require.Equal(t, 200, rr.Code) - token, err := util.ReadJSON[apiAccountTokenResponse](io.NopCloser(rr.Body)) + token, err := util.UnmarshalJSON[apiAccountTokenResponse](io.NopCloser(rr.Body)) require.Nil(t, err) // Delete token failure (using basic auth) diff --git a/server/util.go b/server/util.go index 1fbe284..c1eff1f 100644 --- a/server/util.go +++ b/server/util.go @@ -124,8 +124,8 @@ func extractIPAddress(r *http.Request, behindProxy bool) netip.Addr { } func readJSONWithLimit[T any](r io.ReadCloser, limit int) (*T, error) { - obj, err := util.ReadJSONWithLimit[T](r, limit) - if err == util.ErrInvalidJSON { + obj, err := util.UnmarshalJSONWithLimit[T](r, limit) + if err == util.ErrUnmarshalJSON { return nil, errHTTPBadRequestJSONInvalid } else if err == util.ErrTooLargeJSON { return nil, errHTTPEntityTooLargeJSONBody diff --git a/util/util.go b/util/util.go index 1966a86..ac1ed7b 100644 --- a/util/util.go +++ b/util/util.go @@ -31,10 +31,10 @@ var ( noQuotesRegex = regexp.MustCompile(`^[-_./:@a-zA-Z0-9]+$`) ) -// Errors for ReadJSON and ReadJSONWithLimit functions +// Errors for UnmarshalJSON and UnmarshalJSONWithLimit functions var ( - ErrInvalidJSON = errors.New("invalid JSON") - ErrTooLargeJSON = errors.New("too large JSON") + ErrUnmarshalJSON = errors.New("unmarshalling JSON failed") + ErrTooLargeJSON = errors.New("too large JSON") ) // FileExists checks if a file exists, and returns true if it does @@ -295,17 +295,17 @@ func QuoteCommand(command []string) string { return strings.Join(quoted, " ") } -// ReadJSON reads the given io.ReadCloser into a struct -func ReadJSON[T any](body io.ReadCloser) (*T, error) { +// UnmarshalJSON reads the given io.ReadCloser into a struct +func UnmarshalJSON[T any](body io.ReadCloser) (*T, error) { var obj T if err := json.NewDecoder(body).Decode(&obj); err != nil { - return nil, ErrInvalidJSON + return nil, ErrUnmarshalJSON } return &obj, nil } -// ReadJSONWithLimit reads the given io.ReadCloser into a struct, but only until limit is reached -func ReadJSONWithLimit[T any](r io.ReadCloser, limit int) (*T, error) { +// UnmarshalJSONWithLimit reads the given io.ReadCloser into a struct, but only until limit is reached +func UnmarshalJSONWithLimit[T any](r io.ReadCloser, limit int) (*T, error) { defer r.Close() p, err := Peek(r, limit) if err != nil { @@ -315,7 +315,7 @@ func ReadJSONWithLimit[T any](r io.ReadCloser, limit int) (*T, error) { } var obj T if err := json.NewDecoder(p).Decode(&obj); err != nil { - return nil, ErrInvalidJSON + return nil, ErrUnmarshalJSON } return &obj, nil } diff --git a/util/util_test.go b/util/util_test.go index a435c06..04a988a 100644 --- a/util/util_test.go +++ b/util/util_test.go @@ -1,9 +1,11 @@ package util import ( + "io" "net/netip" "os" "path/filepath" + "strings" "testing" "github.com/stretchr/testify/require" @@ -161,3 +163,40 @@ func TestQuoteCommand(t *testing.T) { require.Equal(t, `rsync -av /home/phil/ root@example.com:/home/phil/`, QuoteCommand([]string{"rsync", "-av", "/home/phil/", "root@example.com:/home/phil/"})) require.Equal(t, `/home/sweet/home "Äöü this is a test" "\a\b"`, QuoteCommand([]string{"/home/sweet/home", "Äöü this is a test", "\\a\\b"})) } + +func TestBasicAuth(t *testing.T) { + require.Equal(t, "Basic cGhpbDpwaGls", BasicAuth("phil", "phil")) +} + +func TestBearerAuth(t *testing.T) { + require.Equal(t, "Bearer sometoken", BearerAuth("sometoken")) +} + +type testJSON struct { + Name string `json:"name"` + Something int `json:"something"` +} + +func TestReadJSON_Success(t *testing.T) { + v, err := UnmarshalJSON[testJSON](io.NopCloser(strings.NewReader(`{"name":"some name","something":99}`))) + require.Nil(t, err) + require.Equal(t, "some name", v.Name) + require.Equal(t, 99, v.Something) +} + +func TestReadJSON_Failure(t *testing.T) { + _, err := UnmarshalJSON[testJSON](io.NopCloser(strings.NewReader(`{"na`))) + require.Equal(t, ErrUnmarshalJSON, err) +} + +func TestReadJSONWithLimit_Success(t *testing.T) { + v, err := UnmarshalJSONWithLimit[testJSON](io.NopCloser(strings.NewReader(`{"name":"some name","something":99}`)), 100) + require.Nil(t, err) + require.Equal(t, "some name", v.Name) + require.Equal(t, 99, v.Something) +} + +func TestReadJSONWithLimit_FailureTooLong(t *testing.T) { + _, err := UnmarshalJSONWithLimit[testJSON](io.NopCloser(strings.NewReader(`{"name":"some name","something":99}`)), 10) + require.Equal(t, ErrTooLargeJSON, err) +}