Files
team/server/channels/app/migrations_test.go
de986ae735 Add property system app layer architecture (#35157)
* Refactor property system with app layer routing and access control separation

Establish the app layer as the primary entry point for property operations
with intelligent routing based on group type. This architecture separates
access-controlled operations (CPA groups) from standard operations,
improving performance and code clarity.

Architecture Changes:
- App layer now routes operations based on group type:
  - CPA groups -> PropertyAccessService (enforces access control)
  - Non-CPA groups -> PropertyService (direct, no access control)
- PropertyAccessService simplified to handle only CPA operations
- Eliminated redundant group type checks throughout the codebase

* Move access control routing into PropertyService

This change makes the PropertyService the main entrypoint for property
related operations, and adds a routing mechanism to decide if extra
behaviors or checks should run for each operation, in this case, the
property access service logic.

To add specific payloads that pluggable checks and operations may
need, we use the request context. When the request comes from the API,
the endpoints are in charge of adding the caller ID to the payload,
and in the case of the plugin API, on receiving a request, the server
automatically tags the context with the plugin ID so the property
service can react accordingly.

Finally, the new design enforces all these checks migrating the actual
property logic to internal, non-exposed methods, so any caller from
the App layer needs to go through the service checks that decide if
pluggable logic is needed, avoiding any possibility of a bypass.

* Fix i18n

* Fix bad error string

* Added nil guards to property methods

* Add check for multiple group IDs on value operations

* Add nil guard to the plugin checker

* Fix build error

* Update value tests

* Fix linter

* Adds early return when content flaggin a thread with no replies

* Fix mocks

* Clean the state of plugin property tests before each run

* Do not wrap appErr on API response and fix i18n

* Fix create property field test

* Remove the need to cache cpaGroupID as part of the property service

* Split the property.go file into multiple

* Not found group doesn't bypass access control check

* Unexport SetPluginCheckerForTests

* Rename plugin context getter to be more PSA specific

---------

Co-authored-by: Miguel de la Cruz <[email protected]>
2026-03-26 07:54:50 +00:00

59 lines
2.1 KiB
Go

// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
package app
import (
"testing"
"github.com/mattermost/mattermost/server/public/model"
"github.com/stretchr/testify/require"
)
func TestDoSetupContentFlaggingProperties(t *testing.T) {
t.Run("should register property group and fields", func(t *testing.T) {
//we need to call the Setup method and run the full setup instead of
//just creating a new server via NewServer() because the Setup method
//also takes care of using the correct database DSN based on environment,
//settings, setting up the store and initializing services used in store such as property services.
th := Setup(t)
group, appErr := th.App.GetPropertyGroup(th.Context, model.ContentFlaggingGroupName)
require.Nil(t, appErr)
require.NotNil(t, group)
require.Equal(t, model.ContentFlaggingGroupName, group.Name)
propertyFields, appErr := th.App.SearchPropertyFields(th.Context, group.ID, model.PropertyFieldSearchOpts{PerPage: 100})
require.Nil(t, appErr)
require.Len(t, propertyFields, 11)
data, sysErr := th.Store.System().GetByName(contentFlaggingSetupDoneKey)
require.NoError(t, sysErr)
require.Equal(t, "v5", data.Value)
})
t.Run("the migration is idempotent", func(t *testing.T) {
th := Setup(t)
// Now we will remove the migration done key from systems table to allow the data migration to run again
_, err := th.Store.System().PermanentDeleteByName(contentFlaggingSetupDoneKey)
require.NoError(t, err)
// Run the content flagging data migration again
err = th.Server.doSetupContentFlaggingProperties()
require.NoError(t, err)
group, appErr := th.App.GetPropertyGroup(th.Context, model.ContentFlaggingGroupName)
require.Nil(t, appErr)
require.Equal(t, model.ContentFlaggingGroupName, group.Name)
propertyFields, appErr := th.App.SearchPropertyFields(th.Context, group.ID, model.PropertyFieldSearchOpts{PerPage: 100})
require.Nil(t, appErr)
require.Len(t, propertyFields, 11)
data, sysErr := th.Store.System().GetByName(contentFlaggingSetupDoneKey)
require.NoError(t, sysErr)
require.Equal(t, "v5", data.Value)
})
}