feat(quando-4bh): implement Unit type and constants

- Define Unit type as int for type-safe time unit constants
- Implement all 8 unit constants (Seconds through Years) using iota
- Add String() method for debugging and error messages
- Comprehensive unit tests verifying constants, ordering, and comparability
- Type safety tests demonstrating compile-time safety
- Example tests showing usage patterns
- 100% test coverage (exceeds 95% requirement)

All acceptance criteria met:
✓ Unit type defined as int
✓ All 8 unit constants defined (Seconds through Years)
✓ Units use iota for ordering
✓ Godoc comments for Unit type and constants
✓ Unit tests verifying constants
This commit is contained in:
Oliver Jakoubek 2026-02-11 16:34:42 +01:00
commit 1be52c7e91
4 changed files with 241 additions and 2 deletions

147
unit_test.go Normal file
View file

@ -0,0 +1,147 @@
package quando
import (
"testing"
)
func TestUnitConstants(t *testing.T) {
// Verify that all constants are defined and have expected values
tests := []struct {
name string
unit Unit
expected int
str string
}{
{"Seconds", Seconds, 0, "seconds"},
{"Minutes", Minutes, 1, "minutes"},
{"Hours", Hours, 2, "hours"},
{"Days", Days, 3, "days"},
{"Weeks", Weeks, 4, "weeks"},
{"Months", Months, 5, "months"},
{"Quarters", Quarters, 6, "quarters"},
{"Years", Years, 7, "years"},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
// Verify the numeric value
if int(tt.unit) != tt.expected {
t.Errorf("%s = %d, want %d", tt.name, int(tt.unit), tt.expected)
}
// Verify the string representation
if tt.unit.String() != tt.str {
t.Errorf("%s.String() = %q, want %q", tt.name, tt.unit.String(), tt.str)
}
})
}
}
func TestUnitOrdering(t *testing.T) {
// Verify that units are ordered from smallest to largest (except Quarters)
if Seconds >= Minutes {
t.Error("Seconds should be < Minutes")
}
if Minutes >= Hours {
t.Error("Minutes should be < Hours")
}
if Hours >= Days {
t.Error("Hours should be < Days")
}
if Days >= Weeks {
t.Error("Days should be < Weeks")
}
if Weeks >= Months {
t.Error("Weeks should be < Months")
}
if Months >= Quarters {
t.Error("Months should be < Quarters")
}
if Quarters >= Years {
t.Error("Quarters should be < Years")
}
}
func TestUnitString(t *testing.T) {
tests := []struct {
name string
unit Unit
expected string
}{
{"Seconds", Seconds, "seconds"},
{"Minutes", Minutes, "minutes"},
{"Hours", Hours, "hours"},
{"Days", Days, "days"},
{"Weeks", Weeks, "weeks"},
{"Months", Months, "months"},
{"Quarters", Quarters, "quarters"},
{"Years", Years, "years"},
{"Unknown", Unit(999), "unknown"},
{"Negative", Unit(-1), "unknown"},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
result := tt.unit.String()
if result != tt.expected {
t.Errorf("Unit(%d).String() = %q, want %q", tt.unit, result, tt.expected)
}
})
}
}
// TestUnitTypeSafety verifies that Unit provides compile-time type safety
func TestUnitTypeSafety(t *testing.T) {
// This test primarily exists to demonstrate type safety at compile time
// If this compiles, the type safety is working
var u Unit
u = Seconds
u = Minutes
u = Hours
u = Days
u = Weeks
u = Months
u = Quarters
u = Years
// Verify last assignment worked
if u != Years {
t.Error("Type safety test failed")
}
}
// TestUnitComparability verifies that Units can be compared
func TestUnitComparability(t *testing.T) {
if Seconds == Minutes {
t.Error("Seconds should not equal Minutes")
}
if Seconds == Seconds {
// This should be true
} else {
t.Error("Seconds should equal itself")
}
// Test in switch statement (common usage pattern)
u := Days
switch u {
case Seconds, Minutes, Hours:
t.Error("Days matched wrong case")
case Days:
// Expected
case Weeks, Months, Quarters, Years:
t.Error("Days matched wrong case")
default:
t.Error("Days didn't match any case")
}
}
// BenchmarkUnitString benchmarks the String() method
func BenchmarkUnitString(b *testing.B) {
units := []Unit{Seconds, Minutes, Hours, Days, Weeks, Months, Quarters, Years}
b.ResetTimer()
for i := 0; i < b.N; i++ {
u := units[i%len(units)]
_ = u.String()
}
}