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 LogLevel: log.InfoLevel,
58 ConsumerHost: "consumerHost",
60 InfoCoordinatorAddress: "http://enrichmentservice:8083",
61 SDNRHost: "http://localhost",
64 SDNPassword: "Kp8bJ4SXszM0WXlhak3eHlcse2gAw84vaoGGmJvUy2U",
65 ORUToODUMapFile: "o-ru-to-o-du-map.csv",
67 assertions.Equal(wantedConfiguration, configuration)
69 assertions.Equal(fmt.Sprint(wantedConfiguration.ConsumerPort), consumerPort)
70 assertions.Equal(wantedConfiguration.ConsumerHost+":"+fmt.Sprint(wantedConfiguration.ConsumerPort), jobRegistrationInfo.JobResultUri)
72 wantedLinkFailureConfig := linkfailure.Configuration{
73 SDNRAddress: wantedConfiguration.SDNRHost + ":" + fmt.Sprint(wantedConfiguration.SDNRPort),
74 SDNRUser: wantedConfiguration.SDNRUser,
75 SDNRPassword: wantedConfiguration.SDNPassword,
77 assertions.Equal(wantedLinkFailureConfig, linkfailureConfig)
80 func Test_validateConfiguration(t *testing.T) {
81 assertions := require.New(t)
84 configuration *config.Config
92 name: "Valid config, should return nil",
94 configuration: &config.Config{
101 name: "Invalid config, should return error",
103 configuration: &config.Config{},
105 wantErr: fmt.Errorf("consumer host and port must be provided"),
108 for _, tt := range tests {
109 t.Run(tt.name, func(t *testing.T) {
110 err := validateConfiguration(tt.args.configuration)
111 assertions.Equal(tt.wantErr, err)
116 func Test_initializeLookupService(t *testing.T) {
117 assertions := require.New(t)
121 mockReturn [][]string
122 mockReturnError error
131 name: "Successful initialization, should return nil and lookup service should be initiated with data",
135 mockReturn: [][]string{{"1", "2"}},
140 name: "Unsuccessful initialization, should return error and lookup service should not be initiated with data",
143 mockReturnError: errors.New("Error"),
145 wantInitErr: errors.New("Error"),
148 for _, tt := range tests {
149 t.Run(tt.name, func(t *testing.T) {
150 mockCsvFileHelper := &mocks.CsvFileHelper{}
151 mockCsvFileHelper.On("GetCsvFromFile", mock.Anything).Return(tt.args.mockReturn, tt.args.mockReturnError)
153 err := initializeLookupService(mockCsvFileHelper, tt.args.csvFile)
154 oDuId, _ := lookupService.GetODuID(tt.args.oRuId)
155 assertions.Equal(tt.wantODuId, oDuId)
156 assertions.Equal(tt.wantInitErr, err)
157 mockCsvFileHelper.AssertCalled(t, "GetCsvFromFile", tt.args.csvFile)
162 func Test_getRouter_shouldContainAllPathsWithHandlers(t *testing.T) {
163 assertions := require.New(t)
166 messageHandlerRoute := r.Get("messageHandler")
167 assertions.NotNil(messageHandlerRoute)
168 supportedMethods, err := messageHandlerRoute.GetMethods()
169 assertions.Equal([]string{http.MethodPost}, supportedMethods)
171 path, _ := messageHandlerRoute.GetPathTemplate()
172 assertions.Equal("/", path)
174 startHandlerRoute := r.Get("start")
175 assertions.NotNil(messageHandlerRoute)
176 supportedMethods, err = startHandlerRoute.GetMethods()
177 assertions.Equal([]string{http.MethodPost}, supportedMethods)
179 path, _ = startHandlerRoute.GetPathTemplate()
180 assertions.Equal("/admin/start", path)
182 stopHandlerRoute := r.Get("stop")
183 assertions.NotNil(stopHandlerRoute)
184 supportedMethods, err = stopHandlerRoute.GetMethods()
185 assertions.Equal([]string{http.MethodPost}, supportedMethods)
187 path, _ = stopHandlerRoute.GetPathTemplate()
188 assertions.Equal("/admin/stop", path)
191 func Test_startServer_shouldDeleteJobWhenServerStopsWithErrorAndLog(t *testing.T) {
192 assertions := require.New(t)
197 os.Setenv("CONSUMER_PORT", "wrong")
199 log.SetOutput(os.Stderr)
202 mockServer := &mocks.Server{}
203 mockServer.On("ListenAndServe").Return(errors.New("Server failure"))
205 startServer(mockServer)
208 assertions.Contains(log, "level=error")
209 assertions.Contains(log, "Server stopped unintentionally due to: Server failure. Deleteing job.")
210 assertions.Contains(log, "Please remove job 14e7bb84-a44d-44c1-90b7-6995a92ad43c manually")
213 func Test_startHandler(t *testing.T) {
214 assertions := require.New(t)
216 jobRegistrationInfo.JobResultUri = "host:80"
219 mockReturnBody []byte
229 name: "Start with successful registration, should return ok",
231 mockReturnBody: []byte(""),
232 mockReturnStatus: http.StatusOK,
234 wantedStatus: http.StatusOK,
237 name: "Start with error response at registration, should return error",
239 mockReturnBody: []byte("error"),
240 mockReturnStatus: http.StatusBadRequest,
242 wantedStatus: http.StatusBadRequest,
243 wantedBody: "Unable to register consumer job due to:",
246 for _, tt := range tests {
247 t.Run(tt.name, func(t *testing.T) {
248 clientMock := setUpClientMock(tt.args.mockReturnBody, tt.args.mockReturnStatus)
250 handler := http.HandlerFunc(startHandler)
251 responseRecorder := httptest.NewRecorder()
252 r, _ := http.NewRequest(http.MethodPost, "/start", nil)
254 handler.ServeHTTP(responseRecorder, r)
256 assertions.Equal(tt.wantedStatus, responseRecorder.Code, tt.name)
257 assertions.Contains(responseRecorder.Body.String(), tt.wantedBody, tt.name)
259 var wantedJobRegistrationInfo = struct {
260 InfoTypeId string `json:"info_type_id"`
261 JobResultUri string `json:"job_result_uri"`
262 JobOwner string `json:"job_owner"`
263 JobDefinition interface{} `json:"job_definition"`
265 InfoTypeId: "STD_Fault_Messages",
266 JobResultUri: "host:80",
267 JobOwner: "O-RU Closed Loop Usecase",
270 wantedBody, _ := json.Marshal(wantedJobRegistrationInfo)
272 var actualRequest *http.Request
273 clientMock.AssertCalled(t, "Do", mock.MatchedBy(func(req *http.Request) bool {
277 assertions.Equal(http.MethodPut, actualRequest.Method)
278 assertions.Equal("http", actualRequest.URL.Scheme)
279 assertions.Equal("enrichmentservice:8083", actualRequest.URL.Host)
280 assertions.Equal("/data-consumer/v1/info-jobs/14e7bb84-a44d-44c1-90b7-6995a92ad43c", actualRequest.URL.Path)
281 assertions.Equal("application/json; charset=utf-8", actualRequest.Header.Get("Content-Type"))
282 body, _ := ioutil.ReadAll(actualRequest.Body)
283 expectedBody := wantedBody
284 assertions.Equal(expectedBody, body)
285 clientMock.AssertNumberOfCalls(t, "Do", 1)
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)
350 func Test_deleteOnShutdown(t *testing.T) {
351 assertions := require.New(t)
357 log.SetOutput(os.Stderr)
361 mockReturnBody []byte
370 name: "Delete with successful job deletion, should return ok",
372 mockReturnBody: []byte(""),
373 mockReturnStatus: http.StatusOK,
377 name: "Stop with error response at job deletion, should return error",
379 mockReturnBody: []byte("error"),
380 mockReturnStatus: http.StatusBadRequest,
382 wantedLog: "Please remove job 14e7bb84-a44d-44c1-90b7-6995a92ad43c manually",
385 for _, tt := range tests {
386 t.Run(tt.name, func(t *testing.T) {
387 setUpClientMock(tt.args.mockReturnBody, tt.args.mockReturnStatus)
389 c := make(chan os.Signal, 1)
390 go deleteOnShutdown(c)
393 waitForLogToBeWritten(&buf)
396 if tt.wantedLog != "" {
397 assertions.Contains(log, "level=error")
398 assertions.Contains(log, "Unable to delete job on shutdown due to:")
399 assertions.Contains(log, tt.wantedLog)
405 func waitForLogToBeWritten(logBuf *bytes.Buffer) {
406 wg := sync.WaitGroup{}
409 if waitTimeout(&wg, 10*time.Millisecond) && logBuf.Len() != 0 {
416 // waitTimeout waits for the waitgroup for the specified max timeout.
417 // Returns true if waiting timed out.
418 func waitTimeout(wg *sync.WaitGroup, timeout time.Duration) bool {
419 c := make(chan struct{})
426 return false // completed normally
427 case <-time.After(timeout):
428 return true // timed out
432 func setUpClientMock(body []byte, status int) *mocks.HTTPClient {
433 clientMock := mocks.HTTPClient{}
434 clientMock.On("Do", mock.Anything).Return(&http.Response{
435 Body: ioutil.NopCloser(bytes.NewReader(body)),