| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105 |
- package loaders
- import (
- "context"
- "errors"
- "testing"
- "time"
- "github.com/stretchr/testify/assert"
- "github.com/stretchr/testify/require"
- )
- // TC-1112: parent 取消后 detached ctx 仍存活,到 3s 硬超时才 Done。
- func TestDetachCacheCleanCtx_ParentCancelDoesNotPropagate(t *testing.T) {
- parent, cancel := context.WithCancel(context.Background())
- cleanCtx, cleanCancel := DetachCacheCleanCtx(parent)
- defer cleanCancel()
- cancel()
- assert.NoError(t, cleanCtx.Err(), "parent 取消后 detached ctx 不应提前终止")
- select {
- case <-cleanCtx.Done():
- t.Fatal("parent cancel 被传播到了 detached ctx")
- case <-time.After(100 * time.Millisecond):
- }
- }
- // TC-1113: deadline 必须落在 [now+2.5s, now+3.5s]。
- func TestDetachCacheCleanCtx_HasThreeSecondDeadline(t *testing.T) {
- parent := context.Background()
- before := time.Now()
- cleanCtx, cancel := DetachCacheCleanCtx(parent)
- defer cancel()
- deadline, ok := cleanCtx.Deadline()
- require.True(t, ok, "detached ctx 必须挂 timeout deadline")
- lower := before.Add(2500 * time.Millisecond)
- upper := before.Add(3500 * time.Millisecond)
- assert.WithinRange(t, deadline, lower, upper, "deadline 必须在 ~3s 窗口内")
- }
- // TC-1114: parent 的 Value 透传不被剥离。
- func TestDetachCacheCleanCtx_PreservesValues(t *testing.T) {
- type ctxKey struct{ name string }
- k := ctxKey{name: "trace"}
- parent := context.WithValue(context.Background(), k, "v1")
- cleanCtx, cancel := DetachCacheCleanCtx(parent)
- defer cancel()
- assert.Equal(t, "v1", cleanCtx.Value(k), "trace / tenant 等 Value 必须透传")
- }
- // TC-1115: isCtxCanceledErr 分类口径。
- func TestIsCtxCanceledErr_Classification(t *testing.T) {
- assert.True(t, isCtxCanceledErr(context.Canceled))
- assert.True(t, isCtxCanceledErr(context.DeadlineExceeded))
- assert.True(t, isCtxCanceledErr(
- // 包装过一层仍应识别
- wrapErr(context.Canceled, "clean userDetails: "),
- ))
- assert.False(t, isCtxCanceledErr(errors.New("redis down")))
- assert.False(t, isCtxCanceledErr(nil))
- }
- type wrappedErr struct {
- prefix string
- inner error
- }
- func (w *wrappedErr) Error() string { return w.prefix + w.inner.Error() }
- func (w *wrappedErr) Unwrap() error { return w.inner }
- func wrapErr(inner error, prefix string) error { return &wrappedErr{prefix: prefix, inner: inner} }
- // TC-1116: nil 错误时 logCacheInvalidationErr 早退,不触发任何日志写入。
- func TestLogCacheInvalidationErr_NilShortCircuit(t *testing.T) {
- // 无返回值可断言,此用例仅需保证不 panic / 不阻塞即可。
- // 若将来接入 logx buffer,可在此注入 TestCollector 断言"零条日志"。
- done := make(chan struct{})
- go func() {
- logCacheInvalidationErr(context.Background(), "scope", "detail", nil)
- close(done)
- }()
- select {
- case <-done:
- case <-time.After(200 * time.Millisecond):
- t.Fatal("logCacheInvalidationErr(nil) 不应阻塞")
- }
- }
- // TC-1116 附加:ctx 取消 / 普通错误两条分支都要走通,不允许 panic。
- func TestLogCacheInvalidationErr_DoesNotPanic(t *testing.T) {
- assert.NotPanics(t, func() {
- logCacheInvalidationErr(context.Background(), "scope", "detail", context.Canceled)
- })
- assert.NotPanics(t, func() {
- logCacheInvalidationErr(context.Background(), "scope", "detail", context.DeadlineExceeded)
- })
- assert.NotPanics(t, func() {
- logCacheInvalidationErr(context.Background(), "scope", "detail", errors.New("redis down"))
- })
- }
|