Some checks failed
SonarQube / SonarQube (push) Failing after 5m28s
E2e test on main branch push / server and client test (map[name:admin port:8080]) (push) Failing after 3m33s
Server and lib tests / Library test (js) (push) Failing after 1m36s
Server and lib tests / Library test (vue) (push) Successful in 2m24s
Server and lib tests / Web client test (admin) (push) Successful in 2m52s
Server and lib tests / Web client test (compose) (push) Successful in 3m23s
Server and lib tests / Web client test (discovery) (push) Successful in 2m49s
Server and lib tests / Web client test (one) (push) Successful in 2m52s
Server and lib tests / Web client test (privacy) (push) Successful in 2m44s
Server and lib tests / Web client test (reporter) (push) Successful in 2m56s
Server and lib tests / Web client test (workflow) (push) Successful in 3m0s
Server and lib tests / Server tests (push) Failing after 4m55s
E2e test on main branch push / notify-failure (push) Failing after 9s
Server and lib tests / Send matrix testing notification (push) Successful in 2s
Mark stale issues and pull requests / stale (push) Successful in 40s
603 lines
15 KiB
Go
603 lines
15 KiB
Go
package system
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"net/http"
|
|
"strconv"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/cortezaproject/corteza/server/pkg/id"
|
|
"github.com/cortezaproject/corteza/server/store"
|
|
"github.com/cortezaproject/corteza/server/system/service"
|
|
"github.com/cortezaproject/corteza/server/system/types"
|
|
"github.com/cortezaproject/corteza/server/tests/helpers"
|
|
jsonpath "github.com/steinfletcher/apitest-jsonpath"
|
|
)
|
|
|
|
func (h helper) clearNotifications() {
|
|
h.noError(store.TruncateNotifications(context.Background(), service.DefaultStore))
|
|
}
|
|
|
|
func (h helper) makeNotification() *types.Notification {
|
|
return h.notification(h.cUser.ID, types.NotificationKindSimple, false)
|
|
}
|
|
|
|
func (h helper) makeNotificationByUserID(userID uint64) *types.Notification {
|
|
return h.notification(userID, types.NotificationKindSimple, false)
|
|
}
|
|
|
|
func (h helper) makeReadNotification() *types.Notification {
|
|
ntf := h.makeNotification()
|
|
now := time.Now()
|
|
ntf.ReadAt = &now
|
|
h.noError(store.UpdateNotification(context.Background(), service.DefaultStore, ntf))
|
|
return ntf
|
|
}
|
|
|
|
func (h helper) makeDeletedNotification() *types.Notification {
|
|
return h.notification(h.cUser.ID, types.NotificationKindSimple, true)
|
|
}
|
|
|
|
func (h helper) notification(userID uint64, notificationKind types.NotificationKind, deleted bool) *types.Notification {
|
|
ntf := &types.Notification{
|
|
Kind: notificationKind,
|
|
Config: types.NotificationConfig{
|
|
Simple: types.SimpleNotificationConfig{
|
|
Title: "Test notification",
|
|
Description: "This is a test notification",
|
|
},
|
|
},
|
|
Recipient: userID,
|
|
CreatedBy: h.cUser.ID,
|
|
}
|
|
ntf.ID = id.Next()
|
|
ntf.CreatedAt = time.Now()
|
|
if deleted {
|
|
now := time.Now()
|
|
ntf.DeletedAt = &now
|
|
}
|
|
|
|
h.noError(store.CreateNotification(context.Background(), service.DefaultStore, ntf))
|
|
return ntf
|
|
}
|
|
|
|
func (h helper) lookupNotificationByID(ID uint64) *types.Notification {
|
|
ntf, err := store.LookupNotificationByID(context.Background(), service.DefaultStore, ID)
|
|
h.noError(err)
|
|
return ntf
|
|
}
|
|
|
|
func TestNotificationCreate(t *testing.T) {
|
|
h := newHelper(t)
|
|
h.clearNotifications()
|
|
|
|
ntfTemplate := h.makeNotification()
|
|
|
|
h.apiInit().
|
|
Post("/notification/").
|
|
Header("Accept", "application/json").
|
|
Header("Content-Type", "application/json").
|
|
JSON(map[string]interface{}{
|
|
"kind": string(ntfTemplate.Kind),
|
|
"config": ntfTemplate.Config,
|
|
"recipient": strconv.FormatUint(ntfTemplate.Recipient, 10),
|
|
}).
|
|
Expect(t).
|
|
Status(http.StatusOK).
|
|
Assert(helpers.AssertNoErrors).
|
|
Assert(jsonpath.Equal(`$.response.kind`, string(ntfTemplate.Kind))).
|
|
Assert(jsonpath.Equal(`$.response.config.simple.title`, ntfTemplate.Config.Simple.Title)).
|
|
Assert(jsonpath.Equal(`$.response.config.simple.description`, ntfTemplate.Config.Simple.Description)).
|
|
End()
|
|
}
|
|
|
|
func TestNotificationRead(t *testing.T) {
|
|
h := newHelper(t)
|
|
h.clearNotifications()
|
|
|
|
ntf := h.makeNotification()
|
|
|
|
h.apiInit().
|
|
Get(fmt.Sprintf("/notification/%d", ntf.ID)).
|
|
Expect(t).
|
|
Status(http.StatusOK).
|
|
Assert(helpers.AssertNoErrors).
|
|
Assert(jsonpath.Equal(`$.response.kind`, string(ntf.Kind))).
|
|
Assert(jsonpath.Equal(`$.response.config.simple.title`, ntf.Config.Simple.Title)).
|
|
End()
|
|
}
|
|
|
|
func TestNotificationReadForbidden(t *testing.T) {
|
|
h := newHelper(t)
|
|
h.clearNotifications()
|
|
|
|
ntf := h.makeNotificationByUserID(id.Next())
|
|
|
|
h.apiInit().
|
|
Get(fmt.Sprintf("/notification/%d", ntf.ID)).
|
|
Header("Accept", "application/json").
|
|
Expect(t).
|
|
Status(http.StatusOK).
|
|
Assert(helpers.AssertError("notification.errors.notFound")).
|
|
End()
|
|
}
|
|
|
|
func TestNotificationList(t *testing.T) {
|
|
h := newHelper(t)
|
|
h.clearNotifications()
|
|
|
|
// Create 3 notifications: 2 for current user (1 read, 1 unread) and 1 for another user
|
|
h.makeNotification()
|
|
h.makeReadNotification()
|
|
h.makeNotificationByUserID(id.Next())
|
|
|
|
h.apiInit().
|
|
Get("/notification/").
|
|
Expect(t).
|
|
Status(http.StatusOK).
|
|
Assert(helpers.AssertNoErrors).
|
|
Assert(jsonpath.Len("$.response.set", 1)).
|
|
End()
|
|
}
|
|
|
|
func TestNotificationListUnreadOnly(t *testing.T) {
|
|
h := newHelper(t)
|
|
h.clearNotifications()
|
|
|
|
// Create one unread notification
|
|
h.makeNotification()
|
|
|
|
// Create one read notification
|
|
h.makeReadNotification()
|
|
|
|
// Test with read=0 which should return only unread notifications
|
|
h.apiInit().
|
|
Get("/notification/").
|
|
Query("read", "0").
|
|
Expect(t).
|
|
Status(http.StatusOK).
|
|
Assert(helpers.AssertNoErrors).
|
|
Assert(jsonpath.Len("$.response.set", 1)).
|
|
End()
|
|
}
|
|
|
|
func TestNotificationListWithRead(t *testing.T) {
|
|
h := newHelper(t)
|
|
h.clearNotifications()
|
|
|
|
// Create one unread notification
|
|
h.makeNotification()
|
|
|
|
// Create one read notification
|
|
h.makeReadNotification()
|
|
|
|
// Test with read=1 which should return both read and unread notifications
|
|
h.apiInit().
|
|
Get("/notification/").
|
|
Query("read", "1").
|
|
Expect(t).
|
|
Status(http.StatusOK).
|
|
Assert(helpers.AssertNoErrors).
|
|
Assert(jsonpath.Len("$.response.set", 2)).
|
|
End()
|
|
}
|
|
|
|
func TestNotificationListReadOnly(t *testing.T) {
|
|
h := newHelper(t)
|
|
h.clearNotifications()
|
|
|
|
// Create one unread notification
|
|
h.makeNotification()
|
|
|
|
// Create one read notification
|
|
h.makeReadNotification()
|
|
|
|
// Test with read=2 which should return only read notifications
|
|
h.apiInit().
|
|
Get("/notification/").
|
|
Query("read", "2").
|
|
Expect(t).
|
|
Status(http.StatusOK).
|
|
Assert(helpers.AssertNoErrors).
|
|
Assert(jsonpath.Len("$.response.set", 1)).
|
|
End()
|
|
}
|
|
|
|
func TestNotificationListNoDeleted(t *testing.T) {
|
|
h := newHelper(t)
|
|
h.clearNotifications()
|
|
|
|
h.makeNotification()
|
|
h.makeDeletedNotification()
|
|
|
|
h.apiInit().
|
|
Get("/notification/").
|
|
Query("deleted", "0").
|
|
Expect(t).
|
|
Status(http.StatusOK).
|
|
Assert(helpers.AssertNoErrors).
|
|
Assert(jsonpath.Len("$.response.set", 1)).
|
|
End()
|
|
}
|
|
|
|
func TestNotificationListWithDeleted(t *testing.T) {
|
|
h := newHelper(t)
|
|
h.clearNotifications()
|
|
|
|
h.makeNotification()
|
|
h.makeDeletedNotification()
|
|
|
|
h.apiInit().
|
|
Get("/notification/").
|
|
Query("deleted", "1").
|
|
Expect(t).
|
|
Status(http.StatusOK).
|
|
Assert(helpers.AssertNoErrors).
|
|
Assert(jsonpath.Len("$.response.set", 2)).
|
|
End()
|
|
}
|
|
|
|
func TestNotificationListDeletedOnly(t *testing.T) {
|
|
h := newHelper(t)
|
|
h.clearNotifications()
|
|
|
|
h.makeNotification()
|
|
h.makeDeletedNotification()
|
|
|
|
h.apiInit().
|
|
Get("/notification/").
|
|
Query("deleted", "2").
|
|
Expect(t).
|
|
Status(http.StatusOK).
|
|
Assert(helpers.AssertNoErrors).
|
|
Assert(jsonpath.Len("$.response.set", 1)).
|
|
End()
|
|
}
|
|
|
|
func TestNotificationUpdate(t *testing.T) {
|
|
h := newHelper(t)
|
|
h.clearNotifications()
|
|
|
|
ntf := h.makeNotification()
|
|
|
|
// Create update data
|
|
updatedConfig := types.NotificationConfig{
|
|
Simple: types.SimpleNotificationConfig{
|
|
Title: "Updated Title",
|
|
Description: "Updated Description",
|
|
},
|
|
}
|
|
|
|
h.apiInit().
|
|
Put(fmt.Sprintf("/notification/%d", ntf.ID)).
|
|
Header("Accept", "application/json").
|
|
Header("Content-Type", "application/json").
|
|
JSON(map[string]interface{}{
|
|
"kind": string(ntf.Kind),
|
|
"config": updatedConfig,
|
|
}).
|
|
Expect(t).
|
|
Status(http.StatusOK).
|
|
Assert(helpers.AssertNoErrors).
|
|
Assert(jsonpath.Equal(`$.response.config.simple.title`, updatedConfig.Simple.Title)).
|
|
Assert(jsonpath.Equal(`$.response.config.simple.description`, updatedConfig.Simple.Description)).
|
|
End()
|
|
}
|
|
|
|
func TestNotificationUpdateForbidden(t *testing.T) {
|
|
h := newHelper(t)
|
|
h.clearNotifications()
|
|
|
|
ntf := h.makeNotificationByUserID(id.Next())
|
|
|
|
// Create update data
|
|
updatedConfig := types.NotificationConfig{
|
|
Simple: types.SimpleNotificationConfig{
|
|
Title: "Updated Title",
|
|
Description: "Updated Description",
|
|
},
|
|
}
|
|
|
|
h.apiInit().
|
|
Put(fmt.Sprintf("/notification/%d", ntf.ID)).
|
|
Header("Accept", "application/json").
|
|
Header("Content-Type", "application/json").
|
|
JSON(map[string]interface{}{
|
|
"kind": string(ntf.Kind),
|
|
"config": updatedConfig,
|
|
}).
|
|
Expect(t).
|
|
Status(http.StatusOK).
|
|
Assert(helpers.AssertError("notification.errors.notFound")).
|
|
End()
|
|
}
|
|
|
|
func TestNotificationDelete(t *testing.T) {
|
|
h := newHelper(t)
|
|
h.clearNotifications()
|
|
|
|
ntf := h.makeNotification()
|
|
|
|
h.apiInit().
|
|
Delete(fmt.Sprintf("/notification/%d", ntf.ID)).
|
|
Header("Accept", "application/json").
|
|
Expect(t).
|
|
Status(http.StatusOK).
|
|
Assert(helpers.AssertNoErrors).
|
|
End()
|
|
|
|
// Verify notification is marked as deleted
|
|
deletedNtf := h.lookupNotificationByID(ntf.ID)
|
|
if deletedNtf.DeletedAt == nil {
|
|
t.Error("notification should be marked as deleted")
|
|
}
|
|
}
|
|
|
|
func TestNotificationDeleteForbidden(t *testing.T) {
|
|
h := newHelper(t)
|
|
h.clearNotifications()
|
|
|
|
ntf := h.makeNotificationByUserID(id.Next())
|
|
|
|
h.apiInit().
|
|
Delete(fmt.Sprintf("/notification/%d", ntf.ID)).
|
|
Header("Accept", "application/json").
|
|
Expect(t).
|
|
Status(http.StatusOK).
|
|
Assert(helpers.AssertError("notification.errors.notFound")).
|
|
End()
|
|
}
|
|
|
|
func TestNotificationMarkAsRead(t *testing.T) {
|
|
h := newHelper(t)
|
|
h.clearNotifications()
|
|
|
|
ntf := h.makeNotification()
|
|
|
|
h.apiInit().
|
|
Patch(fmt.Sprintf("/notification/%d/read", ntf.ID)).
|
|
Header("Accept", "application/json").
|
|
Expect(t).
|
|
Status(http.StatusOK).
|
|
Assert(helpers.AssertNoErrors).
|
|
End()
|
|
|
|
// Verify notification is marked as read
|
|
readNtf := h.lookupNotificationByID(ntf.ID)
|
|
if readNtf.ReadAt == nil {
|
|
t.Error("notification should be marked as read")
|
|
}
|
|
}
|
|
|
|
func TestNotificationMarkAsReadForbidden(t *testing.T) {
|
|
h := newHelper(t)
|
|
h.clearNotifications()
|
|
|
|
ntf := h.makeNotificationByUserID(id.Next())
|
|
|
|
h.apiInit().
|
|
Patch(fmt.Sprintf("/notification/%d/read", ntf.ID)).
|
|
Header("Accept", "application/json").
|
|
Expect(t).
|
|
Status(http.StatusOK).
|
|
Assert(helpers.AssertError("notification.errors.notAllowedToRead")).
|
|
End()
|
|
}
|
|
|
|
func TestNotificationMarkAsUnread(t *testing.T) {
|
|
h := newHelper(t)
|
|
h.clearNotifications()
|
|
|
|
ntf := h.makeNotification()
|
|
|
|
// Mark as read first so we can verify unread transition
|
|
h.apiInit().
|
|
Patch(fmt.Sprintf("/notification/%d/read", ntf.ID)).
|
|
Header("Accept", "application/json").
|
|
Expect(t).
|
|
Status(http.StatusOK).
|
|
Assert(helpers.AssertNoErrors).
|
|
End()
|
|
|
|
h.apiInit().
|
|
Patch(fmt.Sprintf("/notification/%d/unread", ntf.ID)).
|
|
Header("Accept", "application/json").
|
|
Expect(t).
|
|
Status(http.StatusOK).
|
|
Assert(helpers.AssertNoErrors).
|
|
End()
|
|
|
|
// Verify notification is marked as unread
|
|
unreadNtf := h.lookupNotificationByID(ntf.ID)
|
|
if unreadNtf.ReadAt != nil {
|
|
t.Error("notification should be marked as unread")
|
|
}
|
|
}
|
|
|
|
func TestNotificationMarkAsUnreadForbidden(t *testing.T) {
|
|
h := newHelper(t)
|
|
h.clearNotifications()
|
|
|
|
ntf := h.makeNotificationByUserID(id.Next())
|
|
|
|
h.apiInit().
|
|
Patch(fmt.Sprintf("/notification/%d/unread", ntf.ID)).
|
|
Header("Accept", "application/json").
|
|
Expect(t).
|
|
Status(http.StatusOK).
|
|
Assert(helpers.AssertError("notification.errors.notAllowedToRead")).
|
|
End()
|
|
}
|
|
|
|
func TestNotificationMarkAllAsRead(t *testing.T) {
|
|
h := newHelper(t)
|
|
h.clearNotifications()
|
|
|
|
// Create multiple unread notifications
|
|
h.makeNotification()
|
|
h.makeNotification()
|
|
h.makeNotification()
|
|
|
|
h.apiInit().
|
|
Patch("/notification/all/read").
|
|
Header("Accept", "application/json").
|
|
Expect(t).
|
|
Status(http.StatusOK).
|
|
Assert(helpers.AssertNoErrors).
|
|
End()
|
|
|
|
// List read notifications and verify all are marked as read
|
|
h.apiInit().
|
|
Get("/notification/").
|
|
Query("read", "2").
|
|
Expect(t).
|
|
Status(http.StatusOK).
|
|
Assert(helpers.AssertNoErrors).
|
|
Assert(jsonpath.Len("$.response.set", 3)).
|
|
End()
|
|
}
|
|
|
|
func TestNotificationMarkAllAsUnread(t *testing.T) {
|
|
h := newHelper(t)
|
|
h.clearNotifications()
|
|
|
|
// Create multiple notifications and mark them as read
|
|
n1 := h.makeNotification()
|
|
n2 := h.makeNotification()
|
|
n3 := h.makeNotification()
|
|
|
|
h.apiInit().
|
|
Patch("/notification/all/read").
|
|
Header("Accept", "application/json").
|
|
Expect(t).
|
|
Status(http.StatusOK).
|
|
Assert(helpers.AssertNoErrors).
|
|
End()
|
|
|
|
// Now mark all as unread
|
|
h.apiInit().
|
|
Patch("/notification/all/unread").
|
|
Header("Accept", "application/json").
|
|
Expect(t).
|
|
Status(http.StatusOK).
|
|
Assert(helpers.AssertNoErrors).
|
|
End()
|
|
|
|
// Verify notifications are marked as unread
|
|
for _, n := range []uint64{n1.ID, n2.ID, n3.ID} {
|
|
ntf := h.lookupNotificationByID(n)
|
|
if ntf.ReadAt != nil {
|
|
t.Errorf("notification %d should be marked as unread", n)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestNotificationAssign_forbidden(t *testing.T) {
|
|
h := newHelper(t)
|
|
h.clearNotifications()
|
|
|
|
// Try to create a notification for another user without the proper permission
|
|
h.apiInit().
|
|
Post("/notification/").
|
|
Header("Accept", "application/json").
|
|
Header("Content-Type", "application/json").
|
|
JSON(map[string]interface{}{
|
|
"kind": string(types.NotificationKindSimple),
|
|
"config": types.NotificationConfig{
|
|
Simple: types.SimpleNotificationConfig{
|
|
Title: "Test notification",
|
|
Description: "This is a test notification",
|
|
},
|
|
},
|
|
"recipient": strconv.FormatUint(id.Next(), 10),
|
|
}).
|
|
Expect(t).
|
|
Status(http.StatusOK).
|
|
Assert(helpers.AssertError("notification.errors.notAllowedToAssign")).
|
|
End()
|
|
}
|
|
|
|
func TestNotificationAssign(t *testing.T) {
|
|
h := newHelper(t)
|
|
h.clearNotifications()
|
|
|
|
// Allow current user to assign notifications
|
|
helpers.AllowMe(h, types.ComponentRbacResource(), "notification.assign")
|
|
|
|
otherUserID := id.Next()
|
|
|
|
// Create a notification for another user with the permission
|
|
h.apiInit().
|
|
Post("/notification/").
|
|
Header("Accept", "application/json").
|
|
Header("Content-Type", "application/json").
|
|
JSON(map[string]interface{}{
|
|
"kind": string(types.NotificationKindSimple),
|
|
"config": types.NotificationConfig{
|
|
Simple: types.SimpleNotificationConfig{
|
|
Title: "Test notification",
|
|
Description: "This is a test notification",
|
|
},
|
|
},
|
|
"recipient": strconv.FormatUint(otherUserID, 10),
|
|
}).
|
|
Expect(t).
|
|
Status(http.StatusOK).
|
|
Assert(helpers.AssertNoErrors).
|
|
Assert(jsonpath.Equal(`$.response.kind`, string(types.NotificationKindSimple))).
|
|
Assert(jsonpath.Equal(`$.response.recipient`, strconv.FormatUint(otherUserID, 10))).
|
|
End()
|
|
}
|
|
|
|
func TestNotificationUpdateRecipient_forbidden(t *testing.T) {
|
|
h := newHelper(t)
|
|
h.clearNotifications()
|
|
|
|
ntf := h.makeNotification()
|
|
otherUserID := id.Next()
|
|
|
|
// Try to update a notification with a different recipient without permission
|
|
h.apiInit().
|
|
Put(fmt.Sprintf("/notification/%d", ntf.ID)).
|
|
Header("Accept", "application/json").
|
|
Header("Content-Type", "application/json").
|
|
JSON(map[string]interface{}{
|
|
"kind": string(ntf.Kind),
|
|
"config": ntf.Config,
|
|
"recipient": strconv.FormatUint(otherUserID, 10),
|
|
}).
|
|
Expect(t).
|
|
Status(http.StatusOK).
|
|
Assert(helpers.AssertError("notification.errors.notAllowedToAssign")).
|
|
End()
|
|
}
|
|
|
|
func TestNotificationUpdateRecipient(t *testing.T) {
|
|
h := newHelper(t)
|
|
h.clearNotifications()
|
|
|
|
// Allow current user to assign notifications
|
|
helpers.AllowMe(h, types.ComponentRbacResource(), "notification.assign")
|
|
|
|
ntf := h.makeNotification()
|
|
otherUserID := id.Next()
|
|
|
|
// Update a notification with a different recipient with permission
|
|
h.apiInit().
|
|
Put(fmt.Sprintf("/notification/%d", ntf.ID)).
|
|
Header("Accept", "application/json").
|
|
Header("Content-Type", "application/json").
|
|
JSON(map[string]interface{}{
|
|
"kind": string(ntf.Kind),
|
|
"config": ntf.Config,
|
|
"recipient": strconv.FormatUint(otherUserID, 10),
|
|
}).
|
|
Expect(t).
|
|
Status(http.StatusOK).
|
|
Assert(helpers.AssertNoErrors).
|
|
Assert(jsonpath.Equal(`$.response.recipient`, strconv.FormatUint(otherUserID, 10))).
|
|
End()
|
|
}
|