mirror of
https://github.com/absmach/magistrala.git
synced 2026-08-07 07:14:46 +00:00
NOISSUE - Update Auth service tests (#3300)
Signed-off-by: dusan <borovcanindusan1@gmail.com>
This commit is contained in:
@@ -0,0 +1,724 @@
|
||||
// Copyright (c) Abstract Machines
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package pats
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
apiutil "github.com/absmach/supermq/api/http/util"
|
||||
"github.com/absmach/supermq/auth"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
var valid = "valid"
|
||||
|
||||
func TestCreatePatReqValidate(t *testing.T) {
|
||||
cases := []struct {
|
||||
desc string
|
||||
req createPatReq
|
||||
err error
|
||||
}{
|
||||
{
|
||||
desc: "valid request",
|
||||
req: createPatReq{
|
||||
token: valid,
|
||||
Name: "test-pat",
|
||||
Description: "test description",
|
||||
Duration: 24 * time.Hour,
|
||||
},
|
||||
err: nil,
|
||||
},
|
||||
{
|
||||
desc: "empty token",
|
||||
req: createPatReq{
|
||||
token: "",
|
||||
Name: "test-pat",
|
||||
Description: "test description",
|
||||
Duration: 24 * time.Hour,
|
||||
},
|
||||
err: apiutil.ErrBearerToken,
|
||||
},
|
||||
{
|
||||
desc: "empty name",
|
||||
req: createPatReq{
|
||||
token: valid,
|
||||
Name: "",
|
||||
Description: "test description",
|
||||
Duration: 24 * time.Hour,
|
||||
},
|
||||
err: apiutil.ErrMissingName,
|
||||
},
|
||||
{
|
||||
desc: "whitespace only name",
|
||||
req: createPatReq{
|
||||
token: valid,
|
||||
Name: " ",
|
||||
Description: "test description",
|
||||
Duration: 24 * time.Hour,
|
||||
},
|
||||
err: apiutil.ErrMissingName,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range cases {
|
||||
t.Run(tc.desc, func(t *testing.T) {
|
||||
err := tc.req.validate()
|
||||
assert.Equal(t, tc.err, err, "validate() error = %v, expected %v", err, tc.err)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestCreatePatReqUnmarshalJSON(t *testing.T) {
|
||||
cases := []struct {
|
||||
desc string
|
||||
data string
|
||||
expected createPatReq
|
||||
err bool
|
||||
}{
|
||||
{
|
||||
desc: "valid JSON with duration",
|
||||
data: `{"name":"test-pat","description":"test desc","duration":"24h"}`,
|
||||
expected: createPatReq{
|
||||
Name: "test-pat",
|
||||
Description: "test desc",
|
||||
Duration: 24 * time.Hour,
|
||||
},
|
||||
err: false,
|
||||
},
|
||||
{
|
||||
desc: "invalid duration format",
|
||||
data: `{"name":"test-pat","description":"test desc","duration":"invalid"}`,
|
||||
err: true,
|
||||
},
|
||||
{
|
||||
desc: "invalid JSON",
|
||||
data: `{invalid json}`,
|
||||
err: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range cases {
|
||||
t.Run(tc.desc, func(t *testing.T) {
|
||||
var req createPatReq
|
||||
err := json.Unmarshal([]byte(tc.data), &req)
|
||||
if tc.err {
|
||||
assert.Error(t, err, "UnmarshalJSON() should return error")
|
||||
} else {
|
||||
assert.NoError(t, err, "UnmarshalJSON() should not return error")
|
||||
assert.Equal(t, tc.expected.Name, req.Name)
|
||||
assert.Equal(t, tc.expected.Description, req.Description)
|
||||
assert.Equal(t, tc.expected.Duration, req.Duration)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestRetrievePatReqValidate(t *testing.T) {
|
||||
cases := []struct {
|
||||
desc string
|
||||
req retrievePatReq
|
||||
err error
|
||||
}{
|
||||
{
|
||||
desc: "valid request",
|
||||
req: retrievePatReq{
|
||||
token: valid,
|
||||
id: "pat-id",
|
||||
},
|
||||
err: nil,
|
||||
},
|
||||
{
|
||||
desc: "empty token",
|
||||
req: retrievePatReq{
|
||||
token: "",
|
||||
id: "pat-id",
|
||||
},
|
||||
err: apiutil.ErrBearerToken,
|
||||
},
|
||||
{
|
||||
desc: "empty id",
|
||||
req: retrievePatReq{
|
||||
token: valid,
|
||||
id: "",
|
||||
},
|
||||
err: apiutil.ErrMissingPATID,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range cases {
|
||||
t.Run(tc.desc, func(t *testing.T) {
|
||||
err := tc.req.validate()
|
||||
assert.Equal(t, tc.err, err, "validate() error = %v, expected %v", err, tc.err)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestUpdatePatNameReqValidate(t *testing.T) {
|
||||
cases := []struct {
|
||||
desc string
|
||||
req updatePatNameReq
|
||||
err error
|
||||
}{
|
||||
{
|
||||
desc: "valid request",
|
||||
req: updatePatNameReq{
|
||||
token: valid,
|
||||
id: "pat-id",
|
||||
Name: "new-name",
|
||||
},
|
||||
err: nil,
|
||||
},
|
||||
{
|
||||
desc: "empty token",
|
||||
req: updatePatNameReq{
|
||||
token: "",
|
||||
id: "pat-id",
|
||||
Name: "new-name",
|
||||
},
|
||||
err: apiutil.ErrBearerToken,
|
||||
},
|
||||
{
|
||||
desc: "empty id",
|
||||
req: updatePatNameReq{
|
||||
token: valid,
|
||||
id: "",
|
||||
Name: "new-name",
|
||||
},
|
||||
err: apiutil.ErrMissingPATID,
|
||||
},
|
||||
{
|
||||
desc: "empty name",
|
||||
req: updatePatNameReq{
|
||||
token: valid,
|
||||
id: "pat-id",
|
||||
Name: "",
|
||||
},
|
||||
err: apiutil.ErrMissingName,
|
||||
},
|
||||
{
|
||||
desc: "whitespace only name",
|
||||
req: updatePatNameReq{
|
||||
token: valid,
|
||||
id: "pat-id",
|
||||
Name: " ",
|
||||
},
|
||||
err: apiutil.ErrMissingName,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range cases {
|
||||
t.Run(tc.desc, func(t *testing.T) {
|
||||
err := tc.req.validate()
|
||||
assert.Equal(t, tc.err, err, "validate() error = %v, expected %v", err, tc.err)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestUpdatePatDescriptionReqValidate(t *testing.T) {
|
||||
cases := []struct {
|
||||
desc string
|
||||
req updatePatDescriptionReq
|
||||
err error
|
||||
}{
|
||||
{
|
||||
desc: "valid request",
|
||||
req: updatePatDescriptionReq{
|
||||
token: valid,
|
||||
id: "pat-id",
|
||||
Description: "new description",
|
||||
},
|
||||
err: nil,
|
||||
},
|
||||
{
|
||||
desc: "empty token",
|
||||
req: updatePatDescriptionReq{
|
||||
token: "",
|
||||
id: "pat-id",
|
||||
Description: "new description",
|
||||
},
|
||||
err: apiutil.ErrBearerToken,
|
||||
},
|
||||
{
|
||||
desc: "empty id",
|
||||
req: updatePatDescriptionReq{
|
||||
token: valid,
|
||||
id: "",
|
||||
Description: "new description",
|
||||
},
|
||||
err: apiutil.ErrMissingPATID,
|
||||
},
|
||||
{
|
||||
desc: "empty description",
|
||||
req: updatePatDescriptionReq{
|
||||
token: valid,
|
||||
id: "pat-id",
|
||||
Description: "",
|
||||
},
|
||||
err: apiutil.ErrMissingDescription,
|
||||
},
|
||||
{
|
||||
desc: "whitespace only description",
|
||||
req: updatePatDescriptionReq{
|
||||
token: valid,
|
||||
id: "pat-id",
|
||||
Description: " ",
|
||||
},
|
||||
err: apiutil.ErrMissingDescription,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range cases {
|
||||
t.Run(tc.desc, func(t *testing.T) {
|
||||
err := tc.req.validate()
|
||||
assert.Equal(t, tc.err, err, "validate() error = %v, expected %v", err, tc.err)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestListPatsReqValidate(t *testing.T) {
|
||||
cases := []struct {
|
||||
desc string
|
||||
req listPatsReq
|
||||
err error
|
||||
}{
|
||||
{
|
||||
desc: "valid request",
|
||||
req: listPatsReq{
|
||||
token: valid,
|
||||
offset: 0,
|
||||
limit: 10,
|
||||
},
|
||||
err: nil,
|
||||
},
|
||||
{
|
||||
desc: "empty token",
|
||||
req: listPatsReq{
|
||||
token: "",
|
||||
offset: 0,
|
||||
limit: 10,
|
||||
},
|
||||
err: apiutil.ErrBearerToken,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range cases {
|
||||
t.Run(tc.desc, func(t *testing.T) {
|
||||
err := tc.req.validate()
|
||||
assert.Equal(t, tc.err, err, "validate() error = %v, expected %v", err, tc.err)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestDeletePatReqValidate(t *testing.T) {
|
||||
cases := []struct {
|
||||
desc string
|
||||
req deletePatReq
|
||||
err error
|
||||
}{
|
||||
{
|
||||
desc: "valid request",
|
||||
req: deletePatReq{
|
||||
token: valid,
|
||||
id: "pat-id",
|
||||
},
|
||||
err: nil,
|
||||
},
|
||||
{
|
||||
desc: "empty token",
|
||||
req: deletePatReq{
|
||||
token: "",
|
||||
id: "pat-id",
|
||||
},
|
||||
err: apiutil.ErrBearerToken,
|
||||
},
|
||||
{
|
||||
desc: "empty id",
|
||||
req: deletePatReq{
|
||||
token: valid,
|
||||
id: "",
|
||||
},
|
||||
err: apiutil.ErrMissingPATID,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range cases {
|
||||
t.Run(tc.desc, func(t *testing.T) {
|
||||
err := tc.req.validate()
|
||||
assert.Equal(t, tc.err, err, "validate() error = %v, expected %v", err, tc.err)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestResetPatSecretReqValidate(t *testing.T) {
|
||||
cases := []struct {
|
||||
desc string
|
||||
req resetPatSecretReq
|
||||
err error
|
||||
}{
|
||||
{
|
||||
desc: "valid request",
|
||||
req: resetPatSecretReq{
|
||||
token: valid,
|
||||
id: "pat-id",
|
||||
Duration: 24 * time.Hour,
|
||||
},
|
||||
err: nil,
|
||||
},
|
||||
{
|
||||
desc: "empty token",
|
||||
req: resetPatSecretReq{
|
||||
token: "",
|
||||
id: "pat-id",
|
||||
Duration: 24 * time.Hour,
|
||||
},
|
||||
err: apiutil.ErrBearerToken,
|
||||
},
|
||||
{
|
||||
desc: "empty id",
|
||||
req: resetPatSecretReq{
|
||||
token: valid,
|
||||
id: "",
|
||||
Duration: 24 * time.Hour,
|
||||
},
|
||||
err: apiutil.ErrMissingPATID,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range cases {
|
||||
t.Run(tc.desc, func(t *testing.T) {
|
||||
err := tc.req.validate()
|
||||
assert.Equal(t, tc.err, err, "validate() error = %v, expected %v", err, tc.err)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestResetPatSecretReqUnmarshalJSON(t *testing.T) {
|
||||
cases := []struct {
|
||||
desc string
|
||||
data string
|
||||
expected resetPatSecretReq
|
||||
err bool
|
||||
}{
|
||||
{
|
||||
desc: "valid JSON with duration",
|
||||
data: `{"duration":"48h"}`,
|
||||
expected: resetPatSecretReq{
|
||||
Duration: 48 * time.Hour,
|
||||
},
|
||||
err: false,
|
||||
},
|
||||
{
|
||||
desc: "invalid duration format",
|
||||
data: `{"duration":"invalid"}`,
|
||||
err: true,
|
||||
},
|
||||
{
|
||||
desc: "invalid JSON",
|
||||
data: `{invalid}`,
|
||||
err: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range cases {
|
||||
t.Run(tc.desc, func(t *testing.T) {
|
||||
var req resetPatSecretReq
|
||||
err := json.Unmarshal([]byte(tc.data), &req)
|
||||
if tc.err {
|
||||
assert.Error(t, err, "UnmarshalJSON() should return error")
|
||||
} else {
|
||||
assert.NoError(t, err, "UnmarshalJSON() should not return error")
|
||||
assert.Equal(t, tc.expected.Duration, req.Duration)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestRevokePatSecretReqValidate(t *testing.T) {
|
||||
cases := []struct {
|
||||
desc string
|
||||
req revokePatSecretReq
|
||||
err error
|
||||
}{
|
||||
{
|
||||
desc: "valid request",
|
||||
req: revokePatSecretReq{
|
||||
token: valid,
|
||||
id: "pat-id",
|
||||
},
|
||||
err: nil,
|
||||
},
|
||||
{
|
||||
desc: "empty token",
|
||||
req: revokePatSecretReq{
|
||||
token: "",
|
||||
id: "pat-id",
|
||||
},
|
||||
err: apiutil.ErrBearerToken,
|
||||
},
|
||||
{
|
||||
desc: "empty id",
|
||||
req: revokePatSecretReq{
|
||||
token: valid,
|
||||
id: "",
|
||||
},
|
||||
err: apiutil.ErrMissingPATID,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range cases {
|
||||
t.Run(tc.desc, func(t *testing.T) {
|
||||
err := tc.req.validate()
|
||||
assert.Equal(t, tc.err, err, "validate() error = %v, expected %v", err, tc.err)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestClearAllPATReqValidate(t *testing.T) {
|
||||
cases := []struct {
|
||||
desc string
|
||||
req clearAllPATReq
|
||||
err error
|
||||
}{
|
||||
{
|
||||
desc: "valid request",
|
||||
req: clearAllPATReq{
|
||||
token: valid,
|
||||
},
|
||||
err: nil,
|
||||
},
|
||||
{
|
||||
desc: "empty token",
|
||||
req: clearAllPATReq{
|
||||
token: "",
|
||||
},
|
||||
err: apiutil.ErrBearerToken,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range cases {
|
||||
t.Run(tc.desc, func(t *testing.T) {
|
||||
err := tc.req.validate()
|
||||
assert.Equal(t, tc.err, err, "validate() error = %v, expected %v", err, tc.err)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestAddScopeReqValidate(t *testing.T) {
|
||||
validScope := auth.Scope{
|
||||
OptionalDomainID: "domain1",
|
||||
EntityType: auth.GroupsType,
|
||||
EntityID: "entity1",
|
||||
Operation: auth.CreateOp,
|
||||
}
|
||||
|
||||
invalidScope := auth.Scope{
|
||||
OptionalDomainID: "",
|
||||
EntityType: auth.GroupsType,
|
||||
EntityID: "",
|
||||
Operation: auth.CreateOp,
|
||||
}
|
||||
|
||||
cases := []struct {
|
||||
desc string
|
||||
req addScopeReq
|
||||
err error
|
||||
}{
|
||||
{
|
||||
desc: "valid request",
|
||||
req: addScopeReq{
|
||||
token: valid,
|
||||
id: "pat-id",
|
||||
Scopes: []auth.Scope{validScope},
|
||||
},
|
||||
err: nil,
|
||||
},
|
||||
{
|
||||
desc: "empty token",
|
||||
req: addScopeReq{
|
||||
token: "",
|
||||
id: "pat-id",
|
||||
Scopes: []auth.Scope{validScope},
|
||||
},
|
||||
err: apiutil.ErrBearerToken,
|
||||
},
|
||||
{
|
||||
desc: "empty id",
|
||||
req: addScopeReq{
|
||||
token: valid,
|
||||
id: "",
|
||||
Scopes: []auth.Scope{validScope},
|
||||
},
|
||||
err: apiutil.ErrMissingPATID,
|
||||
},
|
||||
{
|
||||
desc: "empty scopes",
|
||||
req: addScopeReq{
|
||||
token: valid,
|
||||
id: "pat-id",
|
||||
Scopes: []auth.Scope{},
|
||||
},
|
||||
err: apiutil.ErrValidation,
|
||||
},
|
||||
{
|
||||
desc: "invalid scope",
|
||||
req: addScopeReq{
|
||||
token: valid,
|
||||
id: "pat-id",
|
||||
Scopes: []auth.Scope{invalidScope},
|
||||
},
|
||||
err: apiutil.ErrValidation,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range cases {
|
||||
t.Run(tc.desc, func(t *testing.T) {
|
||||
err := tc.req.validate()
|
||||
if tc.err != nil {
|
||||
assert.Error(t, err, "validate() should return error")
|
||||
} else {
|
||||
assert.NoError(t, err, "validate() should not return error")
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestRemoveScopeReqValidate(t *testing.T) {
|
||||
cases := []struct {
|
||||
desc string
|
||||
req removeScopeReq
|
||||
err error
|
||||
}{
|
||||
{
|
||||
desc: "valid request",
|
||||
req: removeScopeReq{
|
||||
token: valid,
|
||||
id: "pat-id",
|
||||
ScopesID: []string{"scope1", "scope2"},
|
||||
},
|
||||
err: nil,
|
||||
},
|
||||
{
|
||||
desc: "empty token",
|
||||
req: removeScopeReq{
|
||||
token: "",
|
||||
id: "pat-id",
|
||||
ScopesID: []string{"scope1"},
|
||||
},
|
||||
err: apiutil.ErrBearerToken,
|
||||
},
|
||||
{
|
||||
desc: "empty id",
|
||||
req: removeScopeReq{
|
||||
token: valid,
|
||||
id: "",
|
||||
ScopesID: []string{"scope1"},
|
||||
},
|
||||
err: apiutil.ErrMissingPATID,
|
||||
},
|
||||
{
|
||||
desc: "empty scopes list",
|
||||
req: removeScopeReq{
|
||||
token: valid,
|
||||
id: "pat-id",
|
||||
ScopesID: []string{},
|
||||
},
|
||||
err: apiutil.ErrValidation,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range cases {
|
||||
t.Run(tc.desc, func(t *testing.T) {
|
||||
err := tc.req.validate()
|
||||
assert.Equal(t, tc.err, err, "validate() error = %v, expected %v", err, tc.err)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestClearAllScopeReqValidate(t *testing.T) {
|
||||
cases := []struct {
|
||||
desc string
|
||||
req clearAllScopeReq
|
||||
err error
|
||||
}{
|
||||
{
|
||||
desc: "valid request",
|
||||
req: clearAllScopeReq{
|
||||
token: valid,
|
||||
id: "pat-id",
|
||||
},
|
||||
err: nil,
|
||||
},
|
||||
{
|
||||
desc: "empty token",
|
||||
req: clearAllScopeReq{
|
||||
token: "",
|
||||
id: "pat-id",
|
||||
},
|
||||
err: apiutil.ErrBearerToken,
|
||||
},
|
||||
{
|
||||
desc: "empty id",
|
||||
req: clearAllScopeReq{
|
||||
token: valid,
|
||||
id: "",
|
||||
},
|
||||
err: apiutil.ErrMissingPATID,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range cases {
|
||||
t.Run(tc.desc, func(t *testing.T) {
|
||||
err := tc.req.validate()
|
||||
assert.Equal(t, tc.err, err, "validate() error = %v, expected %v", err, tc.err)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestListScopesReqValidate(t *testing.T) {
|
||||
cases := []struct {
|
||||
desc string
|
||||
req listScopesReq
|
||||
err error
|
||||
}{
|
||||
{
|
||||
desc: "valid request",
|
||||
req: listScopesReq{
|
||||
token: valid,
|
||||
offset: 0,
|
||||
limit: 10,
|
||||
patID: "pat-id",
|
||||
},
|
||||
err: nil,
|
||||
},
|
||||
{
|
||||
desc: "empty token",
|
||||
req: listScopesReq{
|
||||
token: "",
|
||||
offset: 0,
|
||||
limit: 10,
|
||||
patID: "pat-id",
|
||||
},
|
||||
err: apiutil.ErrBearerToken,
|
||||
},
|
||||
{
|
||||
desc: "empty patID",
|
||||
req: listScopesReq{
|
||||
token: valid,
|
||||
offset: 0,
|
||||
limit: 10,
|
||||
patID: "",
|
||||
},
|
||||
err: apiutil.ErrMissingPATID,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range cases {
|
||||
t.Run(tc.desc, func(t *testing.T) {
|
||||
err := tc.req.validate()
|
||||
assert.Equal(t, tc.err, err, "validate() error = %v, expected %v", err, tc.err)
|
||||
})
|
||||
}
|
||||
}
|
||||
+193
-2
@@ -12,6 +12,195 @@ import (
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestKeyTypeString(t *testing.T) {
|
||||
cases := []struct {
|
||||
desc string
|
||||
keyType auth.KeyType
|
||||
expected string
|
||||
}{
|
||||
{
|
||||
desc: "Access key type",
|
||||
keyType: auth.AccessKey,
|
||||
expected: "access",
|
||||
},
|
||||
{
|
||||
desc: "Refresh key type",
|
||||
keyType: auth.RefreshKey,
|
||||
expected: "refresh",
|
||||
},
|
||||
{
|
||||
desc: "Recovery key type",
|
||||
keyType: auth.RecoveryKey,
|
||||
expected: "recovery",
|
||||
},
|
||||
{
|
||||
desc: "API key type",
|
||||
keyType: auth.APIKey,
|
||||
expected: "API",
|
||||
},
|
||||
{
|
||||
desc: "Personal access token type",
|
||||
keyType: auth.PersonalAccessToken,
|
||||
expected: "pat",
|
||||
},
|
||||
{
|
||||
desc: "Invitation key type",
|
||||
keyType: auth.InvitationKey,
|
||||
expected: "unknown",
|
||||
},
|
||||
{
|
||||
desc: "Unknown key type",
|
||||
keyType: auth.KeyType(100),
|
||||
expected: "unknown",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range cases {
|
||||
t.Run(tc.desc, func(t *testing.T) {
|
||||
got := tc.keyType.String()
|
||||
assert.Equal(t, tc.expected, got, "String() = %v, expected %v", got, tc.expected)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestKeyTypeValidate(t *testing.T) {
|
||||
cases := []struct {
|
||||
desc string
|
||||
keyType auth.KeyType
|
||||
expected bool
|
||||
}{
|
||||
{
|
||||
desc: "Valid access key",
|
||||
keyType: auth.AccessKey,
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
desc: "Valid refresh key",
|
||||
keyType: auth.RefreshKey,
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
desc: "Valid recovery key",
|
||||
keyType: auth.RecoveryKey,
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
desc: "Valid API key",
|
||||
keyType: auth.APIKey,
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
desc: "Valid personal access token",
|
||||
keyType: auth.PersonalAccessToken,
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
desc: "Valid invitation key",
|
||||
keyType: auth.InvitationKey,
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
desc: "Invalid key type (too large)",
|
||||
keyType: auth.KeyType(100),
|
||||
expected: false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range cases {
|
||||
t.Run(tc.desc, func(t *testing.T) {
|
||||
got := tc.keyType.Validate()
|
||||
assert.Equal(t, tc.expected, got, "Validate() = %v, expected %v", got, tc.expected)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestRoleString(t *testing.T) {
|
||||
cases := []struct {
|
||||
desc string
|
||||
role auth.Role
|
||||
expected string
|
||||
}{
|
||||
{
|
||||
desc: "User role",
|
||||
role: auth.UserRole,
|
||||
expected: "user",
|
||||
},
|
||||
{
|
||||
desc: "Admin role",
|
||||
role: auth.AdminRole,
|
||||
expected: "admin",
|
||||
},
|
||||
{
|
||||
desc: "Unknown role",
|
||||
role: auth.Role(100),
|
||||
expected: "unknown",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range cases {
|
||||
t.Run(tc.desc, func(t *testing.T) {
|
||||
got := tc.role.String()
|
||||
assert.Equal(t, tc.expected, got, "String() = %v, expected %v", got, tc.expected)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestRoleValidate(t *testing.T) {
|
||||
cases := []struct {
|
||||
desc string
|
||||
role auth.Role
|
||||
expected bool
|
||||
}{
|
||||
{
|
||||
desc: "Valid user role",
|
||||
role: auth.UserRole,
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
desc: "Valid admin role",
|
||||
role: auth.AdminRole,
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
desc: "Invalid role (zero)",
|
||||
role: auth.Role(0),
|
||||
expected: false,
|
||||
},
|
||||
{
|
||||
desc: "Invalid role (too large)",
|
||||
role: auth.Role(100),
|
||||
expected: false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range cases {
|
||||
t.Run(tc.desc, func(t *testing.T) {
|
||||
got := tc.role.Validate()
|
||||
assert.Equal(t, tc.expected, got, "Validate() = %v, expected %v", got, tc.expected)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestKeyString(t *testing.T) {
|
||||
key := auth.Key{
|
||||
ID: "test-id",
|
||||
Type: auth.APIKey,
|
||||
Issuer: "test-issuer",
|
||||
Subject: "test-subject",
|
||||
Role: auth.UserRole,
|
||||
IssuedAt: time.Now().UTC().Round(time.Second),
|
||||
ExpiresAt: time.Now().UTC().Add(24 * time.Hour).Round(time.Second),
|
||||
}
|
||||
|
||||
str := key.String()
|
||||
assert.NotEmpty(t, str, "String() should return non-empty string")
|
||||
assert.Contains(t, str, "test-id", "String() should contain ID")
|
||||
assert.Contains(t, str, "test-issuer", "String() should contain Issuer")
|
||||
assert.Contains(t, str, "test-subject", "String() should contain Subject")
|
||||
assert.Contains(t, str, "API", "String() should contain Type")
|
||||
assert.Contains(t, str, "user", "String() should contain Role")
|
||||
}
|
||||
|
||||
func TestExpired(t *testing.T) {
|
||||
exp := time.Now().Add(5 * time.Minute)
|
||||
exp1 := time.Now()
|
||||
@@ -54,7 +243,9 @@ func TestExpired(t *testing.T) {
|
||||
}
|
||||
|
||||
for _, tc := range cases {
|
||||
res := tc.key.Expired()
|
||||
assert.Equal(t, tc.expired, res, fmt.Sprintf("%s: expected %t got %t\n", tc.desc, tc.expired, res))
|
||||
t.Run(tc.desc, func(t *testing.T) {
|
||||
res := tc.key.Expired()
|
||||
assert.Equal(t, tc.expired, res, fmt.Sprintf("%s: expected %t got %t\n", tc.desc, tc.expired, res))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,615 @@
|
||||
// Copyright (c) Abstract Machines
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package auth_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/absmach/supermq/auth"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestOperationString(t *testing.T) {
|
||||
cases := []struct {
|
||||
desc string
|
||||
op auth.Operation
|
||||
expected string
|
||||
}{
|
||||
{
|
||||
desc: "Create operation",
|
||||
op: auth.CreateOp,
|
||||
expected: "create",
|
||||
},
|
||||
{
|
||||
desc: "Read operation",
|
||||
op: auth.ReadOp,
|
||||
expected: "read",
|
||||
},
|
||||
{
|
||||
desc: "List operation",
|
||||
op: auth.ListOp,
|
||||
expected: "list",
|
||||
},
|
||||
{
|
||||
desc: "Update operation",
|
||||
op: auth.UpdateOp,
|
||||
expected: "update",
|
||||
},
|
||||
{
|
||||
desc: "Delete operation",
|
||||
op: auth.DeleteOp,
|
||||
expected: "delete",
|
||||
},
|
||||
{
|
||||
desc: "Share operation",
|
||||
op: auth.ShareOp,
|
||||
expected: "share",
|
||||
},
|
||||
{
|
||||
desc: "Unshare operation",
|
||||
op: auth.UnshareOp,
|
||||
expected: "unshare",
|
||||
},
|
||||
{
|
||||
desc: "Publish operation",
|
||||
op: auth.PublishOp,
|
||||
expected: "publish",
|
||||
},
|
||||
{
|
||||
desc: "Subscribe operation",
|
||||
op: auth.SubscribeOp,
|
||||
expected: "subscribe",
|
||||
},
|
||||
{
|
||||
desc: "Unknown operation",
|
||||
op: auth.Operation(100),
|
||||
expected: "unknown operation type 100",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range cases {
|
||||
t.Run(tc.desc, func(t *testing.T) {
|
||||
got := tc.op.String()
|
||||
assert.Equal(t, tc.expected, got, "String() = %v, expected %v", got, tc.expected)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestOperationValidString(t *testing.T) {
|
||||
cases := []struct {
|
||||
desc string
|
||||
op auth.Operation
|
||||
expected string
|
||||
err bool
|
||||
}{
|
||||
{
|
||||
desc: "Valid create operation",
|
||||
op: auth.CreateOp,
|
||||
expected: "create",
|
||||
err: false,
|
||||
},
|
||||
{
|
||||
desc: "Valid read operation",
|
||||
op: auth.ReadOp,
|
||||
expected: "read",
|
||||
err: false,
|
||||
},
|
||||
{
|
||||
desc: "Invalid operation",
|
||||
op: auth.Operation(100),
|
||||
expected: "",
|
||||
err: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range cases {
|
||||
t.Run(tc.desc, func(t *testing.T) {
|
||||
got, err := tc.op.ValidString()
|
||||
if tc.err {
|
||||
assert.Error(t, err, "ValidString() should return error")
|
||||
} else {
|
||||
assert.NoError(t, err, "ValidString() should not return error")
|
||||
assert.Equal(t, tc.expected, got, "ValidString() = %v, expected %v", got, tc.expected)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseOperation(t *testing.T) {
|
||||
cases := []struct {
|
||||
desc string
|
||||
op string
|
||||
expected auth.Operation
|
||||
err bool
|
||||
}{
|
||||
{
|
||||
desc: "Parse create",
|
||||
op: "create",
|
||||
expected: auth.CreateOp,
|
||||
err: false,
|
||||
},
|
||||
{
|
||||
desc: "Parse read",
|
||||
op: "read",
|
||||
expected: auth.ReadOp,
|
||||
err: false,
|
||||
},
|
||||
{
|
||||
desc: "Parse list",
|
||||
op: "list",
|
||||
expected: auth.ListOp,
|
||||
err: false,
|
||||
},
|
||||
{
|
||||
desc: "Parse update",
|
||||
op: "update",
|
||||
expected: auth.UpdateOp,
|
||||
err: false,
|
||||
},
|
||||
{
|
||||
desc: "Parse delete",
|
||||
op: "delete",
|
||||
expected: auth.DeleteOp,
|
||||
err: false,
|
||||
},
|
||||
{
|
||||
desc: "Parse share",
|
||||
op: "share",
|
||||
expected: auth.ShareOp,
|
||||
err: false,
|
||||
},
|
||||
{
|
||||
desc: "Parse unshare",
|
||||
op: "unshare",
|
||||
expected: auth.UnshareOp,
|
||||
err: false,
|
||||
},
|
||||
{
|
||||
desc: "Parse publish",
|
||||
op: "publish",
|
||||
expected: auth.PublishOp,
|
||||
err: false,
|
||||
},
|
||||
{
|
||||
desc: "Parse subscribe",
|
||||
op: "subscribe",
|
||||
expected: auth.SubscribeOp,
|
||||
err: false,
|
||||
},
|
||||
{
|
||||
desc: "Parse unknown operation",
|
||||
op: "unknown",
|
||||
expected: auth.Operation(0),
|
||||
err: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range cases {
|
||||
t.Run(tc.desc, func(t *testing.T) {
|
||||
got, err := auth.ParseOperation(tc.op)
|
||||
if tc.err {
|
||||
assert.Error(t, err, "ParseOperation() should return error")
|
||||
} else {
|
||||
assert.NoError(t, err, "ParseOperation() should not return error")
|
||||
assert.Equal(t, tc.expected, got, "ParseOperation() = %v, expected %v", got, tc.expected)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestOperationMarshalJSON(t *testing.T) {
|
||||
cases := []struct {
|
||||
desc string
|
||||
op auth.Operation
|
||||
expected []byte
|
||||
err error
|
||||
}{
|
||||
{
|
||||
desc: "Marshal create",
|
||||
op: auth.CreateOp,
|
||||
expected: []byte(`"create"`),
|
||||
err: nil,
|
||||
},
|
||||
{
|
||||
desc: "Marshal read",
|
||||
op: auth.ReadOp,
|
||||
expected: []byte(`"read"`),
|
||||
err: nil,
|
||||
},
|
||||
{
|
||||
desc: "Marshal delete",
|
||||
op: auth.DeleteOp,
|
||||
expected: []byte(`"delete"`),
|
||||
err: nil,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range cases {
|
||||
t.Run(tc.desc, func(t *testing.T) {
|
||||
got, err := tc.op.MarshalJSON()
|
||||
assert.Equal(t, tc.err, err, "MarshalJSON() error = %v, expected %v", err, tc.err)
|
||||
assert.Equal(t, tc.expected, got, "MarshalJSON() = %v, expected %v", got, tc.expected)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestOperationUnmarshalJSON(t *testing.T) {
|
||||
cases := []struct {
|
||||
desc string
|
||||
data []byte
|
||||
expected auth.Operation
|
||||
err bool
|
||||
}{
|
||||
{
|
||||
desc: "Unmarshal create",
|
||||
data: []byte(`"create"`),
|
||||
expected: auth.CreateOp,
|
||||
err: false,
|
||||
},
|
||||
{
|
||||
desc: "Unmarshal read",
|
||||
data: []byte(`"read"`),
|
||||
expected: auth.ReadOp,
|
||||
err: false,
|
||||
},
|
||||
{
|
||||
desc: "Unmarshal unknown",
|
||||
data: []byte(`"unknown"`),
|
||||
expected: auth.Operation(0),
|
||||
err: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range cases {
|
||||
t.Run(tc.desc, func(t *testing.T) {
|
||||
var op auth.Operation
|
||||
err := op.UnmarshalJSON(tc.data)
|
||||
if tc.err {
|
||||
assert.Error(t, err, "UnmarshalJSON() should return error")
|
||||
} else {
|
||||
assert.NoError(t, err, "UnmarshalJSON() should not return error")
|
||||
assert.Equal(t, tc.expected, op, "UnmarshalJSON() = %v, expected %v", op, tc.expected)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestOperationMarshalText(t *testing.T) {
|
||||
cases := []struct {
|
||||
desc string
|
||||
op auth.Operation
|
||||
expected []byte
|
||||
err error
|
||||
}{
|
||||
{
|
||||
desc: "Marshal create as text",
|
||||
op: auth.CreateOp,
|
||||
expected: []byte("create"),
|
||||
err: nil,
|
||||
},
|
||||
{
|
||||
desc: "Marshal read as text",
|
||||
op: auth.ReadOp,
|
||||
expected: []byte("read"),
|
||||
err: nil,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range cases {
|
||||
t.Run(tc.desc, func(t *testing.T) {
|
||||
got, err := tc.op.MarshalText()
|
||||
assert.Equal(t, tc.err, err, "MarshalText() error = %v, expected %v", err, tc.err)
|
||||
assert.Equal(t, tc.expected, got, "MarshalText() = %v, expected %v", got, tc.expected)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestOperationUnmarshalText(t *testing.T) {
|
||||
cases := []struct {
|
||||
desc string
|
||||
data []byte
|
||||
expected auth.Operation
|
||||
err bool
|
||||
}{
|
||||
{
|
||||
desc: "Unmarshal create from text",
|
||||
data: []byte("create"),
|
||||
expected: auth.CreateOp,
|
||||
err: false,
|
||||
},
|
||||
{
|
||||
desc: "Unmarshal read from text",
|
||||
data: []byte("read"),
|
||||
expected: auth.ReadOp,
|
||||
err: false,
|
||||
},
|
||||
{
|
||||
desc: "Unmarshal unknown from text",
|
||||
data: []byte("unknown"),
|
||||
expected: auth.Operation(0),
|
||||
err: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range cases {
|
||||
t.Run(tc.desc, func(t *testing.T) {
|
||||
var op auth.Operation
|
||||
err := op.UnmarshalText(tc.data)
|
||||
if tc.err {
|
||||
assert.Error(t, err, "UnmarshalText() should return error")
|
||||
} else {
|
||||
assert.NoError(t, err, "UnmarshalText() should not return error")
|
||||
assert.Equal(t, tc.expected, op, "UnmarshalText() = %v, expected %v", op, tc.expected)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestEntityTypeString(t *testing.T) {
|
||||
cases := []struct {
|
||||
desc string
|
||||
et auth.EntityType
|
||||
expected string
|
||||
}{
|
||||
{
|
||||
desc: "Groups entity type",
|
||||
et: auth.GroupsType,
|
||||
expected: "groups",
|
||||
},
|
||||
{
|
||||
desc: "Channels entity type",
|
||||
et: auth.ChannelsType,
|
||||
expected: "channels",
|
||||
},
|
||||
{
|
||||
desc: "Clients entity type",
|
||||
et: auth.ClientsType,
|
||||
expected: "clients",
|
||||
},
|
||||
{
|
||||
desc: "Domains entity type",
|
||||
et: auth.DomainsType,
|
||||
expected: "domains",
|
||||
},
|
||||
{
|
||||
desc: "Users entity type",
|
||||
et: auth.UsersType,
|
||||
expected: "users",
|
||||
},
|
||||
{
|
||||
desc: "Dashboard entity type",
|
||||
et: auth.DashboardType,
|
||||
expected: "dashboards",
|
||||
},
|
||||
{
|
||||
desc: "Messages entity type",
|
||||
et: auth.MessagesType,
|
||||
expected: "messages",
|
||||
},
|
||||
{
|
||||
desc: "Unknown entity type",
|
||||
et: auth.EntityType(100),
|
||||
expected: "unknown domain entity type 100",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range cases {
|
||||
t.Run(tc.desc, func(t *testing.T) {
|
||||
got := tc.et.String()
|
||||
assert.Equal(t, tc.expected, got, "String() = %v, expected %v", got, tc.expected)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseEntityType(t *testing.T) {
|
||||
cases := []struct {
|
||||
desc string
|
||||
et string
|
||||
expected auth.EntityType
|
||||
err bool
|
||||
}{
|
||||
{
|
||||
desc: "Parse groups",
|
||||
et: "groups",
|
||||
expected: auth.GroupsType,
|
||||
err: false,
|
||||
},
|
||||
{
|
||||
desc: "Parse channels",
|
||||
et: "channels",
|
||||
expected: auth.ChannelsType,
|
||||
err: false,
|
||||
},
|
||||
{
|
||||
desc: "Parse clients",
|
||||
et: "clients",
|
||||
expected: auth.ClientsType,
|
||||
err: false,
|
||||
},
|
||||
{
|
||||
desc: "Parse domains",
|
||||
et: "domains",
|
||||
expected: auth.DomainsType,
|
||||
err: false,
|
||||
},
|
||||
{
|
||||
desc: "Parse users",
|
||||
et: "users",
|
||||
expected: auth.UsersType,
|
||||
err: false,
|
||||
},
|
||||
{
|
||||
desc: "Parse dashboards",
|
||||
et: "dashboards",
|
||||
expected: auth.DashboardType,
|
||||
err: false,
|
||||
},
|
||||
{
|
||||
desc: "Parse unknown entity type",
|
||||
et: "unknown",
|
||||
expected: auth.EntityType(0),
|
||||
err: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range cases {
|
||||
t.Run(tc.desc, func(t *testing.T) {
|
||||
got, err := auth.ParseEntityType(tc.et)
|
||||
if tc.err {
|
||||
assert.Error(t, err, "ParseEntityType() should return error")
|
||||
} else {
|
||||
assert.NoError(t, err, "ParseEntityType() should not return error")
|
||||
assert.Equal(t, tc.expected, got, "ParseEntityType() = %v, expected %v", got, tc.expected)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestEntityTypeMarshalJSON(t *testing.T) {
|
||||
cases := []struct {
|
||||
desc string
|
||||
et auth.EntityType
|
||||
expected []byte
|
||||
err error
|
||||
}{
|
||||
{
|
||||
desc: "Marshal groups",
|
||||
et: auth.GroupsType,
|
||||
expected: []byte(`"groups"`),
|
||||
err: nil,
|
||||
},
|
||||
{
|
||||
desc: "Marshal channels",
|
||||
et: auth.ChannelsType,
|
||||
expected: []byte(`"channels"`),
|
||||
err: nil,
|
||||
},
|
||||
{
|
||||
desc: "Marshal clients",
|
||||
et: auth.ClientsType,
|
||||
expected: []byte(`"clients"`),
|
||||
err: nil,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range cases {
|
||||
t.Run(tc.desc, func(t *testing.T) {
|
||||
got, err := tc.et.MarshalJSON()
|
||||
assert.Equal(t, tc.err, err, "MarshalJSON() error = %v, expected %v", err, tc.err)
|
||||
assert.Equal(t, tc.expected, got, "MarshalJSON() = %v, expected %v", got, tc.expected)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestEntityTypeUnmarshalJSON(t *testing.T) {
|
||||
cases := []struct {
|
||||
desc string
|
||||
data []byte
|
||||
expected auth.EntityType
|
||||
err bool
|
||||
}{
|
||||
{
|
||||
desc: "Unmarshal groups",
|
||||
data: []byte(`"groups"`),
|
||||
expected: auth.GroupsType,
|
||||
err: false,
|
||||
},
|
||||
{
|
||||
desc: "Unmarshal channels",
|
||||
data: []byte(`"channels"`),
|
||||
expected: auth.ChannelsType,
|
||||
err: false,
|
||||
},
|
||||
{
|
||||
desc: "Unmarshal unknown",
|
||||
data: []byte(`"unknown"`),
|
||||
expected: auth.EntityType(0),
|
||||
err: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range cases {
|
||||
t.Run(tc.desc, func(t *testing.T) {
|
||||
var et auth.EntityType
|
||||
err := et.UnmarshalJSON(tc.data)
|
||||
if tc.err {
|
||||
assert.Error(t, err, "UnmarshalJSON() should return error")
|
||||
} else {
|
||||
assert.NoError(t, err, "UnmarshalJSON() should not return error")
|
||||
assert.Equal(t, tc.expected, et, "UnmarshalJSON() = %v, expected %v", et, tc.expected)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestEntityTypeMarshalText(t *testing.T) {
|
||||
cases := []struct {
|
||||
desc string
|
||||
et auth.EntityType
|
||||
expected []byte
|
||||
err error
|
||||
}{
|
||||
{
|
||||
desc: "Marshal groups as text",
|
||||
et: auth.GroupsType,
|
||||
expected: []byte("groups"),
|
||||
err: nil,
|
||||
},
|
||||
{
|
||||
desc: "Marshal channels as text",
|
||||
et: auth.ChannelsType,
|
||||
expected: []byte("channels"),
|
||||
err: nil,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range cases {
|
||||
t.Run(tc.desc, func(t *testing.T) {
|
||||
got, err := tc.et.MarshalText()
|
||||
assert.Equal(t, tc.err, err, "MarshalText() error = %v, expected %v", err, tc.err)
|
||||
assert.Equal(t, tc.expected, got, "MarshalText() = %v, expected %v", got, tc.expected)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestEntityTypeUnmarshalText(t *testing.T) {
|
||||
cases := []struct {
|
||||
desc string
|
||||
data []byte
|
||||
expected auth.EntityType
|
||||
err bool
|
||||
}{
|
||||
{
|
||||
desc: "Unmarshal groups from text",
|
||||
data: []byte("groups"),
|
||||
expected: auth.GroupsType,
|
||||
err: false,
|
||||
},
|
||||
{
|
||||
desc: "Unmarshal channels from text",
|
||||
data: []byte("channels"),
|
||||
expected: auth.ChannelsType,
|
||||
err: false,
|
||||
},
|
||||
{
|
||||
desc: "Unmarshal unknown from text",
|
||||
data: []byte("unknown"),
|
||||
expected: auth.EntityType(0),
|
||||
err: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range cases {
|
||||
t.Run(tc.desc, func(t *testing.T) {
|
||||
var et auth.EntityType
|
||||
err := et.UnmarshalText(tc.data)
|
||||
if tc.err {
|
||||
assert.Error(t, err, "UnmarshalText() should return error")
|
||||
} else {
|
||||
assert.NoError(t, err, "UnmarshalText() should not return error")
|
||||
assert.Equal(t, tc.expected, et, "UnmarshalText() = %v, expected %v", et, tc.expected)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,369 @@
|
||||
// Copyright (c) Abstract Machines
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package auth_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
apiutil "github.com/absmach/supermq/api/http/util"
|
||||
"github.com/absmach/supermq/auth"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestScopeAuthorized(t *testing.T) {
|
||||
cases := []struct {
|
||||
desc string
|
||||
scope *auth.Scope
|
||||
entityType auth.EntityType
|
||||
optionalDomainID string
|
||||
operation auth.Operation
|
||||
entityID string
|
||||
expected bool
|
||||
}{
|
||||
{
|
||||
desc: "Authorized with matching entity type, domain, operation and entity ID",
|
||||
scope: &auth.Scope{
|
||||
EntityType: auth.GroupsType,
|
||||
OptionalDomainID: "domain1",
|
||||
Operation: auth.CreateOp,
|
||||
EntityID: "entity1",
|
||||
},
|
||||
entityType: auth.GroupsType,
|
||||
optionalDomainID: "domain1",
|
||||
operation: auth.CreateOp,
|
||||
entityID: "entity1",
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
desc: "Authorized with wildcard entity ID",
|
||||
scope: &auth.Scope{
|
||||
EntityType: auth.GroupsType,
|
||||
OptionalDomainID: "domain1",
|
||||
Operation: auth.CreateOp,
|
||||
EntityID: "*",
|
||||
},
|
||||
entityType: auth.GroupsType,
|
||||
optionalDomainID: "domain1",
|
||||
operation: auth.CreateOp,
|
||||
entityID: "any-entity",
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
desc: "Authorized without domain ID",
|
||||
scope: &auth.Scope{
|
||||
EntityType: auth.UsersType,
|
||||
OptionalDomainID: "",
|
||||
Operation: auth.ReadOp,
|
||||
EntityID: "user1",
|
||||
},
|
||||
entityType: auth.UsersType,
|
||||
optionalDomainID: "",
|
||||
operation: auth.ReadOp,
|
||||
entityID: "user1",
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
desc: "Not authorized with different entity type",
|
||||
scope: &auth.Scope{
|
||||
EntityType: auth.GroupsType,
|
||||
OptionalDomainID: "domain1",
|
||||
Operation: auth.CreateOp,
|
||||
EntityID: "entity1",
|
||||
},
|
||||
entityType: auth.ChannelsType,
|
||||
optionalDomainID: "domain1",
|
||||
operation: auth.CreateOp,
|
||||
entityID: "entity1",
|
||||
expected: false,
|
||||
},
|
||||
{
|
||||
desc: "Not authorized with different domain ID",
|
||||
scope: &auth.Scope{
|
||||
EntityType: auth.GroupsType,
|
||||
OptionalDomainID: "domain1",
|
||||
Operation: auth.CreateOp,
|
||||
EntityID: "entity1",
|
||||
},
|
||||
entityType: auth.GroupsType,
|
||||
optionalDomainID: "domain2",
|
||||
operation: auth.CreateOp,
|
||||
entityID: "entity1",
|
||||
expected: false,
|
||||
},
|
||||
{
|
||||
desc: "Not authorized with different operation",
|
||||
scope: &auth.Scope{
|
||||
EntityType: auth.GroupsType,
|
||||
OptionalDomainID: "domain1",
|
||||
Operation: auth.CreateOp,
|
||||
EntityID: "entity1",
|
||||
},
|
||||
entityType: auth.GroupsType,
|
||||
optionalDomainID: "domain1",
|
||||
operation: auth.DeleteOp,
|
||||
entityID: "entity1",
|
||||
expected: false,
|
||||
},
|
||||
{
|
||||
desc: "Not authorized with different entity ID",
|
||||
scope: &auth.Scope{
|
||||
EntityType: auth.GroupsType,
|
||||
OptionalDomainID: "domain1",
|
||||
Operation: auth.CreateOp,
|
||||
EntityID: "entity1",
|
||||
},
|
||||
entityType: auth.GroupsType,
|
||||
optionalDomainID: "domain1",
|
||||
operation: auth.CreateOp,
|
||||
entityID: "entity2",
|
||||
expected: false,
|
||||
},
|
||||
{
|
||||
desc: "Not authorized with nil scope",
|
||||
scope: nil,
|
||||
entityType: auth.GroupsType,
|
||||
optionalDomainID: "domain1",
|
||||
operation: auth.CreateOp,
|
||||
entityID: "entity1",
|
||||
expected: false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range cases {
|
||||
t.Run(tc.desc, func(t *testing.T) {
|
||||
result := tc.scope.Authorized(tc.entityType, tc.optionalDomainID, tc.operation, tc.entityID)
|
||||
assert.Equal(t, tc.expected, result, "Authorized() = %v, expected %v", result, tc.expected)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestScopeValidate(t *testing.T) {
|
||||
cases := []struct {
|
||||
desc string
|
||||
scope *auth.Scope
|
||||
err error
|
||||
}{
|
||||
{
|
||||
desc: "Valid scope for groups with domain ID",
|
||||
scope: &auth.Scope{
|
||||
EntityType: auth.GroupsType,
|
||||
OptionalDomainID: "domain1",
|
||||
Operation: auth.CreateOp,
|
||||
EntityID: "entity1",
|
||||
},
|
||||
err: nil,
|
||||
},
|
||||
{
|
||||
desc: "Valid scope for channels with domain ID",
|
||||
scope: &auth.Scope{
|
||||
EntityType: auth.ChannelsType,
|
||||
OptionalDomainID: "domain1",
|
||||
Operation: auth.ReadOp,
|
||||
EntityID: "channel1",
|
||||
},
|
||||
err: nil,
|
||||
},
|
||||
{
|
||||
desc: "Valid scope for clients with domain ID",
|
||||
scope: &auth.Scope{
|
||||
EntityType: auth.ClientsType,
|
||||
OptionalDomainID: "domain1",
|
||||
Operation: auth.UpdateOp,
|
||||
EntityID: "client1",
|
||||
},
|
||||
err: nil,
|
||||
},
|
||||
{
|
||||
desc: "Valid scope for users without domain ID",
|
||||
scope: &auth.Scope{
|
||||
EntityType: auth.UsersType,
|
||||
OptionalDomainID: "",
|
||||
Operation: auth.DeleteOp,
|
||||
EntityID: "user1",
|
||||
},
|
||||
err: nil,
|
||||
},
|
||||
{
|
||||
desc: "Valid scope for domains without domain ID",
|
||||
scope: &auth.Scope{
|
||||
EntityType: auth.DomainsType,
|
||||
OptionalDomainID: "",
|
||||
Operation: auth.ListOp,
|
||||
EntityID: "domain1",
|
||||
},
|
||||
err: nil,
|
||||
},
|
||||
{
|
||||
desc: "Valid scope with wildcard entity ID",
|
||||
scope: &auth.Scope{
|
||||
EntityType: auth.GroupsType,
|
||||
OptionalDomainID: "domain1",
|
||||
Operation: auth.CreateOp,
|
||||
EntityID: "*",
|
||||
},
|
||||
err: nil,
|
||||
},
|
||||
{
|
||||
desc: "Invalid nil scope",
|
||||
scope: nil,
|
||||
err: assert.AnError, // Will be checked with Contains
|
||||
},
|
||||
{
|
||||
desc: "Invalid scope without entity ID",
|
||||
scope: &auth.Scope{
|
||||
EntityType: auth.GroupsType,
|
||||
OptionalDomainID: "domain1",
|
||||
Operation: auth.CreateOp,
|
||||
EntityID: "",
|
||||
},
|
||||
err: apiutil.ErrMissingEntityID,
|
||||
},
|
||||
{
|
||||
desc: "Invalid scope for groups without domain ID",
|
||||
scope: &auth.Scope{
|
||||
EntityType: auth.GroupsType,
|
||||
OptionalDomainID: "",
|
||||
Operation: auth.CreateOp,
|
||||
EntityID: "entity1",
|
||||
},
|
||||
err: apiutil.ErrMissingDomainID,
|
||||
},
|
||||
{
|
||||
desc: "Invalid scope for channels without domain ID",
|
||||
scope: &auth.Scope{
|
||||
EntityType: auth.ChannelsType,
|
||||
OptionalDomainID: "",
|
||||
Operation: auth.CreateOp,
|
||||
EntityID: "channel1",
|
||||
},
|
||||
err: apiutil.ErrMissingDomainID,
|
||||
},
|
||||
{
|
||||
desc: "Invalid scope for clients without domain ID",
|
||||
scope: &auth.Scope{
|
||||
EntityType: auth.ClientsType,
|
||||
OptionalDomainID: "",
|
||||
Operation: auth.CreateOp,
|
||||
EntityID: "client1",
|
||||
},
|
||||
err: apiutil.ErrMissingDomainID,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range cases {
|
||||
t.Run(tc.desc, func(t *testing.T) {
|
||||
err := tc.scope.Validate()
|
||||
if tc.err != nil {
|
||||
assert.Error(t, err, "Validate() should return error")
|
||||
if tc.err != assert.AnError {
|
||||
assert.Equal(t, tc.err, err, "Validate() error = %v, expected %v", err, tc.err)
|
||||
}
|
||||
} else {
|
||||
assert.NoError(t, err, "Validate() should not return error")
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestPATValidate(t *testing.T) {
|
||||
cases := []struct {
|
||||
desc string
|
||||
pat *auth.PAT
|
||||
err bool
|
||||
}{
|
||||
{
|
||||
desc: "Valid PAT",
|
||||
pat: &auth.PAT{
|
||||
ID: "pat-id",
|
||||
User: "user-id",
|
||||
Name: "test-pat",
|
||||
Description: "test description",
|
||||
},
|
||||
err: false,
|
||||
},
|
||||
{
|
||||
desc: "Invalid nil PAT",
|
||||
pat: nil,
|
||||
err: true,
|
||||
},
|
||||
{
|
||||
desc: "Invalid PAT without name",
|
||||
pat: &auth.PAT{
|
||||
ID: "pat-id",
|
||||
User: "user-id",
|
||||
Name: "",
|
||||
Description: "test description",
|
||||
},
|
||||
err: true,
|
||||
},
|
||||
{
|
||||
desc: "Invalid PAT without user",
|
||||
pat: &auth.PAT{
|
||||
ID: "pat-id",
|
||||
User: "",
|
||||
Name: "test-pat",
|
||||
Description: "test description",
|
||||
},
|
||||
err: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range cases {
|
||||
t.Run(tc.desc, func(t *testing.T) {
|
||||
err := tc.pat.Validate()
|
||||
if tc.err {
|
||||
assert.Error(t, err, "Validate() should return error")
|
||||
} else {
|
||||
assert.NoError(t, err, "Validate() should not return error")
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestPATMarshalUnmarshalBinary(t *testing.T) {
|
||||
pat := auth.PAT{
|
||||
ID: "pat-id",
|
||||
User: "user-id",
|
||||
Name: "test-pat",
|
||||
Description: "test description",
|
||||
Secret: "secret",
|
||||
IssuedAt: time.Now().UTC().Round(time.Second),
|
||||
ExpiresAt: time.Now().UTC().Add(24 * time.Hour).Round(time.Second),
|
||||
Status: auth.ActiveStatus,
|
||||
}
|
||||
|
||||
// Marshal
|
||||
data, err := pat.MarshalBinary()
|
||||
assert.NoError(t, err, "MarshalBinary() should not return error")
|
||||
assert.NotNil(t, data, "MarshalBinary() should return data")
|
||||
|
||||
// Unmarshal
|
||||
var newPAT auth.PAT
|
||||
err = newPAT.UnmarshalBinary(data)
|
||||
assert.NoError(t, err, "UnmarshalBinary() should not return error")
|
||||
|
||||
assert.Equal(t, pat.ID, newPAT.ID, "ID mismatch")
|
||||
assert.Equal(t, pat.User, newPAT.User, "User mismatch")
|
||||
assert.Equal(t, pat.Name, newPAT.Name, "Name mismatch")
|
||||
assert.Equal(t, pat.Description, newPAT.Description, "Description mismatch")
|
||||
assert.Equal(t, pat.Secret, newPAT.Secret, "Secret mismatch")
|
||||
assert.Equal(t, pat.Status, newPAT.Status, "Status mismatch")
|
||||
}
|
||||
|
||||
func TestPATString(t *testing.T) {
|
||||
pat := &auth.PAT{
|
||||
ID: "pat-id",
|
||||
User: "user-id",
|
||||
Name: "test-pat",
|
||||
Description: "test description",
|
||||
Status: auth.ActiveStatus,
|
||||
}
|
||||
|
||||
str := pat.String()
|
||||
assert.NotEmpty(t, str, "String() should return non-empty string")
|
||||
assert.Contains(t, str, "pat-id", "String() should contain ID")
|
||||
assert.Contains(t, str, "user-id", "String() should contain User")
|
||||
assert.Contains(t, str, "test-pat", "String() should contain Name")
|
||||
}
|
||||
@@ -0,0 +1,252 @@
|
||||
// Copyright (c) Abstract Machines
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package auth_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/absmach/supermq/auth"
|
||||
svcerr "github.com/absmach/supermq/pkg/errors/service"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestStatusString(t *testing.T) {
|
||||
cases := []struct {
|
||||
desc string
|
||||
status auth.Status
|
||||
expected string
|
||||
}{
|
||||
{
|
||||
desc: "Active",
|
||||
status: auth.ActiveStatus,
|
||||
expected: "active",
|
||||
},
|
||||
{
|
||||
desc: "Revoked",
|
||||
status: auth.RevokedStatus,
|
||||
expected: "revoked",
|
||||
},
|
||||
{
|
||||
desc: "Expired",
|
||||
status: auth.ExpiredStatus,
|
||||
expected: "expired",
|
||||
},
|
||||
{
|
||||
desc: "All",
|
||||
status: auth.AllStatus,
|
||||
expected: "all",
|
||||
},
|
||||
{
|
||||
desc: "Unknown",
|
||||
status: auth.Status(100),
|
||||
expected: "unknown",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range cases {
|
||||
t.Run(tc.desc, func(t *testing.T) {
|
||||
got := tc.status.String()
|
||||
assert.Equal(t, tc.expected, got, "String() = %v, expected %v", got, tc.expected)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestToStatus(t *testing.T) {
|
||||
cases := []struct {
|
||||
desc string
|
||||
status string
|
||||
expected auth.Status
|
||||
err error
|
||||
}{
|
||||
{
|
||||
desc: "Active",
|
||||
status: "active",
|
||||
expected: auth.ActiveStatus,
|
||||
err: nil,
|
||||
},
|
||||
{
|
||||
desc: "Empty string defaults to Active",
|
||||
status: "",
|
||||
expected: auth.ActiveStatus,
|
||||
err: nil,
|
||||
},
|
||||
{
|
||||
desc: "Revoked",
|
||||
status: "revoked",
|
||||
expected: auth.RevokedStatus,
|
||||
err: nil,
|
||||
},
|
||||
{
|
||||
desc: "Expired",
|
||||
status: "expired",
|
||||
expected: auth.ExpiredStatus,
|
||||
err: nil,
|
||||
},
|
||||
{
|
||||
desc: "All",
|
||||
status: "all",
|
||||
expected: auth.AllStatus,
|
||||
err: nil,
|
||||
},
|
||||
{
|
||||
desc: "Unknown",
|
||||
status: "unknown",
|
||||
expected: auth.Status(0),
|
||||
err: svcerr.ErrInvalidStatus,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range cases {
|
||||
t.Run(tc.desc, func(t *testing.T) {
|
||||
got, err := auth.ToStatus(tc.status)
|
||||
assert.Equal(t, tc.err, err, "ToStatus() error = %v, expected %v", err, tc.err)
|
||||
assert.Equal(t, tc.expected, got, "ToStatus() = %v, expected %v", got, tc.expected)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestStatusMarshalJSON(t *testing.T) {
|
||||
cases := []struct {
|
||||
desc string
|
||||
expected []byte
|
||||
status auth.Status
|
||||
err error
|
||||
}{
|
||||
{
|
||||
desc: "Active",
|
||||
expected: []byte(`"active"`),
|
||||
status: auth.ActiveStatus,
|
||||
err: nil,
|
||||
},
|
||||
{
|
||||
desc: "Revoked",
|
||||
expected: []byte(`"revoked"`),
|
||||
status: auth.RevokedStatus,
|
||||
err: nil,
|
||||
},
|
||||
{
|
||||
desc: "Expired",
|
||||
expected: []byte(`"expired"`),
|
||||
status: auth.ExpiredStatus,
|
||||
err: nil,
|
||||
},
|
||||
{
|
||||
desc: "All",
|
||||
expected: []byte(`"all"`),
|
||||
status: auth.AllStatus,
|
||||
err: nil,
|
||||
},
|
||||
{
|
||||
desc: "Unknown",
|
||||
expected: []byte(`"unknown"`),
|
||||
status: auth.Status(100),
|
||||
err: nil,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range cases {
|
||||
t.Run(tc.desc, func(t *testing.T) {
|
||||
got, err := tc.status.MarshalJSON()
|
||||
assert.Equal(t, tc.err, err, "MarshalJSON() error = %v, expected %v", err, tc.err)
|
||||
assert.Equal(t, tc.expected, got, "MarshalJSON() = %v, expected %v", got, tc.expected)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestStatusUnmarshalJSON(t *testing.T) {
|
||||
cases := []struct {
|
||||
desc string
|
||||
expected auth.Status
|
||||
status []byte
|
||||
err error
|
||||
}{
|
||||
{
|
||||
desc: "Active",
|
||||
expected: auth.ActiveStatus,
|
||||
status: []byte(`"active"`),
|
||||
err: nil,
|
||||
},
|
||||
{
|
||||
desc: "Revoked",
|
||||
expected: auth.RevokedStatus,
|
||||
status: []byte(`"revoked"`),
|
||||
err: nil,
|
||||
},
|
||||
{
|
||||
desc: "Expired",
|
||||
expected: auth.ExpiredStatus,
|
||||
status: []byte(`"expired"`),
|
||||
err: nil,
|
||||
},
|
||||
{
|
||||
desc: "All",
|
||||
expected: auth.AllStatus,
|
||||
status: []byte(`"all"`),
|
||||
err: nil,
|
||||
},
|
||||
{
|
||||
desc: "Unknown",
|
||||
expected: auth.Status(0),
|
||||
status: []byte(`"unknown"`),
|
||||
err: svcerr.ErrInvalidStatus,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range cases {
|
||||
t.Run(tc.desc, func(t *testing.T) {
|
||||
var s auth.Status
|
||||
err := s.UnmarshalJSON(tc.status)
|
||||
assert.Equal(t, tc.err, err, "UnmarshalJSON() error = %v, expected %v", err, tc.err)
|
||||
assert.Equal(t, tc.expected, s, "UnmarshalJSON() = %v, expected %v", s, tc.expected)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestPATMarshalJSON(t *testing.T) {
|
||||
cases := []struct {
|
||||
desc string
|
||||
pat auth.PAT
|
||||
expected string
|
||||
err error
|
||||
}{
|
||||
{
|
||||
desc: "Active PAT",
|
||||
pat: auth.PAT{
|
||||
ID: "test-id",
|
||||
Name: "test-pat",
|
||||
Status: auth.ActiveStatus,
|
||||
},
|
||||
expected: `"status":"active"`,
|
||||
err: nil,
|
||||
},
|
||||
{
|
||||
desc: "Revoked PAT",
|
||||
pat: auth.PAT{
|
||||
ID: "test-id",
|
||||
Name: "test-pat",
|
||||
Status: auth.RevokedStatus,
|
||||
},
|
||||
expected: `"status":"revoked"`,
|
||||
err: nil,
|
||||
},
|
||||
{
|
||||
desc: "Expired PAT",
|
||||
pat: auth.PAT{
|
||||
ID: "test-id",
|
||||
Name: "test-pat",
|
||||
Status: auth.ExpiredStatus,
|
||||
},
|
||||
expected: `"status":"expired"`,
|
||||
err: nil,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range cases {
|
||||
t.Run(tc.desc, func(t *testing.T) {
|
||||
got, err := tc.pat.MarshalJSON()
|
||||
assert.Equal(t, tc.err, err, "MarshalJSON() error = %v, expected %v", err, tc.err)
|
||||
assert.Contains(t, string(got), tc.expected, "MarshalJSON() should contain %v", tc.expected)
|
||||
})
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user