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 }