package nucleus import ( "code.fbi.h-da.de/cocsn/gosdn/mocks" "code.fbi.h-da.de/cocsn/yang-models/generated/openconfig" "errors" "github.com/google/uuid" "github.com/openconfig/ygot/ygot" "github.com/stretchr/testify/mock" "reflect" "testing" ) func TestNewPND(t *testing.T) { pnd := newPnd() if err := pnd.addSbi(&OpenConfig{id: defaultSbiID}); err != nil { t.Error(err) } type args struct { name string description string sbi SouthboundInterface pid uuid.UUID } tests := []struct { name string args args want PrincipalNetworkDomain wantErr bool }{ { name: "default", args: args{ name: "default", description: "default test pnd", sbi: &OpenConfig{id: defaultSbiID}, pid: defaultPndID, }, want: &pnd, 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) 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 { device interface{} } tests := []struct { name string args args wantErr bool }{ { name: "default", args: args{ device: &Device{ UUID: did, }, }, wantErr: false, }, { name: "already exists", args: args{ device: &Device{ UUID: did, }, }, wantErr: true, }, { name: "fails wrong type", args: args{device: &pndImplementation{ id: did, }}, wantErr: true, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { pnd := newPnd() if tt.name == "already exists" { pnd.devices.store[did] = &Device{UUID: did} } err := pnd.AddDevice(tt.args.device) if (err != nil) != tt.wantErr { t.Errorf("AddDevice() error = %v, wantErr %v", err, tt.wantErr) } if tt.name != "fails wrong type" { if err == nil { _, ok := pnd.devices.store[did] if !ok { t.Errorf("AddDevice() Device %v not in device store %v", tt.args.device, pnd.devices) } if err := pnd.devices.delete(did); err != nil { t.Error(err) } } } }) } } func Test_pndImplementation_AddSbi(t *testing.T) { type args struct { sbi interface{} } tests := []struct { name string args args wantErr bool }{ { name: "default", args: args{ sbi: &OpenConfig{ id: defaultSbiID, }, }, wantErr: false, }, { name: "already exists", args: args{ sbi: &OpenConfig{ id: defaultSbiID, }, }, wantErr: true, }, { name: "fails wrong type", args: args{ sbi: &pndImplementation{ id: defaultSbiID, }, }, 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.(*OpenConfig) } 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 { t.Error(err) } } } }) } } func Test_pndImplementation_ContainsDevice(t *testing.T) { type args struct { uuid uuid.UUID device *Device } tests := []struct { name string args args want bool }{ {name: "default", args: args{ uuid: did, device: &Device{UUID: did}, }, want: true}, {name: "fails", args: args{ uuid: uuid.New(), device: &Device{UUID: did}, }, want: false}, {name: "fails empty", args: args{ uuid: uuid.New(), device: &Device{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); err != nil { t.Error(err) } } 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" { t.Error(err) } }) } } func Test_pndImplementation_Destroy(t *testing.T) { type fields struct { name string description string sbi sbiStore devices 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 *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 }{ { 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() d := &Device{ UUID: tt.args.uuid, GoStruct: &openconfig.Device{}, SBI: nil, Transport: nil, } if err := pnd.addDevice(d); err != nil { t.Error(err) } got, err := pnd.MarshalDevice(tt.args.uuid) 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 { t.Error(err) } }) } } 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" { d := &Device{UUID: did} 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) { 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}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { pnd := &pndImplementation{ name: "test-remove-sbi", description: "test-remove-sbi", sbic: sbiStore{store{}}, devices: deviceStore{store{}}, id: defaultPndID, } if tt.name != "fails empty" { if err := pnd.addSbi(&OpenConfig{id: defaultSbiID}); 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("RemoveDevice() SBI still in SBI store %v", pnd.sbic) } }) } } 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() tr := deviceWithMockTransport.Transport.(*mocks.Transport) tr.On("Get", mockContext, mock.Anything).Return(mock.Anything, tt.args.rErr) tr.On("ProcessResponse", mock.Anything, mock.Anything, mock.Anything).Return(tt.args.rErr) _ = pnd.addDevice(&deviceWithMockTransport) if err := pnd.Request(tt.args.uuid, tt.args.path); (err != nil) != tt.wantErr { t.Errorf("Request() error = %v, wantErr %v", err, tt.wantErr) } if err := pnd.devices.delete(mdid); err != nil { t.Error(err) } }) } } 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{ 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() tr := deviceWithMockTransport.Transport.(*mocks.Transport) tr.On("Get", mockContext, mock.Anything).Return(mock.Anything, tt.args.rErr) tr.On("ProcessResponse", mock.Anything, mock.Anything, mock.Anything).Return(tt.args.rErr) _ = 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 { t.Error(err) } }) } } func Test_pndImplementation_ChangeOND(t *testing.T) { type fields struct { } type args struct { uuid uuid.UUID operation interface{} path string value []string } tests := []struct { name string fields fields args args wantErr bool }{ { name: "update", fields: fields{}, args: args{ uuid: mdid, operation: TransportUpdate, path: "/system/config/hostname", value: []string{"ceos3000"}, }, wantErr: false, }, { name: "replace", fields: fields{}, args: args{ uuid: mdid, operation: TransportReplace, path: "/system/config/hostname", value: []string{"ceos3000"}, }, wantErr: false, }, { name: "delete", fields: fields{}, args: args{ uuid: mdid, operation: TransportDelete, path: "/system/config/hostname", }, wantErr: false, }, { name: "delete w/args", fields: fields{}, args: args{ uuid: mdid, operation: TransportDelete, path: "/system/config/hostname", value: []string{"ceos3000"}, }, wantErr: false, }, // Negative test cases { name: "invalid operation", fields: fields{}, args: args{ uuid: mdid, operation: "INVALID", }, wantErr: true, }, { name: "invalid arg count", fields: fields{}, args: args{ uuid: mdid, operation: TransportUpdate, path: "/system/config/hostname", value: []string{"ceos3000", "ceos3001"}, }, wantErr: true, }, { name: "invalid arg count - update, no args", fields: fields{}, args: args{ uuid: mdid, operation: TransportUpdate, path: "/system/config/hostname", }, wantErr: true, }, { name: "invalid arg count - replace, no args", fields: fields{}, args: args{ uuid: mdid, operation: TransportUpdate, path: "/system/config/hostname", }, wantErr: true, }, { name: "device not found", fields: fields{}, args: args{ uuid: did, operation: TransportUpdate, }, wantErr: true, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { p := newPnd() d := mockDevice() if err := p.AddDevice(&d); err != nil { t.Error(err) return } if err := p.ChangeOND(tt.args.uuid, tt.args.operation, tt.args.path, tt.args.value...); (err != nil) != tt.wantErr { t.Errorf("ChangeOND() error = %v, wantErr %v", err, tt.wantErr) return } if !tt.wantErr { if len(p.pendingChanges.store) != 1 { t.Errorf("ChangeOND() unexpected change count. got %v, want 1", len(p.pendingChanges.store)) } } }) } } func Test_pndImplementation_GetDevice(t *testing.T) { p := newPnd() d, err := NewDevice(sbi, &GnmiTransportOptions{}) if err != nil { t.Error(err) return } if err = p.addDevice(d); err != nil { 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) { got, err := p.GetDevice(tt.args.uuid) if (err != nil) != tt.wantErr { t.Errorf("GetDevice() error = %v, wantErr %v", err, tt.wantErr) return } if !reflect.DeepEqual(got, tt.want) { t.Errorf("GetDevice() got = %v, want %v", got, tt.want) } }) } } func Test_pndImplementation_Confirm(t *testing.T) { tests := []struct { name string wantErr bool }{ { name: "default", wantErr: false, }, { name: "uncommitted", wantErr: true, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { pnd := newPnd() d := mockDevice() tr := d.Transport.(*mocks.Transport) tr.On("Set", mockContext, mock.Anything, mock.Anything).Return(nil) if err := pnd.addDevice(&d); err != nil { t.Error(err) return } if err := pnd.ChangeOND(d.ID(), TransportUpdate, "system/config/hostname", "ceos3000"); err != nil { t.Error(err) return } u := pnd.Pending()[0] 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) } }) } }