Add a new API to return the list of undeployed xApps
[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) SearchAll() (s []string) {
59         return s
60 }
61
62 func (h *MockedHelmer) List() (names []string, err error) {
63         return names, helmError
64 }
65
66 func (h *MockedHelmer) Install(m XappDeploy) (Xapp, error) {
67         return xapp, helmError
68 }
69
70 func (h *MockedHelmer) Delete(name string) (Xapp, error) {
71         return xapp, helmError
72 }
73
74 // Test cases
75 func TestMain(m *testing.M) {
76         Logger = NewLogger("xapp-manager")
77         loadConfig()
78
79         xapp = Xapp{}
80         xapps = []Xapp{}
81
82         cm := MockedConfigMapper{}
83         h := MockedHelmer{}
84         x = XappManager{}
85         x.Initialize(&h, &cm)
86
87         // Just run on the background (for coverage)
88         go x.Run()
89         x.ready = true
90
91         time.Sleep(time.Duration(2 * time.Second))
92
93         code := m.Run()
94         os.Exit(code)
95 }
96
97 func TestGetHealthCheck(t *testing.T) {
98         req, _ := http.NewRequest("GET", "/ric/v1/health/ready", nil)
99         response := executeRequest(req)
100
101         checkResponseCode(t, http.StatusOK, response.Code)
102 }
103
104 func TestGetAppsReturnsEmpty(t *testing.T) {
105         req, _ := http.NewRequest("GET", "/ric/v1/xapps", nil)
106         response := executeRequest(req)
107
108         checkResponseCode(t, http.StatusOK, response.Code)
109         if body := response.Body.String(); body != "[]" {
110                 t.Errorf("handler returned unexpected body: got %v want []", body)
111         }
112 }
113
114 func TestCreateXApp(t *testing.T) {
115         xapp = generateXapp("dummy-xapp", "started", "1.0", "dummy-xapp-1234-5678", "running", "127.0.0.1", "9999")
116
117         payload := []byte(`{"name":"dummy-xapp"}`)
118         req, _ := http.NewRequest("POST", "/ric/v1/xapps", bytes.NewBuffer(payload))
119         response := executeRequest(req)
120
121         checkResponseData(t, response, http.StatusCreated, false)
122 }
123
124 func TestGetAppsReturnsListOfXapps(t *testing.T) {
125         xapps = append(xapps, xapp)
126         req, _ := http.NewRequest("GET", "/ric/v1/xapps", nil)
127         response := executeRequest(req)
128
129         checkResponseData(t, response, http.StatusOK, true)
130 }
131
132 func TestGetAppByIdReturnsGivenXapp(t *testing.T) {
133         req, _ := http.NewRequest("GET", "/ric/v1/xapps/"+xapp.Name, nil)
134         response := executeRequest(req)
135
136         checkResponseData(t, response, http.StatusOK, false)
137 }
138
139 func TestGetAppInstanceByIdReturnsGivenXapp(t *testing.T) {
140         req, _ := http.NewRequest("GET", "/ric/v1/xapps/"+xapp.Name+"/instances/dummy-xapp-1234-5678", nil)
141         response := executeRequest(req)
142
143         var ins XappInstance
144         checkResponseCode(t, http.StatusOK, response.Code)
145         json.NewDecoder(response.Body).Decode(&ins)
146
147         if !reflect.DeepEqual(ins, xapp.Instances[0]) {
148                 t.Errorf("handler returned unexpected body: got: %v, expected: %v", ins, xapp.Instances[0])
149         }
150 }
151
152 func TestDeleteAppRemovesGivenXapp(t *testing.T) {
153         req, _ := http.NewRequest("DELETE", "/ric/v1/xapps/"+xapp.Name, nil)
154         response := executeRequest(req)
155
156         checkResponseData(t, response, http.StatusNoContent, false)
157
158         // Xapp not found from the Redis DB
159         helmError = errors.New("Not found")
160
161         req, _ = http.NewRequest("GET", "/ric/v1/xapps/"+xapp.Name, nil)
162         response = executeRequest(req)
163         checkResponseCode(t, http.StatusNotFound, response.Code)
164 }
165
166 func TestGetConfigReturnsEmpty(t *testing.T) {
167         req, _ := http.NewRequest("GET", "/ric/v1/config", nil)
168         response := executeRequest(req)
169
170         checkResponseCode(t, http.StatusOK, response.Code)
171 }
172
173 func TestCreateConfigFailsWithMethodNotAllowed(t *testing.T) {
174         req, _ := http.NewRequest("POST", "/ric/v1/config", nil)
175         response := executeRequest(req)
176
177         checkResponseCode(t, http.StatusMethodNotAllowed, response.Code)
178 }
179
180 func TestCreateConfigOk(t *testing.T) {
181         payload := []byte(`{"name":"dummy-xapp"}`)
182         req, _ := http.NewRequest("POST", "/ric/v1/config", bytes.NewBuffer(payload))
183         response := executeRequest(req)
184
185         checkResponseCode(t, http.StatusCreated, response.Code)
186 }
187
188 func TestDeleteConfigOk(t *testing.T) {
189         payload := []byte(`{"name":"dummy-xapp"}`)
190         req, _ := http.NewRequest("DELETE", "/ric/v1/config", bytes.NewBuffer(payload))
191         response := executeRequest(req)
192
193         checkResponseCode(t, http.StatusNoContent, response.Code)
194 }
195
196 // Error handling
197 func TestGetXappReturnsError(t *testing.T) {
198         helmError = errors.New("Not found")
199
200         req, _ := http.NewRequest("GET", "/ric/v1/xapps/invalidXappName", nil)
201         response := executeRequest(req)
202         checkResponseCode(t, http.StatusNotFound, response.Code)
203 }
204
205 func TestGetXappInstanceReturnsError(t *testing.T) {
206         helmError = errors.New("Some error")
207
208         req, _ := http.NewRequest("GET", "/ric/v1/xapps/"+xapp.Name+"/instances/invalidXappName", nil)
209         response := executeRequest(req)
210         checkResponseCode(t, http.StatusNotFound, response.Code)
211 }
212
213 func TestGetXappListReturnsError(t *testing.T) {
214         helmError = errors.New("Internal error")
215
216         req, _ := http.NewRequest("GET", "/ric/v1/xapps", nil)
217         response := executeRequest(req)
218         checkResponseCode(t, http.StatusInternalServerError, response.Code)
219 }
220
221 func TestCreateXAppWithoutXappData(t *testing.T) {
222         req, _ := http.NewRequest("POST", "/ric/v1/xapps", nil)
223         response := executeRequest(req)
224         checkResponseData(t, response, http.StatusMethodNotAllowed, false)
225 }
226
227 func TestCreateXAppWithInvalidXappData(t *testing.T) {
228         body := []byte("Invalid JSON data ...")
229
230         req, _ := http.NewRequest("POST", "/ric/v1/xapps", bytes.NewBuffer(body))
231         response := executeRequest(req)
232         checkResponseData(t, response, http.StatusMethodNotAllowed, false)
233 }
234
235 func TestCreateXAppReturnsError(t *testing.T) {
236         helmError = errors.New("Not found")
237
238         payload := []byte(`{"name":"dummy-xapp"}`)
239         req, _ := http.NewRequest("POST", "/ric/v1/xapps", bytes.NewBuffer(payload))
240         response := executeRequest(req)
241
242         checkResponseData(t, response, http.StatusInternalServerError, false)
243 }
244
245 func TestDeleteXappListReturnsError(t *testing.T) {
246         helmError = errors.New("Internal error")
247
248         req, _ := http.NewRequest("DELETE", "/ric/v1/xapps/invalidXappName", nil)
249         response := executeRequest(req)
250         checkResponseCode(t, http.StatusInternalServerError, response.Code)
251 }
252
253 // Helper functions
254 type fn func(w http.ResponseWriter, r *http.Request)
255
256 func executeRequest(req *http.Request) *httptest.ResponseRecorder {
257         rr := httptest.NewRecorder()
258
259         vars := map[string]string{
260                 "id": "1",
261         }
262         req = mux.SetURLVars(req, vars)
263
264         x.router.ServeHTTP(rr, req)
265
266         return rr
267 }
268
269 func checkResponseCode(t *testing.T, expected, actual int) {
270         if expected != actual {
271                 t.Errorf("Expected response code %d. Got %d\n", expected, actual)
272         }
273 }
274
275 func checkResponseData(t *testing.T, response *httptest.ResponseRecorder, expectedHttpStatus int, isList bool) {
276         expectedData := xapp
277
278         checkResponseCode(t, expectedHttpStatus, response.Code)
279         if isList == true {
280                 jsonResp := []Xapp{}
281                 json.NewDecoder(response.Body).Decode(&jsonResp)
282
283                 if !reflect.DeepEqual(jsonResp[0], expectedData) {
284                         t.Errorf("handler returned unexpected body: %v", jsonResp)
285                 }
286         } else {
287                 json.NewDecoder(response.Body).Decode(&xapp)
288
289                 if !reflect.DeepEqual(xapp, expectedData) {
290                         t.Errorf("handler returned unexpected body: got: %v, expected: %v", xapp, expectedData)
291                 }
292         }
293 }
294
295 func generateXapp(name, status, ver, iname, istatus, ip, port string) (x Xapp) {
296         x.Name = name
297         x.Status = status
298         x.Version = ver
299         p, _ := strconv.Atoi(port)
300         var msgs MessageTypes
301
302         instance := XappInstance{
303                 Name:       iname,
304                 Status:     istatus,
305                 Ip:         ip,
306                 Port:       p,
307                 TxMessages: msgs.TxMessages,
308                 RxMessages: msgs.RxMessages,
309         }
310         x.Instances = append(x.Instances, instance)
311
312         return
313 }