test: add more service layer tests

Coverage: Service 71.7% → 71.8%

- classified_error_test.go (10 tests): error wrapping, Unwrap, errors.Is
- stats_test.go (12 tests): user stats, dashboard stats, daysAgo utility
This commit is contained in:
Your Name
2026-05-30 17:34:48 +08:00
parent 52161d5a9c
commit 7ad65a0138
2 changed files with 188 additions and 178 deletions

View File

@@ -3,97 +3,72 @@ package service
import (
"errors"
"testing"
"github.com/stretchr/testify/assert"
)
// =============================================================================
// Classified Error Tests
// ClassifiedError Tests
// =============================================================================
func TestClassifiedError(t *testing.T) {
// Test error with message
e1 := &classifiedError{message: "custom message", cause: errors.New("cause")}
if e1.Error() != "custom message" {
t.Errorf("Error() = %q, want %q", e1.Error(), "custom message")
}
// Test error with cause but no message
e2 := &classifiedError{cause: errors.New("underlying error")}
if e2.Error() != "underlying error" {
t.Errorf("Error() = %q, want %q", e2.Error(), "underlying error")
}
// Test error with neither message nor cause
e3 := &classifiedError{}
if e3.Error() != "" {
t.Errorf("Error() = %q, want empty string", e3.Error())
}
func TestClassifiedError_Error_WithMessage(t *testing.T) {
err := newValidationError("custom validation message")
assert.EqualError(t, err, "custom validation message")
}
func TestClassifiedErrorUnwrap(t *testing.T) {
innerErr := errors.New("inner error")
e := &classifiedError{message: "outer", cause: innerErr}
unwrapped := e.Unwrap()
if unwrapped != innerErr {
t.Errorf("Unwrap() = %v, want %v", unwrapped, innerErr)
}
// Test errors.Is
if !errors.Is(e, innerErr) {
t.Error("errors.Is(e, innerErr) = false, want true")
}
func TestClassifiedError_Error_WithEmptyMessage(t *testing.T) {
// Create error with only cause
err := &classifiedError{cause: ErrValidationFailed}
assert.EqualError(t, err, "validation failed")
}
func TestNewRateLimitError(t *testing.T) {
func TestClassifiedError_Error_WithNoMessageOrCause(t *testing.T) {
// Create error with neither message nor cause
err := &classifiedError{}
assert.Equal(t, "", err.Error())
}
func TestClassifiedError_Unwrap(t *testing.T) {
err := newRateLimitError("too many requests")
// Should be a classifiedError
var ce *classifiedError
if !errors.As(err, &ce) {
t.Errorf("errors.As(err, &classifiedError{}) = false")
}
// Should wrap ErrRateLimitExceeded
if !errors.Is(err, ErrRateLimitExceeded) {
t.Error("errors.Is(err, ErrRateLimitExceeded) = false")
}
// Error message should be "too many requests"
if err.Error() != "too many requests" {
t.Errorf("err.Error() = %q, want %q", err.Error(), "too many requests")
}
}
func TestNewValidationError(t *testing.T) {
err := newValidationError("invalid input")
// Should be a classifiedError
var ce *classifiedError
if !errors.As(err, &ce) {
t.Errorf("errors.As(err, &classifiedError{}) = false")
}
// Should wrap ErrValidationFailed
if !errors.Is(err, ErrValidationFailed) {
t.Error("errors.Is(err, ErrValidationFailed) = false")
}
// Error message should be "invalid input"
if err.Error() != "invalid input" {
t.Errorf("err.Error() = %q, want %q", err.Error(), "invalid input")
}
// Unwrap should return the cause
unwrapped := errors.Unwrap(err)
assert.Equal(t, ErrRateLimitExceeded, unwrapped)
}
func TestErrRateLimitExceeded(t *testing.T) {
// ErrRateLimitExceeded is a sentinel error
if ErrRateLimitExceeded.Error() != "rate limit exceeded" {
t.Errorf("ErrRateLimitExceeded.Error() = %q, want %q", ErrRateLimitExceeded.Error(), "rate limit exceeded")
}
assert.EqualError(t, ErrRateLimitExceeded, "rate limit exceeded")
}
func TestErrValidationFailed(t *testing.T) {
// ErrValidationFailed is a sentinel error
if ErrValidationFailed.Error() != "validation failed" {
t.Errorf("ErrValidationFailed.Error() = %q, want %q", ErrValidationFailed.Error(), "validation failed")
}
assert.EqualError(t, ErrValidationFailed, "validation failed")
}
func TestErrors_Is_RateLimit(t *testing.T) {
// Test that wrapped errors can be identified using errors.Is
err := newRateLimitError("too many requests")
assert.True(t, errors.Is(err, ErrRateLimitExceeded))
assert.False(t, errors.Is(err, ErrValidationFailed))
}
func TestErrors_Is_Validation(t *testing.T) {
err := newValidationError("invalid input")
assert.True(t, errors.Is(err, ErrValidationFailed))
assert.False(t, errors.Is(err, ErrRateLimitExceeded))
}
func TestNewRateLimitError(t *testing.T) {
err := newRateLimitError("rate limited")
assert.EqualError(t, err, "rate limited")
assert.True(t, errors.Is(err, ErrRateLimitExceeded))
}
func TestNewValidationError(t *testing.T) {
err := newValidationError("validation failed")
assert.EqualError(t, err, "validation failed")
assert.True(t, errors.Is(err, ErrValidationFailed))
}