Skip to content
Snippets Groups Projects
Code owners
Assign users and groups as approvers for specific file changes. Learn more.
nucleusIntegration_test.go 8.35 KiB
package integration

import (
	"code.fbi.h-da.de/cocsn/gosdn/forks/goarista/gnmi"
	"code.fbi.h-da.de/cocsn/gosdn/nucleus"
	"context"
	gpb "github.com/openconfig/gnmi/proto/gnmi"
	pb "google.golang.org/protobuf/proto"
	"reflect"
	"sort"
	"testing"
	"time"
)

var gnmiMessages map[string]pb.Message

func TestGnmi_SetIntegration(t *testing.T) {
	if testing.Short() {
		t.Skip("skipping integration test")
	}
	type fields struct {
		opt *nucleus.GnmiTransportOptions
	}
	type args struct {
		ctx    context.Context
		params []interface{}
	}
	tests := []struct {
		name    string
		fields  fields
		args    args
		want    interface{}
		wantErr bool
	}{
		{
			name: "destination unreachable",
			fields: fields{opt: &nucleus.GnmiTransportOptions{
				Config: gnmi.Config{
					Addr: "203.0.113.10:6030",
				},
			},
			},
			args: args{
				ctx:    context.Background(),
				params: []interface{}{&gnmi.Operation{}},
			},
			want:    nil,
			wantErr: true,
		},
		{
			name:   "valid update",
			fields: fields{opt: opt},
			args: args{
				ctx: context.Background(),
				params: []interface{}{
					&gnmi.Operation{
						Type:   "update",
						Origin: "",
						Target: "",
						Path: []string{
							"system",
							"config",
							"hostname",
						},
						Val: "ceos3000",
					},
				},
			},
			want:    gnmiMessages["../proto/resp-set-system-config-hostname"],
			wantErr: false,
		},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			g, err := nucleus.NewGnmiTransport(tt.fields.opt)
			if err != nil {
				t.Errorf("NewGnmiTransport() error = %v, wantErr %v", err, tt.wantErr)
				return
			}
			resp, err := g.Set(tt.args.ctx, tt.args.params...)
			if (err != nil) != tt.wantErr {
				t.Errorf("Set() error = %v, wantErr %v", err, tt.wantErr)
				return
			}
			got, ok := resp.(*gpb.SetResponse)
			if !ok {
				t.Errorf("want: %v, got %v, error: %v", reflect.TypeOf(&gpb.SetResponse{}), reflect.TypeOf(resp), &nucleus.ErrInvalidTypeAssertion{})
			}
			if err != nil && tt.wantErr {
				return
			} else if got.Prefix.Target != testAddress ||
				got.Response[0].Op != gpb.UpdateResult_UPDATE {
				t.Errorf("Set() got = %v, want %v", got, tt.want)
			}
		})
	}
}

func TestGnmi_GetIntegration(t *testing.T) {
	if testing.Short() {
		t.Skip("skipping integration test")
	}

	paths := []string{
		"/interfaces/interface",
		"system/config/hostname",
	}
	type fields struct {
		opt *nucleus.GnmiTransportOptions
	}
	type args struct {
		ctx    context.Context
		params []string
	}
	tests := []struct {
		name    string
		fields  fields
		args    args
		want    interface{}
		wantErr bool
	}{
		{
			name:   "default",
			fields: fields{opt: opt},
			args: args{
				ctx:    context.Background(),
				params: paths[:1],
			},
			want:    gnmiMessages["../proto/resp-interfaces-arista-ceos"],
			wantErr: false,
		},
		{
			name: "destination unreachable",
			fields: fields{opt: &nucleus.GnmiTransportOptions{
				Config: gnmi.Config{
					Addr: "203.0.113.10:6030",
				},
			},
			},
			args: args{
				ctx:    context.Background(),
				params: paths,
			},
			want:    nil,
			wantErr: true,
		},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			g, err := nucleus.NewGnmiTransport(tt.fields.opt)
			if err != nil {
				t.Error(err)
				return
			}
			got, err := g.Get(tt.args.ctx, tt.args.params...)
			if (err != nil) != tt.wantErr {
				t.Errorf("Get() error = %v, wantErr %v", err, tt.wantErr)
				return
			}
			if reflect.TypeOf(got) != reflect.TypeOf(tt.want) {
				t.Errorf("Get() got = %v, want %v", got, tt.want)
			}
		})
	}
}
func TestGnmi_SubscribeIntegration(t *testing.T) {
	if testing.Short() {
		t.Skip("skipping integration test")
	}

	type fields struct {
		opt *nucleus.GnmiTransportOptions
	}
	type args struct {
		ctx  context.Context
		opts *gnmi.SubscribeOptions
	}
	tests := []struct {
		name    string
		fields  fields
		args    args
		wantErr bool
	}{
		{
			name: "default",
			fields: fields{
				opt: &nucleus.GnmiTransportOptions{
					Config:   opt.Config,
					RespChan: make(chan *gpb.SubscribeResponse),
				},
			},
			args: args{
				ctx: context.Background(),
				opts: &gnmi.SubscribeOptions{
					Mode:              "stream",
					StreamMode:        "sample",
					SampleInterval:    uint64(1 * time.Second),
					HeartbeatInterval: uint64(100 * time.Millisecond),
					Paths: gnmi.SplitPaths([]string{
						"/interfaces/interface/name",
						"/system/config/hostname",
					}),
					Target: testAddress,
				},
			},
			wantErr: false,
		},
		{
			name: "wrong path",
			fields: fields{
				opt: &nucleus.GnmiTransportOptions{
					Config:   opt.Config,
					RespChan: make(chan *gpb.SubscribeResponse),
				},
			},
			args: args{
				opts: &gnmi.SubscribeOptions{
					Mode:              "stream",
					StreamMode:        "sample",
					SampleInterval:    uint64(1 * time.Second),
					HeartbeatInterval: uint64(100 * time.Millisecond),
					Paths: gnmi.SplitPaths([]string{
						"interfaces/interface/name",
						"ystem/config/hostname",
					}),
					Target: testAddress,
				},
			},
			wantErr: true,
		},
		{
			name: "destination unreachable",
			fields: fields{
				opt: &nucleus.GnmiTransportOptions{
					Config: gnmi.Config{
						Addr: "203.0.113.10:6030",
					},
					RespChan: make(chan *gpb.SubscribeResponse),
				},
			},
			args: args{
				opts: &gnmi.SubscribeOptions{},
			},
			wantErr: false,
		},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			var wantErr = tt.wantErr
			g, err := nucleus.NewGnmiTransport(tt.fields.opt)
			if err != nil {
				t.Error(err)
				return
			}
			ctx := context.WithValue(context.Background(), nucleus.CtxKeyOpts, tt.args.opts) //nolint
			ctx, cancel := context.WithCancel(ctx)
			go func() {
				subErr := g.Subscribe(ctx)
				if (subErr != nil) != wantErr {
					if !wantErr && subErr != nil {
						if subErr.Error() != "rpc error: code = Canceled desc = context canceled" {
							t.Errorf("Subscribe() error = %v, wantErr %v", err, tt.wantErr)
						}
					}
				}
			}()
			time.Sleep(time.Second * 3)
			cancel()
			time.Sleep(time.Second * 1)
		})
	}
}

func TestGnmi_CapabilitiesIntegration(t *testing.T) {
	if testing.Short() {
		t.Skip("skipping integration test")
	}
	type fields struct {
		opt *nucleus.GnmiTransportOptions
	}
	type args struct {
		ctx context.Context
	}
	tests := []struct {
		name    string
		fields  fields
		args    args
		want    interface{}
		wantErr bool
	}{
		{
			name:    "supported models",
			fields:  fields{opt: opt},
			args:    args{ctx: context.Background()},
			want:    gnmiMessages["../proto/cap-resp-arista-ceos"].(*gpb.CapabilityResponse).SupportedModels,
			wantErr: false,
		},
		{
			name:    "supported encodings",
			fields:  fields{opt: opt},
			args:    args{ctx: context.Background()},
			want:    gnmiMessages["../proto/cap-resp-arista-ceos"].(*gpb.CapabilityResponse).SupportedEncodings,
			wantErr: false,
		},
		{
			name:    "gnmi version",
			fields:  fields{opt: opt},
			args:    args{ctx: context.Background()},
			want:    gnmiMessages["../proto/cap-resp-arista-ceos"].(*gpb.CapabilityResponse).GNMIVersion,
			wantErr: false,
		},
		{
			name: "destination unreachable",
			fields: fields{opt: &nucleus.GnmiTransportOptions{
				Config: gnmi.Config{
					Addr: "203.0.113.10:6030",
				},
			},
			},
			args:    args{ctx: context.Background()},
			want:    nil,
			wantErr: true,
		},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			g, err := nucleus.NewGnmiTransport(tt.fields.opt)
			if err != nil {
				t.Error(err)
				return
			}
			resp, err := g.Capabilities(tt.args.ctx)
			if (err != nil) != tt.wantErr {
				t.Errorf("Capabilities() error = %v, wantErr %v", err, tt.wantErr)
				return
			}
			var got interface{}
			switch tt.name {
			case "supported encodings":
				got = resp.(*gpb.CapabilityResponse).SupportedEncodings
			case "supported models":
				t.Skip("test causes false negative")
				got = resp.(*gpb.CapabilityResponse).SupportedModels
				sort.Slice(got.([]*gpb.ModelData), func(i, j int) bool {
					return got.([]*gpb.ModelData)[i].Name < got.([]*gpb.ModelData)[j].Name
				})
				sort.Slice(tt.want.([]*gpb.ModelData), func(i, j int) bool {
					return tt.want.([]*gpb.ModelData)[i].Name < tt.want.([]*gpb.ModelData)[j].Name
				})
			case "gnmi version":
				got = resp.(*gpb.CapabilityResponse).GNMIVersion
			default:
			}
			if !reflect.DeepEqual(got, tt.want) {
				t.Errorf("Type() = %v, want %v", got, tt.want)
			}
		})
	}
}