mattermost-community-enterp.../channels/app/support_packet_test.go
Claude ec1f89217a Merge: Complete Mattermost Server with Community Enterprise
Full Mattermost server source with integrated Community Enterprise features.
Includes vendor directory for offline/air-gapped builds.

Structure:
- enterprise-impl/: Enterprise feature implementations
- enterprise-community/: Init files that register implementations
- enterprise/: Bridge imports (community_imports.go)
- vendor/: All dependencies for offline builds

Build (online):
  go build ./cmd/mattermost

Build (offline/air-gapped):
  go build -mod=vendor ./cmd/mattermost

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-17 23:59:07 +09:00

778 lines
24 KiB
Go

// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
package app
import (
"bytes"
"encoding/json"
"errors"
"os"
"path/filepath"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"gopkg.in/yaml.v3"
"github.com/mattermost/mattermost/server/public/model"
smocks "github.com/mattermost/mattermost/server/v8/channels/store/storetest/mocks"
"github.com/mattermost/mattermost/server/v8/channels/utils/fileutils"
"github.com/mattermost/mattermost/server/v8/config"
)
func TestGenerateSupportPacket(t *testing.T) {
mainHelper.Parallel(t)
th := Setup(t)
defer th.TearDown()
err := th.App.SetPhase2PermissionsMigrationStatus(true)
require.NoError(t, err)
dir, err := os.MkdirTemp("", "")
require.NoError(t, err)
t.Cleanup(func() {
err = os.RemoveAll(dir)
assert.NoError(t, err)
})
th.App.UpdateConfig(func(cfg *model.Config) {
*cfg.LogSettings.FileLocation = dir
})
logLocation := config.GetLogFileLocation(dir)
genMockLogFiles := func() {
d1 := []byte("hello\ngo\n")
genErr := os.WriteFile(logLocation, d1, 0777)
require.NoError(t, genErr)
}
genMockLogFiles()
getFileNames := func(t *testing.T, fileDatas []model.FileData) []string {
var rFileNames []string
for _, fileData := range fileDatas {
require.NotNil(t, fileData)
assert.Positive(t, len(fileData.Body))
rFileNames = append(rFileNames, fileData.Filename)
}
return rFileNames
}
expectedFileNames := []string{
"metadata.yaml",
"stats.yaml",
"jobs.yaml",
"permissions.yaml",
"plugins.json",
"sanitized_config.json",
"diagnostics.yaml",
"cpu.prof",
"heap.prof",
"goroutines",
}
// database_schema.yaml is only generated for Postgres
if *th.App.Config().SqlSettings.DriverName == model.DatabaseDriverPostgres {
expectedFileNames = append(expectedFileNames, "database_schema.yaml")
}
expectedFileNamesWithLogs := append(expectedFileNames, "mattermost.log")
t.Run("generate Support Packet with logs", func(t *testing.T) {
fileDatas := th.App.GenerateSupportPacket(th.Context, &model.SupportPacketOptions{
IncludeLogs: true,
})
rFileNames := getFileNames(t, fileDatas)
assert.ElementsMatch(t, expectedFileNamesWithLogs, rFileNames)
})
t.Run("generate Support Packet without logs", func(t *testing.T) {
fileDatas := th.App.GenerateSupportPacket(th.Context, &model.SupportPacketOptions{
IncludeLogs: false,
})
rFileNames := getFileNames(t, fileDatas)
assert.ElementsMatch(t, expectedFileNames, rFileNames)
})
t.Run("remove the log files and ensure that warning.txt file is generated", func(t *testing.T) {
err = os.Remove(logLocation)
require.NoError(t, err)
t.Cleanup(genMockLogFiles)
fileDatas := th.App.GenerateSupportPacket(th.Context, &model.SupportPacketOptions{
IncludeLogs: true,
})
rFileNames := getFileNames(t, fileDatas)
assert.ElementsMatch(t, append(expectedFileNames, "warning.txt"), rFileNames)
})
t.Run("steps that generated an error should still return file data", func(t *testing.T) {
mockStore := smocks.Store{}
// Mock the post store to trigger an error
ps := &smocks.PostStore{}
ps.On("AnalyticsPostCount", &model.PostCountOptions{}).Return(int64(0), errors.New("all broken"))
ps.On("ClearCaches")
mockStore.On("Post").Return(ps)
mockStore.On("User").Return(th.App.Srv().Store().User())
mockStore.On("Channel").Return(th.App.Srv().Store().Channel())
mockStore.On("Post").Return(th.App.Srv().Store().Post())
mockStore.On("Team").Return(th.App.Srv().Store().Team())
mockStore.On("Job").Return(th.App.Srv().Store().Job())
mockStore.On("FileInfo").Return(th.App.Srv().Store().FileInfo())
mockStore.On("Webhook").Return(th.App.Srv().Store().Webhook())
mockStore.On("System").Return(th.App.Srv().Store().System())
mockStore.On("License").Return(th.App.Srv().Store().License())
mockStore.On("Command").Return(th.App.Srv().Store().Command())
mockStore.On("Role").Return(th.App.Srv().Store().Role())
mockStore.On("Scheme").Return(th.App.Srv().Store().Scheme())
mockStore.On("Close").Return(nil)
mockStore.On("GetDBSchemaVersion").Return(1, nil)
mockStore.On("GetDbVersion", false).Return("1.0.0", nil)
mockStore.On("TotalMasterDbConnections").Return(30)
mockStore.On("TotalReadDbConnections").Return(20)
mockStore.On("TotalSearchDbConnections").Return(10)
mockStore.On("GetSchemaDefinition").Return(&model.SupportPacketDatabaseSchema{
Tables: []model.DatabaseTable{},
}, nil)
oldStore := th.App.Srv().Store()
t.Cleanup(func() {
th.App.Srv().SetStore(oldStore)
})
th.App.Srv().SetStore(&mockStore)
fileDatas := th.App.GenerateSupportPacket(th.Context, &model.SupportPacketOptions{
IncludeLogs: false,
})
rFileNames := getFileNames(t, fileDatas)
assert.Contains(t, rFileNames, "warning.txt")
assert.Contains(t, rFileNames, "stats.yaml")
assert.ElementsMatch(t, append(expectedFileNames, "warning.txt"), rFileNames)
})
pluginID := "testplugin"
pluginCode := `
package main
import (
"github.com/mattermost/mattermost/server/public/plugin"
"github.com/mattermost/mattermost/server/public/model"
)
type TestPlugin struct {
plugin.MattermostPlugin
}
func (p *TestPlugin) GenerateSupportData(c *plugin.Context) ([]*model.FileData, error) {
return []*model.FileData{{
Filename: "testplugin/diagnostics.yaml",
Body: []byte("foo"),
}}, nil
}
func main() {
plugin.ClientMain(&TestPlugin{})
}`
t.Run("Support Packet always contains plugin data if the plugin doesn't define the support_packet prop", func(t *testing.T) {
pluginManifest := `{"id": "testplugin", "server": {"executable": "backend.exe"}}`
setupPluginAPITest(t, pluginCode, pluginManifest, pluginID, th.App, th.Context)
t.Cleanup(func() {
appErr := th.App.ch.RemovePlugin(pluginID)
require.Nil(t, appErr)
})
fileDatas := th.App.GenerateSupportPacket(th.Context, &model.SupportPacketOptions{
IncludeLogs: false,
})
rFileNames := getFileNames(t, fileDatas)
assert.ElementsMatch(t, append(expectedFileNames, "testplugin/diagnostics.yaml"), rFileNames)
})
t.Run("Support Packet contains plugin data if the plugin defines the support_packet prop and it gets queried", func(t *testing.T) {
pluginManifest := `{"id": "testplugin", "server": {"executable": "backend.exe"}, "props": {"support_packet": "some text"}}`
setupPluginAPITest(t, pluginCode, pluginManifest, pluginID, th.App, th.Context)
t.Cleanup(func() {
appErr := th.App.ch.RemovePlugin(pluginID)
require.Nil(t, appErr)
})
fileDatas := th.App.GenerateSupportPacket(th.Context, &model.SupportPacketOptions{
IncludeLogs: false,
PluginPackets: []string{pluginID},
})
rFileNames := getFileNames(t, fileDatas)
assert.ElementsMatch(t, append(expectedFileNames, "testplugin/diagnostics.yaml"), rFileNames)
})
t.Run("Support Packet doesn't contain plugin data if the plugin defines the support_packet prop and it doesn't get queried", func(t *testing.T) {
pluginManifest := `{"id": "testplugin", "server": {"executable": "backend.exe"}, "props": {"support_packet": "some text"}}`
setupPluginAPITest(t, pluginCode, pluginManifest, pluginID, th.App, th.Context)
t.Cleanup(func() {
appErr := th.App.ch.RemovePlugin(pluginID)
require.Nil(t, appErr)
})
fileDatas := th.App.GenerateSupportPacket(th.Context, &model.SupportPacketOptions{
IncludeLogs: false,
})
rFileNames := getFileNames(t, fileDatas)
assert.ElementsMatch(t, expectedFileNames, rFileNames)
})
t.Run("Plugin config values in the Support Packet are obfuscated, if the plugin marks them as secrets", func(t *testing.T) {
pluginManifest := `{"id": "testplugin", "server": {"executable": "backend.exe"}, "settings_schema": {"settings": [{"key": "foo", "type": "text"}, {"key": "bar", "type": "text", "secret": true}]}}`
setupPluginAPITest(t, pluginCode, pluginManifest, pluginID, th.App, th.Context)
t.Cleanup(func() {
appErr := th.App.ch.RemovePlugin(pluginID)
require.Nil(t, appErr)
})
th.App.UpdateConfig(func(cfg *model.Config) {
cfg.PluginSettings.Plugins[pluginID] = map[string]any{
"foo": "foo_value",
"bar": "bar_value",
}
})
fileDatas := th.App.GenerateSupportPacket(th.Context, &model.SupportPacketOptions{
IncludeLogs: false,
})
found := false
for _, f := range fileDatas {
if f.Filename != "sanitized_config.json" {
continue
}
var config model.Config
err = json.Unmarshal(f.Body, &config)
require.NoError(t, err)
assert.Equal(t, "foo_value", config.PluginSettings.Plugins[pluginID]["foo"])
assert.Equal(t, model.FakeSetting, config.PluginSettings.Plugins[pluginID]["bar"])
found = true
}
assert.True(t, found)
})
}
func TestGetPluginsFile(t *testing.T) {
mainHelper.Parallel(t)
th := Setup(t)
defer th.TearDown()
getJobList := func(t *testing.T) *model.SupportPacketPluginList {
t.Helper()
fileData, err := th.App.getPluginsFile(th.Context)
assert.NoError(t, err)
require.NotNil(t, fileData)
assert.Equal(t, "plugins.json", fileData.Filename)
assert.Positive(t, len(fileData.Body))
var pl model.SupportPacketPluginList
err = json.Unmarshal(fileData.Body, &pl)
require.NoError(t, err)
return &pl
}
t.Run("no errors if no plugins are installed", func(t *testing.T) {
pl := getJobList(t)
assert.Len(t, pl.Enabled, 0)
assert.Len(t, pl.Disabled, 0)
})
t.Run("two plugins are installed", func(t *testing.T) {
path, found := fileutils.FindDir("tests")
require.True(t, found, "tests directory not found")
bundle1, err := os.ReadFile(filepath.Join(path, "testplugin.tar.gz"))
require.NoError(t, err)
manifest1, appErr := th.App.InstallPlugin(bytes.NewReader(bundle1), false)
require.Nil(t, appErr)
require.Equal(t, "testplugin", manifest1.Id)
appErr = th.App.EnablePlugin(manifest1.Id)
require.Nil(t, appErr)
bundle2, err := os.ReadFile(filepath.Join(path, "testplugin2.tar.gz"))
require.NoError(t, err)
manifest2, appErr := th.App.InstallPlugin(bytes.NewReader(bundle2), false)
require.Nil(t, appErr)
require.Equal(t, "testplugin2", manifest2.Id)
pl := getJobList(t)
require.Len(t, pl.Enabled, 1)
assert.Equal(t, "testplugin", pl.Enabled[0].Id)
require.Len(t, pl.Disabled, 1)
assert.Equal(t, "testplugin2", pl.Disabled[0].Id)
})
t.Run("error if plugin are disabled", func(t *testing.T) {
// Turn off plugins so we can get an error
th.App.UpdateConfig(func(cfg *model.Config) {
*cfg.PluginSettings.Enable = false
})
// Plugins off in settings so no fileData and we get a warning instead
fileData, err := th.App.getPluginsFile(th.Context)
assert.Nil(t, fileData)
assert.ErrorContains(t, err, "failed to get plugin list for Support Packet")
})
}
func TestGetSupportPacketStats(t *testing.T) {
mainHelper.Parallel(t)
th := Setup(t)
generateStats := func(t *testing.T) *model.SupportPacketStats {
t.Helper()
require.NoError(t, th.App.Srv().Store().Post().RefreshPostStats())
fileData, err := th.App.getSupportPacketStats(th.Context)
require.NotNil(t, fileData)
assert.Equal(t, "stats.yaml", fileData.Filename)
assert.Positive(t, len(fileData.Body))
assert.NoError(t, err)
var packet model.SupportPacketStats
err = yaml.Unmarshal(fileData.Body, &packet)
require.NoError(t, err)
return &packet
}
t.Run("fresh server", func(t *testing.T) {
sp := generateStats(t)
assert.Equal(t, int64(0), sp.RegisteredUsers)
assert.Equal(t, int64(0), sp.ActiveUsers)
assert.Equal(t, int64(0), sp.DailyActiveUsers)
assert.Equal(t, int64(0), sp.MonthlyActiveUsers)
assert.Equal(t, int64(0), sp.DeactivatedUsers)
assert.Equal(t, int64(0), sp.Guests)
assert.Equal(t, int64(0), sp.BotAccounts)
assert.Equal(t, int64(0), sp.Posts)
assert.Equal(t, int64(0), sp.Channels)
assert.Equal(t, int64(0), sp.Teams)
assert.Equal(t, int64(0), sp.SlashCommands)
assert.Equal(t, int64(0), sp.IncomingWebhooks)
assert.Equal(t, int64(0), sp.OutgoingWebhooks)
})
t.Run("Happy path", func(t *testing.T) {
var user *model.User
for range 4 {
user = th.CreateUser()
}
th.BasicUser = user
for range 3 {
deactivatedUser := th.CreateUser()
require.NotNil(t, deactivatedUser)
_, appErr := th.App.UpdateActive(th.Context, deactivatedUser, false)
require.Nil(t, appErr)
}
for range 2 {
guest := th.CreateGuest()
require.NotNil(t, guest)
}
th.CreateBot()
team := th.CreateTeam()
channel := th.CreateChannel(th.Context, team)
for range 3 {
p := th.CreatePost(channel)
require.NotNil(t, p)
}
cmd, appErr := th.App.CreateCommand(&model.Command{
CreatorId: user.Id,
TeamId: team.Id,
Trigger: "test",
Method: model.CommandMethodGet,
URL: "http://nowhere.com/",
})
require.Nil(t, appErr)
require.NotNil(t, cmd)
webhookIn, appErr := th.App.CreateIncomingWebhookForChannel(user.Id, channel, &model.IncomingWebhook{ChannelId: channel.Id})
require.Nil(t, appErr)
require.NotNil(t, webhookIn)
webhookOut, appErr := th.App.CreateOutgoingWebhook(&model.OutgoingWebhook{
ChannelId: channel.Id,
TeamId: channel.TeamId,
CreatorId: th.BasicUser.Id,
CallbackURLs: []string{"http://nowhere.com/"},
})
require.Nil(t, appErr)
require.NotNil(t, webhookOut)
sp := generateStats(t)
assert.Equal(t, int64(9), sp.RegisteredUsers)
assert.Equal(t, int64(6), sp.ActiveUsers)
assert.Equal(t, int64(0), sp.DailyActiveUsers)
assert.Equal(t, int64(0), sp.MonthlyActiveUsers)
assert.Equal(t, int64(3), sp.DeactivatedUsers)
assert.Equal(t, int64(2), sp.Guests)
assert.Equal(t, int64(1), sp.BotAccounts)
assert.Equal(t, int64(4), sp.Posts) // 1 from the bot creation and 3 created directly
assert.Equal(t, int64(3), sp.Channels) // 2 from the team creation and 1 created directly
assert.Equal(t, int64(1), sp.Teams)
assert.Equal(t, int64(1), sp.SlashCommands)
assert.Equal(t, int64(1), sp.IncomingWebhooks)
assert.Equal(t, int64(1), sp.OutgoingWebhooks)
})
// Reset test server
th.TearDown()
th = Setup(t).InitBasic()
defer th.TearDown()
t.Run("post count should be present if number of users extends AnalyticsSettings.MaxUsersForStatistics", func(t *testing.T) {
th.App.UpdateConfig(func(cfg *model.Config) {
cfg.AnalyticsSettings.MaxUsersForStatistics = model.NewPointer(1)
})
for range 5 {
p := th.CreatePost(th.BasicChannel)
require.NotNil(t, p)
}
// InitBasic() already creats 5 posts
packet := generateStats(t)
assert.Equal(t, int64(10), packet.Posts)
})
}
func TestGetSupportPacketJobList(t *testing.T) {
mainHelper.Parallel(t)
th := Setup(t)
defer th.TearDown()
getJobList := func(t *testing.T) *model.SupportPacketJobList {
t.Helper()
fileData, err := th.App.getSupportPacketJobList(th.Context)
require.NoError(t, err)
require.NotNil(t, fileData)
assert.Equal(t, "jobs.yaml", fileData.Filename)
assert.Positive(t, len(fileData.Body))
var jobs model.SupportPacketJobList
err = yaml.Unmarshal(fileData.Body, &jobs)
require.NoError(t, err)
return &jobs
}
t.Run("no jobs run yet", func(t *testing.T) {
jobs := getJobList(t)
assert.Empty(t, jobs.LDAPSyncJobs)
assert.Empty(t, jobs.DataRetentionJobs)
assert.Empty(t, jobs.MessageExportJobs)
assert.Empty(t, jobs.ElasticPostIndexingJobs)
assert.Empty(t, jobs.ElasticPostAggregationJobs)
assert.Empty(t, jobs.MigrationJobs)
})
t.Run("jobs exist", func(t *testing.T) {
getJob := func(jobType string) *model.Job {
return &model.Job{
Id: model.NewId(),
Type: jobType,
Priority: 1,
CreateAt: model.GetMillis() - 1,
StartAt: model.GetMillis(),
LastActivityAt: model.GetMillis() + 1,
Status: model.JobStatusPending,
Progress: 51,
Data: model.StringMap{"key": "value"},
}
}
// Create some jobs
jobsToCreate := []*model.Job{
getJob(model.JobTypeLdapSync),
getJob(model.JobTypeDataRetention),
getJob(model.JobTypeMessageExport),
getJob(model.JobTypeElasticsearchPostIndexing),
getJob(model.JobTypeElasticsearchPostAggregation),
getJob(model.JobTypeMigrations),
}
var expectedJobs []*model.Job
for _, job := range jobsToCreate {
// Create the job using the store directly.
// Creating the job at the job service would error as the workers require enterprise code.
rJob, err := th.App.Srv().Store().Job().Save(job)
require.NoError(t, err)
expectedJobs = append(expectedJobs, rJob)
}
jobs := getJobList(t)
// Helper to verify job content matches
verifyJob := func(t *testing.T, expected, actual *model.Job) {
t.Helper()
assert.Equal(t, expected.Id, actual.Id)
assert.Equal(t, expected.Type, actual.Type)
assert.Equal(t, expected.Priority, actual.Priority)
assert.Equal(t, expected.CreateAt, actual.CreateAt)
assert.Equal(t, expected.StartAt, actual.StartAt)
assert.Equal(t, expected.LastActivityAt, actual.LastActivityAt)
assert.Equal(t, expected.Status, actual.Status)
assert.Equal(t, expected.Progress, actual.Progress)
assert.Equal(t, expected.Data, actual.Data)
}
// Verify LDAP sync jobs
require.Len(t, jobs.LDAPSyncJobs, 1, "Should have 1 LDAP sync job")
verifyJob(t, expectedJobs[0], jobs.LDAPSyncJobs[0])
// Verify data retention jobs
require.Len(t, jobs.DataRetentionJobs, 1, "Should have 1 data retention job")
verifyJob(t, expectedJobs[1], jobs.DataRetentionJobs[0])
// Verify message export jobs
require.Len(t, jobs.MessageExportJobs, 1, "Should have 1 message export job")
verifyJob(t, expectedJobs[2], jobs.MessageExportJobs[0])
// Verify elasticsearch post indexing jobs
require.Len(t, jobs.ElasticPostIndexingJobs, 1, "Should have 1 elasticsearch post indexing job")
verifyJob(t, expectedJobs[3], jobs.ElasticPostIndexingJobs[0])
// Verify elasticsearch post aggregation jobs
require.Len(t, jobs.ElasticPostAggregationJobs, 1, "Should have 1 elasticsearch post aggregation job")
verifyJob(t, expectedJobs[4], jobs.ElasticPostAggregationJobs[0])
// Verify migration jobs
require.Len(t, jobs.MigrationJobs, 1, "Should have 1 migration job")
verifyJob(t, expectedJobs[5], jobs.MigrationJobs[0])
})
}
func TestGetSupportPacketPermissionsInfo(t *testing.T) {
mainHelper.Parallel(t)
th := Setup(t).InitBasic()
defer th.TearDown()
err := th.App.SetPhase2PermissionsMigrationStatus(true)
require.NoError(t, err)
generatePermissionInfo := func(t *testing.T) *model.SupportPacketPermissionInfo {
t.Helper()
fileData, err := th.App.getSupportPacketPermissionsInfo(th.Context)
require.NotNil(t, fileData)
assert.Equal(t, "permissions.yaml", fileData.Filename)
assert.Positive(t, len(fileData.Body))
assert.NoError(t, err)
var permissions model.SupportPacketPermissionInfo
err = yaml.Unmarshal(fileData.Body, &permissions)
require.NoError(t, err)
return &permissions
}
t.Run("No custom permissions", func(t *testing.T) {
permissions := generatePermissionInfo(t)
assert.Len(t, permissions.Roles, 23)
assert.Empty(t, permissions.Schemes)
})
scheme, appErr := th.App.CreateScheme(&model.Scheme{
Name: "custom_scheme",
DisplayName: "Custom Scheme",
Scope: model.SchemeScopeTeam,
})
require.Nil(t, appErr)
t.Run("with custom scheme", func(t *testing.T) {
permissions := generatePermissionInfo(t)
assert.Len(t, permissions.Roles, 33) // 23 default roles + 10 custom roles from the scheme
require.Len(t, permissions.Schemes, 1)
assert.Equal(t, scheme.Id, permissions.Schemes[0].Id)
assert.Equal(t, model.FakeSetting, permissions.Schemes[0].Name, "Name should be obfuscated")
assert.Equal(t, model.FakeSetting, permissions.Schemes[0].DisplayName, "DisplayName should be obfuscated")
assert.Equal(t, model.FakeSetting, permissions.Schemes[0].Description, "Description should be obfuscated")
})
t.Run("with custom role", func(t *testing.T) {
role, appErr := th.App.CreateRole(&model.Role{
Name: "custom_role",
DisplayName: "Custom Role",
})
require.Nil(t, appErr)
t.Cleanup(func() {
_, appErr := th.App.DeleteRole(role.Id)
require.Nil(t, appErr)
})
permissions := generatePermissionInfo(t)
require.Len(t, permissions.Schemes, 1)
require.Len(t, permissions.Roles, 34) // 23 default roles + 10 custom roles from the scheme + 1 custom role
found := false
for _, r := range permissions.Roles {
// Confirm that sensitive fields are obfuscated
assert.Equal(t, model.FakeSetting, r.DisplayName, "DisplayName should be obfuscated")
assert.Equal(t, model.FakeSetting, r.Description, "Description should be obfuscated")
// Fine the custom role
if r.Id == role.Id {
assert.Equal(t, role.Name, r.Name)
assert.Equal(t, role.CreateAt, r.CreateAt)
assert.Equal(t, role.UpdateAt, r.UpdateAt)
assert.Equal(t, role.DeleteAt, r.DeleteAt)
assert.Equal(t, role.Permissions, r.Permissions)
assert.Equal(t, role.SchemeManaged, r.SchemeManaged)
assert.Equal(t, role.BuiltIn, r.BuiltIn)
found = true
break
}
}
assert.True(t, found)
})
}
func TestGetSupportPacketMetadata(t *testing.T) {
mainHelper.Parallel(t)
th := Setup(t)
defer th.TearDown()
t.Run("Happy path", func(t *testing.T) {
fileData, err := th.App.getSupportPacketMetadata(th.Context)
require.NoError(t, err)
require.NotNil(t, fileData)
assert.Equal(t, "metadata.yaml", fileData.Filename)
assert.Positive(t, len(fileData.Body))
metadate, err := model.ParsePacketMetadata(fileData.Body)
assert.NoError(t, err)
require.NotNil(t, metadate)
assert.Equal(t, model.SupportPacketType, metadate.Type)
assert.Equal(t, model.CurrentVersion, metadate.ServerVersion)
assert.NotEmpty(t, metadate.ServerID)
assert.NotEmpty(t, metadate.GeneratedAt)
})
}
func TestGetSupportPacketDatabaseSchema(t *testing.T) {
th := Setup(t)
defer th.TearDown()
// Mock store for testing
mockStore := &smocks.Store{}
originalStore := th.App.Srv().Store()
th.App.Srv().SetStore(mockStore)
defer th.App.Srv().SetStore(originalStore)
// Set up mock return for schema definition
mockStore.On("GetSchemaDefinition").Return(&model.SupportPacketDatabaseSchema{
Tables: []model.DatabaseTable{
{
Name: "users",
Columns: []model.DatabaseColumn{
{Name: "id", DataType: "varchar", IsNullable: false},
{Name: "username", DataType: "varchar", IsNullable: false},
{Name: "email", DataType: "varchar", IsNullable: false},
},
},
{
Name: "channels",
Columns: []model.DatabaseColumn{
{Name: "id", DataType: "varchar", IsNullable: false},
{Name: "name", DataType: "varchar", IsNullable: false},
},
},
{
Name: "teams",
Columns: []model.DatabaseColumn{
{Name: "id", DataType: "varchar", IsNullable: false},
{Name: "name", DataType: "varchar", IsNullable: false},
},
},
{
Name: "posts",
Columns: []model.DatabaseColumn{
{Name: "id", DataType: "varchar", IsNullable: false},
{Name: "message", DataType: "text", IsNullable: false},
},
},
},
}, nil)
// Test with Postgres
oldDriverName := *th.App.Config().SqlSettings.DriverName
*th.App.Config().SqlSettings.DriverName = model.DatabaseDriverPostgres
defer func() {
*th.App.Config().SqlSettings.DriverName = oldDriverName
}()
fileData, err := th.App.getSupportPacketDatabaseSchema(th.Context)
require.NoError(t, err)
require.NotNil(t, fileData)
assert.Equal(t, "database_schema.yaml", fileData.Filename)
assert.Positive(t, len(fileData.Body))
var schema model.SupportPacketDatabaseSchema
err = yaml.Unmarshal(fileData.Body, &schema)
require.NoError(t, err)
// Verify schema structure
assert.NotEmpty(t, schema.Tables)
// Verify that common tables are present
tableNames := make([]string, 0, len(schema.Tables))
for _, table := range schema.Tables {
tableNames = append(tableNames, table.Name)
}
// Verify some core tables
expectedTables := []string{"users", "channels", "teams", "posts"}
for _, expected := range expectedTables {
assert.Contains(t, tableNames, expected)
}
// Verify table structure
for _, table := range schema.Tables {
if table.Name == "users" {
// Check user table has key columns
columnNames := make([]string, 0, len(table.Columns))
for _, column := range table.Columns {
columnNames = append(columnNames, column.Name)
}
expectedColumns := []string{"id", "username", "email"}
for _, expected := range expectedColumns {
assert.Contains(t, columnNames, expected)
}
break
}
}
}