Skip to content
Snippets Groups Projects
principalNetworkDomain_test.go 31.7 KiB
Newer Older
Manuel Kieweg's avatar
Manuel Kieweg committed
package nucleus
Andre Sterba's avatar
Andre Sterba committed
	"errors"
Andre Sterba's avatar
Andre Sterba committed
	"reflect"
Andre Sterba's avatar
Andre Sterba committed
	"testing"

	"code.fbi.h-da.de/danet/gosdn/api/go/gosdn/csbi"
	cpb "code.fbi.h-da.de/danet/gosdn/api/go/gosdn/csbi"
	ppb "code.fbi.h-da.de/danet/gosdn/api/go/gosdn/pnd"
	spb "code.fbi.h-da.de/danet/gosdn/api/go/gosdn/southbound"
	tpb "code.fbi.h-da.de/danet/gosdn/api/go/gosdn/transport"
	"code.fbi.h-da.de/danet/gosdn/controller/conflict"
	eventservice "code.fbi.h-da.de/danet/gosdn/controller/eventService"
	eventInterfaces "code.fbi.h-da.de/danet/gosdn/controller/interfaces/event"
	"code.fbi.h-da.de/danet/gosdn/controller/interfaces/networkdomain"
	"code.fbi.h-da.de/danet/gosdn/controller/interfaces/networkelement"
	"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/util"
	"code.fbi.h-da.de/danet/gosdn/controller/store"
	"code.fbi.h-da.de/danet/gosdn/models/generated/openconfig"
	"github.com/google/uuid"
Manuel Kieweg's avatar
Manuel Kieweg committed
	gpb "github.com/openconfig/gnmi/proto/gnmi"
Manuel Kieweg's avatar
Manuel Kieweg committed
	"github.com/openconfig/ygot/ygot"
	log "github.com/sirupsen/logrus"
	"github.com/stretchr/testify/mock"
)

func TestNewPND(t *testing.T) {
	p := newPnd()
	type args struct {
		name        string
		description string
Manuel Kieweg's avatar
Manuel Kieweg committed
		pid         uuid.UUID
		name    string
		args    args
		want    networkdomain.NetworkDomain
		wantErr bool
	}{
		{
			name: "default",
			args: args{
				name:        "default",
				description: "default test pnd",
				pid:         defaultPndID,
			want:    &p,
			wantErr: false,
		},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			got, err := NewPND(tt.args.name, tt.args.description, tt.args.pid, nil, nil)
			if (err != nil) != tt.wantErr {
				t.Errorf("NewPND() error = %v, wantErr %v", err, tt.wantErr)
				return
			}
			if got.GetName() != tt.want.GetName() {
				t.Errorf("NewPND.GetName() = %v, want %v", got, tt.want)
			}
			if got.ID() != tt.want.ID() {
				t.Errorf("NewPND.ID() = %v, want %v", got, tt.want)
			}
			if got.GetDescription() != tt.want.GetDescription() {
				t.Errorf("NewPND.GetDescription() = %v, want %v", got, tt.want)
			}
		})
	}
}

func Test_destroy(t *testing.T) {
	tests := []struct {
		name    string
		wantErr bool
	}{
		{name: "dummy", wantErr: false},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			if err := destroy(); (err != nil) != tt.wantErr {
				t.Errorf("destroy() error = %v, wantErr %v", err, tt.wantErr)
			}
		})
	}
}

func Test_pndImplementation_AddNetworkElement(t *testing.T) {
	type args struct {
		name string
		opts *tpb.TransportOption
	}
	tests := []struct {
		name    string
		args    args
		wantErr bool
	}{
		{
			name: "default",
Manuel Kieweg's avatar
Manuel Kieweg committed
				name: "fridolin",
				opts: &tpb.TransportOption{
					TransportOption: &tpb.TransportOption_GnmiTransportOption{
						GnmiTransportOption: &tpb.GnmiTransportOption{},
					},
			wantErr: false,
		},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			pnd := newPnd()
Manuel Kieweg's avatar
Manuel Kieweg committed
			if err := pnd.addSbi(&OpenConfig{id: defaultSbiID}); err != nil {
				t.Error(err)
			}
			_, err := pnd.AddNetworkElement(tt.args.name, tt.args.opts, defaultSbiID)
			if (err != nil) != tt.wantErr {
				t.Errorf("AddNetworkElement() error = %v, wantErr %v", err, tt.wantErr)
			if tt.name != "fails wrong type" {
				if err == nil {
					mne, err := pnd.networkElementService.Get(store.Query{Name: tt.args.name})
Manuel Kieweg's avatar
Manuel Kieweg committed
					if err != nil {
						t.Errorf("AddNetworkElement() error = %v", err)
Manuel Kieweg's avatar
Manuel Kieweg committed
						return
					if mne.Name() != tt.args.name {
						t.Errorf("AddNetworkElement() got = %v, want %v", mne.Name(), tt.args.name)
Manuel Kieweg's avatar
Manuel Kieweg committed
					}
					if err := pnd.networkElementService.Delete(mne); err != nil {
// TODO: refactor test to use store interface instead if direct access.
func Test_pndImplementation_AddSbi(t *testing.T) {
	type args struct {
		sbi southbound.SouthboundInterface
	}
	tests := []struct {
		name    string
		args    args
		wantErr bool
	}{
		{
			name: "default",
			args: args{
				sbi: &OpenConfig{
					id: defaultSbiID,
		// {
		// 	name: "already exists",
		// 	args: args{
		// 		sbi: &OpenConfig{
		// 			id: defaultSbiID,
		// 		},
		// 	},
		// 	wantErr: true,
		// },
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			pnd := newPnd()
			err := pnd.AddSbi(tt.args.sbi)
			if (err != nil) != tt.wantErr {
				t.Errorf("AddSbi() error = %v, wantErr %v", err, tt.wantErr)
			if tt.name != "fails wrong type" {
				if err == nil {
					sbi, err := pnd.southboundService.Get(store.Query{ID: defaultSbiID})
						t.Errorf("AddSbi() SBI %v not in network element store %v",
					if err := pnd.southboundService.Delete(sbi); err != nil {
		})
	}
}

func Test_pndImplementation_Destroy(t *testing.T) {
	type fields struct {
		name            string
		description     string
		sbi             southbound.Service
		networkElements networkelement.Service
	}
	tests := []struct {
		name    string
		fields  fields
		wantErr bool
	}{
		{name: "dummy", fields: fields{}, wantErr: false},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			pnd := &pndImplementation{
				Name:                  tt.fields.name,
				Description:           tt.fields.description,
				southboundService:     tt.fields.sbi,
				networkElementService: tt.fields.networkElements,
			}
			if err := pnd.Destroy(); (err != nil) != tt.wantErr {
				t.Errorf("Destroy() error = %v, wantErr %v", err, tt.wantErr)
			}
		})
	}
}

func Test_pndImplementation_GetDescription(t *testing.T) {
	tests := []struct {
		name string
		want string
	}{
		{name: "default", want: "default test pnd"},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			pnd := newPnd()
			if got := pnd.GetDescription(); got != tt.want {
				t.Errorf("GetDescription() = %v, want %v", got, tt.want)
			}
		})
	}
}

func Test_pndImplementation_GetName(t *testing.T) {
	tests := []struct {
		name string
		want string
	}{
		{name: "default", want: "default"},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			pnd := newPnd()
			if got := pnd.GetName(); got != tt.want {
				t.Errorf("GetName() = %v, want %v", got, tt.want)
			}
		})
	}
}

func Test_pndImplementation_MarshalNetworkElement(t *testing.T) {
	type args struct {
		uuid uuid.UUID
	}
	tests := []struct {
		name    string
		args    args
		want    string
		wantErr bool
	}{
Malte Bauch's avatar
Malte Bauch committed
		{
			name:    "default",
			want:    "{\n\t\"Acl\": null,\n\t\"Bfd\": null,\n\t\"Components\": null,\n\t\"Interfaces\": null,\n\t\"Keychains\": null,\n\t\"Lldp\": null,\n\t\"Messages\": null,\n\t\"NetworkInstances\": null,\n\t\"RoutingPolicy\": null,\n\t\"System\": null\n}",
Malte Bauch's avatar
Malte Bauch committed
			wantErr: false,
		},
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			pnd := newPnd()
			sbi, err := NewSBI(spb.Type_TYPE_OPENCONFIG)
			if err != nil {
				t.Error("could not create sbi")
			}

			err = pnd.addSbi(sbi)
			if err != nil {
				t.Error("could not add sbi")
			}
			mne := &CommonNetworkElement{
				UUID:      tt.args.uuid,
Andre Sterba's avatar
Andre Sterba committed
				Model:     &openconfig.Device{},
Manuel Kieweg's avatar
Manuel Kieweg committed
				transport: nil,
			_, err = pnd.addNetworkElement(mne)
			got, err := pnd.MarshalNetworkElement(tt.args.uuid.String())
			if (err != nil) != tt.wantErr {
				t.Errorf("MarshalNetworkElement() error = %v, wantErr %v", err, tt.wantErr)
				return
			}
			if got != tt.want {
				t.Errorf("MarshalNetworkElement() got = %v, want %v", got, tt.want)
			if err := pnd.networkElementService.Delete(mne); err != nil {
func Test_pndImplementation_RemoveNetworkElement(t *testing.T) {
	type args struct {
		uuid uuid.UUID
	}
	tests := []struct {
		name    string
		args    args
		wantErr bool
	}{
		{name: "default", args: args{uuid: mneid}, wantErr: false},
		{name: "fails", args: args{uuid: uuid.New()}, wantErr: true},
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			t.Parallel()
			pnd := newPnd()
			sbi, err := NewSBI(spb.Type_TYPE_OPENCONFIG)
			if err != nil {
				t.Error("could not create sbi")
			}

			err = pnd.addSbi(sbi)
			if err != nil {
				t.Error("could not add sbi")
			}
			mne := &CommonNetworkElement{
				UUID:      mneid,
				Model:     &openconfig.Device{},
				sbi:       sbi,
				transport: nil,
			}
			_, err = pnd.addNetworkElement(mne)
			if err := pnd.RemoveNetworkElement(tt.args.uuid); (err != nil) != tt.wantErr {
				t.Errorf("RemoveNetworkElement() error = %v, wantErr %v", err, tt.wantErr)
			}
		})
	}
}

func Test_pndImplementation_RemoveSbi(t *testing.T) {
	type args struct {
		id uuid.UUID
	}
	tests := []struct {
		name    string
		args    args
		wantErr bool
	}{
		{name: "default", args: args{id: defaultSbiID}, wantErr: false},
		{name: "fails", args: args{id: uuid.New()}, wantErr: true},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			t.Parallel()
			eventService := eventservice.NewMockEventService()
			sbiStore := NewMemorySbiStore()
			networkElementStore := NewMemoryNetworkElementStore()
			sbiService := NewSbiService(sbiStore, eventService)
			networkElementService := NewNetworkElementService(networkElementStore, sbiService, eventService)

			pnd := &pndImplementation{
				Name:                  "test-remove-sbi",
				Description:           "test-remove-sbi",
				southboundService:     sbiService,
				networkElementService: networkElementService,
				Id:                    defaultPndID,
			}

			sbi, err := NewSBI(spb.Type_TYPE_OPENCONFIG, defaultSbiID)
			if err != nil {
				t.Error("could not create sbi")
			}

			err = pnd.addSbi(sbi)
			if err != nil {
				t.Error("could not add sbi")
			}

			if err := pnd.RemoveSbi(tt.args.id); (err != nil) != tt.wantErr {
				t.Errorf("RemoveSbi() error = %v, wantErr %v", err, tt.wantErr)
			}

			foundSbi, _ := pnd.southboundService.Get(store.Query{ID: tt.args.id})
				t.Errorf("RemoveSbi() SBI still in SBI store %v", pnd.southboundService)
			if tt.name == "exclusively remove associated network elements" {
				allNetworkElements, _ := pnd.networkElementService.GetAll()
				if len(allNetworkElements) != 1 {
					t.Errorf("RemoveSbi() non associated network elements should remain in the storage %v", allNetworkElements)
				allNetworkElements, _ := pnd.networkElementService.GetAll()
				if len(allNetworkElements) != 0 {
					t.Errorf("RemoveSbi() associated network elements have not been removed correctly %v", len(allNetworkElements))
func Test_pndImplementation_RemoveSbiWithAssociatedNetworkElements(t *testing.T) {
	opts := &tpb.TransportOption{
		TransportOption: &tpb.TransportOption_GnmiTransportOption{
			GnmiTransportOption: &tpb.GnmiTransportOption{},
		},
	}
	type args struct {
		id uuid.UUID
	}
	tests := []struct {
		name    string
		args    args
		wantErr bool
	}{
		{name: "exclusively remove associated network elements", args: args{id: defaultSbiID}, wantErr: false},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			t.Parallel()
			eventService := eventservice.NewMockEventService()
			sbiStore := NewMemorySbiStore()
			networkElementStore := NewMemoryNetworkElementStore()
			sbiService := NewSbiService(sbiStore, eventService)
			networkElementService := NewNetworkElementService(networkElementStore, sbiService, eventService)
			pnd := &pndImplementation{
				Name:                  "test-remove-sbi",
				Description:           "test-remove-sbi",
				southboundService:     sbiService,
				networkElementService: networkElementService,
				Id:                    defaultPndID,

			sbi, err := NewSBI(spb.Type_TYPE_OPENCONFIG, defaultSbiID)
			if err != nil {
				t.Error("could not create sbi")
			}

			err = pnd.addSbi(sbi)
			if err != nil {
				t.Error("could not add sbi")
			}

			if err := pnd.addSbi(&OpenConfig{id: defaultSbiID}); err != nil {
				t.Error(err)
			}
			_, err = pnd.AddNetworkElement("associatedNetworkElement", opts, tt.args.id)
			_, err = pnd.AddNetworkElement("associatedNetworkElement2", opts, tt.args.id)
			if tt.name == "exclusively remove associated network elements" {
				newID := uuid.New()
				if err := pnd.addSbi(&OpenConfig{id: newID}); err != nil {
				_, err := pnd.AddNetworkElement("associatedNetworkElement2", opts, newID)
			if err := pnd.RemoveSbi(tt.args.id); (err != nil) != tt.wantErr {
				t.Errorf("RemoveSbi() error = %v, wantErr %v", err, tt.wantErr)
			}
			foundSbi, _ := pnd.southboundService.Get(store.Query{ID: tt.args.id})
				t.Errorf("RemoveSbi() SBI still in SBI store %v", pnd.southboundService)
			if tt.name == "exclusively remove associated network elements" {
				allNetworkElements, _ := pnd.southboundService.GetAll()
				if len(allNetworkElements) != 1 {
					t.Errorf("RemoveSbi() non associated network elements should remain in the storage %v", allNetworkElements)
				allNetworkElements, _ := pnd.southboundService.GetAll()
				if len(allNetworkElements) != 0 {
					t.Errorf("RemoveSbi() associated network elements have not been removed correctly %v", len(allNetworkElements))
			}
		})
	}
}

func Test_pndImplementation_Request(t *testing.T) {
	type args struct {
		uuid uuid.UUID
		path string
		rErr error
	}
	tests := []struct {
		name    string
		args    args
		wantErr bool
	}{
		{
			name: "default",
			args: args{
				uuid: mdid,
				path: "",
				rErr: nil,
			},
			wantErr: false,
		},
		{
			name: "error",
			args: args{
				path: "",
				rErr: errors.New("deliberate test fail"),
			},
			wantErr: true,
		},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			t.Parallel()
			sbiService := NewGenericService[southbound.SouthboundInterface]()
			networkElementService := NewNetworkElementServiceMock()
				Name:                  "default",
				Description:           "default test pnd",
				southboundService:     &sbiService,
				networkElementService: networkElementService,
				changes:               store.NewChangeStore(),
				Id:                    defaultPndID,
			}

			sbi, err := NewSBI(spb.Type_TYPE_OPENCONFIG)
			if err != nil {
				t.Error("could not create sbi")
			}

			err = pnd.addSbi(sbi)
			if err != nil {
				t.Error("could not add sbi")
			}

			transport := mocks.Transport{}
			transport.On("Get", mockContext, mock.Anything).Return(&gpb.GetResponse{}, tt.args.rErr)
			transport.On("ProcessResponse", mock.Anything, mock.Anything, mock.Anything).Return(tt.args.rErr)
			networkElementWithMockTransport := &CommonNetworkElement{
				UUID:      mdid,
				Model:     &openconfig.Device{},
				sbi:       sbi,
				transport: &transport,
			}

			_, _ = pnd.addNetworkElement(networkElementWithMockTransport)
			_, err = pnd.Request(tt.args.uuid, tt.args.path)
			if (err != nil) != tt.wantErr {
				t.Errorf("Request() error = %v, wantErr %v", err, tt.wantErr)
			}
			mne, _ := pnd.networkElementService.Get(store.Query{ID: mdid})
			if mne == nil {
			if err := pnd.networkElementService.Delete(mne); err != nil {
		})
	}
}

func Test_pndImplementation_RequestAll(t *testing.T) {
	type args struct {
		uuid uuid.UUID
		path string
		rErr error
	}
	tests := []struct {
		name    string
		args    args
		wantErr bool
	}{
		{
			name: "default",
			args: args{
				uuid: mdid,
				path: "",
				rErr: nil,
			},
			wantErr: false,
		},
		{
			name: "error",
			args: args{
				path: "",
				rErr: errors.New("deliberate test fail"),
			},
			wantErr: true,
		},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			t.Parallel()
			sbiService := NewGenericService[southbound.SouthboundInterface]()
			networkElementService := NewNetworkElementServiceMock()
				Name:                  "default",
				Description:           "default test pnd",
				southboundService:     &sbiService,
				networkElementService: networkElementService,
				changes:               store.NewChangeStore(),
				Id:                    defaultPndID,
			sbi, err := NewSBI(spb.Type_TYPE_OPENCONFIG)
			if err != nil {
				t.Error("could not create sbi")
			}

			err = pnd.addSbi(sbi)
			if err != nil {
				t.Error("could not add sbi")
			transport := mocks.Transport{}
			transport.On("Get", mockContext, mock.Anything).Return(&gpb.GetResponse{}, tt.args.rErr)
			transport.On("ProcessResponse", mock.Anything, mock.Anything, mock.Anything).Return(tt.args.rErr)

			networkElementWithMockTransport := &CommonNetworkElement{
				UUID:      mdid,
				Model:     &openconfig.Device{},
				sbi:       sbi,
				transport: &transport,
			}

			_, _ = pnd.addNetworkElement(networkElementWithMockTransport)
			mne, _ := pnd.networkElementService.Get(store.Query{ID: mdid})
			if mne == nil {
			if err := pnd.networkElementService.Delete(mne); err != nil {
Manuel Kieweg's avatar
Manuel Kieweg committed

func Test_pndImplementation_ChangeMNE(t *testing.T) {
Manuel Kieweg's avatar
Manuel Kieweg committed
	opts := &tpb.TransportOption{
		TransportOption: &tpb.TransportOption_GnmiTransportOption{
			GnmiTransportOption: &tpb.GnmiTransportOption{},
		},
Manuel Kieweg's avatar
Manuel Kieweg committed
	}
	type args struct {
Manuel Kieweg's avatar
Manuel Kieweg committed
		operation ppb.ApiOperation
Manuel Kieweg's avatar
Manuel Kieweg committed
		path      string
		value     []string
	}
	tests := []struct {
		name    string
		args    args
		wantErr bool
	}{
Manuel Kieweg's avatar
Manuel Kieweg committed
			name: "update",
Fabian Seidl's avatar
Fabian Seidl committed
				operation: ppb.ApiOperation_API_OPERATION_UPDATE,
				path:      "/system/config/hostname",
				value:     []string{"ceos3000"},
			},
			wantErr: false,
		},
		{
Manuel Kieweg's avatar
Manuel Kieweg committed
			name: "replace",
Fabian Seidl's avatar
Fabian Seidl committed
				operation: ppb.ApiOperation_API_OPERATION_REPLACE,
				path:      "/system/config/hostname",
				value:     []string{"ceos3000"},
			},
			wantErr: false,
		},
		{
Manuel Kieweg's avatar
Manuel Kieweg committed
			name: "delete",
Fabian Seidl's avatar
Fabian Seidl committed
				operation: ppb.ApiOperation_API_OPERATION_DELETE,
				path:      "/system/config/hostname",
			},
			wantErr: false,
		},
		{
Manuel Kieweg's avatar
Manuel Kieweg committed
			name: "delete w/args",
Fabian Seidl's avatar
Fabian Seidl committed
				operation: ppb.ApiOperation_API_OPERATION_DELETE,
				path:      "/system/config/hostname",
				value:     []string{"ceos3000"},
			},
			wantErr: false,
		},

		// Negative test cases
		{
Manuel Kieweg's avatar
Manuel Kieweg committed
			name: "invalid operation",
Manuel Kieweg's avatar
Manuel Kieweg committed
				operation: 54,
Manuel Kieweg's avatar
Manuel Kieweg committed
			name: "invalid arg count",
Fabian Seidl's avatar
Fabian Seidl committed
				operation: ppb.ApiOperation_API_OPERATION_UPDATE,
				path:      "/system/config/hostname",
				value:     []string{"ceos3000", "ceos3001"},
			},
			wantErr: true,
		},
Manuel Kieweg's avatar
Manuel Kieweg committed
			name: "invalid arg count - update, no args",
Manuel Kieweg's avatar
Manuel Kieweg committed
			args: args{
Fabian Seidl's avatar
Fabian Seidl committed
				operation: ppb.ApiOperation_API_OPERATION_UPDATE,
Manuel Kieweg's avatar
Manuel Kieweg committed
				path:      "/system/config/hostname",
			},
			wantErr: true,
		},
		{
Manuel Kieweg's avatar
Manuel Kieweg committed
			name: "invalid arg count - replace, no args",
Manuel Kieweg's avatar
Manuel Kieweg committed
			args: args{
Fabian Seidl's avatar
Fabian Seidl committed
				operation: ppb.ApiOperation_API_OPERATION_UPDATE,
Manuel Kieweg's avatar
Manuel Kieweg committed
				path:      "/system/config/hostname",
			},
			wantErr: true,
		},
			name: "network element not found",
Fabian Seidl's avatar
Fabian Seidl committed
				operation: ppb.ApiOperation_API_OPERATION_UPDATE,
Manuel Kieweg's avatar
Manuel Kieweg committed
	}
	for _, tt := range tests {
Manuel Kieweg's avatar
Manuel Kieweg committed
		t.Run(tt.name, func(t *testing.T) {
			t.Parallel()
Manuel Kieweg's avatar
Manuel Kieweg committed
			pnd := newPnd()
			if err := pnd.addSbi(&OpenConfig{id: defaultSbiID}); err != nil {
				t.Error(err)
			}
			_, err := pnd.AddNetworkElement("testnetworkElement", opts, defaultSbiID)
			networkElements, err := pnd.networkElementService.GetAllAsLoaded()
				err := errors.New("error fetching network element")
Manuel Kieweg's avatar
Manuel Kieweg committed
				t.Error(err)
				return
			}

			neUUID, err := uuid.Parse(networkElements[0].ID)
				err := errors.New("error parsing network element uuid")
			_, err = pnd.ChangeMNE(neUUID, tt.args.operation, tt.args.path, tt.args.value...)
			if (err != nil) != tt.wantErr {
				t.Errorf("ChangeMNE() error = %v, wantErr %v", err, tt.wantErr)
				return
			}
			if !tt.wantErr {
				if len(pnd.changes.Store) != 1 {
					t.Errorf("ChangeMNE() unexpected change count. got %v, want 1", len(pnd.changes.Store))
Manuel Kieweg's avatar
Manuel Kieweg committed
			}
		})
	}
func Test_pndImplementation_GetNetworkElement(t *testing.T) {
	opts := &tpb.TransportOption{
		Address:  "",
		Username: "test",
		Password: "test",
		TransportOption: &tpb.TransportOption_GnmiTransportOption{
			GnmiTransportOption: &tpb.GnmiTransportOption{},
		},
	}

	pnd := newPnd()
Fabian Seidl's avatar
Fabian Seidl committed
	sbi, err := NewSBI(spb.Type_TYPE_OPENCONFIG)
	if err != nil {
		t.Errorf("NewSBI() error = %v", err)
Fabian Seidl's avatar
Fabian Seidl committed
	}
	err = pnd.addSbi(sbi)
	if err != nil {
		t.Error(err)
		return
	}
	mne, err := NewNetworkElement("default", mneid, opts, sbi, conflict.Metadata{ResourceVersion: 0})
Manuel Kieweg's avatar
Manuel Kieweg committed
	if err != nil {
		t.Error(err)
		return
	}
	_, err = pnd.addNetworkElement(mne)
Manuel Kieweg's avatar
Manuel Kieweg committed
		t.Error(err)
		return
	}
Manuel Kieweg's avatar
Manuel Kieweg committed
	type args struct {
		uuid uuid.UUID
	}
	tests := []struct {
		name    string
		args    args
		want    ygot.GoStruct
		wantErr bool
	}{
		{
			name:    "default",
Manuel Kieweg's avatar
Manuel Kieweg committed
			want:    sbi.Schema().Root,
			wantErr: false,
		},
		{
			name:    "network element not found",
Manuel Kieweg's avatar
Manuel Kieweg committed
			args:    args{uuid: mdid},
			want:    nil,
			wantErr: true,
		},
	}
	for _, tt := range tests {
Manuel Kieweg's avatar
Manuel Kieweg committed
		t.Run(tt.name, func(t *testing.T) {
			t.Parallel()
			foundNetworkElement, err := pnd.GetNetworkElement(tt.args.uuid.String())
Manuel Kieweg's avatar
Manuel Kieweg committed
			if (err != nil) != tt.wantErr {
				t.Errorf("GetNetworkElement() error = %v, wantErr %v", err, tt.wantErr)
Manuel Kieweg's avatar
Manuel Kieweg committed
				return
			}
			if foundNetworkElement != nil {
				if !reflect.DeepEqual(foundNetworkElement.(networkelement.NetworkElement).GetModel(), tt.want) {
					t.Errorf("GetNetworkElement() got = %v, want %v", foundNetworkElement.(networkelement.NetworkElement).GetModel(), tt.want)
func Test_pndImplementation_GetNetworkElementByName(t *testing.T) {
	opts := &tpb.TransportOption{
		Address:  "",
		Username: "test",
		Password: "test",
		TransportOption: &tpb.TransportOption_GnmiTransportOption{
			GnmiTransportOption: &tpb.GnmiTransportOption{},
		},
	}

Fabian Seidl's avatar
Fabian Seidl committed
	sbi, err := NewSBI(spb.Type_TYPE_OPENCONFIG)
	if err != nil {
		t.Errorf("NewSBI() error = %v", err)
Fabian Seidl's avatar
Fabian Seidl committed
	}
	if err != nil {
		t.Error(err)
		return
	}
	mne, err := NewNetworkElement("my-mne", mneid, opts, sbi, conflict.Metadata{ResourceVersion: 0})
	if err != nil {
		t.Error(err)
		return
	}
	_, err = pnd.addNetworkElement(mne)
		t.Error(err)
		return
	}
	type args struct {
		name string
	}
	tests := []struct {
		name    string
		args    args
		want    ygot.GoStruct
		wantErr bool
	}{
		{
			name:    "default",
			args:    args{name: mne.Name()},
			want:    sbi.Schema().Root,
			wantErr: false,
		},
		{
			name:    "network element not found",
			args:    args{name: "test-mne"},
			want:    nil,
			wantErr: true,
		},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			t.Parallel()
			foundNetworkElement, err := pnd.GetNetworkElement(tt.args.name)
			if (err != nil) != tt.wantErr {
				t.Errorf("GetNetworkElementByName() error = %v, wantErr %v", err, tt.wantErr)
			if foundNetworkElement != nil {
				if !reflect.DeepEqual(foundNetworkElement.(networkelement.NetworkElement).GetModel(), tt.want) {
					t.Errorf("GetNetworkElementByName() got = %v, want %v", foundNetworkElement.(networkelement.NetworkElement).GetModel(), tt.want)
Manuel Kieweg's avatar
Manuel Kieweg committed
		})
	}
}

func Test_pndImplementation_Confirm(t *testing.T) {
	tests := []struct {
		name    string
		wantErr bool
	}{
		{
			name:    "default",
			wantErr: false,
		},
		{
			name:    "uncommitted",
			wantErr: true,
		},
	}
Manuel Kieweg's avatar
Manuel Kieweg committed
	for _, tt := range tests {
Manuel Kieweg's avatar
Manuel Kieweg committed
		t.Run(tt.name, func(t *testing.T) {
			t.Parallel()
			sbiService := NewGenericService[southbound.SouthboundInterface]()
			networkElementService := NewNetworkElementServiceMock()
				Name:                  "default",
				Description:           "default test pnd",
				southboundService:     &sbiService,
				networkElementService: networkElementService,
				changes:               store.NewChangeStore(),
				Id:                    defaultPndID,
			mne := mockNetworkElement()
			tr, ok := mne.Transport().(*mocks.Transport)
			if !ok {
				log.Errorf("Confirm(), failed type conversion: %v", ok)
			}

			tr.On("Set", mockContext, mock.Anything, mock.Anything, mock.Anything).Return(nil)
			_, err := pnd.addNetworkElement(mne)
Manuel Kieweg's avatar
Manuel Kieweg committed
				t.Error(err)
				return
			}
			_, err = pnd.ChangeMNE(mne.ID(), ppb.ApiOperation_API_OPERATION_UPDATE, "system/config/hostname", "ceos3000")
			if err != nil {
Manuel Kieweg's avatar
Manuel Kieweg committed
				t.Error(err)
				return
			}
Manuel Kieweg's avatar
Manuel Kieweg committed
			u := pnd.PendingChanges()[0]
Manuel Kieweg's avatar
Manuel Kieweg committed
			if tt.name != "uncommitted" {
				if err := pnd.Commit(u); (err != nil) != tt.wantErr {
					t.Errorf("Confirm() error = %v, wantErr %v", err, tt.wantErr)
					return
				}
			}
			if err := pnd.Confirm(u); (err != nil) != tt.wantErr {
				t.Errorf("Confirm() error = %v, wantErr %v", err, tt.wantErr)
			}
		})
	}
}
Manuel Kieweg's avatar
Manuel Kieweg committed

func Test_pndImplementation_PendingChanges(t *testing.T) {
	testName := t.Name()
	callback := func(first ygot.GoStruct, second ygot.GoStruct) error {
		log.Infof("callback in test %v", testName)
		return nil
	}

	store := store.NewChangeStore()
	pending := NewChange(mneid, &openconfig.Device{}, &openconfig.Device{}, callback)
	if err := store.Add(pending); err != nil {
		t.Error(err)
		return
	}
Manuel Kieweg's avatar
Manuel Kieweg committed
	tests := []struct {
		name string
		want []uuid.UUID
	}{
		{
			name: "default",
			want: []uuid.UUID{pending.cuid},
Manuel Kieweg's avatar
Manuel Kieweg committed
		},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			pnd := newPnd()
			pnd.changes = store
Manuel Kieweg's avatar
Manuel Kieweg committed
			if got := pnd.PendingChanges(); !reflect.DeepEqual(got, tt.want) {
				t.Errorf("pndImplementation.PendingChanges() = %v, want %v", got, tt.want)
			}
		})
	}
}

func Test_pndImplementation_CommittedChanges(t *testing.T) {
	testName := t.Name()
	callback := func(first ygot.GoStruct, second ygot.GoStruct) error {
		log.Infof("callback in test %v", testName)
		return nil
	}

	store := store.NewChangeStore()
	committed := NewChange(mneid, &openconfig.Device{}, &openconfig.Device{}, callback)
	if err := committed.Commit(); err != nil {
		t.Error(err)
		return
	}
	if err := store.Add(committed); err != nil {
		t.Error(err)
		return
	}
Manuel Kieweg's avatar
Manuel Kieweg committed
	tests := []struct {
		name string
		want []uuid.UUID
	}{
		{
			name: "default",
			want: []uuid.UUID{committed.cuid},
Manuel Kieweg's avatar
Manuel Kieweg committed
		},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			pnd := newPnd()
			pnd.changes = store
Manuel Kieweg's avatar
Manuel Kieweg committed
			if got := pnd.CommittedChanges(); !reflect.DeepEqual(got, tt.want) {
				t.Errorf("pndImplementation.CommittedChanges() = %v, want %v", got, tt.want)
			}
		})
	}
}

func Test_pndImplementation_ConfirmedChanges(t *testing.T) {
	testName := t.Name()
	callback := func(first ygot.GoStruct, second ygot.GoStruct) error {
		log.Infof("callback in test %v", testName)
		return nil
	}
	store := store.NewChangeStore()
	confirmed := NewChange(mneid, &openconfig.Device{}, &openconfig.Device{}, callback)
	if err := confirmed.Commit(); err != nil {
		t.Error(err)
		return
	}
	if err := confirmed.Confirm(); err != nil {
		t.Error(err)
		return
	}
	if err := store.Add(confirmed); err != nil {
		t.Error(err)
		return
	}
Manuel Kieweg's avatar
Manuel Kieweg committed
	tests := []struct {
		name string
		want []uuid.UUID
	}{
		{
			name: "default",
			want: []uuid.UUID{confirmed.cuid},
Manuel Kieweg's avatar
Manuel Kieweg committed
		},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			pnd := newPnd()
			pnd.changes = store
Manuel Kieweg's avatar
Manuel Kieweg committed
			if got := pnd.ConfirmedChanges(); !reflect.DeepEqual(got, tt.want) {
				t.Errorf("pndImplementation.ConfirmedChanges() = %v, want %v", got, tt.want)
			}
		})
	}
}
func Test_pndImplementation_saveGoStructsToFile(t *testing.T) {
	defer removeTestGoStructs()
	type genericGoStructClientArg struct {
		fn    string
		rtrn  []interface{}
		times int
	}
	// Create a new mock for GenericGoStructClient. With
	// genericGoStructClientArg it is possible to set the Return values of the
	// mocks methods.
	newGenericGoStructClient := func(args ...genericGoStructClientArg) *mocks.GenericGoStructClient {
		ggsc := &mocks.GenericGoStructClient{}
		for _, arg := range args {
			ggsc.On(arg.fn).Return(arg.rtrn...).Times(arg.times)
		}
		ggsc.On("CloseSend").Return(nil)
		return ggsc
	}

	type args struct {
		id     uuid.UUID
	}
	tests := []struct {
		name    string
		args    args
		wantErr bool
	}{
		{
			name: "default",
			args: args{
				id: uuid.New(),
				client: newGenericGoStructClient(
					[]genericGoStructClientArg{
						{
							fn: "Recv",
							rtrn: []interface{}{
								&csbi.Payload{Chunk: []byte("test")},
								nil,
							},
							times: 3,
						},
						{
							fn: "Recv",
							rtrn: []interface{}{
								&csbi.Payload{Chunk: nil},
								io.EOF,
							},
							times: 1,
						},
					}...),
			},
			wantErr: false,
		},
		{
			name: "unexpected EOF error",
			args: args{
				id: uuid.New(),
				client: newGenericGoStructClient(
					[]genericGoStructClientArg{
						{
							fn: "Recv",
							rtrn: []interface{}{
								&csbi.Payload{Chunk: nil},
								io.ErrUnexpectedEOF,
							},
							times: 1,
						},
						{
							fn: "CloseSend",
							rtrn: []interface{}{
								nil,
							},
							times: 1,
						},
					}...),
			},
			wantErr: true,
		},
	}

		// waitgroup and extra func needed to be able to clean up generated pnd directories while running test cases in parallel mode,
		// var x is just a dummy to be able to call the func,
		// outer t.Run() required for defered wg.Done().
		// reference: https://stackoverflow.com/a/63609718
		x := func() {
			defer wg.Done()
			t.Run("parallel waiting func", func(t *testing.T) {
				t.Run(tt.name, func(t *testing.T) {
					t.Parallel()
					err := saveStreamToFile(tt.args.client, util.GoStructName, tt.args.id)
					if (err != nil) != tt.wantErr {
						t.Errorf("saveGoStructsToFile() error = %v, wantErr %v", err, tt.wantErr)
					}
				})
			})
		}
		x()

func Test_pndImplementation_SubscribePath(t *testing.T) {
	type fields struct {
		Name                  string
		Description           string
		southboundService     southbound.Service
		networkElementService networkelement.Service
		changes               *store.ChangeStore
		ID                    uuid.UUID
		csbiClient            cpb.CsbiServiceClient
		callback              func(uuid.UUID, chan networkelement.Details)
		eventService          eventInterfaces.Service
	}
	type args struct {
		uuid    uuid.UUID
		subList *ppb.SubscriptionList
	}
	tests := []struct {
		name    string
		fields  fields
		args    args
		wantErr bool
	}{
		//TODO: Implement proper test here!
		// {
		//  name: "default",
		//  args: args{
		//      subList: &ppb.SubscriptionList{
		//          Subscription: []*ppb.Subscription{
		//              {
		//                  Path:           "",
		//                  StreamMode:     ppb.StreamMode_STREAM_MODE_SAMPLE,
		//                  SampleInterval: 1000000000, // 1 second
		//              },
		//          },
		//          Mode: ppb.SubscriptionMode_SUBSCRIPTION_MODE_STREAM,
		//      },
		//  },
		// },
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			pnd := &pndImplementation{
				Name:                  tt.fields.Name,
				Description:           tt.fields.Description,
				southboundService:     tt.fields.southboundService,
				networkElementService: tt.fields.networkElementService,
				changes:               tt.fields.changes,
				Id:                    tt.fields.ID,
				csbiClient:            tt.fields.csbiClient,
				callback:              tt.fields.callback,
				eventService:          tt.fields.eventService,
			}
			if err := pnd.SubscribePath(tt.args.uuid, tt.args.subList); (err != nil) != tt.wantErr {
				t.Errorf("pndImplementation.SubscribePath() error = %v, wantErr %v", err, tt.wantErr)
			}
		})
	}
}