github.com/ouraigua/jenkins-library@v0.0.0-20231028010029-fbeaf2f3aa9b/pkg/orchestrator/azureDevOps_test.go (about)

     1  //go:build unit
     2  // +build unit
     3  
     4  package orchestrator
     5  
     6  import (
     7  	"fmt"
     8  	"net/http"
     9  	"os"
    10  	"testing"
    11  	"time"
    12  
    13  	piperhttp "github.com/SAP/jenkins-library/pkg/http"
    14  	"github.com/jarcoal/httpmock"
    15  	"github.com/pkg/errors"
    16  
    17  	"github.com/stretchr/testify/assert"
    18  )
    19  
    20  func TestAzure(t *testing.T) {
    21  	t.Run("Azure - BranchBuild", func(t *testing.T) {
    22  		defer resetEnv(os.Environ())
    23  		os.Clearenv()
    24  		os.Setenv("AZURE_HTTP_USER_AGENT", "FOO BAR BAZ")
    25  		os.Setenv("BUILD_SOURCEBRANCH", "refs/heads/feat/test-azure")
    26  		os.Setenv("SYSTEM_TEAMFOUNDATIONCOLLECTIONURI", "https://pogo.sap/")
    27  		os.Setenv("SYSTEM_TEAMPROJECT", "foo")
    28  		os.Setenv("BUILD_BUILDID", "42")
    29  		os.Setenv("BUILD_SOURCEVERSION", "abcdef42713")
    30  		os.Setenv("BUILD_REPOSITORY_URI", "github.com/foo/bar")
    31  		os.Setenv("SYSTEM_DEFINITIONNAME", "bar")
    32  		os.Setenv("SYSTEM_DEFINITIONID", "1234")
    33  		p, _ := NewOrchestratorSpecificConfigProvider()
    34  
    35  		assert.False(t, p.IsPullRequest())
    36  		assert.Equal(t, "feat/test-azure", p.GetBranch())
    37  		assert.Equal(t, "refs/heads/feat/test-azure", p.GetReference())
    38  		assert.Equal(t, "https://pogo.sap/foo/bar/_build/results?buildId=42", p.GetBuildURL())
    39  		assert.Equal(t, "abcdef42713", p.GetCommit())
    40  		assert.Equal(t, "github.com/foo/bar", p.GetRepoURL())
    41  		assert.Equal(t, "Azure", p.OrchestratorType())
    42  		assert.Equal(t, "https://pogo.sap/foo/bar/_build?definitionId=1234", p.GetJobURL())
    43  	})
    44  
    45  	t.Run("PR", func(t *testing.T) {
    46  		defer resetEnv(os.Environ())
    47  		os.Clearenv()
    48  		os.Setenv("SYSTEM_PULLREQUEST_SOURCEBRANCH", "feat/test-azure")
    49  		os.Setenv("SYSTEM_PULLREQUEST_TARGETBRANCH", "main")
    50  		os.Setenv("SYSTEM_PULLREQUEST_PULLREQUESTID", "42")
    51  		os.Setenv("BUILD_REASON", "PullRequest")
    52  
    53  		p := AzureDevOpsConfigProvider{}
    54  		c := p.GetPullRequestConfig()
    55  
    56  		assert.True(t, p.IsPullRequest())
    57  		assert.Equal(t, "feat/test-azure", c.Branch)
    58  		assert.Equal(t, "main", c.Base)
    59  		assert.Equal(t, "42", c.Key)
    60  	})
    61  
    62  	t.Run("PR - Branch Policy", func(t *testing.T) {
    63  		defer resetEnv(os.Environ())
    64  		os.Clearenv()
    65  		os.Setenv("SYSTEM_PULLREQUEST_SOURCEBRANCH", "feat/test-azure")
    66  		os.Setenv("SYSTEM_PULLREQUEST_TARGETBRANCH", "main")
    67  		os.Setenv("SYSTEM_PULLREQUEST_PULLREQUESTID", "123456789")
    68  		os.Setenv("SYSTEM_PULLREQUEST_PULLREQUESTNUMBER", "42")
    69  		os.Setenv("BUILD_REASON", "PullRequest")
    70  
    71  		p := AzureDevOpsConfigProvider{}
    72  		c := p.GetPullRequestConfig()
    73  
    74  		assert.True(t, p.IsPullRequest())
    75  		assert.Equal(t, "feat/test-azure", c.Branch)
    76  		assert.Equal(t, "main", c.Base)
    77  		assert.Equal(t, "42", c.Key)
    78  	})
    79  
    80  	t.Run("Azure DevOps - false", func(t *testing.T) {
    81  		defer resetEnv(os.Environ())
    82  		os.Clearenv()
    83  
    84  		os.Setenv("AZURE_HTTP_USER_AGENT", "false")
    85  
    86  		o := DetectOrchestrator()
    87  
    88  		assert.Equal(t, Orchestrator(Unknown), o)
    89  	})
    90  
    91  	t.Run("env variables", func(t *testing.T) {
    92  		defer resetEnv(os.Environ())
    93  		os.Clearenv()
    94  		os.Setenv("SYSTEM_COLLECTIONURI", "https://dev.azure.com/fabrikamfiber/")
    95  		os.Setenv("SYSTEM_TEAMPROJECTID", "123a4567-ab1c-12a1-1234-123456ab7890")
    96  		os.Setenv("BUILD_BUILDID", "42")
    97  		os.Setenv("AGENT_VERSION", "2.193.0")
    98  		os.Setenv("BUILD_BUILDNUMBER", "20220318.16")
    99  		os.Setenv("BUILD_REPOSITORY_NAME", "repo-org/repo-name")
   100  
   101  		p := AzureDevOpsConfigProvider{}
   102  
   103  		assert.Equal(t, "https://dev.azure.com/fabrikamfiber/", p.getSystemCollectionURI())
   104  		assert.Equal(t, "123a4567-ab1c-12a1-1234-123456ab7890", p.getTeamProjectID())
   105  		assert.Equal(t, "42", p.getAzureBuildID())     // Don't confuse getAzureBuildID and GetBuildID!
   106  		assert.Equal(t, "20220318.16", p.GetBuildID()) // buildNumber is used in the UI
   107  		assert.Equal(t, "2.193.0", p.OrchestratorVersion())
   108  		assert.Equal(t, "repo-org/repo-name", p.GetJobName())
   109  
   110  	})
   111  }
   112  
   113  func TestAzureDevOpsConfigProvider_GetPipelineStartTime(t *testing.T) {
   114  
   115  	tests := []struct {
   116  		name           string
   117  		apiInformation map[string]interface{}
   118  		want           time.Time
   119  	}{
   120  		{
   121  			name:           "Retrieve correct time",
   122  			apiInformation: map[string]interface{}{"startTime": "2022-03-18T12:30:42.0Z"},
   123  			want:           time.Date(2022, time.March, 18, 12, 30, 42, 0, time.UTC),
   124  		},
   125  		{
   126  			name:           "Empty apiInformation",
   127  			apiInformation: map[string]interface{}{},
   128  			want:           time.Date(1, time.January, 1, 0, 0, 0, 0, time.UTC),
   129  		},
   130  		{
   131  			name:           "apiInformation does not contain key",
   132  			apiInformation: map[string]interface{}{"someKey": "someValue"},
   133  			want:           time.Date(1, time.January, 1, 0, 0, 0, 0, time.UTC),
   134  		},
   135  		{
   136  			name:           "apiInformation contains malformed date",
   137  			apiInformation: map[string]interface{}{"startTime": "2022-03/18 12:30:42.0Z"},
   138  			want:           time.Date(1, time.January, 1, 0, 0, 0, 0, time.UTC),
   139  		},
   140  	}
   141  	for _, tt := range tests {
   142  		t.Run(tt.name, func(t *testing.T) {
   143  			a := &AzureDevOpsConfigProvider{}
   144  			a.apiInformation = tt.apiInformation
   145  			pipelineStartTime := a.GetPipelineStartTime()
   146  			assert.Equalf(t, tt.want, pipelineStartTime, "GetPipelineStartTime()")
   147  		})
   148  	}
   149  }
   150  
   151  func TestAzureDevOpsConfigProvider_GetBuildStatus(t *testing.T) {
   152  
   153  	tests := []struct {
   154  		name   string
   155  		want   string
   156  		envVar string
   157  	}{
   158  		{
   159  			name:   "Success",
   160  			envVar: "Succeeded",
   161  			want:   "SUCCESS",
   162  		},
   163  		{
   164  			name:   "aborted",
   165  			envVar: "Canceled",
   166  			want:   "ABORTED",
   167  		},
   168  		{
   169  			name:   "failure",
   170  			envVar: "failed",
   171  			want:   "FAILURE",
   172  		},
   173  		{
   174  			name:   "other",
   175  			envVar: "some other status",
   176  			want:   "FAILURE",
   177  		},
   178  	}
   179  	for _, tt := range tests {
   180  		t.Run(tt.name, func(t *testing.T) {
   181  			defer resetEnv(os.Environ())
   182  			os.Clearenv()
   183  			os.Setenv("AGENT_JOBSTATUS", tt.envVar)
   184  			a := &AzureDevOpsConfigProvider{}
   185  
   186  			assert.Equalf(t, tt.want, a.GetBuildStatus(), "GetBuildStatus()")
   187  		})
   188  	}
   189  }
   190  
   191  func TestAzureDevOpsConfigProvider_getAPIInformation(t *testing.T) {
   192  	tests := []struct {
   193  		name                    string
   194  		wantHTTPErr             bool
   195  		wantHTTPStatusCodeError bool
   196  		wantHTTPJSONParseError  bool
   197  		apiInformation          map[string]interface{}
   198  		wantAPIInformation      map[string]interface{}
   199  	}{
   200  		{
   201  			name:               "success case",
   202  			apiInformation:     map[string]interface{}{},
   203  			wantAPIInformation: map[string]interface{}{"Success": "Case"},
   204  		},
   205  		{
   206  			name:               "apiInformation already set",
   207  			apiInformation:     map[string]interface{}{"API info": "set"},
   208  			wantAPIInformation: map[string]interface{}{"API info": "set"},
   209  		},
   210  		{
   211  			name:               "failed to get response",
   212  			apiInformation:     map[string]interface{}{},
   213  			wantHTTPErr:        true,
   214  			wantAPIInformation: map[string]interface{}{},
   215  		},
   216  		{
   217  			name:                    "response code != 200 http.StatusNoContent",
   218  			wantHTTPStatusCodeError: true,
   219  			apiInformation:          map[string]interface{}{},
   220  			wantAPIInformation:      map[string]interface{}{},
   221  		},
   222  		{
   223  			name:                   "parseResponseBodyJson fails",
   224  			wantHTTPJSONParseError: true,
   225  			apiInformation:         map[string]interface{}{},
   226  			wantAPIInformation:     map[string]interface{}{},
   227  		},
   228  	}
   229  
   230  	for _, tt := range tests {
   231  		t.Run(tt.name, func(t *testing.T) {
   232  			a := &AzureDevOpsConfigProvider{
   233  				apiInformation: tt.apiInformation,
   234  			}
   235  
   236  			a.client.SetOptions(piperhttp.ClientOptions{
   237  				MaxRequestDuration:        5 * time.Second,
   238  				Token:                     "TOKEN",
   239  				TransportSkipVerification: true,
   240  				UseDefaultTransport:       true, // need to use default transport for http mock
   241  				MaxRetries:                -1,
   242  			})
   243  
   244  			defer resetEnv(os.Environ())
   245  			os.Clearenv()
   246  			os.Setenv("SYSTEM_COLLECTIONURI", "https://dev.azure.com/fabrikamfiber/")
   247  			os.Setenv("SYSTEM_TEAMPROJECTID", "123a4567-ab1c-12a1-1234-123456ab7890")
   248  			os.Setenv("BUILD_BUILDID", "1234")
   249  
   250  			fakeUrl := "https://dev.azure.com/fabrikamfiber/123a4567-ab1c-12a1-1234-123456ab7890/_apis/build/builds/1234/"
   251  			httpmock.Activate()
   252  			defer httpmock.DeactivateAndReset()
   253  			httpmock.RegisterResponder("GET", fakeUrl,
   254  				func(req *http.Request) (*http.Response, error) {
   255  					if tt.wantHTTPErr {
   256  						return nil, errors.New("this error shows up")
   257  					}
   258  					if tt.wantHTTPStatusCodeError {
   259  						return &http.Response{
   260  							Status:     "204",
   261  							StatusCode: http.StatusNoContent,
   262  							Request:    req,
   263  						}, nil
   264  					}
   265  					if tt.wantHTTPJSONParseError {
   266  						// Intentionally malformed JSON response
   267  						return httpmock.NewJsonResponse(200, "timestamp:broken")
   268  					}
   269  					return httpmock.NewStringResponse(200, "{\"Success\":\"Case\"}"), nil
   270  				},
   271  			)
   272  
   273  			a.fetchAPIInformation()
   274  			assert.Equal(t, tt.wantAPIInformation, a.apiInformation)
   275  		})
   276  	}
   277  }
   278  
   279  func TestAzureDevOpsConfigProvider_GetLog(t *testing.T) {
   280  	tests := []struct {
   281  		name                    string
   282  		want                    []byte
   283  		wantErr                 assert.ErrorAssertionFunc
   284  		wantHTTPErr             bool
   285  		wantHTTPStatusCodeError bool
   286  		wantLogCountError       bool
   287  	}{
   288  		{
   289  			name:    "Successfully got log file",
   290  			want:    []byte("Success"),
   291  			wantErr: assert.NoError,
   292  		},
   293  		{
   294  			name:              "Log count variable not available",
   295  			want:              []byte(""),
   296  			wantErr:           assert.NoError,
   297  			wantLogCountError: true,
   298  		},
   299  		{
   300  			name:        "HTTP error",
   301  			want:        []byte(""),
   302  			wantErr:     assert.Error,
   303  			wantHTTPErr: true,
   304  		},
   305  		{
   306  			name:                    "Status code error",
   307  			want:                    []byte(""),
   308  			wantErr:                 assert.NoError,
   309  			wantHTTPStatusCodeError: true,
   310  		},
   311  	}
   312  	for _, tt := range tests {
   313  		t.Run(tt.name, func(t *testing.T) {
   314  			a := &AzureDevOpsConfigProvider{}
   315  			a.client.SetOptions(piperhttp.ClientOptions{
   316  				MaxRequestDuration:        5 * time.Second,
   317  				Token:                     "TOKEN",
   318  				TransportSkipVerification: true,
   319  				UseDefaultTransport:       true, // need to use default transport for http mock
   320  				MaxRetries:                -1,
   321  			})
   322  
   323  			defer resetEnv(os.Environ())
   324  			os.Clearenv()
   325  			os.Setenv("SYSTEM_COLLECTIONURI", "https://dev.azure.com/fabrikamfiber/")
   326  			os.Setenv("SYSTEM_TEAMPROJECTID", "123a4567-ab1c-12a1-1234-123456ab7890")
   327  			os.Setenv("BUILD_BUILDID", "1234")
   328  
   329  			fakeUrl := "https://dev.azure.com/fabrikamfiber/123a4567-ab1c-12a1-1234-123456ab7890/_apis/build/builds/1234/logs"
   330  			httpmock.Activate()
   331  			defer httpmock.DeactivateAndReset()
   332  			httpmock.RegisterResponder("GET", fakeUrl+"/1",
   333  				func(req *http.Request) (*http.Response, error) {
   334  					return httpmock.NewStringResponse(200, "Success"), nil
   335  				})
   336  			httpmock.RegisterResponder("GET", fakeUrl,
   337  				func(req *http.Request) (*http.Response, error) {
   338  					if tt.wantHTTPErr {
   339  						return nil, errors.New("this error shows up")
   340  					}
   341  					if tt.wantHTTPStatusCodeError {
   342  						return &http.Response{
   343  							Status:     "204",
   344  							StatusCode: http.StatusNoContent,
   345  							Request:    req,
   346  						}, nil
   347  					}
   348  					if tt.wantLogCountError {
   349  						return httpmock.NewJsonResponse(200, map[string]interface{}{
   350  							"some": "value",
   351  						})
   352  					}
   353  					return httpmock.NewJsonResponse(200, map[string]interface{}{
   354  						"count": 1,
   355  					})
   356  				},
   357  			)
   358  			got, err := a.GetLog()
   359  			if !tt.wantErr(t, err, fmt.Sprintf("GetLog()")) {
   360  				return
   361  			}
   362  			assert.Equalf(t, tt.want, got, "GetLog()")
   363  		})
   364  	}
   365  }