Skip to content
Snippets Groups Projects
Code owners
Assign users and groups as approvers for specific file changes. Learn more.
pnd_test.go 10.61 KiB
package server

import (
	"context"
	"os"
	"reflect"
	"testing"
	"time"

	pb "code.fbi.h-da.de/danet/gosdn/api/go/gosdn/core"
	ppb "code.fbi.h-da.de/danet/gosdn/api/go/gosdn/pnd"
	spb "code.fbi.h-da.de/danet/gosdn/api/go/gosdn/southbound"
	"code.fbi.h-da.de/danet/gosdn/controller/interfaces/device"
	"code.fbi.h-da.de/danet/gosdn/controller/interfaces/southbound"
	"code.fbi.h-da.de/danet/gosdn/controller/mocks"
	"code.fbi.h-da.de/danet/gosdn/controller/nucleus"
	"code.fbi.h-da.de/danet/gosdn/controller/rbac"
	"code.fbi.h-da.de/danet/gosdn/models/generated/openconfig"
	"github.com/golang/protobuf/proto"
	"github.com/google/go-cmp/cmp"
	"github.com/google/go-cmp/cmp/cmpopts"
	"github.com/google/uuid"
	"github.com/openconfig/gnmi/proto/gnmi"
	log "github.com/sirupsen/logrus"
	"github.com/stretchr/testify/mock"
)

const pndID = "2043519e-46d1-4963-9a8e-d99007e104b8"
const pendingChangeID = "0992d600-f7d4-4906-9559-409b04d59a5f"
const committedChangeID = "804787d6-e5a8-4dba-a1e6-e73f96b0119e"
const sbiID = "f6fd4b35-f039-4111-9156-5e4501bb8a5a"
const ondID = "7e0ed8cc-ebf5-46fa-9794-741494914883"

var hostname = "manfred"
var domainname = "uwe"
var pndUUID uuid.UUID
var sbiUUID uuid.UUID
var pendingChangeUUID uuid.UUID
var committedChangeUUID uuid.UUID
var deviceUUID uuid.UUID
var mockPnd *mocks.NetworkDomain
var mockDevice device.Device
var sbiStore southbound.Store

func TestMain(m *testing.M) {
	log.SetReportCaller(true)
	var err error
	pndUUID, err = uuid.Parse(pndID)
	if err != nil {
		log.Fatal(err)
	}

	sbiUUID, err = uuid.Parse(sbiID)
	if err != nil {
		log.Fatal(err)
	}

	pendingChangeUUID, err = uuid.Parse(pendingChangeID)
	if err != nil {
		log.Fatal(err)
	}

	committedChangeUUID, err = uuid.Parse(committedChangeID)
	if err != nil {
		log.Fatal(err)
	}

	deviceUUID, err = uuid.Parse(ondID)
	if err != nil {
		log.Fatal(err)
	}

	mockDevice = &nucleus.CommonDevice{
		Model: &openconfig.Device{
			System: &openconfig.OpenconfigSystem_System{
				Config: &openconfig.OpenconfigSystem_System_Config{
					Hostname:   &hostname,
					DomainName: &domainname,
				},
			},
		},
		UUID: deviceUUID,
	}

	sbi, err := nucleus.NewSBI(spb.Type_TYPE_OPENCONFIG, sbiUUID)
	if err != nil {
		log.Fatal(err)
	}
	mockDevice.(*nucleus.CommonDevice).SetSBI(sbi)
	mockDevice.(*nucleus.CommonDevice).SetTransport(&mocks.Transport{})
	mockDevice.(*nucleus.CommonDevice).SetName(hostname)
	sbiStore = nucleus.NewSbiStore(pndUUID)
	if err := sbiStore.Add(mockDevice.SBI()); err != nil {
		log.Fatal(err)
	}

	mockChange := &mocks.Change{}
	mockChange.On("Age").Return(time.Hour)
	mockChange.On("State").Return(ppb.ChangeState_CHANGE_STATE_INCONSISTENT)

	mockPnd = &mocks.NetworkDomain{}
	mockPnd.On("ID").Return(pndUUID)
	mockPnd.On("GetName").Return("test")
	mockPnd.On("GetDescription").Return("test")
	mockPnd.On("GetSBIs").Return(sbiStore)
	mockPnd.On("GetSBI", mock.Anything).Return(mockDevice.SBI(), nil)
	mockPnd.On("Devices").Return([]uuid.UUID{deviceUUID})
	mockPnd.On("PendingChanges").Return([]uuid.UUID{pendingChangeUUID})
	mockPnd.On("CommittedChanges").Return([]uuid.UUID{committedChangeUUID})
	mockPnd.On("GetChange", mock.Anything).Return(mockChange, nil)
	mockPnd.On("AddDevice", mock.Anything, mock.Anything, mock.Anything).Return(nil)
	mockPnd.On("GetDevice", mock.Anything).Return(mockDevice, nil)
	mockPnd.On("Commit", mock.Anything).Return(nil)
	mockPnd.On("Confirm", mock.Anything).Return(nil)
	mockPnd.On("ChangeOND", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(uuid.Nil, nil)
	mockPnd.On("Request", mock.Anything, mock.Anything).Return(nil, nil)

	pndc = nucleus.NewMemoryPndStore()
	if err := pndc.Add(mockPnd); err != nil {
		log.Fatal(err)
	}

	// everyting auth related
	userc = rbac.NewUserService(rbac.NewMemoryUserStore())
	rolec = rbac.NewRoleService(rbac.NewMemoryRoleStore())
	err = clearAndCreateAuthTestSetup()
	if err != nil {
		log.Fatal(err)
	}
	jwt = rbac.NewJWTManager("", 1*time.Minute)

	os.Exit(m.Run())
}

// TODO: We should re-add all tests for changes.
// As of now this is not possible as we can't use the mock pnd, as it can't be serialized because of
// cyclic use of mock in it.
func Test_pnd_Get(t *testing.T) {
	type args struct {
		ctx     context.Context
		request *pb.GetPndRequest
	}
	tests := []struct {
		name    string
		args    args
		want    *pb.GetPndResponse
		wantErr bool
	}{
		{
			name: "get pnd",
			args: args{
				ctx: context.Background(),
				request: &pb.GetPndRequest{
					Pid: pndID,
				},
			},
			want: &pb.GetPndResponse{
				Pnd: &ppb.PrincipalNetworkDomain{
					Id:          pndID,
					Name:        "test",
					Description: "test"},
			},
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			p := core{
				UnimplementedCoreServiceServer: pb.UnimplementedCoreServiceServer{},
			}
			resp, err := p.GetPnd(tt.args.ctx, tt.args.request)
			if (err != nil) != tt.wantErr {
				t.Errorf("Get() error = %v, wantErr %v", err, tt.wantErr)
				return
			}

			got := resp.GetPnd()

			if !reflect.DeepEqual(got, tt.want.Pnd) {
				t.Errorf("Get() got = %v, want %v", got, tt.want.Pnd)
			}
		})
	}
}

func Test_pnd_GetPath(t *testing.T) {
	opts := cmp.Options{
		cmpopts.SortSlices(
			func(x, y *gnmi.Update) bool {
				return x.GetVal().String() < y.GetVal().String()
			},
		),
		cmp.Comparer(proto.Equal),
	}

	type args struct {
		ctx     context.Context
		request *ppb.GetPathRequest
	}
	tests := []struct {
		name    string
		args    args
		want    []*gnmi.Notification
		wantErr bool
	}{
		{
			name: "get path: system/config/hostname",
			args: args{
				ctx: context.Background(),
				request: &ppb.GetPathRequest{
					Timestamp: time.Now().UnixNano(),
					Did:       mockDevice.ID().String(),
					Path:      "system/config/hostname",
					Pid:       mockPnd.ID().String(),
				},
			},
			want: []*gnmi.Notification{
				{
					Update: []*gnmi.Update{
						{
							Path: &gnmi.Path{Element: []string{"hostname"}},
							Val: &gnmi.TypedValue{
								Value: &gnmi.TypedValue_StringVal{
									StringVal: "manfred",
								},
							},
						},
					}},
			},
			wantErr: false,
		},
		{
			name: "get path: system",
			args: args{
				ctx: context.Background(),
				request: &ppb.GetPathRequest{
					Timestamp: time.Now().UnixNano(),
					Did:       mockDevice.ID().String(),
					Path:      "system",
					Pid:       mockPnd.ID().String(),
				},
			},
			want: []*gnmi.Notification{
				{
					Update: []*gnmi.Update{
						{
							Path: &gnmi.Path{Element: []string{"config", "domain-name"}},
							Val: &gnmi.TypedValue{
								Value: &gnmi.TypedValue_StringVal{
									StringVal: "uwe",
								},
							},
						},
						{
							Path: &gnmi.Path{Element: []string{"config", "hostname"}},
							Val: &gnmi.TypedValue{
								Value: &gnmi.TypedValue_StringVal{
									StringVal: "manfred",
								},
							},
						},
					}},
			},
			wantErr: false,
		},
		{
			name: "get path: this/path/is/not/valid",
			args: args{
				ctx: context.Background(),
				request: &ppb.GetPathRequest{
					Timestamp: time.Now().UnixNano(),
					Did:       mockDevice.ID().String(),
					Path:      "this/path/is/not/valid",
					Pid:       mockPnd.ID().String(),
				},
			},
			want:    []*gnmi.Notification{},
			wantErr: true,
		},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			s := pndServer{
				UnimplementedPndServiceServer: ppb.UnimplementedPndServiceServer{},
			}
			resp, err := s.GetPath(tt.args.ctx, tt.args.request)
			if (err != nil) != tt.wantErr {
				t.Errorf("GetPath() error = %v, wantErr %v", err, tt.wantErr)
				return
			}

			got := resp.GetDevice()

			for i, n := range got {
				if diff := cmp.Diff(n.GetUpdate(), tt.want[i].GetUpdate(), opts...); diff != "" {
					t.Errorf("GetPath() diff in the received notification %d: \n%s", i+1, diff)
				}
			}
		})
	}
}

func Test_pnd_Set(t *testing.T) {
	// type args struct {
	// 	ctx     context.Context
	// 	request *ppb.SetRequest
	// }
	// tests := []struct {
	// 	name    string
	// 	args    args
	// 	want    ppb.SetResponseStatus
	// 	wantErr bool
	// }{
	// 	{
	// 		name: "set ond",
	// 		args: args{
	// 			ctx: context.Background(),
	// 			request: &ppb.SetRequest{
	// 				Ond: []*ppb.SetOnd{
	// 					{
	// 						Sbi: &spb.SouthboundInterface{
	// 							Id:   sbiID,
	// 							Type: spb.Type_TYPE_OPENCONFIG,
	// 						},
	// 						DeviceName: hostname,
	// 						TransportOption: &transport.TransportOption{
	// 							Address:  "test",
	// 							Username: "test",
	// 							Password: "test",
	// 							TransportOption: &transport.TransportOption_GnmiTransportOption{
	// 								GnmiTransportOption: &transport.GnmiTransportOption{},
	// 							},
	// 						},
	// 					},
	// 				},
	// 				Pid: pndID,
	// 			},
	// 		},
	// 		want: ppb.SetResponse_OK,
	// 	},
	// 	// {
	// 	// 	name: "set change",
	// 	// 	args: args{
	// 	// 		ctx: context.Background(),
	// 	// 		request: &ppb.SetRequest{
	// 	// 			Pid: pndID,
	// 	// 			Change: []*ppb.SetChange{
	// 	// 				{
	// 	// 					Cuid: pendingChangeID,
	// 	// 					Op:   ppb.SetChange_COMMIT,
	// 	// 				},
	// 	// 				{
	// 	// 					Cuid: committedChangeID,
	// 	// 					Op:   ppb.SetChange_CONFIRM,
	// 	// 				},
	// 	// 			},
	// 	// 		},
	// 	// 	},
	// 	// 	want: ppb.SetResponse_OK,
	// 	// },
	// 	// 	{
	// 	// 		name: "change request",
	// 	// 		args: args{
	// 	// 			ctx: context.Background(),
	// 	// 			request: &ppb.SetRequest{
	// 	// 				Pid: pndID,
	// 	// 				ChangeRequest: []*ppb.ChangeRequest{
	// 	// 					{
	// 	// 						Id:    ondID,
	// 	// 						Path:  "/system/config/hostname",
	// 	// 						Value: "herbert",
	// 	// 						ApiOp: ppb.ApiOperation_UPDATE,
	// 	// 					},
	// 	// 					{
	// 	// 						Id:    ondID,
	// 	// 						Path:  "/system/config/hostname",
	// 	// 						Value: "fridolin",
	// 	// 						ApiOp: ppb.ApiOperation_REPLACE,
	// 	// 					},
	// 	// 					{
	// 	// 						Id:    ondID,
	// 	// 						Path:  "/system/config/hostname",
	// 	// 						ApiOp: ppb.ApiOperation_DELETE,
	// 	// 					},
	// 	// 				},
	// 	// 			},
	// 	// 		},
	// 	// 		want: ppb.SetResponse_OK,
	// 	// 	},
	// }
	// for _, tt := range tests {
	// 	t.Run(tt.name, func(t *testing.T) {
	// 		p := pndServer{
	// 			UnimplementedPndServiceServer: ppb.UnimplementedPndServiceServer{},
	// 		}
	// 		resp, err := p.Set(tt.args.ctx, tt.args.request)
	// 		if (err != nil) != tt.wantErr {
	// 			t.Errorf("Set() error = %v, wantErr %v", err, tt.wantErr)
	// 			return
	// 		}
	// 		got := resp.Status
	// 		if !reflect.DeepEqual(got, tt.want) {
	// 			t.Errorf("Set() got = %v, want %v", got, tt.want)
	// 		}
	// 		for _, r := range resp.Responses {
	// 			got = r.Status
	// 			if !reflect.DeepEqual(got, tt.want) {
	// 				t.Errorf("Set() got = %v, want %v", got, tt.want)
	// 			}
	// 		}
	// 	})
	// }
}