snapshot of https://github.com/cortezaproject/corteza.git (2024.9.x, 2026-09-18; blobs > 131072B omitted)
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

This commit is contained in:
upstream-mirror
2026-09-18 01:22:23 +05:00
commit 7e385fc126
8391 changed files with 1766236 additions and 0 deletions

View File

@@ -0,0 +1,602 @@
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()
}