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  }