package telemetry import ( "encoding/json" "fmt" "github.com/SAP/jenkins-library/pkg/log" "github.com/SAP/jenkins-library/pkg/orchestrator" "github.com/jarcoal/httpmock" "github.com/sirupsen/logrus" "github.com/sirupsen/logrus/hooks/test" "net/http" "reflect" "regexp" "testing" "time" piperhttp "github.com/SAP/jenkins-library/pkg/http" "github.com/stretchr/testify/assert" ) func TestTelemetry_Initialize(t *testing.T) { type fields struct { baseData BaseData baseMetaData BaseMetaData data Data provider orchestrator.OrchestratorSpecificConfigProviding disabled bool client *piperhttp.Client CustomReportingDsn string CustomReportingToken string customClient *piperhttp.Client BaseURL string Endpoint string SiteID string } type args struct { telemetryDisabled bool stepName string } tests := []struct { name string fields fields args args want *piperhttp.Client }{ { name: "telemetry disabled", fields: fields{}, args: args{ telemetryDisabled: true, stepName: "test", }, want: nil, }, { name: "telemetry enabled", fields: fields{}, args: args{ telemetryDisabled: false, stepName: "test", }, want: &piperhttp.Client{}, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { telemetryClient := &Telemetry{} telemetryClient.Initialize(tt.args.telemetryDisabled, tt.args.stepName) // assert assert.NotEqual(t, tt.want, telemetryClient.client) assert.Equal(t, tt.args.stepName, telemetryClient.baseData.StepName) }) } } func TestTelemetry_Send(t *testing.T) { type fields struct { baseData BaseData baseMetaData BaseMetaData data Data provider orchestrator.OrchestratorSpecificConfigProviding disabled bool client *piperhttp.Client CustomReportingDsn string CustomReportingToken string BaseURL string Endpoint string SiteID string } tests := []struct { name string fields fields swaCalls int }{ { name: "Telemetry disabled, reporting disabled", fields: fields{ disabled: true, }, swaCalls: 0, }, { name: "Telemetry enabled", fields: fields{ disabled: false, }, swaCalls: 1, }, { name: "Telemetry disabled", fields: fields{ disabled: true, }, swaCalls: 0, }, } httpmock.Activate() defer httpmock.DeactivateAndReset() for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { httpmock.Reset() telemetryClient := &Telemetry{disabled: tt.fields.disabled} telemetryClient.Initialize(tt.fields.disabled, tt.name) telemetryClient.CustomReportingDsn = tt.fields.CustomReportingDsn if telemetryClient.client == nil { telemetryClient.client = &piperhttp.Client{} } url := telemetryClient.BaseURL + telemetryClient.Endpoint telemetryClient.client.SetOptions(piperhttp.ClientOptions{ MaxRequestDuration: 5 * time.Second, Token: "TOKEN", TransportSkipVerification: true, UseDefaultTransport: true, MaxRetries: -1, }) if tt.fields.CustomReportingDsn != "" { telemetryClient.customClient = &piperhttp.Client{} telemetryClient.customClient.SetOptions(piperhttp.ClientOptions{ MaxRequestDuration: 5 * time.Second, Token: "TOKEN", TransportSkipVerification: true, UseDefaultTransport: true, // Needed for mocking MaxRetries: -1, }) } httpmock.RegisterResponder(http.MethodGet, url, func(req *http.Request) (*http.Response, error) { return httpmock.NewStringResponse(200, "Ok"), nil }, ) httpmock.RegisterResponder(http.MethodPost, telemetryClient.CustomReportingDsn, func(req *http.Request) (*http.Response, error) { return httpmock.NewStringResponse(200, "Ok"), nil }, ) // test telemetryClient.SetData(&CustomData{}) telemetryClient.Send() // assert info := httpmock.GetCallCountInfo() if got := info["GET "+url]; !assert.Equal(t, got, tt.swaCalls) { t.Errorf("Send() = swa calls %v, wanted %v", got, tt.swaCalls) } }) } defer httpmock.DeactivateAndReset() } func TestSetData(t *testing.T) { type args struct { customData *CustomData } tests := []struct { name string args args want Data }{ { name: "Test", args: args{customData: &CustomData{ Duration: "100", ErrorCode: "0", ErrorCategory: "Undefined", PiperCommitHash: "abcd12345", }, }, want: Data{ BaseData: BaseData{ URL: "", ActionName: "", EventType: "", StepName: "TestCreateDataObject", SiteID: "", PipelineURLHash: "", BuildURLHash: "", Orchestrator: "Unknown", }, BaseMetaData: BaseMetaData{ StepNameLabel: "stepName", StageNameLabel: "stageName", PipelineURLHashLabel: "pipelineUrlHash", BuildURLHashLabel: "buildUrlHash", DurationLabel: "duration", ExitCodeLabel: "exitCode", ErrorCategoryLabel: "errorCategory", OrchestratorLabel: "orchestrator", PiperCommitHashLabel: "piperCommitHash", }, CustomData: CustomData{ Duration: "100", ErrorCode: "0", ErrorCategory: "Undefined", PiperCommitHash: "abcd12345", Custom1Label: "", Custom2Label: "", Custom3Label: "", Custom4Label: "", Custom5Label: "", Custom1: "", Custom2: "", Custom3: "", Custom4: "", Custom5: "", }, }, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { telemetryClient := Telemetry{} telemetryClient.Initialize(false, "TestCreateDataObject") telemetryClient.baseData = BaseData{ URL: "", ActionName: "", EventType: "", StepName: "TestCreateDataObject", SiteID: "", PipelineURLHash: "", BuildURLHash: "", Orchestrator: "Unknown", } telemetryClient.baseMetaData = baseMetaData telemetryClient.SetData(tt.args.customData) fmt.Println(telemetryClient.data) fmt.Println(tt.want) if !reflect.DeepEqual(telemetryClient.data, tt.want) { t.Errorf("CreateDataObject() t.data= %v, want %v", telemetryClient.data, tt.want) } }) } } func TestTelemetry_logStepTelemetryData(t *testing.T) { provider := &orchestrator.UnknownOrchestratorConfigProvider{} type fields struct { data Data provider orchestrator.OrchestratorSpecificConfigProviding } tests := []struct { name string fields fields fatalError logrus.Fields logOutput string }{ { name: "logging with error, no fatalError set", fields: fields{ data: Data{ BaseData: BaseData{}, BaseMetaData: BaseMetaData{}, CustomData: CustomData{ ErrorCode: "1", Duration: "200", PiperCommitHash: "n/a", }, }, provider: provider, }, }, { name: "logging with error, fatal error set", fields: fields{ data: Data{ BaseData: BaseData{}, BaseMetaData: BaseMetaData{}, CustomData: CustomData{ ErrorCode: "1", Duration: "200", PiperCommitHash: "n/a", }, }, provider: provider, }, fatalError: logrus.Fields{ "message": "Some error happened", "error": "Oh snap!", "category": "undefined", "result": "failure", "correlationId": "test", "time": "0000-00-00 00:00:00.000", }, }, { name: "logging without error", fields: fields{ data: Data{ CustomData: CustomData{ ErrorCode: "0", Duration: "200", PiperCommitHash: "n/a", }, }, provider: provider, }, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { _, hook := test.NewNullLogger() log.RegisterHook(hook) telemetry := &Telemetry{ data: tt.fields.data, provider: tt.fields.provider, } var re *regexp.Regexp if tt.fatalError != nil { errDetails, _ := json.Marshal(&tt.fatalError) log.SetFatalErrorDetail(errDetails) 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"}}`) } else { re = regexp.MustCompile(`Step telemetry data:{"StepStartTime":".*?","PipelineURLHash":"","BuildURLHash":"","StageName":"","StepName":"","ErrorCode":"\d","StepDuration":"\d+","ErrorCategory":"","CorrelationID":"n/a","PiperCommitHash":"n/a","ErrorDetail":null}`) } telemetry.logStepTelemetryData() assert.Regexp(t, re, hook.LastEntry().Message) hook.Reset() }) } }