Files
sub2api/backend/internal/service/timing_wheel_service_test.go
yangjianbo e489996713 test(backend): 补充改动代码单元测试覆盖率至 85%+
新增 48 个测试用例覆盖修复代码的各分支路径:
- subscription_maintenance_queue: nil receiver/task、Stop 幂等、零值参数 (+6)
- billing_service: CalculateCostWithConfig、错误传播、SoraImageCost 等 (+12)
- timing_wheel_service: Schedule/ScheduleRecurring after Stop (+3)
- sora_media_cleanup_service: nil guard、Start/Stop 各分支、timezone (+10)
- sora_gateway_service: normalizeSoraMediaURLs、buildSoraContent 等辅助函数 (+17)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-10 17:52:10 +08:00

182 lines
4.8 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 service
import (
"errors"
"sync/atomic"
"testing"
"time"
"github.com/zeromicro/go-zero/core/collection"
)
func TestNewTimingWheelService_InitFail_NoPanicAndReturnError(t *testing.T) {
original := newTimingWheel
t.Cleanup(func() { newTimingWheel = original })
newTimingWheel = func(_ time.Duration, _ int, _ collection.Execute) (*collection.TimingWheel, error) {
return nil, errors.New("boom")
}
svc, err := NewTimingWheelService()
if err == nil {
t.Fatalf("期望返回 error但得到 nil")
}
if svc != nil {
t.Fatalf("期望返回 nil svc但得到非空")
}
}
func TestNewTimingWheelService_Success(t *testing.T) {
svc, err := NewTimingWheelService()
if err != nil {
t.Fatalf("期望 err 为 nil但得到: %v", err)
}
if svc == nil {
t.Fatalf("期望 svc 非空,但得到 nil")
}
svc.Stop()
}
func TestNewTimingWheelService_ExecuteCallbackRunsFunc(t *testing.T) {
original := newTimingWheel
t.Cleanup(func() { newTimingWheel = original })
var captured collection.Execute
newTimingWheel = func(interval time.Duration, numSlots int, execute collection.Execute) (*collection.TimingWheel, error) {
captured = execute
return original(interval, numSlots, execute)
}
svc, err := NewTimingWheelService()
if err != nil {
t.Fatalf("期望 err 为 nil但得到: %v", err)
}
if captured == nil {
t.Fatalf("期望 captured 非空,但得到 nil")
}
called := false
captured("k", func() { called = true })
if !called {
t.Fatalf("期望 execute 回调触发传入函数执行")
}
svc.Stop()
}
func TestTimingWheelService_Schedule_ExecutesOnce(t *testing.T) {
original := newTimingWheel
t.Cleanup(func() { newTimingWheel = original })
newTimingWheel = func(_ time.Duration, _ int, execute collection.Execute) (*collection.TimingWheel, error) {
return original(10*time.Millisecond, 128, execute)
}
svc, err := NewTimingWheelService()
if err != nil {
t.Fatalf("期望 err 为 nil但得到: %v", err)
}
defer svc.Stop()
ch := make(chan struct{}, 1)
svc.Schedule("once", 30*time.Millisecond, func() { ch <- struct{}{} })
select {
case <-ch:
case <-time.After(500 * time.Millisecond):
t.Fatalf("等待任务执行超时")
}
select {
case <-ch:
t.Fatalf("任务不应重复执行")
case <-time.After(80 * time.Millisecond):
}
}
func TestTimingWheelService_Cancel_PreventsExecution(t *testing.T) {
original := newTimingWheel
t.Cleanup(func() { newTimingWheel = original })
newTimingWheel = func(_ time.Duration, _ int, execute collection.Execute) (*collection.TimingWheel, error) {
return original(10*time.Millisecond, 128, execute)
}
svc, err := NewTimingWheelService()
if err != nil {
t.Fatalf("期望 err 为 nil但得到: %v", err)
}
defer svc.Stop()
ch := make(chan struct{}, 1)
svc.Schedule("cancel", 80*time.Millisecond, func() { ch <- struct{}{} })
svc.Cancel("cancel")
select {
case <-ch:
t.Fatalf("任务已取消,不应执行")
case <-time.After(200 * time.Millisecond):
}
}
func TestTimingWheelService_Schedule_AfterStop_LogsError(t *testing.T) {
svc, err := NewTimingWheelService()
if err != nil {
t.Fatalf("期望 err 为 nil但得到: %v", err)
}
svc.Stop()
// Stop 后调用 Schedule 应走 error 日志路径,不应 panic
svc.Schedule("after-stop", 100*time.Millisecond, func() {
t.Fatal("不应被执行")
})
}
func TestTimingWheelService_ScheduleRecurring_AfterStop_LogsError(t *testing.T) {
svc, err := NewTimingWheelService()
if err != nil {
t.Fatalf("期望 err 为 nil但得到: %v", err)
}
svc.Stop()
// Stop 后调用 ScheduleRecurring 应走 error 日志路径,不应 panic
svc.ScheduleRecurring("after-stop-rec", 100*time.Millisecond, func() {
t.Fatal("不应被执行")
})
}
func TestTimingWheelService_Stop_Idempotent(t *testing.T) {
svc, err := NewTimingWheelService()
if err != nil {
t.Fatalf("期望 err 为 nil但得到: %v", err)
}
svc.Stop()
svc.Stop() // 第二次调用不应 panic
}
func TestTimingWheelService_ScheduleRecurring_ExecutesMultipleTimes(t *testing.T) {
original := newTimingWheel
t.Cleanup(func() { newTimingWheel = original })
newTimingWheel = func(_ time.Duration, _ int, execute collection.Execute) (*collection.TimingWheel, error) {
return original(10*time.Millisecond, 128, execute)
}
svc, err := NewTimingWheelService()
if err != nil {
t.Fatalf("期望 err 为 nil但得到: %v", err)
}
defer svc.Stop()
var count int32
svc.ScheduleRecurring("rec", 30*time.Millisecond, func() { atomic.AddInt32(&count, 1) })
deadline := time.Now().Add(500 * time.Millisecond)
for atomic.LoadInt32(&count) < 2 && time.Now().Before(deadline) {
time.Sleep(10 * time.Millisecond)
}
if atomic.LoadInt32(&count) < 2 {
t.Fatalf("期望周期任务至少执行 2 次,但只执行了 %d 次", atomic.LoadInt32(&count))
}
}