Files
goclaw/cmd/gateway_errors_test.go
T
viettranx 5040fe4cbe fix(channels): suppress agent-error text on public-facing channels
Rework of #911 against dev with correctness fixes.

Public-facing channels (Facebook, Telegram, Discord, Feishu, WhatsApp,
Zalo OA, Zalo Personal, Pancake, Slack) no longer receive raw internal
error text when an agent run fails — an empty outbound is published
instead so channels still clean up placeholders / typing indicators.
Errors continue to be logged server-side at Error level.

Applied on both hot paths:
  - cmd/gateway_consumer_normal.go   (agent run failure)
  - cmd/gateway_subagent_announce_queue.go (announce lead run failure)

Fixes vs #911:
  - Whitelist uses channels.Type* constants (compile-time safe) instead
    of string literals. The original PR's literals ("zalo", "line") did
    not match any real channel type, so Zalo OA/Personal and Pancake
    leaked errors while "line" was a dead branch.
  - Added TypePancake and TypeSlack which were missing.
  - Empty Content in Facebook.Send and Pancake.Send now short-circuits
    (matching Telegram/Discord/Slack). Previously, suppressed errors
    flowed to Graph/Pancake APIs as empty payloads, triggering 400s and
    channel health degradation.
  - Added table-driven test for isExternalChannel covering all real
    channel types + empty + unknown + legacy "zalo" short form.

Closes #911
2026-04-15 20:40:25 +07:00

50 lines
1.6 KiB
Go

package cmd
import (
"testing"
"github.com/nextlevelbuilder/goclaw/internal/channels"
)
// TestIsExternalChannel ensures the whitelist matches actual channel type
// constants — a mismatch here silently leaks internal error strings to end
// users (e.g. "zalo" vs "zalo_oa"/"zalo_personal"). Always compare against
// channels.Type* constants, never string literals.
func TestIsExternalChannel(t *testing.T) {
t.Parallel()
cases := []struct {
name string
channelType string
want bool
}{
// Public-facing platforms — errors must be suppressed.
{"facebook", channels.TypeFacebook, true},
{"telegram", channels.TypeTelegram, true},
{"discord", channels.TypeDiscord, true},
{"feishu", channels.TypeFeishu, true},
{"whatsapp", channels.TypeWhatsApp, true},
{"zalo_oa", channels.TypeZaloOA, true},
{"zalo_personal", channels.TypeZaloPersonal, true},
{"pancake", channels.TypePancake, true},
{"slack", channels.TypeSlack, true},
// Internal / unknown channel types — errors must still surface.
{"ws", "ws", false},
{"empty", "", false},
{"unknown", "myplatform", false},
// "line" is a Pancake sub-platform, not a top-level channel type.
{"line_not_top_level", "line", false},
// Legacy short "zalo" string must NOT match — real constants are zalo_oa / zalo_personal.
{"zalo_short_form", "zalo", false},
}
for _, tc := range cases {
t.Run(tc.name, func(t *testing.T) {
if got := isExternalChannel(tc.channelType); got != tc.want {
t.Fatalf("isExternalChannel(%q) = %v, want %v", tc.channelType, got, tc.want)
}
})
}
}