Skip to content
Snippets Groups Projects
principalNetworkDomain_test.go 27.2 KiB
Newer Older
  • Learn to ignore specific revisions
  • 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"
    	"testing"
    
    
    	"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/interfaces/device"
    	"code.fbi.h-da.de/danet/gosdn/controller/interfaces/networkdomain"
    	"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/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_AddDevice(t *testing.T) {
    	type args struct {
    
    Malte Bauch's avatar
    Malte Bauch committed
    		device interface{}
    
    Manuel Kieweg's avatar
    Manuel Kieweg committed
    		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.AddDevice(tt.args.name, tt.args.opts, defaultSbiID)
    
    			if (err != nil) != tt.wantErr {
    				t.Errorf("AddDevice() error = %v, wantErr %v", err, tt.wantErr)
    
    			if tt.name != "fails wrong type" {
    				if err == nil {
    
    					d, err := pnd.deviceService.Get(store.Query{Name: tt.args.name})
    
    Manuel Kieweg's avatar
    Manuel Kieweg committed
    					if err != nil {
    						t.Errorf("AddDevice() error = %v", err)
    						return
    
    Manuel Kieweg's avatar
    Manuel Kieweg committed
    					if d.Name() != tt.args.name {
    						t.Errorf("AddDevice() got = %v, want %v", d.Name(), tt.args.name)
    
    Manuel Kieweg's avatar
    Manuel Kieweg committed
    					}
    
    					if err := pnd.deviceService.Delete(d); 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 device 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
    		devices     device.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,
    				deviceService:     tt.fields.devices,
    
    			}
    			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_MarshalDevice(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",
    			args:    args{did},
    
    			want:    "{\n\t\"Acl\": null,\n\t\"Bfd\": null,\n\t\"Components\": null,\n\t\"Interfaces\": null,\n\t\"Keychains\": null,\n\t\"LocalRoutes\": 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")
    			}
    
    Manuel Kieweg's avatar
    Manuel Kieweg committed
    			d := &CommonDevice{
    
    				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.addDevice(d)
    			if err != nil {
    
    			got, err := pnd.MarshalDevice(tt.args.uuid.String())
    
    			if (err != nil) != tt.wantErr {
    				t.Errorf("MarshalDevice() error = %v, wantErr %v", err, tt.wantErr)
    				return
    			}
    			if got != tt.want {
    				t.Errorf("MarshalDevice() got = %v, want %v", got, tt.want)
    			}
    
    			if err := pnd.deviceService.Delete(d); err != nil {
    
    		})
    	}
    }
    
    func Test_pndImplementation_RemoveDevice(t *testing.T) {
    	type args struct {
    		uuid uuid.UUID
    	}
    	tests := []struct {
    		name    string
    		args    args
    		wantErr bool
    	}{
    		{name: "default", args: args{uuid: did}, wantErr: false},
    
    		{name: "fails", args: args{uuid: uuid.New()}, wantErr: true},
    
    	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")
    			}
    			d := &CommonDevice{
    				UUID:      did,
    				Model:     &openconfig.Device{},
    				sbi:       sbi,
    				transport: nil,
    			}
    
    			_, err = pnd.addDevice(d)
    			if err != nil {
    
    			if err := pnd.RemoveDevice(tt.args.uuid); (err != nil) != tt.wantErr {
    
    				t.Errorf("RemoveDevice() 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) {
    			sbiStore := NewSbiStore(defaultPndID)
    			deviceStore := NewDeviceStore(defaultPndID)
    			sbiService := NewSbiService(sbiStore)
    			deviceService := NewDeviceService(deviceStore, sbiService)
    
    			pnd := &pndImplementation{
    
    				Name:              "test-remove-sbi",
    				Description:       "test-remove-sbi",
    				southboundService: sbiService,
    				deviceService:     deviceService,
    				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 devices" {
    
    				allDevices, _ := pnd.deviceService.GetAll()
    
    				if len(allDevices) != 1 {
    					t.Errorf("RemoveSbi() non associated devices should remain in the storage %v", allDevices)
    				}
    			} else {
    
    				allDevices, _ := pnd.deviceService.GetAll()
    
    				if len(allDevices) != 0 {
    					t.Errorf("RemoveSbi() associated devices have not been removed correctly %v", len(allDevices))
    				}
    			}
    		})
    	}
    }
    
    func Test_pndImplementation_RemoveSbiWithAssociatedDevices(t *testing.T) {
    
    	opts := &tpb.TransportOption{
    		TransportOption: &tpb.TransportOption_GnmiTransportOption{
    			GnmiTransportOption: &tpb.GnmiTransportOption{},
    		},
    	}
    
    	}
    	tests := []struct {
    		name    string
    		args    args
    		wantErr bool
    	}{
    
    		{name: "exclusively remove associated devices", args: args{id: defaultSbiID}, wantErr: false},
    
    	}
    	for _, tt := range tests {
    		t.Run(tt.name, func(t *testing.T) {
    
    			sbiStore := NewSbiStore(defaultPndID)
    
    			deviceStore := NewDeviceStore(defaultPndID)
    			sbiService := NewSbiService(sbiStore)
    			deviceService := NewDeviceService(deviceStore, sbiService)
    
    			pnd := &pndImplementation{
    
    				Name:              "test-remove-sbi",
    				Description:       "test-remove-sbi",
    				southboundService: sbiService,
    				deviceService:     deviceService,
    				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.AddDevice("associatedDevice", opts, tt.args.id)
    			if err != nil {
    
    			_, err = pnd.AddDevice("associatedDevice2", opts, tt.args.id)
    			if err != nil {
    
    				t.Error(err)
    			}
    			if tt.name == "exclusively remove associated devices" {
    				newID := uuid.New()
    				if err := pnd.addSbi(&OpenConfig{id: newID}); err != nil {
    
    				_, err := pnd.AddDevice("associatedDevice2", opts, newID)
    				if err != nil {
    
    			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 devices" {
    
    				allDevices, _ := pnd.southboundService.GetAll()
    
    				if len(allDevices) != 1 {
    					t.Errorf("RemoveSbi() non associated devices should remain in the storage %v", allDevices)
    
    				allDevices, _ := pnd.southboundService.GetAll()
    
    				if len(allDevices) != 0 {
    					t.Errorf("RemoveSbi() associated devices have not been removed correctly %v", len(allDevices))
    
    			}
    		})
    	}
    }
    
    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{
    				uuid: did,
    				path: "",
    				rErr: errors.New("deliberate test fail"),
    			},
    			wantErr: true,
    		},
    	}
    
    	for _, tt := range tests {
    		t.Run(tt.name, func(t *testing.T) {
    
    			sbiService := NewGenericService[southbound.SouthboundInterface]()
    			deviceService := NewGenericService[device.Device]()
    
    			pnd := pndImplementation{
    
    				Name:              "default",
    				Description:       "default test pnd",
    				southboundService: &sbiService,
    				deviceService:     &deviceService,
    				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)
    
    
    			deviceWithMockTransport := &CommonDevice{
    				UUID:      mdid,
    				Model:     &openconfig.Device{},
    				sbi:       sbi,
    				transport: &transport,
    			}
    
    
    			_, _ = pnd.addDevice(deviceWithMockTransport)
    
    			_, err = pnd.Request(tt.args.uuid, tt.args.path)
    
    			if (err != nil) != tt.wantErr {
    
    				t.Errorf("Request() error = %v, wantErr %v", err, tt.wantErr)
    			}
    
    			device, _ := pnd.deviceService.Get(store.Query{ID: mdid})
    
    			if err := pnd.deviceService.Delete(device); 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) {
    
    			sbiService := NewGenericService[southbound.SouthboundInterface]()
    			deviceService := NewGenericService[device.Device]()
    
    			pnd := pndImplementation{
    
    				Name:              "default",
    				Description:       "default test pnd",
    				southboundService: &sbiService,
    				deviceService:     &deviceService,
    				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)
    
    			deviceWithMockTransport := &CommonDevice{
    				UUID:      mdid,
    				Model:     &openconfig.Device{},
    				sbi:       sbi,
    				transport: &transport,
    			}
    
    
    			_, _ = pnd.addDevice(deviceWithMockTransport)
    
    			device, _ := pnd.deviceService.Get(store.Query{ID: mdid})
    
    			if err := pnd.deviceService.Delete(device); err != nil {
    
    Manuel Kieweg's avatar
    Manuel Kieweg committed
    
    func Test_pndImplementation_ChangeOND(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,
    		},
    
    Manuel Kieweg's avatar
    Manuel Kieweg committed
    			name: "device 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 {
    		t.Run(tt.name, func(t *testing.T) {
    
    Manuel Kieweg's avatar
    Manuel Kieweg committed
    			pnd := newPnd()
    			if err := pnd.addSbi(&OpenConfig{id: defaultSbiID}); err != nil {
    				t.Error(err)
    			}
    
    			_, err := pnd.AddDevice("testdevice", opts, defaultSbiID)
    			if err != nil {
    
    			devices, err := pnd.deviceService.GetAll()
    
    Manuel Kieweg's avatar
    Manuel Kieweg committed
    				err := errors.New("error fetching device")
    				t.Error(err)
    				return
    			}
    
    
    			_, err = pnd.ChangeOND(devices[0].ID(), tt.args.operation, tt.args.path, tt.args.value...)
    
    			if (err != nil) != tt.wantErr {
    
    Manuel Kieweg's avatar
    Manuel Kieweg committed
    				t.Errorf("ChangeOND() error = %v, wantErr %v", err, tt.wantErr)
    
    				if len(pnd.changes.Store) != 1 {
    					t.Errorf("ChangeOND() unexpected change count. got %v, want 1", len(pnd.changes.Store))
    
    Manuel Kieweg's avatar
    Manuel Kieweg committed
    			}
    		})
    	}
    
    Manuel Kieweg's avatar
    Manuel Kieweg committed
    
    func Test_pndImplementation_GetDevice(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)
    
    	err = pnd.addSbi(sbi)
    	if err != nil {
    		t.Error(err)
    		return
    	}
    
    	d, err := NewDevice("default", did, opts, sbi)
    
    Manuel Kieweg's avatar
    Manuel Kieweg committed
    	if err != nil {
    		t.Error(err)
    		return
    	}
    
    	_, err = pnd.addDevice(d)
    	if err != nil {
    
    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:    "device not found",
    			args:    args{uuid: mdid},
    			want:    nil,
    			wantErr: true,
    		},
    	}
    	for _, tt := range tests {
    		t.Run(tt.name, func(t *testing.T) {
    
    			foundDevice, err := pnd.GetDevice(tt.args.uuid.String())
    
    Manuel Kieweg's avatar
    Manuel Kieweg committed
    			if (err != nil) != tt.wantErr {
    				t.Errorf("GetDevice() error = %v, wantErr %v", err, tt.wantErr)
    				return
    			}
    
    			if foundDevice != nil {
    
    Andre Sterba's avatar
    Andre Sterba committed
    				if !reflect.DeepEqual(foundDevice.(device.Device).GetModel(), tt.want) {
    					t.Errorf("GetDevice() got = %v, want %v", foundDevice.(device.Device).GetModel(), tt.want)
    
    		})
    	}
    }
    
    func Test_pndImplementation_GetDeviceByName(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)
    
    	if err != nil {
    		t.Error(err)
    		return
    	}
    
    	d, err := NewDevice("my-device", did, opts, sbi)
    
    	if err != nil {
    		t.Error(err)
    		return
    	}
    
    	_, err = pnd.addDevice(d)
    	if err != nil {
    
    		t.Error(err)
    		return
    	}
    	type args struct {
    		name string
    	}
    	tests := []struct {
    		name    string
    		args    args
    		want    ygot.GoStruct
    		wantErr bool
    	}{
    		{
    			name:    "default",
    
    Manuel Kieweg's avatar
    Manuel Kieweg committed
    			args:    args{name: d.Name()},
    
    			want:    sbi.Schema().Root,
    			wantErr: false,
    		},
    		{
    			name:    "device not found",
    			args:    args{name: "test-device"},
    			want:    nil,
    			wantErr: true,
    		},
    	}
    	for _, tt := range tests {
    		t.Run(tt.name, func(t *testing.T) {
    
    			foundDevice, err := pnd.GetDevice(tt.args.name)
    
    			if (err != nil) != tt.wantErr {
    				t.Errorf("GetDeviceByName() error = %v, wantErr %v", err, tt.wantErr)
    				return
    			}
    			if foundDevice != nil {
    
    Andre Sterba's avatar
    Andre Sterba committed
    				if !reflect.DeepEqual(foundDevice.(device.Device).GetModel(), tt.want) {
    					t.Errorf("GetDeviceByName() got = %v, want %v", foundDevice.(device.Device).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 {
    		t.Run(tt.name, func(t *testing.T) {
    
    			sbiService := NewGenericService[southbound.SouthboundInterface]()
    			deviceService := NewGenericService[device.Device]()
    
    			pnd := pndImplementation{
    
    				Name:              "default",
    				Description:       "default test pnd",
    				southboundService: &sbiService,
    				deviceService:     &deviceService,
    				changes:           store.NewChangeStore(),
    				Id:                defaultPndID,
    
    Manuel Kieweg's avatar
    Manuel Kieweg committed
    			d := mockDevice()
    
    Manuel Kieweg's avatar
    Manuel Kieweg committed
    			tr := d.Transport().(*mocks.Transport)
    
    Manuel Kieweg's avatar
    Manuel Kieweg committed
    			tr.On("Set", mockContext, mock.Anything, mock.Anything).Return(nil)
    
    			_, err := pnd.addDevice(d)
    			if err != nil {
    
    Manuel Kieweg's avatar
    Manuel Kieweg committed
    				t.Error(err)
    				return
    			}
    
    			_, err = pnd.ChangeOND(d.ID(), ppb.ApiOperation_API_OPERATION_UPDATE, "system/config/hostname", "ceos3000")
    
    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(did, &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