From 1f168263a2ff73ddee1193cccdeea533f6d501fe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jes=C3=BAs=20Espino?= Date: Wed, 1 Aug 2018 15:45:26 +0200 Subject: Split app/import.go in multiple files (#9179) --- app/import_functions_test.go | 2501 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 2501 insertions(+) create mode 100644 app/import_functions_test.go (limited to 'app/import_functions_test.go') diff --git a/app/import_functions_test.go b/app/import_functions_test.go new file mode 100644 index 000000000..e0859af45 --- /dev/null +++ b/app/import_functions_test.go @@ -0,0 +1,2501 @@ +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. +// See License.txt for license information. + +package app + +import ( + "path/filepath" + "strings" + "testing" + + "github.com/mattermost/mattermost-server/model" + "github.com/mattermost/mattermost-server/store" + "github.com/mattermost/mattermost-server/utils" + "github.com/stretchr/testify/assert" +) + +func TestImportImportScheme(t *testing.T) { + th := Setup() + defer th.TearDown() + + // Mark the phase 2 permissions migration as completed. + <-th.App.Srv.Store.System().Save(&model.System{Name: model.MIGRATION_KEY_ADVANCED_PERMISSIONS_PHASE_2, Value: "true"}) + + defer func() { + <-th.App.Srv.Store.System().PermanentDeleteByName(model.MIGRATION_KEY_ADVANCED_PERMISSIONS_PHASE_2) + }() + + // Try importing an invalid scheme in dryRun mode. + data := SchemeImportData{ + Name: ptrStr(model.NewId()), + Scope: ptrStr("team"), + DefaultTeamUserRole: &RoleImportData{ + Name: ptrStr(model.NewId()), + DisplayName: ptrStr(model.NewId()), + }, + DefaultTeamAdminRole: &RoleImportData{ + Name: ptrStr(model.NewId()), + DisplayName: ptrStr(model.NewId()), + }, + DefaultChannelUserRole: &RoleImportData{ + Name: ptrStr(model.NewId()), + DisplayName: ptrStr(model.NewId()), + }, + DefaultChannelAdminRole: &RoleImportData{ + Name: ptrStr(model.NewId()), + DisplayName: ptrStr(model.NewId()), + }, + Description: ptrStr("description"), + } + + if err := th.App.ImportScheme(&data, true); err == nil { + t.Fatalf("Should have failed to import.") + } + + if res := <-th.App.Srv.Store.Scheme().GetByName(*data.Name); res.Err == nil { + t.Fatalf("Scheme should not have imported.") + } + + // Try importing a valid scheme in dryRun mode. + data.DisplayName = ptrStr("display name") + + if err := th.App.ImportScheme(&data, true); err != nil { + t.Fatalf("Should have succeeded.") + } + + if res := <-th.App.Srv.Store.Scheme().GetByName(*data.Name); res.Err == nil { + t.Fatalf("Scheme should not have imported.") + } + + // Try importing an invalid scheme. + data.DisplayName = nil + + if err := th.App.ImportScheme(&data, false); err == nil { + t.Fatalf("Should have failed to import.") + } + + if res := <-th.App.Srv.Store.Scheme().GetByName(*data.Name); res.Err == nil { + t.Fatalf("Scheme should not have imported.") + } + + // Try importing a valid scheme with all params set. + data.DisplayName = ptrStr("display name") + + if err := th.App.ImportScheme(&data, false); err != nil { + t.Fatalf("Should have succeeded.") + } + + if res := <-th.App.Srv.Store.Scheme().GetByName(*data.Name); res.Err != nil { + t.Fatalf("Failed to import scheme: %v", res.Err) + } else { + scheme := res.Data.(*model.Scheme) + assert.Equal(t, *data.Name, scheme.Name) + assert.Equal(t, *data.DisplayName, scheme.DisplayName) + assert.Equal(t, *data.Description, scheme.Description) + assert.Equal(t, *data.Scope, scheme.Scope) + + if res := <-th.App.Srv.Store.Role().GetByName(scheme.DefaultTeamAdminRole); res.Err != nil { + t.Fatalf("Should have found the imported role.") + } else { + role := res.Data.(*model.Role) + assert.Equal(t, *data.DefaultTeamAdminRole.DisplayName, role.DisplayName) + assert.False(t, role.BuiltIn) + assert.True(t, role.SchemeManaged) + } + + if res := <-th.App.Srv.Store.Role().GetByName(scheme.DefaultTeamUserRole); res.Err != nil { + t.Fatalf("Should have found the imported role.") + } else { + role := res.Data.(*model.Role) + assert.Equal(t, *data.DefaultTeamUserRole.DisplayName, role.DisplayName) + assert.False(t, role.BuiltIn) + assert.True(t, role.SchemeManaged) + } + + if res := <-th.App.Srv.Store.Role().GetByName(scheme.DefaultChannelAdminRole); res.Err != nil { + t.Fatalf("Should have found the imported role.") + } else { + role := res.Data.(*model.Role) + assert.Equal(t, *data.DefaultChannelAdminRole.DisplayName, role.DisplayName) + assert.False(t, role.BuiltIn) + assert.True(t, role.SchemeManaged) + } + + if res := <-th.App.Srv.Store.Role().GetByName(scheme.DefaultChannelUserRole); res.Err != nil { + t.Fatalf("Should have found the imported role.") + } else { + role := res.Data.(*model.Role) + assert.Equal(t, *data.DefaultChannelUserRole.DisplayName, role.DisplayName) + assert.False(t, role.BuiltIn) + assert.True(t, role.SchemeManaged) + } + } + + // Try modifying all the fields and re-importing. + data.DisplayName = ptrStr("new display name") + data.Description = ptrStr("new description") + + if err := th.App.ImportScheme(&data, false); err != nil { + t.Fatalf("Should have succeeded: %v", err) + } + + if res := <-th.App.Srv.Store.Scheme().GetByName(*data.Name); res.Err != nil { + t.Fatalf("Failed to import scheme: %v", res.Err) + } else { + scheme := res.Data.(*model.Scheme) + assert.Equal(t, *data.Name, scheme.Name) + assert.Equal(t, *data.DisplayName, scheme.DisplayName) + assert.Equal(t, *data.Description, scheme.Description) + assert.Equal(t, *data.Scope, scheme.Scope) + + if res := <-th.App.Srv.Store.Role().GetByName(scheme.DefaultTeamAdminRole); res.Err != nil { + t.Fatalf("Should have found the imported role.") + } else { + role := res.Data.(*model.Role) + assert.Equal(t, *data.DefaultTeamAdminRole.DisplayName, role.DisplayName) + assert.False(t, role.BuiltIn) + assert.True(t, role.SchemeManaged) + } + + if res := <-th.App.Srv.Store.Role().GetByName(scheme.DefaultTeamUserRole); res.Err != nil { + t.Fatalf("Should have found the imported role.") + } else { + role := res.Data.(*model.Role) + assert.Equal(t, *data.DefaultTeamUserRole.DisplayName, role.DisplayName) + assert.False(t, role.BuiltIn) + assert.True(t, role.SchemeManaged) + } + + if res := <-th.App.Srv.Store.Role().GetByName(scheme.DefaultChannelAdminRole); res.Err != nil { + t.Fatalf("Should have found the imported role.") + } else { + role := res.Data.(*model.Role) + assert.Equal(t, *data.DefaultChannelAdminRole.DisplayName, role.DisplayName) + assert.False(t, role.BuiltIn) + assert.True(t, role.SchemeManaged) + } + + if res := <-th.App.Srv.Store.Role().GetByName(scheme.DefaultChannelUserRole); res.Err != nil { + t.Fatalf("Should have found the imported role.") + } else { + role := res.Data.(*model.Role) + assert.Equal(t, *data.DefaultChannelUserRole.DisplayName, role.DisplayName) + assert.False(t, role.BuiltIn) + assert.True(t, role.SchemeManaged) + } + } + + // Try changing the scope of the scheme and reimporting. + data.Scope = ptrStr("channel") + + if err := th.App.ImportScheme(&data, false); err == nil { + t.Fatalf("Should have failed to import.") + } + + if res := <-th.App.Srv.Store.Scheme().GetByName(*data.Name); res.Err != nil { + t.Fatalf("Failed to import scheme: %v", res.Err) + } else { + scheme := res.Data.(*model.Scheme) + assert.Equal(t, *data.Name, scheme.Name) + assert.Equal(t, *data.DisplayName, scheme.DisplayName) + assert.Equal(t, *data.Description, scheme.Description) + assert.Equal(t, "team", scheme.Scope) + } +} + +func TestImportImportRole(t *testing.T) { + th := Setup() + defer th.TearDown() + + // Try importing an invalid role in dryRun mode. + rid1 := model.NewId() + data := RoleImportData{ + Name: &rid1, + } + + if err := th.App.ImportRole(&data, true, false); err == nil { + t.Fatalf("Should have failed to import.") + } + + if res := <-th.App.Srv.Store.Role().GetByName(rid1); res.Err == nil { + t.Fatalf("Role should not have imported.") + } + + // Try importing the valid role in dryRun mode. + data.DisplayName = ptrStr("display name") + + if err := th.App.ImportRole(&data, true, false); err != nil { + t.Fatalf("Should have succeeded.") + } + + if res := <-th.App.Srv.Store.Role().GetByName(rid1); res.Err == nil { + t.Fatalf("Role should not have imported as we are in dry run mode.") + } + + // Try importing an invalid role. + data.DisplayName = nil + + if err := th.App.ImportRole(&data, false, false); err == nil { + t.Fatalf("Should have failed to import.") + } + + if res := <-th.App.Srv.Store.Role().GetByName(rid1); res.Err == nil { + t.Fatalf("Role should not have imported.") + } + + // Try importing a valid role with all params set. + data.DisplayName = ptrStr("display name") + data.Description = ptrStr("description") + data.Permissions = &[]string{"invite_user", "add_user_to_team"} + + if err := th.App.ImportRole(&data, false, false); err != nil { + t.Fatalf("Should have succeeded.") + } + + if res := <-th.App.Srv.Store.Role().GetByName(rid1); res.Err != nil { + t.Fatalf("Should have found the imported role.") + } else { + role := res.Data.(*model.Role) + assert.Equal(t, *data.Name, role.Name) + assert.Equal(t, *data.DisplayName, role.DisplayName) + assert.Equal(t, *data.Description, role.Description) + assert.Equal(t, *data.Permissions, role.Permissions) + assert.False(t, role.BuiltIn) + assert.False(t, role.SchemeManaged) + } + + // Try changing all the params and reimporting. + data.DisplayName = ptrStr("new display name") + data.Description = ptrStr("description") + data.Permissions = &[]string{"use_slash_commands"} + + if err := th.App.ImportRole(&data, false, true); err != nil { + t.Fatalf("Should have succeeded. %v", err) + } + + if res := <-th.App.Srv.Store.Role().GetByName(rid1); res.Err != nil { + t.Fatalf("Should have found the imported role.") + } else { + role := res.Data.(*model.Role) + assert.Equal(t, *data.Name, role.Name) + assert.Equal(t, *data.DisplayName, role.DisplayName) + assert.Equal(t, *data.Description, role.Description) + assert.Equal(t, *data.Permissions, role.Permissions) + assert.False(t, role.BuiltIn) + assert.True(t, role.SchemeManaged) + } + + // Check that re-importing with only required fields doesn't update the others. + data2 := RoleImportData{ + Name: &rid1, + DisplayName: ptrStr("new display name again"), + } + + if err := th.App.ImportRole(&data2, false, false); err != nil { + t.Fatalf("Should have succeeded.") + } + + if res := <-th.App.Srv.Store.Role().GetByName(rid1); res.Err != nil { + t.Fatalf("Should have found the imported role.") + } else { + role := res.Data.(*model.Role) + assert.Equal(t, *data2.Name, role.Name) + assert.Equal(t, *data2.DisplayName, role.DisplayName) + assert.Equal(t, *data.Description, role.Description) + assert.Equal(t, *data.Permissions, role.Permissions) + assert.False(t, role.BuiltIn) + assert.False(t, role.SchemeManaged) + } +} + +func TestImportImportTeam(t *testing.T) { + th := Setup() + defer th.TearDown() + + // Mark the phase 2 permissions migration as completed. + <-th.App.Srv.Store.System().Save(&model.System{Name: model.MIGRATION_KEY_ADVANCED_PERMISSIONS_PHASE_2, Value: "true"}) + + defer func() { + <-th.App.Srv.Store.System().PermanentDeleteByName(model.MIGRATION_KEY_ADVANCED_PERMISSIONS_PHASE_2) + }() + + scheme1 := th.SetupTeamScheme() + scheme2 := th.SetupTeamScheme() + + // Check how many teams are in the database. + var teamsCount int64 + if r := <-th.App.Srv.Store.Team().AnalyticsTeamCount(); r.Err == nil { + teamsCount = r.Data.(int64) + } else { + t.Fatalf("Failed to get team count.") + } + + data := TeamImportData{ + Name: ptrStr(model.NewId()), + DisplayName: ptrStr("Display Name"), + Type: ptrStr("XYZ"), + Description: ptrStr("The team description."), + AllowOpenInvite: ptrBool(true), + Scheme: &scheme1.Name, + } + + // Try importing an invalid team in dryRun mode. + if err := th.App.ImportTeam(&data, true); err == nil { + t.Fatalf("Should have received an error importing an invalid team.") + } + + // Do a valid team in dry-run mode. + data.Type = ptrStr("O") + if err := th.App.ImportTeam(&data, true); err != nil { + t.Fatalf("Received an error validating valid team.") + } + + // Check that no more teams are in the DB. + th.CheckTeamCount(t, teamsCount) + + // Do an invalid team in apply mode, check db changes. + data.Type = ptrStr("XYZ") + if err := th.App.ImportTeam(&data, false); err == nil { + t.Fatalf("Import should have failed on invalid team.") + } + + // Check that no more teams are in the DB. + th.CheckTeamCount(t, teamsCount) + + // Do a valid team in apply mode, check db changes. + data.Type = ptrStr("O") + if err := th.App.ImportTeam(&data, false); err != nil { + t.Fatalf("Received an error importing valid team: %v", err) + } + + // Check that one more team is in the DB. + th.CheckTeamCount(t, teamsCount+1) + + // Get the team and check that all the fields are correct. + if team, err := th.App.GetTeamByName(*data.Name); err != nil { + t.Fatalf("Failed to get team from database.") + } else { + assert.Equal(t, *data.DisplayName, team.DisplayName) + assert.Equal(t, *data.Type, team.Type) + assert.Equal(t, *data.Description, team.Description) + assert.Equal(t, *data.AllowOpenInvite, team.AllowOpenInvite) + assert.Equal(t, scheme1.Id, *team.SchemeId) + } + + // Alter all the fields of that team (apart from unique identifier) and import again. + data.DisplayName = ptrStr("Display Name 2") + data.Type = ptrStr("P") + data.Description = ptrStr("The new description") + data.AllowOpenInvite = ptrBool(false) + data.Scheme = &scheme2.Name + + // Check that the original number of teams are again in the DB (because this query doesn't include deleted). + data.Type = ptrStr("O") + if err := th.App.ImportTeam(&data, false); err != nil { + t.Fatalf("Received an error importing updated valid team.") + } + + th.CheckTeamCount(t, teamsCount+1) + + // Get the team and check that all fields are correct. + if team, err := th.App.GetTeamByName(*data.Name); err != nil { + t.Fatalf("Failed to get team from database.") + } else { + assert.Equal(t, *data.DisplayName, team.DisplayName) + assert.Equal(t, *data.Type, team.Type) + assert.Equal(t, *data.Description, team.Description) + assert.Equal(t, *data.AllowOpenInvite, team.AllowOpenInvite) + assert.Equal(t, scheme2.Id, *team.SchemeId) + } +} + +func TestImportImportChannel(t *testing.T) { + th := Setup() + defer th.TearDown() + + // Mark the phase 2 permissions migration as completed. + <-th.App.Srv.Store.System().Save(&model.System{Name: model.MIGRATION_KEY_ADVANCED_PERMISSIONS_PHASE_2, Value: "true"}) + + defer func() { + <-th.App.Srv.Store.System().PermanentDeleteByName(model.MIGRATION_KEY_ADVANCED_PERMISSIONS_PHASE_2) + }() + + scheme1 := th.SetupChannelScheme() + scheme2 := th.SetupChannelScheme() + + // Import a Team. + teamName := model.NewId() + th.App.ImportTeam(&TeamImportData{ + Name: &teamName, + DisplayName: ptrStr("Display Name"), + Type: ptrStr("O"), + }, false) + team, err := th.App.GetTeamByName(teamName) + if err != nil { + t.Fatalf("Failed to get team from database.") + } + + // Check how many channels are in the database. + var channelCount int64 + if r := <-th.App.Srv.Store.Channel().AnalyticsTypeCount("", model.CHANNEL_OPEN); r.Err == nil { + channelCount = r.Data.(int64) + } else { + t.Fatalf("Failed to get team count.") + } + + // Do an invalid channel in dry-run mode. + data := ChannelImportData{ + Team: &teamName, + DisplayName: ptrStr("Display Name"), + Type: ptrStr("O"), + Header: ptrStr("Channe Header"), + Purpose: ptrStr("Channel Purpose"), + Scheme: &scheme1.Name, + } + if err := th.App.ImportChannel(&data, true); err == nil { + t.Fatalf("Expected error due to invalid name.") + } + + // Check that no more channels are in the DB. + th.CheckChannelsCount(t, channelCount) + + // Do a valid channel with a nonexistent team in dry-run mode. + data.Name = ptrStr("channelname") + data.Team = ptrStr(model.NewId()) + if err := th.App.ImportChannel(&data, true); err != nil { + t.Fatalf("Expected success as cannot validate channel name in dry run mode.") + } + + // Check that no more channels are in the DB. + th.CheckChannelsCount(t, channelCount) + + // Do a valid channel in dry-run mode. + data.Team = &teamName + if err := th.App.ImportChannel(&data, true); err != nil { + t.Fatalf("Expected success as valid team.") + } + + // Check that no more channels are in the DB. + th.CheckChannelsCount(t, channelCount) + + // Do an invalid channel in apply mode. + data.Name = nil + if err := th.App.ImportChannel(&data, false); err == nil { + t.Fatalf("Expected error due to invalid name (apply mode).") + } + + // Check that no more channels are in the DB. + th.CheckChannelsCount(t, channelCount) + + // Do a valid channel in apply mode with a non-existent team. + data.Name = ptrStr("channelname") + data.Team = ptrStr(model.NewId()) + if err := th.App.ImportChannel(&data, false); err == nil { + t.Fatalf("Expected error due to non-existent team (apply mode).") + } + + // Check that no more channels are in the DB. + th.CheckChannelsCount(t, channelCount) + + // Do a valid channel in apply mode. + data.Team = &teamName + if err := th.App.ImportChannel(&data, false); err != nil { + t.Fatalf("Expected success in apply mode: %v", err.Error()) + } + + // Check that 1 more channel is in the DB. + th.CheckChannelsCount(t, channelCount+1) + + // Get the Channel and check all the fields are correct. + if channel, err := th.App.GetChannelByName(*data.Name, team.Id, false); err != nil { + t.Fatalf("Failed to get channel from database.") + } else { + assert.Equal(t, *data.Name, channel.Name) + assert.Equal(t, *data.DisplayName, channel.DisplayName) + assert.Equal(t, *data.Type, channel.Type) + assert.Equal(t, *data.Header, channel.Header) + assert.Equal(t, *data.Purpose, channel.Purpose) + assert.Equal(t, scheme1.Id, *channel.SchemeId) + } + + // Alter all the fields of that channel. + data.DisplayName = ptrStr("Chaned Disp Name") + data.Type = ptrStr(model.CHANNEL_PRIVATE) + data.Header = ptrStr("New Header") + data.Purpose = ptrStr("New Purpose") + data.Scheme = &scheme2.Name + if err := th.App.ImportChannel(&data, false); err != nil { + t.Fatalf("Expected success in apply mode: %v", err.Error()) + } + + // Check channel count the same. + th.CheckChannelsCount(t, channelCount) + + // Get the Channel and check all the fields are correct. + if channel, err := th.App.GetChannelByName(*data.Name, team.Id, false); err != nil { + t.Fatalf("Failed to get channel from database.") + } else { + assert.Equal(t, *data.Name, channel.Name) + assert.Equal(t, *data.DisplayName, channel.DisplayName) + assert.Equal(t, *data.Type, channel.Type) + assert.Equal(t, *data.Header, channel.Header) + assert.Equal(t, *data.Purpose, channel.Purpose) + assert.Equal(t, scheme2.Id, *channel.SchemeId) + } + +} + +func TestImportImportUser(t *testing.T) { + th := Setup() + defer th.TearDown() + + // Check how many users are in the database. + var userCount int64 + if r := <-th.App.Srv.Store.User().GetTotalUsersCount(); r.Err == nil { + userCount = r.Data.(int64) + } else { + t.Fatalf("Failed to get user count.") + } + + // Do an invalid user in dry-run mode. + data := UserImportData{ + Username: ptrStr(model.NewId()), + } + if err := th.App.ImportUser(&data, true); err == nil { + t.Fatalf("Should have failed to import invalid user.") + } + + // Check that no more users are in the DB. + if r := <-th.App.Srv.Store.User().GetTotalUsersCount(); r.Err == nil { + if r.Data.(int64) != userCount { + t.Fatalf("Unexpected number of users") + } + } else { + t.Fatalf("Failed to get user count.") + } + + // Do a valid user in dry-run mode. + data = UserImportData{ + Username: ptrStr(model.NewId()), + Email: ptrStr(model.NewId() + "@example.com"), + } + if err := th.App.ImportUser(&data, true); err != nil { + t.Fatalf("Should have succeeded to import valid user.") + } + + // Check that no more users are in the DB. + if r := <-th.App.Srv.Store.User().GetTotalUsersCount(); r.Err == nil { + if r.Data.(int64) != userCount { + t.Fatalf("Unexpected number of users") + } + } else { + t.Fatalf("Failed to get user count.") + } + + // Do an invalid user in apply mode. + data = UserImportData{ + Username: ptrStr(model.NewId()), + } + if err := th.App.ImportUser(&data, false); err == nil { + t.Fatalf("Should have failed to import invalid user.") + } + + // Check that no more users are in the DB. + if r := <-th.App.Srv.Store.User().GetTotalUsersCount(); r.Err == nil { + if r.Data.(int64) != userCount { + t.Fatalf("Unexpected number of users") + } + } else { + t.Fatalf("Failed to get user count.") + } + + // Do a valid user in apply mode. + username := model.NewId() + testsDir, _ := utils.FindDir("tests") + data = UserImportData{ + ProfileImage: ptrStr(filepath.Join(testsDir, "test.png")), + Username: &username, + Email: ptrStr(model.NewId() + "@example.com"), + Nickname: ptrStr(model.NewId()), + FirstName: ptrStr(model.NewId()), + LastName: ptrStr(model.NewId()), + Position: ptrStr(model.NewId()), + } + if err := th.App.ImportUser(&data, false); err != nil { + t.Fatalf("Should have succeeded to import valid user.") + } + + // Check that one more user is in the DB. + if r := <-th.App.Srv.Store.User().GetTotalUsersCount(); r.Err == nil { + if r.Data.(int64) != userCount+1 { + t.Fatalf("Unexpected number of users") + } + } else { + t.Fatalf("Failed to get user count.") + } + + // Get the user and check all the fields are correct. + if user, err := th.App.GetUserByUsername(username); err != nil { + t.Fatalf("Failed to get user from database.") + } else { + if user.Email != *data.Email || user.Nickname != *data.Nickname || user.FirstName != *data.FirstName || user.LastName != *data.LastName || user.Position != *data.Position { + t.Fatalf("User properties do not match Import Data.") + } + // Check calculated properties. + if user.AuthService != "" { + t.Fatalf("Expected Auth Service to be empty.") + } + + if !(user.AuthData == nil || *user.AuthData == "") { + t.Fatalf("Expected AuthData to be empty.") + } + + if len(user.Password) == 0 { + t.Fatalf("Expected password to be set.") + } + + if !user.EmailVerified { + t.Fatalf("Expected EmailVerified to be true.") + } + + if user.Locale != *th.App.Config().LocalizationSettings.DefaultClientLocale { + t.Fatalf("Expected Locale to be the default.") + } + + if user.Roles != "system_user" { + t.Fatalf("Expected roles to be system_user") + } + } + + // Alter all the fields of that user. + data.Email = ptrStr(model.NewId() + "@example.com") + data.ProfileImage = ptrStr(filepath.Join(testsDir, "testgif.gif")) + data.AuthService = ptrStr("ldap") + data.AuthData = &username + data.Nickname = ptrStr(model.NewId()) + data.FirstName = ptrStr(model.NewId()) + data.LastName = ptrStr(model.NewId()) + data.Position = ptrStr(model.NewId()) + data.Roles = ptrStr("system_admin system_user") + data.Locale = ptrStr("zh_CN") + if err := th.App.ImportUser(&data, false); err != nil { + t.Fatalf("Should have succeeded to update valid user %v", err) + } + + // Check user count the same. + if r := <-th.App.Srv.Store.User().GetTotalUsersCount(); r.Err == nil { + if r.Data.(int64) != userCount+1 { + t.Fatalf("Unexpected number of users") + } + } else { + t.Fatalf("Failed to get user count.") + } + + // Get the user and check all the fields are correct. + if user, err := th.App.GetUserByUsername(username); err != nil { + t.Fatalf("Failed to get user from database.") + } else { + if user.Email != *data.Email || user.Nickname != *data.Nickname || user.FirstName != *data.FirstName || user.LastName != *data.LastName || user.Position != *data.Position { + t.Fatalf("Updated User properties do not match Import Data.") + } + // Check calculated properties. + if user.AuthService != "ldap" { + t.Fatalf("Expected Auth Service to be ldap \"%v\"", user.AuthService) + } + + if !(user.AuthData == data.AuthData || *user.AuthData == *data.AuthData) { + t.Fatalf("Expected AuthData to be set.") + } + + if len(user.Password) != 0 { + t.Fatalf("Expected password to be empty.") + } + + if !user.EmailVerified { + t.Fatalf("Expected EmailVerified to be true.") + } + + if user.Locale != *data.Locale { + t.Fatalf("Expected Locale to be the set.") + } + + if user.Roles != *data.Roles { + t.Fatalf("Expected roles to be set: %v", user.Roles) + } + } + + // Check Password and AuthData together. + data.Password = ptrStr("PasswordTest") + if err := th.App.ImportUser(&data, false); err == nil { + t.Fatalf("Should have failed to import invalid user.") + } + + data.AuthData = nil + if err := th.App.ImportUser(&data, false); err != nil { + t.Fatalf("Should have succeeded to update valid user %v", err) + } + + data.Password = ptrStr("") + if err := th.App.ImportUser(&data, false); err == nil { + t.Fatalf("Should have failed to import invalid user.") + } + + data.Password = ptrStr(strings.Repeat("0123456789", 10)) + if err := th.App.ImportUser(&data, false); err == nil { + t.Fatalf("Should have failed to import invalid user.") + } + + data.Password = ptrStr("TestPassword") + + // Test team and channel memberships + teamName := model.NewId() + th.App.ImportTeam(&TeamImportData{ + Name: &teamName, + DisplayName: ptrStr("Display Name"), + Type: ptrStr("O"), + }, false) + team, err := th.App.GetTeamByName(teamName) + if err != nil { + t.Fatalf("Failed to get team from database.") + } + + channelName := model.NewId() + th.App.ImportChannel(&ChannelImportData{ + Team: &teamName, + Name: &channelName, + DisplayName: ptrStr("Display Name"), + Type: ptrStr("O"), + }, false) + channel, err := th.App.GetChannelByName(channelName, team.Id, false) + if err != nil { + t.Fatalf("Failed to get channel from database.") + } + + username = model.NewId() + data = UserImportData{ + Username: &username, + Email: ptrStr(model.NewId() + "@example.com"), + Nickname: ptrStr(model.NewId()), + FirstName: ptrStr(model.NewId()), + LastName: ptrStr(model.NewId()), + Position: ptrStr(model.NewId()), + } + + teamMembers, err := th.App.GetTeamMembers(team.Id, 0, 1000) + if err != nil { + t.Fatalf("Failed to get team member count") + } + teamMemberCount := len(teamMembers) + + channelMemberCount, err := th.App.GetChannelMemberCount(channel.Id) + if err != nil { + t.Fatalf("Failed to get channel member count") + } + + // Test with an invalid team & channel membership in dry-run mode. + data.Teams = &[]UserTeamImportData{ + { + Roles: ptrStr("invalid"), + Channels: &[]UserChannelImportData{ + { + Roles: ptrStr("invalid"), + }, + }, + }, + } + if err := th.App.ImportUser(&data, true); err == nil { + t.Fatalf("Should have failed.") + } + + // Test with an unknown team name & invalid channel membership in dry-run mode. + data.Teams = &[]UserTeamImportData{ + { + Name: ptrStr(model.NewId()), + Channels: &[]UserChannelImportData{ + { + Roles: ptrStr("invalid"), + }, + }, + }, + } + if err := th.App.ImportUser(&data, true); err == nil { + t.Fatalf("Should have failed.") + } + + // Test with a valid team & invalid channel membership in dry-run mode. + data.Teams = &[]UserTeamImportData{ + { + Name: &teamName, + Channels: &[]UserChannelImportData{ + { + Roles: ptrStr("invalid"), + }, + }, + }, + } + if err := th.App.ImportUser(&data, true); err == nil { + t.Fatalf("Should have failed.") + } + + // Test with a valid team & unknown channel name in dry-run mode. + data.Teams = &[]UserTeamImportData{ + { + Name: &teamName, + Channels: &[]UserChannelImportData{ + { + Name: ptrStr(model.NewId()), + }, + }, + }, + } + if err := th.App.ImportUser(&data, true); err != nil { + t.Fatalf("Should have succeeded.") + } + + // Test with a valid team & valid channel name in dry-run mode. + data.Teams = &[]UserTeamImportData{ + { + Name: &teamName, + Channels: &[]UserChannelImportData{ + { + Name: &channelName, + }, + }, + }, + } + if err := th.App.ImportUser(&data, true); err != nil { + t.Fatalf("Should have succeeded.") + } + + // Check no new member objects were created because dry run mode. + if tmc, err := th.App.GetTeamMembers(team.Id, 0, 1000); err != nil { + t.Fatalf("Failed to get Team Member Count") + } else if len(tmc) != teamMemberCount { + t.Fatalf("Number of team members not as expected") + } + + if cmc, err := th.App.GetChannelMemberCount(channel.Id); err != nil { + t.Fatalf("Failed to get Channel Member Count") + } else if cmc != channelMemberCount { + t.Fatalf("Number of channel members not as expected") + } + + // Test with an invalid team & channel membership in apply mode. + data.Teams = &[]UserTeamImportData{ + { + Roles: ptrStr("invalid"), + Channels: &[]UserChannelImportData{ + { + Roles: ptrStr("invalid"), + }, + }, + }, + } + if err := th.App.ImportUser(&data, false); err == nil { + t.Fatalf("Should have failed.") + } + + // Test with an unknown team name & invalid channel membership in apply mode. + data.Teams = &[]UserTeamImportData{ + { + Name: ptrStr(model.NewId()), + Channels: &[]UserChannelImportData{ + { + Roles: ptrStr("invalid"), + }, + }, + }, + } + if err := th.App.ImportUser(&data, false); err == nil { + t.Fatalf("Should have failed.") + } + + // Test with a valid team & invalid channel membership in apply mode. + data.Teams = &[]UserTeamImportData{ + { + Name: &teamName, + Channels: &[]UserChannelImportData{ + { + Roles: ptrStr("invalid"), + }, + }, + }, + } + if err := th.App.ImportUser(&data, false); err == nil { + t.Fatalf("Should have failed.") + } + + // Check no new member objects were created because all tests should have failed so far. + if tmc, err := th.App.GetTeamMembers(team.Id, 0, 1000); err != nil { + t.Fatalf("Failed to get Team Member Count") + } else if len(tmc) != teamMemberCount { + t.Fatalf("Number of team members not as expected") + } + + if cmc, err := th.App.GetChannelMemberCount(channel.Id); err != nil { + t.Fatalf("Failed to get Channel Member Count") + } else if cmc != channelMemberCount { + t.Fatalf("Number of channel members not as expected") + } + + // Test with a valid team & unknown channel name in apply mode. + data.Teams = &[]UserTeamImportData{ + { + Name: &teamName, + Channels: &[]UserChannelImportData{ + { + Name: ptrStr(model.NewId()), + }, + }, + }, + } + if err := th.App.ImportUser(&data, false); err == nil { + t.Fatalf("Should have failed.") + } + + // Check only new team member object created because dry run mode. + if tmc, err := th.App.GetTeamMembers(team.Id, 0, 1000); err != nil { + t.Fatalf("Failed to get Team Member Count") + } else if len(tmc) != teamMemberCount+1 { + t.Fatalf("Number of team members not as expected") + } + + if cmc, err := th.App.GetChannelMemberCount(channel.Id); err != nil { + t.Fatalf("Failed to get Channel Member Count") + } else if cmc != channelMemberCount { + t.Fatalf("Number of channel members not as expected") + } + + // Check team member properties. + user, err := th.App.GetUserByUsername(username) + if err != nil { + t.Fatalf("Failed to get user from database.") + } + if teamMember, err := th.App.GetTeamMember(team.Id, user.Id); err != nil { + t.Fatalf("Failed to get team member from database.") + } else if teamMember.Roles != "team_user" { + t.Fatalf("Team member properties not as expected") + } + + // Test with a valid team & valid channel name in apply mode. + data.Teams = &[]UserTeamImportData{ + { + Name: &teamName, + Channels: &[]UserChannelImportData{ + { + Name: &channelName, + }, + }, + }, + } + if err := th.App.ImportUser(&data, false); err != nil { + t.Fatalf("Should have succeeded.") + } + + // Check only new channel member object created because dry run mode. + if tmc, err := th.App.GetTeamMembers(team.Id, 0, 1000); err != nil { + t.Fatalf("Failed to get Team Member Count") + } else if len(tmc) != teamMemberCount+1 { + t.Fatalf("Number of team members not as expected") + } + + if cmc, err := th.App.GetChannelMemberCount(channel.Id); err != nil { + t.Fatalf("Failed to get Channel Member Count") + } else if cmc != channelMemberCount+1 { + t.Fatalf("Number of channel members not as expected") + } + + // Check channel member properties. + if channelMember, err := th.App.GetChannelMember(channel.Id, user.Id); err != nil { + t.Fatalf("Failed to get channel member from database.") + } else if channelMember.Roles != "channel_user" || channelMember.NotifyProps[model.DESKTOP_NOTIFY_PROP] != "default" || channelMember.NotifyProps[model.PUSH_NOTIFY_PROP] != "default" || channelMember.NotifyProps[model.MARK_UNREAD_NOTIFY_PROP] != "all" { + t.Fatalf("Channel member properties not as expected") + } + + // Test with the properties of the team and channel membership changed. + data.Teams = &[]UserTeamImportData{ + { + Name: &teamName, + Roles: ptrStr("team_user team_admin"), + Channels: &[]UserChannelImportData{ + { + Name: &channelName, + Roles: ptrStr("channel_user channel_admin"), + NotifyProps: &UserChannelNotifyPropsImportData{ + Desktop: ptrStr(model.USER_NOTIFY_MENTION), + Mobile: ptrStr(model.USER_NOTIFY_MENTION), + MarkUnread: ptrStr(model.USER_NOTIFY_MENTION), + }, + Favorite: ptrBool(true), + }, + }, + }, + } + if err := th.App.ImportUser(&data, false); err != nil { + t.Fatalf("Should have succeeded.") + } + + // Check both member properties. + if teamMember, err := th.App.GetTeamMember(team.Id, user.Id); err != nil { + t.Fatalf("Failed to get team member from database.") + } else if teamMember.Roles != "team_user team_admin" { + t.Fatalf("Team member properties not as expected: %v", teamMember.Roles) + } + + if channelMember, err := th.App.GetChannelMember(channel.Id, user.Id); err != nil { + t.Fatalf("Failed to get channel member Desktop from database.") + } else if channelMember.Roles != "channel_user channel_admin" || channelMember.NotifyProps[model.DESKTOP_NOTIFY_PROP] != model.USER_NOTIFY_MENTION || channelMember.NotifyProps[model.PUSH_NOTIFY_PROP] != model.USER_NOTIFY_MENTION || channelMember.NotifyProps[model.MARK_UNREAD_NOTIFY_PROP] != model.USER_NOTIFY_MENTION { + t.Fatalf("Channel member properties not as expected") + } + + checkPreference(t, th.App, user.Id, model.PREFERENCE_CATEGORY_FAVORITE_CHANNEL, channel.Id, "true") + + // No more new member objects. + if tmc, err := th.App.GetTeamMembers(team.Id, 0, 1000); err != nil { + t.Fatalf("Failed to get Team Member Count") + } else if len(tmc) != teamMemberCount+1 { + t.Fatalf("Number of team members not as expected") + } + + if cmc, err := th.App.GetChannelMemberCount(channel.Id); err != nil { + t.Fatalf("Failed to get Channel Member Count") + } else if cmc != channelMemberCount+1 { + t.Fatalf("Number of channel members not as expected") + } + + // Add a user with some preferences. + username = model.NewId() + data = UserImportData{ + Username: &username, + Email: ptrStr(model.NewId() + "@example.com"), + Theme: ptrStr(`{"awayIndicator":"#DCBD4E","buttonBg":"#23A2FF","buttonColor":"#FFFFFF","centerChannelBg":"#ffffff","centerChannelColor":"#333333","codeTheme":"github","image":"/static/files/a4a388b38b32678e83823ef1b3e17766.png","linkColor":"#2389d7","mentionBg":"#2389d7","mentionColor":"#ffffff","mentionHighlightBg":"#fff2bb","mentionHighlightLink":"#2f81b7","newMessageSeparator":"#FF8800","onlineIndicator":"#7DBE00","sidebarBg":"#fafafa","sidebarHeaderBg":"#3481B9","sidebarHeaderTextColor":"#ffffff","sidebarText":"#333333","sidebarTextActiveBorder":"#378FD2","sidebarTextActiveColor":"#111111","sidebarTextHoverBg":"#e6f2fa","sidebarUnreadText":"#333333","type":"Mattermost"}`), + UseMilitaryTime: ptrStr("true"), + CollapsePreviews: ptrStr("true"), + MessageDisplay: ptrStr("compact"), + ChannelDisplayMode: ptrStr("centered"), + TutorialStep: ptrStr("3"), + } + if err := th.App.ImportUser(&data, false); err != nil { + t.Fatalf("Should have succeeded.") + } + + // Check their values. + user, err = th.App.GetUserByUsername(username) + if err != nil { + t.Fatalf("Failed to get user from database.") + } + + checkPreference(t, th.App, user.Id, model.PREFERENCE_CATEGORY_THEME, "", *data.Theme) + checkPreference(t, th.App, user.Id, model.PREFERENCE_CATEGORY_DISPLAY_SETTINGS, "use_military_time", *data.UseMilitaryTime) + checkPreference(t, th.App, user.Id, model.PREFERENCE_CATEGORY_DISPLAY_SETTINGS, "collapse_previews", *data.CollapsePreviews) + checkPreference(t, th.App, user.Id, model.PREFERENCE_CATEGORY_DISPLAY_SETTINGS, "message_display", *data.MessageDisplay) + checkPreference(t, th.App, user.Id, model.PREFERENCE_CATEGORY_DISPLAY_SETTINGS, "channel_display_mode", *data.ChannelDisplayMode) + checkPreference(t, th.App, user.Id, model.PREFERENCE_CATEGORY_TUTORIAL_STEPS, user.Id, *data.TutorialStep) + + // Change those preferences. + data = UserImportData{ + Username: &username, + Email: ptrStr(model.NewId() + "@example.com"), + Theme: ptrStr(`{"awayIndicator":"#123456","buttonBg":"#23A2FF","buttonColor":"#FFFFFF","centerChannelBg":"#ffffff","centerChannelColor":"#333333","codeTheme":"github","image":"/static/files/a4a388b38b32678e83823ef1b3e17766.png","linkColor":"#2389d7","mentionBg":"#2389d7","mentionColor":"#ffffff","mentionHighlightBg":"#fff2bb","mentionHighlightLink":"#2f81b7","newMessageSeparator":"#FF8800","onlineIndicator":"#7DBE00","sidebarBg":"#fafafa","sidebarHeaderBg":"#3481B9","sidebarHeaderTextColor":"#ffffff","sidebarText":"#333333","sidebarTextActiveBorder":"#378FD2","sidebarTextActiveColor":"#111111","sidebarTextHoverBg":"#e6f2fa","sidebarUnreadText":"#333333","type":"Mattermost"}`), + UseMilitaryTime: ptrStr("false"), + CollapsePreviews: ptrStr("false"), + MessageDisplay: ptrStr("clean"), + ChannelDisplayMode: ptrStr("full"), + TutorialStep: ptrStr("2"), + } + if err := th.App.ImportUser(&data, false); err != nil { + t.Fatalf("Should have succeeded.") + } + + // Check their values again. + checkPreference(t, th.App, user.Id, model.PREFERENCE_CATEGORY_THEME, "", *data.Theme) + checkPreference(t, th.App, user.Id, model.PREFERENCE_CATEGORY_DISPLAY_SETTINGS, "use_military_time", *data.UseMilitaryTime) + checkPreference(t, th.App, user.Id, model.PREFERENCE_CATEGORY_DISPLAY_SETTINGS, "collapse_previews", *data.CollapsePreviews) + checkPreference(t, th.App, user.Id, model.PREFERENCE_CATEGORY_DISPLAY_SETTINGS, "message_display", *data.MessageDisplay) + checkPreference(t, th.App, user.Id, model.PREFERENCE_CATEGORY_DISPLAY_SETTINGS, "channel_display_mode", *data.ChannelDisplayMode) + checkPreference(t, th.App, user.Id, model.PREFERENCE_CATEGORY_TUTORIAL_STEPS, user.Id, *data.TutorialStep) + + // Set Notify Props + data.NotifyProps = &UserNotifyPropsImportData{ + Desktop: ptrStr(model.USER_NOTIFY_ALL), + DesktopSound: ptrStr("true"), + Email: ptrStr("true"), + Mobile: ptrStr(model.USER_NOTIFY_ALL), + MobilePushStatus: ptrStr(model.STATUS_ONLINE), + ChannelTrigger: ptrStr("true"), + CommentsTrigger: ptrStr(model.COMMENTS_NOTIFY_ROOT), + MentionKeys: ptrStr("valid,misc"), + } + if err := th.App.ImportUser(&data, false); err != nil { + t.Fatalf("Should have succeeded.") + } + + user, err = th.App.GetUserByUsername(username) + if err != nil { + t.Fatalf("Failed to get user from database.") + } + + checkNotifyProp(t, user, model.DESKTOP_NOTIFY_PROP, model.USER_NOTIFY_ALL) + checkNotifyProp(t, user, model.DESKTOP_SOUND_NOTIFY_PROP, "true") + checkNotifyProp(t, user, model.EMAIL_NOTIFY_PROP, "true") + checkNotifyProp(t, user, model.PUSH_NOTIFY_PROP, model.USER_NOTIFY_ALL) + checkNotifyProp(t, user, model.PUSH_STATUS_NOTIFY_PROP, model.STATUS_ONLINE) + checkNotifyProp(t, user, model.CHANNEL_MENTIONS_NOTIFY_PROP, "true") + checkNotifyProp(t, user, model.COMMENTS_NOTIFY_PROP, model.COMMENTS_NOTIFY_ROOT) + checkNotifyProp(t, user, model.MENTION_KEYS_NOTIFY_PROP, "valid,misc") + + // Change Notify Props + data.NotifyProps = &UserNotifyPropsImportData{ + Desktop: ptrStr(model.USER_NOTIFY_MENTION), + DesktopSound: ptrStr("false"), + Email: ptrStr("false"), + Mobile: ptrStr(model.USER_NOTIFY_NONE), + MobilePushStatus: ptrStr(model.STATUS_AWAY), + ChannelTrigger: ptrStr("false"), + CommentsTrigger: ptrStr(model.COMMENTS_NOTIFY_ANY), + MentionKeys: ptrStr("misc"), + } + if err := th.App.ImportUser(&data, false); err != nil { + t.Fatalf("Should have succeeded.") + } + + user, err = th.App.GetUserByUsername(username) + if err != nil { + t.Fatalf("Failed to get user from database.") + } + + checkNotifyProp(t, user, model.DESKTOP_NOTIFY_PROP, model.USER_NOTIFY_MENTION) + checkNotifyProp(t, user, model.DESKTOP_SOUND_NOTIFY_PROP, "false") + checkNotifyProp(t, user, model.EMAIL_NOTIFY_PROP, "false") + checkNotifyProp(t, user, model.PUSH_NOTIFY_PROP, model.USER_NOTIFY_NONE) + checkNotifyProp(t, user, model.PUSH_STATUS_NOTIFY_PROP, model.STATUS_AWAY) + checkNotifyProp(t, user, model.CHANNEL_MENTIONS_NOTIFY_PROP, "false") + checkNotifyProp(t, user, model.COMMENTS_NOTIFY_PROP, model.COMMENTS_NOTIFY_ANY) + checkNotifyProp(t, user, model.MENTION_KEYS_NOTIFY_PROP, "misc") + + // Check Notify Props get set on *create* user. + username = model.NewId() + data = UserImportData{ + Username: &username, + Email: ptrStr(model.NewId() + "@example.com"), + } + data.NotifyProps = &UserNotifyPropsImportData{ + Desktop: ptrStr(model.USER_NOTIFY_MENTION), + DesktopSound: ptrStr("false"), + Email: ptrStr("false"), + Mobile: ptrStr(model.USER_NOTIFY_NONE), + MobilePushStatus: ptrStr(model.STATUS_AWAY), + ChannelTrigger: ptrStr("false"), + CommentsTrigger: ptrStr(model.COMMENTS_NOTIFY_ANY), + MentionKeys: ptrStr("misc"), + } + + if err := th.App.ImportUser(&data, false); err != nil { + t.Fatalf("Should have succeeded.") + } + + user, err = th.App.GetUserByUsername(username) + if err != nil { + t.Fatalf("Failed to get user from database.") + } + + checkNotifyProp(t, user, model.DESKTOP_NOTIFY_PROP, model.USER_NOTIFY_MENTION) + checkNotifyProp(t, user, model.DESKTOP_SOUND_NOTIFY_PROP, "false") + checkNotifyProp(t, user, model.EMAIL_NOTIFY_PROP, "false") + checkNotifyProp(t, user, model.PUSH_NOTIFY_PROP, model.USER_NOTIFY_NONE) + checkNotifyProp(t, user, model.PUSH_STATUS_NOTIFY_PROP, model.STATUS_AWAY) + checkNotifyProp(t, user, model.CHANNEL_MENTIONS_NOTIFY_PROP, "false") + checkNotifyProp(t, user, model.COMMENTS_NOTIFY_PROP, model.COMMENTS_NOTIFY_ANY) + checkNotifyProp(t, user, model.MENTION_KEYS_NOTIFY_PROP, "misc") + + // Test importing a user with roles set to a team and a channel which are affected by an override scheme. + // The import subsystem should translate `channel_admin/channel_user/team_admin/team_user` + // to the appropriate scheme-managed-role booleans. + + // Mark the phase 2 permissions migration as completed. + <-th.App.Srv.Store.System().Save(&model.System{Name: model.MIGRATION_KEY_ADVANCED_PERMISSIONS_PHASE_2, Value: "true"}) + + defer func() { + <-th.App.Srv.Store.System().PermanentDeleteByName(model.MIGRATION_KEY_ADVANCED_PERMISSIONS_PHASE_2) + }() + + teamSchemeData := &SchemeImportData{ + Name: ptrStr(model.NewId()), + DisplayName: ptrStr(model.NewId()), + Scope: ptrStr("team"), + DefaultTeamUserRole: &RoleImportData{ + Name: ptrStr(model.NewId()), + DisplayName: ptrStr(model.NewId()), + }, + DefaultTeamAdminRole: &RoleImportData{ + Name: ptrStr(model.NewId()), + DisplayName: ptrStr(model.NewId()), + }, + DefaultChannelUserRole: &RoleImportData{ + Name: ptrStr(model.NewId()), + DisplayName: ptrStr(model.NewId()), + }, + DefaultChannelAdminRole: &RoleImportData{ + Name: ptrStr(model.NewId()), + DisplayName: ptrStr(model.NewId()), + }, + Description: ptrStr("description"), + } + + if err := th.App.ImportScheme(teamSchemeData, false); err != nil { + t.Fatalf("Should have succeeded.") + } + + var teamScheme *model.Scheme + if res := <-th.App.Srv.Store.Scheme().GetByName(*teamSchemeData.Name); res.Err != nil { + t.Fatalf("Failed to import scheme: %v", res.Err) + } else { + teamScheme = res.Data.(*model.Scheme) + } + + teamData := &TeamImportData{ + Name: ptrStr(model.NewId()), + DisplayName: ptrStr("Display Name"), + Type: ptrStr("O"), + Description: ptrStr("The team description."), + AllowOpenInvite: ptrBool(true), + Scheme: &teamScheme.Name, + } + if err := th.App.ImportTeam(teamData, false); err != nil { + t.Fatalf("Import should have succeeded: %v", err.Error()) + } + team, err = th.App.GetTeamByName(teamName) + if err != nil { + t.Fatalf("Failed to get team from database.") + } + + channelData := &ChannelImportData{ + Team: &teamName, + Name: ptrStr(model.NewId()), + DisplayName: ptrStr("Display Name"), + Type: ptrStr("O"), + Header: ptrStr("Channe Header"), + Purpose: ptrStr("Channel Purpose"), + } + if err := th.App.ImportChannel(channelData, false); err != nil { + t.Fatalf("Import should have succeeded.") + } + channel, err = th.App.GetChannelByName(*channelData.Name, team.Id, false) + if err != nil { + t.Fatalf("Failed to get channel from database: %v", err.Error()) + } + + // Test with a valid team & valid channel name in apply mode. + userData := &UserImportData{ + Username: &username, + Email: ptrStr(model.NewId() + "@example.com"), + Teams: &[]UserTeamImportData{ + { + Name: &team.Name, + Roles: ptrStr("team_user team_admin"), + Channels: &[]UserChannelImportData{ + { + Name: &channel.Name, + Roles: ptrStr("channel_admin channel_user"), + }, + }, + }, + }, + } + if err := th.App.ImportUser(userData, false); err != nil { + t.Fatalf("Should have succeeded.") + } + + user, err = th.App.GetUserByUsername(*userData.Username) + if err != nil { + t.Fatalf("Failed to get user from database.") + } + + teamMember, err := th.App.GetTeamMember(team.Id, user.Id) + if err != nil { + t.Fatalf("Failed to get the team member") + } + assert.True(t, teamMember.SchemeAdmin) + assert.True(t, teamMember.SchemeUser) + assert.Equal(t, "", teamMember.ExplicitRoles) + + channelMember, err := th.App.GetChannelMember(channel.Id, user.Id) + if err != nil { + t.Fatalf("Failed to get the channel member") + } + assert.True(t, channelMember.SchemeAdmin) + assert.True(t, channelMember.SchemeUser) + assert.Equal(t, "", channelMember.ExplicitRoles) + +} + +func TestImportImportPost(t *testing.T) { + th := Setup() + defer th.TearDown() + + // Create a Team. + teamName := model.NewId() + th.App.ImportTeam(&TeamImportData{ + Name: &teamName, + DisplayName: ptrStr("Display Name"), + Type: ptrStr("O"), + }, false) + team, err := th.App.GetTeamByName(teamName) + if err != nil { + t.Fatalf("Failed to get team from database.") + } + + // Create a Channel. + channelName := model.NewId() + th.App.ImportChannel(&ChannelImportData{ + Team: &teamName, + Name: &channelName, + DisplayName: ptrStr("Display Name"), + Type: ptrStr("O"), + }, false) + channel, err := th.App.GetChannelByName(channelName, team.Id, false) + if err != nil { + t.Fatalf("Failed to get channel from database.") + } + + // Create a user. + username := model.NewId() + th.App.ImportUser(&UserImportData{ + Username: &username, + Email: ptrStr(model.NewId() + "@example.com"), + }, false) + user, err := th.App.GetUserByUsername(username) + if err != nil { + t.Fatalf("Failed to get user from database.") + } + + // Count the number of posts in the testing team. + var initialPostCount int64 + if result := <-th.App.Srv.Store.Post().AnalyticsPostCount(team.Id, false, false); result.Err != nil { + t.Fatal(result.Err) + } else { + initialPostCount = result.Data.(int64) + } + + // Try adding an invalid post in dry run mode. + data := &PostImportData{ + Team: &teamName, + Channel: &channelName, + User: &username, + } + if err := th.App.ImportPost(data, true); err == nil { + t.Fatalf("Expected error.") + } + AssertAllPostsCount(t, th.App, initialPostCount, 0, team.Id) + + // Try adding a valid post in dry run mode. + data = &PostImportData{ + Team: &teamName, + Channel: &channelName, + User: &username, + Message: ptrStr("Hello"), + CreateAt: ptrInt64(model.GetMillis()), + } + if err := th.App.ImportPost(data, true); err != nil { + t.Fatalf("Expected success.") + } + AssertAllPostsCount(t, th.App, initialPostCount, 0, team.Id) + + // Try adding an invalid post in apply mode. + data = &PostImportData{ + Team: &teamName, + Channel: &channelName, + User: &username, + CreateAt: ptrInt64(model.GetMillis()), + } + if err := th.App.ImportPost(data, false); err == nil { + t.Fatalf("Expected error.") + } + AssertAllPostsCount(t, th.App, initialPostCount, 0, team.Id) + + // Try adding a valid post with invalid team in apply mode. + data = &PostImportData{ + Team: ptrStr(model.NewId()), + Channel: &channelName, + User: &username, + Message: ptrStr("Message"), + CreateAt: ptrInt64(model.GetMillis()), + } + if err := th.App.ImportPost(data, false); err == nil { + t.Fatalf("Expected error.") + } + AssertAllPostsCount(t, th.App, initialPostCount, 0, team.Id) + + // Try adding a valid post with invalid channel in apply mode. + data = &PostImportData{ + Team: &teamName, + Channel: ptrStr(model.NewId()), + User: &username, + Message: ptrStr("Message"), + CreateAt: ptrInt64(model.GetMillis()), + } + if err := th.App.ImportPost(data, false); err == nil { + t.Fatalf("Expected error.") + } + AssertAllPostsCount(t, th.App, initialPostCount, 0, team.Id) + + // Try adding a valid post with invalid user in apply mode. + data = &PostImportData{ + Team: &teamName, + Channel: &channelName, + User: ptrStr(model.NewId()), + Message: ptrStr("Message"), + CreateAt: ptrInt64(model.GetMillis()), + } + if err := th.App.ImportPost(data, false); err == nil { + t.Fatalf("Expected error.") + } + AssertAllPostsCount(t, th.App, initialPostCount, 0, team.Id) + + // Try adding a valid post in apply mode. + time := model.GetMillis() + data = &PostImportData{ + Team: &teamName, + Channel: &channelName, + User: &username, + Message: ptrStr("Message"), + CreateAt: &time, + } + if err := th.App.ImportPost(data, false); err != nil { + t.Fatalf("Expected success.") + } + AssertAllPostsCount(t, th.App, initialPostCount, 1, team.Id) + + // Check the post values. + if result := <-th.App.Srv.Store.Post().GetPostsCreatedAt(channel.Id, time); result.Err != nil { + t.Fatal(result.Err.Error()) + } else { + posts := result.Data.([]*model.Post) + if len(posts) != 1 { + t.Fatal("Unexpected number of posts found.") + } + post := posts[0] + if post.Message != *data.Message || post.CreateAt != *data.CreateAt || post.UserId != user.Id { + t.Fatal("Post properties not as expected") + } + } + + // Update the post. + data = &PostImportData{ + Team: &teamName, + Channel: &channelName, + User: &username, + Message: ptrStr("Message"), + CreateAt: &time, + } + if err := th.App.ImportPost(data, false); err != nil { + t.Fatalf("Expected success.") + } + AssertAllPostsCount(t, th.App, initialPostCount, 1, team.Id) + + // Check the post values. + if result := <-th.App.Srv.Store.Post().GetPostsCreatedAt(channel.Id, time); result.Err != nil { + t.Fatal(result.Err.Error()) + } else { + posts := result.Data.([]*model.Post) + if len(posts) != 1 { + t.Fatal("Unexpected number of posts found.") + } + post := posts[0] + if post.Message != *data.Message || post.CreateAt != *data.CreateAt || post.UserId != user.Id { + t.Fatal("Post properties not as expected") + } + } + + // Save the post with a different time. + newTime := time + 1 + data = &PostImportData{ + Team: &teamName, + Channel: &channelName, + User: &username, + Message: ptrStr("Message"), + CreateAt: &newTime, + } + if err := th.App.ImportPost(data, false); err != nil { + t.Fatalf("Expected success.") + } + AssertAllPostsCount(t, th.App, initialPostCount, 2, team.Id) + + // Save the post with a different message. + data = &PostImportData{ + Team: &teamName, + Channel: &channelName, + User: &username, + Message: ptrStr("Message 2"), + CreateAt: &time, + } + if err := th.App.ImportPost(data, false); err != nil { + t.Fatalf("Expected success.") + } + AssertAllPostsCount(t, th.App, initialPostCount, 3, team.Id) + + // Test with hashtags + hashtagTime := time + 2 + data = &PostImportData{ + Team: &teamName, + Channel: &channelName, + User: &username, + Message: ptrStr("Message 2 #hashtagmashupcity"), + CreateAt: &hashtagTime, + } + if err := th.App.ImportPost(data, false); err != nil { + t.Fatalf("Expected success.") + } + AssertAllPostsCount(t, th.App, initialPostCount, 4, team.Id) + + if result := <-th.App.Srv.Store.Post().GetPostsCreatedAt(channel.Id, hashtagTime); result.Err != nil { + t.Fatal(result.Err.Error()) + } else { + posts := result.Data.([]*model.Post) + if len(posts) != 1 { + t.Fatal("Unexpected number of posts found.") + } + post := posts[0] + if post.Message != *data.Message || post.CreateAt != *data.CreateAt || post.UserId != user.Id { + t.Fatal("Post properties not as expected") + } + if post.Hashtags != "#hashtagmashupcity" { + t.Fatalf("Hashtags not as expected: %s", post.Hashtags) + } + } + + // Post with flags. + username2 := model.NewId() + th.App.ImportUser(&UserImportData{ + Username: &username2, + Email: ptrStr(model.NewId() + "@example.com"), + }, false) + user2, err := th.App.GetUserByUsername(username2) + if err != nil { + t.Fatalf("Failed to get user from database.") + } + + flagsTime := hashtagTime + 1 + data = &PostImportData{ + Team: &teamName, + Channel: &channelName, + User: &username, + Message: ptrStr("Message with Favorites"), + CreateAt: &flagsTime, + FlaggedBy: &[]string{ + username, + username2, + }, + } + if err := th.App.ImportPost(data, false); err != nil { + t.Fatalf("Expected success.") + } + AssertAllPostsCount(t, th.App, initialPostCount, 5, team.Id) + + // Check the post values. + if result := <-th.App.Srv.Store.Post().GetPostsCreatedAt(channel.Id, flagsTime); result.Err != nil { + t.Fatal(result.Err.Error()) + } else { + posts := result.Data.([]*model.Post) + if len(posts) != 1 { + t.Fatal("Unexpected number of posts found.") + } + post := posts[0] + if post.Message != *data.Message || post.CreateAt != *data.CreateAt || post.UserId != user.Id { + t.Fatal("Post properties not as expected") + } + + checkPreference(t, th.App, user.Id, model.PREFERENCE_CATEGORY_FLAGGED_POST, post.Id, "true") + checkPreference(t, th.App, user2.Id, model.PREFERENCE_CATEGORY_FLAGGED_POST, post.Id, "true") + } + + // Post with reaction. + reactionPostTime := hashtagTime + 2 + reactionTime := hashtagTime + 3 + data = &PostImportData{ + Team: &teamName, + Channel: &channelName, + User: &username, + Message: ptrStr("Message with reaction"), + CreateAt: &reactionPostTime, + Reactions: &[]ReactionImportData{{ + User: &user2.Username, + EmojiName: ptrStr("+1"), + CreateAt: &reactionTime, + }}, + } + if err := th.App.ImportPost(data, false); err != nil { + t.Fatalf("Expected success.") + } + AssertAllPostsCount(t, th.App, initialPostCount, 6, team.Id) + + // Check the post values. + if result := <-th.App.Srv.Store.Post().GetPostsCreatedAt(channel.Id, reactionPostTime); result.Err != nil { + t.Fatal(result.Err.Error()) + } else { + posts := result.Data.([]*model.Post) + if len(posts) != 1 { + t.Fatal("Unexpected number of posts found.") + } + post := posts[0] + if post.Message != *data.Message || post.CreateAt != *data.CreateAt || post.UserId != user.Id || !post.HasReactions { + t.Fatal("Post properties not as expected") + } + + if result := <-th.App.Srv.Store.Reaction().GetForPost(post.Id, false); result.Err != nil { + t.Fatal("Can't get reaction") + } else if len(result.Data.([]*model.Reaction)) != 1 { + t.Fatal("Invalid number of reactions") + } + } + + // Post with reply. + replyPostTime := hashtagTime + 4 + replyTime := hashtagTime + 5 + data = &PostImportData{ + Team: &teamName, + Channel: &channelName, + User: &username, + Message: ptrStr("Message with reply"), + CreateAt: &replyPostTime, + Replies: &[]ReplyImportData{{ + User: &user2.Username, + Message: ptrStr("Message reply"), + CreateAt: &replyTime, + }}, + } + if err := th.App.ImportPost(data, false); err != nil { + t.Fatalf("Expected success.") + } + AssertAllPostsCount(t, th.App, initialPostCount, 8, team.Id) + + // Check the post values. + if result := <-th.App.Srv.Store.Post().GetPostsCreatedAt(channel.Id, replyPostTime); result.Err != nil { + t.Fatal(result.Err.Error()) + } else { + posts := result.Data.([]*model.Post) + if len(posts) != 1 { + t.Fatal("Unexpected number of posts found.") + } + post := posts[0] + if post.Message != *data.Message || post.CreateAt != *data.CreateAt || post.UserId != user.Id { + t.Fatal("Post properties not as expected") + } + + // Check the reply values. + if result := <-th.App.Srv.Store.Post().GetPostsCreatedAt(channel.Id, replyTime); result.Err != nil { + t.Fatal(result.Err.Error()) + } else { + replies := result.Data.([]*model.Post) + if len(replies) != 1 { + t.Fatal("Unexpected number of posts found.") + } + reply := replies[0] + if reply.Message != *(*data.Replies)[0].Message || reply.CreateAt != *(*data.Replies)[0].CreateAt || reply.UserId != user2.Id { + t.Fatal("Post properties not as expected") + } + + if reply.RootId != post.Id { + t.Fatal("Unexpected reply RootId") + } + } + } +} + +func TestImportImportDirectChannel(t *testing.T) { + th := Setup().InitBasic() + defer th.TearDown() + + // Check how many channels are in the database. + var directChannelCount int64 + if r := <-th.App.Srv.Store.Channel().AnalyticsTypeCount("", model.CHANNEL_DIRECT); r.Err == nil { + directChannelCount = r.Data.(int64) + } else { + t.Fatalf("Failed to get direct channel count.") + } + + var groupChannelCount int64 + if r := <-th.App.Srv.Store.Channel().AnalyticsTypeCount("", model.CHANNEL_GROUP); r.Err == nil { + groupChannelCount = r.Data.(int64) + } else { + t.Fatalf("Failed to get group channel count.") + } + + // Do an invalid channel in dry-run mode. + data := DirectChannelImportData{ + Members: &[]string{ + model.NewId(), + }, + Header: ptrStr("Channel Header"), + } + if err := th.App.ImportDirectChannel(&data, true); err == nil { + t.Fatalf("Expected error due to invalid name.") + } + + // Check that no more channels are in the DB. + AssertChannelCount(t, th.App, model.CHANNEL_DIRECT, directChannelCount) + AssertChannelCount(t, th.App, model.CHANNEL_GROUP, groupChannelCount) + + // Do a valid DIRECT channel with a nonexistent member in dry-run mode. + data.Members = &[]string{ + model.NewId(), + model.NewId(), + } + if err := th.App.ImportDirectChannel(&data, true); err != nil { + t.Fatalf("Expected success as cannot validate existence of channel members in dry run mode.") + } + + // Check that no more channels are in the DB. + AssertChannelCount(t, th.App, model.CHANNEL_DIRECT, directChannelCount) + AssertChannelCount(t, th.App, model.CHANNEL_GROUP, groupChannelCount) + + // Do a valid GROUP channel with a nonexistent member in dry-run mode. + data.Members = &[]string{ + model.NewId(), + model.NewId(), + model.NewId(), + } + if err := th.App.ImportDirectChannel(&data, true); err != nil { + t.Fatalf("Expected success as cannot validate existence of channel members in dry run mode.") + } + + // Check that no more channels are in the DB. + AssertChannelCount(t, th.App, model.CHANNEL_DIRECT, directChannelCount) + AssertChannelCount(t, th.App, model.CHANNEL_GROUP, groupChannelCount) + + // Do an invalid channel in apply mode. + data.Members = &[]string{ + model.NewId(), + } + if err := th.App.ImportDirectChannel(&data, false); err == nil { + t.Fatalf("Expected error due to invalid member (apply mode).") + } + + // Check that no more channels are in the DB. + AssertChannelCount(t, th.App, model.CHANNEL_DIRECT, directChannelCount) + AssertChannelCount(t, th.App, model.CHANNEL_GROUP, groupChannelCount) + + // Do a valid DIRECT channel. + data.Members = &[]string{ + th.BasicUser.Username, + th.BasicUser2.Username, + } + if err := th.App.ImportDirectChannel(&data, false); err != nil { + t.Fatalf("Expected success: %v", err.Error()) + } + + // Check that one more DIRECT channel is in the DB. + AssertChannelCount(t, th.App, model.CHANNEL_DIRECT, directChannelCount+1) + AssertChannelCount(t, th.App, model.CHANNEL_GROUP, groupChannelCount) + + // Do the same DIRECT channel again. + if err := th.App.ImportDirectChannel(&data, false); err != nil { + t.Fatalf("Expected success.") + } + + // Check that no more channels are in the DB. + AssertChannelCount(t, th.App, model.CHANNEL_DIRECT, directChannelCount+1) + AssertChannelCount(t, th.App, model.CHANNEL_GROUP, groupChannelCount) + + // Update the channel's HEADER + data.Header = ptrStr("New Channel Header 2") + if err := th.App.ImportDirectChannel(&data, false); err != nil { + t.Fatalf("Expected success.") + } + + // Check that no more channels are in the DB. + AssertChannelCount(t, th.App, model.CHANNEL_DIRECT, directChannelCount+1) + AssertChannelCount(t, th.App, model.CHANNEL_GROUP, groupChannelCount) + + // Get the channel to check that the header was updated. + if channel, err := th.App.createDirectChannel(th.BasicUser.Id, th.BasicUser2.Id); err == nil || err.Id != store.CHANNEL_EXISTS_ERROR { + t.Fatal("Should have got store.CHANNEL_EXISTS_ERROR") + } else { + if channel.Header != *data.Header { + t.Fatal("Channel header has not been updated successfully.") + } + } + + // Do a GROUP channel with an extra invalid member. + user3 := th.CreateUser() + data.Members = &[]string{ + th.BasicUser.Username, + th.BasicUser2.Username, + user3.Username, + model.NewId(), + } + if err := th.App.ImportDirectChannel(&data, false); err == nil { + t.Fatalf("Should have failed due to invalid member in list.") + } + + // Check that no more channels are in the DB. + AssertChannelCount(t, th.App, model.CHANNEL_DIRECT, directChannelCount+1) + AssertChannelCount(t, th.App, model.CHANNEL_GROUP, groupChannelCount) + + // Do a valid GROUP channel. + data.Members = &[]string{ + th.BasicUser.Username, + th.BasicUser2.Username, + user3.Username, + } + if err := th.App.ImportDirectChannel(&data, false); err != nil { + t.Fatalf("Expected success.") + } + + // Check that one more GROUP channel is in the DB. + AssertChannelCount(t, th.App, model.CHANNEL_DIRECT, directChannelCount+1) + AssertChannelCount(t, th.App, model.CHANNEL_GROUP, groupChannelCount+1) + + // Do the same DIRECT channel again. + if err := th.App.ImportDirectChannel(&data, false); err != nil { + t.Fatalf("Expected success.") + } + + // Check that no more channels are in the DB. + AssertChannelCount(t, th.App, model.CHANNEL_DIRECT, directChannelCount+1) + AssertChannelCount(t, th.App, model.CHANNEL_GROUP, groupChannelCount+1) + + // Update the channel's HEADER + data.Header = ptrStr("New Channel Header 3") + if err := th.App.ImportDirectChannel(&data, false); err != nil { + t.Fatalf("Expected success.") + } + + // Check that no more channels are in the DB. + AssertChannelCount(t, th.App, model.CHANNEL_DIRECT, directChannelCount+1) + AssertChannelCount(t, th.App, model.CHANNEL_GROUP, groupChannelCount+1) + + // Get the channel to check that the header was updated. + userIds := []string{ + th.BasicUser.Id, + th.BasicUser2.Id, + user3.Id, + } + if channel, err := th.App.createGroupChannel(userIds, th.BasicUser.Id); err.Id != store.CHANNEL_EXISTS_ERROR { + t.Fatal("Should have got store.CHANNEL_EXISTS_ERROR") + } else { + if channel.Header != *data.Header { + t.Fatal("Channel header has not been updated successfully.") + } + } + + // Import a channel with some favorites. + data.Members = &[]string{ + th.BasicUser.Username, + th.BasicUser2.Username, + } + data.FavoritedBy = &[]string{ + th.BasicUser.Username, + th.BasicUser2.Username, + } + if err := th.App.ImportDirectChannel(&data, false); err != nil { + t.Fatal(err) + } + + if channel, err := th.App.createDirectChannel(th.BasicUser.Id, th.BasicUser2.Id); err == nil || err.Id != store.CHANNEL_EXISTS_ERROR { + t.Fatal("Should have got store.CHANNEL_EXISTS_ERROR") + } else { + checkPreference(t, th.App, th.BasicUser.Id, model.PREFERENCE_CATEGORY_FAVORITE_CHANNEL, channel.Id, "true") + checkPreference(t, th.App, th.BasicUser2.Id, model.PREFERENCE_CATEGORY_FAVORITE_CHANNEL, channel.Id, "true") + } +} + +func TestImportImportDirectPost(t *testing.T) { + th := Setup().InitBasic() + defer th.TearDown() + + // Create the DIRECT channel. + channelData := DirectChannelImportData{ + Members: &[]string{ + th.BasicUser.Username, + th.BasicUser2.Username, + }, + } + if err := th.App.ImportDirectChannel(&channelData, false); err != nil { + t.Fatalf("Expected success: %v", err.Error()) + } + + // Get the channel. + var directChannel *model.Channel + if channel, err := th.App.createDirectChannel(th.BasicUser.Id, th.BasicUser2.Id); err.Id != store.CHANNEL_EXISTS_ERROR { + t.Fatal("Should have got store.CHANNEL_EXISTS_ERROR") + } else { + directChannel = channel + } + + // Get the number of posts in the system. + var initialPostCount int64 + if result := <-th.App.Srv.Store.Post().AnalyticsPostCount("", false, false); result.Err != nil { + t.Fatal(result.Err) + } else { + initialPostCount = result.Data.(int64) + } + + // Try adding an invalid post in dry run mode. + data := &DirectPostImportData{ + ChannelMembers: &[]string{ + th.BasicUser.Username, + th.BasicUser2.Username, + }, + User: ptrStr(th.BasicUser.Username), + CreateAt: ptrInt64(model.GetMillis()), + } + if err := th.App.ImportDirectPost(data, true); err == nil { + t.Fatalf("Expected error.") + } + AssertAllPostsCount(t, th.App, initialPostCount, 0, "") + + // Try adding a valid post in dry run mode. + data = &DirectPostImportData{ + ChannelMembers: &[]string{ + th.BasicUser.Username, + th.BasicUser2.Username, + }, + User: ptrStr(th.BasicUser.Username), + Message: ptrStr("Message"), + CreateAt: ptrInt64(model.GetMillis()), + } + if err := th.App.ImportDirectPost(data, true); err != nil { + t.Fatalf("Expected success.") + } + AssertAllPostsCount(t, th.App, initialPostCount, 0, "") + + // Try adding an invalid post in apply mode. + data = &DirectPostImportData{ + ChannelMembers: &[]string{ + th.BasicUser.Username, + model.NewId(), + }, + User: ptrStr(th.BasicUser.Username), + Message: ptrStr("Message"), + CreateAt: ptrInt64(model.GetMillis()), + } + if err := th.App.ImportDirectPost(data, false); err == nil { + t.Fatalf("Expected error.") + } + AssertAllPostsCount(t, th.App, initialPostCount, 0, "") + + // Try adding a valid post in apply mode. + data = &DirectPostImportData{ + ChannelMembers: &[]string{ + th.BasicUser.Username, + th.BasicUser2.Username, + }, + User: ptrStr(th.BasicUser.Username), + Message: ptrStr("Message"), + CreateAt: ptrInt64(model.GetMillis()), + } + if err := th.App.ImportDirectPost(data, false); err != nil { + t.Fatalf("Expected success: %v", err.Error()) + } + AssertAllPostsCount(t, th.App, initialPostCount, 1, "") + + // Check the post values. + if result := <-th.App.Srv.Store.Post().GetPostsCreatedAt(directChannel.Id, *data.CreateAt); result.Err != nil { + t.Fatal(result.Err.Error()) + } else { + posts := result.Data.([]*model.Post) + if len(posts) != 1 { + t.Fatal("Unexpected number of posts found.") + } + post := posts[0] + if post.Message != *data.Message || post.CreateAt != *data.CreateAt || post.UserId != th.BasicUser.Id { + t.Fatal("Post properties not as expected") + } + } + + // Import the post again. + if err := th.App.ImportDirectPost(data, false); err != nil { + t.Fatalf("Expected success.") + } + AssertAllPostsCount(t, th.App, initialPostCount, 1, "") + + // Check the post values. + if result := <-th.App.Srv.Store.Post().GetPostsCreatedAt(directChannel.Id, *data.CreateAt); result.Err != nil { + t.Fatal(result.Err.Error()) + } else { + posts := result.Data.([]*model.Post) + if len(posts) != 1 { + t.Fatal("Unexpected number of posts found.") + } + post := posts[0] + if post.Message != *data.Message || post.CreateAt != *data.CreateAt || post.UserId != th.BasicUser.Id { + t.Fatal("Post properties not as expected") + } + } + + // Save the post with a different time. + data.CreateAt = ptrInt64(*data.CreateAt + 1) + if err := th.App.ImportDirectPost(data, false); err != nil { + t.Fatalf("Expected success.") + } + AssertAllPostsCount(t, th.App, initialPostCount, 2, "") + + // Save the post with a different message. + data.Message = ptrStr("Message 2") + if err := th.App.ImportDirectPost(data, false); err != nil { + t.Fatalf("Expected success.") + } + AssertAllPostsCount(t, th.App, initialPostCount, 3, "") + + // Test with hashtags + data.Message = ptrStr("Message 2 #hashtagmashupcity") + data.CreateAt = ptrInt64(*data.CreateAt + 1) + if err := th.App.ImportDirectPost(data, false); err != nil { + t.Fatalf("Expected success.") + } + AssertAllPostsCount(t, th.App, initialPostCount, 4, "") + + if result := <-th.App.Srv.Store.Post().GetPostsCreatedAt(directChannel.Id, *data.CreateAt); result.Err != nil { + t.Fatal(result.Err.Error()) + } else { + posts := result.Data.([]*model.Post) + if len(posts) != 1 { + t.Fatal("Unexpected number of posts found.") + } + post := posts[0] + if post.Message != *data.Message || post.CreateAt != *data.CreateAt || post.UserId != th.BasicUser.Id { + t.Fatal("Post properties not as expected") + } + if post.Hashtags != "#hashtagmashupcity" { + t.Fatalf("Hashtags not as expected: %s", post.Hashtags) + } + } + + // Test with some flags. + data = &DirectPostImportData{ + ChannelMembers: &[]string{ + th.BasicUser.Username, + th.BasicUser2.Username, + }, + FlaggedBy: &[]string{ + th.BasicUser.Username, + th.BasicUser2.Username, + }, + User: ptrStr(th.BasicUser.Username), + Message: ptrStr("Message"), + CreateAt: ptrInt64(model.GetMillis()), + } + + if err := th.App.ImportDirectPost(data, false); err != nil { + t.Fatalf("Expected success: %v", err.Error()) + } + + // Check the post values. + if result := <-th.App.Srv.Store.Post().GetPostsCreatedAt(directChannel.Id, *data.CreateAt); result.Err != nil { + t.Fatal(result.Err.Error()) + } else { + posts := result.Data.([]*model.Post) + if len(posts) != 1 { + t.Fatal("Unexpected number of posts found.") + } + post := posts[0] + checkPreference(t, th.App, th.BasicUser.Id, model.PREFERENCE_CATEGORY_FLAGGED_POST, post.Id, "true") + checkPreference(t, th.App, th.BasicUser2.Id, model.PREFERENCE_CATEGORY_FLAGGED_POST, post.Id, "true") + } + + // ------------------ Group Channel ------------------------- + + // Create the GROUP channel. + user3 := th.CreateUser() + channelData = DirectChannelImportData{ + Members: &[]string{ + th.BasicUser.Username, + th.BasicUser2.Username, + user3.Username, + }, + } + if err := th.App.ImportDirectChannel(&channelData, false); err != nil { + t.Fatalf("Expected success: %v", err.Error()) + } + + // Get the channel. + var groupChannel *model.Channel + userIds := []string{ + th.BasicUser.Id, + th.BasicUser2.Id, + user3.Id, + } + if channel, err := th.App.createGroupChannel(userIds, th.BasicUser.Id); err.Id != store.CHANNEL_EXISTS_ERROR { + t.Fatal("Should have got store.CHANNEL_EXISTS_ERROR") + } else { + groupChannel = channel + } + + // Get the number of posts in the system. + if result := <-th.App.Srv.Store.Post().AnalyticsPostCount("", false, false); result.Err != nil { + t.Fatal(result.Err) + } else { + initialPostCount = result.Data.(int64) + } + + // Try adding an invalid post in dry run mode. + data = &DirectPostImportData{ + ChannelMembers: &[]string{ + th.BasicUser.Username, + th.BasicUser2.Username, + user3.Username, + }, + User: ptrStr(th.BasicUser.Username), + CreateAt: ptrInt64(model.GetMillis()), + } + if err := th.App.ImportDirectPost(data, true); err == nil { + t.Fatalf("Expected error.") + } + AssertAllPostsCount(t, th.App, initialPostCount, 0, "") + + // Try adding a valid post in dry run mode. + data = &DirectPostImportData{ + ChannelMembers: &[]string{ + th.BasicUser.Username, + th.BasicUser2.Username, + user3.Username, + }, + User: ptrStr(th.BasicUser.Username), + Message: ptrStr("Message"), + CreateAt: ptrInt64(model.GetMillis()), + } + if err := th.App.ImportDirectPost(data, true); err != nil { + t.Fatalf("Expected success.") + } + AssertAllPostsCount(t, th.App, initialPostCount, 0, "") + + // Try adding an invalid post in apply mode. + data = &DirectPostImportData{ + ChannelMembers: &[]string{ + th.BasicUser.Username, + th.BasicUser2.Username, + user3.Username, + model.NewId(), + }, + User: ptrStr(th.BasicUser.Username), + Message: ptrStr("Message"), + CreateAt: ptrInt64(model.GetMillis()), + } + if err := th.App.ImportDirectPost(data, false); err == nil { + t.Fatalf("Expected error.") + } + AssertAllPostsCount(t, th.App, initialPostCount, 0, "") + + // Try adding a valid post in apply mode. + data = &DirectPostImportData{ + ChannelMembers: &[]string{ + th.BasicUser.Username, + th.BasicUser2.Username, + user3.Username, + }, + User: ptrStr(th.BasicUser.Username), + Message: ptrStr("Message"), + CreateAt: ptrInt64(model.GetMillis()), + } + if err := th.App.ImportDirectPost(data, false); err != nil { + t.Fatalf("Expected success: %v", err.Error()) + } + AssertAllPostsCount(t, th.App, initialPostCount, 1, "") + + // Check the post values. + if result := <-th.App.Srv.Store.Post().GetPostsCreatedAt(groupChannel.Id, *data.CreateAt); result.Err != nil { + t.Fatal(result.Err.Error()) + } else { + posts := result.Data.([]*model.Post) + if len(posts) != 1 { + t.Fatal("Unexpected number of posts found.") + } + post := posts[0] + if post.Message != *data.Message || post.CreateAt != *data.CreateAt || post.UserId != th.BasicUser.Id { + t.Fatal("Post properties not as expected") + } + } + + // Import the post again. + if err := th.App.ImportDirectPost(data, false); err != nil { + t.Fatalf("Expected success.") + } + AssertAllPostsCount(t, th.App, initialPostCount, 1, "") + + // Check the post values. + if result := <-th.App.Srv.Store.Post().GetPostsCreatedAt(groupChannel.Id, *data.CreateAt); result.Err != nil { + t.Fatal(result.Err.Error()) + } else { + posts := result.Data.([]*model.Post) + if len(posts) != 1 { + t.Fatal("Unexpected number of posts found.") + } + post := posts[0] + if post.Message != *data.Message || post.CreateAt != *data.CreateAt || post.UserId != th.BasicUser.Id { + t.Fatal("Post properties not as expected") + } + } + + // Save the post with a different time. + data.CreateAt = ptrInt64(*data.CreateAt + 1) + if err := th.App.ImportDirectPost(data, false); err != nil { + t.Fatalf("Expected success.") + } + AssertAllPostsCount(t, th.App, initialPostCount, 2, "") + + // Save the post with a different message. + data.Message = ptrStr("Message 2") + if err := th.App.ImportDirectPost(data, false); err != nil { + t.Fatalf("Expected success.") + } + AssertAllPostsCount(t, th.App, initialPostCount, 3, "") + + // Test with hashtags + data.Message = ptrStr("Message 2 #hashtagmashupcity") + data.CreateAt = ptrInt64(*data.CreateAt + 1) + if err := th.App.ImportDirectPost(data, false); err != nil { + t.Fatalf("Expected success.") + } + AssertAllPostsCount(t, th.App, initialPostCount, 4, "") + + if result := <-th.App.Srv.Store.Post().GetPostsCreatedAt(groupChannel.Id, *data.CreateAt); result.Err != nil { + t.Fatal(result.Err.Error()) + } else { + posts := result.Data.([]*model.Post) + if len(posts) != 1 { + t.Fatal("Unexpected number of posts found.") + } + post := posts[0] + if post.Message != *data.Message || post.CreateAt != *data.CreateAt || post.UserId != th.BasicUser.Id { + t.Fatal("Post properties not as expected") + } + if post.Hashtags != "#hashtagmashupcity" { + t.Fatalf("Hashtags not as expected: %s", post.Hashtags) + } + } + + // Test with some flags. + data = &DirectPostImportData{ + ChannelMembers: &[]string{ + th.BasicUser.Username, + th.BasicUser2.Username, + user3.Username, + }, + FlaggedBy: &[]string{ + th.BasicUser.Username, + th.BasicUser2.Username, + }, + User: ptrStr(th.BasicUser.Username), + Message: ptrStr("Message"), + CreateAt: ptrInt64(model.GetMillis()), + } + + if err := th.App.ImportDirectPost(data, false); err != nil { + t.Fatalf("Expected success: %v", err.Error()) + } + + // Check the post values. + if result := <-th.App.Srv.Store.Post().GetPostsCreatedAt(groupChannel.Id, *data.CreateAt); result.Err != nil { + t.Fatal(result.Err.Error()) + } else { + posts := result.Data.([]*model.Post) + if len(posts) != 1 { + t.Fatal("Unexpected number of posts found.") + } + post := posts[0] + checkPreference(t, th.App, th.BasicUser.Id, model.PREFERENCE_CATEGORY_FLAGGED_POST, post.Id, "true") + checkPreference(t, th.App, th.BasicUser2.Id, model.PREFERENCE_CATEGORY_FLAGGED_POST, post.Id, "true") + } +} + +func TestImportImportEmoji(t *testing.T) { + th := Setup() + defer th.TearDown() + + th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.EnableCustomEmoji = true }) + + testsDir, _ := utils.FindDir("tests") + testImage := filepath.Join(testsDir, "test.png") + + data := EmojiImportData{Name: ptrStr(model.NewId())} + err := th.App.ImportEmoji(&data, true) + assert.NotNil(t, err, "Invalid emoji should have failed dry run") + + result := <-th.App.Srv.Store.Emoji().GetByName(*data.Name) + assert.Nil(t, result.Data, "Emoji should not have been imported") + + data.Image = ptrStr(testImage) + err = th.App.ImportEmoji(&data, true) + assert.Nil(t, err, "Valid emoji should have passed dry run") + + data = EmojiImportData{Name: ptrStr(model.NewId())} + err = th.App.ImportEmoji(&data, false) + assert.NotNil(t, err, "Invalid emoji should have failed apply mode") + + data.Image = ptrStr("non-existent-file") + err = th.App.ImportEmoji(&data, false) + assert.NotNil(t, err, "Emoji with bad image file should have failed apply mode") + + data.Image = ptrStr(testImage) + err = th.App.ImportEmoji(&data, false) + assert.Nil(t, err, "Valid emoji should have succeeded apply mode") + + result = <-th.App.Srv.Store.Emoji().GetByName(*data.Name) + assert.NotNil(t, result.Data, "Emoji should have been imported") + + err = th.App.ImportEmoji(&data, false) + assert.Nil(t, err, "Second run should have succeeded apply mode") +} + +func TestImportAttachment(t *testing.T) { + th := Setup() + defer th.TearDown() + + testsDir, _ := utils.FindDir("tests") + testImage := filepath.Join(testsDir, "test.png") + invalidPath := "some-invalid-path" + + userId := model.NewId() + data := AttachmentImportData{Path: &testImage} + _, err := th.App.ImportAttachment(&data, &model.Post{UserId: userId, ChannelId: "some-channel"}, "some-team", true) + assert.Nil(t, err, "sample run without errors") + + attachments := GetAttachments(userId, th, t) + assert.Equal(t, len(attachments), 1) + + data = AttachmentImportData{Path: &invalidPath} + _, err = th.App.ImportAttachment(&data, &model.Post{UserId: model.NewId(), ChannelId: "some-channel"}, "some-team", true) + assert.NotNil(t, err, "should have failed when opening the file") + assert.Equal(t, err.Id, "app.import.attachment.bad_file.error") +} + +func TestImportPostAndRepliesWithAttachments(t *testing.T) { + + th := Setup() + defer th.TearDown() + + // Create a Team. + teamName := model.NewId() + th.App.ImportTeam(&TeamImportData{ + Name: &teamName, + DisplayName: ptrStr("Display Name"), + Type: ptrStr("O"), + }, false) + team, err := th.App.GetTeamByName(teamName) + if err != nil { + t.Fatalf("Failed to get team from database.") + } + + // Create a Channel. + channelName := model.NewId() + th.App.ImportChannel(&ChannelImportData{ + Team: &teamName, + Name: &channelName, + DisplayName: ptrStr("Display Name"), + Type: ptrStr("O"), + }, false) + _, err = th.App.GetChannelByName(channelName, team.Id, false) + if err != nil { + t.Fatalf("Failed to get channel from database.") + } + + // Create a user3. + username := model.NewId() + th.App.ImportUser(&UserImportData{ + Username: &username, + Email: ptrStr(model.NewId() + "@example.com"), + }, false) + user3, err := th.App.GetUserByUsername(username) + if err != nil { + t.Fatalf("Failed to get user3 from database.") + } + + username2 := model.NewId() + th.App.ImportUser(&UserImportData{ + Username: &username2, + Email: ptrStr(model.NewId() + "@example.com"), + }, false) + user4, err := th.App.GetUserByUsername(username2) + if err != nil { + t.Fatalf("Failed to get user3 from database.") + } + + // Post with attachments. + time := model.GetMillis() + attachmentsPostTime := time + attachmentsReplyTime := time + 1 + testsDir, _ := utils.FindDir("tests") + testImage := filepath.Join(testsDir, "test.png") + testMarkDown := filepath.Join(testsDir, "test-attachments.md") + data := &PostImportData{ + Team: &teamName, + Channel: &channelName, + User: &username, + Message: ptrStr("Message with reply"), + CreateAt: &attachmentsPostTime, + Attachments: &[]AttachmentImportData{{Path: &testImage}, {Path: &testMarkDown}}, + Replies: &[]ReplyImportData{{ + User: &user4.Username, + Message: ptrStr("Message reply"), + CreateAt: &attachmentsReplyTime, + Attachments: &[]AttachmentImportData{{Path: &testImage}}, + }}, + } + + if err := th.App.ImportPost(data, false); err != nil { + t.Fatalf("Expected success.") + } + + attachments := GetAttachments(user3.Id, th, t) + assert.Equal(t, len(attachments), 2) + assert.Contains(t, attachments[0].Path, team.Id) + assert.Contains(t, attachments[1].Path, team.Id) + AssertFileIdsInPost(attachments, th, t) + + attachments = GetAttachments(user4.Id, th, t) + assert.Equal(t, len(attachments), 1) + assert.Contains(t, attachments[0].Path, team.Id) + AssertFileIdsInPost(attachments, th, t) + + // Reply with Attachments in Direct Post + + // Create direct post users. + + username3 := model.NewId() + th.App.ImportUser(&UserImportData{ + Username: &username3, + Email: ptrStr(model.NewId() + "@example.com"), + }, false) + user3, err = th.App.GetUserByUsername(username3) + if err != nil { + t.Fatalf("Failed to get user3 from database.") + } + + username4 := model.NewId() + th.App.ImportUser(&UserImportData{ + Username: &username4, + Email: ptrStr(model.NewId() + "@example.com"), + }, false) + + user4, err = th.App.GetUserByUsername(username4) + if err != nil { + t.Fatalf("Failed to get user3 from database.") + } + + directImportData := &DirectPostImportData{ + ChannelMembers: &[]string{ + user3.Username, + user4.Username, + }, + User: &user3.Username, + Message: ptrStr("Message with Replies"), + CreateAt: ptrInt64(model.GetMillis()), + Replies: &[]ReplyImportData{{ + User: &user4.Username, + Message: ptrStr("Message reply with attachment"), + CreateAt: ptrInt64(model.GetMillis()), + Attachments: &[]AttachmentImportData{{Path: &testImage}}, + }}, + } + + if err := th.App.ImportDirectPost(directImportData, false); err != nil { + t.Fatalf("Expected success.") + } + + attachments = GetAttachments(user4.Id, th, t) + assert.Equal(t, len(attachments), 1) + assert.Contains(t, attachments[0].Path, "noteam") + AssertFileIdsInPost(attachments, th, t) + +} -- cgit v1.2.3-1-g7c22