hyapp-server/pkg/rocketmqx/rocketmqx_test.go

95 lines
3.7 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package rocketmqx
import (
"bytes"
"context"
"encoding/json"
"errors"
"strings"
"testing"
"hyapp/pkg/logx"
"github.com/apache/rocketmq-client-go/v2/consumer"
"github.com/apache/rocketmq-client-go/v2/primitive"
)
type fakePushConsumer struct {
callback func(context.Context, ...*primitive.MessageExt) (consumer.ConsumeResult, error)
}
func (f *fakePushConsumer) Start() error { return nil }
func (f *fakePushConsumer) Shutdown() error { return nil }
func (f *fakePushConsumer) Subscribe(_ string, _ consumer.MessageSelector, callback func(context.Context, ...*primitive.MessageExt) (consumer.ConsumeResult, error)) error {
f.callback = callback
return nil
}
func (f *fakePushConsumer) Unsubscribe(string) error { return nil }
func (f *fakePushConsumer) Suspend() {}
func (f *fakePushConsumer) Resume() {}
func (f *fakePushConsumer) GetOffsetDiffMap() map[string]int64 { return nil }
func TestSubscribeHandlerErrorReturnsRetryLaterWithoutSDKError(t *testing.T) {
var output bytes.Buffer
if err := logx.Init(logx.Config{Service: "rocketmqx-test", Env: "test", Format: "json", Output: &output}); err != nil {
t.Fatalf("init test logger: %v", err)
}
client := &fakePushConsumer{}
consumerWrapper := &Consumer{client: client, group: "group-safe-log"}
handlerErr := errors.New("projection unavailable")
if err := consumerWrapper.Subscribe("topic-safe-log", "tag-safe-log", func(context.Context, ConsumedMessage) error {
return handlerErr
}); err != nil {
t.Fatalf("subscribe: %v", err)
}
if client.callback == nil {
t.Fatal("subscribe callback was not captured")
}
extended := &primitive.MessageExt{MsgId: "msg-safe-log", ReconsumeTimes: 3}
// 直接初始化嵌入的 Message避免复制其 RWMutexQueue 仍故意保持 nil确保日志路径
// 一旦把 MessageExt 交给 String 格式化就会在测试中立即暴露。
extended.Topic = "topic-safe-log"
extended.Body = []byte("sensitive-body-must-not-be-logged")
extended.WithTag("tag-safe-log")
extended.WithKeys([]string{"event-safe-log"})
extended.WithProperty("private-property", "secret-value-must-not-be-logged")
// Queue 故意保持 nilMessageExt.String 会解引用 Queue 并 panic。callback 能完成并写日志,
// 因而同时锁定包装层只记录复制后的安全标量,绝不把 MessageExt 交给格式化器。
result, callbackErr := client.callback(context.Background(), extended)
if result != consumer.ConsumeRetryLater {
t.Fatalf("consume result = %v, want ConsumeRetryLater", result)
}
if callbackErr != nil {
t.Fatalf("callback error = %v, want nil so SDK does not format the shared message batch", callbackErr)
}
rawLog := strings.TrimSpace(output.String())
var record map[string]any
if err := json.Unmarshal([]byte(rawLog), &record); err != nil {
t.Fatalf("decode safe consume log %q: %v", rawLog, err)
}
for key, want := range map[string]string{
"msg": "rocketmq_consume_handler_failed",
"consumer_group": "group-safe-log",
"topic": "topic-safe-log",
"tag": "tag-safe-log",
"keys": "event-safe-log",
"msg_id": "msg-safe-log",
"error": handlerErr.Error(),
} {
if got := record[key]; got != want {
t.Errorf("log field %s = %#v, want %q", key, got, want)
}
}
if got := record["reconsume_times"]; got != float64(3) {
t.Errorf("log field reconsume_times = %#v, want 3", got)
}
for _, forbidden := range []string{"sensitive-body-must-not-be-logged", "private-property", "secret-value-must-not-be-logged"} {
if strings.Contains(rawLog, forbidden) {
t.Errorf("safe consume log leaked forbidden message data %q: %s", forbidden, rawLog)
}
}
}