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"
208 mockReturnBody []byte
218 name: "Start with successful registration, should return ok",
220 mockReturnBody: []byte(""),
221 mockReturnStatus: http.StatusOK,
223 wantedStatus: http.StatusOK,
226 name: "Start with error response at registration, should return error",
228 mockReturnBody: []byte("error"),
229 mockReturnStatus: http.StatusBadRequest,
231 wantedStatus: http.StatusBadRequest,
232 wantedBody: "Unable to register consumer job due to:",
235 for _, tt := range tests {
236 t.Run(tt.name, func(t *testing.T) {
237 clientMock := setUpClientMock(tt.args.mockReturnBody, tt.args.mockReturnStatus)
239 handler := http.HandlerFunc(startHandler)
240 responseRecorder := httptest.NewRecorder()
241 r, _ := http.NewRequest(http.MethodPost, "/start", nil)
243 handler.ServeHTTP(responseRecorder, r)
245 assertions.Equal(tt.wantedStatus, responseRecorder.Code, tt.name)
246 assertions.Contains(responseRecorder.Body.String(), tt.wantedBody, tt.name)
248 var wantedJobRegistrationInfo = struct {
249 InfoTypeId string `json:"info_type_id"`
250 JobResultUri string `json:"job_result_uri"`
251 JobOwner string `json:"job_owner"`
252 JobDefinition interface{} `json:"job_definition"`
254 InfoTypeId: "STD_Fault_Messages",
255 JobResultUri: "host:80",
256 JobOwner: "O-RU Closed Loop Usecase",
259 wantedBody, _ := json.Marshal(wantedJobRegistrationInfo)
261 var actualRequest *http.Request
262 clientMock.AssertCalled(t, "Do", mock.MatchedBy(func(req *http.Request) bool {
266 assertions.Equal(http.MethodPut, actualRequest.Method)
267 assertions.Equal("http", actualRequest.URL.Scheme)
268 assertions.Equal("enrichmentservice:8083", actualRequest.URL.Host)
269 assertions.Equal("/data-consumer/v1/info-jobs/14e7bb84-a44d-44c1-90b7-6995a92ad43c", actualRequest.URL.Path)
270 assertions.Equal("application/json; charset=utf-8", actualRequest.Header.Get("Content-Type"))
271 body, _ := ioutil.ReadAll(actualRequest.Body)
272 expectedBody := wantedBody
273 assertions.Equal(expectedBody, body)
274 clientMock.AssertNumberOfCalls(t, "Do", 1)
276 // Check that the running status is "started"
277 statusHandler := http.HandlerFunc(statusHandler)
278 statusResponseRecorder := httptest.NewRecorder()
279 statusRequest, _ := http.NewRequest(http.MethodGet, "/status", nil)
281 statusHandler.ServeHTTP(statusResponseRecorder, statusRequest)
283 assertions.Equal(http.StatusOK, statusResponseRecorder.Code)
284 assertions.Equal(`{"status": "started"}`, statusResponseRecorder.Body.String())
289 func Test_stopHandler(t *testing.T) {
290 assertions := require.New(t)
292 jobRegistrationInfo.JobResultURI = "host:80"
295 mockReturnBody []byte
305 name: "Stop with successful job deletion, should return ok",
307 mockReturnBody: []byte(""),
308 mockReturnStatus: http.StatusOK,
310 wantedStatus: http.StatusOK,
313 name: "Stop with error response at job deletion, should return error",
315 mockReturnBody: []byte("error"),
316 mockReturnStatus: http.StatusBadRequest,
318 wantedStatus: http.StatusBadRequest,
319 wantedBody: "Please remove job 14e7bb84-a44d-44c1-90b7-6995a92ad43c manually",
322 for _, tt := range tests {
323 t.Run(tt.name, func(t *testing.T) {
324 clientMock := setUpClientMock(tt.args.mockReturnBody, tt.args.mockReturnStatus)
326 handler := http.HandlerFunc(stopHandler)
327 responseRecorder := httptest.NewRecorder()
328 r, _ := http.NewRequest(http.MethodPost, "/stop", nil)
330 handler.ServeHTTP(responseRecorder, r)
332 assertions.Equal(tt.wantedStatus, responseRecorder.Code, tt.name)
333 assertions.Contains(responseRecorder.Body.String(), tt.wantedBody, tt.name)
335 var actualRequest *http.Request
336 clientMock.AssertCalled(t, "Do", mock.MatchedBy(func(req *http.Request) bool {
340 assertions.Equal(http.MethodDelete, actualRequest.Method)
341 assertions.Equal("http", actualRequest.URL.Scheme)
342 assertions.Equal("enrichmentservice:8083", actualRequest.URL.Host)
343 assertions.Equal("/data-consumer/v1/info-jobs/14e7bb84-a44d-44c1-90b7-6995a92ad43c", actualRequest.URL.Path)
344 clientMock.AssertNumberOfCalls(t, "Do", 1)
346 // Check that the running status is "stopped"
347 statusHandler := http.HandlerFunc(statusHandler)
348 statusResponseRecorder := httptest.NewRecorder()
349 statusRequest, _ := http.NewRequest(http.MethodGet, "/status", nil)
351 statusHandler.ServeHTTP(statusResponseRecorder, statusRequest)
353 assertions.Equal(http.StatusOK, statusResponseRecorder.Code)
354 assertions.Equal(`{"status": "stopped"}`, statusResponseRecorder.Body.String())
359 func Test_deleteOnShutdown(t *testing.T) {
360 assertions := require.New(t)
366 log.SetOutput(os.Stderr)
370 mockReturnBody []byte
379 name: "Delete with successful job deletion, should return ok",
381 mockReturnBody: []byte(""),
382 mockReturnStatus: http.StatusOK,
386 name: "Stop with error response at job deletion, should return error",
388 mockReturnBody: []byte("error"),
389 mockReturnStatus: http.StatusBadRequest,
391 wantedLog: "Please remove job 14e7bb84-a44d-44c1-90b7-6995a92ad43c manually",
394 for _, tt := range tests {
395 t.Run(tt.name, func(t *testing.T) {
396 setUpClientMock(tt.args.mockReturnBody, tt.args.mockReturnStatus)
398 c := make(chan os.Signal, 1)
399 go deleteOnShutdown(c)
402 waitForLogToBeWritten(&buf)
405 if tt.wantedLog != "" {
406 assertions.Contains(log, "level=error")
407 assertions.Contains(log, "Unable to delete job on shutdown due to:")
408 assertions.Contains(log, tt.wantedLog)
414 func waitForLogToBeWritten(logBuf *bytes.Buffer) {
415 wg := sync.WaitGroup{}
418 if waitTimeout(&wg, 10*time.Millisecond) && logBuf.Len() != 0 {
425 // waitTimeout waits for the waitgroup for the specified max timeout.
426 // Returns true if waiting timed out.
427 func waitTimeout(wg *sync.WaitGroup, timeout time.Duration) bool {
428 c := make(chan struct{})
435 return false // completed normally
436 case <-time.After(timeout):
437 return true // timed out
441 func setUpClientMock(body []byte, status int) *mocks.HTTPClient {
442 clientMock := mocks.HTTPClient{}
443 clientMock.On("Do", mock.Anything).Return(&http.Response{
444 Body: ioutil.NopCloser(bytes.NewReader(body)),