Skip to content
Snippets Groups Projects
topology_test.go 12.9 KiB
Newer Older
  • Learn to ignore specific revisions
  • package server
    
    import (
    	"context"
    	"reflect"
    	"testing"
    
    
    	"buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go/buf/validate"
    
    	apb "code.fbi.h-da.de/danet/gosdn/api/go/gosdn/topology"
    	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/links"
    	"code.fbi.h-da.de/danet/gosdn/controller/topology/nodes"
    	"code.fbi.h-da.de/danet/gosdn/controller/topology/ports"
    	"code.fbi.h-da.de/danet/gosdn/controller/topology/ports/configuration"
    	"code.fbi.h-da.de/danet/gosdn/controller/topology/store"
    
    
    	"github.com/bufbuild/protovalidate-go"
    
    	"github.com/google/uuid"
    
    	"github.com/stretchr/testify/assert"
    
    )
    
    func getTestNodeService() nodes.Service {
    	eventService := eventservice.NewMockEventService()
    
    	nodeStore := store.NewGenericStore[nodes.Node]()
    	nodeService := nodes.NewNodeService(nodeStore, eventService)
    
    	return nodeService
    }
    
    func getTestPortService() ports.Service {
    	eventService := eventservice.NewMockEventService()
    
    	portStore := store.NewGenericStore[ports.Port]()
    	portService := ports.NewPortService(portStore, eventService)
    
    	return portService
    }
    
    func getTestTopologyService() topology.Service {
    	eventService := eventservice.NewMockEventService()
    
    	nodeStore := store.NewGenericStore[nodes.Node]()
    	nodeService := nodes.NewNodeService(nodeStore, eventService)
    
    	portStore := store.NewGenericStore[ports.Port]()
    	portService := ports.NewPortService(portStore, eventService)
    
    	linkStore := store.NewGenericStore[links.Link]()
    	topologyService := topology.NewTopologyService(linkStore, nodeService, portService, eventService)
    
    	return topologyService
    }
    
    func getTestTopologyServer(
    	t *testing.T,
    	nodesToAdd []nodes.Node,
    	portsToAdd []ports.Port,
    	linksToAdd []links.Link,
    
    ) *TopologyServer {
    
    	eventService := eventservice.NewMockEventService()
    
    	nodeStore := getTestStoreWithNodes(t, nodesToAdd)
    	nodeService := nodes.NewNodeService(nodeStore, eventService)
    
    	portStore := getTestStoreWithPorts(t, portsToAdd)
    	portService := ports.NewPortService(portStore, eventService)
    
    	linkStore := getTestStoreWithLinks(t, linksToAdd)
    	topologyService := topology.NewTopologyService(linkStore, nodeService, portService, eventService)
    
    
    	protoValidator, err := protovalidate.New()
    	if err != nil {
    		panic(err)
    	}
    
    	s := NewTopologyServer(topologyService, nodeService, portService, protoValidator)
    
    
    	return s
    }
    
    func getTestSourceNode() nodes.Node {
    	return nodes.Node{
    		ID:   uuid.MustParse("44fb4aa4-c53c-4cf9-a081-5aabc61c7610"),
    		Name: "Test-Source-Node",
    	}
    }
    
    func getTestTargetNode() nodes.Node {
    	return nodes.Node{
    		ID:   uuid.MustParse("44fb4aa4-c53c-4cf9-a081-5aabc61c7612"),
    		Name: "Test-Target-Node",
    	}
    }
    
    func getTestPortIPConfiguration() configuration.IPConfig {
    	config, _ := configuration.New("10.13.37.0", 24)
    
    	return config
    }
    
    func getTestSourcePort() ports.Port {
    	return ports.Port{
    		ID:            uuid.MustParse("1fa479e7-d393-4d45-822d-485cc1f05fce"),
    		Name:          "Test-Source-Port",
    		Configuration: getTestPortIPConfiguration(),
    	}
    }
    
    func getTestTargetPort() ports.Port {
    	return ports.Port{
    		ID:            uuid.MustParse("1fa479e7-d393-4d45-822d-485cc1f05fc2"),
    		Name:          "Test-Target-Port",
    		Configuration: getTestPortIPConfiguration(),
    	}
    }
    
    func getTestLinkInternal() links.Link {
    	return links.Link{
    		ID:         uuid.MustParse("5eb474f1-428e-4503-ba68-dcf9bef53467"),
    		Name:       "Test-Link",
    		SourceNode: getTestSourceNode(),
    		TargetNode: getTestTargetNode(),
    		SourcePort: getTestSourcePort(),
    		TargetPort: getTestTargetPort(),
    	}
    }
    
    func getTestStoreWithLinks(t *testing.T, nodes []links.Link) topology.Store {
    	store := store.NewGenericStore[links.Link]()
    
    	for _, node := range nodes {
    
    		err := store.Add(context.TODO(), node)
    
    		if err != nil {
    			t.Fatalf("failed to prepare test store while adding node: %v", err)
    		}
    	}
    
    	return store
    }
    
    func getTestStoreWithNodes(t *testing.T, nodesToAdd []nodes.Node) nodes.Store {
    	store := store.NewGenericStore[nodes.Node]()
    
    	for _, node := range nodesToAdd {
    
    		err := store.Add(context.TODO(), node)
    
    		if err != nil {
    			t.Fatalf("failed to prepare test store while adding node: %v", err)
    		}
    	}
    
    	return store
    }
    
    func getTestStoreWithPorts(t *testing.T, portsToAdd []ports.Port) ports.Store {
    	store := store.NewGenericStore[ports.Port]()
    
    	for _, port := range portsToAdd {
    
    		err := store.Add(context.TODO(), port)
    
    		if err != nil {
    			t.Fatalf("failed to prepare test store while adding port: %v", err)
    		}
    	}
    
    	return store
    }
    
    func getTestLink() *apb.Link {
    	return &apb.Link{
    		Name: "test-link",
    		SourceNode: &apb.Node{
    			Name: "test-source-node",
    		},
    		SourcePort: &apb.Port{
    			Name: "test-source-port",
    			Configuration: &apb.Configuration{
    				Ip:           "10.13.37.0",
    				PrefixLength: 24,
    			},
    		},
    		TargetNode: &apb.Node{
    			Name: "test-target-node",
    		},
    		TargetPort: &apb.Port{
    			Name: "test-target-port",
    			Configuration: &apb.Configuration{
    				Ip:           "10.13.38.0",
    				PrefixLength: 24,
    			},
    		},
    	}
    }
    
    func TestNewTopologyServer(t *testing.T) {
    	type args struct {
    
    		service        topology.Service
    		nodeService    nodes.Service
    		portService    ports.Service
    		protoValidator *protovalidate.Validator
    
    	}
    	tests := []struct {
    		name string
    		args args
    
    		want *TopologyServer
    
    	}{
    		{
    			name: "should create a new topology service",
    			args: args{
    
    				service:        getTestTopologyService(),
    				nodeService:    getTestNodeService(),
    				portService:    getTestPortService(),
    				protoValidator: &protovalidate.Validator{},
    
    			want: &TopologyServer{
    
    				topologyService: getTestTopologyService(),
    				nodeService:     getTestNodeService(),
    				portService:     getTestPortService(),
    
    				protoValidator:  &protovalidate.Validator{},
    
    			},
    		},
    	}
    	for _, tt := range tests {
    		t.Run(tt.name, func(t *testing.T) {
    
    			if got := NewTopologyServer(
    				tt.args.service,
    				tt.args.nodeService,
    				tt.args.portService,
    				tt.args.protoValidator,
    			); !assert.Equal(t, got, tt.want) {
    
    				t.Errorf("NewTopologyServer() = %v, want %v", got, tt.want)
    			}
    		})
    	}
    }
    
    func TestTopology_AddLink(t *testing.T) {
    	type fields struct {
    		ports []ports.Port
    		nodes []nodes.Node
    		links []links.Link
    	}
    	type args struct {
    		ctx     context.Context
    		request *apb.AddLinkRequest
    	}
    	tests := []struct {
    
    		name             string
    		fields           fields
    		args             args
    		want             *apb.AddLinkResponse
    		wantErr          bool
    		validationErrors []*validate.Violation
    
    	}{
    		{
    			name: "should add a new link",
    			fields: fields{
    
    				ports: []ports.Port{getTestSourcePort(), getTestTargetPort()},
    				nodes: []nodes.Node{getTestSourceNode(), getTestTargetNode()},
    				links: []links.Link{getTestLinkInternal()},
    
    			},
    			args: args{
    				ctx: context.TODO(),
    				request: &apb.AddLinkRequest{
    					Link: getTestLink(),
    				},
    			},
    
    			want:    &apb.AddLinkResponse{},
    
    		{
    			name: "add a new link with missing fields should fail",
    			fields: fields{
    				ports: []ports.Port{},
    				nodes: []nodes.Node{},
    				links: []links.Link{},
    			},
    			args: args{
    				ctx: context.TODO(),
    				request: &apb.AddLinkRequest{
    					Link: &apb.Link{},
    				},
    			},
    
    			want:    &apb.AddLinkResponse{},
    
    			wantErr: true,
    
    			validationErrors: []*validate.Violation{
    				{
    
    					FieldPath:    stringToPointer("link.name"),
    					ConstraintId: stringToPointer("string.min_len"),
    					Message:      stringToPointer("value length must be at least 1 characters"),
    
    					FieldPath:    stringToPointer("link.sourceNode"),
    					ConstraintId: stringToPointer("required"),
    					Message:      stringToPointer("value is required"),
    
    					FieldPath:    stringToPointer("link.targetNode"),
    					ConstraintId: stringToPointer("required"),
    					Message:      stringToPointer("value is required"),
    
    					FieldPath:    stringToPointer("link.sourcePort"),
    					ConstraintId: stringToPointer("required"),
    					Message:      stringToPointer("value is required"),
    
    					FieldPath:    stringToPointer("link.targetPort"),
    					ConstraintId: stringToPointer("required"),
    					Message:      stringToPointer("value is required"),
    
    	}
    	for _, tt := range tests {
    		t.Run(tt.name, func(t *testing.T) {
    			tr := getTestTopologyServer(t, tt.fields.nodes, tt.fields.ports, tt.fields.links)
    
    			_, err := tr.AddLink(tt.args.ctx, tt.args.request)
    
    			if err != nil {
    				if tt.wantErr {
    
    					assertValidationErrors(t, err, tt.validationErrors)
    
    
    				t.Errorf("Topology.AddLink() error = %v, wantErr %v", err, tt.wantErr)
    				return
    			}
    		})
    	}
    }
    
    func TestTopology_GetTopology(t *testing.T) {
    	type fields struct {
    		ports []ports.Port
    		nodes []nodes.Node
    		links []links.Link
    	}
    	type args struct {
    		ctx     context.Context
    		request *apb.GetTopologyRequest
    	}
    	tests := []struct {
    		name    string
    		fields  fields
    		args    args
    		want    *apb.GetTopologyResponse
    		wantErr bool
    	}{
    		{
    
    			name: "should get an existing link",
    
    			fields: fields{
    				ports: []ports.Port{getTestSourcePort(), getTestTargetPort()},
    				nodes: []nodes.Node{getTestSourceNode(), getTestTargetNode()},
    				links: []links.Link{getTestLinkInternal()},
    			},
    			args: args{
    				ctx:     context.TODO(),
    				request: &apb.GetTopologyRequest{},
    			},
    			want: &apb.GetTopologyResponse{
    				Toplogy: &apb.Topology{
    					Links: []*apb.Link{
    						{
    							Id:   "5eb474f1-428e-4503-ba68-dcf9bef53467",
    							Name: "Test-Link",
    							SourceNode: &apb.Node{
    								Id:   "44fb4aa4-c53c-4cf9-a081-5aabc61c7610",
    								Name: "Test-Source-Node",
    							},
    							SourcePort: &apb.Port{
    								Id:   "1fa479e7-d393-4d45-822d-485cc1f05fce",
    								Name: "Test-Source-Port",
    								Configuration: &apb.Configuration{
    									Ip:           "10.13.37.0",
    									PrefixLength: 24,
    								},
    							},
    							TargetNode: &apb.Node{
    								Id:   "44fb4aa4-c53c-4cf9-a081-5aabc61c7612",
    								Name: "Test-Target-Node",
    							},
    							TargetPort: &apb.Port{
    								Id:   "1fa479e7-d393-4d45-822d-485cc1f05fc2",
    								Name: "Test-Target-Port",
    								Configuration: &apb.Configuration{
    									Ip:           "10.13.37.0",
    									PrefixLength: 24,
    								},
    							},
    						},
    					},
    				},
    			},
    			wantErr: false,
    		},
    	}
    	for _, tt := range tests {
    		t.Run(tt.name, func(t *testing.T) {
    			tr := getTestTopologyServer(t, tt.fields.nodes, tt.fields.ports, tt.fields.links)
    			got, err := tr.GetTopology(tt.args.ctx, tt.args.request)
    			if (err != nil) != tt.wantErr {
    				t.Errorf("Topology.GetTopology() error = %v, wantErr %v", err, tt.wantErr)
    				return
    			}
    			if !reflect.DeepEqual(got.Toplogy, tt.want.Toplogy) {
    				t.Errorf("Topology.GetTopology() = %v, want %v", got.Toplogy, tt.want.Toplogy)
    			}
    		})
    	}
    }
    
    func TestTopology_DeleteLink(t *testing.T) {
    	type fields struct {
    		ports []ports.Port
    		nodes []nodes.Node
    		links []links.Link
    	}
    	type args struct {
    		ctx     context.Context
    		request *apb.DeleteLinkRequest
    	}
    	tests := []struct {
    
    		name             string
    		fields           fields
    		args             args
    		want             *apb.DeleteLinkResponse
    		wantErr          bool
    		validationErrors []*validate.Violation
    
    			name: "should delete an existing link",
    
    			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: "5eb474f1-428e-4503-ba68-dcf9bef53467",
    				},
    			},
    
    			want:    &apb.DeleteLinkResponse{},
    
    		{
    			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:    stringToPointer("id"),
    					ConstraintId: stringToPointer("required"),
    					Message:      stringToPointer("value is required"),
    
    	}
    	for _, tt := range tests {
    		t.Run(tt.name, func(t *testing.T) {
    			tr := getTestTopologyServer(t, tt.fields.nodes, tt.fields.ports, tt.fields.links)
    			got, err := tr.DeleteLink(tt.args.ctx, tt.args.request)
    			if (err != nil) != tt.wantErr {
    				t.Errorf("Topology.DeleteLink() error = %v, wantErr %v", err, tt.wantErr)
    				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{})
    			if (err != nil) != tt.wantErr {
    				t.Errorf("Topology.GetTopology() error = %v, wantErr %v", err, tt.wantErr)
    				return
    			}
    
    			if !reflect.DeepEqual(gotAfterDelete.Toplogy, &apb.Topology{}) {
    				t.Errorf("Topology.GetTopology() = %v, want %v", got, tt.want)
    			}
    		})
    	}
    }