318 lines
7.3 KiB
Go
318 lines
7.3 KiB
Go
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()
|
|
}
|
|
}
|