Update logging interface
[ric-plt/appmgr.git] / cmd / appmgr / api_test.go
1 /*
2 ==================================================================================
3   Copyright (c) 2019 AT&T Intellectual Property.
4   Copyright (c) 2019 Nokia
5
6    Licensed under the Apache License, Version 2.0 (the "License");
7    you may not use this file except in compliance with the License.
8    You may obtain a copy of the License at
9
10        http://www.apache.org/licenses/LICENSE-2.0
11
12    Unless required by applicable law or agreed to in writing, software
13    distributed under the License is distributed on an "AS IS" BASIS,
14    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15    See the License for the specific language governing permissions and
16    limitations under the License.
17 ==================================================================================
18 */
19
20 package main
21
22 import (
23         "bytes"
24         "encoding/json"
25         "errors"
26         "github.com/gorilla/mux"
27         "net/http"
28         "net/http/httptest"
29         "os"
30         "reflect"
31         "strconv"
32         "testing"
33         "time"
34 )
35
36 var x XappManager
37 var xapp Xapp
38 var xapps []Xapp
39 var helmError error
40
41 type MockedHelmer struct {
42 }
43
44 func (h *MockedHelmer) SetCM(cm ConfigMapper) {
45 }
46
47 func (sd *MockedHelmer) Initialize() {
48 }
49
50 func (h *MockedHelmer) Status(name string) (Xapp, error) {
51         return xapp, helmError
52 }
53
54 func (h *MockedHelmer) StatusAll() ([]Xapp, error) {
55         return xapps, helmError
56 }
57
58 func (h *MockedHelmer) List() (names []string, err error) {
59         return names, helmError
60 }
61
62 func (h *MockedHelmer) Install(m XappDeploy) (Xapp, error) {
63         return xapp, helmError
64 }
65
66 func (h *MockedHelmer) Delete(name string) (Xapp, error) {
67         return xapp, helmError
68 }
69
70 // Test cases
71 func TestMain(m *testing.M) {
72         Logger = NewLogger("xapp-manager")
73         loadConfig()
74
75         xapp = Xapp{}
76         xapps = []Xapp{}
77
78         cm := MockedConfigMapper{}
79         h := MockedHelmer{}
80         x = XappManager{}
81         x.Initialize(&h, &cm)
82
83         // Just run on the background (for coverage)
84         go x.Run()
85         x.ready = true
86
87         time.Sleep(time.Duration(2 * time.Second))
88
89         code := m.Run()
90         os.Exit(code)
91 }
92
93 func TestGetHealthCheck(t *testing.T) {
94         req, _ := http.NewRequest("GET", "/ric/v1/health/ready", nil)
95         response := executeRequest(req)
96
97         checkResponseCode(t, http.StatusOK, response.Code)
98 }
99
100 func TestGetAppsReturnsEmpty(t *testing.T) {
101         req, _ := http.NewRequest("GET", "/ric/v1/xapps", nil)
102         response := executeRequest(req)
103
104         checkResponseCode(t, http.StatusOK, response.Code)
105         if body := response.Body.String(); body != "[]" {
106                 t.Errorf("handler returned unexpected body: got %v want []", body)
107         }
108 }
109
110 func TestCreateXApp(t *testing.T) {
111         xapp = generateXapp("dummy-xapp", "started", "1.0", "dummy-xapp-1234-5678", "running", "127.0.0.1", "9999")
112
113         payload := []byte(`{"name":"dummy-xapp"}`)
114         req, _ := http.NewRequest("POST", "/ric/v1/xapps", bytes.NewBuffer(payload))
115         response := executeRequest(req)
116
117         checkResponseData(t, response, http.StatusCreated, false)
118 }
119
120 func TestGetAppsReturnsListOfXapps(t *testing.T) {
121         xapps = append(xapps, xapp)
122         req, _ := http.NewRequest("GET", "/ric/v1/xapps", nil)
123         response := executeRequest(req)
124
125         checkResponseData(t, response, http.StatusOK, true)
126 }
127
128 func TestGetAppByIdReturnsGivenXapp(t *testing.T) {
129         req, _ := http.NewRequest("GET", "/ric/v1/xapps/"+xapp.Name, nil)
130         response := executeRequest(req)
131
132         checkResponseData(t, response, http.StatusOK, false)
133 }
134
135 func TestGetAppInstanceByIdReturnsGivenXapp(t *testing.T) {
136         req, _ := http.NewRequest("GET", "/ric/v1/xapps/"+xapp.Name+"/instances/dummy-xapp-1234-5678", nil)
137         response := executeRequest(req)
138
139         var ins XappInstance
140         checkResponseCode(t, http.StatusOK, response.Code)
141         json.NewDecoder(response.Body).Decode(&ins)
142
143         if !reflect.DeepEqual(ins, xapp.Instances[0]) {
144                 t.Errorf("handler returned unexpected body: got: %v, expected: %v", ins, xapp.Instances[0])
145         }
146 }
147
148 func TestDeleteAppRemovesGivenXapp(t *testing.T) {
149         req, _ := http.NewRequest("DELETE", "/ric/v1/xapps/"+xapp.Name, nil)
150         response := executeRequest(req)
151
152         checkResponseData(t, response, http.StatusNoContent, false)
153
154         // Xapp not found from the Redis DB
155         helmError = errors.New("Not found")
156
157         req, _ = http.NewRequest("GET", "/ric/v1/xapps/"+xapp.Name, nil)
158         response = executeRequest(req)
159         checkResponseCode(t, http.StatusNotFound, response.Code)
160 }
161
162 func TestGetConfigReturnsEmpty(t *testing.T) {
163         req, _ := http.NewRequest("GET", "/ric/v1/config", nil)
164         response := executeRequest(req)
165
166         checkResponseCode(t, http.StatusOK, response.Code)
167 }
168
169 func TestCreateConfigFailsWithMethodNotAllowed(t *testing.T) {
170         req, _ := http.NewRequest("POST", "/ric/v1/config", nil)
171         response := executeRequest(req)
172
173         checkResponseCode(t, http.StatusMethodNotAllowed, response.Code)
174 }
175
176 func TestCreateConfigOk(t *testing.T) {
177         payload := []byte(`{"name":"dummy-xapp"}`)
178         req, _ := http.NewRequest("POST", "/ric/v1/config", bytes.NewBuffer(payload))
179         response := executeRequest(req)
180
181         checkResponseCode(t, http.StatusCreated, response.Code)
182 }
183
184 func TestDeleteConfigOk(t *testing.T) {
185         payload := []byte(`{"name":"dummy-xapp"}`)
186         req, _ := http.NewRequest("DELETE", "/ric/v1/config", bytes.NewBuffer(payload))
187         response := executeRequest(req)
188
189         checkResponseCode(t, http.StatusNoContent, response.Code)
190 }
191
192 // Error handling
193 func TestGetXappReturnsError(t *testing.T) {
194         helmError = errors.New("Not found")
195
196         req, _ := http.NewRequest("GET", "/ric/v1/xapps/invalidXappName", nil)
197         response := executeRequest(req)
198         checkResponseCode(t, http.StatusNotFound, response.Code)
199 }
200
201 func TestGetXappInstanceReturnsError(t *testing.T) {
202         helmError = errors.New("Some error")
203
204         req, _ := http.NewRequest("GET", "/ric/v1/xapps/"+xapp.Name+"/instances/invalidXappName", nil)
205         response := executeRequest(req)
206         checkResponseCode(t, http.StatusNotFound, response.Code)
207 }
208
209 func TestGetXappListReturnsError(t *testing.T) {
210         helmError = errors.New("Internal error")
211
212         req, _ := http.NewRequest("GET", "/ric/v1/xapps", nil)
213         response := executeRequest(req)
214         checkResponseCode(t, http.StatusInternalServerError, response.Code)
215 }
216
217 func TestCreateXAppWithoutXappData(t *testing.T) {
218         req, _ := http.NewRequest("POST", "/ric/v1/xapps", nil)
219         response := executeRequest(req)
220         checkResponseData(t, response, http.StatusMethodNotAllowed, false)
221 }
222
223 func TestCreateXAppWithInvalidXappData(t *testing.T) {
224         body := []byte("Invalid JSON data ...")
225
226         req, _ := http.NewRequest("POST", "/ric/v1/xapps", bytes.NewBuffer(body))
227         response := executeRequest(req)
228         checkResponseData(t, response, http.StatusMethodNotAllowed, false)
229 }
230
231 func TestCreateXAppReturnsError(t *testing.T) {
232         helmError = errors.New("Not found")
233
234         payload := []byte(`{"name":"dummy-xapp"}`)
235         req, _ := http.NewRequest("POST", "/ric/v1/xapps", bytes.NewBuffer(payload))
236         response := executeRequest(req)
237
238         checkResponseData(t, response, http.StatusInternalServerError, false)
239 }
240
241 func TestDeleteXappListReturnsError(t *testing.T) {
242         helmError = errors.New("Internal error")
243
244         req, _ := http.NewRequest("DELETE", "/ric/v1/xapps/invalidXappName", nil)
245         response := executeRequest(req)
246         checkResponseCode(t, http.StatusInternalServerError, response.Code)
247 }
248
249 // Helper functions
250 type fn func(w http.ResponseWriter, r *http.Request)
251
252 func executeRequest(req *http.Request) *httptest.ResponseRecorder {
253         rr := httptest.NewRecorder()
254
255         vars := map[string]string{
256                 "id": "1",
257         }
258         req = mux.SetURLVars(req, vars)
259
260         x.router.ServeHTTP(rr, req)
261
262         return rr
263 }
264
265 func checkResponseCode(t *testing.T, expected, actual int) {
266         if expected != actual {
267                 t.Errorf("Expected response code %d. Got %d\n", expected, actual)
268         }
269 }
270
271 func checkResponseData(t *testing.T, response *httptest.ResponseRecorder, expectedHttpStatus int, isList bool) {
272         expectedData := xapp
273
274         checkResponseCode(t, expectedHttpStatus, response.Code)
275         if isList == true {
276                 jsonResp := []Xapp{}
277                 json.NewDecoder(response.Body).Decode(&jsonResp)
278
279                 if !reflect.DeepEqual(jsonResp[0], expectedData) {
280                         t.Errorf("handler returned unexpected body: %v", jsonResp)
281                 }
282         } else {
283                 json.NewDecoder(response.Body).Decode(&xapp)
284
285                 if !reflect.DeepEqual(xapp, expectedData) {
286                         t.Errorf("handler returned unexpected body: got: %v, expected: %v", xapp, expectedData)
287                 }
288         }
289 }
290
291 func generateXapp(name, status, ver, iname, istatus, ip, port string) (x Xapp) {
292         x.Name = name
293         x.Status = status
294         x.Version = ver
295         p, _ := strconv.Atoi(port)
296         var msgs MessageTypes
297
298         instance := XappInstance{
299                 Name:       iname,
300                 Status:     istatus,
301                 Ip:         ip,
302                 Port:       p,
303                 TxMessages: msgs.TxMessages,
304                 RxMessages: msgs.RxMessages,
305         }
306         x.Instances = append(x.Instances, instance)
307
308         return
309 }