package unix import ( "encoding/json" "fmt" "testing" "time" ) func TestNow(t *testing.T) { before := time.Now() unixTime := Now() after := time.Now() if unixTime == nil { t.Fatal("Now() returned nil") } // Check that the time is within reasonable bounds if unixTime.Time.Before(before) || unixTime.Time.After(after) { t.Errorf("Now() returned time outside expected range: got %v, expected between %v and %v", unixTime.Time, before, after) } } func TestTime_MarshalJSON(t *testing.T) { tests := []struct { name string time time.Time expected string }{ { name: "epoch", time: time.Unix(0, 0), expected: "0", }, { name: "positive timestamp", time: time.Unix(1234567890, 0), expected: "1234567890", }, { name: "recent timestamp", time: time.Unix(1700000000, 0), expected: "1700000000", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { unixTime := &Time{Time: tt.time} result, err := unixTime.MarshalJSON() if err != nil { t.Fatalf("MarshalJSON() error = %v", err) } if string(result) != tt.expected { t.Errorf("MarshalJSON() = %s, want %s", string(result), tt.expected) } }) } } func TestTime_UnmarshalJSON(t *testing.T) { tests := []struct { name string input string expected time.Time wantErr bool }{ { name: "epoch", input: "0", expected: time.Unix(0, 0), wantErr: false, }, { name: "positive timestamp", input: "1234567890", expected: time.Unix(1234567890, 0), wantErr: false, }, { name: "recent timestamp", input: "1700000000", expected: time.Unix(1700000000, 0), wantErr: false, }, { name: "invalid input", input: "invalid", expected: time.Time{}, wantErr: true, }, { name: "empty input", input: "", expected: time.Time{}, wantErr: true, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { unixTime := &Time{} err := unixTime.UnmarshalJSON([]byte(tt.input)) if (err != nil) != tt.wantErr { t.Errorf("UnmarshalJSON() error = %v, wantErr %v", err, tt.wantErr) return } if !tt.wantErr && !unixTime.Time.Equal(tt.expected) { t.Errorf("UnmarshalJSON() time = %v, want %v", unixTime.Time, tt.expected) } }) } } func TestTime_JSONRoundTrip(t *testing.T) { tests := []time.Time{ time.Unix(0, 0), time.Unix(1234567890, 0), time.Unix(1700000000, 0), time.Now().Truncate(time.Second), // Truncate to second precision since we lose nanoseconds } for i, original := range tests { t.Run(fmt.Sprintf("roundtrip_%d", i), func(t *testing.T) { unixTime := &Time{Time: original} // Marshal data, err := unixTime.MarshalJSON() if err != nil { t.Fatalf("MarshalJSON() error = %v", err) } // Unmarshal var restored Time err = restored.UnmarshalJSON(data) if err != nil { t.Fatalf("UnmarshalJSON() error = %v", err) } if !restored.Time.Equal(original) { t.Errorf("Round trip failed: original = %v, restored = %v", original, restored.Time) } }) } } func TestTime_JSONCompatibility(t *testing.T) { // Test compatibility with standard JSON marshaling unixTime := &Time{Time: time.Unix(1234567890, 0)} // Test that our custom marshaling works with json.Marshal data, err := json.Marshal(unixTime) if err != nil { t.Fatalf("json.Marshal() error = %v", err) } // Test that our custom unmarshaling works with json.Unmarshal var restored Time err = json.Unmarshal(data, &restored) if err != nil { t.Fatalf("json.Unmarshal() error = %v", err) } if !restored.Time.Equal(unixTime.Time) { t.Errorf("JSON compatibility failed: original = %v, restored = %v", unixTime.Time, restored.Time) } } func TestTime_EdgeCases(t *testing.T) { t.Run("negative_timestamp", func(t *testing.T) { // Test with negative timestamp (before epoch) unixTime := &Time{Time: time.Unix(-1, 0)} data, err := unixTime.MarshalJSON() if err != nil { t.Fatalf("MarshalJSON() with negative timestamp error = %v", err) } // Negative timestamps will be converted to large positive uint64 values // This is expected behavior when casting int64 to uint64 var restored Time err = restored.UnmarshalJSON(data) if err != nil { t.Fatalf("UnmarshalJSON() with negative timestamp error = %v", err) } // The restored time won't equal the original due to uint64 conversion t.Logf("Original: %v, Marshaled: %s, Restored: %v", unixTime.Time, string(data), restored.Time) }) t.Run("very_large_timestamp", func(t *testing.T) { // Test with very large timestamp unixTime := &Time{Time: time.Unix(9223372036, 0)} // Large but within int64 range data, err := unixTime.MarshalJSON() if err != nil { t.Fatalf("MarshalJSON() with large timestamp error = %v", err) } var restored Time err = restored.UnmarshalJSON(data) if err != nil { t.Fatalf("UnmarshalJSON() with large timestamp error = %v", err) } if !restored.Time.Equal(unixTime.Time) { t.Errorf("Large timestamp round trip failed: original = %v, restored = %v", unixTime.Time, restored.Time) } }) t.Run("zero_time", func(t *testing.T) { // Test with zero time unixTime := &Time{} data, err := unixTime.MarshalJSON() if err != nil { t.Fatalf("MarshalJSON() with zero time error = %v", err) } expected := "0" if string(data) != expected { t.Errorf("MarshalJSON() with zero time = %s, want %s", string(data), expected) } }) t.Run("unmarshal_with_extra_data", func(t *testing.T) { // Test unmarshaling with extra data after the number unixTime := &Time{} err := unixTime.UnmarshalJSON([]byte("1234567890,")) if err != nil { t.Fatalf("UnmarshalJSON() with extra data error = %v", err) } expected := time.Unix(1234567890, 0) if !unixTime.Time.Equal(expected) { t.Errorf("UnmarshalJSON() with extra data = %v, want %v", unixTime.Time, expected) } }) } func TestTime_Concurrent(t *testing.T) { // Test concurrent access to ensure thread safety const numGoroutines = 100 const numOperations = 100 done := make(chan bool, numGoroutines) for i := 0; i < numGoroutines; i++ { go func(id int) { defer func() { done <- true }() for j := 0; j < numOperations; j++ { // Test Now() unixTime := Now() if unixTime == nil { t.Errorf("Goroutine %d: Now() returned nil", id) return } // Test MarshalJSON data, err := unixTime.MarshalJSON() if err != nil { t.Errorf("Goroutine %d: MarshalJSON() error = %v", id, err) return } // Test UnmarshalJSON var restored Time err = restored.UnmarshalJSON(data) if err != nil { t.Errorf("Goroutine %d: UnmarshalJSON() error = %v", id, err) return } } }(i) } // Wait for all goroutines to complete for i := 0; i < numGoroutines; i++ { <-done } } func BenchmarkTime_MarshalJSON(b *testing.B) { unixTime := &Time{Time: time.Unix(1234567890, 0)} b.ResetTimer() for i := 0; i < b.N; i++ { _, err := unixTime.MarshalJSON() if err != nil { b.Fatal(err) } } } func BenchmarkTime_UnmarshalJSON(b *testing.B) { data := []byte("1234567890") b.ResetTimer() for i := 0; i < b.N; i++ { var unixTime Time err := unixTime.UnmarshalJSON(data) if err != nil { b.Fatal(err) } } } func BenchmarkTime_Now(b *testing.B) { for i := 0; i < b.N; i++ { _ = Now() } }