github.com/haalcala/mattermost-server-change-repo@v0.0.0-20210713015153-16753fbeee5f/app/plugin_install_test.go (about)

     1  // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
     2  // See LICENSE.txt for license information.
     3  
     4  package app
     5  
     6  import (
     7  	"archive/tar"
     8  	"bytes"
     9  	"compress/gzip"
    10  	"io"
    11  	"os"
    12  	"path/filepath"
    13  	"sort"
    14  	"testing"
    15  
    16  	"github.com/stretchr/testify/assert"
    17  	"github.com/stretchr/testify/require"
    18  
    19  	"github.com/mattermost/mattermost-server/v5/model"
    20  	"github.com/mattermost/mattermost-server/v5/utils/fileutils"
    21  )
    22  
    23  type nilReadSeeker struct {
    24  }
    25  
    26  func (r *nilReadSeeker) Read(p []byte) (int, error) {
    27  	return 0, io.EOF
    28  }
    29  
    30  func (r *nilReadSeeker) Seek(offset int64, whence int) (int64, error) {
    31  	return 0, nil
    32  }
    33  
    34  type testFile struct {
    35  	Name, Body string
    36  }
    37  
    38  func makeInMemoryGzipTarFile(t *testing.T, files []testFile) *bytes.Reader {
    39  	var buf bytes.Buffer
    40  	gzWriter := gzip.NewWriter(&buf)
    41  
    42  	tgz := tar.NewWriter(gzWriter)
    43  
    44  	for _, file := range files {
    45  		hdr := &tar.Header{
    46  			Name: file.Name,
    47  			Mode: 0600,
    48  			Size: int64(len(file.Body)),
    49  		}
    50  		err := tgz.WriteHeader(hdr)
    51  		require.NoError(t, err, "failed to write %s to in-memory tar file", file.Name)
    52  		_, err = tgz.Write([]byte(file.Body))
    53  		require.NoError(t, err, "failed to write body of %s to in-memory tar file", file.Name)
    54  	}
    55  	err := tgz.Close()
    56  	require.NoError(t, err, "failed to close in-memory tar file")
    57  
    58  	err = gzWriter.Close()
    59  	require.NoError(t, err, "failed to close in-memory tar.gz file")
    60  
    61  	return bytes.NewReader(buf.Bytes())
    62  }
    63  
    64  type byBundleInfoId []*model.BundleInfo
    65  
    66  func (b byBundleInfoId) Len() int           { return len(b) }
    67  func (b byBundleInfoId) Swap(i, j int)      { b[i], b[j] = b[j], b[i] }
    68  func (b byBundleInfoId) Less(i, j int) bool { return b[i].Manifest.Id < b[j].Manifest.Id }
    69  
    70  func TestInstallPluginLocally(t *testing.T) {
    71  	t.Run("invalid tar", func(t *testing.T) {
    72  		th := Setup(t)
    73  		defer th.TearDown()
    74  
    75  		actualManifest, appErr := th.App.installPluginLocally(&nilReadSeeker{}, nil, installPluginLocallyOnlyIfNew)
    76  		require.NotNil(t, appErr)
    77  		assert.Equal(t, "app.plugin.extract.app_error", appErr.Id, appErr.Error())
    78  		require.Nil(t, actualManifest)
    79  	})
    80  
    81  	t.Run("missing manifest", func(t *testing.T) {
    82  		th := Setup(t)
    83  		defer th.TearDown()
    84  
    85  		reader := makeInMemoryGzipTarFile(t, []testFile{
    86  			{"test", "test file"},
    87  		})
    88  
    89  		actualManifest, appErr := th.App.installPluginLocally(reader, nil, installPluginLocallyOnlyIfNew)
    90  		require.NotNil(t, appErr)
    91  		assert.Equal(t, "app.plugin.manifest.app_error", appErr.Id, appErr.Error())
    92  		require.Nil(t, actualManifest)
    93  	})
    94  
    95  	installPlugin := func(t *testing.T, th *TestHelper, id, version string, installationStrategy pluginInstallationStrategy) (*model.Manifest, *model.AppError) {
    96  		t.Helper()
    97  
    98  		manifest := &model.Manifest{
    99  			Id:      id,
   100  			Version: version,
   101  		}
   102  		reader := makeInMemoryGzipTarFile(t, []testFile{
   103  			{"plugin.json", manifest.ToJson()},
   104  		})
   105  
   106  		actualManifest, appError := th.App.installPluginLocally(reader, nil, installationStrategy)
   107  		if actualManifest != nil {
   108  			require.Equal(t, manifest, actualManifest)
   109  		}
   110  
   111  		return actualManifest, appError
   112  	}
   113  
   114  	t.Run("invalid plugin id", func(t *testing.T) {
   115  		th := Setup(t)
   116  		defer th.TearDown()
   117  
   118  		actualManifest, appErr := installPlugin(t, th, "invalid#plugin#id", "version", installPluginLocallyOnlyIfNew)
   119  		require.NotNil(t, appErr)
   120  		assert.Equal(t, "app.plugin.invalid_id.app_error", appErr.Id, appErr.Error())
   121  		require.Nil(t, actualManifest)
   122  	})
   123  
   124  	// The following tests fail mysteriously on CI due to an unexpected bundle being present.
   125  	// This exists to clean up manually until we figure out what test isn't cleaning up after
   126  	// itself.
   127  	cleanExistingBundles := func(t *testing.T, th *TestHelper) {
   128  		pluginsEnvironment := th.App.GetPluginsEnvironment()
   129  		require.NotNil(t, pluginsEnvironment)
   130  		bundleInfos, err := pluginsEnvironment.Available()
   131  		require.NoError(t, err)
   132  
   133  		for _, bundleInfo := range bundleInfos {
   134  			err := th.App.removePluginLocally(bundleInfo.Manifest.Id)
   135  			require.Nilf(t, err, "failed to remove existing plugin %s", bundleInfo.Manifest.Id)
   136  		}
   137  	}
   138  
   139  	assertBundleInfoManifests := func(t *testing.T, th *TestHelper, manifests []*model.Manifest) {
   140  		pluginsEnvironment := th.App.GetPluginsEnvironment()
   141  		require.NotNil(t, pluginsEnvironment)
   142  		bundleInfos, err := pluginsEnvironment.Available()
   143  		require.NoError(t, err)
   144  
   145  		sort.Sort(byBundleInfoId(bundleInfos))
   146  
   147  		actualManifests := make([]*model.Manifest, 0, len(bundleInfos))
   148  		for _, bundleInfo := range bundleInfos {
   149  			actualManifests = append(actualManifests, bundleInfo.Manifest)
   150  		}
   151  
   152  		require.Equal(t, manifests, actualManifests)
   153  	}
   154  
   155  	t.Run("no plugins already installed", func(t *testing.T) {
   156  		th := Setup(t)
   157  		defer th.TearDown()
   158  		cleanExistingBundles(t, th)
   159  
   160  		manifest, appErr := installPlugin(t, th, "valid", "0.0.1", installPluginLocallyOnlyIfNew)
   161  		require.Nil(t, appErr)
   162  		require.NotNil(t, manifest)
   163  
   164  		assertBundleInfoManifests(t, th, []*model.Manifest{manifest})
   165  	})
   166  
   167  	t.Run("different plugin already installed", func(t *testing.T) {
   168  		th := Setup(t)
   169  		defer th.TearDown()
   170  		cleanExistingBundles(t, th)
   171  
   172  		otherManifest, appErr := installPlugin(t, th, "other", "0.0.1", installPluginLocallyOnlyIfNew)
   173  		require.Nil(t, appErr)
   174  		require.NotNil(t, otherManifest)
   175  
   176  		manifest, appErr := installPlugin(t, th, "valid", "0.0.1", installPluginLocallyOnlyIfNew)
   177  		require.Nil(t, appErr)
   178  		require.NotNil(t, manifest)
   179  
   180  		assertBundleInfoManifests(t, th, []*model.Manifest{otherManifest, manifest})
   181  	})
   182  
   183  	t.Run("same plugin already installed", func(t *testing.T) {
   184  		t.Run("install only if new", func(t *testing.T) {
   185  			th := Setup(t)
   186  			defer th.TearDown()
   187  			cleanExistingBundles(t, th)
   188  
   189  			existingManifest, appErr := installPlugin(t, th, "valid", "0.0.1", installPluginLocallyOnlyIfNew)
   190  			require.Nil(t, appErr)
   191  			require.NotNil(t, existingManifest)
   192  
   193  			manifest, appErr := installPlugin(t, th, "valid", "0.0.1", installPluginLocallyOnlyIfNew)
   194  			require.NotNil(t, appErr)
   195  			require.Equal(t, "app.plugin.install_id.app_error", appErr.Id, appErr.Error())
   196  			require.Nil(t, manifest)
   197  
   198  			assertBundleInfoManifests(t, th, []*model.Manifest{existingManifest})
   199  		})
   200  
   201  		t.Run("install if upgrade, but older", func(t *testing.T) {
   202  			th := Setup(t)
   203  			defer th.TearDown()
   204  			cleanExistingBundles(t, th)
   205  
   206  			existingManifest, appErr := installPlugin(t, th, "valid", "0.0.2", installPluginLocallyOnlyIfNewOrUpgrade)
   207  			require.Nil(t, appErr)
   208  			require.NotNil(t, existingManifest)
   209  
   210  			manifest, appErr := installPlugin(t, th, "valid", "0.0.1", installPluginLocallyOnlyIfNewOrUpgrade)
   211  			require.Nil(t, appErr)
   212  			require.Nil(t, manifest)
   213  
   214  			assertBundleInfoManifests(t, th, []*model.Manifest{existingManifest})
   215  		})
   216  
   217  		t.Run("install if upgrade, but same version", func(t *testing.T) {
   218  			th := Setup(t)
   219  			defer th.TearDown()
   220  			cleanExistingBundles(t, th)
   221  
   222  			existingManifest, appErr := installPlugin(t, th, "valid", "0.0.2", installPluginLocallyOnlyIfNewOrUpgrade)
   223  			require.Nil(t, appErr)
   224  			require.NotNil(t, existingManifest)
   225  
   226  			manifest, appErr := installPlugin(t, th, "valid", "0.0.2", installPluginLocallyOnlyIfNewOrUpgrade)
   227  			require.Nil(t, appErr)
   228  			require.Nil(t, manifest)
   229  
   230  			assertBundleInfoManifests(t, th, []*model.Manifest{existingManifest})
   231  		})
   232  
   233  		t.Run("install if upgrade, newer version", func(t *testing.T) {
   234  			th := Setup(t)
   235  			defer th.TearDown()
   236  			cleanExistingBundles(t, th)
   237  
   238  			existingManifest, appErr := installPlugin(t, th, "valid", "0.0.2", installPluginLocallyOnlyIfNewOrUpgrade)
   239  			require.Nil(t, appErr)
   240  			require.NotNil(t, existingManifest)
   241  
   242  			manifest, appErr := installPlugin(t, th, "valid", "0.0.3", installPluginLocallyOnlyIfNewOrUpgrade)
   243  			require.Nil(t, appErr)
   244  			require.NotNil(t, manifest)
   245  
   246  			assertBundleInfoManifests(t, th, []*model.Manifest{manifest})
   247  		})
   248  
   249  		t.Run("install always, old version", func(t *testing.T) {
   250  			th := Setup(t)
   251  			defer th.TearDown()
   252  			cleanExistingBundles(t, th)
   253  
   254  			existingManifest, appErr := installPlugin(t, th, "valid", "0.0.2", installPluginLocallyAlways)
   255  			require.Nil(t, appErr)
   256  			require.NotNil(t, existingManifest)
   257  
   258  			manifest, appErr := installPlugin(t, th, "valid", "0.0.1", installPluginLocallyAlways)
   259  			require.Nil(t, appErr)
   260  			require.NotNil(t, manifest)
   261  
   262  			assertBundleInfoManifests(t, th, []*model.Manifest{manifest})
   263  		})
   264  	})
   265  }
   266  
   267  func TestInstallPluginAlreadyActive(t *testing.T) {
   268  	th := Setup(t)
   269  	defer th.TearDown()
   270  
   271  	path, _ := fileutils.FindDir("tests")
   272  	reader, err := os.Open(filepath.Join(path, "testplugin.tar.gz"))
   273  	require.NoError(t, err)
   274  
   275  	actualManifest, appError := th.App.InstallPlugin(reader, true)
   276  	require.NotNil(t, actualManifest)
   277  	require.Nil(t, appError)
   278  	appError = th.App.EnablePlugin(actualManifest.Id)
   279  	require.Nil(t, appError)
   280  
   281  	pluginsEnvironment := th.App.GetPluginsEnvironment()
   282  	require.NotNil(t, pluginsEnvironment)
   283  	bundleInfos, err := pluginsEnvironment.Available()
   284  	require.NoError(t, err)
   285  	require.NotEmpty(t, bundleInfos)
   286  	for _, bundleInfo := range bundleInfos {
   287  		if bundleInfo.Manifest.Id == actualManifest.Id {
   288  			err := os.RemoveAll(bundleInfo.Path)
   289  			require.NoError(t, err)
   290  		}
   291  	}
   292  
   293  	actualManifest, appError = th.App.InstallPlugin(reader, true)
   294  	require.NotNil(t, appError)
   295  	require.Nil(t, actualManifest)
   296  	require.Equal(t, "app.plugin.restart.app_error", appError.Id)
   297  }