sessions/internal/router/rest/session_test.go

316 lines
8.5 KiB
Go

package rest_test
import (
"context"
"encoding/base64"
"net/http"
"strings"
"testing"
"time"
"gitea.dwysokinski.me/twhelp/sessions/internal/domain"
"gitea.dwysokinski.me/twhelp/sessions/internal/router/rest/internal/mock"
"gitea.dwysokinski.me/twhelp/sessions/internal/router/rest/internal/model"
"github.com/google/uuid"
"github.com/stretchr/testify/assert"
)
func TestSession_createOrUpdate(t *testing.T) {
t.Parallel()
now := time.Now()
apiKey := uuid.NewString()
tests := []struct {
name string
setup func(*mock.FakeAPIKeyVerifier, *mock.FakeSessionService)
apiKey string
serverKey string
sid string
expectedStatus int
target any
expectedResponse any
}{
{
name: "OK",
setup: func(apiKeySvc *mock.FakeAPIKeyVerifier, sessionSvc *mock.FakeSessionService) {
apiKeySvc.VerifyReturns(domain.User{
ID: 111,
Name: "name",
CreatedAt: now,
}, nil)
sessionSvc.CreateOrUpdateReturns(domain.Session{}, nil)
},
apiKey: apiKey,
serverKey: "pl151",
sid: base64.StdEncoding.EncodeToString([]byte(uuid.NewString())),
expectedStatus: http.StatusNoContent,
},
{
name: "ERR: len(serverKey) > 10",
setup: func(apiKeySvc *mock.FakeAPIKeyVerifier, sessionSvc *mock.FakeSessionService) {
apiKeySvc.VerifyReturns(domain.User{
ID: 111,
Name: "name",
CreatedAt: now,
}, nil)
},
apiKey: apiKey,
serverKey: "012345678890",
sid: base64.StdEncoding.EncodeToString([]byte(uuid.NewString())),
expectedStatus: http.StatusBadRequest,
target: &model.ErrorResp{},
expectedResponse: &model.ErrorResp{
Error: model.APIError{
Code: domain.ErrorCodeValidationError.String(),
Message: "ServerKey: the length must be no more than 10",
},
},
},
{
name: "ERR: SID is required",
setup: func(apiKeySvc *mock.FakeAPIKeyVerifier, sessionSvc *mock.FakeSessionService) {
apiKeySvc.VerifyReturns(domain.User{
ID: 111,
Name: "name",
CreatedAt: now,
}, nil)
},
apiKey: apiKey,
serverKey: "pl151",
sid: "",
expectedStatus: http.StatusBadRequest,
target: &model.ErrorResp{},
expectedResponse: &model.ErrorResp{
Error: model.APIError{
Code: domain.ErrorCodeValidationError.String(),
Message: "SID: cannot be blank",
},
},
},
{
name: "ERR: SID is not a valid base64",
setup: func(apiKeySvc *mock.FakeAPIKeyVerifier, sessionSvc *mock.FakeSessionService) {
apiKeySvc.VerifyReturns(domain.User{
ID: 111,
Name: "name",
CreatedAt: now,
}, nil)
},
apiKey: apiKey,
serverKey: "pl151",
sid: uuid.NewString(),
expectedStatus: http.StatusBadRequest,
target: &model.ErrorResp{},
expectedResponse: &model.ErrorResp{
Error: model.APIError{
Code: domain.ErrorCodeValidationError.String(),
Message: "SID: must be encoded in Base64",
},
},
},
{
name: "ERR: apiKey == \"\"",
setup: func(apiKeySvc *mock.FakeAPIKeyVerifier, sessionSvc *mock.FakeSessionService) {},
apiKey: "",
expectedStatus: http.StatusUnauthorized,
serverKey: "pl151",
target: &model.ErrorResp{},
expectedResponse: &model.ErrorResp{
Error: model.APIError{
Code: "unauthorized",
Message: "invalid API key",
},
},
},
{
name: "ERR: unexpected API key",
setup: func(apiKeySvc *mock.FakeAPIKeyVerifier, sessionSvc *mock.FakeSessionService) {
apiKeySvc.VerifyCalls(func(ctx context.Context, key string) (domain.User, error) {
if key != apiKey {
return domain.User{}, domain.APIKeyNotFoundError{
Key: key,
}
}
return domain.User{
ID: 111,
Name: "name",
CreatedAt: now,
}, nil
})
},
apiKey: uuid.NewString(),
serverKey: "pl151",
expectedStatus: http.StatusUnauthorized,
target: &model.ErrorResp{},
expectedResponse: &model.ErrorResp{
Error: model.APIError{
Code: "unauthorized",
Message: "invalid API key",
},
},
},
}
for _, tt := range tests {
tt := tt
t.Run(tt.name, func(t *testing.T) {
t.Parallel()
apiKeySvc := &mock.FakeAPIKeyVerifier{}
sessionSvc := &mock.FakeSessionService{}
tt.setup(apiKeySvc, sessionSvc)
router := newRouter(withAPIKeyVerifier(apiKeySvc), withSessionService(sessionSvc))
resp := doRequest(
router,
http.MethodPut,
"/v1/user/sessions/"+tt.serverKey,
tt.apiKey,
strings.NewReader(tt.sid),
)
defer resp.Body.Close()
if tt.expectedStatus == http.StatusNoContent {
assert.Equal(t, tt.expectedStatus, resp.StatusCode)
return
}
assertJSONResponse(t, resp, tt.expectedStatus, tt.expectedResponse, tt.target)
})
}
}
func TestSession_getCurrentUser(t *testing.T) {
t.Parallel()
now := time.Now()
apiKey := uuid.NewString()
sid := base64.StdEncoding.EncodeToString([]byte(uuid.NewString()))
tests := []struct {
name string
setup func(*mock.FakeAPIKeyVerifier, *mock.FakeSessionService)
apiKey string
serverKey string
expectedStatus int
target any
expectedResponse any
}{
{
name: "OK",
setup: func(apiKeySvc *mock.FakeAPIKeyVerifier, sessionSvc *mock.FakeSessionService) {
apiKeySvc.VerifyReturns(domain.User{
ID: 111,
Name: "name",
CreatedAt: now,
}, nil)
sessionSvc.GetCalls(func(ctx context.Context, userID int64, serverKey string) (domain.Session, error) {
return domain.Session{
ID: 111,
UserID: userID,
ServerKey: serverKey,
SID: sid,
CreatedAt: now,
UpdatedAt: now,
}, nil
})
},
apiKey: apiKey,
serverKey: "pl151",
expectedStatus: http.StatusOK,
target: &model.GetSessionResp{},
expectedResponse: &model.GetSessionResp{
Data: model.Session{
ServerKey: "pl151",
SID: sid,
},
},
},
{
name: "ERR: session not found",
setup: func(apiKeySvc *mock.FakeAPIKeyVerifier, sessionSvc *mock.FakeSessionService) {
apiKeySvc.VerifyReturns(domain.User{
ID: 111,
Name: "name",
CreatedAt: now,
}, nil)
sessionSvc.GetCalls(func(ctx context.Context, userID int64, serverKey string) (domain.Session, error) {
return domain.Session{}, domain.SessionNotFoundError{
ServerKey: serverKey,
}
})
},
apiKey: apiKey,
expectedStatus: http.StatusNotFound,
serverKey: "pl151",
target: &model.ErrorResp{},
expectedResponse: &model.ErrorResp{
Error: model.APIError{
Code: domain.ErrorCodeEntityNotFound.String(),
Message: "session (ServerKey=pl151) not found",
},
},
},
{
name: "ERR: apiKey == \"\"",
setup: func(apiKeySvc *mock.FakeAPIKeyVerifier, sessionSvc *mock.FakeSessionService) {},
apiKey: "",
expectedStatus: http.StatusUnauthorized,
serverKey: "pl151",
target: &model.ErrorResp{},
expectedResponse: &model.ErrorResp{
Error: model.APIError{
Code: "unauthorized",
Message: "invalid API key",
},
},
},
{
name: "ERR: unexpected API key",
setup: func(apiKeySvc *mock.FakeAPIKeyVerifier, sessionSvc *mock.FakeSessionService) {
apiKeySvc.VerifyCalls(func(ctx context.Context, key string) (domain.User, error) {
if key != apiKey {
return domain.User{}, domain.APIKeyNotFoundError{
Key: key,
}
}
return domain.User{
ID: 111,
Name: "name",
CreatedAt: now,
}, nil
})
},
apiKey: uuid.NewString(),
serverKey: "pl151",
expectedStatus: http.StatusUnauthorized,
target: &model.ErrorResp{},
expectedResponse: &model.ErrorResp{
Error: model.APIError{
Code: "unauthorized",
Message: "invalid API key",
},
},
},
}
for _, tt := range tests {
tt := tt
t.Run(tt.name, func(t *testing.T) {
t.Parallel()
apiKeySvc := &mock.FakeAPIKeyVerifier{}
sessionSvc := &mock.FakeSessionService{}
tt.setup(apiKeySvc, sessionSvc)
router := newRouter(withAPIKeyVerifier(apiKeySvc), withSessionService(sessionSvc))
resp := doRequest(router, http.MethodGet, "/v1/user/sessions/"+tt.serverKey, tt.apiKey, nil)
defer resp.Body.Close()
assertJSONResponse(t, resp, tt.expectedStatus, tt.expectedResponse, tt.target)
})
}
}