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

     1  //go:build unit
     2  // +build unit
     3  
     4  package telemetry
     5  
     6  import (
     7  	"encoding/json"
     8  	"fmt"
     9  	"github.com/SAP/jenkins-library/pkg/log"
    10  	"github.com/SAP/jenkins-library/pkg/orchestrator"
    11  	"github.com/jarcoal/httpmock"
    12  	"github.com/sirupsen/logrus"
    13  	"github.com/sirupsen/logrus/hooks/test"
    14  	"net/http"
    15  	"reflect"
    16  	"regexp"
    17  	"testing"
    18  	"time"
    19  
    20  	piperhttp "github.com/SAP/jenkins-library/pkg/http"
    21  	"github.com/stretchr/testify/assert"
    22  )
    23  
    24  func TestTelemetry_Initialize(t *testing.T) {
    25  	type fields struct {
    26  		baseData             BaseData
    27  		baseMetaData         BaseMetaData
    28  		data                 Data
    29  		provider             orchestrator.OrchestratorSpecificConfigProviding
    30  		disabled             bool
    31  		client               *piperhttp.Client
    32  		CustomReportingDsn   string
    33  		CustomReportingToken string
    34  		customClient         *piperhttp.Client
    35  		BaseURL              string
    36  		Endpoint             string
    37  		SiteID               string
    38  	}
    39  	type args struct {
    40  		telemetryDisabled bool
    41  		stepName          string
    42  	}
    43  	tests := []struct {
    44  		name   string
    45  		fields fields
    46  		args   args
    47  		want   *piperhttp.Client
    48  	}{
    49  		{
    50  			name:   "telemetry disabled",
    51  			fields: fields{},
    52  			args: args{
    53  				telemetryDisabled: true,
    54  				stepName:          "test",
    55  			},
    56  			want: nil,
    57  		},
    58  		{
    59  			name:   "telemetry enabled",
    60  			fields: fields{},
    61  			args: args{
    62  				telemetryDisabled: false,
    63  				stepName:          "test",
    64  			},
    65  			want: &piperhttp.Client{},
    66  		},
    67  	}
    68  	for _, tt := range tests {
    69  		t.Run(tt.name, func(t *testing.T) {
    70  			telemetryClient := &Telemetry{}
    71  			telemetryClient.Initialize(tt.args.telemetryDisabled, tt.args.stepName)
    72  			// assert
    73  			assert.NotEqual(t, tt.want, telemetryClient.client)
    74  			assert.Equal(t, tt.args.stepName, telemetryClient.baseData.StepName)
    75  		})
    76  	}
    77  }
    78  
    79  func TestTelemetry_Send(t *testing.T) {
    80  	type fields struct {
    81  		baseData             BaseData
    82  		baseMetaData         BaseMetaData
    83  		data                 Data
    84  		provider             orchestrator.OrchestratorSpecificConfigProviding
    85  		disabled             bool
    86  		client               *piperhttp.Client
    87  		CustomReportingDsn   string
    88  		CustomReportingToken string
    89  		BaseURL              string
    90  		Endpoint             string
    91  		SiteID               string
    92  	}
    93  	tests := []struct {
    94  		name     string
    95  		fields   fields
    96  		swaCalls int
    97  	}{
    98  		{
    99  			name: "Telemetry disabled, reporting disabled",
   100  			fields: fields{
   101  				disabled: true,
   102  			},
   103  			swaCalls: 0,
   104  		},
   105  		{
   106  			name: "Telemetry enabled",
   107  			fields: fields{
   108  				disabled: false,
   109  			},
   110  			swaCalls: 1,
   111  		},
   112  		{
   113  			name: "Telemetry disabled",
   114  			fields: fields{
   115  				disabled: true,
   116  			},
   117  			swaCalls: 0,
   118  		},
   119  	}
   120  
   121  	httpmock.Activate()
   122  	defer httpmock.DeactivateAndReset()
   123  	for _, tt := range tests {
   124  		t.Run(tt.name, func(t *testing.T) {
   125  			httpmock.Reset()
   126  			telemetryClient := &Telemetry{disabled: tt.fields.disabled}
   127  			telemetryClient.Initialize(tt.fields.disabled, tt.name)
   128  			telemetryClient.CustomReportingDsn = tt.fields.CustomReportingDsn
   129  			if telemetryClient.client == nil {
   130  				telemetryClient.client = &piperhttp.Client{}
   131  			}
   132  
   133  			url := telemetryClient.BaseURL + telemetryClient.Endpoint
   134  
   135  			telemetryClient.client.SetOptions(piperhttp.ClientOptions{
   136  				MaxRequestDuration:        5 * time.Second,
   137  				Token:                     "TOKEN",
   138  				TransportSkipVerification: true,
   139  				UseDefaultTransport:       true,
   140  				MaxRetries:                -1,
   141  			})
   142  
   143  			if tt.fields.CustomReportingDsn != "" {
   144  				telemetryClient.customClient = &piperhttp.Client{}
   145  				telemetryClient.customClient.SetOptions(piperhttp.ClientOptions{
   146  					MaxRequestDuration:        5 * time.Second,
   147  					Token:                     "TOKEN",
   148  					TransportSkipVerification: true,
   149  					UseDefaultTransport:       true, // Needed for mocking
   150  					MaxRetries:                -1,
   151  				})
   152  			}
   153  
   154  			httpmock.RegisterResponder(http.MethodGet, url,
   155  				func(req *http.Request) (*http.Response, error) {
   156  					return httpmock.NewStringResponse(200, "Ok"), nil
   157  				},
   158  			)
   159  			httpmock.RegisterResponder(http.MethodPost, telemetryClient.CustomReportingDsn,
   160  				func(req *http.Request) (*http.Response, error) {
   161  					return httpmock.NewStringResponse(200, "Ok"), nil
   162  				},
   163  			)
   164  
   165  			// test
   166  			telemetryClient.SetData(&CustomData{})
   167  			telemetryClient.Send()
   168  
   169  			// assert
   170  			info := httpmock.GetCallCountInfo()
   171  
   172  			if got := info["GET "+url]; !assert.Equal(t, got, tt.swaCalls) {
   173  				t.Errorf("Send() = swa calls %v, wanted %v", got, tt.swaCalls)
   174  			}
   175  
   176  		})
   177  	}
   178  	defer httpmock.DeactivateAndReset()
   179  }
   180  
   181  func TestSetData(t *testing.T) {
   182  	type args struct {
   183  		customData *CustomData
   184  	}
   185  	tests := []struct {
   186  		name string
   187  		args args
   188  		want Data
   189  	}{
   190  		{
   191  			name: "Test",
   192  			args: args{customData: &CustomData{
   193  				Duration:        "100",
   194  				ErrorCode:       "0",
   195  				ErrorCategory:   "Undefined",
   196  				PiperCommitHash: "abcd12345",
   197  			},
   198  			},
   199  			want: Data{
   200  				BaseData: BaseData{
   201  					URL:             "",
   202  					ActionName:      "",
   203  					EventType:       "",
   204  					StepName:        "TestCreateDataObject",
   205  					SiteID:          "",
   206  					PipelineURLHash: "",
   207  					BuildURLHash:    "",
   208  					Orchestrator:    "Unknown",
   209  				},
   210  				BaseMetaData: BaseMetaData{
   211  					StepNameLabel:        "stepName",
   212  					StageNameLabel:       "stageName",
   213  					PipelineURLHashLabel: "pipelineUrlHash",
   214  					BuildURLHashLabel:    "buildUrlHash",
   215  					DurationLabel:        "duration",
   216  					ExitCodeLabel:        "exitCode",
   217  					ErrorCategoryLabel:   "errorCategory",
   218  					OrchestratorLabel:    "orchestrator",
   219  					PiperCommitHashLabel: "piperCommitHash",
   220  				},
   221  				CustomData: CustomData{
   222  					Duration:        "100",
   223  					ErrorCode:       "0",
   224  					ErrorCategory:   "Undefined",
   225  					PiperCommitHash: "abcd12345",
   226  					Custom1Label:    "",
   227  					Custom2Label:    "",
   228  					Custom3Label:    "",
   229  					Custom4Label:    "",
   230  					Custom5Label:    "",
   231  					Custom1:         "",
   232  					Custom2:         "",
   233  					Custom3:         "",
   234  					Custom4:         "",
   235  					Custom5:         "",
   236  				},
   237  			},
   238  		},
   239  	}
   240  	for _, tt := range tests {
   241  		t.Run(tt.name, func(t *testing.T) {
   242  			telemetryClient := Telemetry{}
   243  			telemetryClient.Initialize(false, "TestCreateDataObject")
   244  			telemetryClient.baseData = BaseData{
   245  				URL:             "",
   246  				ActionName:      "",
   247  				EventType:       "",
   248  				StepName:        "TestCreateDataObject",
   249  				SiteID:          "",
   250  				PipelineURLHash: "",
   251  				BuildURLHash:    "",
   252  				Orchestrator:    "Unknown",
   253  			}
   254  			telemetryClient.baseMetaData = baseMetaData
   255  			telemetryClient.SetData(tt.args.customData)
   256  			fmt.Println(telemetryClient.data)
   257  			fmt.Println(tt.want)
   258  			if !reflect.DeepEqual(telemetryClient.data, tt.want) {
   259  				t.Errorf("CreateDataObject() t.data= %v, want %v", telemetryClient.data, tt.want)
   260  			}
   261  		})
   262  	}
   263  }
   264  
   265  func TestTelemetry_logStepTelemetryData(t *testing.T) {
   266  
   267  	provider := &orchestrator.UnknownOrchestratorConfigProvider{}
   268  
   269  	type fields struct {
   270  		data     Data
   271  		provider orchestrator.OrchestratorSpecificConfigProviding
   272  	}
   273  	tests := []struct {
   274  		name       string
   275  		fields     fields
   276  		fatalError logrus.Fields
   277  		logOutput  string
   278  	}{
   279  		{
   280  			name: "logging with error, no fatalError set",
   281  			fields: fields{
   282  				data: Data{
   283  					BaseData:     BaseData{},
   284  					BaseMetaData: BaseMetaData{},
   285  					CustomData: CustomData{
   286  						ErrorCode:       "1",
   287  						Duration:        "200",
   288  						PiperCommitHash: "n/a",
   289  					},
   290  				},
   291  				provider: provider,
   292  			},
   293  		},
   294  		{
   295  			name: "logging with error, fatal error set",
   296  			fields: fields{
   297  				data: Data{
   298  					BaseData:     BaseData{},
   299  					BaseMetaData: BaseMetaData{},
   300  					CustomData: CustomData{
   301  						ErrorCode:       "1",
   302  						Duration:        "200",
   303  						PiperCommitHash: "n/a",
   304  					},
   305  				},
   306  				provider: provider,
   307  			},
   308  			fatalError: logrus.Fields{
   309  				"message":       "Some error happened",
   310  				"error":         "Oh snap!",
   311  				"category":      "undefined",
   312  				"result":        "failure",
   313  				"correlationId": "test",
   314  				"time":          "0000-00-00 00:00:00.000",
   315  			},
   316  		},
   317  		{
   318  			name: "logging without error",
   319  			fields: fields{
   320  				data: Data{
   321  					CustomData: CustomData{
   322  						ErrorCode:       "0",
   323  						Duration:        "200",
   324  						PiperCommitHash: "n/a",
   325  					},
   326  				},
   327  				provider: provider,
   328  			},
   329  		},
   330  	}
   331  	for _, tt := range tests {
   332  		t.Run(tt.name, func(t *testing.T) {
   333  			_, hook := test.NewNullLogger()
   334  			log.RegisterHook(hook)
   335  			telemetry := &Telemetry{
   336  				data:     tt.fields.data,
   337  				provider: tt.fields.provider,
   338  			}
   339  			var re *regexp.Regexp
   340  			if tt.fatalError != nil {
   341  				errDetails, _ := json.Marshal(&tt.fatalError)
   342  				log.SetFatalErrorDetail(errDetails)
   343  				re = regexp.MustCompile(`Step telemetry data:{"StepStartTime":".*?","PipelineURLHash":"","BuildURLHash":"","StageName":"","StepName":"","ErrorCode":"\d","StepDuration":"\d+","ErrorCategory":"","CorrelationID":"n/a","PiperCommitHash":"n/a","ErrorDetail":{"category":"undefined","correlationId":"test","error":"Oh snap!","message":"Some error happened","result":"failure","time":"0000-00-00 00:00:00.000"}}`)
   344  
   345  			} else {
   346  				re = regexp.MustCompile(`Step telemetry data:{"StepStartTime":".*?","PipelineURLHash":"","BuildURLHash":"","StageName":"","StepName":"","ErrorCode":"\d","StepDuration":"\d+","ErrorCategory":"","CorrelationID":"n/a","PiperCommitHash":"n/a","ErrorDetail":null}`)
   347  			}
   348  			telemetry.logStepTelemetryData()
   349  			assert.Regexp(t, re, hook.LastEntry().Message)
   350  			hook.Reset()
   351  		})
   352  	}
   353  }