github.com/jaylevin/jenkins-library@v1.230.4/cmd/githubPublishRelease_test.go (about) 1 package cmd 2 3 import ( 4 "context" 5 "fmt" 6 "net/http" 7 "os" 8 "path/filepath" 9 "testing" 10 "time" 11 12 "github.com/SAP/jenkins-library/cmd/mocks" 13 "github.com/google/go-github/v32/github" 14 "github.com/stretchr/testify/assert" 15 "github.com/stretchr/testify/mock" 16 ) 17 18 type ghRCMock struct { 19 createErr error 20 latestRelease *github.RepositoryRelease 21 release *github.RepositoryRelease 22 delErr error 23 delID int64 24 delOwner string 25 delRepo string 26 listErr error 27 listID int64 28 listOwner string 29 listReleaseAssets []*github.ReleaseAsset 30 listRepo string 31 listOpts *github.ListOptions 32 latestStatusCode int 33 latestErr error 34 preRelease bool 35 uploadID int64 36 uploadOpts *github.UploadOptions 37 uploadOwner string 38 uploadRepo string 39 } 40 41 func (g *ghRCMock) CreateRelease(ctx context.Context, owner string, repo string, release *github.RepositoryRelease) (*github.RepositoryRelease, *github.Response, error) { 42 g.release = release 43 return release, nil, g.createErr 44 } 45 46 func (g *ghRCMock) DeleteReleaseAsset(ctx context.Context, owner string, repo string, id int64) (*github.Response, error) { 47 g.delOwner = owner 48 g.delRepo = repo 49 g.delID = id 50 return nil, g.delErr 51 } 52 53 func (g *ghRCMock) GetLatestRelease(ctx context.Context, owner string, repo string) (*github.RepositoryRelease, *github.Response, error) { 54 hc := http.Response{StatusCode: 200} 55 if g.latestStatusCode != 0 { 56 hc.StatusCode = g.latestStatusCode 57 } 58 59 if len(owner) == 0 { 60 return g.latestRelease, nil, g.latestErr 61 } 62 63 ghResp := github.Response{Response: &hc} 64 return g.latestRelease, &ghResp, g.latestErr 65 } 66 67 func (g *ghRCMock) ListReleaseAssets(ctx context.Context, owner string, repo string, id int64, opt *github.ListOptions) ([]*github.ReleaseAsset, *github.Response, error) { 68 g.listID = id 69 g.listOwner = owner 70 g.listRepo = repo 71 g.listOpts = opt 72 return g.listReleaseAssets, nil, g.listErr 73 } 74 75 func (g *ghRCMock) UploadReleaseAsset(ctx context.Context, owner string, repo string, id int64, opt *github.UploadOptions, file *os.File) (*github.ReleaseAsset, *github.Response, error) { 76 g.uploadID = id 77 g.uploadOwner = owner 78 g.uploadRepo = repo 79 g.uploadOpts = opt 80 return nil, nil, nil 81 } 82 83 type ghICMock struct { 84 issues []*github.Issue 85 lastPublished time.Time 86 owner string 87 repo string 88 options *github.IssueListByRepoOptions 89 } 90 91 func (g *ghICMock) ListByRepo(ctx context.Context, owner string, repo string, opt *github.IssueListByRepoOptions) ([]*github.Issue, *github.Response, error) { 92 g.owner = owner 93 g.repo = repo 94 g.options = opt 95 g.lastPublished = opt.Since 96 return g.issues, nil, nil 97 } 98 99 func TestRunGithubPublishRelease(t *testing.T) { 100 ctx := context.Background() 101 102 t.Run("Success - first release & no body", func(t *testing.T) { 103 ghIssueClient := ghICMock{} 104 ghRepoClient := ghRCMock{ 105 latestStatusCode: 404, 106 latestErr: fmt.Errorf("not found"), 107 } 108 109 myGithubPublishReleaseOptions := githubPublishReleaseOptions{ 110 AddDeltaToLastRelease: true, 111 Commitish: "master", 112 Owner: "TEST", 113 PreRelease: true, 114 Repository: "test", 115 ServerURL: "https://github.com", 116 ReleaseBodyHeader: "Header", 117 Version: "1.0", 118 } 119 err := runGithubPublishRelease(ctx, &myGithubPublishReleaseOptions, &ghRepoClient, &ghIssueClient) 120 assert.NoError(t, err, "Error occurred but none expected.") 121 122 assert.Equal(t, "Header\n", ghRepoClient.release.GetBody()) 123 assert.Equal(t, true, ghRepoClient.release.GetPrerelease()) 124 assert.Equal(t, "1.0", ghRepoClient.release.GetTagName()) 125 }) 126 127 t.Run("Success - first release with tag prefix set & no body", func(t *testing.T) { 128 ghIssueClient := ghICMock{} 129 ghRepoClient := ghRCMock{ 130 latestStatusCode: 404, 131 latestErr: fmt.Errorf("not found"), 132 } 133 134 myGithubPublishReleaseOptions := githubPublishReleaseOptions{ 135 AddDeltaToLastRelease: true, 136 Commitish: "master", 137 Owner: "TEST", 138 PreRelease: true, 139 Repository: "test", 140 ServerURL: "https://github.com", 141 ReleaseBodyHeader: "Header", 142 Version: "1.0", 143 TagPrefix: "v", 144 } 145 err := runGithubPublishRelease(ctx, &myGithubPublishReleaseOptions, &ghRepoClient, &ghIssueClient) 146 assert.NoError(t, err, "Error occurred but none expected.") 147 148 assert.Equal(t, "Header\n", ghRepoClient.release.GetBody()) 149 assert.Equal(t, true, ghRepoClient.release.GetPrerelease()) 150 assert.Equal(t, "v1.0", ghRepoClient.release.GetTagName()) 151 }) 152 153 t.Run("Success - subsequent releases & with body", func(t *testing.T) { 154 lastTag := "1.0" 155 lastPublishedAt := github.Timestamp{Time: time.Date(2019, 01, 01, 0, 0, 0, 0, time.UTC)} 156 ghRepoClient := ghRCMock{ 157 createErr: nil, 158 latestRelease: &github.RepositoryRelease{ 159 TagName: &lastTag, 160 PublishedAt: &lastPublishedAt, 161 }, 162 } 163 prHTMLURL := "https://github.com/TEST/test/pull/1" 164 prTitle := "Pull" 165 prNo := 1 166 167 issHTMLURL := "https://github.com/TEST/test/issues/2" 168 issTitle := "Issue" 169 issNo := 2 170 171 ghIssueClient := ghICMock{ 172 issues: []*github.Issue{ 173 {Number: &prNo, Title: &prTitle, HTMLURL: &prHTMLURL, PullRequestLinks: &github.PullRequestLinks{URL: &prHTMLURL}}, 174 {Number: &issNo, Title: &issTitle, HTMLURL: &issHTMLURL}, 175 }, 176 } 177 myGithubPublishReleaseOptions := githubPublishReleaseOptions{ 178 AddClosedIssues: true, 179 AddDeltaToLastRelease: true, 180 Commitish: "master", 181 Owner: "TEST", 182 Repository: "test", 183 ServerURL: "https://github.com", 184 ReleaseBodyHeader: "Header", 185 Version: "1.1", 186 } 187 err := runGithubPublishRelease(ctx, &myGithubPublishReleaseOptions, &ghRepoClient, &ghIssueClient) 188 189 assert.NoError(t, err, "Error occurred but none expected.") 190 191 assert.Equal(t, "Header\n\n**List of closed pull-requests since last release**\n[#1](https://github.com/TEST/test/pull/1): Pull\n\n**List of closed issues since last release**\n[#2](https://github.com/TEST/test/issues/2): Issue\n\n**Changes**\n[1.0...1.1](https://github.com/TEST/test/compare/1.0...1.1)\n", ghRepoClient.release.GetBody()) 192 assert.Equal(t, "1.1", ghRepoClient.release.GetName()) 193 assert.Equal(t, "1.1", ghRepoClient.release.GetTagName()) 194 assert.Equal(t, "master", ghRepoClient.release.GetTargetCommitish()) 195 196 assert.Equal(t, lastPublishedAt.Time, ghIssueClient.lastPublished) 197 }) 198 199 t.Run("Success - update asset", func(t *testing.T) { 200 var releaseID int64 = 1 201 ghIssueClient := ghICMock{} 202 ghRepoClient := ghRCMock{ 203 latestRelease: &github.RepositoryRelease{ 204 ID: &releaseID, 205 }, 206 } 207 208 myGithubPublishReleaseOptions := githubPublishReleaseOptions{ 209 AssetPath: filepath.Join("testdata", t.Name()+"_test.txt"), 210 Version: "latest", 211 } 212 213 err := runGithubPublishRelease(ctx, &myGithubPublishReleaseOptions, &ghRepoClient, &ghIssueClient) 214 215 assert.NoError(t, err, "Error occurred but none expected.") 216 217 assert.Nil(t, ghRepoClient.release) 218 219 assert.Equal(t, releaseID, ghRepoClient.listID) 220 assert.Equal(t, releaseID, ghRepoClient.uploadID) 221 }) 222 223 t.Run("Error - get release", func(t *testing.T) { 224 ghIssueClient := ghICMock{} 225 ghRepoClient := ghRCMock{ 226 latestErr: fmt.Errorf("Latest release error"), 227 } 228 myGithubPublishReleaseOptions := githubPublishReleaseOptions{ 229 Owner: "TEST", 230 Repository: "test", 231 } 232 err := runGithubPublishRelease(ctx, &myGithubPublishReleaseOptions, &ghRepoClient, &ghIssueClient) 233 234 assert.Equal(t, "Error occurred when retrieving latest GitHub release (TEST/test): Latest release error", fmt.Sprint(err)) 235 }) 236 237 t.Run("Error - get release no response", func(t *testing.T) { 238 ghIssueClient := ghICMock{} 239 ghRepoClient := ghRCMock{ 240 latestErr: fmt.Errorf("Latest release error, no response"), 241 } 242 myGithubPublishReleaseOptions := githubPublishReleaseOptions{ 243 Owner: "", 244 Repository: "test", 245 } 246 err := runGithubPublishRelease(ctx, &myGithubPublishReleaseOptions, &ghRepoClient, &ghIssueClient) 247 248 assert.Equal(t, "Error occurred when retrieving latest GitHub release (/test): Latest release error, no response", fmt.Sprint(err)) 249 }) 250 251 t.Run("Error - create release", func(t *testing.T) { 252 ghIssueClient := ghICMock{} 253 ghRepoClient := ghRCMock{ 254 createErr: fmt.Errorf("Create release error"), 255 } 256 myGithubPublishReleaseOptions := githubPublishReleaseOptions{ 257 Version: "1.0", 258 } 259 err := runGithubPublishRelease(ctx, &myGithubPublishReleaseOptions, &ghRepoClient, &ghIssueClient) 260 261 assert.Equal(t, "Creation of release '1.0' failed: Create release error", fmt.Sprint(err)) 262 }) 263 } 264 265 func TestGetClosedIssuesText(t *testing.T) { 266 ctx := context.Background() 267 publishedAt := github.Timestamp{Time: time.Date(2019, 01, 01, 0, 0, 0, 0, time.UTC)} 268 269 t.Run("No issues", func(t *testing.T) { 270 ghIssueClient := ghICMock{} 271 myGithubPublishReleaseOptions := githubPublishReleaseOptions{ 272 Version: "1.0", 273 } 274 275 res := getClosedIssuesText(ctx, publishedAt, &myGithubPublishReleaseOptions, &ghIssueClient) 276 277 assert.Equal(t, "", res) 278 }) 279 280 t.Run("All issues", func(t *testing.T) { 281 ctx := context.Background() 282 publishedAt := github.Timestamp{Time: time.Date(2019, 01, 01, 0, 0, 0, 0, time.UTC)} 283 284 prHTMLURL := []string{"https://github.com/TEST/test/pull/1", "https://github.com/TEST/test/pull/2"} 285 prTitle := []string{"Pull1", "Pull2"} 286 prNo := []int{1, 2} 287 288 issHTMLURL := []string{"https://github.com/TEST/test/issues/3", "https://github.com/TEST/test/issues/4"} 289 issTitle := []string{"Issue3", "Issue4"} 290 issNo := []int{3, 4} 291 292 ghIssueClient := ghICMock{ 293 issues: []*github.Issue{ 294 {Number: &prNo[0], Title: &prTitle[0], HTMLURL: &prHTMLURL[0], PullRequestLinks: &github.PullRequestLinks{URL: &prHTMLURL[0]}}, 295 {Number: &prNo[1], Title: &prTitle[1], HTMLURL: &prHTMLURL[1], PullRequestLinks: &github.PullRequestLinks{URL: &prHTMLURL[1]}}, 296 {Number: &issNo[0], Title: &issTitle[0], HTMLURL: &issHTMLURL[0]}, 297 {Number: &issNo[1], Title: &issTitle[1], HTMLURL: &issHTMLURL[1]}, 298 }, 299 } 300 301 myGithubPublishReleaseOptions := githubPublishReleaseOptions{ 302 Owner: "TEST", 303 Repository: "test", 304 } 305 306 res := getClosedIssuesText(ctx, publishedAt, &myGithubPublishReleaseOptions, &ghIssueClient) 307 308 assert.Equal(t, "\n**List of closed pull-requests since last release**\n[#1](https://github.com/TEST/test/pull/1): Pull1\n[#2](https://github.com/TEST/test/pull/2): Pull2\n\n**List of closed issues since last release**\n[#3](https://github.com/TEST/test/issues/3): Issue3\n[#4](https://github.com/TEST/test/issues/4): Issue4\n", res) 309 assert.Equal(t, "TEST", ghIssueClient.owner, "Owner not properly passed") 310 assert.Equal(t, "test", ghIssueClient.repo, "Repo not properly passed") 311 assert.Equal(t, "closed", ghIssueClient.options.State, "Issue state not properly passed") 312 assert.Equal(t, "asc", ghIssueClient.options.Direction, "Sort direction not properly passed") 313 assert.Equal(t, publishedAt.Time, ghIssueClient.options.Since, "PublishedAt not properly passed") 314 }) 315 316 } 317 318 func TestGetReleaseDeltaText(t *testing.T) { 319 myGithubPublishReleaseOptions := githubPublishReleaseOptions{ 320 Owner: "TEST", 321 Repository: "test", 322 ServerURL: "https://github.com", 323 Version: "1.1", 324 } 325 lastTag := "1.0" 326 lastRelease := github.RepositoryRelease{ 327 TagName: &lastTag, 328 } 329 330 res := getReleaseDeltaText(&myGithubPublishReleaseOptions, &lastRelease) 331 332 assert.Equal(t, "\n**Changes**\n[1.0...1.1](https://github.com/TEST/test/compare/1.0...1.1)\n", res) 333 } 334 335 func TestUploadReleaseAsset(t *testing.T) { 336 ctx := context.Background() 337 338 t.Run("Success - existing asset", func(t *testing.T) { 339 var releaseID int64 = 1 340 assetName := "Success_-_existing_asset_test.txt" 341 var assetID int64 = 11 342 ghRepoClient := ghRCMock{ 343 latestRelease: &github.RepositoryRelease{ 344 ID: &releaseID, 345 }, 346 listReleaseAssets: []*github.ReleaseAsset{ 347 {Name: &assetName, ID: &assetID}, 348 }, 349 } 350 351 myGithubPublishReleaseOptions := githubPublishReleaseOptions{ 352 Owner: "TEST", 353 Repository: "test", 354 AssetPath: filepath.Join("testdata", t.Name()+"_test.txt"), 355 } 356 357 err := uploadReleaseAsset(ctx, releaseID, &myGithubPublishReleaseOptions, &ghRepoClient) 358 359 assert.NoError(t, err, "Error occurred but none expected.") 360 361 assert.Equal(t, "TEST", ghRepoClient.listOwner, "Owner not properly passed - list") 362 assert.Equal(t, "test", ghRepoClient.listRepo, "Repo not properly passed - list") 363 assert.Equal(t, releaseID, ghRepoClient.listID, "Relase ID not properly passed - list") 364 365 assert.Equal(t, "TEST", ghRepoClient.delOwner, "Owner not properly passed - del") 366 assert.Equal(t, "test", ghRepoClient.delRepo, "Repo not properly passed - del") 367 assert.Equal(t, assetID, ghRepoClient.delID, "Relase ID not properly passed - del") 368 369 assert.Equal(t, "TEST", ghRepoClient.uploadOwner, "Owner not properly passed - upload") 370 assert.Equal(t, "test", ghRepoClient.uploadRepo, "Repo not properly passed - upload") 371 assert.Equal(t, releaseID, ghRepoClient.uploadID, "Relase ID not properly passed - upload") 372 assert.Equal(t, "text/plain; charset=utf-8", ghRepoClient.uploadOpts.MediaType, "Wrong MediaType passed - upload") 373 }) 374 375 t.Run("Success - no asset", func(t *testing.T) { 376 var releaseID int64 = 1 377 assetName := "notFound" 378 var assetID int64 = 11 379 ghRepoClient := ghRCMock{ 380 latestRelease: &github.RepositoryRelease{ 381 ID: &releaseID, 382 }, 383 listReleaseAssets: []*github.ReleaseAsset{ 384 {Name: &assetName, ID: &assetID}, 385 }, 386 } 387 388 myGithubPublishReleaseOptions := githubPublishReleaseOptions{ 389 Owner: "TEST", 390 Repository: "test", 391 AssetPath: filepath.Join("testdata", t.Name()+"_test.txt"), 392 } 393 394 err := uploadReleaseAsset(ctx, releaseID, &myGithubPublishReleaseOptions, &ghRepoClient) 395 396 assert.NoError(t, err, "Error occurred but none expected.") 397 398 assert.Equal(t, int64(0), ghRepoClient.delID, "Relase ID should not be populated") 399 }) 400 401 t.Run("Error - List Assets", func(t *testing.T) { 402 var releaseID int64 = 1 403 ghRepoClient := ghRCMock{ 404 listErr: fmt.Errorf("List Asset Error"), 405 } 406 myGithubPublishReleaseOptions := githubPublishReleaseOptions{} 407 408 err := uploadReleaseAsset(ctx, releaseID, &myGithubPublishReleaseOptions, &ghRepoClient) 409 assert.Equal(t, "Failed to get list of release assets.: List Asset Error", fmt.Sprint(err), "Wrong error received") 410 }) 411 } 412 413 func TestUploadReleaseAssetList(t *testing.T) { 414 ctx := context.Background() 415 owner := "OWNER" 416 repository := "REPOSITORY" 417 var releaseID int64 = 1 418 419 t.Run("Success - multiple asset", func(t *testing.T) { 420 // init 421 assetURL := mock.Anything 422 asset1 := filepath.Join("testdata", t.Name()+"_1_test.txt") 423 asset2 := filepath.Join("testdata", t.Name()+"_2_test.txt") 424 assetName1 := filepath.Base(asset1) 425 assetName2 := filepath.Base(asset2) 426 var assetID1 int64 = 11 427 var assetID2 int64 = 12 428 stepConfig := githubPublishReleaseOptions{ 429 Owner: owner, 430 Repository: repository, 431 AssetPathList: []string{asset1, asset2}, 432 } 433 // mocking 434 ghClient := &mocks.GithubRepoClient{} 435 ghClient.Test(t) 436 ghClient. 437 On("ListReleaseAssets", ctx, owner, repository, releaseID, mock.AnythingOfType("*github.ListOptions")).Return( 438 []*github.ReleaseAsset{ 439 {Name: &assetName1, ID: &assetID1, URL: &assetURL}, 440 {Name: &assetName2, ID: &assetID2, URL: &assetURL}, 441 }, 442 nil, 443 nil, 444 ). 445 On("DeleteReleaseAsset", ctx, owner, repository, mock.AnythingOfType("int64")).Return( 446 &github.Response{Response: &http.Response{StatusCode: 200}}, 447 nil, 448 ). 449 On("UploadReleaseAsset", ctx, owner, repository, releaseID, mock.AnythingOfType("*github.UploadOptions"), mock.AnythingOfType("*os.File")).Return( 450 &github.ReleaseAsset{URL: &assetURL}, 451 &github.Response{Response: &http.Response{StatusCode: 200}}, 452 nil, 453 ) 454 // test 455 err := uploadReleaseAssetList(ctx, releaseID, &stepConfig, ghClient) 456 // asserts 457 assert.NoError(t, err) 458 ghClient.AssertExpectations(t) 459 }) 460 } 461 462 func TestIsExcluded(t *testing.T) { 463 464 l1 := "label1" 465 l2 := "label2" 466 467 tt := []struct { 468 issue *github.Issue 469 excludeLabels []string 470 expected bool 471 }{ 472 {issue: nil, excludeLabels: nil, expected: false}, 473 {issue: &github.Issue{}, excludeLabels: nil, expected: false}, 474 {issue: &github.Issue{Labels: []*github.Label{{Name: &l1}}}, excludeLabels: nil, expected: false}, 475 {issue: &github.Issue{Labels: []*github.Label{{Name: &l1}}}, excludeLabels: []string{"label0"}, expected: false}, 476 {issue: &github.Issue{Labels: []*github.Label{{Name: &l1}}}, excludeLabels: []string{"label1"}, expected: true}, 477 {issue: &github.Issue{Labels: []*github.Label{{Name: &l1}, {Name: &l2}}}, excludeLabels: []string{}, expected: false}, 478 {issue: &github.Issue{Labels: []*github.Label{{Name: &l1}, {Name: &l2}}}, excludeLabels: []string{"label1"}, expected: true}, 479 } 480 481 for k, v := range tt { 482 assert.Equal(t, v.expected, isExcluded(v.issue, v.excludeLabels), fmt.Sprintf("Run %v failed", k)) 483 } 484 485 }