package kook import ( "context" "errors" "testing" "git.yeij.top/AskaEth/Cyrene_v2/internal/channel/adapter" ) func TestName(t *testing.T) { t.Parallel() a := New(Config{BotToken: "x"}) if got := a.Name(); got != "kook" { t.Fatalf("Name() = %q, want kook", got) } } func TestConfig(t *testing.T) { t.Parallel() a := New(Config{BotToken: "abc123", BaseURL: "https://example.com/api/v3"}) got := a.Config() if got["bot_token"] != "abc123" { t.Fatalf("bot_token = %v, want abc123", got["bot_token"]) } if got["base_url"] != "https://example.com/api/v3" { t.Fatalf("base_url = %v, want https://example.com/api/v3", got["base_url"]) } var round Config if err := adapter.DecodeConfig(got, &round); err != nil { t.Fatalf("DecodeConfig: %v", err) } if round.BotToken != "abc123" || round.BaseURL != "https://example.com/api/v3" { t.Fatalf("round trip mismatch: %+v", round) } } func TestValidate(t *testing.T) { t.Parallel() if got := (Config{BotToken: "t"}).Validate(); got != nil { t.Fatalf("Validate with token = %v, want nil", got) } if err := (Config{}).Validate(); !errors.Is(err, ErrMissingToken) { t.Fatalf("Validate blank = %v, want ErrMissingToken", err) } if err := (Config{BotToken: " "}).Validate(); !errors.Is(err, ErrMissingToken) { t.Fatalf("Validate whitespace = %v, want ErrMissingToken", err) } } func TestCapabilities(t *testing.T) { t.Parallel() a := New(Config{BotToken: "x"}) caps := a.Capabilities() if !caps.Proactive { t.Error("Proactive should be true for kook") } if !caps.Markdown { t.Error("kook should support kmarkdown") } if caps.Image || caps.Voice || caps.Reaction || caps.TypingHint { t.Errorf("expected Image/Voice/Reaction/TypingHint unsupported, got %+v", caps) } if caps.MaxMessageLength != maxMessageLen { t.Errorf("MaxMessageLength = %d, want %d", caps.MaxMessageLength, maxMessageLen) } if caps.RecommendBurstMax <= 0 { t.Errorf("RecommendBurstMax should be positive, got %d", caps.RecommendBurstMax) } } func TestListChannels_Empty(t *testing.T) { t.Parallel() a := New(Config{BotToken: "x"}) got, err := a.ListChannels(context.Background()) if err != nil { t.Fatalf("ListChannels: %v", err) } if got == nil || len(got) != 0 { t.Fatalf("expected empty non-nil slice, got %#v", got) } } func TestConnect_MissingToken(t *testing.T) { t.Parallel() a := New(Config{}) err := a.Connect(context.Background()) if !errors.Is(err, ErrMissingToken) { t.Fatalf("err = %v, want ErrMissingToken", err) } if a.IsConnected() { t.Fatal("should not be connected") } } func TestHealthCheck_NotConnected(t *testing.T) { t.Parallel() a := New(Config{BotToken: "x"}) err := a.HealthCheck(context.Background()) if !errors.Is(err, adapter.ErrNotConnected) { t.Fatalf("err = %v, want ErrNotConnected", err) } } func TestSendMessage_NotConnected(t *testing.T) { t.Parallel() a := New(Config{BotToken: "x"}) err := a.SendMessage(context.Background(), adapter.UnifiedResponse{}) if !errors.Is(err, adapter.ErrNotConnected) { t.Fatalf("err = %v, want ErrNotConnected", err) } if err := a.SendProactive(context.Background(), adapter.UnifiedMessage{}); !errors.Is(err, adapter.ErrNotConnected) { t.Fatalf("SendProactive err = %v, want ErrNotConnected", err) } } func TestNormalizedReceiveMode(t *testing.T) { t.Parallel() if got := (Config{}).normalizedReceiveMode(); got != modeWS { t.Fatalf("empty ReceiveMode = %q, want %q", got, modeWS) } if got := (Config{ReceiveMode: modeWebhook}).normalizedReceiveMode(); got != modeWebhook { t.Fatalf("webhook ReceiveMode = %q, want %q", got, modeWebhook) } if got := (Config{ReceiveMode: " ws "}).normalizedReceiveMode(); got != modeWS { t.Fatalf("trimmed ReceiveMode = %q, want %q", got, modeWS) } if (Config{}).IsWebhook() { t.Error("empty ReceiveMode should not be webhook") } if !(Config{ReceiveMode: modeWebhook}).IsWebhook() { t.Error("webhook ReceiveMode should be webhook") } } func TestConfigReceiveModeSnapshotAndRoundTrip(t *testing.T) { t.Parallel() a := New(Config{ BotToken: "abc123", ReceiveMode: modeWebhook, WebhookPath: "/cb", WebhookVerifyToken: "vt-1", WebhookEncryptKey: "ek-1", }) got := a.Config() if got["receive_mode"] != modeWebhook { t.Errorf("receive_mode = %v, want %q", got["receive_mode"], modeWebhook) } if got["webhook_path"] != "/cb" { t.Errorf("webhook_path = %v, want /cb", got["webhook_path"]) } if got["webhook_verify_token"] != "vt-1" || got["webhook_encrypt_key"] != "ek-1" { t.Errorf("webhook secrets = %v/%v, want vt-1/ek-1", got["webhook_verify_token"], got["webhook_encrypt_key"]) } var round Config if err := adapter.DecodeConfig(got, &round); err != nil { t.Fatalf("DecodeConfig: %v", err) } if round.ReceiveMode != modeWebhook || round.WebhookPath != "/cb" || round.WebhookVerifyToken != "vt-1" || round.WebhookEncryptKey != "ek-1" { t.Fatalf("round trip mismatch: %+v", round) } } func TestConfigDefaultReceiveModeSnapshot(t *testing.T) { t.Parallel() a := New(Config{BotToken: "x"}) if got := a.Config()["receive_mode"]; got != modeWS { t.Errorf("default receive_mode = %v, want %q", got, modeWS) } } func TestValidateRequiresTokenBothModes(t *testing.T) { t.Parallel() if err := (Config{BotToken: "t", ReceiveMode: modeWebhook}).Validate(); err != nil { t.Fatalf("webhook mode with token Validate = %v, want nil", err) } if err := (Config{ReceiveMode: modeWebhook}).Validate(); !errors.Is(err, ErrMissingToken) { t.Fatalf("webhook mode without token Validate = %v, want ErrMissingToken", err) } }