Newer
Older
"code.fbi.h-da.de/danet/gosdn/controller/interfaces/change"
"code.fbi.h-da.de/danet/gosdn/controller/nucleus/types"
mnepb "code.fbi.h-da.de/danet/gosdn/api/go/gosdn/networkelement"
tpb "code.fbi.h-da.de/danet/gosdn/api/go/gosdn/transport"
"code.fbi.h-da.de/danet/gosdn/controller/mocks"
"code.fbi.h-da.de/danet/gosdn/forks/goarista/gnmi"
openconfig "code.fbi.h-da.de/danet/gosdn/models/generated/openconfig"
pluginSDK "code.fbi.h-da.de/danet/gosdn/plugins/sdk"
"github.com/openconfig/ygot/ygot"
"github.com/stretchr/testify/mock"
// testSetupGnmi bootstraps tests for gnmi transport.
func testSetupGnmi() {
// TODO: Set sane defaults
gnmiConfig = &gnmi.Config{
Username: "test",
Password: "test",
Addr: "localhost:13371",
Encoding: gpb.Encoding_PROTO,
}
startGnmiTarget = make(chan string)
stopGnmiTarget = make(chan bool)
go targetRunner()
}
func TestGnmi_Capabilities(t *testing.T) {
transport := mockTransport(t)
capabilityResponse := &gpb.CapabilityResponse{
SupportedModels: nil,
SupportedEncodings: []gpb.Encoding{gpb.Encoding_PROTO, gpb.Encoding_JSON_IETF, gpb.Encoding_JSON},
GNMIVersion: "0.6.0",
Extension: nil,
}
capabilityRequest := &gpb.CapabilityRequest{}
transport.client.(*mocks.GNMIClient).
On("NewContext", mockContext, mock.Anything).
Return(mockContext)
transport.client.(*mocks.GNMIClient).
On("Capabilities", mockContext, capabilityRequest).
Return(capabilityResponse, nil)
}
tests := []struct {
name string
fields fields
args args
want *gpb.CapabilityResponse
wantErr bool
}{
{
fields: fields{transport: &transport},
endpoint: gnmiConfig.Addr,
want: capabilityResponse,
wantErr: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if tt.args.runEndpoint {
startGnmiTarget <- tt.args.endpoint
}
got, err := tt.fields.transport.Capabilities(context.Background())
if (err != nil) != tt.wantErr {
t.Errorf("Capabilities() error = %v, wantErr %v", err, tt.wantErr)
return
}
if !reflect.DeepEqual(got, tt.want) {
t.Errorf("Get() got = %v, want %v", got, tt.want)
if tt.args.runEndpoint {
stopGnmiTarget <- true
}
})
}
}
func TestGnmi_Close(t *testing.T) {
type fields struct {
SetNode func(path *gpb.Path, value *gpb.TypedValue) error
RespChan chan *gpb.SubscribeResponse
}
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) {
g := &Gnmi{
SetNode: tt.fields.SetNode,
RespChan: tt.fields.RespChan,
}
if err := g.Close(); (err != nil) != tt.wantErr {
t.Errorf("Close() error = %v, wantErr %v", err, tt.wantErr)
}
})
}
}
func TestGnmi_Get(t *testing.T) {
transport := mockTransport(t)
getRequest := &gpb.GetRequest{
Prefix: nil,
Path: nil,
Type: 0,
Encoding: 0,
UseModels: nil,
Extension: nil,
}
getResponse := &gpb.GetResponse{
Notification: nil,
Error: nil,
Extension: nil,
}
transport.client.(*mocks.GNMIClient).
On("NewContext", mockContext, mock.Anything).
Return(mockContext)
transport.client.(*mocks.GNMIClient).
On("NewGetRequest", mockContext, mock.Anything, mock.Anything).
Return(getRequest, nil)
transport.client.(*mocks.GNMIClient).
On("Get", mockContext, mock.Anything).
Return(getResponse, nil)
}
tests := []struct {
name string
fields fields
args args
want interface{}
wantErr bool
}{
fields: fields{&Gnmi{}},
args: args{
params: nil,
},
fields: fields{transport: &transport},
args: args{},
want: getResponse,
fields: fields{transport: &transport},
args: args{
runEndpoint: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if tt.args.runEndpoint {
got, err := tt.fields.transport.Get(context.Background(), tt.args.params...)
if (err != nil) != tt.wantErr {
t.Errorf("Get() error = %v, wantErr %v", err, tt.wantErr)
return
}
if !reflect.DeepEqual(got, tt.want) {
t.Errorf("Get() got = %v, want %v", got, tt.want)
}
if tt.args.runEndpoint {
stopGnmiTarget <- true
}
})
}
}
func TestGnmi_ProcessResponse(t *testing.T) {
type args struct {
}
tests := []struct {
name string
args args
wantErr bool
}{
name: "Interfaces Interface",
path: "../test/proto/resp-interfaces-interface-arista-ceos",
Malte Bauch
committed
wantErr: false,
name: "Interfaces Wildcard",
path: "../test/proto/resp-interfaces-wildcard",
},
wantErr: false,
},
{
name: "Root",
path: "../test/proto/resp-full-node-arista-ceos",
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
networkElementModel, err := pluginSDK.NewDeviceModel(openconfig.Schema, openconfig.Unmarshal, openconfig.SchemaTreeGzip)
if err != nil {
t.Errorf("ProcessResponse() error = %v", err)
}
g := &Gnmi{
SetNode: networkElementModel.SetNode,
Unmarshal: networkElementModel.Unmarshal,
}
if err := g.ProcessResponse(resp); (err != nil) != tt.wantErr {
t.Errorf("ProcessResponse() error = %v, wantErr %v", err, tt.wantErr)
}
})
}
}
func TestGnmi_Set(t *testing.T) {
hostnamePath, err := ygot.StringToStructuredPath("system/config/hostname")
if err != nil {
t.Fatal(err)
}
setResponse := &gpb.SetResponse{}
transport Gnmi
mockArgumentMatcher any
path string
ctx context.Context
}
tests := []struct {
name string
fields fields
args args
wantErr bool
}{
{
name: "updateValue",
fields: fields{
transport: mockTransport(t),
mockArgumentMatcher: mock.MatchedBy(func(input *gpb.SetRequest) bool {
if len(input.Update) == 0 {
return false
}
path, err := ygot.PathToString(input.Update[0].Path)
if err != nil {
return false
}
value := input.Update[0].Val.GetStringVal()
valCheck := value == "newName"
pathCheck := path == "/system/config/hostname"
return valCheck && pathCheck
}),
},
args: args{
payload: change.Payload{
Original: []byte("update"),
Modified: []byte("update2"),
Diff: &gpb.Notification{
Update: []*gpb.Update{
{
Path: hostnamePath,
Val: &gpb.TypedValue{
Value: &gpb.TypedValue_StringVal{
StringVal: "newName",
},
},
},
},
},
},
path: "/system/config/hostname",
ctx: context.WithValue(context.Background(), types.CtxKeyOperation, mnepb.ApiOperation_API_OPERATION_UPDATE), // nolint
},
wantErr: false,
},
{
name: "removeValue",
fields: fields{
transport: mockTransport(t),
mockArgumentMatcher: mock.MatchedBy(func(input *gpb.SetRequest) bool {
if len(input.Delete) == 0 {
return false
}
test, _ := ygot.PathToString(input.Delete[0])
return test == "/system/config/hostname"
}),
},
args: args{
payload: change.Payload{
Original: []byte("delete"),
Modified: []byte("delete2"),
Diff: &gpb.Notification{
Delete: []*gpb.Path{
hostnamePath,
},
},
},
path: "/system/config/hostname",
ctx: context.WithValue(context.Background(), types.CtxKeyOperation, mnepb.ApiOperation_API_OPERATION_DELETE), // nolint
},
wantErr: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
tt.fields.transport.client.(*mocks.GNMIClient).
On("Set", mockContext, tt.fields.mockArgumentMatcher).Return(setResponse, nil)
err := tt.fields.transport.Set(tt.args.ctx, tt.args.payload)
if (err != nil) != tt.wantErr {
t.Errorf("Set() error = %v, wantErr %v", err, tt.wantErr)
}
})
}
}
func TestGnmi_Subscribe(t *testing.T) {
type args struct {
ctx context.Context
params []string
}
tests := []struct {
name string
args args
wantErr bool
}{
{name: "nil client", args: args{}, wantErr: true},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if err := g.Subscribe(tt.args.ctx, tt.args.params...); (err != nil) != tt.wantErr {
t.Errorf("Subscribe() error = %v, wantErr %v", err, tt.wantErr)
}
})
}
}
func TestGnmi_Type(t *testing.T) {
tests := []struct {
name string
want string
{name: "dummy", want: "gnmi"},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
g := &Gnmi{}
if got := g.Type(); got != tt.want {
t.Errorf("Type() = %v, want %v", got, tt.want)
}
})
}
}
//nolint:errcheck
func TestGnmi_getWithRequest(t *testing.T) {
transport := mockTransport(t)
reqFullNode := gnmiMessages["../test/proto/req-full-node"].(*gpb.GetRequest)
reqInterfacesWildcard := gnmiMessages["../test/proto/req-interfaces-wildcard"].(*gpb.GetRequest)
respFullNode := gnmiMessages["../test/proto/resp-full-node"].(*gpb.GetResponse)
respInterfacesWildcard := gnmiMessages["../test/proto/resp-interfaces-wildcard"].(*gpb.GetResponse)
transport.client.(*mocks.GNMIClient).
On("Get", mockContext, reqFullNode).
Return(respFullNode, nil)
transport.client.(*mocks.GNMIClient).
On("Get", mockContext, reqInterfacesWildcard).
Return(respInterfacesWildcard, nil)
transport.client.(*mocks.GNMIClient).
On("Get", mockContext, mock.Anything).
Return(nil, errors.New("expected mock gnmi error"))
}
tests := []struct {
name string
fields fields
args args
want interface{}
wantErr bool
}{
fields: fields{transport: &transport},
name: "getInterfacesWildcard",
fields: fields{transport: &transport},
args: args{
request: reqInterfacesWildcard,
},
{
name: "invalid request",
fields: fields{transport: &transport},
args: args{
request: nil,
runEndpoint: false,
},
want: nil,
wantErr: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := tt.fields.transport.getWithRequest(context.Background(), tt.args.request)
if (err != nil) != tt.wantErr {
t.Errorf("getWithRequest() error = %v, wantErr %v", err, tt.wantErr)
return
}
if !reflect.DeepEqual(got, tt.want) {
t.Errorf("getWithRequest() got = %v, want %v", got, tt.want)
}
})
}
}
func TestNewGnmiTransport(t *testing.T) {
type args struct {
}
tests := []struct {
name string
args args
want *Gnmi
wantErr bool
}{
{
name: "unsupported compression",
args: args{
opts: &tpb.TransportOption{
TransportOption: &tpb.TransportOption_GnmiTransportOption{
GnmiTransportOption: &tpb.GnmiTransportOption{
Compression: "brotli",
},
}}},
want: nil,
wantErr: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if tt.name == "default" {
startGnmiTarget <- gnmiConfig.Addr
}
mockPlugin := mockPlugin(t)
got, err := newGnmiTransport(tt.args.opts, mockPlugin)
if (err != nil) != tt.wantErr {
t.Errorf("NewGnmiTransport() error = %v, wantErr %v", err, tt.wantErr)
return
}
if tt.name == "default" && got != nil {
tt.want.client = got.client
tt.want.config = got.config
tt.want.SetNode = got.SetNode
tt.want.RespChan = got.RespChan
t.Errorf("NewGnmiTransport() got = %#v, want %#v", got, tt.want)
}
if tt.name == "default" {
stopGnmiTarget <- true
}
})
}