diff --git a/controller/topology/nodes/nodeService.go b/controller/topology/nodes/nodeService.go index c540f85c6f7fea907f60ca960ddcd77b077147bf..1a444a48b18db7d5c519e94b8ef80d8df2f39912 100644 --- a/controller/topology/nodes/nodeService.go +++ b/controller/topology/nodes/nodeService.go @@ -1,7 +1,7 @@ package nodes import ( - "code.fbi.h-da.de/danet/gosdn/controller/store" + "code.fbi.h-da.de/danet/gosdn/controller/topology/store" "github.com/google/uuid" ) diff --git a/controller/topology/nodes/nodeService_test.go b/controller/topology/nodes/nodeService_test.go new file mode 100644 index 0000000000000000000000000000000000000000..2472d8564c6d8159ea70f917c4bac01b9c99d0c3 --- /dev/null +++ b/controller/topology/nodes/nodeService_test.go @@ -0,0 +1,321 @@ +package nodes + +import ( + "reflect" + "testing" + + "code.fbi.h-da.de/danet/gosdn/controller/topology/store" + "github.com/google/uuid" +) + +func getTestNode() Node { + return Node{ + ID: uuid.MustParse("44fb4aa4-c53c-4cf9-a081-5aabc61c7610"), + Name: "Test-Node", + } +} + +func getEmptyNode() Node { + return Node{ + ID: uuid.Nil, + Name: "", + } +} + +func getTestStoreWithNodes(t *testing.T, nodes []Node) Store { + store := store.NewGenericStore[Node]() + + for _, node := range nodes { + err := store.Add(node) + if err != nil { + t.Fatalf("failed to prepare test store while adding node: %v", err) + } + } + + return store +} + +func TestNewNodeService(t *testing.T) { + type args struct { + store Store + } + tests := []struct { + name string + args args + want Service + }{ + { + name: "should create a new node service", + args: args{ + store: getTestStoreWithNodes(t, []Node{}), + }, + want: NewNodeService(getTestStoreWithNodes(t, []Node{})), + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := NewNodeService(tt.args.store); !reflect.DeepEqual(got, tt.want) { + t.Errorf("NewNodeService() = %v, want %v", got, tt.want) + } + }) + } +} + +func TestNodeService_EnsureExists(t *testing.T) { + type fields struct { + store Store + } + type args struct { + node Node + } + tests := []struct { + name string + fields fields + args args + want Node + wantErr bool + }{ + // { + // name: "should add not existing node", + // fields: fields{ + // store: store.NewGenericStore[Node](), + // }, + // args: args{ + // node: Node{ + // ID: uuid.Nil, + // Name: "Test-Node", + // }, + // }, + // want: Node{ + // Name: "Test-Node", + // }, + // wantErr: false, + // }, + { + name: "should error if node with uuid is not in store", + fields: fields{ + store: store.NewGenericStore[Node](), + }, + args: args{ + node: getTestNode(), + }, + want: getEmptyNode(), + wantErr: true, + }, + { + name: "should return node that is in the store", + fields: fields{ + store: getTestStoreWithNodes(t, []Node{getTestNode()}), + }, + args: args{ + node: getTestNode(), + }, + want: getTestNode(), + wantErr: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + p := &NodeService{ + store: tt.fields.store, + } + got, err := p.EnsureExists(tt.args.node) + if (err != nil) != tt.wantErr { + t.Errorf("NodeService.EnsureExists() error = %v, wantErr %v", err, tt.wantErr) + return + } + if !reflect.DeepEqual(got, tt.want) { + t.Errorf("NodeService.EnsureExists() = %v, want %v", got, tt.want) + } + }) + } +} + +func TestNodeService_Update(t *testing.T) { + type fields struct { + store Store + } + type args struct { + node Node + } + tests := []struct { + name string + fields fields + args args + want Node + wantErr bool + }{ + { + name: "should update an existing node", + fields: fields{ + store: store.NewGenericStore[Node](), + }, + args: args{ + node: getTestNode(), + }, + want: getTestNode(), + wantErr: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + p := &NodeService{ + store: tt.fields.store, + } + if err := p.Update(tt.args.node); (err != nil) != tt.wantErr { + t.Errorf("NodeService.Update() error = %v, wantErr %v", err, tt.wantErr) + } + + updatedNode, err := p.Get(store.Query(tt.args.node)) + if err != nil { + t.Errorf("NodeService.Get() failed %v", err) + } + + if !reflect.DeepEqual(updatedNode, tt.want) { + t.Errorf("Got updated node = %v, want %v", updatedNode, tt.want) + } + }) + } +} + +func TestNodeService_Delete(t *testing.T) { + type fields struct { + store Store + } + type args struct { + node Node + } + tests := []struct { + name string + fields fields + args args + wantErr bool + }{ + { + name: "should delete an existing node", + fields: fields{ + store: store.NewGenericStore[Node](), + }, + args: args{ + node: getTestNode(), + }, + wantErr: false, + }, + { + name: "should fail if a node does not exists", + fields: fields{ + store: store.NewGenericStore[Node](), + }, + args: args{ + node: getTestNode(), + }, + wantErr: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + p := &NodeService{ + store: tt.fields.store, + } + if err := p.Delete(tt.args.node); (err != nil) != tt.wantErr { + t.Errorf("NodeService.Delete() error = %v, wantErr %v", err, tt.wantErr) + } + }) + } +} + +func TestNodeService_Get(t *testing.T) { + type fields struct { + store Store + } + type args struct { + query store.Query + } + tests := []struct { + name string + fields fields + args args + want Node + wantErr bool + }{ + { + name: "should error if node with uuid is not in store", + fields: fields{ + store: store.NewGenericStore[Node](), + }, + args: args{ + query: store.Query{ + ID: getTestNode().ID, + Name: getTestNode().Name, + }, + }, + want: getEmptyNode(), + wantErr: true, + }, + { + name: "should return node that is in the store", + fields: fields{ + store: getTestStoreWithNodes(t, []Node{getTestNode()}), + }, + args: args{ + query: store.Query{ + ID: getTestNode().ID, + Name: getTestNode().Name, + }, + }, + want: getTestNode(), + wantErr: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + p := &NodeService{ + store: tt.fields.store, + } + got, err := p.Get(tt.args.query) + if (err != nil) != tt.wantErr { + t.Errorf("NodeService.Get() error = %v, wantErr %v", err, tt.wantErr) + return + } + if !reflect.DeepEqual(got, tt.want) { + t.Errorf("NodeService.Get() = %v, want %v", got, tt.want) + } + }) + } +} + +func TestNodeService_GetAll(t *testing.T) { + type fields struct { + store Store + } + tests := []struct { + name string + fields fields + want []Node + wantErr bool + }{ + { + name: "should fail if a node does not exists", + fields: fields{ + store: getTestStoreWithNodes(t, []Node{getTestNode()}), + }, + want: []Node{getTestNode()}, + wantErr: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + p := &NodeService{ + store: tt.fields.store, + } + got, err := p.GetAll() + if (err != nil) != tt.wantErr { + t.Errorf("NodeService.GetAll() error = %v, wantErr %v", err, tt.wantErr) + return + } + if !reflect.DeepEqual(got, tt.want) { + t.Errorf("NodeService.GetAll() = %v, want %v", got, tt.want) + } + }) + } +} diff --git a/controller/topology/nodes/nodeStore.go b/controller/topology/nodes/nodeStore.go index 517c4f74db054d650718c8df0aacfc696c733b55..9057b0286aac258466efded641aec37ceac57e54 100644 --- a/controller/topology/nodes/nodeStore.go +++ b/controller/topology/nodes/nodeStore.go @@ -6,7 +6,7 @@ import ( "code.fbi.h-da.de/danet/gosdn/controller/nucleus/database" "code.fbi.h-da.de/danet/gosdn/controller/nucleus/errors" - "code.fbi.h-da.de/danet/gosdn/controller/store" + "code.fbi.h-da.de/danet/gosdn/controller/topology/store" "github.com/google/uuid" "go.mongodb.org/mongo-driver/bson" "go.mongodb.org/mongo-driver/bson/primitive" diff --git a/controller/topology/genericStore.go b/controller/topology/store/genericStore.go similarity index 79% rename from controller/topology/genericStore.go rename to controller/topology/store/genericStore.go index c4f0d6683b7d978496f998b26adf882c65e5e883..9d02d1764e8f3819401cdd2525bab43b42314084 100644 --- a/controller/topology/genericStore.go +++ b/controller/topology/store/genericStore.go @@ -1,9 +1,8 @@ -package topology +package store import ( "errors" - "code.fbi.h-da.de/danet/gosdn/controller/store" "github.com/google/uuid" ) @@ -17,8 +16,8 @@ type GenericStore[T storableConstraint] struct { } // NewGenericStore returns a specific in-memory store for a type T. -func NewGenericStore[T storableConstraint]() GenericStore[T] { - return GenericStore[T]{ +func NewGenericStore[T storableConstraint]() *GenericStore[T] { + return &GenericStore[T]{ Store: make(map[uuid.UUID]T), } } @@ -37,7 +36,7 @@ func (t *GenericStore[T]) Add(item T) error { func (t *GenericStore[T]) Update(item T) error { _, ok := t.Store[item.GetID()] if ok { - return nil + return errors.New("item not found") } t.Store[item.GetID()] = item @@ -46,12 +45,17 @@ func (t *GenericStore[T]) Update(item T) error { } func (t *GenericStore[T]) Delete(item T) error { + _, ok := t.Store[item.GetID()] + if ok { + return errors.New("item not found") + } + delete(t.Store, item.GetID()) return nil } -func (t *GenericStore[T]) Get(query store.Query) (T, error) { +func (t *GenericStore[T]) Get(query Query) (T, error) { // First search for direct hit on UUID. item, ok := t.Store[query.ID] if !ok { diff --git a/controller/topology/store/query.go b/controller/topology/store/query.go new file mode 100644 index 0000000000000000000000000000000000000000..4235ff48e1b162552c3cf3acc024b0a6b71b57b5 --- /dev/null +++ b/controller/topology/store/query.go @@ -0,0 +1,8 @@ +package store + +import "github.com/google/uuid" + +type Query struct { + ID uuid.UUID + Name string +}