mirror of
https://github.com/absmach/supermq.git
synced 2026-06-23 07:30:25 +00:00
67feea693e
Signed-off-by: dusan <borovcanindusan1@gmail.com>
1405 lines
42 KiB
Go
1405 lines
42 KiB
Go
// Copyright (c) Abstract Machines
|
|
// SPDX-License-Identifier: Apache-2.0
|
|
|
|
package api_test
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"io"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/0x6flab/namegenerator"
|
|
"github.com/absmach/magistrala/internal/testsutil"
|
|
pkgSch "github.com/absmach/magistrala/pkg/schedule"
|
|
"github.com/absmach/magistrala/reports"
|
|
"github.com/absmach/magistrala/reports/api"
|
|
"github.com/absmach/magistrala/reports/mocks"
|
|
apiutil "github.com/absmach/supermq/api/http/util"
|
|
"github.com/absmach/supermq/auth"
|
|
smqlog "github.com/absmach/supermq/logger"
|
|
smqauthn "github.com/absmach/supermq/pkg/authn"
|
|
authnmocks "github.com/absmach/supermq/pkg/authn/mocks"
|
|
"github.com/absmach/supermq/pkg/errors"
|
|
svcerr "github.com/absmach/supermq/pkg/errors/service"
|
|
"github.com/go-chi/chi/v5"
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/mock"
|
|
)
|
|
|
|
const contentType = "application/json"
|
|
|
|
var (
|
|
namegen = namegenerator.NewGenerator()
|
|
domainID = testsutil.GenerateUUID(&testing.T{})
|
|
userID = testsutil.GenerateUUID(&testing.T{})
|
|
validID = testsutil.GenerateUUID(&testing.T{})
|
|
validToken = "valid"
|
|
invalidToken = "invalid"
|
|
now = time.Now().UTC().Truncate(time.Minute)
|
|
future = now.Add(1 * time.Hour)
|
|
schedule = pkgSch.Schedule{
|
|
StartDateTime: future,
|
|
Recurring: pkgSch.Daily,
|
|
RecurringPeriod: 1,
|
|
Time: future,
|
|
}
|
|
reportConfig = reports.ReportConfig{
|
|
ID: validID,
|
|
Name: namegen.Generate(),
|
|
DomainID: domainID,
|
|
Schedule: schedule,
|
|
Status: reports.EnabledStatus,
|
|
Metrics: []reports.ReqMetric{
|
|
{
|
|
ChannelID: "channel1",
|
|
ClientIDs: []string{"client1"},
|
|
Name: "metric_name",
|
|
},
|
|
},
|
|
Config: &reports.MetricConfig{
|
|
From: "now()-1h",
|
|
To: "now()",
|
|
Title: title,
|
|
Aggregation: reports.AggConfig{AggType: reports.AggregationAVG, Interval: "1h"},
|
|
},
|
|
Email: &reports.EmailSetting{
|
|
To: []string{"test@example.com"},
|
|
Subject: "Test Report",
|
|
},
|
|
}
|
|
title = "test_title"
|
|
)
|
|
|
|
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", apiutil.BearerPrefix+tr.token)
|
|
}
|
|
|
|
if tr.contentType != "" {
|
|
req.Header.Set("Content-Type", tr.contentType)
|
|
}
|
|
|
|
req.Header.Set("Referer", "http://localhost")
|
|
|
|
return tr.client.Do(req)
|
|
}
|
|
|
|
func newReportsServer() (*httptest.Server, *mocks.Service, *authnmocks.Authentication) {
|
|
svc := new(mocks.Service)
|
|
authn := new(authnmocks.Authentication)
|
|
|
|
logger := smqlog.NewMock()
|
|
mux := chi.NewRouter()
|
|
am := smqauthn.NewAuthNMiddleware(authn, smqauthn.WithAllowUnverifiedUser(true))
|
|
|
|
api.MakeHandler(svc, am, mux, logger, "")
|
|
|
|
return httptest.NewServer(mux), svc, authn
|
|
}
|
|
|
|
func toJSON(data any) string {
|
|
jsonData, err := json.Marshal(data)
|
|
if err != nil {
|
|
return ""
|
|
}
|
|
return string(jsonData)
|
|
}
|
|
|
|
func TestAddReportConfigEndpoint(t *testing.T) {
|
|
ts, svc, authn := newReportsServer()
|
|
defer ts.Close()
|
|
|
|
scheduleInPast := pkgSch.Schedule{
|
|
StartDateTime: now,
|
|
Recurring: pkgSch.Daily,
|
|
RecurringPeriod: 1,
|
|
Time: now,
|
|
}
|
|
|
|
reportInPast := reportConfig
|
|
reportInPast.Schedule = scheduleInPast
|
|
|
|
cases := []struct {
|
|
desc string
|
|
cfg reports.ReportConfig
|
|
domainID string
|
|
token string
|
|
contentType string
|
|
status int
|
|
authnRes smqauthn.Session
|
|
authnErr error
|
|
svcRes reports.ReportConfig
|
|
svcErr error
|
|
err error
|
|
}{
|
|
{
|
|
desc: "add report config successfully",
|
|
cfg: reportConfig,
|
|
token: validToken,
|
|
contentType: contentType,
|
|
domainID: domainID,
|
|
authnRes: smqauthn.Session{DomainUserID: auth.EncodeDomainUserID(domainID, userID), UserID: userID, DomainID: domainID},
|
|
status: http.StatusCreated,
|
|
svcRes: reportConfig,
|
|
},
|
|
{
|
|
desc: "add report config with invalid token",
|
|
cfg: reportConfig,
|
|
token: invalidToken,
|
|
authnRes: smqauthn.Session{},
|
|
domainID: domainID,
|
|
contentType: contentType,
|
|
authnErr: svcerr.ErrAuthentication,
|
|
status: http.StatusUnauthorized,
|
|
err: svcerr.ErrAuthentication,
|
|
},
|
|
{
|
|
desc: "add report config with empty token",
|
|
token: "",
|
|
authnRes: smqauthn.Session{},
|
|
domainID: domainID,
|
|
cfg: reportConfig,
|
|
contentType: contentType,
|
|
status: http.StatusUnauthorized,
|
|
err: apiutil.ErrBearerToken,
|
|
},
|
|
{
|
|
desc: "add report config with empty domainID",
|
|
token: validToken,
|
|
cfg: reportConfig,
|
|
contentType: contentType,
|
|
status: http.StatusBadRequest,
|
|
err: apiutil.ErrMissingDomainID,
|
|
},
|
|
{
|
|
desc: "add report config with invalid content type",
|
|
token: validToken,
|
|
domainID: domainID,
|
|
cfg: reportConfig,
|
|
contentType: "application/xml",
|
|
status: http.StatusUnsupportedMediaType,
|
|
err: apiutil.ErrUnsupportedContentType,
|
|
},
|
|
{
|
|
desc: "add report config with startdatetime in past",
|
|
token: validToken,
|
|
domainID: domainID,
|
|
authnRes: smqauthn.Session{DomainUserID: auth.EncodeDomainUserID(domainID, userID), UserID: userID, DomainID: domainID},
|
|
cfg: reportInPast,
|
|
contentType: contentType,
|
|
status: http.StatusBadRequest,
|
|
err: apiutil.ErrValidation,
|
|
},
|
|
{
|
|
desc: "add report config with service error",
|
|
token: validToken,
|
|
domainID: domainID,
|
|
authnRes: smqauthn.Session{DomainUserID: auth.EncodeDomainUserID(domainID, userID), UserID: userID, DomainID: domainID},
|
|
cfg: reportConfig,
|
|
contentType: contentType,
|
|
svcErr: svcerr.ErrCreateEntity,
|
|
status: http.StatusUnprocessableEntity,
|
|
err: svcerr.ErrCreateEntity,
|
|
},
|
|
}
|
|
|
|
for _, tc := range cases {
|
|
t.Run(tc.desc, func(t *testing.T) {
|
|
data := toJSON(tc.cfg)
|
|
req := testRequest{
|
|
client: ts.Client(),
|
|
method: http.MethodPost,
|
|
url: fmt.Sprintf("%s/%s/reports/configs", ts.URL, tc.domainID),
|
|
contentType: tc.contentType,
|
|
token: tc.token,
|
|
body: strings.NewReader(data),
|
|
}
|
|
|
|
authCall := authn.On("Authenticate", mock.Anything, tc.token).Return(tc.authnRes, tc.authnErr)
|
|
svcCall := svc.On("AddReportConfig", mock.Anything, tc.authnRes, mock.Anything).Return(tc.svcRes, tc.svcErr)
|
|
res, err := req.make()
|
|
|
|
assert.Nil(t, err, fmt.Sprintf("%s: unexpected error %s", tc.desc, err))
|
|
var errRes respBody
|
|
err = json.NewDecoder(res.Body).Decode(&errRes)
|
|
assert.Nil(t, err, fmt.Sprintf("%s: unexpected error while decoding response body: %s", tc.desc, err))
|
|
if errRes.Err != "" || errRes.Message != "" {
|
|
err = errors.Wrap(errors.New(errRes.Err), errors.New(errRes.Message))
|
|
}
|
|
assert.True(t, errors.Contains(err, tc.err), fmt.Sprintf("%s: expected %s got %s\n", tc.desc, tc.err, err))
|
|
assert.Equal(t, tc.status, res.StatusCode, fmt.Sprintf("%s: expected status code %d got %d", tc.desc, tc.status, res.StatusCode))
|
|
svcCall.Unset()
|
|
authCall.Unset()
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestViewReportConfigEndpoint(t *testing.T) {
|
|
ts, svc, authn := newReportsServer()
|
|
defer ts.Close()
|
|
|
|
cases := []struct {
|
|
desc string
|
|
id string
|
|
domainID string
|
|
token string
|
|
contentType string
|
|
status int
|
|
authnRes smqauthn.Session
|
|
authnErr error
|
|
svcRes reports.ReportConfig
|
|
svcErr error
|
|
err error
|
|
}{
|
|
{
|
|
desc: "view report config successfully",
|
|
id: validID,
|
|
token: validToken,
|
|
contentType: contentType,
|
|
domainID: domainID,
|
|
authnRes: smqauthn.Session{DomainUserID: auth.EncodeDomainUserID(domainID, userID), UserID: userID, DomainID: domainID},
|
|
status: http.StatusOK,
|
|
svcRes: reportConfig,
|
|
},
|
|
{
|
|
desc: "view report config with invalid token",
|
|
id: validID,
|
|
token: invalidToken,
|
|
authnRes: smqauthn.Session{},
|
|
domainID: domainID,
|
|
contentType: contentType,
|
|
authnErr: svcerr.ErrAuthentication,
|
|
status: http.StatusUnauthorized,
|
|
err: svcerr.ErrAuthentication,
|
|
},
|
|
{
|
|
desc: "view report config with empty token",
|
|
token: "",
|
|
authnRes: smqauthn.Session{},
|
|
domainID: domainID,
|
|
id: validID,
|
|
contentType: contentType,
|
|
status: http.StatusUnauthorized,
|
|
err: apiutil.ErrBearerToken,
|
|
},
|
|
{
|
|
desc: "view report config with empty domainID",
|
|
token: validToken,
|
|
id: validID,
|
|
contentType: contentType,
|
|
status: http.StatusBadRequest,
|
|
err: apiutil.ErrMissingDomainID,
|
|
},
|
|
{
|
|
desc: "view report config with service error",
|
|
token: validToken,
|
|
domainID: domainID,
|
|
authnRes: smqauthn.Session{DomainUserID: auth.EncodeDomainUserID(domainID, userID), UserID: userID, DomainID: domainID},
|
|
id: validID,
|
|
contentType: contentType,
|
|
svcErr: svcerr.ErrAuthorization,
|
|
status: http.StatusForbidden,
|
|
err: svcerr.ErrAuthorization,
|
|
},
|
|
}
|
|
|
|
for _, tc := range cases {
|
|
t.Run(tc.desc, func(t *testing.T) {
|
|
req := testRequest{
|
|
client: ts.Client(),
|
|
method: http.MethodGet,
|
|
url: fmt.Sprintf("%s/%s/reports/configs/%s", ts.URL, tc.domainID, tc.id),
|
|
contentType: tc.contentType,
|
|
token: tc.token,
|
|
}
|
|
|
|
authCall := authn.On("Authenticate", mock.Anything, tc.token).Return(tc.authnRes, tc.authnErr)
|
|
svcCall := svc.On("ViewReportConfig", mock.Anything, tc.authnRes, tc.id, false).Return(tc.svcRes, tc.svcErr)
|
|
res, err := req.make()
|
|
assert.Nil(t, err, fmt.Sprintf("%s: unexpected error %s", tc.desc, err))
|
|
var errRes respBody
|
|
err = json.NewDecoder(res.Body).Decode(&errRes)
|
|
assert.Nil(t, err, fmt.Sprintf("%s: unexpected error while decoding response body: %s", tc.desc, err))
|
|
if errRes.Err != "" || errRes.Message != "" {
|
|
err = errors.Wrap(errors.New(errRes.Err), errors.New(errRes.Message))
|
|
}
|
|
assert.True(t, errors.Contains(err, tc.err), fmt.Sprintf("%s: expected %s got %s\n", tc.desc, tc.err, err))
|
|
assert.Equal(t, tc.status, res.StatusCode, fmt.Sprintf("%s: expected status code %d got %d", tc.desc, tc.status, res.StatusCode))
|
|
svcCall.Unset()
|
|
authCall.Unset()
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestListReportsConfigEndpoint(t *testing.T) {
|
|
ts, svc, authn := newReportsServer()
|
|
defer ts.Close()
|
|
|
|
cases := []struct {
|
|
desc string
|
|
query string
|
|
domainID string
|
|
token string
|
|
session smqauthn.Session
|
|
listReportsResponse reports.ReportConfigPage
|
|
status int
|
|
authnErr error
|
|
err error
|
|
}{
|
|
{
|
|
desc: "list reports config successfully",
|
|
domainID: domainID,
|
|
token: validToken,
|
|
status: http.StatusOK,
|
|
listReportsResponse: reports.ReportConfigPage{
|
|
ReportConfigs: []reports.ReportConfig{reportConfig},
|
|
PageMeta: reports.PageMeta{Total: 1},
|
|
},
|
|
err: nil,
|
|
},
|
|
{
|
|
desc: "list reports config with empty token",
|
|
domainID: domainID,
|
|
token: "",
|
|
status: http.StatusUnauthorized,
|
|
err: apiutil.ErrBearerToken,
|
|
},
|
|
{
|
|
desc: "list reports config with invalid token",
|
|
domainID: domainID,
|
|
token: invalidToken,
|
|
status: http.StatusUnauthorized,
|
|
authnErr: svcerr.ErrAuthentication,
|
|
err: svcerr.ErrAuthentication,
|
|
},
|
|
}
|
|
|
|
for _, tc := range cases {
|
|
t.Run(tc.desc, func(t *testing.T) {
|
|
req := testRequest{
|
|
client: ts.Client(),
|
|
method: http.MethodGet,
|
|
url: ts.URL + "/" + tc.domainID + "/reports/configs?" + tc.query,
|
|
contentType: contentType,
|
|
token: tc.token,
|
|
}
|
|
if tc.token == validToken {
|
|
tc.session = smqauthn.Session{DomainUserID: auth.EncodeDomainUserID(domainID, userID), UserID: userID, DomainID: domainID}
|
|
}
|
|
authCall := authn.On("Authenticate", mock.Anything, tc.token).Return(tc.session, tc.authnErr)
|
|
svcCall := svc.On("ListReportsConfig", mock.Anything, tc.session, mock.Anything).Return(tc.listReportsResponse, tc.err)
|
|
res, err := req.make()
|
|
assert.Nil(t, err, fmt.Sprintf("%s: unexpected error %s", tc.desc, err))
|
|
var bodyRes respBody
|
|
err = json.NewDecoder(res.Body).Decode(&bodyRes)
|
|
assert.Nil(t, err, fmt.Sprintf("%s: unexpected error while decoding response body: %s", tc.desc, err))
|
|
if bodyRes.Err != "" || bodyRes.Message != "" {
|
|
err = errors.Wrap(errors.New(bodyRes.Err), errors.New(bodyRes.Message))
|
|
}
|
|
assert.True(t, errors.Contains(err, tc.err), fmt.Sprintf("%s: expected %s got %s\n", tc.desc, tc.err, err))
|
|
assert.Equal(t, tc.status, res.StatusCode, fmt.Sprintf("%s: expected status code %d got %d", tc.desc, tc.status, res.StatusCode))
|
|
svcCall.Unset()
|
|
authCall.Unset()
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestUpdateReportConfigEndpoint(t *testing.T) {
|
|
ts, svc, authn := newReportsServer()
|
|
defer ts.Close()
|
|
|
|
cases := []struct {
|
|
desc string
|
|
token string
|
|
id string
|
|
domainID string
|
|
updateReq reports.ReportConfig
|
|
contentType string
|
|
session smqauthn.Session
|
|
svcResp reports.ReportConfig
|
|
svcErr error
|
|
status int
|
|
authnErr error
|
|
err error
|
|
}{
|
|
{
|
|
desc: "update report config successfully",
|
|
token: validToken,
|
|
domainID: domainID,
|
|
id: validID,
|
|
updateReq: reportConfig,
|
|
contentType: contentType,
|
|
svcResp: reportConfig,
|
|
status: http.StatusOK,
|
|
err: nil,
|
|
},
|
|
{
|
|
desc: "update report config with invalid token",
|
|
token: invalidToken,
|
|
session: smqauthn.Session{},
|
|
domainID: domainID,
|
|
id: validID,
|
|
updateReq: reportConfig,
|
|
contentType: contentType,
|
|
authnErr: svcerr.ErrAuthentication,
|
|
status: http.StatusUnauthorized,
|
|
err: svcerr.ErrAuthentication,
|
|
},
|
|
{
|
|
desc: "update report config with empty token",
|
|
token: "",
|
|
session: smqauthn.Session{},
|
|
domainID: domainID,
|
|
id: validID,
|
|
updateReq: reportConfig,
|
|
contentType: contentType,
|
|
status: http.StatusUnauthorized,
|
|
err: apiutil.ErrBearerToken,
|
|
},
|
|
{
|
|
desc: "update report config with empty domainID",
|
|
token: validToken,
|
|
id: validID,
|
|
updateReq: reportConfig,
|
|
contentType: contentType,
|
|
status: http.StatusBadRequest,
|
|
err: apiutil.ErrMissingDomainID,
|
|
},
|
|
{
|
|
desc: "update report config with invalid content type",
|
|
token: validToken,
|
|
id: validID,
|
|
domainID: domainID,
|
|
updateReq: reportConfig,
|
|
contentType: "application/xml",
|
|
svcResp: reportConfig,
|
|
status: http.StatusUnsupportedMediaType,
|
|
err: apiutil.ErrUnsupportedContentType,
|
|
},
|
|
{
|
|
desc: "update report config with service error",
|
|
token: validToken,
|
|
id: validID,
|
|
domainID: domainID,
|
|
updateReq: reportConfig,
|
|
contentType: contentType,
|
|
svcResp: reports.ReportConfig{},
|
|
svcErr: svcerr.ErrAuthorization,
|
|
status: http.StatusForbidden,
|
|
err: svcerr.ErrAuthorization,
|
|
},
|
|
}
|
|
|
|
for _, tc := range cases {
|
|
t.Run(tc.desc, func(t *testing.T) {
|
|
data := toJSON(tc.updateReq)
|
|
req := testRequest{
|
|
client: ts.Client(),
|
|
method: http.MethodPatch,
|
|
url: fmt.Sprintf("%s/%s/reports/configs/%s", ts.URL, tc.domainID, tc.id),
|
|
contentType: tc.contentType,
|
|
token: tc.token,
|
|
body: strings.NewReader(data),
|
|
}
|
|
if tc.token == validToken {
|
|
tc.session = smqauthn.Session{DomainUserID: auth.EncodeDomainUserID(domainID, userID), UserID: userID, DomainID: domainID}
|
|
}
|
|
authCall := authn.On("Authenticate", mock.Anything, tc.token).Return(tc.session, tc.authnErr)
|
|
svcCall := svc.On("UpdateReportConfig", mock.Anything, tc.session, mock.Anything).Return(tc.svcResp, tc.svcErr)
|
|
res, err := req.make()
|
|
assert.Nil(t, err, fmt.Sprintf("%s: unexpected error %s", tc.desc, err))
|
|
var errRes respBody
|
|
err = json.NewDecoder(res.Body).Decode(&errRes)
|
|
assert.Nil(t, err, fmt.Sprintf("%s: unexpected error while decoding response body: %s", tc.desc, err))
|
|
if errRes.Err != "" || errRes.Message != "" {
|
|
err = errors.Wrap(errors.New(errRes.Err), errors.New(errRes.Message))
|
|
}
|
|
assert.True(t, errors.Contains(err, tc.err), fmt.Sprintf("%s: expected %s got %s\n", tc.desc, tc.err, err))
|
|
assert.Equal(t, tc.status, res.StatusCode, fmt.Sprintf("%s: expected status code %d got %d", tc.desc, tc.status, res.StatusCode))
|
|
svcCall.Unset()
|
|
authCall.Unset()
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestDeleteReportConfigEndpoint(t *testing.T) {
|
|
ts, svc, authn := newReportsServer()
|
|
defer ts.Close()
|
|
|
|
cases := []struct {
|
|
desc string
|
|
token string
|
|
id string
|
|
domainID string
|
|
session smqauthn.Session
|
|
svcErr error
|
|
status int
|
|
authnErr error
|
|
err error
|
|
}{
|
|
{
|
|
desc: "delete report config successfully",
|
|
token: validToken,
|
|
domainID: domainID,
|
|
id: validID,
|
|
svcErr: nil,
|
|
status: http.StatusNoContent,
|
|
err: nil,
|
|
},
|
|
{
|
|
desc: "delete report config with invalid token",
|
|
token: invalidToken,
|
|
session: smqauthn.Session{},
|
|
domainID: domainID,
|
|
id: validID,
|
|
authnErr: svcerr.ErrAuthentication,
|
|
status: http.StatusUnauthorized,
|
|
err: svcerr.ErrAuthentication,
|
|
},
|
|
{
|
|
desc: "delete report config with empty token",
|
|
token: "",
|
|
session: smqauthn.Session{},
|
|
domainID: domainID,
|
|
id: validID,
|
|
status: http.StatusUnauthorized,
|
|
err: apiutil.ErrBearerToken,
|
|
},
|
|
{
|
|
desc: "delete report config with empty domainID",
|
|
token: validToken,
|
|
id: validID,
|
|
status: http.StatusBadRequest,
|
|
err: apiutil.ErrMissingDomainID,
|
|
},
|
|
{
|
|
desc: "delete report config with service error",
|
|
token: validToken,
|
|
id: validID,
|
|
domainID: domainID,
|
|
svcErr: svcerr.ErrAuthorization,
|
|
status: http.StatusForbidden,
|
|
err: svcerr.ErrAuthorization,
|
|
},
|
|
}
|
|
|
|
for _, tc := range cases {
|
|
t.Run(tc.desc, func(t *testing.T) {
|
|
req := testRequest{
|
|
client: ts.Client(),
|
|
method: http.MethodDelete,
|
|
url: fmt.Sprintf("%s/%s/reports/configs/%s", ts.URL, tc.domainID, tc.id),
|
|
token: tc.token,
|
|
}
|
|
if tc.token == validToken {
|
|
tc.session = smqauthn.Session{DomainUserID: auth.EncodeDomainUserID(domainID, userID), UserID: userID, DomainID: domainID}
|
|
}
|
|
authCall := authn.On("Authenticate", mock.Anything, tc.token).Return(tc.session, tc.authnErr)
|
|
svcCall := svc.On("RemoveReportConfig", mock.Anything, tc.session, tc.id).Return(tc.svcErr)
|
|
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))
|
|
svcCall.Unset()
|
|
authCall.Unset()
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestEnableReportConfigEndpoint(t *testing.T) {
|
|
ts, svc, authn := newReportsServer()
|
|
defer ts.Close()
|
|
|
|
cases := []struct {
|
|
desc string
|
|
token string
|
|
id string
|
|
domainID string
|
|
session smqauthn.Session
|
|
svcResp reports.ReportConfig
|
|
svcErr error
|
|
status int
|
|
authnErr error
|
|
err error
|
|
}{
|
|
{
|
|
desc: "enable report config successfully",
|
|
token: validToken,
|
|
domainID: domainID,
|
|
id: validID,
|
|
svcResp: reportConfig,
|
|
svcErr: nil,
|
|
status: http.StatusOK,
|
|
err: nil,
|
|
},
|
|
{
|
|
desc: "enable report config with invalid token",
|
|
token: invalidToken,
|
|
session: smqauthn.Session{},
|
|
domainID: domainID,
|
|
id: validID,
|
|
authnErr: svcerr.ErrAuthentication,
|
|
status: http.StatusUnauthorized,
|
|
err: svcerr.ErrAuthentication,
|
|
},
|
|
{
|
|
desc: "enable report config with empty token",
|
|
token: "",
|
|
session: smqauthn.Session{},
|
|
domainID: domainID,
|
|
id: validID,
|
|
status: http.StatusUnauthorized,
|
|
err: apiutil.ErrBearerToken,
|
|
},
|
|
{
|
|
desc: "enable report config with empty domainID",
|
|
token: validToken,
|
|
id: validID,
|
|
status: http.StatusBadRequest,
|
|
err: apiutil.ErrMissingDomainID,
|
|
},
|
|
{
|
|
desc: "enable report config with service error",
|
|
token: validToken,
|
|
id: validID,
|
|
domainID: domainID,
|
|
svcResp: reports.ReportConfig{},
|
|
svcErr: svcerr.ErrAuthorization,
|
|
status: http.StatusForbidden,
|
|
err: svcerr.ErrAuthorization,
|
|
},
|
|
{
|
|
desc: "enable report config with empty id",
|
|
token: validToken,
|
|
id: "",
|
|
domainID: domainID,
|
|
status: http.StatusBadRequest,
|
|
err: apiutil.ErrMissingID,
|
|
},
|
|
}
|
|
|
|
for _, tc := range cases {
|
|
t.Run(tc.desc, func(t *testing.T) {
|
|
req := testRequest{
|
|
client: ts.Client(),
|
|
method: http.MethodPost,
|
|
url: fmt.Sprintf("%s/%s/reports/configs/%s/enable", ts.URL, tc.domainID, tc.id),
|
|
token: tc.token,
|
|
}
|
|
if tc.token == validToken {
|
|
tc.session = smqauthn.Session{DomainUserID: auth.EncodeDomainUserID(domainID, userID), UserID: userID, DomainID: domainID}
|
|
}
|
|
authCall := authn.On("Authenticate", mock.Anything, tc.token).Return(tc.session, tc.authnErr)
|
|
svcCall := svc.On("EnableReportConfig", mock.Anything, tc.session, tc.id).Return(tc.svcResp, tc.svcErr)
|
|
res, err := req.make()
|
|
assert.Nil(t, err, fmt.Sprintf("%s: unexpected error %s", tc.desc, err))
|
|
var errRes respBody
|
|
err = json.NewDecoder(res.Body).Decode(&errRes)
|
|
assert.Nil(t, err, fmt.Sprintf("%s: unexpected error while decoding response body: %s", tc.desc, err))
|
|
if errRes.Err != "" || errRes.Message != "" {
|
|
err = errors.Wrap(errors.New(errRes.Err), errors.New(errRes.Message))
|
|
}
|
|
assert.True(t, errors.Contains(err, tc.err), fmt.Sprintf("%s: expected %s got %s\n", tc.desc, tc.err, err))
|
|
assert.Equal(t, tc.status, res.StatusCode, fmt.Sprintf("%s: expected status code %d got %d", tc.desc, tc.status, res.StatusCode))
|
|
svcCall.Unset()
|
|
authCall.Unset()
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestDisableReportConfigEndpoint(t *testing.T) {
|
|
ts, svc, authn := newReportsServer()
|
|
defer ts.Close()
|
|
|
|
cases := []struct {
|
|
desc string
|
|
token string
|
|
id string
|
|
domainID string
|
|
session smqauthn.Session
|
|
svcResp reports.ReportConfig
|
|
svcErr error
|
|
status int
|
|
authnErr error
|
|
err error
|
|
}{
|
|
{
|
|
desc: "disable report config successfully",
|
|
token: validToken,
|
|
domainID: domainID,
|
|
id: validID,
|
|
svcResp: reportConfig,
|
|
svcErr: nil,
|
|
status: http.StatusOK,
|
|
err: nil,
|
|
},
|
|
{
|
|
desc: "disable report config with invalid token",
|
|
token: invalidToken,
|
|
session: smqauthn.Session{},
|
|
domainID: domainID,
|
|
id: validID,
|
|
authnErr: svcerr.ErrAuthentication,
|
|
status: http.StatusUnauthorized,
|
|
err: svcerr.ErrAuthentication,
|
|
},
|
|
{
|
|
desc: "disable report config with empty token",
|
|
token: "",
|
|
session: smqauthn.Session{},
|
|
domainID: domainID,
|
|
id: validID,
|
|
status: http.StatusUnauthorized,
|
|
err: apiutil.ErrBearerToken,
|
|
},
|
|
{
|
|
desc: "disable report config with empty domainID",
|
|
token: validToken,
|
|
id: validID,
|
|
status: http.StatusBadRequest,
|
|
err: apiutil.ErrMissingDomainID,
|
|
},
|
|
{
|
|
desc: "disable report config with service error",
|
|
token: validToken,
|
|
id: validID,
|
|
domainID: domainID,
|
|
svcResp: reports.ReportConfig{},
|
|
svcErr: svcerr.ErrAuthorization,
|
|
status: http.StatusForbidden,
|
|
err: svcerr.ErrAuthorization,
|
|
},
|
|
{
|
|
desc: "disable report config with empty id",
|
|
token: validToken,
|
|
id: "",
|
|
domainID: domainID,
|
|
status: http.StatusBadRequest,
|
|
err: apiutil.ErrMissingID,
|
|
},
|
|
}
|
|
|
|
for _, tc := range cases {
|
|
t.Run(tc.desc, func(t *testing.T) {
|
|
req := testRequest{
|
|
client: ts.Client(),
|
|
method: http.MethodPost,
|
|
url: fmt.Sprintf("%s/%s/reports/configs/%s/disable", ts.URL, tc.domainID, tc.id),
|
|
token: tc.token,
|
|
}
|
|
if tc.token == validToken {
|
|
tc.session = smqauthn.Session{DomainUserID: auth.EncodeDomainUserID(domainID, userID), UserID: userID, DomainID: domainID}
|
|
}
|
|
authCall := authn.On("Authenticate", mock.Anything, tc.token).Return(tc.session, tc.authnErr)
|
|
svcCall := svc.On("DisableReportConfig", mock.Anything, tc.session, tc.id).Return(tc.svcResp, tc.svcErr)
|
|
res, err := req.make()
|
|
assert.Nil(t, err, fmt.Sprintf("%s: unexpected error %s", tc.desc, err))
|
|
var errRes respBody
|
|
err = json.NewDecoder(res.Body).Decode(&errRes)
|
|
assert.Nil(t, err, fmt.Sprintf("%s: unexpected error while decoding response body: %s", tc.desc, err))
|
|
if errRes.Err != "" || errRes.Message != "" {
|
|
err = errors.Wrap(errors.New(errRes.Err), errors.New(errRes.Message))
|
|
}
|
|
assert.True(t, errors.Contains(err, tc.err), fmt.Sprintf("%s: expected %s got %s\n", tc.desc, tc.err, err))
|
|
assert.Equal(t, tc.status, res.StatusCode, fmt.Sprintf("%s: expected status code %d got %d", tc.desc, tc.status, res.StatusCode))
|
|
svcCall.Unset()
|
|
authCall.Unset()
|
|
})
|
|
}
|
|
}
|
|
|
|
type respBody struct {
|
|
Err string `json:"error"`
|
|
Message string `json:"message"`
|
|
Total uint64 `json:"total"`
|
|
ID string `json:"id"`
|
|
Status reports.Status `json:"status"`
|
|
}
|
|
|
|
const (
|
|
validTemplate = `<!DOCTYPE html>
|
|
<html>
|
|
<head>
|
|
<title>{{$.Title}}</title>
|
|
<style>
|
|
body { font-family: Arial, sans-serif; }
|
|
.header { background-color: #f0f0f0; padding: 10px; }
|
|
.content { padding: 20px; }
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<div class="header">
|
|
<h1>{{$.Title}}</h1>
|
|
<p>Generated on: {{$.GeneratedDate}}</p>
|
|
</div>
|
|
<div class="content">
|
|
<h2>Messages</h2>
|
|
{{range .Messages}}
|
|
<div class="message">
|
|
<p>Time: {{formatTime .Time}}</p>
|
|
<p>Value: {{formatValue .}}</p>
|
|
</div>
|
|
{{end}}
|
|
</div>
|
|
</body>
|
|
</html>`
|
|
|
|
templateWithoutTitle = `<!DOCTYPE html>
|
|
<html>
|
|
<head>
|
|
<title>Report</title>
|
|
<style>
|
|
body { font-family: Arial, sans-serif; }
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<h1>Report</h1>
|
|
{{range .Messages}}
|
|
<p>Time: {{formatTime .Time}}</p>
|
|
<p>Value: {{formatValue .}}</p>
|
|
{{end}}
|
|
</body>
|
|
</html>`
|
|
|
|
templateWithSyntaxError = `<!DOCTYPE html>
|
|
<html>
|
|
<head>
|
|
<title>{{$.Title}}</title>
|
|
</head>
|
|
<body>
|
|
<h1>{{$.Title}}</h1>
|
|
{{range .Messages}}
|
|
<p>Time: {{formatTime .Time}}</p>
|
|
<p>Value: {{formatValue .}}</p>
|
|
{{end
|
|
</body>
|
|
</html>`
|
|
)
|
|
|
|
func TestUpdateReportTemplateEndpoint(t *testing.T) {
|
|
ts, svc, authn := newReportsServer()
|
|
defer ts.Close()
|
|
|
|
cases := []struct {
|
|
desc string
|
|
id string
|
|
template reports.ReportTemplate
|
|
domainID string
|
|
token string
|
|
contentType string
|
|
status int
|
|
authnRes smqauthn.Session
|
|
authnErr error
|
|
svcErr error
|
|
err error
|
|
}{
|
|
{
|
|
desc: "update report template successfully",
|
|
id: validID,
|
|
template: reports.ReportTemplate(validTemplate),
|
|
token: validToken,
|
|
contentType: contentType,
|
|
domainID: domainID,
|
|
authnRes: smqauthn.Session{DomainUserID: auth.EncodeDomainUserID(domainID, userID), UserID: userID, DomainID: domainID},
|
|
status: http.StatusNoContent,
|
|
},
|
|
{
|
|
desc: "update report template with invalid token",
|
|
id: validID,
|
|
template: reports.ReportTemplate(validTemplate),
|
|
token: invalidToken,
|
|
authnRes: smqauthn.Session{},
|
|
domainID: domainID,
|
|
contentType: contentType,
|
|
authnErr: svcerr.ErrAuthentication,
|
|
status: http.StatusUnauthorized,
|
|
err: svcerr.ErrAuthentication,
|
|
},
|
|
{
|
|
desc: "update report template with empty token",
|
|
id: validID,
|
|
template: reports.ReportTemplate(validTemplate),
|
|
token: "",
|
|
authnRes: smqauthn.Session{},
|
|
domainID: domainID,
|
|
contentType: contentType,
|
|
status: http.StatusUnauthorized,
|
|
err: apiutil.ErrBearerToken,
|
|
},
|
|
{
|
|
desc: "update report template with empty domainID",
|
|
id: validID,
|
|
template: reports.ReportTemplate(validTemplate),
|
|
token: validToken,
|
|
contentType: contentType,
|
|
status: http.StatusBadRequest,
|
|
err: apiutil.ErrMissingDomainID,
|
|
},
|
|
{
|
|
desc: "update report template with invalid content type",
|
|
id: validID,
|
|
template: reports.ReportTemplate(validTemplate),
|
|
token: validToken,
|
|
domainID: domainID,
|
|
contentType: "application/xml",
|
|
status: http.StatusUnsupportedMediaType,
|
|
err: apiutil.ErrUnsupportedContentType,
|
|
},
|
|
{
|
|
desc: "update report template with empty ID",
|
|
id: "",
|
|
template: reports.ReportTemplate(validTemplate),
|
|
token: validToken,
|
|
domainID: domainID,
|
|
contentType: contentType,
|
|
status: http.StatusBadRequest,
|
|
err: apiutil.ErrMissingID,
|
|
},
|
|
{
|
|
desc: "update report template with empty template",
|
|
id: validID,
|
|
token: validToken,
|
|
domainID: domainID,
|
|
contentType: contentType,
|
|
status: http.StatusBadRequest,
|
|
err: apiutil.ErrValidation,
|
|
},
|
|
{
|
|
desc: "update report template without title field",
|
|
id: validID,
|
|
template: reports.ReportTemplate(templateWithoutTitle),
|
|
token: validToken,
|
|
domainID: domainID,
|
|
contentType: contentType,
|
|
status: http.StatusBadRequest,
|
|
err: apiutil.ErrValidation,
|
|
},
|
|
{
|
|
desc: "update report template with syntax error",
|
|
id: validID,
|
|
template: reports.ReportTemplate(templateWithSyntaxError),
|
|
token: validToken,
|
|
domainID: domainID,
|
|
contentType: contentType,
|
|
status: http.StatusBadRequest,
|
|
err: apiutil.ErrValidation,
|
|
},
|
|
{
|
|
desc: "update report template with service error",
|
|
id: validID,
|
|
template: reports.ReportTemplate(validTemplate),
|
|
token: validToken,
|
|
domainID: domainID,
|
|
authnRes: smqauthn.Session{DomainUserID: auth.EncodeDomainUserID(domainID, userID), UserID: userID, DomainID: domainID},
|
|
contentType: contentType,
|
|
svcErr: svcerr.ErrUpdateEntity,
|
|
status: http.StatusUnprocessableEntity,
|
|
err: svcerr.ErrUpdateEntity,
|
|
},
|
|
}
|
|
|
|
for _, tc := range cases {
|
|
t.Run(tc.desc, func(t *testing.T) {
|
|
data := toJSON(map[string]any{
|
|
"report_template": tc.template,
|
|
})
|
|
req := testRequest{
|
|
client: ts.Client(),
|
|
method: http.MethodPut,
|
|
url: fmt.Sprintf("%s/%s/reports/configs/%s/template", ts.URL, tc.domainID, tc.id),
|
|
contentType: tc.contentType,
|
|
token: tc.token,
|
|
body: strings.NewReader(data),
|
|
}
|
|
|
|
authCall := authn.On("Authenticate", mock.Anything, tc.token).Return(tc.authnRes, tc.authnErr)
|
|
svcCall := svc.On("UpdateReportTemplate", mock.Anything, tc.authnRes, mock.Anything).Return(tc.svcErr)
|
|
res, err := req.make()
|
|
assert.Nil(t, err, fmt.Sprintf("%s: unexpected error %s", tc.desc, err))
|
|
if res.StatusCode != http.StatusNoContent {
|
|
var errRes respBody
|
|
err = json.NewDecoder(res.Body).Decode(&errRes)
|
|
assert.Nil(t, err, fmt.Sprintf("%s: unexpected error while decoding response body: %s", tc.desc, err))
|
|
if errRes.Err != "" || errRes.Message != "" {
|
|
err = errors.Wrap(errors.New(errRes.Err), errors.New(errRes.Message))
|
|
}
|
|
}
|
|
|
|
assert.True(t, errors.Contains(err, tc.err), fmt.Sprintf("%s: expected %s got %s\n", tc.desc, tc.err, err))
|
|
assert.Equal(t, tc.status, res.StatusCode, fmt.Sprintf("%s: expected status code %d got %d", tc.desc, tc.status, res.StatusCode))
|
|
svcCall.Unset()
|
|
authCall.Unset()
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestViewReportTemplateEndpoint(t *testing.T) {
|
|
ts, svc, authn := newReportsServer()
|
|
defer ts.Close()
|
|
|
|
cases := []struct {
|
|
desc string
|
|
id string
|
|
domainID string
|
|
token string
|
|
contentType string
|
|
status int
|
|
authnRes smqauthn.Session
|
|
authnErr error
|
|
svcRes reports.ReportTemplate
|
|
svcErr error
|
|
err error
|
|
}{
|
|
{
|
|
desc: "view report template successfully",
|
|
id: validID,
|
|
token: validToken,
|
|
contentType: contentType,
|
|
domainID: domainID,
|
|
authnRes: smqauthn.Session{DomainUserID: auth.EncodeDomainUserID(domainID, userID), UserID: userID, DomainID: domainID},
|
|
status: http.StatusOK,
|
|
svcRes: reports.ReportTemplate(validTemplate),
|
|
},
|
|
{
|
|
desc: "view report template with invalid token",
|
|
id: validID,
|
|
token: invalidToken,
|
|
authnRes: smqauthn.Session{},
|
|
domainID: domainID,
|
|
contentType: contentType,
|
|
authnErr: svcerr.ErrAuthentication,
|
|
status: http.StatusUnauthorized,
|
|
err: svcerr.ErrAuthentication,
|
|
},
|
|
{
|
|
desc: "view report template with empty token",
|
|
token: "",
|
|
authnRes: smqauthn.Session{},
|
|
domainID: domainID,
|
|
id: validID,
|
|
contentType: contentType,
|
|
status: http.StatusUnauthorized,
|
|
err: apiutil.ErrBearerToken,
|
|
},
|
|
{
|
|
desc: "view report template with empty domainID",
|
|
token: validToken,
|
|
id: validID,
|
|
contentType: contentType,
|
|
status: http.StatusBadRequest,
|
|
err: apiutil.ErrMissingDomainID,
|
|
},
|
|
{
|
|
desc: "view report template with empty ID",
|
|
token: validToken,
|
|
id: "",
|
|
domainID: domainID,
|
|
contentType: contentType,
|
|
status: http.StatusBadRequest,
|
|
err: apiutil.ErrMissingID,
|
|
},
|
|
{
|
|
desc: "view report template with service error",
|
|
token: validToken,
|
|
domainID: domainID,
|
|
authnRes: smqauthn.Session{DomainUserID: auth.EncodeDomainUserID(domainID, userID), UserID: userID, DomainID: domainID},
|
|
id: validID,
|
|
contentType: contentType,
|
|
svcErr: svcerr.ErrViewEntity,
|
|
status: http.StatusUnprocessableEntity,
|
|
err: svcerr.ErrViewEntity,
|
|
},
|
|
}
|
|
|
|
for _, tc := range cases {
|
|
t.Run(tc.desc, func(t *testing.T) {
|
|
req := testRequest{
|
|
client: ts.Client(),
|
|
method: http.MethodGet,
|
|
url: fmt.Sprintf("%s/%s/reports/configs/%s/template", ts.URL, tc.domainID, tc.id),
|
|
contentType: tc.contentType,
|
|
token: tc.token,
|
|
}
|
|
|
|
authCall := authn.On("Authenticate", mock.Anything, tc.token).Return(tc.authnRes, tc.authnErr)
|
|
svcCall := svc.On("ViewReportTemplate", mock.Anything, tc.authnRes, tc.id).Return(tc.svcRes, tc.svcErr)
|
|
res, err := req.make()
|
|
|
|
assert.Nil(t, err, fmt.Sprintf("%s: unexpected error %s", tc.desc, err))
|
|
var errRes respBody
|
|
err = json.NewDecoder(res.Body).Decode(&errRes)
|
|
assert.Nil(t, err, fmt.Sprintf("%s: unexpected error while decoding response body: %s", tc.desc, err))
|
|
if errRes.Err != "" || errRes.Message != "" {
|
|
err = errors.Wrap(errors.New(errRes.Err), errors.New(errRes.Message))
|
|
}
|
|
assert.True(t, errors.Contains(err, tc.err), fmt.Sprintf("%s: expected %s got %s\n", tc.desc, tc.err, err))
|
|
assert.Equal(t, tc.status, res.StatusCode, fmt.Sprintf("%s: expected status code %d got %d", tc.desc, tc.status, res.StatusCode))
|
|
svcCall.Unset()
|
|
authCall.Unset()
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestDeleteReportTemplateEndpoint(t *testing.T) {
|
|
ts, svc, authn := newReportsServer()
|
|
defer ts.Close()
|
|
|
|
cases := []struct {
|
|
desc string
|
|
id string
|
|
domainID string
|
|
token string
|
|
contentType string
|
|
status int
|
|
authnRes smqauthn.Session
|
|
authnErr error
|
|
svcErr error
|
|
err error
|
|
}{
|
|
{
|
|
desc: "delete report template successfully",
|
|
id: validID,
|
|
token: validToken,
|
|
contentType: contentType,
|
|
domainID: domainID,
|
|
authnRes: smqauthn.Session{DomainUserID: auth.EncodeDomainUserID(domainID, userID), UserID: userID, DomainID: domainID},
|
|
status: http.StatusNoContent,
|
|
},
|
|
{
|
|
desc: "delete report template with invalid token",
|
|
id: validID,
|
|
token: invalidToken,
|
|
authnRes: smqauthn.Session{},
|
|
domainID: domainID,
|
|
contentType: contentType,
|
|
authnErr: svcerr.ErrAuthentication,
|
|
status: http.StatusUnauthorized,
|
|
err: svcerr.ErrAuthentication,
|
|
},
|
|
{
|
|
desc: "delete report template with empty token",
|
|
token: "",
|
|
authnRes: smqauthn.Session{},
|
|
domainID: domainID,
|
|
id: validID,
|
|
contentType: contentType,
|
|
status: http.StatusUnauthorized,
|
|
err: apiutil.ErrBearerToken,
|
|
},
|
|
{
|
|
desc: "delete report template with empty domainID",
|
|
token: validToken,
|
|
id: validID,
|
|
contentType: contentType,
|
|
status: http.StatusBadRequest,
|
|
err: apiutil.ErrMissingDomainID,
|
|
},
|
|
{
|
|
desc: "delete report template with empty ID",
|
|
token: validToken,
|
|
id: "",
|
|
domainID: domainID,
|
|
contentType: contentType,
|
|
status: http.StatusBadRequest,
|
|
err: apiutil.ErrMissingID,
|
|
},
|
|
{
|
|
desc: "delete report template with service error",
|
|
token: validToken,
|
|
domainID: domainID,
|
|
authnRes: smqauthn.Session{DomainUserID: auth.EncodeDomainUserID(domainID, userID), UserID: userID, DomainID: domainID},
|
|
id: validID,
|
|
contentType: contentType,
|
|
svcErr: svcerr.ErrRemoveEntity,
|
|
status: http.StatusUnprocessableEntity,
|
|
err: svcerr.ErrRemoveEntity,
|
|
},
|
|
}
|
|
|
|
for _, tc := range cases {
|
|
t.Run(tc.desc, func(t *testing.T) {
|
|
req := testRequest{
|
|
client: ts.Client(),
|
|
method: http.MethodDelete,
|
|
url: fmt.Sprintf("%s/%s/reports/configs/%s/template", ts.URL, tc.domainID, tc.id),
|
|
contentType: tc.contentType,
|
|
token: tc.token,
|
|
}
|
|
|
|
authCall := authn.On("Authenticate", mock.Anything, tc.token).Return(tc.authnRes, tc.authnErr)
|
|
svcCall := svc.On("DeleteReportTemplate", mock.Anything, tc.authnRes, tc.id).Return(tc.svcErr)
|
|
res, err := req.make()
|
|
|
|
assert.Nil(t, err, fmt.Sprintf("%s: unexpected error %s", tc.desc, err))
|
|
|
|
if res.StatusCode != http.StatusNoContent {
|
|
var errRes respBody
|
|
err = json.NewDecoder(res.Body).Decode(&errRes)
|
|
assert.Nil(t, err, fmt.Sprintf("%s: unexpected error while decoding response body: %s", tc.desc, err))
|
|
if errRes.Err != "" || errRes.Message != "" {
|
|
err = errors.Wrap(errors.New(errRes.Err), errors.New(errRes.Message))
|
|
}
|
|
}
|
|
|
|
assert.True(t, errors.Contains(err, tc.err), fmt.Sprintf("%s: expected %s got %s\n", tc.desc, tc.err, err))
|
|
assert.Equal(t, tc.status, res.StatusCode, fmt.Sprintf("%s: expected status code %d got %d", tc.desc, tc.status, res.StatusCode))
|
|
svcCall.Unset()
|
|
authCall.Unset()
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestGenerateReportWithTemplateValidation(t *testing.T) {
|
|
ts, svc, authn := newReportsServer()
|
|
defer ts.Close()
|
|
|
|
cases := []struct {
|
|
desc string
|
|
cfg reports.ReportConfig
|
|
action string
|
|
domainID string
|
|
token string
|
|
contentType string
|
|
status int
|
|
authnRes smqauthn.Session
|
|
authnErr error
|
|
svcRes reports.ReportPage
|
|
svcErr error
|
|
err error
|
|
}{
|
|
{
|
|
desc: "generate report with valid template successfully",
|
|
cfg: reports.ReportConfig{
|
|
ID: validID,
|
|
Name: namegen.Generate(),
|
|
DomainID: domainID,
|
|
Metrics: []reports.ReqMetric{
|
|
{
|
|
ChannelID: "channel1",
|
|
ClientIDs: []string{"client1"},
|
|
Name: "metric_name",
|
|
},
|
|
},
|
|
Config: &reports.MetricConfig{
|
|
From: "now()-1h",
|
|
To: "now()",
|
|
Title: title,
|
|
Aggregation: reports.AggConfig{AggType: reports.AggregationAVG, Interval: "1h"},
|
|
},
|
|
ReportTemplate: reports.ReportTemplate(validTemplate),
|
|
},
|
|
action: "view",
|
|
token: validToken,
|
|
contentType: contentType,
|
|
domainID: domainID,
|
|
authnRes: smqauthn.Session{DomainUserID: auth.EncodeDomainUserID(domainID, userID), UserID: userID, DomainID: domainID},
|
|
status: http.StatusOK,
|
|
svcRes: reports.ReportPage{},
|
|
},
|
|
{
|
|
desc: "generate report with invalid template",
|
|
cfg: reports.ReportConfig{
|
|
ID: validID,
|
|
Name: namegen.Generate(),
|
|
DomainID: domainID,
|
|
Metrics: []reports.ReqMetric{
|
|
{
|
|
ChannelID: "channel1",
|
|
ClientIDs: []string{"client1"},
|
|
Name: "metric_name",
|
|
},
|
|
},
|
|
Config: &reports.MetricConfig{
|
|
From: "now()-1h",
|
|
To: "now()",
|
|
Title: title,
|
|
Aggregation: reports.AggConfig{AggType: reports.AggregationAVG, Interval: "1h"},
|
|
},
|
|
ReportTemplate: reports.ReportTemplate(templateWithoutTitle),
|
|
},
|
|
action: "view",
|
|
token: validToken,
|
|
contentType: contentType,
|
|
domainID: domainID,
|
|
status: http.StatusBadRequest,
|
|
err: apiutil.ErrValidation,
|
|
},
|
|
{
|
|
desc: "generate report with template syntax error",
|
|
cfg: reports.ReportConfig{
|
|
ID: validID,
|
|
Name: namegen.Generate(),
|
|
DomainID: domainID,
|
|
Metrics: []reports.ReqMetric{
|
|
{
|
|
ChannelID: "channel1",
|
|
ClientIDs: []string{"client1"},
|
|
Name: "metric_name",
|
|
},
|
|
},
|
|
Config: &reports.MetricConfig{
|
|
From: "now()-1h",
|
|
To: "now()",
|
|
Title: title,
|
|
Aggregation: reports.AggConfig{AggType: reports.AggregationAVG, Interval: "1h"},
|
|
},
|
|
ReportTemplate: reports.ReportTemplate(templateWithSyntaxError),
|
|
},
|
|
action: "view",
|
|
token: validToken,
|
|
contentType: contentType,
|
|
domainID: domainID,
|
|
status: http.StatusBadRequest,
|
|
err: apiutil.ErrValidation,
|
|
},
|
|
}
|
|
|
|
for _, tc := range cases {
|
|
t.Run(tc.desc, func(t *testing.T) {
|
|
data := toJSON(tc.cfg)
|
|
req := testRequest{
|
|
client: ts.Client(),
|
|
method: http.MethodPost,
|
|
url: fmt.Sprintf("%s/%s/reports?action=%s", ts.URL, tc.domainID, tc.action),
|
|
contentType: tc.contentType,
|
|
token: tc.token,
|
|
body: strings.NewReader(data),
|
|
}
|
|
|
|
authCall := authn.On("Authenticate", mock.Anything, tc.token).Return(tc.authnRes, tc.authnErr)
|
|
svcCall := svc.On("GenerateReport", mock.Anything, tc.authnRes, mock.Anything, mock.Anything).Return(tc.svcRes, tc.svcErr)
|
|
res, err := req.make()
|
|
|
|
assert.Nil(t, err, fmt.Sprintf("%s: unexpected error %s", tc.desc, err))
|
|
var errRes respBody
|
|
err = json.NewDecoder(res.Body).Decode(&errRes)
|
|
assert.Nil(t, err, fmt.Sprintf("%s: unexpected error while decoding response body: %s", tc.desc, err))
|
|
if errRes.Err != "" || errRes.Message != "" {
|
|
err = errors.Wrap(errors.New(errRes.Err), errors.New(errRes.Message))
|
|
}
|
|
assert.True(t, errors.Contains(err, tc.err), fmt.Sprintf("%s: expected %s got %s\n", tc.desc, tc.err, err))
|
|
assert.Equal(t, tc.status, res.StatusCode, fmt.Sprintf("%s: expected status code %d got %d", tc.desc, tc.status, res.StatusCode))
|
|
svcCall.Unset()
|
|
authCall.Unset()
|
|
})
|
|
}
|
|
}
|