package quando import ( "testing" "time" ) func TestNewClock(t *testing.T) { clock := NewClock() if clock == nil { t.Fatal("NewClock() returned nil") } // Verify it's a DefaultClock if _, ok := clock.(*DefaultClock); !ok { t.Errorf("NewClock() returned %T, want *DefaultClock", clock) } } func TestDefaultClock_Now(t *testing.T) { clock := NewClock() before := time.Now() date := clock.Now() after := time.Now() // Verify that Now() returns a time between before and after if date.Time().Before(before) || date.Time().After(after) { t.Errorf("DefaultClock.Now() returned time outside expected range") } } func TestDefaultClock_From(t *testing.T) { clock := NewClock() testTime := time.Date(2026, 2, 9, 12, 30, 45, 0, time.UTC) date := clock.From(testTime) if !date.Time().Equal(testTime) { t.Errorf("DefaultClock.From() = %v, want %v", date.Time(), testTime) } } func TestNewFixedClock(t *testing.T) { fixedTime := time.Date(2026, 2, 9, 12, 0, 0, 0, time.UTC) clock := NewFixedClock(fixedTime) if clock == nil { t.Fatal("NewFixedClock() returned nil") } // Verify it's a FixedClock if _, ok := clock.(*FixedClock); !ok { t.Errorf("NewFixedClock() returned %T, want *FixedClock", clock) } } func TestFixedClock_Now(t *testing.T) { fixedTime := time.Date(2026, 2, 9, 12, 0, 0, 0, time.UTC) clock := NewFixedClock(fixedTime) // Call Now() multiple times to verify it always returns the same time date1 := clock.Now() time.Sleep(1 * time.Millisecond) date2 := clock.Now() time.Sleep(1 * time.Millisecond) date3 := clock.Now() // All should return the same fixed time if !date1.Time().Equal(fixedTime) { t.Errorf("FixedClock.Now() (call 1) = %v, want %v", date1.Time(), fixedTime) } if !date2.Time().Equal(fixedTime) { t.Errorf("FixedClock.Now() (call 2) = %v, want %v", date2.Time(), fixedTime) } if !date3.Time().Equal(fixedTime) { t.Errorf("FixedClock.Now() (call 3) = %v, want %v", date3.Time(), fixedTime) } // Verify all three are equal to each other if !date1.Time().Equal(date2.Time()) || !date2.Time().Equal(date3.Time()) { t.Error("FixedClock.Now() returned different times on successive calls") } } func TestFixedClock_From(t *testing.T) { fixedTime := time.Date(2026, 2, 9, 12, 0, 0, 0, time.UTC) clock := NewFixedClock(fixedTime) // Test with different time testTime := time.Date(2025, 5, 15, 8, 30, 0, 0, time.UTC) date := clock.From(testTime) // From() should use the provided time, not the fixed time if !date.Time().Equal(testTime) { t.Errorf("FixedClock.From() = %v, want %v", date.Time(), testTime) } } // TestFixedClock_DeterministicTesting demonstrates how FixedClock enables deterministic tests func TestFixedClock_DeterministicTesting(t *testing.T) { // This test demonstrates a deterministic test pattern using FixedClock // Create a fixed clock for a specific test scenario testTime := time.Date(2026, 2, 9, 12, 0, 0, 0, time.UTC) clock := NewFixedClock(testTime) // Use the clock in test code now := clock.Now() // We can now make deterministic assertions expected := "2026-02-09 12:00:00" if now.String() != expected { t.Errorf("String() = %v, want %v", now.String(), expected) } expectedUnix := int64(1770638400) if now.Unix() != expectedUnix { t.Errorf("Unix() = %d, want %d", now.Unix(), expectedUnix) } } // TestClock_Interface verifies that both implementations satisfy the Clock interface func TestClock_Interface(t *testing.T) { var _ Clock = &DefaultClock{} var _ Clock = &FixedClock{} // This test will fail at compile time if either type doesn't implement Clock } // TestClock_EdgeCases tests edge cases for clock implementations func TestClock_EdgeCases(t *testing.T) { tests := []struct { name string fixedTime time.Time }{ { name: "epoch", fixedTime: time.Unix(0, 0), }, { name: "year 0001", fixedTime: time.Date(1, 1, 1, 0, 0, 0, 0, time.UTC), }, { name: "year 9999", fixedTime: time.Date(9999, 12, 31, 23, 59, 59, 0, time.UTC), }, { name: "with nanoseconds", fixedTime: time.Date(2026, 2, 9, 12, 30, 45, 123456789, time.UTC), }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { clock := NewFixedClock(tt.fixedTime) date := clock.Now() if !date.Time().Equal(tt.fixedTime) { t.Errorf("FixedClock.Now() = %v, want %v", date.Time(), tt.fixedTime) } }) } } // TestClock_Timezones verifies that clocks preserve timezone information func TestClock_Timezones(t *testing.T) { loc, err := time.LoadLocation("Europe/Berlin") if err != nil { t.Skipf("Skipping timezone test: %v", err) } berlinTime := time.Date(2026, 2, 9, 12, 0, 0, 0, loc) // Test DefaultClock defaultClock := NewClock() date1 := defaultClock.From(berlinTime) if date1.Time().Location() != loc { t.Errorf("DefaultClock.From() location = %v, want %v", date1.Time().Location(), loc) } // Test FixedClock fixedClock := NewFixedClock(berlinTime) date2 := fixedClock.Now() if date2.Time().Location() != loc { t.Errorf("FixedClock.Now() location = %v, want %v", date2.Time().Location(), loc) } } // BenchmarkDefaultClock_Now benchmarks DefaultClock.Now() func BenchmarkDefaultClock_Now(b *testing.B) { clock := NewClock() b.ResetTimer() for i := 0; i < b.N; i++ { _ = clock.Now() } } // BenchmarkFixedClock_Now benchmarks FixedClock.Now() func BenchmarkFixedClock_Now(b *testing.B) { fixedTime := time.Date(2026, 2, 9, 12, 0, 0, 0, time.UTC) clock := NewFixedClock(fixedTime) b.ResetTimer() for i := 0; i < b.N; i++ { _ = clock.Now() } } // BenchmarkDefaultClock_From benchmarks DefaultClock.From() func BenchmarkDefaultClock_From(b *testing.B) { clock := NewClock() t := time.Now() b.ResetTimer() for i := 0; i < b.N; i++ { _ = clock.From(t) } } // BenchmarkFixedClock_From benchmarks FixedClock.From() func BenchmarkFixedClock_From(b *testing.B) { fixedTime := time.Date(2026, 2, 9, 12, 0, 0, 0, time.UTC) clock := NewFixedClock(fixedTime) t := time.Now() b.ResetTimer() for i := 0; i < b.N; i++ { _ = clock.From(t) } }