diff --git a/cmd/server/main.go b/cmd/server/main.go index 54d51a3..12740f2 100644 --- a/cmd/server/main.go +++ b/cmd/server/main.go @@ -129,7 +129,11 @@ func runServer(cfgPath string) error { // Application services msgSvc := service.NewMessageService(repo, queue, parserTypes, parserRegistry) - worker := service.NewRelayWorker(queue, repo, ruleReader, registry, exprRegistry, buildRelayWorkerConfig(cfg.Worker)) + workerCfg, err := buildRelayWorkerConfig(cfg.Worker) + if err != nil { + return fmt.Errorf("worker config: %w", err) + } + worker := service.NewRelayWorker(queue, repo, ruleReader, registry, exprRegistry, workerCfg) // HTTP + WebSocket adapter assembly resolver := newConfigInputResolver(cfg) @@ -239,22 +243,19 @@ func parserToContentType(parserType string) string { } } -func buildRelayWorkerConfig(wc cfgpkg.WorkerConfig) service.RelayWorkerConfig { - def := service.DefaultRelayWorkerConfig() +func buildRelayWorkerConfig(wc cfgpkg.WorkerConfig) (service.RelayWorkerConfig, error) { cfg := service.RelayWorkerConfig{DefaultRetryCount: wc.DefaultRetryCount} - if d, err := time.ParseDuration(wc.DefaultRetryDelay); err == nil { - cfg.DefaultRetryDelay = d - } else { - slog.Warn("invalid worker.defaultRetryDelay, using default", "value", wc.DefaultRetryDelay, "err", err) - cfg.DefaultRetryDelay = def.DefaultRetryDelay + d, err := time.ParseDuration(wc.DefaultRetryDelay) + if err != nil { + return service.RelayWorkerConfig{}, fmt.Errorf("invalid worker.defaultRetryDelay %q: %w", wc.DefaultRetryDelay, err) } - if d, err := time.ParseDuration(wc.PollBackoff); err == nil { - cfg.PollBackoff = d - } else { - slog.Warn("invalid worker.pollBackoff, using default", "value", wc.PollBackoff, "err", err) - cfg.PollBackoff = def.PollBackoff + cfg.DefaultRetryDelay = d + d, err = time.ParseDuration(wc.PollBackoff) + if err != nil { + return service.RelayWorkerConfig{}, fmt.Errorf("invalid worker.pollBackoff %q: %w", wc.PollBackoff, err) } - return cfg + cfg.PollBackoff = d + return cfg, nil } func setupLogger(cfg *cfgpkg.Config) { diff --git a/cmd/server/main_test.go b/cmd/server/main_test.go index 104a0d5..64a02fb 100644 --- a/cmd/server/main_test.go +++ b/cmd/server/main_test.go @@ -2,6 +2,7 @@ package main import ( "testing" + "time" cfgpkg "relaybox/internal/config" ) @@ -35,3 +36,40 @@ func TestNewRepository_UnsupportedType(t *testing.T) { t.Fatal("expected error for unsupported storage type") } } + +func TestBuildRelayWorkerConfig_ValidDurations(t *testing.T) { + wc := cfgpkg.WorkerConfig{ + DefaultRetryCount: 5, + DefaultRetryDelay: "2s", + PollBackoff: "100ms", + } + cfg, err := buildRelayWorkerConfig(wc) + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + if cfg.DefaultRetryDelay != 2*time.Second { + t.Errorf("DefaultRetryDelay = %v, want 2s", cfg.DefaultRetryDelay) + } + if cfg.PollBackoff != 100*time.Millisecond { + t.Errorf("PollBackoff = %v, want 100ms", cfg.PollBackoff) + } + if cfg.DefaultRetryCount != 5 { + t.Errorf("DefaultRetryCount = %d, want 5", cfg.DefaultRetryCount) + } +} + +func TestBuildRelayWorkerConfig_InvalidRetryDelay(t *testing.T) { + wc := cfgpkg.WorkerConfig{DefaultRetryDelay: "not-a-duration", PollBackoff: "500ms"} + _, err := buildRelayWorkerConfig(wc) + if err == nil { + t.Fatal("expected error for invalid defaultRetryDelay") + } +} + +func TestBuildRelayWorkerConfig_InvalidPollBackoff(t *testing.T) { + wc := cfgpkg.WorkerConfig{DefaultRetryDelay: "1s", PollBackoff: "bad"} + _, err := buildRelayWorkerConfig(wc) + if err == nil { + t.Fatal("expected error for invalid pollBackoff") + } +} diff --git a/internal/adapter/input/http/middleware.go b/internal/adapter/input/http/middleware.go index 577c34a..51ad95b 100644 --- a/internal/adapter/input/http/middleware.go +++ b/internal/adapter/input/http/middleware.go @@ -20,7 +20,11 @@ const ( ) func inputTypeFromContext(ctx context.Context) domain.InputType { - return ctx.Value(inputTypeKey).(domain.InputType) + v, ok := ctx.Value(inputTypeKey).(domain.InputType) + if !ok { + panic("inputAuthMiddleware not applied: inputType missing from context") + } + return v } func inputAuthMiddleware(resolver input.InputResolver) func(http.Handler) http.Handler { diff --git a/internal/adapter/input/http/middleware_internal_test.go b/internal/adapter/input/http/middleware_internal_test.go new file mode 100644 index 0000000..e542cb6 --- /dev/null +++ b/internal/adapter/input/http/middleware_internal_test.go @@ -0,0 +1,21 @@ +package http + +import ( + "context" + "strings" + "testing" +) + +func TestInputTypeFromContext_PanicsWithMessage(t *testing.T) { + defer func() { + r := recover() + if r == nil { + t.Fatal("expected panic, got none") + } + msg, ok := r.(string) + if !ok || !strings.Contains(msg, "inputAuthMiddleware not applied") { + t.Errorf("unexpected panic value: %v", r) + } + }() + inputTypeFromContext(context.Background()) +} diff --git a/internal/adapter/output/webhook/sender.go b/internal/adapter/output/webhook/sender.go index c4ff192..1e383e7 100644 --- a/internal/adapter/output/webhook/sender.go +++ b/internal/adapter/output/webhook/sender.go @@ -20,12 +20,10 @@ type Sender struct { } func NewSender() *Sender { - return &Sender{ - transport: &http.Transport{}, - insecureTransport: &http.Transport{ - TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, //nolint:gosec - }, - } + base := http.DefaultTransport.(*http.Transport).Clone() + insecure := http.DefaultTransport.(*http.Transport).Clone() + insecure.TLSClientConfig = &tls.Config{InsecureSkipVerify: true} //nolint:gosec + return &Sender{transport: base, insecureTransport: insecure} } func (s *Sender) Send(ctx context.Context, out domain.Output, payload []byte) error {