3
0

Added integration tests for channels

This commit is contained in:
Denis Arh
2019-09-09 03:18:07 +02:00
parent d04cbbb058
commit ae3d74992e
11 changed files with 599 additions and 55 deletions

View File

@@ -8,7 +8,7 @@ import (
)
type (
Assert func(*http.Response, *http.Request) error
assertFn func(*http.Response, *http.Request) error
StdErrorResponse struct{ Error struct{ Message string } }
)
@@ -44,8 +44,28 @@ func firstErr(ee ...interface{}) error {
return nil
}
// Ensures there are no errors in the response
func NoErrors(rsp *http.Response, _ *http.Request) (err error) {
// AssertNoErrors ensures there are no errors in the response
func AssertNoErrors(rsp *http.Response, _ *http.Request) (err error) {
tmp := StdErrorResponse{}
return firstErr(decodeBody(rsp, &tmp), tmp)
}
// AssertError ensures there are no errors in the response
func AssertError(expectedError string) assertFn {
return func(rsp *http.Response, _ *http.Request) (err error) {
tmp := StdErrorResponse{}
if err = decodeBody(rsp, &tmp); err != nil {
return errors.Errorf("Could not decode body: %v", err)
}
if tmp.Error.Message == "" {
return errors.Errorf("No error, expecting: %v", expectedError)
}
if expectedError != tmp.Error.Message {
return errors.Errorf("Expecting error %v, got: %v", expectedError, tmp.Error.Message)
}
return nil
}
}

View File

@@ -0,0 +1,83 @@
package messaging
import (
"bytes"
"fmt"
"mime/multipart"
"net/http"
"net/url"
"testing"
"time"
"github.com/steinfletcher/apitest"
jsonpath "github.com/steinfletcher/apitest-jsonpath"
"github.com/stretchr/testify/assert"
"github.com/cortezaproject/corteza-server/messaging/types"
"github.com/cortezaproject/corteza-server/tests/helpers"
)
func chAttach(h helper, ch *types.Channel, file []byte) *apitest.Response {
body := &bytes.Buffer{}
writer := multipart.NewWriter(body)
part, err := writer.CreateFormFile("upload", "test.txt")
h.a.NoError(err)
_, err = part.Write(file)
h.a.NoError(err)
h.a.NoError(writer.Close())
return h.testAPI().
Debug().
Post(fmt.Sprintf("/channels/%v/attach", ch.ID)).
Body(body.String()).
ContentType(writer.FormDataContentType()).
Expect(h.t).
Status(http.StatusOK)
}
// Non members should not be able to attach files to non-public channels
func TestChannelAttachNotMember(t *testing.T) {
h := newHelper(t)
ch := h.makePrivateCh()
chAttach(h, ch, []byte("NOPE")).
Assert(helpers.AssertError("messaging.service.NoPermissions")).
End()
}
func TestChannelAttach(t *testing.T) {
h := newHelper(t)
uploadFileContent := "hello corteza, time here is " + time.Now().String()
ch := h.makePublicCh()
h.makeMember(ch, h.cUser)
out := chAttach(h, ch, []byte(uploadFileContent)).
Assert(helpers.AssertNoErrors).
Assert(jsonpath.Present(`$.response.url`)).
Assert(jsonpath.Present(`$.response.previewUrl`)).
End()
// Now, fetch uploaded file and check the content
var rval = struct {
Response struct {
Url string
PreviewUrl string
}
}{}
out.JSON(&rval)
attUrl, err := url.Parse(rval.Response.Url)
assert.NoError(t, err)
h.testAPI().
Get(attUrl.Path).
Query("sign", attUrl.Query().Get("sign")).
Query("userID", attUrl.Query().Get("userID")).
Expect(t).
Status(http.StatusOK).
Body(uploadFileContent).
End()
}

View File

@@ -0,0 +1,42 @@
package messaging
import (
"net/http"
"strconv"
"testing"
jsonpath "github.com/steinfletcher/apitest-jsonpath"
"github.com/cortezaproject/corteza-server/messaging/types"
"github.com/cortezaproject/corteza-server/tests/helpers"
)
func TestChannelCreateDenied(t *testing.T) {
h := newHelper(t)
h.deny(types.MessagingPermissionResource, "channel.public.create")
h.testAPI().
Post("/channels/").
JSON(`{"name":"test channel","type":"public"}`).
Expect(t).
Status(http.StatusOK).
Assert(helpers.AssertError("messaging.service.NoPermissions")).
End()
}
func TestChannelCreate(t *testing.T) {
h := newHelper(t)
h.allow(types.MessagingPermissionResource, "channel.public.create")
h.testAPI().
Post("/channels/").
JSON(`{"name":"test channel","type":"public"}`).
Expect(t).
Status(http.StatusOK).
Assert(helpers.AssertNoErrors).
Assert(jsonpath.Present(`$.response.name`)).
Assert(jsonpath.Present(`$.response.channelID`)).
// Creator should be a member
Assert(jsonpath.Contains(`$.response.members`, strconv.FormatUint(h.cUser.ID, 10))).
End()
}

View File

@@ -0,0 +1,64 @@
package messaging
import (
"fmt"
"net/http"
"testing"
"github.com/steinfletcher/apitest"
"github.com/cortezaproject/corteza-server/messaging/types"
"github.com/cortezaproject/corteza-server/tests/helpers"
)
func TestChannelSetFlag(t *testing.T) {
h := newHelper(t)
ch := h.makePublicCh()
h.makeMember(ch, h.cUser)
flagCheck := func(ID uint64, flag string) {
mm, err := h.repoChMember().Find(&types.ChannelMemberFilter{ChannelID: ID, MemberID: h.cUser.ID})
h.a.NoError(err)
h.a.Len(mm, 1, "expecting 1 member")
h.a.Equal(flag, string(mm[0].Flag), "expecting flags to match")
}
flagChannel := func(ID uint64, flag string) *apitest.Response {
return h.testAPI().
Put(fmt.Sprintf("/channels/%d/flag", ID)).
FormData("flag", flag).
Expect(t).
Status(http.StatusOK)
}
flagChannelOK := func(ID uint64, flag string) {
flagChannel(ID, flag).
Assert(helpers.AssertNoErrors).
End()
flagCheck(ID, flag)
}
unflagChannel := func(ID uint64) {
h.testAPI().
Delete(fmt.Sprintf("/channels/%d/flag", ID)).
Expect(t).
Status(http.StatusOK).
End()
flagCheck(ID, string(types.ChannelMembershipFlagNone))
}
flagCheck(ch.ID, string(types.ChannelMembershipFlagNone))
flagChannelOK(ch.ID, string(types.ChannelMembershipFlagPinned))
flagChannelOK(ch.ID, string(types.ChannelMembershipFlagHidden))
flagChannelOK(ch.ID, string(types.ChannelMembershipFlagIgnored))
flagChannel(ch.ID, "foo").
Assert(helpers.AssertError("invalid flag"))
flagCheck(ch.ID, string(types.ChannelMembershipFlagIgnored))
unflagChannel(ch.ID)
}

View File

@@ -0,0 +1,19 @@
package messaging
import (
"net/http"
"testing"
"github.com/cortezaproject/corteza-server/tests/helpers"
)
func TestChannelList(t *testing.T) {
h := newHelper(t)
h.testAPI().
Get("/channels/").
Expect(t).
Status(http.StatusOK).
Assert(helpers.AssertNoErrors).
End()
}

View File

@@ -0,0 +1,76 @@
package messaging
import (
"fmt"
"net/http"
"testing"
"github.com/titpetric/factory"
"github.com/cortezaproject/corteza-server/messaging/types"
sysType "github.com/cortezaproject/corteza-server/system/types"
"github.com/cortezaproject/corteza-server/tests/helpers"
)
func TestChannelMemberList(t *testing.T) {
h := newHelper(t)
ch := h.makePublicCh()
h.testAPI().
Get(fmt.Sprintf("/channels/%d/members", ch.ID)).
Expect(t).
Status(http.StatusOK).
Assert(helpers.AssertNoErrors).
End()
}
func TestChannelMemberJoinSelf(t *testing.T) {
h := newHelper(t)
ch := h.makePublicCh()
h.testAPI().
Put(fmt.Sprintf("/channels/%d/members/%d", ch.ID, h.cUser.ID)).
Expect(t).
Status(http.StatusOK).
Assert(helpers.AssertNoErrors).
End()
h.chAssertMember(ch, h.cUser, types.ChannelMembershipTypeMember)
}
func TestChannelMemberLeaveSelf(t *testing.T) {
h := newHelper(t)
ch := h.makePublicCh()
h.makeMember(ch, h.cUser)
h.testAPI().
Delete(fmt.Sprintf("/channels/%d/members/%d", ch.ID, h.cUser.ID)).
Expect(t).
Status(http.StatusOK).
Assert(helpers.AssertNoErrors).
End()
h.chAssertNotMember(ch, h.cUser)
}
func TestChannelMemberInvite(t *testing.T) {
t.SkipNow()
h := newHelper(t)
ch := h.makePublicCh()
h.allow(ch.PermissionResource(), "members.manage")
invitee := &sysType.User{ID: factory.Sonyflake.NextID()}
h.testAPI().
Put(fmt.Sprintf("/channels/%d/members/%d", ch.ID, invitee.ID)).
Expect(t).
Status(http.StatusOK).
Assert(helpers.AssertNoErrors).
End()
h.chAssertMember(ch, invitee, types.ChannelMembershipTypeInvitee)
}

View File

@@ -0,0 +1,37 @@
package messaging
import (
"fmt"
"net/http"
"testing"
"github.com/cortezaproject/corteza-server/internal/permissions"
"github.com/cortezaproject/corteza-server/messaging/types"
"github.com/cortezaproject/corteza-server/tests/helpers"
)
func TestChannelAlterState(t *testing.T) {
h := newHelper(t)
ch := h.makePublicCh()
stateUrl := fmt.Sprintf("/channels/%d/state", ch.ID)
testState := func(state string) {
p.ClearGrants()
h.mockPermissionsWithAccess()
h.allow(types.ChannelPermissionResource.AppendWildcard(), permissions.Operation(state))
h.testAPI().
Put(stateUrl).
FormData("state", state).
Expect(t).
Status(http.StatusOK).
Assert(helpers.AssertNoErrors).
End()
}
testState("archive")
testState("unarchive")
testState("delete")
testState("undelete")
}

View File

@@ -0,0 +1,77 @@
package messaging
import (
"context"
"time"
"github.com/titpetric/factory"
"github.com/cortezaproject/corteza-server/messaging/repository"
"github.com/cortezaproject/corteza-server/messaging/types"
sysTypes "github.com/cortezaproject/corteza-server/system/types"
)
func (h helper) repoChannel() repository.ChannelRepository {
var (
ctx = context.Background()
db = factory.Database.MustGet("messaging").With(ctx)
)
return repository.Channel(ctx, db)
}
func (h helper) repoChMember() repository.ChannelMemberRepository {
var (
ctx = context.Background()
db = factory.Database.MustGet("messaging").With(ctx)
)
return repository.ChannelMember(ctx, db)
}
func (h helper) makePublicCh() *types.Channel {
ch, err := h.repoChannel().Create(&types.Channel{
Name: "Test channel " + time.Now().String(),
Type: types.ChannelTypePublic,
})
h.a.NoError(err)
return ch
}
func (h helper) makePrivateCh() *types.Channel {
ch, err := h.repoChannel().Create(&types.Channel{
Name: "Test channel " + time.Now().String(),
Type: types.ChannelTypePrivate,
})
h.a.NoError(err)
return ch
}
func (h helper) makeMember(ch *types.Channel, u *sysTypes.User) *types.ChannelMember {
m, err := h.
repoChMember().
Create(&types.ChannelMember{ChannelID: ch.ID, UserID: h.cUser.ID, Type: types.ChannelMembershipTypeMember})
h.a.NoError(err)
return m
}
func (h helper) chAssertNotMember(ch *types.Channel, u *sysTypes.User) {
mm, err := h.repoChMember().Find(&types.ChannelMemberFilter{ChannelID: ch.ID, MemberID: h.cUser.ID})
h.a.NoError(err)
h.a.NotNil(mm)
h.a.NotContains(mm.AllMemberIDs(), u.ID, "not expecting to find a member")
}
func (h helper) chAssertMember(ch *types.Channel, u *sysTypes.User, typ types.ChannelMembershipType) {
mm, err := h.repoChMember().Find(&types.ChannelMemberFilter{ChannelID: ch.ID, MemberID: h.cUser.ID})
h.a.NoError(err)
h.a.NotNil(mm)
h.a.NotNil(mm.FindByUserID(u.ID), "expecting to find a member")
h.a.Equal(typ, mm.FindByUserID(u.ID).Type, "expecting to find a member")
}

View File

@@ -0,0 +1,84 @@
package messaging
import (
"encoding/json"
"fmt"
"net/http"
"strconv"
"testing"
"github.com/steinfletcher/apitest"
jsonpath "github.com/steinfletcher/apitest-jsonpath"
"github.com/titpetric/factory"
"github.com/cortezaproject/corteza-server/messaging/rest/request"
"github.com/cortezaproject/corteza-server/messaging/types"
"github.com/cortezaproject/corteza-server/tests/helpers"
)
func (h helper) chUpdate(ch *request.ChannelUpdate) *apitest.Response {
payload, err := json.Marshal(ch)
h.a.NoError(err)
return h.testAPI().
Put(fmt.Sprintf("/channels/%v", ch.ChannelID)).
JSON(string(payload)).
Expect(h.t).
Status(http.StatusOK)
}
func channelToRequest(ch *types.Channel) *request.ChannelUpdate {
req := &request.ChannelUpdate{
ChannelID: ch.ID,
Name: ch.Name,
Topic: ch.Topic,
MembershipPolicy: ch.MembershipPolicy,
Type: ch.Type.String(),
}
return req
}
func TestChannelUpdateNonexistent(t *testing.T) {
h := newHelper(t)
req := &request.ChannelUpdate{
ChannelID: factory.Sonyflake.NextID(),
Name: "some name",
}
h.chUpdate(req).
Assert(helpers.AssertError("messaging.repository.ChannelNotFound")).
End()
}
func TestChannelUpdateDenied(t *testing.T) {
h := newHelper(t)
ch := h.makePublicCh()
h.deny(ch.PermissionResource(), "update")
req := channelToRequest(ch)
req.Name = "Updated name"
h.chUpdate(req).
Assert(helpers.AssertError("messaging.service.NoPermissions")).
End()
}
func TestChannelUpdate(t *testing.T) {
h := newHelper(t)
ch := h.makePublicCh()
h.allow(ch.PermissionResource(), "update")
req := channelToRequest(ch)
req.Name = "Updated name"
h.chUpdate(req).
Assert(helpers.AssertNoErrors).
Assert(jsonpath.Equal(`$.response.name`, req.Name)).
Assert(jsonpath.Equal(`$.response.channelID`, strconv.FormatUint(ch.ID, 10))).
End()
}

View File

@@ -1,39 +0,0 @@
package messaging
import (
"net/http"
"testing"
"github.com/cortezaproject/corteza-server/system/types"
"github.com/cortezaproject/corteza-server/tests/helpers"
)
func TestChannelList(t *testing.T) {
NewApiTest("get list of channels", &types.User{ID: 5}).
Get("/channels/").
Expect(t).
Status(http.StatusOK).
Assert(helpers.NoErrors).
End()
}
func TestChannelRead(t *testing.T) {
NewApiTest("find single channel by ID", &types.User{ID: 5}).
Get("/channels/324234").
Expect(t).
Status(http.StatusOK).
Assert(helpers.NoErrors).
End()
}
func TestChannelCreate(t *testing.T) {
t.Skip()
NewApiTest("create channel", &types.User{ID: 5}).
Post("/channels/").
Body(`{"name":"test channel"}`).
Expect(t).
Status(http.StatusOK).
Assert(helpers.NoErrors).
End()
}

View File

@@ -6,27 +6,48 @@ import (
"testing"
_ "github.com/joho/godotenv/autoload"
"github.com/spf13/afero"
"github.com/steinfletcher/apitest"
"github.com/stretchr/testify/require"
"github.com/titpetric/factory"
"github.com/go-chi/chi"
"go.uber.org/zap"
"github.com/steinfletcher/apitest"
"github.com/cortezaproject/corteza-server/internal/auth"
"github.com/cortezaproject/corteza-server/internal/permissions"
"github.com/cortezaproject/corteza-server/internal/rand"
"github.com/cortezaproject/corteza-server/internal/store"
"github.com/cortezaproject/corteza-server/messaging"
"github.com/cortezaproject/corteza-server/messaging/rest"
"github.com/cortezaproject/corteza-server/messaging/service"
"github.com/cortezaproject/corteza-server/messaging/types"
"github.com/cortezaproject/corteza-server/pkg/api"
"github.com/cortezaproject/corteza-server/pkg/cli"
"github.com/cortezaproject/corteza-server/pkg/logger"
"github.com/cortezaproject/corteza-server/system/types"
sysTypes "github.com/cortezaproject/corteza-server/system/types"
"github.com/cortezaproject/corteza-server/tests/helpers"
)
type (
helper struct {
t *testing.T
a *require.Assertions
cUser *sysTypes.User
roleID uint64
}
)
var (
cfg *cli.Config
r chi.Router
p = permissions.NewTestService()
)
func InitConfig() {
var err error
if cfg != nil {
return
}
@@ -41,11 +62,18 @@ func InitConfig() {
cfg.Init()
if err := cfg.RootCommandDBSetup.Run(ctx, nil, cfg); err != nil {
auth.SetupDefault(string(rand.Bytes(32)), 10)
if err = cfg.RootCommandDBSetup.Run(ctx, nil, cfg); err != nil {
panic(err)
}
logger.SetDefault(log)
service.DefaultPermissions = p
if service.DefaultStore, err = store.NewWithAfero(afero.NewMemMapFs(), "test"); err != nil {
panic(err)
}
cfg.InitServices(ctx, cfg)
}
@@ -63,16 +91,69 @@ func InitApp() {
rest.MountRoutes(r)
}
func NewApiTest(name string, user *types.User) *apitest.APITest {
InitApp()
return apitest.
New(name).
Handler(r).
Intercept(helpers.ReqHeaderAuthBearer(user))
}
func TestMain(m *testing.M) {
InitApp()
os.Exit(m.Run())
}
func newHelper(t *testing.T) helper {
h := helper{
t: t,
a: require.New(t),
roleID: factory.Sonyflake.NextID(),
cUser: &sysTypes.User{
ID: factory.Sonyflake.NextID(),
},
}
h.cUser.SetRoles([]uint64{h.roleID})
p.ClearGrants()
// Setup permissions with allowed access to messaging
h.mockPermissions(
permissions.AllowRule(permissions.EveryoneRoleID, types.MessagingPermissionResource, "access"),
)
return h
}
// apitest basics, initialize, set handler, add auth
func (h helper) testAPI() *apitest.APITest {
InitApp()
return apitest.
New().
Handler(r).
Intercept(helpers.ReqHeaderAuthBearer(h.cUser))
}
func (h helper) mockPermissions(rules ...*permissions.Rule) {
h.a.NoError(p.Grant(
// TestService we use does not have any backend storage,
context.Background(),
// We want to make sure we did not make a mistake with any of the mocked resources or actions
service.DefaultAccessControl.Whitelist(),
rules...,
))
}
// Prepends allow access rule for messaging service for everyone
func (h helper) mockPermissionsWithAccess(rules ...*permissions.Rule) {
rules = append(
rules,
permissions.AllowRule(permissions.EveryoneRoleID, types.MessagingPermissionResource, "access"),
)
h.mockPermissions(rules...)
}
// Set allow permision for test role
func (h helper) allow(r permissions.Resource, o permissions.Operation) {
h.mockPermissions(permissions.AllowRule(h.roleID, r, o))
}
// set deny permission for test role
func (h helper) deny(r permissions.Resource, o permissions.Operation) {
h.mockPermissions(permissions.DenyRule(h.roleID, r, o))
}