添加完善部分测试用例
This commit is contained in:
+158
@@ -260,3 +260,161 @@ func TestClusterMultiKeyLuaKeysShareSlot(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestInitCluster_NilRedis(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
_, err := timerx.InitCluster(ctx, nil, "test")
|
||||
if err == nil {
|
||||
t.Error("expected error for nil redis")
|
||||
}
|
||||
}
|
||||
|
||||
func TestClusterKeyPrefixSanitization(t *testing.T) {
|
||||
// 验证 sanitizeKeyPrefix 在 key 格式中的作用
|
||||
// keyPrefix 中含有 {} 时应该被替换为 _
|
||||
expectedHashTag := "{app_env_test}"
|
||||
|
||||
zsetKey := "timer:" + expectedHashTag + ":cluster_zset"
|
||||
listKey := "timer:" + expectedHashTag + ":cluster_list"
|
||||
|
||||
tag1 := extractHashTag(zsetKey)
|
||||
tag2 := extractHashTag(listKey)
|
||||
|
||||
if tag1 != "app_env_test" {
|
||||
t.Errorf("unexpected hash tag: %q", tag1)
|
||||
}
|
||||
if tag1 != tag2 {
|
||||
t.Errorf("hash tags differ: %q != %q", tag1, tag2)
|
||||
}
|
||||
}
|
||||
|
||||
func TestClusterEverySpace_InvalidDuration(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
redis := redisInit()
|
||||
defer redis.Close()
|
||||
|
||||
cluster, err := timerx.InitCluster(ctx, redis, "test_invalid")
|
||||
if err != nil {
|
||||
t.Skipf("redis not available: %v", err)
|
||||
}
|
||||
defer cluster.Stop()
|
||||
|
||||
err = cluster.EverySpace(ctx, "testNegative", -1*time.Second,
|
||||
func(ctx context.Context, data interface{}) error { return nil }, nil)
|
||||
if err == nil {
|
||||
t.Error("expected error for negative duration")
|
||||
}
|
||||
}
|
||||
|
||||
func TestClusterAddDuplicateTaskId(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
redis := redisInit()
|
||||
defer redis.Close()
|
||||
|
||||
cluster, err := timerx.InitCluster(ctx, redis, "test_dup")
|
||||
if err != nil {
|
||||
t.Skipf("redis not available: %v", err)
|
||||
}
|
||||
defer cluster.Stop()
|
||||
|
||||
callback := func(ctx context.Context, data interface{}) error { return nil }
|
||||
err = cluster.EveryMinute(ctx, "dup_task", 0, callback, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("first add should succeed: %v", err)
|
||||
}
|
||||
|
||||
err = cluster.EveryMinute(ctx, "dup_task", 0, callback, nil)
|
||||
if err != timerx.ErrTaskIdExists {
|
||||
t.Errorf("expected ErrTaskIdExists, got %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestClusterEveryMonth_InvalidDay(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
redis := redisInit()
|
||||
defer redis.Close()
|
||||
|
||||
cluster, err := timerx.InitCluster(ctx, redis, "test_inv_day")
|
||||
if err != nil {
|
||||
t.Skipf("redis not available: %v", err)
|
||||
}
|
||||
defer cluster.Stop()
|
||||
|
||||
callback := func(ctx context.Context, data interface{}) error { return nil }
|
||||
err = cluster.EveryMonth(ctx, "test", 32, 0, 0, 0, callback, nil)
|
||||
if err != timerx.ErrMonthDay {
|
||||
t.Errorf("expected ErrMonthDay, got %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestClusterEveryWeek_InvalidWeekday(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
redis := redisInit()
|
||||
defer redis.Close()
|
||||
|
||||
cluster, err := timerx.InitCluster(ctx, redis, "test_inv_wd")
|
||||
if err != nil {
|
||||
t.Skipf("redis not available: %v", err)
|
||||
}
|
||||
defer cluster.Stop()
|
||||
|
||||
callback := func(ctx context.Context, data interface{}) error { return nil }
|
||||
err = cluster.EveryWeek(ctx, "test", 7, 0, 0, 0, callback, nil)
|
||||
if err != timerx.ErrWeekday {
|
||||
t.Errorf("expected ErrWeekday, got %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestClusterEveryDay_InvalidHour(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
redis := redisInit()
|
||||
defer redis.Close()
|
||||
|
||||
cluster, err := timerx.InitCluster(ctx, redis, "test_inv_h")
|
||||
if err != nil {
|
||||
t.Skipf("redis not available: %v", err)
|
||||
}
|
||||
defer cluster.Stop()
|
||||
|
||||
callback := func(ctx context.Context, data interface{}) error { return nil }
|
||||
err = cluster.EveryDay(ctx, "test", 24, 0, 0, callback, nil)
|
||||
if err != timerx.ErrHour {
|
||||
t.Errorf("expected ErrHour, got %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestClusterEveryHour_InvalidMinute(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
redis := redisInit()
|
||||
defer redis.Close()
|
||||
|
||||
cluster, err := timerx.InitCluster(ctx, redis, "test_inv_m")
|
||||
if err != nil {
|
||||
t.Skipf("redis not available: %v", err)
|
||||
}
|
||||
defer cluster.Stop()
|
||||
|
||||
callback := func(ctx context.Context, data interface{}) error { return nil }
|
||||
err = cluster.EveryHour(ctx, "test", 60, 0, callback, nil)
|
||||
if err != timerx.ErrMinute {
|
||||
t.Errorf("expected ErrMinute, got %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestClusterEveryMinute_InvalidSecond(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
redis := redisInit()
|
||||
defer redis.Close()
|
||||
|
||||
cluster, err := timerx.InitCluster(ctx, redis, "test_inv_s")
|
||||
if err != nil {
|
||||
t.Skipf("redis not available: %v", err)
|
||||
}
|
||||
defer cluster.Stop()
|
||||
|
||||
callback := func(ctx context.Context, data interface{}) error { return nil }
|
||||
err = cluster.EveryMinute(ctx, "test", 60, callback, nil)
|
||||
if err != timerx.ErrSecond {
|
||||
t.Errorf("expected ErrSecond, got %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
+27
-5
@@ -28,8 +28,8 @@ func main() {
|
||||
// re()
|
||||
// d()
|
||||
// cluster()
|
||||
// once()
|
||||
single()
|
||||
once()
|
||||
// single()
|
||||
// prioritys()
|
||||
|
||||
select {}
|
||||
@@ -89,10 +89,32 @@ func once() {
|
||||
}
|
||||
// dy, _ := json.Marshal(d)
|
||||
|
||||
err = one.Create("test", "test3", 1*time.Second, d)
|
||||
err = one.Create(ctx, "test", "test3", 1*time.Second, d)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
}
|
||||
|
||||
t := time.Now()
|
||||
|
||||
fmt.Println(t.Format(time.DateTime))
|
||||
|
||||
ts := []time.Time{
|
||||
t.Add(time.Second * 11),
|
||||
t.Add(time.Second * 12),
|
||||
t.Add(time.Second * 13),
|
||||
t.Add(time.Second * 14),
|
||||
t.Add(time.Second * 15),
|
||||
t.Add(time.Second * 16),
|
||||
t.Add(time.Second * 17),
|
||||
t.Add(time.Second * 18),
|
||||
t.Add(time.Second * 19),
|
||||
t.Add(time.Second * 20),
|
||||
t.Add(time.Second * 21),
|
||||
t.Add(time.Second * 22),
|
||||
}
|
||||
|
||||
one.CreateByList(ctx, "test", "CreateByTime", ts, d)
|
||||
|
||||
// d = OnceData{
|
||||
// Num: 4,
|
||||
// }
|
||||
@@ -124,9 +146,9 @@ func (l OnceWorker) Worker(ctx context.Context, taskType timerx.OnceTaskType, ta
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
file.WriteString(fmt.Sprintf("执行时间:%s\n", time.Now().Format("2006-01-02 15:04:05")))
|
||||
file.WriteString(fmt.Sprintf("taskType:%s taskId:%s 执行时间:%s\n", taskType, taskId, time.Now().Format("2006-01-02 15:04:05")))
|
||||
|
||||
fmt.Println("执行时间:", time.Now().Format("2006-01-02 15:04:05"))
|
||||
fmt.Println(taskType, ":", taskId, "执行时间:", time.Now().Format("2006-01-02 15:04:05"))
|
||||
// fmt.Println(taskType, taskId)
|
||||
|
||||
// fmt.Printf("原来的参数:%+v %T\n", attachData, attachData)
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
package heartbeat
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/redis/go-redis/v9"
|
||||
)
|
||||
|
||||
func TestInitHeartBeat_NilRedis(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
_, err := InitHeartBeat(ctx, nil, "test")
|
||||
if err == nil {
|
||||
t.Error("expected error for nil redis")
|
||||
}
|
||||
}
|
||||
|
||||
func TestHeartbeatKeyFormat(t *testing.T) {
|
||||
key := "timer:{myapp}:heartbeat_cluster"
|
||||
|
||||
if !strings.Contains(key, "{myapp}") {
|
||||
t.Errorf("heartbeat key missing hash tag: %s", key)
|
||||
}
|
||||
|
||||
tag := extractHashTag(key)
|
||||
if tag != "myapp" {
|
||||
t.Errorf("unexpected hash tag: %q", tag)
|
||||
}
|
||||
}
|
||||
|
||||
func TestHeartbeatKeyFormat_Once(t *testing.T) {
|
||||
key := "timer:{app_v2}:heartbeat_once"
|
||||
|
||||
if !strings.Contains(key, "{app_v2}") {
|
||||
t.Errorf("heartbeat key missing hash tag: %s", key)
|
||||
}
|
||||
|
||||
tag := extractHashTag(key)
|
||||
if tag != "app_v2" {
|
||||
t.Errorf("unexpected hash tag: %q", tag)
|
||||
}
|
||||
}
|
||||
|
||||
func extractHashTag(key string) string {
|
||||
start := strings.Index(key, "{")
|
||||
end := strings.Index(key, "}")
|
||||
if start >= 0 && end > start {
|
||||
return key[start+1 : end]
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
var _ redis.UniversalClient
|
||||
@@ -0,0 +1,43 @@
|
||||
package timerx
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestSanitizeKeyPrefix(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
input string
|
||||
expected string
|
||||
}{
|
||||
{"normal prefix", "myapp", "myapp"},
|
||||
{"with left brace", "my{app", "my_app"},
|
||||
{"with right brace", "my}app", "my_app"},
|
||||
{"with both braces", "my{app}env", "my_app_env"},
|
||||
{"only left brace", "{", "_"},
|
||||
{"only right brace", "}", "_"},
|
||||
{"empty string", "", ""},
|
||||
{"multiple braces", "a{b{c}d}e", "a_b_c_d_e"},
|
||||
{"braces at edges", "{prefix}", "_prefix_"},
|
||||
{"already clean", "clean_key_123", "clean_key_123"},
|
||||
{"special chars no braces", "my:app/ver", "my:app/ver"},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
got := sanitizeKeyPrefix(tt.input)
|
||||
if got != tt.expected {
|
||||
t.Errorf("sanitizeKeyPrefix(%q) = %q, want %q", tt.input, got, tt.expected)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestSanitizeKeyPrefixConsistency(t *testing.T) {
|
||||
// 两个仅含 {} 不同的 keyPrefix 应该 sanitize 成相同值
|
||||
a := sanitizeKeyPrefix("foo{bar")
|
||||
b := sanitizeKeyPrefix("foo_bar")
|
||||
if a != b {
|
||||
t.Errorf("expected consistent sanitization: %q != %q", a, b)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
package leader
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/redis/go-redis/v9"
|
||||
)
|
||||
|
||||
func TestInitLeader_NilRedis(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
_, err := InitLeader(ctx, nil, "test")
|
||||
if err == nil {
|
||||
t.Error("expected error for nil redis")
|
||||
}
|
||||
}
|
||||
|
||||
func TestLeaderKeyFormat(t *testing.T) {
|
||||
keyPrefix := "myapp"
|
||||
source := "cluster"
|
||||
|
||||
lockKey := "timer:{myapp}:leader_lock_cluster"
|
||||
leaderKey := "timer:{myapp}:leader_cluster"
|
||||
|
||||
if !strings.Contains(lockKey, "{myapp}") {
|
||||
t.Errorf("lockKey missing hash tag: %s", lockKey)
|
||||
}
|
||||
if !strings.Contains(leaderKey, "{myapp}") {
|
||||
t.Errorf("leaderKey missing hash tag: %s", leaderKey)
|
||||
}
|
||||
|
||||
_ = keyPrefix
|
||||
_ = source
|
||||
}
|
||||
|
||||
func TestLeaderKeyFormat_Once(t *testing.T) {
|
||||
lockKey := "timer:{app}:leader_lock_once"
|
||||
leaderKey := "timer:{app}:leader_once"
|
||||
|
||||
tag1 := extractHashTag(lockKey)
|
||||
tag2 := extractHashTag(leaderKey)
|
||||
|
||||
if tag1 != "app" {
|
||||
t.Errorf("unexpected hash tag: %q", tag1)
|
||||
}
|
||||
if tag1 != tag2 {
|
||||
t.Errorf("hash tags differ: %q != %q", tag1, tag2)
|
||||
}
|
||||
}
|
||||
|
||||
func extractHashTag(key string) string {
|
||||
start := strings.Index(key, "{")
|
||||
end := strings.Index(key, "}")
|
||||
if start >= 0 && end > start {
|
||||
return key[start+1 : end]
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// ensure redis import used for InitLeader type checking
|
||||
var _ redis.UniversalClient
|
||||
+100
-1
@@ -326,7 +326,7 @@ func TestCalculateNextInterval(t *testing.T) {
|
||||
IntervalTime: 24 * time.Hour,
|
||||
},
|
||||
currentTime: now,
|
||||
expected: time.Date(2023, 6, 16, 0, 0, 0, 0, time.UTC),
|
||||
expected: createTime.Add(24 * time.Hour),
|
||||
},
|
||||
}
|
||||
|
||||
@@ -857,3 +857,102 @@ func TestGetNextTime_Timezone(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetCronSche_NilParser(t *testing.T) {
|
||||
_, err := GetCronSche("* * * * *", nil)
|
||||
if err != ErrCronParser {
|
||||
t.Errorf("expected ErrCronParser, got %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetCronSche_EmptyExpression(t *testing.T) {
|
||||
parser := cron.NewParser(cron.Minute | cron.Hour | cron.Dom | cron.Month | cron.Dow)
|
||||
_, err := GetCronSche("", &parser)
|
||||
if err != ErrCronExpression {
|
||||
t.Errorf("expected ErrCronExpression, got %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetCronSche_InvalidExpression(t *testing.T) {
|
||||
parser := cron.NewParser(cron.Minute | cron.Hour | cron.Dom | cron.Month | cron.Dow)
|
||||
_, err := GetCronSche("invalid cron", &parser)
|
||||
if err == nil {
|
||||
t.Error("expected error for invalid cron expression")
|
||||
}
|
||||
}
|
||||
|
||||
func TestJobTypeEverySecond_Unhandled(t *testing.T) {
|
||||
job := JobData{
|
||||
JobType: JobTypeEverySecond,
|
||||
}
|
||||
_, err := GetNextTime(time.Now(), job)
|
||||
if err == nil {
|
||||
t.Error("expected error for unhandled JobTypeEverySecond")
|
||||
}
|
||||
}
|
||||
|
||||
func TestValidateJobData_CronNilSchedule(t *testing.T) {
|
||||
job := JobData{
|
||||
JobType: JobTypeCron,
|
||||
CronExpression: "* * * * *",
|
||||
CronSchedule: nil,
|
||||
}
|
||||
err := validateJobData(job)
|
||||
if err != ErrCronParser {
|
||||
t.Errorf("expected ErrCronParser, got %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestValidateJobData_CronEmptyExpression(t *testing.T) {
|
||||
job := JobData{
|
||||
JobType: JobTypeCron,
|
||||
CronExpression: "",
|
||||
CronSchedule: nil,
|
||||
}
|
||||
err := validateJobData(job)
|
||||
if err != ErrCronExpression {
|
||||
t.Errorf("expected ErrCronExpression, got %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestValidateJobData_IntervalNegative(t *testing.T) {
|
||||
job := JobData{
|
||||
JobType: JobTypeInterval,
|
||||
IntervalTime: 0,
|
||||
BaseTime: time.Now(),
|
||||
}
|
||||
err := validateJobData(job)
|
||||
if err != ErrIntervalTime {
|
||||
t.Errorf("expected ErrIntervalTime, got %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestValidateJobData_IntervalZeroBaseTime(t *testing.T) {
|
||||
job := JobData{
|
||||
JobType: JobTypeInterval,
|
||||
IntervalTime: time.Second,
|
||||
BaseTime: time.Time{},
|
||||
}
|
||||
err := validateJobData(job)
|
||||
if err != ErrBaseTime {
|
||||
t.Errorf("expected ErrBaseTime, got %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCalculateNextInterval_ExactBoundary(t *testing.T) {
|
||||
baseTime := time.Date(2025, 1, 1, 0, 0, 0, 0, time.UTC)
|
||||
now := baseTime.Add(2 * time.Second)
|
||||
job := JobData{
|
||||
JobType: JobTypeInterval,
|
||||
BaseTime: baseTime,
|
||||
IntervalTime: time.Second,
|
||||
}
|
||||
next, err := calculateNextInterval(now, job)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
expected := baseTime.Add(3 * time.Second)
|
||||
if !next.Equal(expected) {
|
||||
t.Errorf("expected %v, got %v", expected, next)
|
||||
}
|
||||
}
|
||||
|
||||
+112
@@ -1,6 +1,8 @@
|
||||
package timerx
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
@@ -143,6 +145,116 @@ func extractHashTagForOnce(key string) string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func TestInitOnce_NilRedis(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
_, err := InitOnce(ctx, nil, "test", nil)
|
||||
if err == nil {
|
||||
t.Error("expected error for nil redis")
|
||||
}
|
||||
}
|
||||
|
||||
func TestInitOnce_NilCallback(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
// nil worker callback should be rejected before redis check
|
||||
_, err := InitOnce(ctx, nil, "test", nil)
|
||||
if err == nil {
|
||||
t.Error("expected error for nil callback")
|
||||
}
|
||||
}
|
||||
|
||||
func TestOnceErrorVariables(t *testing.T) {
|
||||
// 验证 sentinel errors 可用且可比较
|
||||
if ErrExecuteTime == nil {
|
||||
t.Error("ErrExecuteTime should not be nil")
|
||||
}
|
||||
if ErrRunCount == nil {
|
||||
t.Error("ErrRunCount should not be nil")
|
||||
}
|
||||
if ErrDelayTime == nil {
|
||||
t.Error("ErrDelayTime should not be nil")
|
||||
}
|
||||
if ErrTaskExists == nil {
|
||||
t.Error("ErrTaskExists should not be nil")
|
||||
}
|
||||
if ErrTaskIdExists == nil {
|
||||
t.Error("ErrTaskIdExists should not be nil")
|
||||
}
|
||||
}
|
||||
|
||||
func TestClusterErrorVariables(t *testing.T) {
|
||||
if ErrMonthDay == nil {
|
||||
t.Error("ErrMonthDay should not be nil")
|
||||
}
|
||||
if ErrWeekday == nil {
|
||||
t.Error("ErrWeekday should not be nil")
|
||||
}
|
||||
if ErrHour == nil {
|
||||
t.Error("ErrHour should not be nil")
|
||||
}
|
||||
if ErrMinute == nil {
|
||||
t.Error("ErrMinute should not be nil")
|
||||
}
|
||||
if ErrSecond == nil {
|
||||
t.Error("ErrSecond should not be nil")
|
||||
}
|
||||
if ErrIntervalTime == nil {
|
||||
t.Error("ErrIntervalTime should not be nil")
|
||||
}
|
||||
if ErrBaseTime == nil {
|
||||
t.Error("ErrBaseTime should not be nil")
|
||||
}
|
||||
if ErrCronExpression == nil {
|
||||
t.Error("ErrCronExpression should not be nil")
|
||||
}
|
||||
if ErrCronParser == nil {
|
||||
t.Error("ErrCronParser should not be nil")
|
||||
}
|
||||
}
|
||||
|
||||
func TestOnceKeySeparator(t *testing.T) {
|
||||
// buildRedisKey 使用 keySeparator
|
||||
o := &Once{keySeparator: "[:]"}
|
||||
key := o.buildRedisKey("normal", "task_1")
|
||||
if key != "normal[:]task_1" {
|
||||
t.Errorf("unexpected key: %s", key)
|
||||
}
|
||||
|
||||
// parseRedisKey
|
||||
tp, id, err := o.parseRedisKey(key)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
if tp != "normal" || id != "task_1" {
|
||||
t.Errorf("unexpected parse result: %s, %s", tp, id)
|
||||
}
|
||||
|
||||
// custom separator
|
||||
o = &Once{keySeparator: ":"}
|
||||
key = o.buildRedisKey("urgent", "id123")
|
||||
if key != "urgent:id123" {
|
||||
t.Errorf("unexpected key with custom separator: %s", key)
|
||||
}
|
||||
}
|
||||
|
||||
func TestOnceSanitizedKeyPrefix(t *testing.T) {
|
||||
// 模拟 sanitizeKeyPrefix 对 Once key 的影响
|
||||
raw := "app{v1}"
|
||||
sanitized := sanitizeKeyPrefix(raw)
|
||||
if sanitized != "app_v1_" {
|
||||
t.Errorf("unexpected sanitized prefix: %s", sanitized)
|
||||
}
|
||||
|
||||
dataKey := fmt.Sprintf("timer:{%s}:once_data:%s", sanitized, "type[:]id")
|
||||
zsetKey := fmt.Sprintf("timer:{%s}:once_zset", sanitized)
|
||||
|
||||
if !strings.Contains(dataKey, "{app_v1_}") {
|
||||
t.Errorf("data key missing expected hash tag: %s", dataKey)
|
||||
}
|
||||
if !strings.Contains(zsetKey, "{app_v1_}") {
|
||||
t.Errorf("zset key missing expected hash tag: %s", zsetKey)
|
||||
}
|
||||
}
|
||||
|
||||
func newDefaultLoggerForTest() logger.Logger {
|
||||
return logger.NewLogger()
|
||||
}
|
||||
|
||||
+182
@@ -0,0 +1,182 @@
|
||||
package timerx
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/yuninks/timerx/logger"
|
||||
)
|
||||
|
||||
func TestDefaultOptions(t *testing.T) {
|
||||
o := defaultOptions()
|
||||
|
||||
if o.timeout != time.Hour {
|
||||
t.Errorf("default timeout = %v, want %v", o.timeout, time.Hour)
|
||||
}
|
||||
if o.location != time.Local {
|
||||
t.Errorf("default location = %v, want Local", o.location)
|
||||
}
|
||||
if o.priorityType != priorityTypeNone {
|
||||
t.Errorf("default priorityType = %v, want %v", o.priorityType, priorityTypeNone)
|
||||
}
|
||||
if o.batchSize != 100 {
|
||||
t.Errorf("default batchSize = %d, want 100", o.batchSize)
|
||||
}
|
||||
if o.maxRunCount != 0 {
|
||||
t.Errorf("default maxRunCount = %d, want 0", o.maxRunCount)
|
||||
}
|
||||
if o.maxWorkers != 100 {
|
||||
t.Errorf("default maxWorkers = %d, want 100", o.maxWorkers)
|
||||
}
|
||||
if o.cronParser == nil {
|
||||
t.Error("default cronParser should not be nil")
|
||||
}
|
||||
}
|
||||
|
||||
func TestNewOptions(t *testing.T) {
|
||||
o := newOptions()
|
||||
// newOptions 应用默认值
|
||||
if o.batchSize != 100 {
|
||||
t.Errorf("expected default batchSize 100, got %d", o.batchSize)
|
||||
}
|
||||
}
|
||||
|
||||
func TestNewEmptyOptions(t *testing.T) {
|
||||
o := newEmptyOptions()
|
||||
// newEmptyOptions 返回零值,不应用默认值
|
||||
if o.batchSize != 0 {
|
||||
t.Errorf("expected empty batchSize 0, got %d", o.batchSize)
|
||||
}
|
||||
if o.maxWorkers != 0 {
|
||||
t.Errorf("expected empty maxWorkers 0, got %d", o.maxWorkers)
|
||||
}
|
||||
}
|
||||
|
||||
func TestWithBatchSize(t *testing.T) {
|
||||
tests := []struct {
|
||||
input int
|
||||
expected int
|
||||
}{
|
||||
{0, 1},
|
||||
{1, 1},
|
||||
{-1, 1},
|
||||
{-100, 1},
|
||||
{50, 50},
|
||||
{1000, 1000},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
o := newOptions(WithBatchSize(tt.input))
|
||||
if o.batchSize != tt.expected {
|
||||
t.Errorf("WithBatchSize(%d) = %d, want %d", tt.input, o.batchSize, tt.expected)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestWithMaxWorkers(t *testing.T) {
|
||||
tests := []struct {
|
||||
input int
|
||||
expected int
|
||||
}{
|
||||
{0, 100},
|
||||
{-1, 100},
|
||||
{-100, 100},
|
||||
{1, 1},
|
||||
{50, 50},
|
||||
{200, 200},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
o := newOptions(WithMaxWorkers(tt.input))
|
||||
if o.maxWorkers != tt.expected {
|
||||
t.Errorf("WithMaxWorkers(%d) = %d, want %d", tt.input, o.maxWorkers, tt.expected)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestWithMaxRetryCount(t *testing.T) {
|
||||
tests := []struct {
|
||||
input int
|
||||
expected int
|
||||
}{
|
||||
{-1, 0},
|
||||
{-100, 0},
|
||||
{0, 0},
|
||||
{3, 3},
|
||||
{100, 100},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
o := newOptions(WithMaxRetryCount(tt.input))
|
||||
if o.maxRunCount != tt.expected {
|
||||
t.Errorf("WithMaxRetryCount(%d) = %d, want %d", tt.input, o.maxRunCount, tt.expected)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestWithTimeout(t *testing.T) {
|
||||
d := 30 * time.Second
|
||||
o := newOptions(WithTimeout(d))
|
||||
if o.timeout != d {
|
||||
t.Errorf("WithTimeout(%v) = %v", d, o.timeout)
|
||||
}
|
||||
}
|
||||
|
||||
func TestWithPriority(t *testing.T) {
|
||||
o := newOptions(WithPriority(42))
|
||||
if o.priorityType != priorityTypePriority {
|
||||
t.Errorf("priorityType = %v, want %v", o.priorityType, priorityTypePriority)
|
||||
}
|
||||
if o.priorityVal != 42 {
|
||||
t.Errorf("priorityVal = %d, want 42", o.priorityVal)
|
||||
}
|
||||
}
|
||||
|
||||
func TestWithPriorityByVersion(t *testing.T) {
|
||||
o := newOptions(WithPriorityByVersion("v2.3.4"))
|
||||
if o.priorityType != priorityTypeVersion {
|
||||
t.Errorf("priorityType = %v, want %v", o.priorityType, priorityTypeVersion)
|
||||
}
|
||||
if o.priorityVersion != "v2.3.4" {
|
||||
t.Errorf("priorityVersion = %s, want v2.3.4", o.priorityVersion)
|
||||
}
|
||||
}
|
||||
|
||||
func TestWithLogger(t *testing.T) {
|
||||
original := defaultOptions().logger
|
||||
custom := logger.NewLogger()
|
||||
o := newOptions(WithLogger(custom))
|
||||
// logger 不是 comparable,但地址应该不同
|
||||
if o.logger == nil {
|
||||
t.Error("logger should not be nil")
|
||||
}
|
||||
_ = original
|
||||
}
|
||||
|
||||
func TestWithLocation(t *testing.T) {
|
||||
loc := time.UTC
|
||||
o := newOptions(WithLocation(loc))
|
||||
if o.location != loc {
|
||||
t.Errorf("location = %v, want %v", o.location, loc)
|
||||
}
|
||||
}
|
||||
|
||||
func TestWithCronParserOptions(t *testing.T) {
|
||||
// WithCronParserSecond
|
||||
o := newOptions(WithCronParserSecond())
|
||||
if o.cronParser == nil {
|
||||
t.Error("cronParser should not be nil after WithCronParserSecond")
|
||||
}
|
||||
|
||||
// WithCronParserLinux
|
||||
o = newOptions(WithCronParserLinux())
|
||||
if o.cronParser == nil {
|
||||
t.Error("cronParser should not be nil after WithCronParserLinux")
|
||||
}
|
||||
|
||||
// WithCronParserDescriptor
|
||||
o = newOptions(WithCronParserDescriptor())
|
||||
if o.cronParser == nil {
|
||||
t.Error("cronParser should not be nil after WithCronParserDescriptor")
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user