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