Skip to content
Snippets Groups Projects
principalNetworkDomain_test.go 28.5 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"
	"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"
Manuel Kieweg's avatar
Manuel Kieweg committed
	"code.fbi.h-da.de/danet/yang-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 removeExistingPNDStore() {
	os.Remove(fmt.Sprintf("stores/device-store-%s.json", defaultPndID))
}

func TestNewPND(t *testing.T) {
	p := newPnd()
	if err := p.addSbi(&OpenConfig{id: defaultSbiID}); err != nil {
	type args struct {
		name        string
		description string
		sbi         southbound.SouthboundInterface
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",
				sbi:         &OpenConfig{id: defaultSbiID},
				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, tt.args.sbi, nil, nil)
			if (err != nil) != tt.wantErr {
				t.Errorf("NewPND() error = %v, wantErr %v", err, tt.wantErr)
				return
			}
			if !reflect.DeepEqual(got, tt.want) {
				t.Errorf("NewPND() = %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)
			}
			if tt.name == "already exists" {
				pnd.devices.Store[did] = tt.args.device.(device.Device)
Manuel Kieweg's avatar
Manuel Kieweg committed
			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.devices.GetDevice(store.FromString(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.devices.Delete(d.ID()); 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,
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			pnd := newPnd()
			if tt.name == "already exists" {
				pnd.sbic.Store[defaultSbiID] = tt.args.sbi
			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 {
					_, ok := pnd.sbic.Store[defaultSbiID]
					if !ok {
						t.Errorf("AddSbi() SBI %v not in device store %v",
							tt.args.sbi, pnd.GetSBIs())
					}
					if err := pnd.sbic.Delete(defaultSbiID); err != nil {
func Test_pndImplementation_AddSbiFromStore(t *testing.T) {
	type args struct {
		uuid  uuid.UUID
		ttype spb.Type
		sbi   southbound.SouthboundInterface
		path  string
	}
	tests := []struct {
		name    string
		args    args
		wantErr bool
	}{
		{
			name: "default",
			args: args{
				uuid:  defaultSbiID,
				ttype: spb.Type_TYPE_OPENCONFIG,
				path:  "",
			},
			wantErr: false,
		},
		{
			name: "already exists",
			args: args{
				uuid:  defaultSbiID,
				ttype: spb.Type_TYPE_OPENCONFIG,
				path:  "",
				sbi: &OpenConfig{
					id:     defaultSbiID,
					schema: nil,
					path:   "",
				},
			},
			wantErr: true,
		},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			pnd := newPnd()

			if tt.name == "already exists" {
				pnd.sbic.Store[defaultSbiID] = tt.args.sbi.(southbound.SouthboundInterface)
			}

			err := pnd.AddSbiFromStore(tt.args.uuid, tt.args.ttype.String(), tt.args.path)
			if (err != nil) != tt.wantErr {
				t.Errorf("AddSbiFromStore() error = %v, wantErr %v", err, tt.wantErr)
			}
		})
	}
}

func Test_pndImplementation_ContainsDevice(t *testing.T) {
	type args struct {
		uuid   uuid.UUID
		device device.Device
	}
	tests := []struct {
		name string
		args args
		want bool
	}{
		{name: "default", args: args{
			uuid:   did,
Manuel Kieweg's avatar
Manuel Kieweg committed
			device: &CommonDevice{UUID: did},
		}, want: true},
		{name: "fails", args: args{
			uuid:   uuid.New(),
Manuel Kieweg's avatar
Manuel Kieweg committed
			device: &CommonDevice{UUID: did},
		{name: "fails empty", args: args{
			uuid:   uuid.New(),
Manuel Kieweg's avatar
Manuel Kieweg committed
			device: &CommonDevice{UUID: did},
		}, want: false},
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			pnd := newPnd()
			if tt.name != "fails empty" {
				if err := pnd.devices.Add(tt.args.device, "test"); err != nil {
			if got := pnd.ContainsDevice(tt.args.uuid); got != tt.want {
				t.Errorf("ContainsDevice() = %v, want %v", got, tt.want)
			}
			if err := pnd.devices.Delete(did); err != nil && tt.name != "fails empty" {
		})
	}
}

func Test_pndImplementation_Destroy(t *testing.T) {
	type fields struct {
		name        string
		description string
		sbi         *store.SbiStore
		devices     *store.DeviceStore
	}
	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,
				sbic:        tt.fields.sbi,
				devices:     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_GetSBIs(t *testing.T) {
	pnd := newPnd()
	tests := []struct {
		name string
		want *store.SbiStore
		{name: "default", want: pnd.sbic},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			if got := pnd.GetSBIs(); !reflect.DeepEqual(got, tt.want) {
				t.Errorf("GetSBIs() = %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\"Bgp\": null,\n\t\"Components\": null,\n\t\"Interfaces\": null,\n\t\"LocalRoutes\": null,\n\t\"Messages\": null,\n\t\"NetworkInstances\": null,\n\t\"RoutingPolicy\": null,\n\t\"System\": null\n}",
			wantErr: false,
		},
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			pnd := newPnd()
Manuel Kieweg's avatar
Manuel Kieweg committed
			d := &CommonDevice{
				UUID:      tt.args.uuid,
Manuel Kieweg's avatar
Manuel Kieweg committed
				GoStruct:  &openconfig.Device{},
Manuel Kieweg's avatar
Manuel Kieweg committed
				sbi:       nil,
				transport: nil,
			if err := pnd.addDevice(d); err != nil {
				t.Error(err)
			}
			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.devices.Delete(did); 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},
		{name: "fails empty", args: args{uuid: did}, wantErr: true},
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			pnd := newPnd()
			if tt.name != "fails empty" {
				sbi, err := NewSBI(spb.Type_TYPE_OPENCONFIG)
				if err != nil {
					t.Error(err)
					return
				}
Malte Bauch's avatar
Malte Bauch committed
				d := &CommonDevice{
					UUID: did,
Fabian Seidl's avatar
Fabian Seidl committed
					sbi:  sbi,
Malte Bauch's avatar
Malte Bauch committed
				}
				if err := pnd.addDevice(d); err != nil {
					t.Error(err)
				}
			}
			if err := pnd.RemoveDevice(tt.args.uuid); (err != nil) != tt.wantErr {
				t.Errorf("RemoveDevice() error = %v, wantErr %v", err, tt.wantErr)
			}
			if pnd.devices.Exists(did) && tt.name == "default" {
				t.Errorf("RemoveDevice() device still in device store %v", pnd.devices)
			}
		})
	}
}

func Test_pndImplementation_RemoveSbi(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: "default", args: args{id: defaultSbiID}, wantErr: false},
		{name: "fails", args: args{id: uuid.New()}, wantErr: true},
		{name: "fails empty", args: args{id: defaultSbiID}, wantErr: true},
		{name: "exclusively remove associated devices", args: args{id: defaultSbiID}, wantErr: false},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			pnd := &pndImplementation{
				Name:        "test-remove-sbi",
				Description: "test-remove-sbi",
				sbic:        store.NewSbiStore(defaultPndID),
				devices:     store.NewDeviceStore(defaultPndID),
				Id:          defaultPndID,
			if tt.name != "fails empty" && tt.name != "fails" {
				if err := pnd.addSbi(&OpenConfig{id: defaultSbiID}); err != nil {
				if err := pnd.AddDevice("associatedDevice", opts, tt.args.id); err != nil {
					t.Error(err)
				}
				if err := pnd.AddDevice("associatedDevice2", opts, tt.args.id); err != nil {
					t.Error(err)
				}
				if tt.name == "exclusively remove associated devices" {
					newID := uuid.New()
					if err := pnd.addSbi(&OpenConfig{id: newID}); err != nil {
						t.Error(err)
					}
					if err := pnd.AddDevice("associatedDevice2", opts, newID); err != nil {
						t.Error(err)
					}
				}
			if err := pnd.RemoveSbi(tt.args.id); (err != nil) != tt.wantErr {
				t.Errorf("RemoveSbi() error = %v, wantErr %v", err, tt.wantErr)
			}
			if pnd.sbic.Exists(tt.args.id) {
				t.Errorf("RemoveSbi() SBI still in SBI store %v", pnd.sbic)
			}

			if tt.name == "exclusively remove associated devices" {
				if len(pnd.devices.Store) != 1 {
					t.Errorf("RemoveSbi() non associated devices should remain in the storage %v", pnd.devices)
				}
			} else {
				if len(pnd.devices.Store) != 0 {
					t.Errorf("RemoveSbi() associated devices have not been removed correctly %v", len(pnd.devices.Store))
				}
			}
		})
	}
}

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) {
			deviceWithMockTransport := mockDevice()
			pnd := newPnd()
Manuel Kieweg's avatar
Manuel Kieweg committed
			tr := deviceWithMockTransport.Transport().(*mocks.Transport)
			tr.On("Get", mockContext, mock.Anything).Return(&gpb.GetResponse{}, tt.args.rErr)
			tr.On("ProcessResponse", mock.Anything, mock.Anything, mock.Anything).Return(tt.args.rErr)
Manuel Kieweg's avatar
Manuel Kieweg committed
			_ = 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)
			}
			if err := pnd.devices.Delete(mdid); 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) {
			deviceWithMockTransport := mockDevice()
			pnd := newPnd()
Manuel Kieweg's avatar
Manuel Kieweg committed
			tr := deviceWithMockTransport.Transport().(*mocks.Transport)
			tr.On("Get", mockContext, mock.Anything).Return(&gpb.GetResponse{}, tt.args.rErr)
			tr.On("ProcessResponse", mock.Anything, mock.Anything, mock.Anything).Return(tt.args.rErr)
Manuel Kieweg's avatar
Manuel Kieweg committed
			_ = pnd.addDevice(deviceWithMockTransport)
			if err := pnd.RequestAll(tt.args.path); (err != nil) != tt.wantErr {
				t.Errorf("RequestAll() error = %v, wantErr %v", err, tt.wantErr)
			}
			if err := pnd.devices.Delete(mdid); 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)
			}
			if err := pnd.AddDevice("testdevice", opts, defaultSbiID); err != nil {
			did, ok := pnd.devices.DeviceNameToUUIDLookup["testdevice"]
Manuel Kieweg's avatar
Manuel Kieweg committed
			if !ok {
				err := errors.New("error fetching device")
				t.Error(err)
				return
			}

			_, err := pnd.ChangeOND(did, 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)
				return
			}
			if !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) {
	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
	}
	d, err := NewDevice("", uuid.Nil, newGnmiTransportOptions(), sbi)
Manuel Kieweg's avatar
Manuel Kieweg committed
	if err != nil {
		t.Error(err)
		return
	}
	if err = pnd.addDevice(d); err != nil {
Manuel Kieweg's avatar
Manuel Kieweg committed
		t.Error(err)
		return
	}
	type args struct {
		uuid uuid.UUID
	}
	tests := []struct {
		name    string
		args    args
		want    ygot.GoStruct
		wantErr bool
	}{
		{
			name:    "default",
			args:    args{uuid: d.ID()},
			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 {
				if !reflect.DeepEqual(foundDevice.(device.Device).Model(), tt.want) {
					t.Errorf("GetDevice() got = %v, want %v", foundDevice.(device.Device).Model(), tt.want)
		})
	}
}

func Test_pndImplementation_GetDeviceByName(t *testing.T) {
	p := 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
	}
	d, err := NewDevice("my-device", uuid.Nil, newGnmiTransportOptions(), sbi)
	if err != nil {
		t.Error(err)
		return
	}
	if err = p.addDevice(d); 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 := p.GetDevice(tt.args.name)
			if (err != nil) != tt.wantErr {
				t.Errorf("GetDeviceByName() error = %v, wantErr %v", err, tt.wantErr)
				return
			}
			if foundDevice != nil {
				if !reflect.DeepEqual(foundDevice.(device.Device).Model(), tt.want) {
					t.Errorf("GetDeviceByName() got = %v, want %v", foundDevice.(device.Device).Model(), tt.want)
Manuel Kieweg's avatar
Manuel Kieweg committed
		})
	}
}

func Test_pndImplementation_Confirm(t *testing.T) {
Manuel Kieweg's avatar
Manuel Kieweg committed
	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) {
			pnd := newPnd()
			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)
Manuel Kieweg's avatar
Manuel Kieweg committed
			if err := pnd.addDevice(d); err != nil {
Manuel Kieweg's avatar
Manuel Kieweg committed
				t.Error(err)
				return
			}
Fabian Seidl's avatar
Fabian Seidl committed
			_, err := pnd.ChangeOND(d.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(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
		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(did, &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(did, &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_LoadStoredDevices(t *testing.T) {
	type args struct {
		device interface{}
		name   string
		opts   *tpb.TransportOption
	}
	tests := []struct {
		name    string
		args    args
		wantErr bool
	}{
		{
			name: "default",
			args: args{
				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()
			if err := pnd.addSbi(&OpenConfig{id: defaultSbiID}); err != nil {
				t.Error(err)
			}
			if tt.name == "already exists" {
				pnd.devices.Store[did] = tt.args.device.(device.Device)
			}
			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)
			}
		})
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			pnd := newPnd()
			if err := pnd.addSbi(&OpenConfig{id: defaultSbiID}); err != nil {
				t.Error(err)
			}

			err := pnd.loadStoredDevices()
			if err != nil {
				t.Error(err)
			}

			dev, err := pnd.GetDevice(tt.args.name)
			if err != nil {
				t.Errorf("GetDevice() error = %v, want no err", err)
			}

			if dev.Name() != tt.args.name {
				t.Errorf("Device name is = %s, want %s", dev.Name(), tt.args.name)
			}
		})
	}
}

// TODO(mbauch): This test case looks unfinished. For example there are no
// cases for 'already exists' and 'fails wrong type'.
func Test_pndImplementation_AddDeviceWithUUID(t *testing.T) {
	type args struct {
		uuid   uuid.UUID
		device interface{}
		name   string
		opts   *tpb.TransportOption
	}
	tests := []struct {
		name    string
		args    args
		wantErr bool
	}{
		{
			name: "default",
			args: args{
				uuid: did,
				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()
			if err := pnd.addSbi(&OpenConfig{id: defaultSbiID}); err != nil {
				t.Error(err)
			}
			if tt.name == "already exists" {
				pnd.devices.Store[did] = tt.args.device.(device.Device)
			}

			err := pnd.AddDeviceFromStore(tt.args.name, tt.args.uuid, 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.devices.GetDevice(store.FromString(tt.args.name))
					if err != nil {
						t.Errorf("AddDevice() error = %v", err)
						return
					}
					if d.Name() != tt.args.name {
						t.Errorf("AddDevice() got = %v, want %v", d.Name(), tt.args.name)
					}
					if d.ID() != tt.args.uuid {
						t.Errorf("AddDevice() got = %v, want %v", d.ID(), tt.args.uuid)
					}
					if err := pnd.devices.Delete(d.ID()); err != nil {
						t.Error(err)
					}
				}
			}
		})
	}
}

func Test_pndImplementation_saveGoStructsToFile(t *testing.T) {
	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
		client GenericGrpcClient
	}
	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,
		},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			_, err := saveGenericClientStreamToFile(tt.args.client, "gostructs.go", tt.args.id)
			if (err != nil) != tt.wantErr {
				t.Errorf("saveGoStructsToFile() error = %v, wantErr %v", err, tt.wantErr)
			}
		})
	}
}