From 7e2d76ef3a183b9f10a3f22cde9fb1a79365a6cf Mon Sep 17 00:00:00 2001 From: Joel Wetzell Date: Sun, 28 Dec 2025 18:09:51 -0600 Subject: [PATCH] add test for router creation and basic input output --- router_test.go | 302 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 302 insertions(+) create mode 100644 router_test.go diff --git a/router_test.go b/router_test.go new file mode 100644 index 0000000..eba551e --- /dev/null +++ b/router_test.go @@ -0,0 +1,302 @@ +package showbridge_test + +import ( + "context" + "errors" + "fmt" + "log/slog" + "sync" + "testing" + + "github.com/jwetzell/showbridge-go" + "github.com/jwetzell/showbridge-go/internal/config" + "github.com/jwetzell/showbridge-go/internal/module" + "github.com/jwetzell/showbridge-go/internal/route" +) + +type MockModule struct { + config config.ModuleConfig + ctx context.Context + outputCount int + router route.RouteIO + logger *slog.Logger +} + +func (m *MockModule) Id() string { + return m.config.Id +} + +func (m *MockModule) Output(context.Context, any) error { + m.outputCount += 1 + return nil +} + +func (m *MockModule) Run() error { + <-m.ctx.Done() + return nil +} + +func (m *MockModule) Type() string { + return m.config.Type +} + +func init() { + module.RegisterModule(module.ModuleRegistration{ + Type: "mock.counter", + New: func(ctx context.Context, config config.ModuleConfig) (module.Module, error) { + + router, ok := ctx.Value(route.RouterContextKey).(route.RouteIO) + + if !ok { + return nil, errors.New("mock.counter unable to get router from context") + } + + return &MockModule{config: config, ctx: ctx, router: router, logger: slog.Default()}, nil + }, + }) +} + +func TestNewRouter(t *testing.T) { + routerConfig := config.Config{ + Modules: []config.ModuleConfig{ + { + Type: "mock.counter", + }, + }, + } + + _, moduleErrors, routeErrors := showbridge.NewRouter(t.Context(), routerConfig) + + if moduleErrors != nil { + t.Fatalf("router should not have returned any module errors: %v", moduleErrors) + } + + if routeErrors != nil { + t.Fatalf("router should not have returned any route errors: %v", routeErrors) + } +} + +func TestRouterInputSingleRoute(t *testing.T) { + routerConfig := config.Config{ + Modules: []config.ModuleConfig{ + { + Id: "mock", + Type: "mock.counter", + }, + }, + Routes: []config.RouteConfig{ + { + Input: "mock", + Output: "mock", + }, + }, + } + + router, moduleErrors, routeErrors := showbridge.NewRouter(t.Context(), routerConfig) + + if moduleErrors != nil { + t.Fatalf("router should not have returned any module errors: %v", moduleErrors) + } + + if routeErrors != nil { + t.Fatalf("router should not have returned any route errors: %v", routeErrors) + } + + routerRunner := sync.WaitGroup{} + + routerRunner.Go(func() { + router.Run() + }) + + defer router.Stop() + + mockModuleInputCount := 3 + for i := range mockModuleInputCount { + aRouteFound, routingErrors := router.HandleInput(t.Context(), "mock", fmt.Sprintf("test %d", i)) + + if routingErrors != nil { + t.Fatalf("router should not have encountered routing errors") + } + + if !aRouteFound { + t.Fatalf("router should have found a valid route for the input") + } + } + + for _, moduleInstance := range router.ModuleInstances { + if moduleInstance.Id() == "mock" { + mockModuleInstance, ok := moduleInstance.(*MockModule) + if !ok { + t.Fatalf("couldn't get mock module") + } + + if mockModuleInstance.outputCount != mockModuleInputCount { + t.Fatalf("mock module output count did not matched expected: %d got: %d", mockModuleInputCount, mockModuleInstance.outputCount) + } + } + } +} + +func TestRouterInputMultipleRoutes(t *testing.T) { + routerConfig := config.Config{ + Modules: []config.ModuleConfig{ + { + Id: "mock", + Type: "mock.counter", + }, + }, + Routes: []config.RouteConfig{ + { + Input: "mock", + Output: "mock", + }, + { + Input: "mock", + Output: "mock", + }, + { + Input: "mock", + Output: "mock", + }, + }, + } + + router, moduleErrors, routeErrors := showbridge.NewRouter(t.Context(), routerConfig) + + if moduleErrors != nil { + t.Fatalf("router should not have returned any module errors: %v", moduleErrors) + } + + if routeErrors != nil { + t.Fatalf("router should not have returned any route errors: %v", routeErrors) + } + + routerRunner := sync.WaitGroup{} + + routerRunner.Go(func() { + router.Run() + }) + + defer router.Stop() + + mockModuleInputCount := 3 + for i := range mockModuleInputCount { + aRouteFound, routingErrors := router.HandleInput(t.Context(), "mock", fmt.Sprintf("test %d", i)) + + if routingErrors != nil { + t.Fatalf("router should not have encountered routing errors") + } + + if !aRouteFound { + t.Fatalf("router should have found a valid route for the input") + } + } + + for _, moduleInstance := range router.ModuleInstances { + if moduleInstance.Id() == "mock" { + mockModuleInstance, ok := moduleInstance.(*MockModule) + if !ok { + t.Fatalf("couldn't get mock module") + } + + if mockModuleInstance.outputCount != len(router.RouteInstances)*mockModuleInputCount { + t.Fatalf("mock module output count did not matched expected: %d got: %d", len(router.RouteInstances)*mockModuleInputCount, mockModuleInstance.outputCount) + } + break + } + } +} + +func TestRouterInputMultipleModules(t *testing.T) { + routerConfig := config.Config{ + Modules: []config.ModuleConfig{ + { + Id: "mock1", + Type: "mock.counter", + }, + { + Id: "mock2", + Type: "mock.counter", + }, + }, + Routes: []config.RouteConfig{ + { + Input: "mock1", + Output: "mock1", + }, + { + Input: "mock2", + Output: "mock2", + }, + }, + } + + router, moduleErrors, routeErrors := showbridge.NewRouter(t.Context(), routerConfig) + + if moduleErrors != nil { + t.Fatalf("router should not have returned any module errors: %v", moduleErrors) + } + + if routeErrors != nil { + t.Fatalf("router should not have returned any route errors: %v", routeErrors) + } + + routerRunner := sync.WaitGroup{} + + routerRunner.Go(func() { + router.Run() + }) + + defer router.Stop() + + mock1ModuleInputCount := 3 + for i := range mock1ModuleInputCount { + aRouteFound, routingErrors := router.HandleInput(t.Context(), "mock1", fmt.Sprintf("test %d", i)) + + if routingErrors != nil { + t.Fatalf("router should not have encountered routing errors") + } + + if !aRouteFound { + t.Fatalf("router should have found a valid route for the input") + } + } + + mock2ModuleInputCount := 2 + for i := range mock2ModuleInputCount { + aRouteFound, routingErrors := router.HandleInput(t.Context(), "mock2", fmt.Sprintf("test %d", i)) + + if routingErrors != nil { + t.Fatalf("router should not have encountered routing errors") + } + + if !aRouteFound { + t.Fatalf("router should have found a valid route for the input") + } + } + + for _, moduleInstance := range router.ModuleInstances { + if moduleInstance.Id() == "mock1" { + mockModuleInstance, ok := moduleInstance.(*MockModule) + if !ok { + t.Fatalf("couldn't get mock module") + } + + if mockModuleInstance.outputCount != mock1ModuleInputCount { + t.Fatalf("mock module output count did not matched expected: %d got: %d", mock1ModuleInputCount, mockModuleInstance.outputCount) + } + break + } + if moduleInstance.Id() == "mock2" { + mockModuleInstance, ok := moduleInstance.(*MockModule) + if !ok { + t.Fatalf("couldn't get mock module") + } + + if mockModuleInstance.outputCount != mock2ModuleInputCount { + t.Fatalf("mock module output count did not matched expected: %d got: %d", mock2ModuleInputCount, mockModuleInstance.outputCount) + } + break + } + } +}