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)
194 func Test_startHandler(t *testing.T) {
195 assertions := require.New(t)
197 jobRegistrationInfo.JobResultUri = "host:80"
200 mockReturnBody []byte
210 name: "Start with successful registration, should return ok",
212 mockReturnBody: []byte(""),
213 mockReturnStatus: http.StatusOK,
215 wantedStatus: http.StatusOK,
218 name: "Start with error response at registration, should return error",
220 mockReturnBody: []byte("error"),
221 mockReturnStatus: http.StatusBadRequest,
223 wantedStatus: http.StatusBadRequest,
224 wantedBody: "Unable to register consumer job due to:",
227 for _, tt := range tests {
228 t.Run(tt.name, func(t *testing.T) {
229 clientMock := setUpClientMock(tt.args.mockReturnBody, tt.args.mockReturnStatus)
231 handler := http.HandlerFunc(startHandler)
232 responseRecorder := httptest.NewRecorder()
233 r, _ := http.NewRequest(http.MethodPost, "/start", nil)
235 handler.ServeHTTP(responseRecorder, r)
237 assertions.Equal(tt.wantedStatus, responseRecorder.Code, tt.name)
238 assertions.Contains(responseRecorder.Body.String(), tt.wantedBody, tt.name)
240 var wantedJobRegistrationInfo = struct {
241 InfoTypeId string `json:"info_type_id"`
242 JobResultUri string `json:"job_result_uri"`
243 JobOwner string `json:"job_owner"`
244 JobDefinition interface{} `json:"job_definition"`
246 InfoTypeId: "STD_Fault_Messages",
247 JobResultUri: "host:80",
248 JobOwner: "O-RU Closed Loop Usecase",
251 wantedBody, _ := json.Marshal(wantedJobRegistrationInfo)
253 var actualRequest *http.Request
254 clientMock.AssertCalled(t, "Do", mock.MatchedBy(func(req *http.Request) bool {
258 assertions.Equal(http.MethodPut, actualRequest.Method)
259 assertions.Equal("http", actualRequest.URL.Scheme)
260 assertions.Equal("enrichmentservice:8083", actualRequest.URL.Host)
261 assertions.Equal("/data-consumer/v1/info-jobs/14e7bb84-a44d-44c1-90b7-6995a92ad43c", actualRequest.URL.Path)
262 assertions.Equal("application/json; charset=utf-8", actualRequest.Header.Get("Content-Type"))
263 body, _ := ioutil.ReadAll(actualRequest.Body)
264 expectedBody := wantedBody
265 assertions.Equal(expectedBody, body)
266 clientMock.AssertNumberOfCalls(t, "Do", 1)
271 func Test_stopHandler(t *testing.T) {
272 assertions := require.New(t)
274 jobRegistrationInfo.JobResultUri = "host:80"
277 mockReturnBody []byte
287 name: "Stop with successful job deletion, should return ok",
289 mockReturnBody: []byte(""),
290 mockReturnStatus: http.StatusOK,
292 wantedStatus: http.StatusOK,
295 name: "Stop with error response at job deletion, should return error",
297 mockReturnBody: []byte("error"),
298 mockReturnStatus: http.StatusBadRequest,
300 wantedStatus: http.StatusBadRequest,
301 wantedBody: "Please remove job 14e7bb84-a44d-44c1-90b7-6995a92ad43c manually",
304 for _, tt := range tests {
305 t.Run(tt.name, func(t *testing.T) {
306 clientMock := setUpClientMock(tt.args.mockReturnBody, tt.args.mockReturnStatus)
308 handler := http.HandlerFunc(stopHandler)
309 responseRecorder := httptest.NewRecorder()
310 r, _ := http.NewRequest(http.MethodPost, "/stop", nil)
312 handler.ServeHTTP(responseRecorder, r)
314 assertions.Equal(tt.wantedStatus, responseRecorder.Code, tt.name)
315 assertions.Contains(responseRecorder.Body.String(), tt.wantedBody, tt.name)
317 var actualRequest *http.Request
318 clientMock.AssertCalled(t, "Do", mock.MatchedBy(func(req *http.Request) bool {
322 assertions.Equal(http.MethodDelete, actualRequest.Method)
323 assertions.Equal("http", actualRequest.URL.Scheme)
324 assertions.Equal("enrichmentservice:8083", actualRequest.URL.Host)
325 assertions.Equal("/data-consumer/v1/info-jobs/14e7bb84-a44d-44c1-90b7-6995a92ad43c", actualRequest.URL.Path)
326 clientMock.AssertNumberOfCalls(t, "Do", 1)
331 func Test_deleteOnShutdown(t *testing.T) {
332 assertions := require.New(t)
338 log.SetOutput(os.Stderr)
342 mockReturnBody []byte
351 name: "Delete with successful job deletion, should return ok",
353 mockReturnBody: []byte(""),
354 mockReturnStatus: http.StatusOK,
358 name: "Stop with error response at job deletion, should return error",
360 mockReturnBody: []byte("error"),
361 mockReturnStatus: http.StatusBadRequest,
363 wantedLog: "Please remove job 14e7bb84-a44d-44c1-90b7-6995a92ad43c manually",
366 for _, tt := range tests {
367 t.Run(tt.name, func(t *testing.T) {
368 setUpClientMock(tt.args.mockReturnBody, tt.args.mockReturnStatus)
370 c := make(chan os.Signal, 1)
371 go deleteOnShutdown(c)
374 waitForLogToBeWritten(&buf)
377 if tt.wantedLog != "" {
378 assertions.Contains(log, "level=error")
379 assertions.Contains(log, "Unable to delete job on shutdown due to:")
380 assertions.Contains(log, tt.wantedLog)
386 func waitForLogToBeWritten(logBuf *bytes.Buffer) {
387 wg := sync.WaitGroup{}
390 if waitTimeout(&wg, 10*time.Millisecond) && logBuf.Len() != 0 {
397 // waitTimeout waits for the waitgroup for the specified max timeout.
398 // Returns true if waiting timed out.
399 func waitTimeout(wg *sync.WaitGroup, timeout time.Duration) bool {
400 c := make(chan struct{})
407 return false // completed normally
408 case <-time.After(timeout):
409 return true // timed out
413 func setUpClientMock(body []byte, status int) *mocks.HTTPClient {
414 clientMock := mocks.HTTPClient{}
415 clientMock.On("Do", mock.Anything).Return(&http.Response{
416 Body: ioutil.NopCloser(bytes.NewReader(body)),