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