package webhook import ( "bytes" "context" "errors" "net/http" "net/http/httptest" "testing" "time" "git.yeij.top/AskaEth/Cyrene_v2/internal/channel/adapter" ) func TestName(t *testing.T) { t.Parallel() if got := New().Name(); got != "webhook" { t.Errorf("Name() = %q, want webhook", got) } } func TestConfigSnapshot(t *testing.T) { t.Parallel() cfg := Config{ EndpointPath: "/cb", ChannelID: "ch-1", ChannelName: "My Hook", Secret: "s3cr3t", Methods: []string{http.MethodPost, http.MethodPut}, } got := New(WithConfig(cfg)).Config() if got["endpointPath"] != "/cb" { t.Errorf("Config() endpointPath = %v, want /cb", got["endpointPath"]) } if got["channelId"] != "ch-1" { t.Errorf("Config() channelId = %v, want ch-1", got["channelId"]) } if got["secret"] != "s3cr3t" { t.Errorf("Config() secret = %v, want s3cr3t", got["secret"]) } if got["signatureHeader"] != defaultSignatureHeader { t.Errorf("Config() signatureHeader = %v, want %s", got["signatureHeader"], defaultSignatureHeader) } } func TestConnectDisconnectHealthCheck(t *testing.T) { t.Parallel() a := New() if a.IsConnected() { t.Error("IsConnected() = true before Connect, want false") } if err := a.HealthCheck(context.Background()); !errors.Is(err, adapter.ErrNotConnected) { t.Errorf("HealthCheck() before connect error = %v, want ErrNotConnected", err) } if err := a.Connect(context.Background()); err != nil { t.Errorf("Connect() error = %v", err) } if !a.IsConnected() { t.Error("IsConnected() = false after Connect, want true") } if err := a.HealthCheck(context.Background()); err != nil { t.Errorf("HealthCheck() after connect error = %v", err) } if err := a.Disconnect(context.Background()); err != nil { t.Errorf("Disconnect() error = %v", err) } if a.IsConnected() { t.Error("IsConnected() = true after Disconnect, want false") } // Idempotent. if err := a.Disconnect(context.Background()); err != nil { t.Errorf("Disconnect() second call error = %v", err) } if err := a.Connect(context.Background()); err != nil { t.Errorf("Connect() second call error = %v", err) } } func TestCapabilitiesProactiveDisabled(t *testing.T) { t.Parallel() caps := New().Capabilities() if caps.Proactive { t.Error("Capabilities().Proactive = true, want false") } if caps.MaxMessageLength <= 0 { t.Errorf("Capabilities().MaxMessageLength = %d, want normalized nonzero", caps.MaxMessageLength) } if caps.RecommendBurstMax <= 0 { t.Errorf("Capabilities().RecommendBurstMax = %d, want normalized nonzero", caps.RecommendBurstMax) } } func TestListChannelsConfigured(t *testing.T) { t.Parallel() a := New(WithConfig(Config{ChannelID: "ch-1", ChannelName: "Hook"})) chans, err := a.ListChannels(context.Background()) if err != nil { t.Fatalf("ListChannels() error = %v", err) } if len(chans) != 1 { t.Fatalf("ListChannels() len = %d, want 1", len(chans)) } if chans[0].ChannelID != "ch-1" || chans[0].ChannelName != "Hook" { t.Errorf("ListChannels()[0] = %+v, want {ch-1 Hook}", chans[0]) } } func TestListChannelsEmpty(t *testing.T) { t.Parallel() chans, err := New().ListChannels(context.Background()) if err != nil { t.Fatalf("ListChannels() error = %v", err) } if len(chans) != 0 { t.Errorf("ListChannels() len = %d, want 0", len(chans)) } } func TestSendProactiveUnsupported(t *testing.T) { t.Parallel() err := New().SendProactive(context.Background(), adapter.UnifiedMessage{}) if !errors.Is(err, adapter.ErrUnsupported) { t.Errorf("SendProactive() error = %v, want ErrUnsupported", err) } } func TestSendMessageUnsupported(t *testing.T) { t.Parallel() err := New().SendMessage(context.Background(), adapter.UnifiedResponse{}) if !errors.Is(err, adapter.ErrUnsupported) { t.Errorf("SendMessage() error = %v, want ErrUnsupported", err) } } func TestSetMessageHandlerAndClear(t *testing.T) { t.Parallel() a := New() a.SetMessageHandler(func(context.Context, adapter.UnifiedMessage) {}) // A nil handler clears the installed one, so delivery must fail. a.SetMessageHandler(nil) rec := httptest.NewRecorder() req := httptest.NewRequest(http.MethodPost, "/webhook", bytes.NewReader([]byte(`{"content":"hi"}`))) req.Header.Set("Content-Type", "application/json") a.InboundHandler().ServeHTTP(rec, req) if rec.Code != http.StatusServiceUnavailable { t.Errorf("status = %d, want 503", rec.Code) } } func TestReadMessagesBlocksUntilCancel(t *testing.T) { a := New() ctx, cancel := context.WithCancel(context.Background()) cancel() // pre-cancel so the loop returns deterministically done := make(chan struct{}) go func() { a.ReadMessages(ctx, func(adapter.UnifiedMessage) {}) close(done) }() select { case <-done: case <-time.After(2 * time.Second): t.Fatal("ReadMessages() did not return on canceled context") } }