Fix sonar job after Go version uplift
[nonrtric/rapp/orufhrecovery.git] / goversion / main_test.go
1 // -
2 //   ========================LICENSE_START=================================
3 //   O-RAN-SC
4 //   %%
5 //   Copyright (C) 2021: Nordix Foundation
6 //   %%
7 //   Licensed under the Apache License, Version 2.0 (the "License");
8 //   you may not use this file except in compliance with the License.
9 //   You may obtain a copy of the License at
10 //
11 //        http://www.apache.org/licenses/LICENSE-2.0
12 //
13 //   Unless required by applicable law or agreed to in writing, software
14 //   distributed under the License is distributed on an "AS IS" BASIS,
15 //   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 //   See the License for the specific language governing permissions and
17 //   limitations under the License.
18 //   ========================LICENSE_END===================================
19 //
20
21 package main
22
23 import (
24         "bytes"
25         "encoding/json"
26         "errors"
27         "fmt"
28         "io/ioutil"
29         "net/http"
30         "net/http/httptest"
31         "os"
32         "sync"
33         "syscall"
34         "testing"
35         "time"
36
37         log "github.com/sirupsen/logrus"
38         "github.com/stretchr/testify/mock"
39         "github.com/stretchr/testify/require"
40         "oransc.org/usecase/oruclosedloop/internal/config"
41         "oransc.org/usecase/oruclosedloop/internal/linkfailure"
42         repomock "oransc.org/usecase/oruclosedloop/internal/repository/mocks"
43         "oransc.org/usecase/oruclosedloop/internal/restclient/mocks"
44 )
45
46 func Test_init(t *testing.T) {
47         assertions := require.New(t)
48
49         os.Setenv("CONSUMER_HOST", "consumerHost")
50         os.Setenv("CONSUMER_PORT", "8095")
51         t.Cleanup(func() {
52                 os.Clearenv()
53         })
54
55         doInit()
56
57         wantedConfiguration := &config.Config{
58                 ConsumerHost:           "consumerHost",
59                 ConsumerPort:           8095,
60                 InfoCoordinatorAddress: "http://enrichmentservice:8083",
61                 SDNRAddress:            "http://localhost:3904",
62                 SDNRUser:               "admin",
63                 SDNPassword:            "Kp8bJ4SXszM0WXlhak3eHlcse2gAw84vaoGGmJvUy2U",
64                 ORUToODUMapFile:        "o-ru-to-o-du-map.csv",
65                 ConsumerCertPath:       "security/consumer.crt",
66                 ConsumerKeyPath:        "security/consumer.key",
67                 LogLevel:               log.InfoLevel,
68         }
69         assertions.Equal(wantedConfiguration, configuration)
70
71         assertions.Equal(fmt.Sprint(wantedConfiguration.ConsumerPort), consumerPort)
72         assertions.Equal(wantedConfiguration.ConsumerHost+":"+fmt.Sprint(wantedConfiguration.ConsumerPort), jobRegistrationInfo.JobResultURI)
73
74         wantedLinkFailureConfig := linkfailure.Configuration{
75                 SDNRAddress:  wantedConfiguration.SDNRAddress,
76                 SDNRUser:     wantedConfiguration.SDNRUser,
77                 SDNRPassword: wantedConfiguration.SDNPassword,
78         }
79         assertions.Equal(wantedLinkFailureConfig, linkfailureConfig)
80 }
81
82 func Test_validateConfiguration(t *testing.T) {
83         assertions := require.New(t)
84
85         type args struct {
86                 configuration *config.Config
87         }
88         tests := []struct {
89                 name    string
90                 args    args
91                 wantErr error
92         }{
93                 {
94                         name: "Valid config, should return nil",
95                         args: args{
96                                 configuration: &config.Config{
97                                         ConsumerHost:     "host",
98                                         ConsumerPort:     80,
99                                         ConsumerCertPath: "security/consumer.crt",
100                                         ConsumerKeyPath:  "security/consumer.key",
101                                 },
102                         },
103                 },
104                 {
105                         name: "Invalid config, should return error",
106                         args: args{
107                                 configuration: &config.Config{},
108                         },
109                         wantErr: fmt.Errorf("consumer host and port must be provided"),
110                 },
111         }
112         for _, tt := range tests {
113                 t.Run(tt.name, func(t *testing.T) {
114                         err := validateConfiguration(tt.args.configuration)
115                         assertions.Equal(tt.wantErr, err)
116                 })
117         }
118 }
119
120 func Test_initializeLookupService(t *testing.T) {
121         assertions := require.New(t)
122         type args struct {
123                 csvFile         string
124                 oRuId           string
125                 mockReturn      [][]string
126                 mockReturnError error
127         }
128         tests := []struct {
129                 name        string
130                 args        args
131                 wantODuId   string
132                 wantInitErr error
133         }{
134                 {
135                         name: "Successful initialization, should return nil and lookup service should be initiated with data",
136                         args: args{
137                                 csvFile:    "file",
138                                 oRuId:      "1",
139                                 mockReturn: [][]string{{"1", "2"}},
140                         },
141                         wantODuId: "2",
142                 },
143                 {
144                         name: "Unsuccessful initialization, should return error and lookup service should not be initiated with data",
145                         args: args{
146                                 csvFile:         "file",
147                                 mockReturnError: errors.New("Error"),
148                         },
149                         wantInitErr: errors.New("Error"),
150                 },
151         }
152         for _, tt := range tests {
153                 t.Run(tt.name, func(t *testing.T) {
154                         mockCsvFileHelper := &repomock.CsvFileHelper{}
155                         mockCsvFileHelper.On("GetCsvFromFile", mock.Anything).Return(tt.args.mockReturn, tt.args.mockReturnError)
156
157                         err := initializeLookupService(mockCsvFileHelper, tt.args.csvFile)
158                         oDuId, _ := lookupService.GetODuID(tt.args.oRuId)
159                         assertions.Equal(tt.wantODuId, oDuId)
160                         assertions.Equal(tt.wantInitErr, err)
161                         mockCsvFileHelper.AssertCalled(t, "GetCsvFromFile", tt.args.csvFile)
162                 })
163         }
164 }
165
166 func Test_getRouter_shouldContainAllPathsWithHandlers(t *testing.T) {
167         assertions := require.New(t)
168
169         r := getRouter()
170         messageHandlerRoute := r.Get("messageHandler")
171         assertions.NotNil(messageHandlerRoute)
172         supportedMethods, err := messageHandlerRoute.GetMethods()
173         assertions.Equal([]string{http.MethodPost}, supportedMethods)
174         assertions.Nil(err)
175         path, _ := messageHandlerRoute.GetPathTemplate()
176         assertions.Equal("/", path)
177
178         startHandlerRoute := r.Get("start")
179         assertions.NotNil(messageHandlerRoute)
180         supportedMethods, err = startHandlerRoute.GetMethods()
181         assertions.Equal([]string{http.MethodPost}, supportedMethods)
182         assertions.Nil(err)
183         path, _ = startHandlerRoute.GetPathTemplate()
184         assertions.Equal("/admin/start", path)
185
186         stopHandlerRoute := r.Get("stop")
187         assertions.NotNil(stopHandlerRoute)
188         supportedMethods, err = stopHandlerRoute.GetMethods()
189         assertions.Equal([]string{http.MethodPost}, supportedMethods)
190         assertions.Nil(err)
191         path, _ = stopHandlerRoute.GetPathTemplate()
192         assertions.Equal("/admin/stop", path)
193
194         statusHandlerRoute := r.Get("status")
195         assertions.NotNil(statusHandlerRoute)
196         supportedMethods, err = statusHandlerRoute.GetMethods()
197         assertions.Equal([]string{http.MethodGet}, supportedMethods)
198         assertions.Nil(err)
199         path, _ = statusHandlerRoute.GetPathTemplate()
200         assertions.Equal("/status", path)
201 }
202
203 func Test_startHandler(t *testing.T) {
204         assertions := require.New(t)
205
206         jobRegistrationInfo.JobResultURI = "host:80"
207         var job_definition interface{}
208
209         type args struct {
210                 mockReturnBody   []byte
211                 mockReturnStatus int
212         }
213         tests := []struct {
214                 name         string
215                 args         args
216                 wantedStatus int
217                 wantedBody   string
218         }{
219                 {
220                         name: "Start with successful registration, should return ok",
221                         args: args{
222                                 mockReturnBody:   []byte(""),
223                                 mockReturnStatus: http.StatusOK,
224                         },
225                         wantedStatus: http.StatusOK,
226                 },
227                 {
228                         name: "Start with error response at registration, should return error",
229                         args: args{
230                                 mockReturnBody:   []byte("error"),
231                                 mockReturnStatus: http.StatusBadRequest,
232                         },
233                         wantedStatus: http.StatusBadRequest,
234                         wantedBody:   "Unable to register consumer job due to:",
235                 },
236         }
237         for _, tt := range tests {
238                 t.Run(tt.name, func(t *testing.T) {
239                         clientMock := setUpClientMock(tt.args.mockReturnBody, tt.args.mockReturnStatus)
240
241                         handler := http.HandlerFunc(startHandler)
242                         responseRecorder := httptest.NewRecorder()
243                         r, _ := http.NewRequest(http.MethodPost, "/start", nil)
244
245                         handler.ServeHTTP(responseRecorder, r)
246
247                         assertions.Equal(tt.wantedStatus, responseRecorder.Code, tt.name)
248                         assertions.Contains(responseRecorder.Body.String(), tt.wantedBody, tt.name)
249
250                         var wantedJobRegistrationInfo = struct {
251                                 InfoTypeId    string      `json:"info_type_id"`
252                                 JobResultUri  string      `json:"job_result_uri"`
253                                 JobOwner      string      `json:"job_owner"`
254                                 JobDefinition interface{} `json:"job_definition"`
255                         }{
256                                 InfoTypeId:    "STD_Fault_Messages",
257                                 JobResultUri:  "host:80",
258                                 JobOwner:      "O-RU Closed Loop Usecase",
259                                 JobDefinition: job_definition,
260                         }
261                         wantedBody, _ := json.Marshal(wantedJobRegistrationInfo)
262
263                         var actualRequest *http.Request
264                         clientMock.AssertCalled(t, "Do", mock.MatchedBy(func(req *http.Request) bool {
265                                 actualRequest = req
266                                 return true
267                         }))
268                         assertions.Equal(http.MethodPut, actualRequest.Method)
269                         assertions.Equal("http", actualRequest.URL.Scheme)
270                         assertions.Equal("enrichmentservice:8083", actualRequest.URL.Host)
271                         assertions.Equal("/data-consumer/v1/info-jobs/14e7bb84-a44d-44c1-90b7-6995a92ad43c", actualRequest.URL.Path)
272                         assertions.Equal("application/json; charset=utf-8", actualRequest.Header.Get("Content-Type"))
273                         body, _ := ioutil.ReadAll(actualRequest.Body)
274                         expectedBody := wantedBody
275                         assertions.Equal(expectedBody, body)
276                         clientMock.AssertNumberOfCalls(t, "Do", 1)
277
278                         // Check that the running status is "started"
279                         statusHandler := http.HandlerFunc(statusHandler)
280                         statusResponseRecorder := httptest.NewRecorder()
281                         statusRequest, _ := http.NewRequest(http.MethodGet, "/status", nil)
282
283                         statusHandler.ServeHTTP(statusResponseRecorder, statusRequest)
284
285                         assertions.Equal(http.StatusOK, statusResponseRecorder.Code)
286                         assertions.Equal(`{"status": "started"}`, statusResponseRecorder.Body.String())
287                 })
288         }
289 }
290
291 func Test_stopHandler(t *testing.T) {
292         assertions := require.New(t)
293
294         jobRegistrationInfo.JobResultURI = "host:80"
295
296         type args struct {
297                 mockReturnBody   []byte
298                 mockReturnStatus int
299         }
300         tests := []struct {
301                 name         string
302                 args         args
303                 wantedStatus int
304                 wantedBody   string
305         }{
306                 {
307                         name: "Stop with successful job deletion, should return ok",
308                         args: args{
309                                 mockReturnBody:   []byte(""),
310                                 mockReturnStatus: http.StatusOK,
311                         },
312                         wantedStatus: http.StatusOK,
313                 },
314                 {
315                         name: "Stop with error response at job deletion, should return error",
316                         args: args{
317                                 mockReturnBody:   []byte("error"),
318                                 mockReturnStatus: http.StatusBadRequest,
319                         },
320                         wantedStatus: http.StatusBadRequest,
321                         wantedBody:   "Please remove job 14e7bb84-a44d-44c1-90b7-6995a92ad43c manually",
322                 },
323         }
324         for _, tt := range tests {
325                 t.Run(tt.name, func(t *testing.T) {
326                         clientMock := setUpClientMock(tt.args.mockReturnBody, tt.args.mockReturnStatus)
327
328                         handler := http.HandlerFunc(stopHandler)
329                         responseRecorder := httptest.NewRecorder()
330                         r, _ := http.NewRequest(http.MethodPost, "/stop", nil)
331
332                         handler.ServeHTTP(responseRecorder, r)
333
334                         assertions.Equal(tt.wantedStatus, responseRecorder.Code, tt.name)
335                         assertions.Contains(responseRecorder.Body.String(), tt.wantedBody, tt.name)
336
337                         var actualRequest *http.Request
338                         clientMock.AssertCalled(t, "Do", mock.MatchedBy(func(req *http.Request) bool {
339                                 actualRequest = req
340                                 return true
341                         }))
342                         assertions.Equal(http.MethodDelete, actualRequest.Method)
343                         assertions.Equal("http", actualRequest.URL.Scheme)
344                         assertions.Equal("enrichmentservice:8083", actualRequest.URL.Host)
345                         assertions.Equal("/data-consumer/v1/info-jobs/14e7bb84-a44d-44c1-90b7-6995a92ad43c", actualRequest.URL.Path)
346                         clientMock.AssertNumberOfCalls(t, "Do", 1)
347
348                         // Check that the running status is "stopped"
349                         statusHandler := http.HandlerFunc(statusHandler)
350                         statusResponseRecorder := httptest.NewRecorder()
351                         statusRequest, _ := http.NewRequest(http.MethodGet, "/status", nil)
352
353                         statusHandler.ServeHTTP(statusResponseRecorder, statusRequest)
354
355                         assertions.Equal(http.StatusOK, statusResponseRecorder.Code)
356                         assertions.Equal(`{"status": "stopped"}`, statusResponseRecorder.Body.String())
357                 })
358         }
359 }
360
361 func Test_deleteOnShutdown(t *testing.T) {
362         assertions := require.New(t)
363
364         var buf bytes.Buffer
365         log.SetOutput(&buf)
366
367         t.Cleanup(func() {
368                 log.SetOutput(os.Stderr)
369         })
370
371         type args struct {
372                 mockReturnBody   []byte
373                 mockReturnStatus int
374         }
375         tests := []struct {
376                 name      string
377                 args      args
378                 wantedLog string
379         }{
380                 {
381                         name: "Delete with successful job deletion, should return ok",
382                         args: args{
383                                 mockReturnBody:   []byte(""),
384                                 mockReturnStatus: http.StatusOK,
385                         },
386                 },
387                 {
388                         name: "Stop with error response at job deletion, should return error",
389                         args: args{
390                                 mockReturnBody:   []byte("error"),
391                                 mockReturnStatus: http.StatusBadRequest,
392                         },
393                         wantedLog: "Please remove job 14e7bb84-a44d-44c1-90b7-6995a92ad43c manually",
394                 },
395         }
396         for _, tt := range tests {
397                 t.Run(tt.name, func(t *testing.T) {
398                         setUpClientMock(tt.args.mockReturnBody, tt.args.mockReturnStatus)
399
400                         c := make(chan os.Signal, 1)
401                         go deleteOnShutdown(c)
402                         c <- syscall.SIGTERM
403
404                         waitForLogToBeWritten(&buf)
405
406                         log := buf.String()
407                         if tt.wantedLog != "" {
408                                 assertions.Contains(log, "level=error")
409                                 assertions.Contains(log, "Unable to delete job on shutdown due to:")
410                                 assertions.Contains(log, tt.wantedLog)
411                         }
412                 })
413         }
414 }
415
416 func waitForLogToBeWritten(logBuf *bytes.Buffer) {
417         wg := sync.WaitGroup{}
418         wg.Add(1)
419         for {
420                 if waitTimeout(&wg, 10*time.Millisecond) && logBuf.Len() != 0 {
421                         wg.Done()
422                         break
423                 }
424         }
425 }
426
427 // waitTimeout waits for the waitgroup for the specified max timeout.
428 // Returns true if waiting timed out.
429 func waitTimeout(wg *sync.WaitGroup, timeout time.Duration) bool {
430         c := make(chan struct{})
431         go func() {
432                 defer close(c)
433                 wg.Wait()
434         }()
435         select {
436         case <-c:
437                 return false // completed normally
438         case <-time.After(timeout):
439                 return true // timed out
440         }
441 }
442
443 func setUpClientMock(body []byte, status int) *mocks.HTTPClient {
444         clientMock := mocks.HTTPClient{}
445         clientMock.On("Do", mock.Anything).Return(&http.Response{
446                 Body:       ioutil.NopCloser(bytes.NewReader(body)),
447                 StatusCode: status,
448         }, nil)
449         client = &clientMock
450         return &clientMock
451 }