yao/agent/robot/utils/utils_test.go
Max f330a92a2b Enhance Robot Cache Implementation and Documentation
- Marked the Cache Implementation as complete in TODO.md, confirming all tasks are finished with comprehensive integration tests.
- Updated cache.go to provide a thread-safe in-memory cache for Robot instances, improving performance and reliability.
- Enhanced the Load method to include pagination and configurable model name, ensuring efficient data handling.
- Added detailed comments and structured code for better readability and maintainability across the cache implementation.
- Improved validation and error handling in various utility functions to ensure robustness in data processing.
2026-01-14 18:54:18 +08:00

299 lines
7 KiB
Go

package utils_test
import (
"testing"
"time"
"github.com/stretchr/testify/assert"
"github.com/yaoapp/yao/agent/robot/utils"
)
// ID tests
func TestNewID(t *testing.T) {
id1 := utils.NewID()
id2 := utils.NewID()
assert.NotEmpty(t, id1)
assert.NotEmpty(t, id2)
assert.NotEqual(t, id1, id2, "IDs should be unique")
}
func TestNewIDWithPrefix(t *testing.T) {
id := utils.NewIDWithPrefix("exec_")
assert.NotEmpty(t, id)
assert.Contains(t, id, "exec_")
}
// Time tests
func TestParseTime(t *testing.T) {
t.Run("valid time", func(t *testing.T) {
hour, minute, err := utils.ParseTime("14:30")
assert.NoError(t, err)
assert.Equal(t, 14, hour)
assert.Equal(t, 30, minute)
})
t.Run("invalid format", func(t *testing.T) {
_, _, err := utils.ParseTime("14-30")
assert.Error(t, err)
})
t.Run("invalid hour", func(t *testing.T) {
_, _, err := utils.ParseTime("25:30")
assert.Error(t, err)
})
t.Run("invalid minute", func(t *testing.T) {
_, _, err := utils.ParseTime("14:65")
assert.Error(t, err)
})
}
func TestFormatTime(t *testing.T) {
result := utils.FormatTime(9, 5)
assert.Equal(t, "09:05", result)
result = utils.FormatTime(14, 30)
assert.Equal(t, "14:30", result)
}
func TestLoadLocation(t *testing.T) {
t.Run("valid timezone", func(t *testing.T) {
loc := utils.LoadLocation("Asia/Shanghai")
assert.NotNil(t, loc)
assert.Equal(t, "Asia/Shanghai", loc.String())
})
t.Run("empty timezone returns Local", func(t *testing.T) {
loc := utils.LoadLocation("")
assert.Equal(t, time.Local, loc)
})
t.Run("invalid timezone returns Local", func(t *testing.T) {
loc := utils.LoadLocation("Invalid/Timezone")
assert.Equal(t, time.Local, loc)
})
}
func TestParseDuration(t *testing.T) {
t.Run("valid duration", func(t *testing.T) {
dur := utils.ParseDuration("30m", 10*time.Minute)
assert.Equal(t, 30*time.Minute, dur)
})
t.Run("empty returns default", func(t *testing.T) {
dur := utils.ParseDuration("", 10*time.Minute)
assert.Equal(t, 10*time.Minute, dur)
})
t.Run("invalid returns default", func(t *testing.T) {
dur := utils.ParseDuration("invalid", 10*time.Minute)
assert.Equal(t, 10*time.Minute, dur)
})
}
func TestIsTimeMatch(t *testing.T) {
loc := time.UTC
testTime := time.Date(2024, 1, 15, 14, 30, 0, 0, loc)
t.Run("exact match", func(t *testing.T) {
assert.True(t, utils.IsTimeMatch(testTime, "14:30", loc))
})
t.Run("no match", func(t *testing.T) {
assert.False(t, utils.IsTimeMatch(testTime, "14:31", loc))
assert.False(t, utils.IsTimeMatch(testTime, "15:30", loc))
})
t.Run("invalid time format", func(t *testing.T) {
assert.False(t, utils.IsTimeMatch(testTime, "invalid", loc))
})
}
func TestIsDayMatch(t *testing.T) {
monday := time.Date(2024, 1, 15, 10, 0, 0, 0, time.UTC) // Monday
t.Run("match specific day", func(t *testing.T) {
assert.True(t, utils.IsDayMatch(monday, []string{"Mon"}))
})
t.Run("match wildcard", func(t *testing.T) {
assert.True(t, utils.IsDayMatch(monday, []string{"*"}))
})
t.Run("no match", func(t *testing.T) {
assert.False(t, utils.IsDayMatch(monday, []string{"Tue", "Wed"}))
})
t.Run("empty days returns true", func(t *testing.T) {
assert.True(t, utils.IsDayMatch(monday, []string{}))
})
}
// Convert tests
func TestToJSON(t *testing.T) {
data := map[string]interface{}{
"name": "test",
"age": 30,
}
json, err := utils.ToJSON(data)
assert.NoError(t, err)
assert.Contains(t, json, "test")
assert.Contains(t, json, "30")
}
func TestFromJSON(t *testing.T) {
jsonStr := `{"name":"test","age":30}`
var result map[string]interface{}
err := utils.FromJSON(jsonStr, &result)
assert.NoError(t, err)
assert.Equal(t, "test", result["name"])
assert.Equal(t, float64(30), result["age"]) // JSON numbers are float64
}
func TestToMap(t *testing.T) {
type TestStruct struct {
Name string `json:"name"`
Age int `json:"age"`
}
s := TestStruct{Name: "test", Age: 30}
m, err := utils.ToMap(s)
assert.NoError(t, err)
assert.Equal(t, "test", m["name"])
assert.Equal(t, float64(30), m["age"]) // JSON conversion makes it float64
}
func TestFromMap(t *testing.T) {
type TestStruct struct {
Name string `json:"name"`
Age int `json:"age"`
}
m := map[string]interface{}{
"name": "test",
"age": 30,
}
var result TestStruct
err := utils.FromMap(m, &result)
assert.NoError(t, err)
assert.Equal(t, "test", result.Name)
assert.Equal(t, 30, result.Age)
}
func TestToString(t *testing.T) {
assert.Equal(t, "test", utils.ToString("test"))
assert.Equal(t, "42", utils.ToString(42))
assert.Equal(t, "true", utils.ToString(true))
}
func TestMergeMap(t *testing.T) {
target := map[string]interface{}{
"a": 1,
"b": 2,
}
source := map[string]interface{}{
"b": 3,
"c": 4,
}
result := utils.MergeMap(target, source)
assert.Equal(t, 1, result["a"])
assert.Equal(t, 3, result["b"]) // overwritten
assert.Equal(t, 4, result["c"])
}
func TestCloneMap(t *testing.T) {
original := map[string]interface{}{
"a": 1,
"b": 2,
}
cloned := utils.CloneMap(original)
cloned["a"] = 999
assert.Equal(t, 1, original["a"]) // original unchanged
assert.Equal(t, 999, cloned["a"])
}
// Validate tests
func TestIsEmpty(t *testing.T) {
assert.True(t, utils.IsEmpty(""))
assert.False(t, utils.IsEmpty("test"))
}
func TestIsValidEmail(t *testing.T) {
assert.True(t, utils.IsValidEmail("test@example.com"))
assert.True(t, utils.IsValidEmail("user+tag@domain.co.uk"))
assert.False(t, utils.IsValidEmail("invalid"))
assert.False(t, utils.IsValidEmail("@example.com"))
assert.False(t, utils.IsValidEmail("test@"))
}
func TestIsValidTime(t *testing.T) {
assert.True(t, utils.IsValidTime("09:00"))
assert.True(t, utils.IsValidTime("14:30"))
assert.True(t, utils.IsValidTime("23:59"))
assert.False(t, utils.IsValidTime("25:00"))
assert.False(t, utils.IsValidTime("14:65"))
assert.False(t, utils.IsValidTime("14-30"))
}
func TestValidateRequired(t *testing.T) {
t.Run("nil value", func(t *testing.T) {
err := utils.ValidateRequired("field", nil)
assert.Error(t, err)
})
t.Run("empty string", func(t *testing.T) {
err := utils.ValidateRequired("field", "")
assert.Error(t, err)
})
t.Run("valid string", func(t *testing.T) {
err := utils.ValidateRequired("field", "value")
assert.NoError(t, err)
})
t.Run("empty slice", func(t *testing.T) {
err := utils.ValidateRequired("field", []string{})
assert.Error(t, err)
})
}
func TestValidateRange(t *testing.T) {
t.Run("within range", func(t *testing.T) {
err := utils.ValidateRange("field", 5, 1, 10)
assert.NoError(t, err)
})
t.Run("below range", func(t *testing.T) {
err := utils.ValidateRange("field", 0, 1, 10)
assert.Error(t, err)
})
t.Run("above range", func(t *testing.T) {
err := utils.ValidateRange("field", 11, 1, 10)
assert.Error(t, err)
})
}
func TestValidateOneOf(t *testing.T) {
allowed := []string{"apple", "banana", "cherry"}
t.Run("valid value", func(t *testing.T) {
err := utils.ValidateOneOf("field", "banana", allowed)
assert.NoError(t, err)
})
t.Run("invalid value", func(t *testing.T) {
err := utils.ValidateOneOf("field", "orange", allowed)
assert.Error(t, err)
})
}