github.com/haalcala/mattermost-server-change-repo/v5@v5.33.2/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 }