2 // ========================LICENSE_START=================================
5 // Copyright (C) 2021: Nordix Foundation
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
11 // http://www.apache.org/licenses/LICENSE-2.0
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===================================
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"
46 func Test_init(t *testing.T) {
47 assertions := require.New(t)
49 os.Setenv("CONSUMER_HOST", "consumerHost")
50 os.Setenv("CONSUMER_PORT", "8095")
57 wantedConfiguration := &config.Config{
58 ConsumerHost: "consumerHost",
60 InfoCoordinatorAddress: "http://enrichmentservice:8083",
61 SDNRAddress: "http://localhost:3904",
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,
69 assertions.Equal(wantedConfiguration, configuration)
71 assertions.Equal(fmt.Sprint(wantedConfiguration.ConsumerPort), consumerPort)
72 assertions.Equal(wantedConfiguration.ConsumerHost+":"+fmt.Sprint(wantedConfiguration.ConsumerPort), jobRegistrationInfo.JobResultURI)
74 wantedLinkFailureConfig := linkfailure.Configuration{
75 SDNRAddress: wantedConfiguration.SDNRAddress,
76 SDNRUser: wantedConfiguration.SDNRUser,
77 SDNRPassword: wantedConfiguration.SDNPassword,
79 assertions.Equal(wantedLinkFailureConfig, linkfailureConfig)
82 func Test_validateConfiguration(t *testing.T) {
83 assertions := require.New(t)
86 configuration *config.Config
94 name: "Valid config, should return nil",
96 configuration: &config.Config{
99 ConsumerCertPath: "security/consumer.crt",
100 ConsumerKeyPath: "security/consumer.key",
105 name: "Invalid config, should return error",
107 configuration: &config.Config{},
109 wantErr: fmt.Errorf("consumer host and port must be provided"),
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)
120 func Test_initializeLookupService(t *testing.T) {
121 assertions := require.New(t)
125 mockReturn [][]string
126 mockReturnError error
135 name: "Successful initialization, should return nil and lookup service should be initiated with data",
139 mockReturn: [][]string{{"1", "2"}},
144 name: "Unsuccessful initialization, should return error and lookup service should not be initiated with data",
147 mockReturnError: errors.New("Error"),
149 wantInitErr: errors.New("Error"),
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)
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)
166 func Test_getRouter_shouldContainAllPathsWithHandlers(t *testing.T) {
167 assertions := require.New(t)
170 messageHandlerRoute := r.Get("messageHandler")
171 assertions.NotNil(messageHandlerRoute)
172 supportedMethods, err := messageHandlerRoute.GetMethods()
173 assertions.Equal([]string{http.MethodPost}, supportedMethods)
175 path, _ := messageHandlerRoute.GetPathTemplate()
176 assertions.Equal("/", path)
178 startHandlerRoute := r.Get("start")
179 assertions.NotNil(messageHandlerRoute)
180 supportedMethods, err = startHandlerRoute.GetMethods()
181 assertions.Equal([]string{http.MethodPost}, supportedMethods)
183 path, _ = startHandlerRoute.GetPathTemplate()
184 assertions.Equal("/admin/start", path)
186 stopHandlerRoute := r.Get("stop")
187 assertions.NotNil(stopHandlerRoute)
188 supportedMethods, err = stopHandlerRoute.GetMethods()
189 assertions.Equal([]string{http.MethodPost}, supportedMethods)
191 path, _ = stopHandlerRoute.GetPathTemplate()
192 assertions.Equal("/admin/stop", path)
194 statusHandlerRoute := r.Get("status")
195 assertions.NotNil(statusHandlerRoute)
196 supportedMethods, err = statusHandlerRoute.GetMethods()
197 assertions.Equal([]string{http.MethodGet}, supportedMethods)
199 path, _ = statusHandlerRoute.GetPathTemplate()
200 assertions.Equal("/status", path)
203 func Test_startHandler(t *testing.T) {
204 assertions := require.New(t)
206 jobRegistrationInfo.JobResultURI = "host:80"
207 var job_definition interface{}
210 mockReturnBody []byte
220 name: "Start with successful registration, should return ok",
222 mockReturnBody: []byte(""),
223 mockReturnStatus: http.StatusOK,
225 wantedStatus: http.StatusOK,
228 name: "Start with error response at registration, should return error",
230 mockReturnBody: []byte("error"),
231 mockReturnStatus: http.StatusBadRequest,
233 wantedStatus: http.StatusBadRequest,
234 wantedBody: "Unable to register consumer job due to:",
237 for _, tt := range tests {
238 t.Run(tt.name, func(t *testing.T) {
239 clientMock := setUpClientMock(tt.args.mockReturnBody, tt.args.mockReturnStatus)
241 handler := http.HandlerFunc(startHandler)
242 responseRecorder := httptest.NewRecorder()
243 r, _ := http.NewRequest(http.MethodPost, "/start", nil)
245 handler.ServeHTTP(responseRecorder, r)
247 assertions.Equal(tt.wantedStatus, responseRecorder.Code, tt.name)
248 assertions.Contains(responseRecorder.Body.String(), tt.wantedBody, tt.name)
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"`
256 InfoTypeId: "STD_Fault_Messages",
257 JobResultUri: "host:80",
258 JobOwner: "O-RU Closed Loop Usecase",
259 JobDefinition: job_definition,
261 wantedBody, _ := json.Marshal(wantedJobRegistrationInfo)
263 var actualRequest *http.Request
264 clientMock.AssertCalled(t, "Do", mock.MatchedBy(func(req *http.Request) bool {
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)
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)
283 statusHandler.ServeHTTP(statusResponseRecorder, statusRequest)
285 assertions.Equal(http.StatusOK, statusResponseRecorder.Code)
286 assertions.Equal(`{"status": "started"}`, statusResponseRecorder.Body.String())
291 func Test_stopHandler(t *testing.T) {
292 assertions := require.New(t)
294 jobRegistrationInfo.JobResultURI = "host:80"
297 mockReturnBody []byte
307 name: "Stop with successful job deletion, should return ok",
309 mockReturnBody: []byte(""),
310 mockReturnStatus: http.StatusOK,
312 wantedStatus: http.StatusOK,
315 name: "Stop with error response at job deletion, should return error",
317 mockReturnBody: []byte("error"),
318 mockReturnStatus: http.StatusBadRequest,
320 wantedStatus: http.StatusBadRequest,
321 wantedBody: "Please remove job 14e7bb84-a44d-44c1-90b7-6995a92ad43c manually",
324 for _, tt := range tests {
325 t.Run(tt.name, func(t *testing.T) {
326 clientMock := setUpClientMock(tt.args.mockReturnBody, tt.args.mockReturnStatus)
328 handler := http.HandlerFunc(stopHandler)
329 responseRecorder := httptest.NewRecorder()
330 r, _ := http.NewRequest(http.MethodPost, "/stop", nil)
332 handler.ServeHTTP(responseRecorder, r)
334 assertions.Equal(tt.wantedStatus, responseRecorder.Code, tt.name)
335 assertions.Contains(responseRecorder.Body.String(), tt.wantedBody, tt.name)
337 var actualRequest *http.Request
338 clientMock.AssertCalled(t, "Do", mock.MatchedBy(func(req *http.Request) bool {
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)
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)
353 statusHandler.ServeHTTP(statusResponseRecorder, statusRequest)
355 assertions.Equal(http.StatusOK, statusResponseRecorder.Code)
356 assertions.Equal(`{"status": "stopped"}`, statusResponseRecorder.Body.String())
361 func Test_deleteOnShutdown(t *testing.T) {
362 assertions := require.New(t)
368 log.SetOutput(os.Stderr)
372 mockReturnBody []byte
381 name: "Delete with successful job deletion, should return ok",
383 mockReturnBody: []byte(""),
384 mockReturnStatus: http.StatusOK,
388 name: "Stop with error response at job deletion, should return error",
390 mockReturnBody: []byte("error"),
391 mockReturnStatus: http.StatusBadRequest,
393 wantedLog: "Please remove job 14e7bb84-a44d-44c1-90b7-6995a92ad43c manually",
396 for _, tt := range tests {
397 t.Run(tt.name, func(t *testing.T) {
398 setUpClientMock(tt.args.mockReturnBody, tt.args.mockReturnStatus)
400 c := make(chan os.Signal, 1)
401 go deleteOnShutdown(c)
404 waitForLogToBeWritten(&buf)
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)
416 func waitForLogToBeWritten(logBuf *bytes.Buffer) {
417 wg := sync.WaitGroup{}
420 if waitTimeout(&wg, 10*time.Millisecond) && logBuf.Len() != 0 {
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{})
437 return false // completed normally
438 case <-time.After(timeout):
439 return true // timed out
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)),