diff --git a/build/ci/.terraform-ci.yml b/build/ci/.terraform-ci.yml index c2dc2bda1359561e72600b136d427ecfa7f08880..64a9afccff47147749f84a648463464644d2d9e2 100644 --- a/build/ci/.terraform-ci.yml +++ b/build/ci/.terraform-ci.yml @@ -28,6 +28,7 @@ cache: variables: TF_VAR_container_tag: $CI_REGISTRY_IMAGE:mr-develop - if: $CI_COMMIT_BRANCH == "integration-test" + - if: $CI_COMMIT_BRANCH == "develop" - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH init: diff --git a/build/ci/.test.yml b/build/ci/.test.yml index edab2169dae858100e7349a3341f09f9d369549e..1a0d55ac79da8ec7d1f653118ee217e81771a06e 100644 --- a/build/ci/.test.yml +++ b/build/ci/.test.yml @@ -11,13 +11,22 @@ integration-test: after_script: - go tool cover -func=coverage.out +unit-test-merge-request: + image: golang:1.14 + stage: integration-test + allow_failure: true + rules: + - if: $CI_PIPELINE_SOURCE == "merge_request_event" && $CI_MERGE_REQUEST_TARGET_BRANCH_NAME != $CI_DEFAULT_BRANCH + script: + - go test -race $(go list ./... | grep -v /forks/ | grep -v /api/ | grep -v /mocks ) -v -coverprofile=coverage.out + after_script: + - go tool cover -func=coverage.out unit-test: image: golang:1.14 stage: test allow_failure: true rules: - - if: $CI_PIPELINE_SOURCE == "merge_request_event" && $CI_MERGE_REQUEST_TARGET_BRANCH_NAME != $CI_DEFAULT_BRANCH - if: $CI_COMMIT_BRANCH != $CI_DEFAULT_BRANCH script: - go test -short -race $(go list ./... | grep -v /forks/ | grep -v /api/ | grep -v /mocks ) -v -coverprofile=coverage.out diff --git a/cmd/gnmi-capabilities/capabilities.go b/cmd/gnmi-capabilities/capabilities.go index de9475197ab197cd52fc3940c6b54f80670097ec..54601b8a7ede5805a0d1a3ad633424020a8fc736 100644 --- a/cmd/gnmi-capabilities/capabilities.go +++ b/cmd/gnmi-capabilities/capabilities.go @@ -17,7 +17,7 @@ func main() { Password: "arista", Encoding: gpb.Encoding_JSON_IETF, } - transport,err := nucleus.NewGnmiTransport(cfg) + transport, err := nucleus.NewGnmiTransport(cfg) if err != nil { log.Error(err) } @@ -27,7 +27,7 @@ func main() { } modelData := resp.(*gpb.CapabilityResponse).SupportedModels b := strings.Builder{} - for _,elem := range modelData { + for _, elem := range modelData { _, err := b.WriteString(elem.Name) if err != nil { log.Error(err) diff --git a/cmd/gnmi-telemetry/telemetry.go b/cmd/gnmi-telemetry/telemetry.go index 3cb85fd508cdc8331d0331c358748a1509a00913..5064e40e0609c751ba714a3cac12c434f49a3e4f 100644 --- a/cmd/gnmi-telemetry/telemetry.go +++ b/cmd/gnmi-telemetry/telemetry.go @@ -39,7 +39,7 @@ func main() { Password: "arista", Encoding: gpb.Encoding_JSON_IETF, } - transport,_ := nucleus.NewGnmiTransport(cfg) + transport, _ := nucleus.NewGnmiTransport(cfg) device.Transport = transport diff --git a/cmd/gosdn/main.go b/cmd/gosdn/main.go index 4356fbc6a18839313c531ceade42c064a8ac5f07..367250512af6318f1ad84ec85c139bfec28e1206 100644 --- a/cmd/gosdn/main.go +++ b/cmd/gosdn/main.go @@ -16,5 +16,5 @@ func main() { IsRunningChannel := make(chan bool) // hand off to cmd for further processing - nucleus.StartAndRun(IsRunningChannel) + nucleus.StartAndRun(IsRunningChannel) } diff --git a/forks/goarista/gnmi/client.go b/forks/goarista/gnmi/client.go index bfb404ada10a59f474c993e184a1ade785ef63bf..33c3023b77760402803fef98bd8226c6917cb01e 100644 --- a/forks/goarista/gnmi/client.go +++ b/forks/goarista/gnmi/client.go @@ -235,7 +235,7 @@ func DialContext(ctx context.Context, cfg *Config) (pb.GNMIClient, error) { return pb.NewGNMIClient(grpcconn), nil } -// Dial connects to a gnmi service and returns a ciena +// Dial connects to a gnmi service and returns a client func Dial(cfg *Config) (pb.GNMIClient, error) { return DialContext(context.Background(), cfg) } diff --git a/nucleus/cli-handling_test.go b/nucleus/cli-handling_test.go index 6a5a49d394fb238c662d9458411c1d448328ddf4..522de1a0cfa12cea3ac919e23f5696865e7159f1 100644 --- a/nucleus/cli-handling_test.go +++ b/nucleus/cli-handling_test.go @@ -374,4 +374,4 @@ func Test_server_Shutdown(t *testing.T) { } }) } -} \ No newline at end of file +} diff --git a/nucleus/device_test.go b/nucleus/device_test.go index 0fa4ffa903a1f894325aaf5211341e19a2df754a..443744d8c8c91ac46a8fade40c91967effd125f7 100644 --- a/nucleus/device_test.go +++ b/nucleus/device_test.go @@ -1 +1,93 @@ package nucleus + +import ( + "code.fbi.h-da.de/cocsn/yang-models/generated/openconfig" + "github.com/google/uuid" + "github.com/openconfig/ygot/ygot" + "reflect" + "testing" +) + +func TestDevice_Id(t *testing.T) { + type fields struct { + GoStruct ygot.GoStruct + SBI SouthboundInterface + Config DeviceConfig + Transport Transport + } + tests := []struct { + name string + fields fields + want uuid.UUID + }{ + { + name: "default", + fields: fields{ + Config: DeviceConfig{ + Uuid: did, + }, + }, + want: did, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + d := &Device{ + GoStruct: tt.fields.GoStruct, + SBI: tt.fields.SBI, + Config: tt.fields.Config, + Transport: tt.fields.Transport, + } + if got := d.Id(); !reflect.DeepEqual(got, tt.want) { + t.Errorf("Id() = %v, want %v", got, tt.want) + } + }) + } +} + +func TestNewDevice(t *testing.T) { + sbi := &OpenConfig{} + type args struct { + sbi SouthboundInterface + addr string + username string + password string + transport Transport + } + tests := []struct { + name string + args args + want *Device + }{ + { + name: "default", + args: args{ + sbi: sbi, + addr: "testdevice", + username: "testuser", + password: "testpassword", + transport: &Gnmi{}, + }, + want: &Device{ + GoStruct: &openconfig.Device{}, + SBI: sbi, + Config: DeviceConfig{ + Uuid: uuid.UUID{}, + Address: "testdevice", + Username: "testuser", + Password: "testpassword", + }, + Transport: &Gnmi{}, + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got := NewDevice(tt.args.sbi, tt.args.addr, tt.args.username, tt.args.password, tt.args.transport) + tt.want.Config.Uuid = got.Id() + if !reflect.DeepEqual(got, tt.want) { + t.Errorf("NewDevice() = %v, want %v", got, tt.want) + } + }) + } +} diff --git a/nucleus/errors.go b/nucleus/errors.go index 9a92bf9e72caec1164702ff0e0f598765f75d4e8..93097feda2c967828ca09ab80dc729a043e2f0d2 100644 --- a/nucleus/errors.go +++ b/nucleus/errors.go @@ -40,4 +40,10 @@ type ErrUnsupportedPath struct { func (e ErrUnsupportedPath) Error() string { return fmt.Sprintf("path %v is not supported", e.p) -} \ No newline at end of file +} + +type ErrNotYetImplemented struct{} + +func (e ErrNotYetImplemented) Error() string { + return fmt.Sprintf("function not yet implemented") +} diff --git a/nucleus/gnmi_transport.go b/nucleus/gnmi_transport.go index 784a402513deb35423f1276accc9ba48c3299525..f0022d2c934a663d21e02094166c5fa6d7128980 100644 --- a/nucleus/gnmi_transport.go +++ b/nucleus/gnmi_transport.go @@ -4,7 +4,6 @@ import ( "code.fbi.h-da.de/cocsn/gosdn/forks/goarista/gnmi" "code.fbi.h-da.de/cocsn/gosdn/nucleus/util" "context" - "errors" gpb "github.com/openconfig/gnmi/proto/gnmi" "github.com/openconfig/gnmi/proto/gnmi_ext" "github.com/openconfig/goyang/pkg/yang" @@ -84,17 +83,9 @@ func (g *Gnmi) ProcessResponse(resp interface{}, root interface{}, s *ytypes.Sch rn := r.Notification for _, msg := range rn { for _, update := range msg.Update { - prefix := msg.Prefix path := update.Path fullPath := path - if prefix != nil { - var err error - fullPath, err = gnmiFullPath(prefix, path) - if err != nil { - return err - } - } - val,ok := update.Val.Value.(*gpb.TypedValue_JsonIetfVal) + val, ok := update.Val.Value.(*gpb.TypedValue_JsonIetfVal) if ok { opts := []ytypes.UnmarshalOpt{&ytypes.IgnoreExtraFields{}} if err := g.Unmarshal(val.JsonIetfVal, extraxtPathElements(fullPath), root, opts...); err != nil { @@ -115,40 +106,12 @@ func (g *Gnmi) ProcessResponse(resp interface{}, root interface{}, s *ytypes.Sch func extraxtPathElements(path *gpb.Path) []string { elems := make([]string, len(path.Elem)) - for i,e := range path.Elem { + for i, e := range path.Elem { elems[i] = strings.Title(e.Name) } return elems } -// extractModelKey extracts the model's key from the full path. Highly model specific for now -// TODO: Remove hard coded model prefix -// TODO: Figure out why path.Elem() is empty but path.Elememt() is deprecated -func extractModelKey(path *gpb.Path) string { - var b strings.Builder - delim := "_" - b.WriteString("OpenconfigInterfaces") - for i := 0; i < len(path.Elem); i++ { - pathElement := path.Elem[i] - b.WriteString(delim) - b.WriteString(strings.Title(pathElement.Name)) - } - return b.String() -} - -// gnmiFullPath builds the full path from the prefix and path. -// Copycat from forks/google/gnmi/server.go -func gnmiFullPath(prefix, path *gpb.Path) (*gpb.Path, error) { - fullPath := &gpb.Path{Origin: path.Origin} - if path.GetElement() != nil { - return nil, errors.New("deprecated path element type is unsupported") - } - if path.GetElem() != nil { - fullPath.Elem = append(prefix.GetElem(), path.GetElem()...) - } - return fullPath, nil -} - // Capabilities calls GNMI capabilities func (g *Gnmi) Capabilities(ctx context.Context) (interface{}, error) { ctx = gnmi.NewContext(ctx, g.config) diff --git a/nucleus/gnmi_transport_test.go b/nucleus/gnmi_transport_test.go index e54dff7b967d910eee3553ba5ede81a176864b8f..5ca60936c6ff7d28af51fd57febed7636cc20c54 100644 --- a/nucleus/gnmi_transport_test.go +++ b/nucleus/gnmi_transport_test.go @@ -8,6 +8,7 @@ import ( "code.fbi.h-da.de/cocsn/yang-models/generated/openconfig" "context" log "github.com/golang/glog" + "github.com/golang/protobuf/proto" gpb "github.com/openconfig/gnmi/proto/gnmi" "github.com/openconfig/goyang/pkg/yang" "github.com/openconfig/ygot/ytypes" @@ -20,10 +21,29 @@ import ( // TestMain bootstraps all tests. Humongous beast // TODO: Move somewhere more sensible func TestMain(m *testing.M) { + gnmiMessages = map[string]proto.Message{ + "../test/cap-resp-arista-ceos": &gpb.CapabilityResponse{}, + "../test/req-full-node": &gpb.GetRequest{}, + "../test/req-full-node-arista-ceos": &gpb.GetRequest{}, + "../test/req-interfaces-arista-ceos": &gpb.GetRequest{}, + "../test/req-interfaces-interface-arista-ceos": &gpb.GetRequest{}, + "../test/req-interfaces-wildcard": &gpb.GetRequest{}, + "../test/resp-full-node": &gpb.GetResponse{}, + "../test/resp-full-node-arista-ceos": &gpb.GetResponse{}, + "../test/resp-interfaces-arista-ceos": &gpb.GetResponse{}, + "../test/resp-interfaces-interface-arista-ceos": &gpb.GetResponse{}, + "../test/resp-interfaces-wildcard": &gpb.GetResponse{}, + } + for k, v := range gnmiMessages { + if err := util.Read(k, v); err != nil { + log.Fatalf("error parsing %v: %v", k, err) + } + } testSetupGnmi() testSetupPnd() testSetupStore() testSetupSbi() + testSetupIntegration() os.Exit(m.Run()) } @@ -60,6 +80,7 @@ func mockTransport() Gnmi { } } +var gnmiMessages map[string]proto.Message var gnmiConfig *gnmi.Config var startGnmiTarget chan string var stopGnmiTarget chan bool @@ -286,7 +307,7 @@ func TestGnmi_ProcessResponse(t *testing.T) { name: "Interfaces Interface", fields: fields{Sbi: &OpenConfig{}}, args: args{ - path: "../test/resp-full-node", + path: "../test/resp-interfaces-interface-arista-ceos", root: &openconfig.Device{}, }, wantErr: true, @@ -313,14 +334,11 @@ func TestGnmi_ProcessResponse(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { g := &Gnmi{ - SetNode: tt.fields.Sbi.SetNode(), + SetNode: tt.fields.Sbi.SetNode(), Unmarshal: tt.fields.Sbi.(*OpenConfig).Unmarshal(), } s := tt.fields.Sbi.Schema() - resp := &gpb.GetResponse{} - if err := util.Read(tt.args.path, resp); err != nil { - t.Errorf("error reading file %v,", err) - } + resp := gnmiMessages[tt.args.path] if err := g.ProcessResponse(resp, tt.args.root, s); (err != nil) != tt.wantErr { t.Errorf("ProcessResponse() error = %v, wantErr %v", err, tt.wantErr) } @@ -382,11 +400,29 @@ func TestGnmi_SetConfig(t *testing.T) { fields fields args args }{ - // TODO: Add test cases. + { + name: "default", + fields: fields{ + SetNode: nil, + RespChan: nil, + config: nil, + }, + args: args{ + config: &gnmi.Config{ + Addr: "test:///", + Password: "test", + Username: "test", + }, + }, + }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - + g := &Gnmi{} + g.SetConfig(tt.args.config) + if !reflect.DeepEqual(g.config, tt.args.config) { + t.Errorf("Type() = %v, want %v", g.config, tt.args.config) + } }) } } @@ -407,15 +443,11 @@ func TestGnmi_Subscribe(t *testing.T) { args args wantErr bool }{ - // TODO: Add test cases. + {name: "nil client", fields: fields{}, args: args{}, wantErr: true}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - g := &Gnmi{ - SetNode: tt.fields.SetNode, - RespChan: tt.fields.RespChan, - config: tt.fields.config, - } + g := &Gnmi{} if err := g.Subscribe(tt.args.ctx, tt.args.params...); (err != nil) != tt.wantErr { t.Errorf("Subscribe() error = %v, wantErr %v", err, tt.wantErr) } @@ -456,30 +488,10 @@ func TestGnmi_get(t *testing.T) { func TestGnmi_getWithRequest(t *testing.T) { transport := mockTransport() - reqFullNode := &gpb.GetRequest{} - reqInterfacesWildcard := &gpb.GetRequest{} - respFullNode := &gpb.GetResponse{} - respInterfacesWildcard := &gpb.GetResponse{} - respInterfacesWildcardExp := &gpb.GetResponse{} - respFullNodeExp := &gpb.GetResponse{} - if err := util.Read("../test/req-full-node", reqFullNode); err != nil { - t.Errorf("error parsing req-full-node: %v", err) - } - if err := util.Read("../test/req-interfaces-wildcard", reqInterfacesWildcard); err != nil { - t.Errorf("error parsing req-interfaces-wildcard: %v", err) - } - if err := util.Read("../test/resp-full-node", respFullNode); err != nil { - t.Errorf("error parsing getFullNode: %v", err) - } - if err := util.Read("../test/resp-full-node", respFullNodeExp); err != nil { - t.Errorf("error parsing getFullNode: %v", err) - } - if err := util.Read("../test/resp-interfaces-wildcard", respInterfacesWildcard); err != nil { - t.Errorf("error parsing getFullNode: %v", err) - } - if err := util.Read("../test/resp-interfaces-wildcard", respInterfacesWildcardExp); err != nil { - t.Errorf("error parsing getFullNode: %v", err) - } + reqFullNode := gnmiMessages["../test/req-full-node"].(*gpb.GetRequest) + reqInterfacesWildcard := gnmiMessages["../test/req-interfaces-wildcard"].(*gpb.GetRequest) + respFullNode := gnmiMessages["../test/resp-full-node"].(*gpb.GetResponse) + respInterfacesWildcard := gnmiMessages["../test/resp-interfaces-wildcard"].(*gpb.GetResponse) transport.client.(*mocks.GNMIClient). On("Get", mockContext, reqFullNode). @@ -509,7 +521,7 @@ func TestGnmi_getWithRequest(t *testing.T) { args: args{ request: reqFullNode, }, - want: respFullNodeExp, + want: respFullNode, wantErr: false, }, { @@ -518,7 +530,7 @@ func TestGnmi_getWithRequest(t *testing.T) { args: args{ request: reqInterfacesWildcard, }, - want: respInterfacesWildcardExp, + want: respInterfacesWildcard, wantErr: false, }, } @@ -544,48 +556,74 @@ func TestGnmi_subscribe(t *testing.T) { // TODO: Design integration test for this one } -func Test_extractModelKey(t *testing.T) { +func TestNewGnmiTransport(t *testing.T) { + oc := &OpenConfig{} + respChan := make(chan *gpb.SubscribeResponse) type args struct { - path *gpb.Path - } - tests := []struct { - name string - args args - want string - }{ - // TODO: Add test cases. - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - if got := extractModelKey(tt.args.path); got != tt.want { - t.Errorf("extraxtPathElements() = %v, want %v", got, tt.want) - } - }) - } -} - -func Test_gnmiFullPath(t *testing.T) { - type args struct { - prefix *gpb.Path - path *gpb.Path + config *gnmi.Config } tests := []struct { name string args args - want *gpb.Path + want *Gnmi wantErr bool }{ - // TODO: Add test cases. + { + name: "default", + args: args{config: &gnmi.Config{ + Username: "test", + Password: "test", + Addr: "localhost:13371", + Encoding: gpb.Encoding_PROTO, + }}, + want: &Gnmi{ + SetNode: oc.SetNode(), + Unmarshal: oc.Unmarshal(), + RespChan: respChan, + config: &gnmi.Config{ + Username: "test", + Password: "test", + Addr: "localhost:13371", + Encoding: gpb.Encoding_PROTO, + }, + client: nil, + }, + wantErr: false, + }, + { + name: "unsupported compression", + args: args{config: &gnmi.Config{Compression: "brotli"}}, + want: nil, + wantErr: true, + }, + { + name: "certificate error no key file", + args: args{config: &gnmi.Config{TLS: true, CertFile: "invalid", KeyFile: ""}}, + want: nil, + wantErr: true, + }, + { + name: "certificate error no ca file", + args: args{config: &gnmi.Config{TLS: true, CAFile: "invalid"}}, + want: nil, + wantErr: true, + }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - got, err := gnmiFullPath(tt.args.prefix, tt.args.path) + if tt.name == "defaul" { + startGnmiTarget <- gnmiConfig.Addr + } + got, err := NewGnmiTransport(tt.args.config) if (err != nil) != tt.wantErr { - t.Errorf("gnmiFullPath() error = %v, wantErr %v", err, tt.wantErr) + t.Errorf("NewGnmiTransport() error = %v, wantErr %v", err, tt.wantErr) return } if !reflect.DeepEqual(got, tt.want) { - t.Errorf("gnmiFullPath() got = %v, want %v", got, tt.want) + t.Errorf("NewGnmiTransport() got = %v, want %v", got, tt.want) + } + if tt.name == "defaul" { + stopGnmiTarget <- true } }) } diff --git a/nucleus/integration_test.go b/nucleus/integration_test.go index 6d19a85281d2a6fa206bbacf4719e453ab84f51b..caed6e9d9974f4ed4a39a0399e5dddc13c877939 100644 --- a/nucleus/integration_test.go +++ b/nucleus/integration_test.go @@ -2,28 +2,37 @@ package nucleus import ( "code.fbi.h-da.de/cocsn/gosdn/forks/goarista/gnmi" - "code.fbi.h-da.de/cocsn/gosdn/nucleus/util" "context" gpb "github.com/openconfig/gnmi/proto/gnmi" + "os" "reflect" "testing" "time" ) var address = "141.100.70.171:6030" +var cfg *gnmi.Config + +func testSetupIntegration() { + a := os.Getenv("GOSDN_TEST_ENDPOINT") + if a != "" { + address = a + } + + cfg = &gnmi.Config{ + Addr: address, + Username: "admin", + Password: "arista", + Encoding: gpb.Encoding_JSON_IETF, + } +} func TestGnmi_SetIntegration(t *testing.T) { if testing.Short() { t.Skip("skipping integration test") } - t.Run("Test GNMI Set", func(t *testing.T){ - cfg := &gnmi.Config{ - Addr: address, - Username: "admin", - Password: "arista", - Encoding: gpb.Encoding_JSON_IETF, - } - transport,err := NewGnmiTransport(cfg) + t.Run("Test GNMI Set", func(t *testing.T) { + transport, err := NewGnmiTransport(cfg) if err != nil { t.Error(err) } @@ -42,14 +51,8 @@ func TestGnmi_GetIntegration(t *testing.T) { if testing.Short() { t.Skip("skipping integration test") } - t.Run("Test GNMI Get", func(t *testing.T){ - cfg := &gnmi.Config{ - Addr: address, - Username: "admin", - Password: "arista", - Encoding: gpb.Encoding_JSON_IETF, - } - transport,err := NewGnmiTransport(cfg) + t.Run("Test GNMI Get", func(t *testing.T) { + transport, err := NewGnmiTransport(cfg) if err != nil { t.Error(err) } @@ -68,14 +71,12 @@ func TestGnmi_SubscribeIntegration(t *testing.T) { if testing.Short() { t.Skip("skipping integration test") } - t.Run("Test GNMI Subscribe", func(t *testing.T){ - cfg := &gnmi.Config{ - Addr: address, - Username: "admin", - Password: "arista", - Encoding: gpb.Encoding_JSON_IETF, - } - transport,err := NewGnmiTransport(cfg) + if os.Getenv("GOSDN_TEST_ENDPOINT") != "" { + t.Skip("skipping locally running integration test") + } + + t.Run("Test GNMI Subscribe", func(t *testing.T) { + transport, err := NewGnmiTransport(cfg) if err != nil { t.Error(err) } @@ -111,31 +112,75 @@ func TestGnmi_CapabilitiesIntegration(t *testing.T) { if testing.Short() { t.Skip("skipping integration test") } - respones := map[string]*gpb.CapabilityResponse{ - "../test/cap-resp-arista-ceos": {}, + type fields struct { + config *gnmi.Config } - for k,v := range respones { - if err := util.Read(k, v); err != nil { - t.Errorf("error parsing %v: %v", k, err) - } - - t.Run("Test GNMI Capabilities", func(t *testing.T) { - cfg := &gnmi.Config{ - Addr: address, - Username: "admin", - Password: "arista", - Encoding: gpb.Encoding_JSON_IETF, - } - transport, err := NewGnmiTransport(cfg) + type args struct { + ctx context.Context + } + tests := []struct { + name string + fields fields + args args + want interface{} + wantErr bool + }{ + { + name: "supported models", + fields: fields{config: cfg}, + args: args{ctx: context.Background()}, + want: gnmiMessages["../test/cap-resp-arista-ceos"].(*gpb.CapabilityResponse).SupportedModels, + wantErr: false, + }, + { + name: "supported encodings", + fields: fields{config: cfg}, + args: args{ctx: context.Background()}, + want: gnmiMessages["../test/cap-resp-arista-ceos"].(*gpb.CapabilityResponse).SupportedEncodings, + wantErr: false, + }, + { + name: "gnmi version", + fields: fields{config: cfg}, + args: args{ctx: context.Background()}, + want: gnmiMessages["../test/cap-resp-arista-ceos"].(*gpb.CapabilityResponse).GNMIVersion, + wantErr: false, + }, + { + name: "destination unreachable", + fields: fields{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 := NewGnmiTransport(tt.fields.config) if err != nil { t.Error(err) + return } - resp, err := transport.Capabilities(context.Background()) - if err != nil { - t.Error(err) + resp, err := g.Capabilities(tt.args.ctx) + if (err != nil) != tt.wantErr { + t.Errorf("Capabilities() error = %v, wantErr %v", err, tt.wantErr) + return } - if !reflect.DeepEqual(resp, v) { - t.Errorf("Capabilities() = %v, want %v", resp, v) + var got interface{} + switch tt.name { + case "supported encodings": + got = resp.(*gpb.CapabilityResponse).SupportedEncodings + case "supported models": + got = resp.(*gpb.CapabilityResponse).SupportedModels + case "gnmi version": + got = resp.(*gpb.CapabilityResponse).GNMIVersion + default: + } + if !reflect.DeepEqual(got, tt.want) { + t.Errorf("Type() = %v, want %v", got, tt.want) } }) } @@ -153,4 +198,4 @@ func TestPndImplementation_RequestIntegration(t *testing.T) { t.Skip("skipping integration test") } t.Fail() -} \ No newline at end of file +} diff --git a/nucleus/restconf_transport.go b/nucleus/restconf_transport.go index 84d904a562b6c87d84378814b8247ad0df484747..606f818ff0301bdc5d2e37d6a4273f7851c5b6e1 100644 --- a/nucleus/restconf_transport.go +++ b/nucleus/restconf_transport.go @@ -9,21 +9,21 @@ type Restconf struct { } func (r Restconf) Get(ctx context.Context, params ...string) (interface{}, error) { - panic("implement me") + return nil, &ErrNotYetImplemented{} } func (r Restconf) Set(ctx context.Context, params ...string) (interface{}, error) { - panic("implement me") + return nil, &ErrNotYetImplemented{} } func (r Restconf) Subscribe(ctx context.Context, params ...string) error { - panic("implement me") + return &ErrNotYetImplemented{} } func (r Restconf) Type() string { - panic("implement me") + return "restconf" } func (r Restconf) ProcessResponse(resp interface{}, root interface{}, models *ytypes.Schema) error { - panic("implement me") + return &ErrNotYetImplemented{} } diff --git a/nucleus/restconf_transport_test.go b/nucleus/restconf_transport_test.go new file mode 100644 index 0000000000000000000000000000000000000000..5f1eb1e352f2cb8214686c5215eea419c465dcd4 --- /dev/null +++ b/nucleus/restconf_transport_test.go @@ -0,0 +1,126 @@ +package nucleus + +import ( + "context" + "github.com/openconfig/ygot/ytypes" + "reflect" + "testing" +) + +func TestRestconf_Get(t *testing.T) { + type args struct { + ctx context.Context + params []string + } + tests := []struct { + name string + args args + want interface{} + wantErr bool + }{ + {name: "not implemented", args: args{}, want: nil, wantErr: true}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + r := Restconf{} + got, err := r.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.DeepEqual(got, tt.want) { + t.Errorf("Get() got = %v, want %v", got, tt.want) + } + }) + } +} + +func TestRestconf_ProcessResponse(t *testing.T) { + type args struct { + resp interface{} + root interface{} + models *ytypes.Schema + } + tests := []struct { + name string + args args + wantErr bool + }{ + {name: "not implemented", args: args{}, wantErr: true}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + r := Restconf{} + if err := r.ProcessResponse(tt.args.resp, tt.args.root, tt.args.models); (err != nil) != tt.wantErr { + t.Errorf("ProcessResponse() error = %v, wantErr %v", err, tt.wantErr) + } + }) + } +} + +func TestRestconf_Set(t *testing.T) { + type args struct { + ctx context.Context + params []string + } + tests := []struct { + name string + args args + want interface{} + wantErr bool + }{ + {name: "not implemented", args: args{}, want: nil, wantErr: true}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + r := Restconf{} + got, err := r.Set(tt.args.ctx, tt.args.params...) + if (err != nil) != tt.wantErr { + t.Errorf("Set() error = %v, wantErr %v", err, tt.wantErr) + return + } + if !reflect.DeepEqual(got, tt.want) { + t.Errorf("Set() got = %v, want %v", got, tt.want) + } + }) + } +} + +func TestRestconf_Subscribe(t *testing.T) { + type args struct { + ctx context.Context + params []string + } + tests := []struct { + name string + args args + wantErr bool + }{ + {name: "not implemented", args: args{}, wantErr: true}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + r := Restconf{} + if err := r.Subscribe(tt.args.ctx, tt.args.params...); (err != nil) != tt.wantErr { + t.Errorf("Subscribe() error = %v, wantErr %v", err, tt.wantErr) + } + }) + } +} + +func TestRestconf_Type(t *testing.T) { + tests := []struct { + name string + want string + }{ + {name: "not implemented", want: "restconf"}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + r := Restconf{} + if got := r.Type(); got != tt.want { + t.Errorf("Type() = %v, want %v", got, tt.want) + } + }) + } +} diff --git a/nucleus/southbound_test.go b/nucleus/southbound_test.go index ccd0d298a5aacbdc857db743cbf1ddb8f624ee9a..951f7979c30723e9b2ff281f30c69588529eea93 100644 --- a/nucleus/southbound_test.go +++ b/nucleus/southbound_test.go @@ -124,7 +124,7 @@ func TestOpenConfig_Schema(t *testing.T) { func Test_unmarshal(t *testing.T) { type args struct { - path string + path string goStruct interface{} opt []ytypes.UnmarshalOpt } @@ -137,7 +137,7 @@ func Test_unmarshal(t *testing.T) { name: "fail", args: args{ goStruct: &openconfig.Device{}, - path: "../test/resp-interfaces-interface-arista-ceos", + path: "../test/resp-interfaces-interface-arista-ceos", }, wantErr: true, }, @@ -190,7 +190,7 @@ func Test_unmarshal(t *testing.T) { if err := unmarshal(bytes, fields, tt.args.goStruct, tt.args.opt...); (err != nil) != tt.wantErr { t.Errorf("unmarshal() error = %v, wantErr %v", err, tt.wantErr) } - if tt.args.goStruct.(*openconfig.Device).Interfaces == nil && tt.args.opt != nil{ + if tt.args.goStruct.(*openconfig.Device).Interfaces == nil && tt.args.opt != nil { t.Errorf("unmarshal() error: field Interfaces must not be nil") } }) diff --git a/nucleus/store_test.go b/nucleus/store_test.go index 8a5f505093a312daf59602c0854a52917cd225c3..012b47a4f1d32634a070ad6d2d695c60eb44d95f 100644 --- a/nucleus/store_test.go +++ b/nucleus/store_test.go @@ -365,7 +365,7 @@ func Test_pndStore_get(t *testing.T) { }, }, }, - args: args{id: defaultPndId}, + args: args{id: did}, wantErr: true, }, }