Files
ziti/controller/db/terminator_store_test.go
T

310 lines
8.8 KiB
Go

/*
Copyright NetFoundry, Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
https://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package db
import (
"fmt"
"github.com/google/uuid"
"github.com/openziti/fabric/controller/xt"
"github.com/openziti/foundation/storage/boltz"
"github.com/openziti/foundation/util/stringz"
"go.etcd.io/bbolt"
"math"
"testing"
"time"
)
func Test_TerminatorStore(t *testing.T) {
ctx := NewTestContext(t)
defer ctx.Cleanup()
xt.GlobalRegistry().RegisterFactory(&testStrategyFactory{})
t.Run("test create invalid terminators", ctx.testCreateInvalidTerminators)
t.Run("test create/delete terminators", ctx.testCreateTerminators)
t.Run("test create/delete terminators", ctx.testLoadQueryTerminators)
t.Run("test update terminators", ctx.testUpdateTerminators)
t.Run("test delete terminators", ctx.testDeleteTerminators)
t.Run("test patch terminators", ctx.testPatchTerminator)
}
func (ctx *TestContext) testCreateInvalidTerminators(t *testing.T) {
ctx.NextTest(t)
defer ctx.cleanupAll()
terminator := &Terminator{
Binding: uuid.New().String(),
Address: uuid.New().String(),
Service: uuid.New().String(),
Router: uuid.New().String(),
}
err := ctx.Update(terminator)
ctx.EqualError(err, "cannot update terminator with blank id")
terminator.Binding = ""
err = ctx.Create(terminator)
ctx.EqualError(err, "the value '' for 'binding' is invalid: binding is required")
terminator.Binding = uuid.New().String()
terminator.Address = ""
err = ctx.Create(terminator)
ctx.EqualError(err, "the value '' for 'address' is invalid: address is required")
terminator.Router = ""
terminator.Address = uuid.New().String()
err = ctx.Create(terminator)
ctx.EqualError(err, "the value '' for 'router' is invalid: router is required")
terminator.Service = ""
terminator.Router = uuid.New().String()
err = ctx.Create(terminator)
ctx.EqualError(err, "the value '' for 'service' is invalid: service is required")
service := ctx.requireNewService()
router := ctx.requireNewRouter()
terminator.Service = uuid.New().String()
terminator.Router = router.Id
err = ctx.Create(terminator)
ctx.EqualError(err, fmt.Sprintf("service with id %v not found", terminator.Service))
terminator.Service = service.Id
terminator.Router = uuid.New().String()
err = ctx.Create(terminator)
ctx.EqualError(err, fmt.Sprintf("router with id %v not found", terminator.Router))
}
type terminatorTestEntities struct {
service *Service
service2 *Service
router *Router
router2 *Router
terminator *Terminator
terminator2 *Terminator
terminator3 *Terminator
}
func (ctx *TestContext) createTestTerminators() *terminatorTestEntities {
e := &terminatorTestEntities{}
e.service = ctx.requireNewService()
e.router = ctx.requireNewRouter()
e.terminator = &Terminator{}
e.terminator.Service = e.service.Id
e.terminator.Router = e.router.Id
e.terminator.Binding = uuid.New().String()
e.terminator.Address = uuid.New().String()
e.terminator.Cost = 0
ctx.RequireCreate(e.terminator)
e.router2 = ctx.requireNewRouter()
e.terminator2 = &Terminator{}
e.terminator2.Service = e.service.Id
e.terminator2.Router = e.router2.Id
e.terminator2.Binding = uuid.New().String()
e.terminator2.Address = uuid.New().String()
e.terminator2.Cost = 100
ctx.RequireCreate(e.terminator2)
e.service2 = ctx.requireNewService()
e.terminator3 = &Terminator{}
e.terminator3.Service = e.service2.Id
e.terminator3.Router = e.router2.Id
e.terminator3.Binding = uuid.New().String()
e.terminator3.Address = uuid.New().String()
e.terminator3.Cost = math.MaxUint16
ctx.RequireCreate(e.terminator3)
return e
}
func (ctx *TestContext) testCreateTerminators(t *testing.T) {
ctx.NextTest(t)
defer ctx.cleanupAll()
e := ctx.createTestTerminators()
ctx.ValidateBaseline(e.terminator)
ctx.ValidateBaseline(e.terminator2)
ctx.ValidateBaseline(e.terminator3)
terminatorIds := ctx.GetRelatedIds(e.service, EntityTypeTerminators)
ctx.EqualValues(2, len(terminatorIds))
ctx.True(stringz.Contains(terminatorIds, e.terminator.Id))
ctx.True(stringz.Contains(terminatorIds, e.terminator2.Id))
terminatorIds = ctx.GetRelatedIds(e.router, EntityTypeTerminators)
ctx.EqualValues(1, len(terminatorIds))
ctx.EqualValues(e.terminator.Id, terminatorIds[0])
terminatorIds = ctx.GetRelatedIds(e.router2, EntityTypeTerminators)
ctx.EqualValues(2, len(terminatorIds))
ctx.True(stringz.Contains(terminatorIds, e.terminator2.Id))
ctx.True(stringz.Contains(terminatorIds, e.terminator3.Id))
terminatorIds = ctx.GetRelatedIds(e.service2, EntityTypeTerminators)
ctx.EqualValues(1, len(terminatorIds))
ctx.EqualValues(e.terminator3.Id, terminatorIds[0])
}
func (ctx *TestContext) testLoadQueryTerminators(t *testing.T) {
ctx.NextTest(t)
defer ctx.cleanupAll()
e := ctx.createTestTerminators()
err := ctx.GetDb().View(func(tx *bbolt.Tx) error {
loadedTerminator, err := ctx.stores.Terminator.LoadOneById(tx, e.terminator.Id)
ctx.NoError(err)
ctx.NotNil(loadedTerminator)
ctx.EqualValues(e.terminator.Id, loadedTerminator.Id)
ctx.EqualValues(e.terminator.Service, loadedTerminator.Service)
ctx.EqualValues(e.terminator.Router, loadedTerminator.Router)
ctx.EqualValues(e.terminator.Binding, loadedTerminator.Binding)
ctx.EqualValues(e.terminator.Address, loadedTerminator.Address)
ctx.EqualValues(e.terminator.Cost, loadedTerminator.Cost)
ids, _, err := ctx.stores.Terminator.QueryIdsf(tx, `service = "%v"`, e.service.Id)
ctx.NoError(err)
ctx.EqualValues(2, len(ids))
ctx.True(stringz.Contains(ids, e.terminator.Id))
ctx.True(stringz.Contains(ids, e.terminator2.Id))
ids, _, err = ctx.stores.Terminator.QueryIdsf(tx, `router = "%v"`, e.router2.Id)
ctx.NoError(err)
ctx.EqualValues(2, len(ids))
ctx.True(stringz.Contains(ids, e.terminator2.Id))
ctx.True(stringz.Contains(ids, e.terminator3.Id))
ids, _, err = ctx.stores.Service.QueryIdsf(tx, `anyOf(terminators) = "%v"`, e.terminator.Id)
ctx.NoError(err)
ctx.EqualValues(1, len(ids))
ctx.True(stringz.Contains(ids, e.service.Id))
ids, _, err = ctx.stores.Router.QueryIdsf(tx, `anyOf(terminators) = "%v"`, e.terminator.Id)
ctx.NoError(err)
ctx.EqualValues(1, len(ids))
ctx.True(stringz.Contains(ids, e.router.Id))
return nil
})
ctx.NoError(err)
}
func (ctx *TestContext) testUpdateTerminators(t *testing.T) {
ctx.NextTest(t)
defer ctx.cleanupAll()
e := ctx.createTestTerminators()
terminator := e.terminator
ctx.RequireReload(terminator)
time.Sleep(time.Millisecond * 10) // ensure updatedAt is after createdAt
terminator.Service = e.service2.Id
terminator.Router = e.router2.Id
terminator.Binding = uuid.New().String()
terminator.Address = uuid.New().String()
terminator.Tags = ctx.CreateTags()
ctx.RequireUpdate(terminator)
terminator.Service = e.service.Id // service should not be updated
ctx.ValidateUpdated(terminator)
}
func (ctx *TestContext) testDeleteTerminators(t *testing.T) {
ctx.NextTest(t)
defer ctx.cleanupAll()
e := ctx.createTestTerminators()
ctx.RequireDelete(e.terminator3)
ctx.RequireDelete(e.router2)
ctx.ValidateDeleted(e.terminator2.Id)
ctx.ValidateDeleted(e.terminator3.Id)
ctx.RequireDelete(e.service)
ctx.ValidateDeleted(e.terminator.Id)
}
func (ctx *TestContext) testPatchTerminator(t *testing.T) {
service := ctx.requireNewService()
router := ctx.requireNewRouter()
terminator := &Terminator{}
terminator.Service = service.Id
terminator.Router = router.Id
terminator.Binding = uuid.New().String()
terminator.Address = uuid.New().String()
terminator.Cost = 0
terminator.PeerData = map[uint32][]byte{
1: {1, 2, 3},
2: {3, 4, 5},
}
ctx.RequireCreate(terminator)
ctx.ValidateBaseline(terminator)
terminator.Cost = 100
checker := boltz.MapFieldChecker{
FieldTerminatorCost: struct{}{},
}
ctx.RequirePatch(terminator, checker)
ctx.ValidateUpdated(terminator)
terminator.PeerData = map[uint32][]byte{
1: {7, 8, 9},
}
checker = boltz.MapFieldChecker{
FieldServerPeerData: struct{}{},
}
ctx.RequirePatch(terminator, checker)
ctx.ValidateUpdated(terminator)
}
type testStrategyFactory struct{}
func (t testStrategyFactory) GetStrategyName() string {
return "smartrouting"
}
func (t testStrategyFactory) NewStrategy() xt.Strategy {
return &testStrategy{}
}
type testStrategy struct{}
func (t testStrategy) Select(terminators []xt.CostedTerminator) (xt.Terminator, error) {
return terminators[0], nil
}
func (t testStrategy) HandleTerminatorChange(xt.StrategyChangeEvent) error {
return nil
}
func (t testStrategy) NotifyEvent(xt.TerminatorEvent) {
}