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 "oransc.org/usecase/oruclosedloop/mocks"
45 func Test_init(t *testing.T) {
46 assertions := require.New(t)
48 os.Setenv("CONSUMER_HOST", "consumerHost")
49 os.Setenv("CONSUMER_PORT", "8095")
56 wantedConfiguration := &config.Config{
57 ConsumerHost: "consumerHost",
59 InfoCoordinatorAddress: "http://enrichmentservice:8083",
60 SDNRAddress: "http://localhost:3904",
62 SDNPassword: "Kp8bJ4SXszM0WXlhak3eHlcse2gAw84vaoGGmJvUy2U",
63 ORUToODUMapFile: "o-ru-to-o-du-map.csv",
64 ConsumerCertPath: "security/consumer.crt",
65 ConsumerKeyPath: "security/consumer.key",
66 LogLevel: log.InfoLevel,
68 assertions.Equal(wantedConfiguration, configuration)
70 assertions.Equal(fmt.Sprint(wantedConfiguration.ConsumerPort), consumerPort)
71 assertions.Equal(wantedConfiguration.ConsumerHost+":"+fmt.Sprint(wantedConfiguration.ConsumerPort), jobRegistrationInfo.JobResultURI)
73 wantedLinkFailureConfig := linkfailure.Configuration{
74 SDNRAddress: wantedConfiguration.SDNRAddress,
75 SDNRUser: wantedConfiguration.SDNRUser,
76 SDNRPassword: wantedConfiguration.SDNPassword,
78 assertions.Equal(wantedLinkFailureConfig, linkfailureConfig)
81 func Test_validateConfiguration(t *testing.T) {
82 assertions := require.New(t)
85 configuration *config.Config
93 name: "Valid config, should return nil",
95 configuration: &config.Config{
98 ConsumerCertPath: "security/consumer.crt",
99 ConsumerKeyPath: "security/consumer.key",
104 name: "Invalid config, should return error",
106 configuration: &config.Config{},
108 wantErr: fmt.Errorf("consumer host and port must be provided"),
111 for _, tt := range tests {
112 t.Run(tt.name, func(t *testing.T) {
113 err := validateConfiguration(tt.args.configuration)
114 assertions.Equal(tt.wantErr, err)
119 func Test_initializeLookupService(t *testing.T) {
120 assertions := require.New(t)
124 mockReturn [][]string
125 mockReturnError error
134 name: "Successful initialization, should return nil and lookup service should be initiated with data",
138 mockReturn: [][]string{{"1", "2"}},
143 name: "Unsuccessful initialization, should return error and lookup service should not be initiated with data",
146 mockReturnError: errors.New("Error"),
148 wantInitErr: errors.New("Error"),
151 for _, tt := range tests {
152 t.Run(tt.name, func(t *testing.T) {
153 mockCsvFileHelper := &mocks.CsvFileHelper{}
154 mockCsvFileHelper.On("GetCsvFromFile", mock.Anything).Return(tt.args.mockReturn, tt.args.mockReturnError)
156 err := initializeLookupService(mockCsvFileHelper, tt.args.csvFile)
157 oDuId, _ := lookupService.GetODuID(tt.args.oRuId)
158 assertions.Equal(tt.wantODuId, oDuId)
159 assertions.Equal(tt.wantInitErr, err)
160 mockCsvFileHelper.AssertCalled(t, "GetCsvFromFile", tt.args.csvFile)
165 func Test_getRouter_shouldContainAllPathsWithHandlers(t *testing.T) {
166 assertions := require.New(t)
169 messageHandlerRoute := r.Get("messageHandler")
170 assertions.NotNil(messageHandlerRoute)
171 supportedMethods, err := messageHandlerRoute.GetMethods()
172 assertions.Equal([]string{http.MethodPost}, supportedMethods)
174 path, _ := messageHandlerRoute.GetPathTemplate()
175 assertions.Equal("/", path)
177 startHandlerRoute := r.Get("start")
178 assertions.NotNil(messageHandlerRoute)
179 supportedMethods, err = startHandlerRoute.GetMethods()
180 assertions.Equal([]string{http.MethodPost}, supportedMethods)
182 path, _ = startHandlerRoute.GetPathTemplate()
183 assertions.Equal("/admin/start", path)
185 stopHandlerRoute := r.Get("stop")
186 assertions.NotNil(stopHandlerRoute)
187 supportedMethods, err = stopHandlerRoute.GetMethods()
188 assertions.Equal([]string{http.MethodPost}, supportedMethods)
190 path, _ = stopHandlerRoute.GetPathTemplate()
191 assertions.Equal("/admin/stop", path)
193 statusHandlerRoute := r.Get("status")
194 assertions.NotNil(statusHandlerRoute)
195 supportedMethods, err = statusHandlerRoute.GetMethods()
196 assertions.Equal([]string{http.MethodGet}, supportedMethods)
198 path, _ = statusHandlerRoute.GetPathTemplate()
199 assertions.Equal("/status", path)
202 func Test_startHandler(t *testing.T) {
203 assertions := require.New(t)
205 jobRegistrationInfo.JobResultURI = "host:80"
206 var job_definition interface{}
209 mockReturnBody []byte
219 name: "Start with successful registration, should return ok",
221 mockReturnBody: []byte(""),
222 mockReturnStatus: http.StatusOK,
224 wantedStatus: http.StatusOK,
227 name: "Start with error response at registration, should return error",
229 mockReturnBody: []byte("error"),
230 mockReturnStatus: http.StatusBadRequest,
232 wantedStatus: http.StatusBadRequest,
233 wantedBody: "Unable to register consumer job due to:",
236 for _, tt := range tests {
237 t.Run(tt.name, func(t *testing.T) {
238 clientMock := setUpClientMock(tt.args.mockReturnBody, tt.args.mockReturnStatus)
240 handler := http.HandlerFunc(startHandler)
241 responseRecorder := httptest.NewRecorder()
242 r, _ := http.NewRequest(http.MethodPost, "/start", nil)
244 handler.ServeHTTP(responseRecorder, r)
246 assertions.Equal(tt.wantedStatus, responseRecorder.Code, tt.name)
247 assertions.Contains(responseRecorder.Body.String(), tt.wantedBody, tt.name)
249 var wantedJobRegistrationInfo = struct {
250 InfoTypeId string `json:"info_type_id"`
251 JobResultUri string `json:"job_result_uri"`
252 JobOwner string `json:"job_owner"`
253 JobDefinition interface{} `json:"job_definition"`
255 InfoTypeId: "STD_Fault_Messages",
256 JobResultUri: "host:80",
257 JobOwner: "O-RU Closed Loop Usecase",
258 JobDefinition: job_definition,
260 wantedBody, _ := json.Marshal(wantedJobRegistrationInfo)
262 var actualRequest *http.Request
263 clientMock.AssertCalled(t, "Do", mock.MatchedBy(func(req *http.Request) bool {
267 assertions.Equal(http.MethodPut, actualRequest.Method)
268 assertions.Equal("http", actualRequest.URL.Scheme)
269 assertions.Equal("enrichmentservice:8083", actualRequest.URL.Host)
270 assertions.Equal("/data-consumer/v1/info-jobs/14e7bb84-a44d-44c1-90b7-6995a92ad43c", actualRequest.URL.Path)
271 assertions.Equal("application/json; charset=utf-8", actualRequest.Header.Get("Content-Type"))
272 body, _ := ioutil.ReadAll(actualRequest.Body)
273 expectedBody := wantedBody
274 assertions.Equal(expectedBody, body)
275 clientMock.AssertNumberOfCalls(t, "Do", 1)
277 // Check that the running status is "started"
278 statusHandler := http.HandlerFunc(statusHandler)
279 statusResponseRecorder := httptest.NewRecorder()
280 statusRequest, _ := http.NewRequest(http.MethodGet, "/status", nil)
282 statusHandler.ServeHTTP(statusResponseRecorder, statusRequest)
284 assertions.Equal(http.StatusOK, statusResponseRecorder.Code)
285 assertions.Equal(`{"status": "started"}`, statusResponseRecorder.Body.String())
290 func Test_stopHandler(t *testing.T) {
291 assertions := require.New(t)
293 jobRegistrationInfo.JobResultURI = "host:80"
296 mockReturnBody []byte
306 name: "Stop with successful job deletion, should return ok",
308 mockReturnBody: []byte(""),
309 mockReturnStatus: http.StatusOK,
311 wantedStatus: http.StatusOK,
314 name: "Stop with error response at job deletion, should return error",
316 mockReturnBody: []byte("error"),
317 mockReturnStatus: http.StatusBadRequest,
319 wantedStatus: http.StatusBadRequest,
320 wantedBody: "Please remove job 14e7bb84-a44d-44c1-90b7-6995a92ad43c manually",
323 for _, tt := range tests {
324 t.Run(tt.name, func(t *testing.T) {
325 clientMock := setUpClientMock(tt.args.mockReturnBody, tt.args.mockReturnStatus)
327 handler := http.HandlerFunc(stopHandler)
328 responseRecorder := httptest.NewRecorder()
329 r, _ := http.NewRequest(http.MethodPost, "/stop", nil)
331 handler.ServeHTTP(responseRecorder, r)
333 assertions.Equal(tt.wantedStatus, responseRecorder.Code, tt.name)
334 assertions.Contains(responseRecorder.Body.String(), tt.wantedBody, tt.name)
336 var actualRequest *http.Request
337 clientMock.AssertCalled(t, "Do", mock.MatchedBy(func(req *http.Request) bool {
341 assertions.Equal(http.MethodDelete, actualRequest.Method)
342 assertions.Equal("http", actualRequest.URL.Scheme)
343 assertions.Equal("enrichmentservice:8083", actualRequest.URL.Host)
344 assertions.Equal("/data-consumer/v1/info-jobs/14e7bb84-a44d-44c1-90b7-6995a92ad43c", actualRequest.URL.Path)
345 clientMock.AssertNumberOfCalls(t, "Do", 1)
347 // Check that the running status is "stopped"
348 statusHandler := http.HandlerFunc(statusHandler)
349 statusResponseRecorder := httptest.NewRecorder()
350 statusRequest, _ := http.NewRequest(http.MethodGet, "/status", nil)
352 statusHandler.ServeHTTP(statusResponseRecorder, statusRequest)
354 assertions.Equal(http.StatusOK, statusResponseRecorder.Code)
355 assertions.Equal(`{"status": "stopped"}`, statusResponseRecorder.Body.String())
360 func Test_deleteOnShutdown(t *testing.T) {
361 assertions := require.New(t)
367 log.SetOutput(os.Stderr)
371 mockReturnBody []byte
380 name: "Delete with successful job deletion, should return ok",
382 mockReturnBody: []byte(""),
383 mockReturnStatus: http.StatusOK,
387 name: "Stop with error response at job deletion, should return error",
389 mockReturnBody: []byte("error"),
390 mockReturnStatus: http.StatusBadRequest,
392 wantedLog: "Please remove job 14e7bb84-a44d-44c1-90b7-6995a92ad43c manually",
395 for _, tt := range tests {
396 t.Run(tt.name, func(t *testing.T) {
397 setUpClientMock(tt.args.mockReturnBody, tt.args.mockReturnStatus)
399 c := make(chan os.Signal, 1)
400 go deleteOnShutdown(c)
403 waitForLogToBeWritten(&buf)
406 if tt.wantedLog != "" {
407 assertions.Contains(log, "level=error")
408 assertions.Contains(log, "Unable to delete job on shutdown due to:")
409 assertions.Contains(log, tt.wantedLog)
415 func waitForLogToBeWritten(logBuf *bytes.Buffer) {
416 wg := sync.WaitGroup{}
419 if waitTimeout(&wg, 10*time.Millisecond) && logBuf.Len() != 0 {
426 // waitTimeout waits for the waitgroup for the specified max timeout.
427 // Returns true if waiting timed out.
428 func waitTimeout(wg *sync.WaitGroup, timeout time.Duration) bool {
429 c := make(chan struct{})
436 return false // completed normally
437 case <-time.After(timeout):
438 return true // timed out
442 func setUpClientMock(body []byte, status int) *mocks.HTTPClient {
443 clientMock := mocks.HTTPClient{}
444 clientMock.On("Do", mock.Anything).Return(&http.Response{
445 Body: ioutil.NopCloser(bytes.NewReader(body)),