package adapter import ( "errors" "fmt" "testing" ) // selfDescribedFailure 是自描述投递失败的替身(适配器实现 DeliveryFailure 的最小形状)。 type selfDescribedFailure struct { category DeliveryCategory channel string text string } func (e *selfDescribedFailure) Error() string { return e.text } func (e *selfDescribedFailure) DeliveryCategory() DeliveryCategory { return e.category } func (e *selfDescribedFailure) DeliveryChannel() string { return e.channel } // TestDeliveryCategoryOf 锁定原因码口径:自报优先、契约层哨兵次之、兜底 // CategoryFailed —— 绝不替一个没说清楚原因的错误编一个更精确的码。 func TestDeliveryCategoryOf(t *testing.T) { t.Parallel() window := &selfDescribedFailure{ category: "proactive_window_closed", channel: "weixin-ilink", text: "weixin_ilink: proactive push outside 24h window", } tests := []struct { name string err error want DeliveryCategory }{ {name: "没有失败就没有码", err: nil, want: ""}, {name: "适配器自报的码被采信", err: window, want: "proactive_window_closed"}, {name: "多气泡包装不藏自报码", err: fmt.Errorf("channelbridge: send bubble #1 of 2: %w", window), want: "proactive_window_closed"}, {name: "桥接器包装不藏自报码", err: fmt.Errorf("channelbridge: send proactive via %q: %w", "weixin-ilink", window), want: "proactive_window_closed"}, {name: "未连接", err: ErrNotConnected, want: CategoryNotConnected}, {name: "不支持", err: fmt.Errorf("adapter: send: %w", ErrUnsupported), want: CategoryUnsupported}, {name: "已关闭", err: ErrClosed, want: CategoryClosed}, {name: "无自报的裸错误落兜底", err: errors.New("boom"), want: CategoryFailed}, {name: "自报空码落兜底(不编更精确的码)", err: &selfDescribedFailure{text: "boom"}, want: CategoryFailed}, } for _, tc := range tests { tc := tc t.Run(tc.name, func(t *testing.T) { t.Parallel() if got := DeliveryCategoryOf(tc.err); got != tc.want { t.Fatalf("DeliveryCategoryOf(%v) = %q, want %q", tc.err, got, tc.want) } }) } } // TestDeliveryChannelOf 锁定渠道自报口径:只有自报才有渠道名,未自报是**空串** // (= 不知道),绝不替它猜一个平台名。 func TestDeliveryChannelOf(t *testing.T) { t.Parallel() window := &selfDescribedFailure{category: "proactive_window_closed", channel: "weixin-ilink", text: "closed"} if got := DeliveryChannelOf(fmt.Errorf("channelbridge: send bubble #1 of 2: %w", window)); got != "weixin-ilink" { t.Fatalf("DeliveryChannelOf(wrapped) = %q, want weixin-ilink", got) } if got := DeliveryChannelOf(fmt.Errorf("channelbridge: no connected owner IM channel for proactive push")); got != "" { t.Fatalf("DeliveryChannelOf(unreported) = %q, want empty (unknown)", got) } if got := DeliveryChannelOf(nil); got != "" { t.Fatalf("DeliveryChannelOf(nil) = %q, want empty", got) } if got := DeliveryChannelOf(ErrNotConnected); got != "" { t.Fatalf("DeliveryChannelOf(ErrNotConnected) = %q, want empty", got) } }