X-Git-Url: https://gerrit.o-ran-sc.org/r/gitweb?a=blobdiff_plain;f=pkg%2Fresthooks%2Fresthooks_test.go;h=e89383fb5fa09172effc1aac2758749db6a96db1;hb=d3aa5b98789c65107e5ac41fc5be11f08acf11d8;hp=d1ac790132607a7e07599d3bbad66fdb27e5a81c;hpb=2a4099785bcc1dda74d5192f93f69b33533585b3;p=ric-plt%2Fappmgr.git diff --git a/pkg/resthooks/resthooks_test.go b/pkg/resthooks/resthooks_test.go index d1ac790..e89383f 100755 --- a/pkg/resthooks/resthooks_test.go +++ b/pkg/resthooks/resthooks_test.go @@ -20,8 +20,18 @@ package resthooks import ( + "encoding/json" + "errors" + "fmt" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/mock" + "net" + "net/http" + "net/http/httptest" "os" + "time" + + "strconv" "testing" "gerrit.o-ran-sc.org/r/ric-plt/appmgr/pkg/appmgr" @@ -30,31 +40,40 @@ import ( var rh *Resthook var resp models.SubscriptionResponse +var mockedSdl *SdlMock // Test cases func TestMain(m *testing.M) { appmgr.Init() appmgr.Logger.SetLevel(0) - rh = NewResthook(false) + mockedSdl = new(SdlMock) + rh = createResthook(false, mockedSdl) code := m.Run() os.Exit(code) } func TestAddSubscriptionSuccess(t *testing.T) { - resp := rh.AddSubscription(CreateSubscription(models.EventTypeCreated, int64(5), int64(10), "http://localhost:8087/xapps_hook")) + var mockSdlRetOk error + subsReq := createSubscription(models.EventTypeCreated, int64(5), int64(10), "http://localhost:8087/xapps_hook") + + mockedSdl.expectDbSet(t, subsReq, mockSdlRetOk) + resp := rh.AddSubscription(subsReq) assert.Equal(t, resp.Version, int64(0)) assert.Equal(t, resp.EventType, models.EventTypeCreated) } func TestAddSubscriptionExists(t *testing.T) { - resp := rh.AddSubscription(CreateSubscription(models.EventTypeCreated, int64(5), int64(10), "http://localhost:8087/xapps_hook")) + resp := rh.AddSubscription(createSubscription(models.EventTypeCreated, int64(5), int64(10), "http://localhost:8087/xapps_hook")) assert.Equal(t, resp.Version, int64(0)) assert.Equal(t, resp.EventType, models.EventTypeCreated) } func TestDeletesubscriptionSuccess(t *testing.T) { - resp := rh.AddSubscription(CreateSubscription(models.EventTypeDeleted, int64(5), int64(10), "http://localhost:8087/xapps_hook2")) + var mockSdlRetOk error + + mockedSdl.On("Set", mock.Anything).Return(mockSdlRetOk) + resp := rh.AddSubscription(createSubscription(models.EventTypeDeleted, int64(5), int64(10), "http://localhost:8087/xapps_hook2")) assert.Equal(t, resp.Version, int64(0)) assert.Equal(t, resp.EventType, models.EventTypeDeleted) @@ -72,17 +91,23 @@ func TestDeletesubscriptionInvalid(t *testing.T) { } func TestModifySubscriptionSuccess(t *testing.T) { - resp := rh.AddSubscription(CreateSubscription(models.EventTypeCreated, int64(5), int64(10), "http://localhost:8087/xapps_hook2")) + resp := rh.AddSubscription(createSubscription(models.EventTypeCreated, int64(5), int64(10), "http://localhost:8087/xapps_hook2")) assert.Equal(t, resp.Version, int64(0)) assert.Equal(t, resp.EventType, models.EventTypeCreated) - resp, ok := rh.ModifySubscription(resp.ID, CreateSubscription(models.EventTypeModified, int64(5), int64(10), "http://localhost:8087/xapps_hook2")) + resp, ok := rh.ModifySubscription(resp.ID, createSubscription(models.EventTypeModified, int64(5), int64(10), "http://localhost:8087/xapps_hook2")) assert.Equal(t, ok, true) assert.Equal(t, resp.Version, int64(0)) assert.Equal(t, resp.EventType, models.EventTypeModified) } -func TestModifysubscriptionInvalid(t *testing.T) { +func TestModifySubscriptionForNonExistingSubscription(t *testing.T) { + resp, ok := rh.ModifySubscription("Non-existent-ID", createSubscription(models.EventTypeModified, int64(5), int64(10), "http://localhost:8087/xapps_hook2")) + assert.Equal(t, ok, false) + assert.Equal(t, *resp, models.SubscriptionResponse{}) +} + +func TestDeleteSubscriptionForNonExistingSubscription(t *testing.T) { resp, ok := rh.DeleteSubscription("Non-existent-ID") assert.Equal(t, ok, false) assert.Equal(t, resp.Version, int64(0)) @@ -90,21 +115,22 @@ func TestModifysubscriptionInvalid(t *testing.T) { } func TestGetAllSubscriptionSuccess(t *testing.T) { - rh.FlushSubscriptions() + flushExistingSubscriptions() subscriptions := rh.GetAllSubscriptions() assert.Equal(t, len(subscriptions), 0) - rh.AddSubscription(CreateSubscription(models.EventTypeCreated, int64(5), int64(10), "http://localhost:8087/xapps_hook")) - rh.AddSubscription(CreateSubscription(models.EventTypeModified, int64(5), int64(10), "http://localhost:8087/xapps_hook2")) + rh.AddSubscription(createSubscription(models.EventTypeCreated, int64(5), int64(10), "http://localhost:8087/xapps_hook")) + rh.AddSubscription(createSubscription(models.EventTypeModified, int64(5), int64(10), "http://localhost:8087/xapps_hook2")) subscriptions = rh.GetAllSubscriptions() assert.Equal(t, len(subscriptions), 2) } func TestGetSubscriptionByIdSuccess(t *testing.T) { - rh.FlushSubscriptions() - sub1 := CreateSubscription(models.EventTypeCreated, int64(5), int64(10), "http://localhost:8087/xapps_hook") - sub2 := CreateSubscription(models.EventTypeModified, int64(5), int64(10), "http://localhost:8087/xapps_hook2") + flushExistingSubscriptions() + + sub1 := createSubscription(models.EventTypeCreated, int64(5), int64(10), "http://localhost:8087/xapps_hook") + sub2 := createSubscription(models.EventTypeModified, int64(5), int64(10), "http://localhost:8087/xapps_hook2") r1 := rh.AddSubscription(sub1) r2 := rh.AddSubscription(sub2) @@ -117,10 +143,212 @@ func TestGetSubscriptionByIdSuccess(t *testing.T) { assert.Equal(t, resp2.Data, sub2.Data) } +func TestGetSubscriptionByIdForNonExistingSubscription(t *testing.T) { + resp, ok := rh.GetSubscriptionById("Non-existent-ID") + assert.Equal(t, ok, false) + assert.Equal(t, resp, models.Subscription{}) +} + +func TestNotifyClientsNoXapp(t *testing.T) { + rh.NotifyClients(models.AllDeployedXapps{}, models.EventTypeUndeployed) +} + +func TestNotifySuccess(t *testing.T) { + flushExistingSubscriptions() + + sub := createSubscription(models.EventTypeCreated, int64(5), int64(10), "http://localhost:8087/xapps_hook") + resp := rh.AddSubscription(sub) + + xapp := getDummyXapp() + ts := createHTTPServer(t, "POST", "/xapps_hook", 8087, http.StatusOK, nil) + defer ts.Close() + + v, ok := rh.subscriptions.Get(resp.ID) + assert.True(t, ok) + err := rh.notify(models.AllDeployedXapps{&xapp}, models.EventTypeUndeployed, v.(SubscriptionInfo), 1) + assert.Nil(t, err) +} + +func TestNotifySuccessIfHttpErrorResponse(t *testing.T) { + flushExistingSubscriptions() + + sub := createSubscription(models.EventTypeCreated, int64(5), int64(10), "http://localhost:8087/xapps_hook") + resp := rh.AddSubscription(sub) + + xapp := getDummyXapp() + ts := createHTTPServer(t, "POST", "/xapps_hook", 8087, http.StatusInternalServerError, nil) + defer ts.Close() + + v, ok := rh.subscriptions.Get(resp.ID) + assert.True(t, ok) + err := rh.notify(models.AllDeployedXapps{&xapp}, models.EventTypeUndeployed, v.(SubscriptionInfo), 1) + assert.Nil(t, err) +} + +func TestNotifyReturnsErrorAfterRetriesIfNoHttpServer(t *testing.T) { + flushExistingSubscriptions() + + sub := createSubscription(models.EventTypeCreated, int64(2), int64(1), "http://localhost:8087/xapps_hook") + resp := rh.AddSubscription(sub) + + xapp := getDummyXapp() + + v, ok := rh.subscriptions.Get(resp.ID) + assert.True(t, ok) + err := rh.notify(models.AllDeployedXapps{&xapp}, models.EventTypeUndeployed, v.(SubscriptionInfo), 1) + assert.NotNil(t, err) + assert.Equal(t, 0, len(rh.subscriptions.Items())) +} + +func TestRestoreSubscriptionsSuccess(t *testing.T) { + var mockSdlRetOk error + mSdl := new(SdlMock) + key := "key-1" + + subsReq := createSubscription(models.EventTypeCreated, int64(5), int64(10), "http://localhost:8087/xapps_hook") + serializedSubsReq, err := json.Marshal(subsReq) + assert.Nil(t, err) + + mockSdlGetRetVal := make(map[string]interface{}) + //Cast data to string to act like a real SDL/Redis client + mockSdlGetRetVal[key] = string(serializedSubsReq) + mSdl.On("GetAll").Return([]string{key}, mockSdlRetOk).Twice() + mSdl.On("Get", []string{key}).Return(mockSdlGetRetVal, mockSdlRetOk).Once() + restHook := createResthook(true, mSdl) + + val, found := restHook.subscriptions.Get(key) + assert.True(t, found) + assert.Equal(t, subsReq, val.(SubscriptionInfo).req) +} + +func TestRestoreSubscriptionsFailsIfSdlGetAllFails(t *testing.T) { + var mockSdlRetStatus error + mSdl := new(SdlMock) + getCalled := 0 + mGetAllCall := mSdl.On("GetAll") + mGetAllCall.RunFn = func(args mock.Arguments) { + if getCalled > 0 { + mockSdlRetStatus = errors.New("some SDL error") + } + getCalled++ + mGetAllCall.ReturnArguments = mock.Arguments{[]string{}, mockSdlRetStatus} + } + + restHook := createResthook(true, mSdl) + assert.Equal(t, 0, len(restHook.subscriptions.Items())) +} + +func TestRestoreSubscriptionsFailsIfSdlGetFails(t *testing.T) { + var mockSdlRetOk error + mSdl := new(SdlMock) + mockSdlRetNok := errors.New("some SDL error") + key := "key-1" + subsReq := createSubscription(models.EventTypeCreated, int64(5), int64(10), "http://localhost:8087/xapps_hook") + serializedSubsReq, err := json.Marshal(subsReq) + assert.Nil(t, err) + + mockSdlGetRetVal := make(map[string]interface{}) + mockSdlGetRetVal[key] = serializedSubsReq + + mSdl.On("GetAll").Return([]string{key}, mockSdlRetOk).Twice() + mSdl.On("Get", []string{key}).Return(mockSdlGetRetVal, mockSdlRetNok).Once() + + restHook := createResthook(true, mSdl) + assert.Equal(t, 0, len(restHook.subscriptions.Items())) +} + func TestTeardown(t *testing.T) { + var mockSdlRetOk error + mockedSdl.On("RemoveAll").Return(mockSdlRetOk).Once() + rh.FlushSubscriptions() } -func CreateSubscription(et models.EventType, maxRetries, retryTimer int64, targetUrl string) models.SubscriptionRequest { +func createSubscription(et models.EventType, maxRetries, retryTimer int64, targetUrl string) models.SubscriptionRequest { return models.SubscriptionRequest{&models.SubscriptionData{et, &maxRetries, &retryTimer, &targetUrl}} } + +func getDummyXapp() models.Xapp { + return generateXapp("dummy-xapp", "deployed", "1.0", "dummy-xapp-8984fc9fd-bkcbp", "running", "service-ricxapp-dummy-xapp-rmr.ricxapp", "4560") +} + +func generateXapp(name, status, ver, iname, istatus, ip, port string) (x models.Xapp) { + x.Name = &name + x.Status = status + x.Version = ver + p, _ := strconv.Atoi(port) + var msgs appmgr.RtmData + + instance := &models.XappInstance{ + Name: &iname, + Status: istatus, + IP: ip, + Port: int64(p), + TxMessages: msgs.TxMessages, + RxMessages: msgs.RxMessages, + } + x.Instances = append(x.Instances, instance) + return +} + +func flushExistingSubscriptions() { + var mockSdlRetOk error + mockedSdl.On("RemoveAll").Return(mockSdlRetOk).Once() + rh.FlushSubscriptions() +} + +func createHTTPServer(t *testing.T, method, url string, port, status int, respData interface{}) *httptest.Server { + l, err := net.Listen("tcp", fmt.Sprintf("localhost:%d", port)) + if err != nil { + t.Error("Failed to create listener: " + err.Error()) + } + ts := httptest.NewUnstartedServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + assert.Equal(t, r.Method, method) + assert.Equal(t, r.URL.String(), url) + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(status) + b, _ := json.Marshal(respData) + w.Write(b) + })) + ts.Listener.Close() + ts.Listener = l + + ts.Start() + time.Sleep(time.Duration(1 * time.Second)) + return ts +} + +func (m *SdlMock) expectDbSet(t *testing.T, subsReq models.SubscriptionRequest, mockRet error) { + serializedSubReq, _ := json.Marshal(subsReq) + m.On("Set", mock.Anything).Run( + func(args mock.Arguments) { + sdlKVs := args.Get(0).([]interface{}) + assert.Equal(t, 2, len(sdlKVs)) + //Validate that subscription request is set to SDL + assert.Equal(t, serializedSubReq, sdlKVs[1]) + }).Return(mockRet).Once() +} + +type SdlMock struct { + mock.Mock +} + +func (m *SdlMock) Set(pairs ...interface{}) error { + a := m.Called(pairs) + return a.Error(0) +} + +func (m *SdlMock) Get(keys []string) (map[string]interface{}, error) { + a := m.Called(keys) + return a.Get(0).(map[string]interface{}), a.Error(1) +} + +func (m *SdlMock) GetAll() ([]string, error) { + a := m.Called() + return a.Get(0).([]string), a.Error(1) +} + +func (m *SdlMock) RemoveAll() error { + a := m.Called() + return a.Error(0) +}