Skip to content
Snippets Groups Projects
Commit a320eed3 authored by André Sterba's avatar André Sterba Committed by Martin Stiemerling
Browse files

Add validation tests for topology

See merge request !573
parent 998e79d9
Branches
Tags
1 merge request!573Add validation tests for topology
Pipeline #164686 passed
...@@ -2,6 +2,7 @@ package server ...@@ -2,6 +2,7 @@ package server
import ( import (
"context" "context"
"errors"
"time" "time"
topopb "code.fbi.h-da.de/danet/gosdn/api/go/gosdn/topology" topopb "code.fbi.h-da.de/danet/gosdn/api/go/gosdn/topology"
...@@ -15,6 +16,7 @@ import ( ...@@ -15,6 +16,7 @@ import (
"github.com/google/uuid" "github.com/google/uuid"
"google.golang.org/grpc/codes" "google.golang.org/grpc/codes"
"google.golang.org/grpc/status" "google.golang.org/grpc/status"
"google.golang.org/protobuf/reflect/protoreflect"
) )
// TopologyServer holds a topologyService and represents a TopologyServiceServer. // TopologyServer holds a topologyService and represents a TopologyServiceServer.
...@@ -41,10 +43,29 @@ func NewTopologyServer( ...@@ -41,10 +43,29 @@ func NewTopologyServer(
} }
} }
func (t TopologyServer) checkForValidationErrors(request protoreflect.ProtoMessage) error {
err := t.protoValidator.Validate(request)
if err != nil {
var valErr *protovalidate.ValidationError
if ok := errors.As(err, &valErr); ok {
protoErr := valErr.ToProto()
grpcError, _ := status.New(codes.Aborted, "Validation failed").WithDetails(protoErr)
return grpcError.Err()
}
return status.Errorf(codes.Aborted, "%v", err)
}
return nil
}
// AddLink adds a new link to the topology. // AddLink adds a new link to the topology.
func (t *TopologyServer) AddLink(ctx context.Context, request *topopb.AddLinkRequest) (*topopb.AddLinkResponse, error) { func (t *TopologyServer) AddLink(ctx context.Context, request *topopb.AddLinkRequest) (*topopb.AddLinkResponse, error) {
if err := t.protoValidator.Validate(request); err != nil { err := t.checkForValidationErrors(request)
return nil, status.Errorf(codes.Aborted, "%v", err) if err != nil {
return nil, err
} }
sourceNode, sourcePort, err := t.ensureNodeAndPortExists(request.Link.SourceNode, request.Link.SourcePort) sourceNode, sourcePort, err := t.ensureNodeAndPortExists(request.Link.SourceNode, request.Link.SourcePort)
...@@ -77,8 +98,9 @@ func (t *TopologyServer) AddLink(ctx context.Context, request *topopb.AddLinkReq ...@@ -77,8 +98,9 @@ func (t *TopologyServer) AddLink(ctx context.Context, request *topopb.AddLinkReq
// GetTopology returns the current topology in the form of all links. // GetTopology returns the current topology in the form of all links.
func (t *TopologyServer) GetTopology(ctx context.Context, request *topopb.GetTopologyRequest) (*topopb.GetTopologyResponse, error) { func (t *TopologyServer) GetTopology(ctx context.Context, request *topopb.GetTopologyRequest) (*topopb.GetTopologyResponse, error) {
if err := t.protoValidator.Validate(request); err != nil { err := t.checkForValidationErrors(request)
return nil, status.Errorf(codes.Aborted, "%v", err) if err != nil {
return nil, err
} }
topo, err := t.topologyService.GetAll() topo, err := t.topologyService.GetAll()
...@@ -127,8 +149,9 @@ func (t *TopologyServer) GetTopology(ctx context.Context, request *topopb.GetTop ...@@ -127,8 +149,9 @@ func (t *TopologyServer) GetTopology(ctx context.Context, request *topopb.GetTop
// DeleteLink deletes a link. // DeleteLink deletes a link.
func (t *TopologyServer) DeleteLink(ctx context.Context, request *topopb.DeleteLinkRequest) (*topopb.DeleteLinkResponse, error) { func (t *TopologyServer) DeleteLink(ctx context.Context, request *topopb.DeleteLinkRequest) (*topopb.DeleteLinkResponse, error) {
if err := t.protoValidator.Validate(request); err != nil { err := t.checkForValidationErrors(request)
return nil, status.Errorf(codes.Aborted, "%v", err) if err != nil {
return nil, err
} }
linkID, err := uuid.Parse(request.Id) linkID, err := uuid.Parse(request.Id)
......
...@@ -5,6 +5,7 @@ import ( ...@@ -5,6 +5,7 @@ import (
"reflect" "reflect"
"testing" "testing"
"buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go/buf/validate"
apb "code.fbi.h-da.de/danet/gosdn/api/go/gosdn/topology" apb "code.fbi.h-da.de/danet/gosdn/api/go/gosdn/topology"
eventservice "code.fbi.h-da.de/danet/gosdn/controller/eventService" eventservice "code.fbi.h-da.de/danet/gosdn/controller/eventService"
"code.fbi.h-da.de/danet/gosdn/controller/topology" "code.fbi.h-da.de/danet/gosdn/controller/topology"
...@@ -244,11 +245,12 @@ func TestTopology_AddLink(t *testing.T) { ...@@ -244,11 +245,12 @@ func TestTopology_AddLink(t *testing.T) {
request *apb.AddLinkRequest request *apb.AddLinkRequest
} }
tests := []struct { tests := []struct {
name string name string
fields fields fields fields
args args args args
want *apb.AddLinkResponse want *apb.AddLinkResponse
wantErr bool wantErr bool
validationErrors []*validate.Violation
}{ }{
{ {
name: "should add a new link", name: "should add a new link",
...@@ -281,6 +283,33 @@ func TestTopology_AddLink(t *testing.T) { ...@@ -281,6 +283,33 @@ func TestTopology_AddLink(t *testing.T) {
}, },
want: &apb.AddLinkResponse{}, want: &apb.AddLinkResponse{},
wantErr: true, wantErr: true,
validationErrors: []*validate.Violation{
{
FieldPath: "link.name",
ConstraintId: "string.min_len",
Message: "value length must be at least 1 characters",
},
{
FieldPath: "link.sourceNode",
ConstraintId: "required",
Message: "value is required",
},
{
FieldPath: "link.targetNode",
ConstraintId: "required",
Message: "value is required",
},
{
FieldPath: "link.sourcePort",
ConstraintId: "required",
Message: "value is required",
},
{
FieldPath: "link.targetPort",
ConstraintId: "required",
Message: "value is required",
},
},
}, },
} }
for _, tt := range tests { for _, tt := range tests {
...@@ -289,7 +318,8 @@ func TestTopology_AddLink(t *testing.T) { ...@@ -289,7 +318,8 @@ func TestTopology_AddLink(t *testing.T) {
_, err := tr.AddLink(tt.args.ctx, tt.args.request) _, err := tr.AddLink(tt.args.ctx, tt.args.request)
if err != nil { if err != nil {
if tt.wantErr { if tt.wantErr {
// TODO: check error assertValidationErrors(t, err, tt.validationErrors)
return return
} }
...@@ -318,7 +348,7 @@ func TestTopology_GetTopology(t *testing.T) { ...@@ -318,7 +348,7 @@ func TestTopology_GetTopology(t *testing.T) {
wantErr bool wantErr bool
}{ }{
{ {
name: "should add a new link", name: "should get an existing link",
fields: fields{ fields: fields{
ports: []ports.Port{getTestSourcePort(), getTestTargetPort()}, ports: []ports.Port{getTestSourcePort(), getTestTargetPort()},
nodes: []nodes.Node{getTestSourceNode(), getTestTargetNode()}, nodes: []nodes.Node{getTestSourceNode(), getTestTargetNode()},
...@@ -391,14 +421,15 @@ func TestTopology_DeleteLink(t *testing.T) { ...@@ -391,14 +421,15 @@ func TestTopology_DeleteLink(t *testing.T) {
request *apb.DeleteLinkRequest request *apb.DeleteLinkRequest
} }
tests := []struct { tests := []struct {
name string name string
fields fields fields fields
args args args args
want *apb.DeleteLinkResponse want *apb.DeleteLinkResponse
wantErr bool wantErr bool
validationErrors []*validate.Violation
}{ }{
{ {
name: "should add a new link", name: "should delete an existing link",
fields: fields{ fields: fields{
ports: []ports.Port{getTestSourcePort(), getTestTargetPort()}, ports: []ports.Port{getTestSourcePort(), getTestTargetPort()},
nodes: []nodes.Node{getTestSourceNode(), getTestTargetNode()}, nodes: []nodes.Node{getTestSourceNode(), getTestTargetNode()},
...@@ -413,6 +444,28 @@ func TestTopology_DeleteLink(t *testing.T) { ...@@ -413,6 +444,28 @@ func TestTopology_DeleteLink(t *testing.T) {
want: &apb.DeleteLinkResponse{}, want: &apb.DeleteLinkResponse{},
wantErr: false, wantErr: false,
}, },
{
name: "should error due to missing link id",
fields: fields{
ports: []ports.Port{getTestSourcePort(), getTestTargetPort()},
nodes: []nodes.Node{getTestSourceNode(), getTestTargetNode()},
links: []links.Link{getTestLinkInternal()},
},
args: args{
ctx: context.TODO(),
request: &apb.DeleteLinkRequest{
Id: "",
},
},
want: &apb.DeleteLinkResponse{},
wantErr: true,
validationErrors: []*validate.Violation{
{
FieldPath: "id",
ConstraintId: "required",
Message: "value is required",
}},
},
} }
for _, tt := range tests { for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) { t.Run(tt.name, func(t *testing.T) {
...@@ -423,11 +476,18 @@ func TestTopology_DeleteLink(t *testing.T) { ...@@ -423,11 +476,18 @@ func TestTopology_DeleteLink(t *testing.T) {
return return
} }
if tt.wantErr {
assertValidationErrors(t, err, tt.validationErrors)
// There is no need to fetch again if we are expecting a validation error.
return
}
gotAfterDelete, err := tr.GetTopology(tt.args.ctx, &apb.GetTopologyRequest{}) gotAfterDelete, err := tr.GetTopology(tt.args.ctx, &apb.GetTopologyRequest{})
if (err != nil) != tt.wantErr { if (err != nil) != tt.wantErr {
t.Errorf("Topology.GetTopology() error = %v, wantErr %v", err, tt.wantErr) t.Errorf("Topology.GetTopology() error = %v, wantErr %v", err, tt.wantErr)
return return
} }
if !reflect.DeepEqual(gotAfterDelete.Toplogy, &apb.Topology{}) { if !reflect.DeepEqual(gotAfterDelete.Toplogy, &apb.Topology{}) {
t.Errorf("Topology.GetTopology() = %v, want %v", got, tt.want) t.Errorf("Topology.GetTopology() = %v, want %v", got, tt.want)
} }
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment