- Introduced NanoToTime and TimeToNano functions for converting between UnixNano and time.Time, improving clarity and consistency in timestamp management. - Updated Assistant model to utilize the new utility functions for created_at and updated_at fields. - Refactored Xun store methods to ensure UTC compatibility for timestamp serialization across different database drivers. - Added unit tests for the new conversion functions to validate their correctness and behavior.
173 lines
4.9 KiB
Go
173 lines
4.9 KiB
Go
package utils
|
|
|
|
import (
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
)
|
|
|
|
func TestDBTimestampToUnix(t *testing.T) {
|
|
ref := time.Date(2024, 6, 15, 10, 30, 45, 0, time.UTC)
|
|
refUnix := ref.Unix()
|
|
|
|
tests := []struct {
|
|
name string
|
|
input interface{}
|
|
expect interface{}
|
|
}{
|
|
{"nil", nil, nil},
|
|
{"empty string", "", nil},
|
|
{"MySQL DATETIME", "2024-06-15 10:30:45", refUnix},
|
|
{"PG with fractional seconds", "2024-06-15 10:30:45.123456", refUnix},
|
|
{"PG with +00 offset", "2024-06-15 10:30:45.123456+00", refUnix},
|
|
{"PG with -07 offset", "2024-06-15 03:30:45.123456-07", refUnix},
|
|
{"ISO Z suffix", "2024-06-15T10:30:45Z", refUnix},
|
|
{"RFC3339", "2024-06-15T10:30:45+00:00", refUnix},
|
|
{"RFC3339Nano", "2024-06-15T10:30:45.123456789+00:00", refUnix},
|
|
{"*string nil", (*string)(nil), nil},
|
|
{"*string valid", strPtr("2024-06-15 10:30:45"), refUnix},
|
|
{"*string empty", strPtr(""), nil},
|
|
{"non-string type", 12345, nil},
|
|
{"unparseable string", "not-a-date", nil},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
result := DBTimestampToUnix(tt.input)
|
|
assert.Equal(t, tt.expect, result)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestUnixToDBTimestamp(t *testing.T) {
|
|
refUnix := int64(1718444445)
|
|
expected := time.Unix(refUnix, 0).UTC().Format("2006-01-02 15:04:05")
|
|
|
|
tests := []struct {
|
|
name string
|
|
input interface{}
|
|
expect interface{}
|
|
}{
|
|
{"nil", nil, nil},
|
|
{"int64", refUnix, expected},
|
|
{"*int64 valid", int64Ptr(refUnix), expected},
|
|
{"*int64 nil", (*int64)(nil), nil},
|
|
{"int", int(refUnix), expected},
|
|
{"float64", float64(refUnix), expected},
|
|
{"unknown type", "not-a-number", nil},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
result := UnixToDBTimestamp(tt.input)
|
|
assert.Equal(t, tt.expect, result)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestToUnixTimestamp(t *testing.T) {
|
|
refUnix := int64(1718444445)
|
|
|
|
tests := []struct {
|
|
name string
|
|
input interface{}
|
|
expect interface{}
|
|
}{
|
|
{"nil", nil, nil},
|
|
{"int64", refUnix, refUnix},
|
|
{"*int64 valid", int64Ptr(refUnix), refUnix},
|
|
{"*int64 nil", (*int64)(nil), nil},
|
|
{"int", int(refUnix), refUnix},
|
|
{"float64", float64(refUnix), refUnix},
|
|
{"string MySQL", "2024-06-15 10:00:45", time.Date(2024, 6, 15, 10, 0, 45, 0, time.UTC).Unix()},
|
|
{"string PG fractional", "2024-06-15 10:00:45.123456", time.Date(2024, 6, 15, 10, 0, 45, 0, time.UTC).Unix()},
|
|
{"string RFC3339", "2024-06-15T10:00:45Z", time.Date(2024, 6, 15, 10, 0, 45, 0, time.UTC).Unix()},
|
|
{"*string valid", strPtr("2024-06-15 10:00:45"), time.Date(2024, 6, 15, 10, 0, 45, 0, time.UTC).Unix()},
|
|
{"*string nil", (*string)(nil), nil},
|
|
{"string empty", "", nil},
|
|
{"unknown type", struct{}{}, nil},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
result := ToUnixTimestamp(tt.input)
|
|
assert.Equal(t, tt.expect, result)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestToTimeStringWithPGFormats(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
input interface{}
|
|
expect string
|
|
}{
|
|
{"PG fractional", "2024-06-15 10:30:45.123456", "2024-06-15T10:30:45Z"},
|
|
{"PG +00 offset", "2024-06-15 10:30:45.123456+00", "2024-06-15T10:30:45Z"},
|
|
{"PG -07 offset", "2024-06-15 03:30:45.123456-07", "2024-06-15T03:30:45-07:00"},
|
|
{"MySQL format", "2024-06-15 10:30:45", "2024-06-15T10:30:45Z"},
|
|
{"RFC3339Nano", "2024-06-15T10:30:45.123456789+00:00", "2024-06-15T10:30:45Z"},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
result := ToTimeString(tt.input)
|
|
assert.Equal(t, tt.expect, result)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestNanoToTime(t *testing.T) {
|
|
t.Run("Zero", func(t *testing.T) {
|
|
assert.True(t, NanoToTime(0).IsZero())
|
|
})
|
|
|
|
t.Run("ValidTimestamp", func(t *testing.T) {
|
|
ns := int64(1609459200000000000) // 2021-01-01 00:00:00 UTC
|
|
got := NanoToTime(ns)
|
|
assert.Equal(t, 2021, got.Year())
|
|
assert.Equal(t, time.January, got.Month())
|
|
assert.Equal(t, 1, got.Day())
|
|
assert.Equal(t, 0, got.Hour())
|
|
assert.Equal(t, time.UTC, got.Location())
|
|
})
|
|
|
|
t.Run("PreservesNanoseconds", func(t *testing.T) {
|
|
ns := int64(1609459200123456789)
|
|
got := NanoToTime(ns)
|
|
assert.Equal(t, 123456789, got.Nanosecond())
|
|
})
|
|
|
|
t.Run("Negative", func(t *testing.T) {
|
|
got := NanoToTime(-1)
|
|
assert.False(t, got.IsZero())
|
|
assert.Equal(t, time.UTC, got.Location())
|
|
})
|
|
}
|
|
|
|
func TestTimeToNano(t *testing.T) {
|
|
t.Run("Zero", func(t *testing.T) {
|
|
assert.Equal(t, int64(0), TimeToNano(time.Time{}))
|
|
})
|
|
|
|
t.Run("ValidTime", func(t *testing.T) {
|
|
ts := time.Date(2021, 1, 1, 0, 0, 0, 0, time.UTC)
|
|
assert.Equal(t, int64(1609459200000000000), TimeToNano(ts))
|
|
})
|
|
|
|
t.Run("WithNanoseconds", func(t *testing.T) {
|
|
ts := time.Date(2021, 1, 1, 0, 0, 0, 123456789, time.UTC)
|
|
assert.Equal(t, int64(1609459200123456789), TimeToNano(ts))
|
|
})
|
|
|
|
t.Run("RoundTrip", func(t *testing.T) {
|
|
original := time.Date(2026, 3, 26, 15, 30, 45, 123456789, time.UTC)
|
|
ns := TimeToNano(original)
|
|
restored := NanoToTime(ns)
|
|
assert.True(t, original.Equal(restored))
|
|
})
|
|
}
|
|
|
|
func strPtr(s string) *string { return &s }
|
|
func int64Ptr(i int64) *int64 { return &i }
|