MF-170 - Add manager API unit tests (#211)
* Fix connection request validation and EOF error handling Fix validation of connection and client id in connection request. Fix EOF error handling by returning HTTP status Bad Request. Signed-off-by: Aleksandar Novakovic <anovakovic01@gmail.com> * Add manager API tests and update swagger Implement unit tests for every manager API endpoint. Update client and connection mock implementations by switching to UUIDs. Update swagger file with correct status codes. Signed-off-by: Aleksandar Novakovic <anovakovic01@gmail.com> * Add content type check and update documentation Add content type check in implementation and update documentation accordingly. Refactor tests and add empty content type test cases. Add code coverage badge to readme. Signed-off-by: Aleksandar Novakovic <anovakovic01@gmail.com>
This commit is contained in:
parent
88b30626dd
commit
2dace5564f
|
@ -2,6 +2,7 @@
|
|||
|
||||
[![build][ci-badge]][ci-url]
|
||||
[![go report card][grc-badge]][grc-url]
|
||||
[![coverage][cov-badge]][cov-url]
|
||||
[![license][license]](LICENSE)
|
||||
[![chat][gitter-badge]][gitter]
|
||||
|
||||
|
@ -61,5 +62,7 @@ Thank you for your interest in Mainflux and wish to contribute!
|
|||
[gitter-badge]: https://badges.gitter.im/Join%20Chat.svg
|
||||
[grc-badge]: https://goreportcard.com/badge/github.com/mainflux/mainflux
|
||||
[grc-url]: https://goreportcard.com/report/github.com/mainflux/mainflux
|
||||
[cov-badge]: https://codecov.io/gh/mainflux/mainflux/branch/master/graph/badge.svg
|
||||
[cov-url]: https://codecov.io/gh/mainflux/mainflux
|
||||
[license]: https://img.shields.io/badge/license-Apache%20v2.0-blue.svg
|
||||
[twitter]: https://twitter.com/mainflux
|
||||
|
|
|
@ -137,7 +137,7 @@ func (req connectionReq) validate() error {
|
|||
return manager.ErrUnauthorizedAccess
|
||||
}
|
||||
|
||||
if !govalidator.IsUUID(req.chanId) && !govalidator.IsUUID(req.clientId) {
|
||||
if !govalidator.IsUUID(req.chanId) || !govalidator.IsUUID(req.clientId) {
|
||||
return manager.ErrNotFound
|
||||
}
|
||||
|
||||
|
|
|
@ -3,6 +3,8 @@ package api
|
|||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"io"
|
||||
"net/http"
|
||||
|
||||
kithttp "github.com/go-kit/kit/transport/http"
|
||||
|
@ -12,6 +14,8 @@ import (
|
|||
"github.com/prometheus/client_golang/prometheus/promhttp"
|
||||
)
|
||||
|
||||
var errUnsupportedContentType = errors.New("unsupported content type")
|
||||
|
||||
// MakeHandler returns a HTTP handler for API endpoints.
|
||||
func MakeHandler(svc manager.Service) http.Handler {
|
||||
opts := []kithttp.ServerOption{
|
||||
|
@ -147,6 +151,10 @@ func decodeIdentity(_ context.Context, r *http.Request) (interface{}, error) {
|
|||
}
|
||||
|
||||
func decodeCredentials(_ context.Context, r *http.Request) (interface{}, error) {
|
||||
if r.Header.Get("Content-Type") != contentType {
|
||||
return nil, errUnsupportedContentType
|
||||
}
|
||||
|
||||
var user manager.User
|
||||
if err := json.NewDecoder(r.Body).Decode(&user); err != nil {
|
||||
return nil, err
|
||||
|
@ -156,6 +164,10 @@ func decodeCredentials(_ context.Context, r *http.Request) (interface{}, error)
|
|||
}
|
||||
|
||||
func decodeClientCreation(_ context.Context, r *http.Request) (interface{}, error) {
|
||||
if r.Header.Get("Content-Type") != contentType {
|
||||
return nil, errUnsupportedContentType
|
||||
}
|
||||
|
||||
var client manager.Client
|
||||
if err := json.NewDecoder(r.Body).Decode(&client); err != nil {
|
||||
return nil, err
|
||||
|
@ -170,6 +182,10 @@ func decodeClientCreation(_ context.Context, r *http.Request) (interface{}, erro
|
|||
}
|
||||
|
||||
func decodeClientUpdate(_ context.Context, r *http.Request) (interface{}, error) {
|
||||
if r.Header.Get("Content-Type") != contentType {
|
||||
return nil, errUnsupportedContentType
|
||||
}
|
||||
|
||||
var client manager.Client
|
||||
if err := json.NewDecoder(r.Body).Decode(&client); err != nil {
|
||||
return nil, err
|
||||
|
@ -185,6 +201,10 @@ func decodeClientUpdate(_ context.Context, r *http.Request) (interface{}, error)
|
|||
}
|
||||
|
||||
func decodeChannelCreation(_ context.Context, r *http.Request) (interface{}, error) {
|
||||
if r.Header.Get("Content-Type") != contentType {
|
||||
return nil, errUnsupportedContentType
|
||||
}
|
||||
|
||||
var channel manager.Channel
|
||||
if err := json.NewDecoder(r.Body).Decode(&channel); err != nil {
|
||||
return nil, err
|
||||
|
@ -199,6 +219,10 @@ func decodeChannelCreation(_ context.Context, r *http.Request) (interface{}, err
|
|||
}
|
||||
|
||||
func decodeChannelUpdate(_ context.Context, r *http.Request) (interface{}, error) {
|
||||
if r.Header.Get("Content-Type") != contentType {
|
||||
return nil, errUnsupportedContentType
|
||||
}
|
||||
|
||||
var channel manager.Channel
|
||||
if err := json.NewDecoder(r.Body).Decode(&channel); err != nil {
|
||||
return nil, err
|
||||
|
@ -272,6 +296,12 @@ func encodeError(_ context.Context, err error, w http.ResponseWriter) {
|
|||
w.WriteHeader(http.StatusNotFound)
|
||||
case manager.ErrConflict:
|
||||
w.WriteHeader(http.StatusConflict)
|
||||
case errUnsupportedContentType:
|
||||
w.WriteHeader(http.StatusUnsupportedMediaType)
|
||||
case io.ErrUnexpectedEOF:
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
case io.EOF:
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
default:
|
||||
switch err.(type) {
|
||||
case *json.SyntaxError:
|
||||
|
|
|
@ -0,0 +1,737 @@
|
|||
package api_test
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/mainflux/mainflux/manager"
|
||||
"github.com/mainflux/mainflux/manager/api"
|
||||
"github.com/mainflux/mainflux/manager/mocks"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
const (
|
||||
contentType = "application/json; charset=utf-8"
|
||||
invalidEmail = "userexample.com"
|
||||
wrongID = "123e4567-e89b-12d3-a456-000000000042"
|
||||
)
|
||||
|
||||
var (
|
||||
user = manager.User{"user@example.com", "password"}
|
||||
client = manager.Client{Type: "app", Name: "test_app", Payload: "test_payload"}
|
||||
channel = manager.Channel{Name: "test"}
|
||||
)
|
||||
|
||||
type testRequest struct {
|
||||
client *http.Client
|
||||
method string
|
||||
url string
|
||||
contentType string
|
||||
token string
|
||||
body io.Reader
|
||||
}
|
||||
|
||||
func (tr testRequest) make() (*http.Response, error) {
|
||||
req, err := http.NewRequest(tr.method, tr.url, tr.body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if tr.token != "" {
|
||||
req.Header.Set("Authorization", tr.token)
|
||||
}
|
||||
if tr.contentType != "" {
|
||||
req.Header.Set("Content-Type", tr.contentType)
|
||||
}
|
||||
return tr.client.Do(req)
|
||||
}
|
||||
|
||||
func newService() manager.Service {
|
||||
users := mocks.NewUserRepository()
|
||||
clients := mocks.NewClientRepository()
|
||||
channels := mocks.NewChannelRepository(clients)
|
||||
hasher := mocks.NewHasher()
|
||||
idp := mocks.NewIdentityProvider()
|
||||
|
||||
return manager.New(users, clients, channels, hasher, idp)
|
||||
}
|
||||
|
||||
func newServer(svc manager.Service) *httptest.Server {
|
||||
mux := api.MakeHandler(svc)
|
||||
return httptest.NewServer(mux)
|
||||
}
|
||||
|
||||
func toJSON(data interface{}) string {
|
||||
jsonData, _ := json.Marshal(data)
|
||||
return string(jsonData)
|
||||
}
|
||||
|
||||
func TestRegister(t *testing.T) {
|
||||
svc := newService()
|
||||
ts := newServer(svc)
|
||||
defer ts.Close()
|
||||
client := ts.Client()
|
||||
|
||||
data := toJSON(user)
|
||||
invalidData := toJSON(manager.User{Email: invalidEmail, Password: "password"})
|
||||
|
||||
cases := []struct {
|
||||
desc string
|
||||
req string
|
||||
contentType string
|
||||
status int
|
||||
}{
|
||||
{"register new user", data, contentType, http.StatusCreated},
|
||||
{"register existing user", data, contentType, http.StatusConflict},
|
||||
{"register user with invalid email address", invalidData, contentType, http.StatusBadRequest},
|
||||
{"register user with invalid request format", "{", contentType, http.StatusBadRequest},
|
||||
{"register user with empty JSON request", "{}", contentType, http.StatusBadRequest},
|
||||
{"register user with empty request", "", contentType, http.StatusBadRequest},
|
||||
{"register user with missing content type", data, "", http.StatusUnsupportedMediaType},
|
||||
}
|
||||
|
||||
for _, tc := range cases {
|
||||
req := testRequest{
|
||||
client: client,
|
||||
method: http.MethodPost,
|
||||
url: fmt.Sprintf("%s/users", ts.URL),
|
||||
contentType: tc.contentType,
|
||||
body: strings.NewReader(tc.req),
|
||||
}
|
||||
res, err := req.make()
|
||||
assert.Nil(t, err, fmt.Sprintf("%s: unexpected error %s", tc.desc, err))
|
||||
assert.Equal(t, tc.status, res.StatusCode, fmt.Sprintf("%s: expected status code %d got %d", tc.desc, tc.status, res.StatusCode))
|
||||
}
|
||||
}
|
||||
|
||||
func TestLogin(t *testing.T) {
|
||||
svc := newService()
|
||||
ts := newServer(svc)
|
||||
defer ts.Close()
|
||||
client := ts.Client()
|
||||
|
||||
tokenData := toJSON(map[string]string{"token": user.Email})
|
||||
data := toJSON(user)
|
||||
invalidEmailData := toJSON(manager.User{Email: invalidEmail, Password: "password"})
|
||||
invalidData := toJSON(manager.User{"user@example.com", "invalid_password"})
|
||||
nonexistentData := toJSON(manager.User{"non-existentuser@example.com", "pass"})
|
||||
svc.Register(user)
|
||||
|
||||
cases := []struct {
|
||||
desc string
|
||||
req string
|
||||
contentType string
|
||||
status int
|
||||
res string
|
||||
}{
|
||||
{"login with valid credentials", data, contentType, http.StatusCreated, tokenData},
|
||||
{"login with invalid credentials", invalidData, contentType, http.StatusForbidden, ""},
|
||||
{"login with invalid email address", invalidEmailData, contentType, http.StatusBadRequest, ""},
|
||||
{"login non-existent user", nonexistentData, contentType, http.StatusForbidden, ""},
|
||||
{"login with invalid request format", "{", contentType, http.StatusBadRequest, ""},
|
||||
{"login with empty JSON request", "{}", contentType, http.StatusBadRequest, ""},
|
||||
{"login with empty request", "", contentType, http.StatusBadRequest, ""},
|
||||
{"login with missing content type", data, "", http.StatusUnsupportedMediaType, ""},
|
||||
}
|
||||
|
||||
for _, tc := range cases {
|
||||
req := testRequest{
|
||||
client: client,
|
||||
method: http.MethodPost,
|
||||
url: fmt.Sprintf("%s/tokens", ts.URL),
|
||||
contentType: tc.contentType,
|
||||
body: strings.NewReader(tc.req),
|
||||
}
|
||||
res, err := req.make()
|
||||
assert.Nil(t, err, fmt.Sprintf("%s: unexpected error %s", tc.desc, err))
|
||||
body, err := ioutil.ReadAll(res.Body)
|
||||
assert.Nil(t, err, fmt.Sprintf("%s: unexpected error %s", tc.desc, err))
|
||||
token := strings.Trim(string(body), "\n")
|
||||
|
||||
assert.Equal(t, tc.status, res.StatusCode, fmt.Sprintf("%s: expected status code %d got %d", tc.desc, tc.status, res.StatusCode))
|
||||
assert.Equal(t, tc.res, token, fmt.Sprintf("%s: expected body %s got %s", tc.desc, tc.res, token))
|
||||
}
|
||||
}
|
||||
|
||||
func TestAddClient(t *testing.T) {
|
||||
svc := newService()
|
||||
ts := newServer(svc)
|
||||
defer ts.Close()
|
||||
cli := ts.Client()
|
||||
|
||||
data := toJSON(client)
|
||||
invalidData := toJSON(manager.Client{
|
||||
Type: "foo",
|
||||
Name: "invalid_client",
|
||||
Payload: "some_payload",
|
||||
})
|
||||
svc.Register(user)
|
||||
|
||||
cases := []struct {
|
||||
desc string
|
||||
req string
|
||||
contentType string
|
||||
auth string
|
||||
status int
|
||||
}{
|
||||
{"add valid client", data, contentType, user.Email, http.StatusCreated},
|
||||
{"add client with invalid data", invalidData, contentType, user.Email, http.StatusBadRequest},
|
||||
{"add client with invalid auth token", data, contentType, "invalid_token", http.StatusForbidden},
|
||||
{"add client with invalid request format", "}", contentType, user.Email, http.StatusBadRequest},
|
||||
{"add client with empty JSON request", "{}", contentType, user.Email, http.StatusBadRequest},
|
||||
{"add client with empty request", "", contentType, user.Email, http.StatusBadRequest},
|
||||
{"add client with missing content type", data, "", user.Email, http.StatusUnsupportedMediaType},
|
||||
}
|
||||
|
||||
for _, tc := range cases {
|
||||
req := testRequest{
|
||||
client: cli,
|
||||
method: http.MethodPost,
|
||||
url: fmt.Sprintf("%s/clients", ts.URL),
|
||||
contentType: tc.contentType,
|
||||
token: tc.auth,
|
||||
body: strings.NewReader(tc.req),
|
||||
}
|
||||
res, err := req.make()
|
||||
assert.Nil(t, err, fmt.Sprintf("%s: unexpected error %s", tc.desc, err))
|
||||
assert.Equal(t, tc.status, res.StatusCode, fmt.Sprintf("%s: expected status code %d got %d", tc.desc, tc.status, res.StatusCode))
|
||||
}
|
||||
}
|
||||
|
||||
func TestUpdateClient(t *testing.T) {
|
||||
svc := newService()
|
||||
ts := newServer(svc)
|
||||
defer ts.Close()
|
||||
cli := ts.Client()
|
||||
|
||||
data := toJSON(client)
|
||||
invalidData := toJSON(manager.Client{
|
||||
Type: "foo",
|
||||
Name: client.Name,
|
||||
Payload: client.Payload,
|
||||
})
|
||||
svc.Register(user)
|
||||
id, _ := svc.AddClient(user.Email, client)
|
||||
|
||||
cases := []struct {
|
||||
desc string
|
||||
req string
|
||||
id string
|
||||
contentType string
|
||||
auth string
|
||||
status int
|
||||
}{
|
||||
{"update existing client", data, id, contentType, user.Email, http.StatusOK},
|
||||
{"update non-existent client", data, wrongID, contentType, user.Email, http.StatusNotFound},
|
||||
{"update client with invalid id", data, "1", contentType, user.Email, http.StatusNotFound},
|
||||
{"update client with invalid data", invalidData, id, contentType, user.Email, http.StatusBadRequest},
|
||||
{"update client with invalid user token", data, id, contentType, invalidEmail, http.StatusForbidden},
|
||||
{"update client with invalid data format", "{", id, contentType, user.Email, http.StatusBadRequest},
|
||||
{"update client with empty JSON request", "{}", id, contentType, user.Email, http.StatusBadRequest},
|
||||
{"update client with empty request", "", id, contentType, user.Email, http.StatusBadRequest},
|
||||
{"update client with missing content type", data, id, "", user.Email, http.StatusUnsupportedMediaType},
|
||||
}
|
||||
|
||||
for _, tc := range cases {
|
||||
req := testRequest{
|
||||
client: cli,
|
||||
method: http.MethodPut,
|
||||
url: fmt.Sprintf("%s/clients/%s", ts.URL, tc.id),
|
||||
contentType: tc.contentType,
|
||||
token: tc.auth,
|
||||
body: strings.NewReader(tc.req),
|
||||
}
|
||||
res, err := req.make()
|
||||
assert.Nil(t, err, fmt.Sprintf("%s: unexpected error %s", tc.desc, err))
|
||||
assert.Equal(t, tc.status, res.StatusCode, fmt.Sprintf("%s: expected status code %d got %d", tc.desc, tc.status, res.StatusCode))
|
||||
}
|
||||
}
|
||||
|
||||
func TestViewClient(t *testing.T) {
|
||||
svc := newService()
|
||||
ts := newServer(svc)
|
||||
defer ts.Close()
|
||||
cli := ts.Client()
|
||||
|
||||
svc.Register(user)
|
||||
id, _ := svc.AddClient(user.Email, client)
|
||||
|
||||
client.ID = id
|
||||
client.Key = id
|
||||
data := toJSON(client)
|
||||
|
||||
cases := []struct {
|
||||
desc string
|
||||
id string
|
||||
auth string
|
||||
status int
|
||||
res string
|
||||
}{
|
||||
{"view existing client", id, user.Email, http.StatusOK, data},
|
||||
{"view non-existent client", wrongID, user.Email, http.StatusNotFound, ""},
|
||||
{"view client by passing invalid id", "1", user.Email, http.StatusNotFound, ""},
|
||||
{"view client by passing invalid token", id, invalidEmail, http.StatusForbidden, ""},
|
||||
}
|
||||
|
||||
for _, tc := range cases {
|
||||
req := testRequest{
|
||||
client: cli,
|
||||
method: http.MethodGet,
|
||||
url: fmt.Sprintf("%s/clients/%s", ts.URL, tc.id),
|
||||
token: tc.auth,
|
||||
}
|
||||
res, err := req.make()
|
||||
assert.Nil(t, err, fmt.Sprintf("%s: unexpected error %s", tc.desc, err))
|
||||
body, err := ioutil.ReadAll(res.Body)
|
||||
assert.Nil(t, err, fmt.Sprintf("%s: unexpected error %s", tc.desc, err))
|
||||
data := strings.Trim(string(body), "\n")
|
||||
assert.Equal(t, tc.status, res.StatusCode, fmt.Sprintf("%s: expected status code %d got %d", tc.desc, tc.status, res.StatusCode))
|
||||
assert.Equal(t, tc.res, data, fmt.Sprintf("%s: expected body %s got %s", tc.desc, tc.res, data))
|
||||
}
|
||||
}
|
||||
|
||||
func TestListClients(t *testing.T) {
|
||||
svc := newService()
|
||||
ts := newServer(svc)
|
||||
defer ts.Close()
|
||||
cli := ts.Client()
|
||||
|
||||
svc.Register(user)
|
||||
noClientsUser := manager.User{Email: "no_clients_user@example.com", Password: user.Password}
|
||||
svc.Register(noClientsUser)
|
||||
clients := []manager.Client{}
|
||||
for i := 0; i < 10; i++ {
|
||||
id, _ := svc.AddClient(user.Email, client)
|
||||
client.ID = id
|
||||
client.Key = id
|
||||
clients = append(clients, client)
|
||||
}
|
||||
|
||||
cases := []struct {
|
||||
desc string
|
||||
auth string
|
||||
status int
|
||||
res []manager.Client
|
||||
}{
|
||||
{"fetch list of clients", user.Email, http.StatusOK, clients},
|
||||
{"fetch empty list of clients", noClientsUser.Email, http.StatusOK, []manager.Client{}},
|
||||
{"fetch list of clients with invalid token", invalidEmail, http.StatusForbidden, nil},
|
||||
}
|
||||
|
||||
for _, tc := range cases {
|
||||
req := testRequest{
|
||||
client: cli,
|
||||
method: http.MethodGet,
|
||||
url: fmt.Sprintf("%s/clients", ts.URL),
|
||||
token: tc.auth,
|
||||
}
|
||||
res, err := req.make()
|
||||
assert.Nil(t, err, fmt.Sprintf("%s: unexpected error %s", tc.desc, err))
|
||||
var data map[string][]manager.Client
|
||||
json.NewDecoder(res.Body).Decode(&data)
|
||||
assert.Equal(t, tc.status, res.StatusCode, fmt.Sprintf("%s: expected status code %d got %d", tc.desc, tc.status, res.StatusCode))
|
||||
assert.ElementsMatch(t, tc.res, data["clients"], fmt.Sprintf("%s: expected body %s got %s", tc.desc, tc.res, data["clients"]))
|
||||
}
|
||||
}
|
||||
|
||||
func TestRemoveClient(t *testing.T) {
|
||||
svc := newService()
|
||||
ts := newServer(svc)
|
||||
defer ts.Close()
|
||||
cli := ts.Client()
|
||||
|
||||
svc.Register(user)
|
||||
id, _ := svc.AddClient(user.Email, client)
|
||||
|
||||
cases := []struct {
|
||||
desc string
|
||||
id string
|
||||
auth string
|
||||
status int
|
||||
}{
|
||||
{"delete existing client", id, user.Email, http.StatusNoContent},
|
||||
{"delete non-existent client", wrongID, user.Email, http.StatusNoContent},
|
||||
{"delete client with invalid id", "1", user.Email, http.StatusNoContent},
|
||||
{"delete client with invalid token", id, invalidEmail, http.StatusForbidden},
|
||||
}
|
||||
|
||||
for _, tc := range cases {
|
||||
req := testRequest{
|
||||
client: cli,
|
||||
method: http.MethodDelete,
|
||||
url: fmt.Sprintf("%s/clients/%s", ts.URL, tc.id),
|
||||
token: tc.auth,
|
||||
}
|
||||
res, err := req.make()
|
||||
assert.Nil(t, err, fmt.Sprintf("%s: unexpected error %s", tc.desc, err))
|
||||
assert.Equal(t, tc.status, res.StatusCode, fmt.Sprintf("%s: expected status code %d got %d", tc.desc, tc.status, res.StatusCode))
|
||||
}
|
||||
}
|
||||
|
||||
func TestCreateChannel(t *testing.T) {
|
||||
svc := newService()
|
||||
ts := newServer(svc)
|
||||
defer ts.Close()
|
||||
client := ts.Client()
|
||||
|
||||
data := toJSON(channel)
|
||||
svc.Register(user)
|
||||
|
||||
cases := []struct {
|
||||
desc string
|
||||
req string
|
||||
contentType string
|
||||
auth string
|
||||
status int
|
||||
}{
|
||||
{"create new channel", data, contentType, user.Email, http.StatusCreated},
|
||||
{"create new channel with invalid token", data, contentType, invalidEmail, http.StatusForbidden},
|
||||
{"create new channel with invalid data format", "{", contentType, user.Email, http.StatusBadRequest},
|
||||
{"create new channel with empty JSON request", "{}", contentType, user.Email, http.StatusCreated},
|
||||
{"create new channel with empty request", "", contentType, user.Email, http.StatusBadRequest},
|
||||
{"create new channel with missing content type", data, "", user.Email, http.StatusUnsupportedMediaType},
|
||||
}
|
||||
|
||||
for _, tc := range cases {
|
||||
req := testRequest{
|
||||
client: client,
|
||||
method: http.MethodPost,
|
||||
url: fmt.Sprintf("%s/channels", ts.URL),
|
||||
contentType: tc.contentType,
|
||||
token: tc.auth,
|
||||
body: strings.NewReader(tc.req),
|
||||
}
|
||||
res, err := req.make()
|
||||
assert.Nil(t, err, fmt.Sprintf("%s: unexpected error %s", tc.desc, err))
|
||||
assert.Equal(t, tc.status, res.StatusCode, fmt.Sprintf("%s: expected status code %d got %d", tc.desc, tc.status, res.StatusCode))
|
||||
}
|
||||
}
|
||||
|
||||
func TestUpdateChannel(t *testing.T) {
|
||||
svc := newService()
|
||||
ts := newServer(svc)
|
||||
defer ts.Close()
|
||||
client := ts.Client()
|
||||
|
||||
updateData := toJSON(map[string]string{
|
||||
"name": "updated_channel",
|
||||
})
|
||||
svc.Register(user)
|
||||
id, _ := svc.CreateChannel(user.Email, channel)
|
||||
|
||||
cases := []struct {
|
||||
desc string
|
||||
req string
|
||||
id string
|
||||
contentType string
|
||||
auth string
|
||||
status int
|
||||
}{
|
||||
{"update existing channel", updateData, id, contentType, user.Email, http.StatusOK},
|
||||
{"update non-existing channel", updateData, wrongID, contentType, user.Email, http.StatusNotFound},
|
||||
{"update channel with invalid token", updateData, id, contentType, invalidEmail, http.StatusForbidden},
|
||||
{"update channel with invalid id", updateData, "1", contentType, user.Email, http.StatusNotFound},
|
||||
{"update channel with invalid data format", "}", id, contentType, user.Email, http.StatusBadRequest},
|
||||
{"update channel with empty JSON object", "{}", id, contentType, user.Email, http.StatusOK},
|
||||
{"update channel with empty request", "", id, contentType, user.Email, http.StatusBadRequest},
|
||||
{"update channel with missing content type", updateData, id, "", user.Email, http.StatusUnsupportedMediaType},
|
||||
}
|
||||
|
||||
for _, tc := range cases {
|
||||
req := testRequest{
|
||||
client: client,
|
||||
method: http.MethodPut,
|
||||
url: fmt.Sprintf("%s/channels/%s", ts.URL, tc.id),
|
||||
contentType: tc.contentType,
|
||||
token: tc.auth,
|
||||
body: strings.NewReader(tc.req),
|
||||
}
|
||||
res, err := req.make()
|
||||
assert.Nil(t, err, fmt.Sprintf("%s: unexpected error %s", tc.desc, err))
|
||||
assert.Equal(t, tc.status, res.StatusCode, fmt.Sprintf("%s: expected status code %d got %d", tc.desc, tc.status, res.StatusCode))
|
||||
}
|
||||
}
|
||||
|
||||
func TestViewChannel(t *testing.T) {
|
||||
svc := newService()
|
||||
ts := newServer(svc)
|
||||
defer ts.Close()
|
||||
client := ts.Client()
|
||||
|
||||
svc.Register(user)
|
||||
id, _ := svc.CreateChannel(user.Email, channel)
|
||||
channel.ID = id
|
||||
data := toJSON(channel)
|
||||
|
||||
cases := []struct {
|
||||
desc string
|
||||
id string
|
||||
auth string
|
||||
status int
|
||||
res string
|
||||
}{
|
||||
{"view existing channel", id, user.Email, http.StatusOK, data},
|
||||
{"view non-existent channel", wrongID, user.Email, http.StatusNotFound, ""},
|
||||
{"view channel with invalid id", "1", user.Email, http.StatusNotFound, ""},
|
||||
{"view channel with invalid token", id, invalidEmail, http.StatusForbidden, ""},
|
||||
}
|
||||
|
||||
for _, tc := range cases {
|
||||
req := testRequest{
|
||||
client: client,
|
||||
method: http.MethodGet,
|
||||
url: fmt.Sprintf("%s/channels/%s", ts.URL, tc.id),
|
||||
token: tc.auth,
|
||||
}
|
||||
res, err := req.make()
|
||||
assert.Nil(t, err, fmt.Sprintf("%s: unexpected error %s", tc.desc, err))
|
||||
data, err := ioutil.ReadAll(res.Body)
|
||||
assert.Nil(t, err, fmt.Sprintf("%s: unexpected error %s", tc.desc, err))
|
||||
body := strings.Trim(string(data), "\n")
|
||||
assert.Equal(t, tc.status, res.StatusCode, fmt.Sprintf("%s: expected status code %d got %d", tc.desc, tc.status, res.StatusCode))
|
||||
assert.Equal(t, tc.res, body, fmt.Sprintf("%s: expected body %s got %s", tc.desc, tc.res, body))
|
||||
}
|
||||
}
|
||||
|
||||
func TestListChannels(t *testing.T) {
|
||||
svc := newService()
|
||||
ts := newServer(svc)
|
||||
defer ts.Close()
|
||||
client := ts.Client()
|
||||
|
||||
svc.Register(user)
|
||||
channels := []manager.Channel{}
|
||||
for i := 0; i < 10; i++ {
|
||||
id, _ := svc.CreateChannel(user.Email, channel)
|
||||
channel.ID = id
|
||||
channels = append(channels, channel)
|
||||
}
|
||||
|
||||
cases := []struct {
|
||||
desc string
|
||||
auth string
|
||||
status int
|
||||
res []manager.Channel
|
||||
}{
|
||||
{"get a list of channels", user.Email, http.StatusOK, channels},
|
||||
{"get a list of channels with invalid token", invalidEmail, http.StatusForbidden, nil},
|
||||
}
|
||||
|
||||
for _, tc := range cases {
|
||||
req := testRequest{
|
||||
client: client,
|
||||
method: http.MethodGet,
|
||||
url: fmt.Sprintf("%s/channels", ts.URL),
|
||||
token: tc.auth,
|
||||
}
|
||||
res, err := req.make()
|
||||
assert.Nil(t, err, fmt.Sprintf("%s: unexpected error %s", tc.desc, err))
|
||||
var body map[string][]manager.Channel
|
||||
json.NewDecoder(res.Body).Decode(&body)
|
||||
assert.Equal(t, tc.status, res.StatusCode, fmt.Sprintf("%s: expected status code %d got %d", tc.desc, tc.status, res.StatusCode))
|
||||
assert.ElementsMatch(t, tc.res, body["channels"], fmt.Sprintf("%s: expected body %s got %s", tc.desc, tc.res, body["channels"]))
|
||||
}
|
||||
}
|
||||
|
||||
func TestRemoveChannel(t *testing.T) {
|
||||
svc := newService()
|
||||
ts := newServer(svc)
|
||||
defer ts.Close()
|
||||
client := ts.Client()
|
||||
|
||||
svc.Register(user)
|
||||
id, _ := svc.CreateChannel(user.Email, channel)
|
||||
channel.ID = id
|
||||
|
||||
cases := []struct {
|
||||
desc string
|
||||
id string
|
||||
auth string
|
||||
status int
|
||||
}{
|
||||
{"remove existing channel", channel.ID, user.Email, http.StatusNoContent},
|
||||
{"remove non-existent channel", channel.ID, user.Email, http.StatusNoContent},
|
||||
{"remove channel with invalid id", wrongID, user.Email, http.StatusNoContent},
|
||||
{"remove channel with invalid token", channel.ID, invalidEmail, http.StatusForbidden},
|
||||
}
|
||||
|
||||
for _, tc := range cases {
|
||||
req := testRequest{
|
||||
client: client,
|
||||
method: http.MethodDelete,
|
||||
url: fmt.Sprintf("%s/channels/%s", ts.URL, tc.id),
|
||||
token: tc.auth,
|
||||
}
|
||||
res, err := req.make()
|
||||
assert.Nil(t, err, fmt.Sprintf("%s: unexpected error %s", tc.desc, err))
|
||||
assert.Equal(t, tc.status, res.StatusCode, fmt.Sprintf("%s: expected status code %d got %d", tc.desc, tc.status, res.StatusCode))
|
||||
}
|
||||
}
|
||||
|
||||
func TestConnect(t *testing.T) {
|
||||
svc := newService()
|
||||
ts := newServer(svc)
|
||||
defer ts.Close()
|
||||
cli := ts.Client()
|
||||
|
||||
svc.Register(user)
|
||||
clientID, _ := svc.AddClient(user.Email, client)
|
||||
chanID, _ := svc.CreateChannel(user.Email, channel)
|
||||
|
||||
otherUser := manager.User{Email: "other_user@example.com", Password: "password"}
|
||||
svc.Register(otherUser)
|
||||
otherClientID, _ := svc.AddClient(otherUser.Email, client)
|
||||
otherChanID, _ := svc.CreateChannel(otherUser.Email, channel)
|
||||
|
||||
cases := []struct {
|
||||
desc string
|
||||
chanID string
|
||||
clientID string
|
||||
auth string
|
||||
status int
|
||||
}{
|
||||
{"connect existing client to existing channel", chanID, clientID, user.Email, http.StatusOK},
|
||||
{"connect existing client to non-existent channel", wrongID, clientID, user.Email, http.StatusNotFound},
|
||||
{"connect client with invalid id to channel", chanID, "1", user.Email, http.StatusNotFound},
|
||||
{"connect client to channel with invalid id", "1", clientID, user.Email, http.StatusNotFound},
|
||||
{"connect existing client to existing channel with invalid token", chanID, clientID, invalidEmail, http.StatusForbidden},
|
||||
{"connect client from owner to channel of other user", otherChanID, clientID, user.Email, http.StatusNotFound},
|
||||
{"connect client from other user to owner's channel", chanID, otherClientID, user.Email, http.StatusNotFound},
|
||||
}
|
||||
|
||||
for _, tc := range cases {
|
||||
req := testRequest{
|
||||
client: cli,
|
||||
method: http.MethodPut,
|
||||
url: fmt.Sprintf("%s/channels/%s/clients/%s", ts.URL, tc.chanID, tc.clientID),
|
||||
token: tc.auth,
|
||||
}
|
||||
res, err := req.make()
|
||||
assert.Nil(t, err, fmt.Sprintf("%s: unexpected error %s", tc.desc, err))
|
||||
assert.Equal(t, tc.status, res.StatusCode, fmt.Sprintf("%s: expected status code %d got %d", tc.desc, tc.status, res.StatusCode))
|
||||
}
|
||||
}
|
||||
|
||||
func TestDisconnnect(t *testing.T) {
|
||||
svc := newService()
|
||||
ts := newServer(svc)
|
||||
defer ts.Close()
|
||||
cli := ts.Client()
|
||||
|
||||
svc.Register(user)
|
||||
clientID, _ := svc.AddClient(user.Email, client)
|
||||
chanID, _ := svc.CreateChannel(user.Email, channel)
|
||||
svc.Connect(user.Email, chanID, clientID)
|
||||
otherUser := manager.User{Email: "other_user@example.com", Password: "password"}
|
||||
svc.Register(otherUser)
|
||||
otherClientID, _ := svc.AddClient(otherUser.Email, client)
|
||||
otherChanID, _ := svc.CreateChannel(otherUser.Email, channel)
|
||||
svc.Connect(otherUser.Email, otherChanID, otherClientID)
|
||||
|
||||
cases := []struct {
|
||||
desc string
|
||||
chanID string
|
||||
clientID string
|
||||
auth string
|
||||
status int
|
||||
}{
|
||||
{"disconnect connected client from channel", chanID, clientID, user.Email, http.StatusNoContent},
|
||||
{"disconnect non-connected client from channel", chanID, clientID, user.Email, http.StatusNotFound},
|
||||
{"disconnect non-existent client from channel", chanID, "1", user.Email, http.StatusNotFound},
|
||||
{"disconnect client from non-existent channel", "1", clientID, user.Email, http.StatusNotFound},
|
||||
{"disconnect client from channel with invalid token", chanID, clientID, invalidEmail, http.StatusForbidden},
|
||||
{"disconnect owner's client from someone elses channel", otherChanID, clientID, user.Email, http.StatusNotFound},
|
||||
{"disconnect other's client from owner's channel", chanID, otherClientID, user.Email, http.StatusNotFound},
|
||||
}
|
||||
|
||||
for _, tc := range cases {
|
||||
req := testRequest{
|
||||
client: cli,
|
||||
method: http.MethodDelete,
|
||||
url: fmt.Sprintf("%s/channels/%s/clients/%s", ts.URL, tc.chanID, tc.clientID),
|
||||
token: tc.auth,
|
||||
}
|
||||
res, err := req.make()
|
||||
assert.Nil(t, err, fmt.Sprintf("%s: unexpected error %s", tc.desc, err))
|
||||
assert.Equal(t, tc.status, res.StatusCode, fmt.Sprintf("%s: expected status code %d got %d", tc.desc, tc.status, res.StatusCode))
|
||||
}
|
||||
}
|
||||
|
||||
func TestIdentity(t *testing.T) {
|
||||
svc := newService()
|
||||
ts := newServer(svc)
|
||||
defer ts.Close()
|
||||
cli := ts.Client()
|
||||
|
||||
svc.Register(user)
|
||||
clientID, _ := svc.AddClient(user.Email, client)
|
||||
|
||||
cases := []struct {
|
||||
desc string
|
||||
key string
|
||||
status int
|
||||
clientID string
|
||||
}{
|
||||
{"get client id using existing client key", clientID, http.StatusOK, clientID},
|
||||
{"get client id using non-existent client key", "", http.StatusForbidden, ""},
|
||||
}
|
||||
|
||||
for _, tc := range cases {
|
||||
req := testRequest{
|
||||
client: cli,
|
||||
method: http.MethodGet,
|
||||
url: fmt.Sprintf("%s/access-grant", ts.URL),
|
||||
token: tc.key,
|
||||
}
|
||||
res, err := req.make()
|
||||
assert.Nil(t, err, fmt.Sprintf("%s: unexpected error %s", tc.desc, err))
|
||||
clientID := res.Header.Get("X-client-id")
|
||||
assert.Equal(t, tc.status, res.StatusCode, fmt.Sprintf("%s: expected status code %d got %d", tc.desc, tc.status, res.StatusCode))
|
||||
assert.Equal(t, tc.clientID, clientID, fmt.Sprintf("%s: expected %s got %s", tc.desc, tc.clientID, clientID))
|
||||
}
|
||||
}
|
||||
|
||||
func TestCanAccess(t *testing.T) {
|
||||
svc := newService()
|
||||
ts := newServer(svc)
|
||||
defer ts.Close()
|
||||
cli := ts.Client()
|
||||
|
||||
svc.Register(user)
|
||||
clientID, _ := svc.AddClient(user.Email, client)
|
||||
notConnectedClientID, _ := svc.AddClient(user.Email, client)
|
||||
chanID, _ := svc.CreateChannel(user.Email, channel)
|
||||
svc.Connect(user.Email, chanID, clientID)
|
||||
|
||||
cases := []struct {
|
||||
desc string
|
||||
chanID string
|
||||
clientKey string
|
||||
status int
|
||||
clientID string
|
||||
}{
|
||||
{"check access to existing channel given connected client", chanID, clientID, http.StatusOK, clientID},
|
||||
{"check access to existing channel given not connected client", chanID, notConnectedClientID, http.StatusForbidden, ""},
|
||||
{"check access to existing channel given non-existent client", chanID, "invalid_token", http.StatusForbidden, ""},
|
||||
{"check access to non-existent channel given existing client", "invalid_token", clientID, http.StatusForbidden, ""},
|
||||
}
|
||||
|
||||
for _, tc := range cases {
|
||||
req := testRequest{
|
||||
client: cli,
|
||||
method: http.MethodGet,
|
||||
url: fmt.Sprintf("%s/channels/%s/access-grant", ts.URL, tc.chanID),
|
||||
token: tc.clientKey,
|
||||
}
|
||||
res, err := req.make()
|
||||
assert.Nil(t, err, fmt.Sprintf("%s: unexpected error %s", tc.desc, err))
|
||||
clientID := res.Header.Get("X-client-id")
|
||||
assert.Equal(t, tc.status, res.StatusCode, fmt.Sprintf("%s: expected status code %d got %d", tc.desc, tc.status, res.StatusCode))
|
||||
assert.Equal(t, tc.clientID, clientID, fmt.Sprintf("%s: expected %s got %s", tc.desc, tc.clientID, clientID))
|
||||
}
|
||||
}
|
|
@ -13,14 +13,14 @@ const wrong string = "wrong-value"
|
|||
|
||||
var (
|
||||
user manager.User = manager.User{"user@example.com", "password"}
|
||||
client manager.Client = manager.Client{ID: "1", Type: "app", Name: "test", Key: "1"}
|
||||
channel manager.Channel = manager.Channel{ID: "1", Name: "test", Clients: []manager.Client{client}}
|
||||
client manager.Client = manager.Client{Type: "app", Name: "test"}
|
||||
channel manager.Channel = manager.Channel{Name: "test", Clients: []manager.Client{}}
|
||||
)
|
||||
|
||||
func newService() manager.Service {
|
||||
users := mocks.NewUserRepository()
|
||||
clients := mocks.NewClientRepository()
|
||||
channels := mocks.NewChannelRepository()
|
||||
channels := mocks.NewChannelRepository(clients)
|
||||
hasher := mocks.NewHasher()
|
||||
idp := mocks.NewIdentityProvider()
|
||||
|
||||
|
@ -89,7 +89,8 @@ func TestUpdateClient(t *testing.T) {
|
|||
svc := newService()
|
||||
svc.Register(user)
|
||||
key, _ := svc.Login(user)
|
||||
svc.AddClient(key, client)
|
||||
clientId, _ := svc.AddClient(key, client)
|
||||
client.ID = clientId
|
||||
|
||||
cases := map[string]struct {
|
||||
client manager.Client
|
||||
|
@ -111,7 +112,8 @@ func TestViewClient(t *testing.T) {
|
|||
svc := newService()
|
||||
svc.Register(user)
|
||||
key, _ := svc.Login(user)
|
||||
svc.AddClient(key, client)
|
||||
clientId, _ := svc.AddClient(key, client)
|
||||
client.ID = clientId
|
||||
|
||||
cases := map[string]struct {
|
||||
id string
|
||||
|
@ -152,7 +154,8 @@ func TestRemoveClient(t *testing.T) {
|
|||
svc := newService()
|
||||
svc.Register(user)
|
||||
key, _ := svc.Login(user)
|
||||
svc.AddClient(key, client)
|
||||
clientId, _ := svc.AddClient(key, client)
|
||||
client.ID = clientId
|
||||
|
||||
cases := map[string]struct {
|
||||
id string
|
||||
|
@ -195,7 +198,8 @@ func TestUpdateChannel(t *testing.T) {
|
|||
svc := newService()
|
||||
svc.Register(user)
|
||||
key, _ := svc.Login(user)
|
||||
svc.CreateChannel(key, channel)
|
||||
chanId, _ := svc.CreateChannel(key, channel)
|
||||
channel.ID = chanId
|
||||
|
||||
cases := map[string]struct {
|
||||
channel manager.Channel
|
||||
|
@ -217,7 +221,8 @@ func TestViewChannel(t *testing.T) {
|
|||
svc := newService()
|
||||
svc.Register(user)
|
||||
key, _ := svc.Login(user)
|
||||
svc.CreateChannel(key, channel)
|
||||
chanId, _ := svc.CreateChannel(key, channel)
|
||||
channel.ID = chanId
|
||||
|
||||
cases := map[string]struct {
|
||||
id string
|
||||
|
@ -258,7 +263,8 @@ func TestRemoveChannel(t *testing.T) {
|
|||
svc := newService()
|
||||
svc.Register(user)
|
||||
key, _ := svc.Login(user)
|
||||
svc.CreateChannel(key, channel)
|
||||
chanId, _ := svc.CreateChannel(key, channel)
|
||||
channel.ID = chanId
|
||||
|
||||
cases := map[string]struct {
|
||||
id string
|
||||
|
@ -283,7 +289,9 @@ func TestConnect(t *testing.T) {
|
|||
key, _ := svc.Login(user)
|
||||
|
||||
clientId, _ := svc.AddClient(key, client)
|
||||
client.ID = clientId
|
||||
chanId, _ := svc.CreateChannel(key, channel)
|
||||
channel.ID = chanId
|
||||
|
||||
cases := map[string]struct {
|
||||
key string
|
||||
|
@ -291,9 +299,9 @@ func TestConnect(t *testing.T) {
|
|||
clientId string
|
||||
err error
|
||||
}{
|
||||
"connect client": {key, chanId, clientId, nil},
|
||||
"connect client with wrong credentials": {wrong, chanId, clientId, manager.ErrUnauthorizedAccess},
|
||||
"connect client to non-existing channel": {key, wrong, clientId, manager.ErrNotFound},
|
||||
"connect client": {key, channel.ID, client.ID, nil},
|
||||
"connect client with wrong credentials": {wrong, channel.ID, client.ID, manager.ErrUnauthorizedAccess},
|
||||
"connect client to non-existing channel": {key, wrong, client.ID, manager.ErrNotFound},
|
||||
}
|
||||
|
||||
for desc, tc := range cases {
|
||||
|
@ -308,7 +316,9 @@ func TestDisconnect(t *testing.T) {
|
|||
key, _ := svc.Login(user)
|
||||
|
||||
clientId, _ := svc.AddClient(key, client)
|
||||
client.ID = clientId
|
||||
chanId, _ := svc.CreateChannel(key, channel)
|
||||
channel.ID = chanId
|
||||
|
||||
svc.Connect(key, chanId, clientId)
|
||||
|
||||
|
@ -319,11 +329,11 @@ func TestDisconnect(t *testing.T) {
|
|||
clientId string
|
||||
err error
|
||||
}{
|
||||
{"disconnect connected client", key, chanId, clientId, nil},
|
||||
{"disconnect disconnected client", key, chanId, clientId, manager.ErrNotFound},
|
||||
{"disconnect client with wrong credentials", wrong, chanId, clientId, manager.ErrUnauthorizedAccess},
|
||||
{"disconnect client from non-existing channel", key, wrong, clientId, manager.ErrNotFound},
|
||||
{"disconnect non-existing client", key, chanId, wrong, manager.ErrNotFound},
|
||||
{"disconnect connected client", key, channel.ID, client.ID, nil},
|
||||
{"disconnect disconnected client", key, channel.ID, client.ID, manager.ErrNotFound},
|
||||
{"disconnect client with wrong credentials", wrong, channel.ID, client.ID, manager.ErrUnauthorizedAccess},
|
||||
{"disconnect client from non-existing channel", key, wrong, client.ID, manager.ErrNotFound},
|
||||
{"disconnect non-existing client", key, channel.ID, wrong, manager.ErrNotFound},
|
||||
}
|
||||
|
||||
for _, tc := range cases {
|
||||
|
@ -357,8 +367,13 @@ func TestCanAccess(t *testing.T) {
|
|||
svc.Register(user)
|
||||
key, _ := svc.Login(user)
|
||||
|
||||
svc.AddClient(key, client)
|
||||
svc.CreateChannel(key, channel)
|
||||
clientId, _ := svc.AddClient(key, client)
|
||||
client.ID = clientId
|
||||
client.Key = clientId
|
||||
|
||||
channel.Clients = []manager.Client{client}
|
||||
chanId, _ := svc.CreateChannel(key, channel)
|
||||
channel.ID = chanId
|
||||
|
||||
cases := map[string]struct {
|
||||
key string
|
||||
|
|
|
@ -2,7 +2,6 @@ package mocks
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
|
@ -15,12 +14,14 @@ type channelRepositoryMock struct {
|
|||
mu sync.Mutex
|
||||
counter int
|
||||
channels map[string]manager.Channel
|
||||
clients manager.ClientRepository
|
||||
}
|
||||
|
||||
// NewChannelRepository creates in-memory channel repository.
|
||||
func NewChannelRepository() manager.ChannelRepository {
|
||||
func NewChannelRepository(clients manager.ClientRepository) manager.ChannelRepository {
|
||||
return &channelRepositoryMock{
|
||||
channels: make(map[string]manager.Channel),
|
||||
clients: clients,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -29,7 +30,7 @@ func (crm *channelRepositoryMock) Save(channel manager.Channel) (string, error)
|
|||
defer crm.mu.Unlock()
|
||||
|
||||
crm.counter += 1
|
||||
channel.ID = strconv.Itoa(crm.counter)
|
||||
channel.ID = fmt.Sprintf("123e4567-e89b-12d3-a456-%012d", crm.counter)
|
||||
|
||||
crm.channels[key(channel.Owner, channel.ID)] = channel
|
||||
|
||||
|
@ -85,10 +86,11 @@ func (crm *channelRepositoryMock) Connect(owner, chanId, clientId string) error
|
|||
return err
|
||||
}
|
||||
|
||||
// Since the current implementation has no way to retrieve a real client
|
||||
// instance, the implementation will assume client always exist and create
|
||||
// a dummy one, containing only the provided ID.
|
||||
channel.Clients = append(channel.Clients, manager.Client{ID: clientId})
|
||||
client, err := crm.clients.One(owner, clientId)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
channel.Clients = append(channel.Clients, client)
|
||||
return crm.Update(channel)
|
||||
}
|
||||
|
||||
|
|
|
@ -2,7 +2,6 @@ package mocks
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
|
@ -29,7 +28,7 @@ func (crm *clientRepositoryMock) Id() string {
|
|||
defer crm.mu.Unlock()
|
||||
|
||||
crm.counter += 1
|
||||
return strconv.Itoa(crm.counter)
|
||||
return fmt.Sprintf("123e4567-e89b-12d3-a456-%012d", crm.counter)
|
||||
}
|
||||
|
||||
func (crm *clientRepositoryMock) Save(client manager.Client) error {
|
||||
|
|
|
@ -30,6 +30,8 @@ paths:
|
|||
description: Failed due to malformed JSON.
|
||||
409:
|
||||
description: Failed due to using an existing email address.
|
||||
415:
|
||||
description: Missing or invalid content type.
|
||||
500:
|
||||
$ref: "#/responses/ServiceError"
|
||||
/tokens:
|
||||
|
@ -53,7 +55,12 @@ paths:
|
|||
$ref: "#/definitions/Token"
|
||||
400:
|
||||
description: |
|
||||
Failed due to malformed JSON or using an invalid credentials.
|
||||
Failed due to malformed JSON.
|
||||
403:
|
||||
description: |
|
||||
Failed due to using invalid credentials.
|
||||
415:
|
||||
description: Missing or invalid content type.
|
||||
500:
|
||||
$ref: "#/responses/ServiceError"
|
||||
/clients:
|
||||
|
@ -83,6 +90,8 @@ paths:
|
|||
description: Failed due to malformed JSON.
|
||||
403:
|
||||
description: Missing or invalid access token provided.
|
||||
415:
|
||||
description: Missing or invalid content type.
|
||||
500:
|
||||
$ref: "#/responses/ServiceError"
|
||||
get:
|
||||
|
@ -160,6 +169,8 @@ paths:
|
|||
description: Missing or invalid access token provided.
|
||||
404:
|
||||
description: Client does not exist.
|
||||
415:
|
||||
description: Missing or invalid content type.
|
||||
500:
|
||||
$ref: "#/responses/ServiceError"
|
||||
delete:
|
||||
|
@ -206,6 +217,8 @@ paths:
|
|||
description: Failed due to malformed JSON.
|
||||
403:
|
||||
description: Missing or invalid access token provided.
|
||||
415:
|
||||
description: Missing or invalid content type.
|
||||
500:
|
||||
$ref: "#/responses/ServiceError"
|
||||
get:
|
||||
|
@ -283,6 +296,8 @@ paths:
|
|||
description: Missing or invalid access token provided.
|
||||
404:
|
||||
description: Channel does not exist.
|
||||
415:
|
||||
description: Missing or invalid content type.
|
||||
500:
|
||||
$ref: "#/responses/ServiceError"
|
||||
delete:
|
||||
|
|
Loading…
Reference in New Issue