Files
team/server/channels/app/slashcommands/command_mobile_logs_test.go
602b8ed226 [MM-67880] Add /mobile-logs slash command (#35658)
* [MM-67880] Add /mobile-logs slash command with E2E tests

Add a new /mobile-logs slash command that allows users to manage the
attach_app_logs preference for themselves or other users (admin-only).
Includes unit tests for all code paths and Playwright E2E tests covering
self-management, admin cross-user management, permission denial, and
error handling.

* Fix i18n error key suffixes and format E2E tests

Rename error i18n keys to use .app_error suffix matching upstream
convention (no_permission, update_error, user_not_found). Run prettier
on the E2E test file to fix formatting.

* Fix E2E test type error: use getUserPreferences instead of getMyPreferences

The getAttachLogsPreference helper was using getMyPreferences() which returns
PreferenceType (not an array), causing TS2345 errors. Switch to
getUserPreferences(userId) which returns the expected array type.

* [MM-67880] Move unreachable usage fallback into switch default case

The return after the switch was unreachable because action is validated
earlier to be "on", "off", or "status". Move it into an explicit default
case with a defensive comment.

* allow up to 2 arguments

* Add audit logging for mobile logs slash command actions

Implement a new function to log audit records when users enable or disable the attach_app_logs preference via the /mobile-logs command. This includes capturing relevant metadata such as user IDs, session information, and the action taken. The logging occurs in both the enable and disable command paths, enhancing traceability and accountability for user preference changes.

* Enhance mobile logs command to handle cross-user permission checks

Add a new response function for cases where a regular user attempts to access mobile log settings for another user, ensuring they receive a neutral error message instead of specific user information. Update the command logic to incorporate this response for both nonexistent users and deactivated accounts. Additionally, modify related tests and internationalization keys to reflect these changes, improving security and user experience.

* Update E2E test for /mobile-logs command to verify permission denial for nonexistent users

Enhance the existing E2E test for the /mobile-logs command by adding assertions to check that users receive a permission denial message when attempting to change mobile log settings for a nonexistent user. This improves test coverage and ensures proper error handling in the application.

* update i18n strings

---------

Co-authored-by: Mattermost Build <[email protected]>

Co-authored-by: Hanzo Dev <[email protected]>
2026-04-20 12:46:36 +02:00

285 lines
9.8 KiB
Go

// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
package slashcommands
import (
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/mattermost/mattermost/server/public/model"
"github.com/mattermost/mattermost/server/public/shared/i18n"
)
func TestMobileLogsGetCommand(t *testing.T) {
th := setup(t).initBasic(t)
cmd := &MobileLogsProvider{}
result := cmd.GetCommand(th.App, i18n.IdentityTfunc())
assert.Equal(t, CmdMobileLogs, result.Trigger)
assert.True(t, result.AutoComplete)
assert.NotEmpty(t, result.AutoCompleteDesc)
assert.NotEmpty(t, result.AutoCompleteHint)
assert.NotEmpty(t, result.DisplayName)
}
func TestMobileLogsOnSelf(t *testing.T) {
th := setup(t).initBasic(t)
cmd := &MobileLogsProvider{}
resp := cmd.DoCommand(th.App, th.Context, &model.CommandArgs{
T: i18n.IdentityTfunc(),
UserId: th.BasicUser.Id,
}, "on")
assert.Equal(t, model.CommandResponseTypeEphemeral, resp.ResponseType)
assert.Equal(t, "api.command_mobile_logs.enabled", resp.Text)
pref, err := th.App.GetPreferenceByCategoryAndNameForUser(th.Context, th.BasicUser.Id, model.PreferenceCategoryAdvancedSettings, model.PreferenceNameAttachAppLogs)
require.Nil(t, err)
assert.Equal(t, "true", pref.Value)
}
func TestMobileLogsOffSelf(t *testing.T) {
th := setup(t).initBasic(t)
cmd := &MobileLogsProvider{}
cmd.DoCommand(th.App, th.Context, &model.CommandArgs{
T: i18n.IdentityTfunc(),
UserId: th.BasicUser.Id,
}, "on")
resp := cmd.DoCommand(th.App, th.Context, &model.CommandArgs{
T: i18n.IdentityTfunc(),
UserId: th.BasicUser.Id,
}, "off")
assert.Equal(t, model.CommandResponseTypeEphemeral, resp.ResponseType)
assert.Equal(t, "api.command_mobile_logs.disabled", resp.Text)
pref, err := th.App.GetPreferenceByCategoryAndNameForUser(th.Context, th.BasicUser.Id, model.PreferenceCategoryAdvancedSettings, model.PreferenceNameAttachAppLogs)
require.Nil(t, err)
assert.Equal(t, "false", pref.Value)
}
func TestMobileLogsStatusDefault(t *testing.T) {
th := setup(t).initBasic(t)
cmd := &MobileLogsProvider{}
resp := cmd.DoCommand(th.App, th.Context, &model.CommandArgs{
T: i18n.IdentityTfunc(),
UserId: th.BasicUser.Id,
}, "status")
assert.Equal(t, model.CommandResponseTypeEphemeral, resp.ResponseType)
assert.Equal(t, "api.command_mobile_logs.status_off", resp.Text)
}
func TestMobileLogsStatusOn(t *testing.T) {
th := setup(t).initBasic(t)
cmd := &MobileLogsProvider{}
cmd.DoCommand(th.App, th.Context, &model.CommandArgs{
T: i18n.IdentityTfunc(),
UserId: th.BasicUser.Id,
}, "on")
resp := cmd.DoCommand(th.App, th.Context, &model.CommandArgs{
T: i18n.IdentityTfunc(),
UserId: th.BasicUser.Id,
}, "status")
assert.Equal(t, model.CommandResponseTypeEphemeral, resp.ResponseType)
assert.Equal(t, "api.command_mobile_logs.status_on", resp.Text)
}
func TestMobileLogsOnOtherUserWithPermission(t *testing.T) {
th := setup(t).initBasic(t)
cmd := &MobileLogsProvider{}
resp := cmd.DoCommand(th.App, th.Context, &model.CommandArgs{
T: i18n.IdentityTfunc(),
UserId: th.SystemAdminUser.Id,
}, "on @"+th.BasicUser.Username)
assert.Equal(t, model.CommandResponseTypeEphemeral, resp.ResponseType)
assert.Equal(t, "api.command_mobile_logs.enabled", resp.Text)
pref, err := th.App.GetPreferenceByCategoryAndNameForUser(th.Context, th.BasicUser.Id, model.PreferenceCategoryAdvancedSettings, model.PreferenceNameAttachAppLogs)
require.Nil(t, err)
assert.Equal(t, "true", pref.Value)
}
func TestMobileLogsOffOtherUserWithPermission(t *testing.T) {
th := setup(t).initBasic(t)
cmd := &MobileLogsProvider{}
cmd.DoCommand(th.App, th.Context, &model.CommandArgs{
T: i18n.IdentityTfunc(),
UserId: th.SystemAdminUser.Id,
}, "on @"+th.BasicUser.Username)
resp := cmd.DoCommand(th.App, th.Context, &model.CommandArgs{
T: i18n.IdentityTfunc(),
UserId: th.SystemAdminUser.Id,
}, "off @"+th.BasicUser.Username)
assert.Equal(t, model.CommandResponseTypeEphemeral, resp.ResponseType)
assert.Equal(t, "api.command_mobile_logs.disabled", resp.Text)
pref, err := th.App.GetPreferenceByCategoryAndNameForUser(th.Context, th.BasicUser.Id, model.PreferenceCategoryAdvancedSettings, model.PreferenceNameAttachAppLogs)
require.Nil(t, err)
assert.Equal(t, "false", pref.Value)
}
func TestMobileLogsStatusOtherUser(t *testing.T) {
th := setup(t).initBasic(t)
cmd := &MobileLogsProvider{}
// Default status for other user
resp := cmd.DoCommand(th.App, th.Context, &model.CommandArgs{
T: i18n.IdentityTfunc(),
UserId: th.SystemAdminUser.Id,
}, "status @"+th.BasicUser.Username)
assert.Equal(t, model.CommandResponseTypeEphemeral, resp.ResponseType)
assert.Equal(t, "api.command_mobile_logs.status_off", resp.Text)
// Enable for other user, then check status
cmd.DoCommand(th.App, th.Context, &model.CommandArgs{
T: i18n.IdentityTfunc(),
UserId: th.SystemAdminUser.Id,
}, "on @"+th.BasicUser.Username)
resp = cmd.DoCommand(th.App, th.Context, &model.CommandArgs{
T: i18n.IdentityTfunc(),
UserId: th.SystemAdminUser.Id,
}, "status @"+th.BasicUser.Username)
assert.Equal(t, model.CommandResponseTypeEphemeral, resp.ResponseType)
assert.Equal(t, "api.command_mobile_logs.status_on", resp.Text)
}
func TestMobileLogsOnOtherUserWithoutPermission(t *testing.T) {
th := setup(t).initBasic(t)
cmd := &MobileLogsProvider{}
resp := cmd.DoCommand(th.App, th.Context, &model.CommandArgs{
T: i18n.IdentityTfunc(),
UserId: th.BasicUser.Id,
}, "on @"+th.BasicUser2.Username)
assert.Equal(t, model.CommandResponseTypeEphemeral, resp.ResponseType)
assert.Equal(t, "api.command_mobile_logs.cross_user_unavailable.app_error", resp.Text)
}
func TestMobileLogsOnOtherUserWithoutAtPrefix(t *testing.T) {
th := setup(t).initBasic(t)
cmd := &MobileLogsProvider{}
resp := cmd.DoCommand(th.App, th.Context, &model.CommandArgs{
T: i18n.IdentityTfunc(),
UserId: th.SystemAdminUser.Id,
}, "on "+th.BasicUser.Username)
assert.Equal(t, model.CommandResponseTypeEphemeral, resp.ResponseType)
assert.Equal(t, "api.command_mobile_logs.enabled", resp.Text)
pref, err := th.App.GetPreferenceByCategoryAndNameForUser(th.Context, th.BasicUser.Id, model.PreferenceCategoryAdvancedSettings, model.PreferenceNameAttachAppLogs)
require.Nil(t, err)
assert.Equal(t, "true", pref.Value)
}
func TestMobileLogsNoArgs(t *testing.T) {
th := setup(t).initBasic(t)
cmd := &MobileLogsProvider{}
resp := cmd.DoCommand(th.App, th.Context, &model.CommandArgs{
T: i18n.IdentityTfunc(),
UserId: th.BasicUser.Id,
}, "")
assert.Equal(t, model.CommandResponseTypeEphemeral, resp.ResponseType)
assert.Equal(t, "api.command_mobile_logs.usage", resp.Text)
}
func TestMobileLogsTooManyArgs(t *testing.T) {
th := setup(t).initBasic(t)
cmd := &MobileLogsProvider{}
cmd.DoCommand(th.App, th.Context, &model.CommandArgs{
T: i18n.IdentityTfunc(),
UserId: th.SystemAdminUser.Id,
}, "on @"+th.BasicUser.Username)
resp := cmd.DoCommand(th.App, th.Context, &model.CommandArgs{
T: i18n.IdentityTfunc(),
UserId: th.SystemAdminUser.Id,
}, "on @"+th.BasicUser.Username+" trailing")
assert.Equal(t, model.CommandResponseTypeEphemeral, resp.ResponseType)
assert.Equal(t, "api.command_mobile_logs.usage", resp.Text)
pref, err := th.App.GetPreferenceByCategoryAndNameForUser(th.Context, th.BasicUser.Id, model.PreferenceCategoryAdvancedSettings, model.PreferenceNameAttachAppLogs)
require.Nil(t, err)
assert.Equal(t, "true", pref.Value)
}
func TestMobileLogsInvalidAction(t *testing.T) {
th := setup(t).initBasic(t)
cmd := &MobileLogsProvider{}
resp := cmd.DoCommand(th.App, th.Context, &model.CommandArgs{
T: i18n.IdentityTfunc(),
UserId: th.BasicUser.Id,
}, "invalid")
assert.Equal(t, model.CommandResponseTypeEphemeral, resp.ResponseType)
assert.Equal(t, "api.command_mobile_logs.usage", resp.Text)
}
func TestMobileLogsUserNotFound(t *testing.T) {
th := setup(t).initBasic(t)
cmd := &MobileLogsProvider{}
resp := cmd.DoCommand(th.App, th.Context, &model.CommandArgs{
T: i18n.IdentityTfunc(),
UserId: th.SystemAdminUser.Id,
}, "on @nonexistentuser12345")
assert.Equal(t, model.CommandResponseTypeEphemeral, resp.ResponseType)
assert.Equal(t, "api.command_mobile_logs.user_not_found.app_error", resp.Text)
}
func TestMobileLogsNonexistentTargetAsRegularUserReturnsNoPermission(t *testing.T) {
th := setup(t).initBasic(t)
cmd := &MobileLogsProvider{}
resp := cmd.DoCommand(th.App, th.Context, &model.CommandArgs{
T: i18n.IdentityTfunc(),
UserId: th.BasicUser.Id,
}, "on @nonexistentuser12345")
assert.Equal(t, model.CommandResponseTypeEphemeral, resp.ResponseType)
assert.Equal(t, "api.command_mobile_logs.cross_user_unavailable.app_error", resp.Text)
}
func TestMobileLogsOnSelfWithAtUsername(t *testing.T) {
th := setup(t).initBasic(t)
cmd := &MobileLogsProvider{}
resp := cmd.DoCommand(th.App, th.Context, &model.CommandArgs{
T: i18n.IdentityTfunc(),
UserId: th.BasicUser.Id,
}, "on @"+th.BasicUser.Username)
assert.Equal(t, model.CommandResponseTypeEphemeral, resp.ResponseType)
assert.Equal(t, "api.command_mobile_logs.enabled", resp.Text)
pref, err := th.App.GetPreferenceByCategoryAndNameForUser(th.Context, th.BasicUser.Id, model.PreferenceCategoryAdvancedSettings, model.PreferenceNameAttachAppLogs)
require.Nil(t, err)
assert.Equal(t, "true", pref.Value)
}
func TestMobileLogsDeactivatedUser(t *testing.T) {
th := setup(t).initBasic(t)
_, err := th.App.UpdateActive(th.Context, th.BasicUser2, false)
require.Nil(t, err)
cmd := &MobileLogsProvider{}
resp := cmd.DoCommand(th.App, th.Context, &model.CommandArgs{
T: i18n.IdentityTfunc(),
UserId: th.SystemAdminUser.Id,
}, "on @"+th.BasicUser2.Username)
assert.Equal(t, model.CommandResponseTypeEphemeral, resp.ResponseType)
assert.Equal(t, "api.command_mobile_logs.user_not_found.app_error", resp.Text)
}