feat(quando-36t): implement error types and handling

- Create errors.go with sentinel errors
- Define ErrInvalidFormat for parsing errors
- Define ErrInvalidTimezone for timezone errors
- Define ErrOverflow for date arithmetic overflow
- Comprehensive godoc for each error with usage context
- Document no-panic policy in error handling
- Document Must* variant panic behavior
- Example tests showing error handling patterns
- Tests for error uniqueness and errors.Is compatibility
- 100% coverage for error definitions

Error Handling Policy:
- Library never panics in normal operations
- All errors returned as values
- Use errors.Is() for error type checking
- Must* functions panic (for test/init use only)

All acceptance criteria met:
✓ errors.go file created
✓ ErrInvalidFormat defined
✓ ErrInvalidTimezone defined
✓ ErrOverflow defined
✓ Godoc for each error with usage context
✓ Documentation of no-panic policy
✓ Documentation of Must* panic behavior
✓ Example tests showing error handling patterns
This commit is contained in:
Oliver Jakoubek 2026-02-11 17:40:01 +01:00
commit 2bf1df03ea
4 changed files with 189 additions and 2 deletions

73
errors_test.go Normal file
View file

@ -0,0 +1,73 @@
package quando
import (
"errors"
"testing"
)
// TestSentinelErrors verifies that all sentinel errors are defined
func TestSentinelErrors(t *testing.T) {
tests := []struct {
name string
err error
check string
}{
{"ErrInvalidFormat", ErrInvalidFormat, "invalid date format"},
{"ErrInvalidTimezone", ErrInvalidTimezone, "invalid timezone"},
{"ErrOverflow", ErrOverflow, "date overflow"},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if tt.err == nil {
t.Errorf("%s is nil, expected error", tt.name)
}
if tt.err.Error() != tt.check {
t.Errorf("%s.Error() = %q, want %q", tt.name, tt.err.Error(), tt.check)
}
})
}
}
// TestErrorsIs verifies that errors.Is works with sentinel errors
func TestErrorsIs(t *testing.T) {
// Test direct comparison
if !errors.Is(ErrInvalidFormat, ErrInvalidFormat) {
t.Error("errors.Is(ErrInvalidFormat, ErrInvalidFormat) should be true")
}
if !errors.Is(ErrInvalidTimezone, ErrInvalidTimezone) {
t.Error("errors.Is(ErrInvalidTimezone, ErrInvalidTimezone) should be true")
}
if !errors.Is(ErrOverflow, ErrOverflow) {
t.Error("errors.Is(ErrOverflow, ErrOverflow) should be true")
}
// Test that different errors are not equal
if errors.Is(ErrInvalidFormat, ErrInvalidTimezone) {
t.Error("ErrInvalidFormat and ErrInvalidTimezone should not be equal")
}
if errors.Is(ErrInvalidTimezone, ErrOverflow) {
t.Error("ErrInvalidTimezone and ErrOverflow should not be equal")
}
}
// TestErrorUniqueness verifies that each error is unique
func TestErrorUniqueness(t *testing.T) {
allErrors := []error{
ErrInvalidFormat,
ErrInvalidTimezone,
ErrOverflow,
}
// Check that no two errors are the same
for i, err1 := range allErrors {
for j, err2 := range allErrors {
if i != j && err1 == err2 {
t.Errorf("Errors at index %d and %d are the same", i, j)
}
}
}
}