Implement Timestamp type for Unix timestamp JSON handling
Add custom Timestamp type that wraps time.Time for Kanboard's Unix timestamp format: UnmarshalJSON supports: - Unix timestamps as integers - Empty strings and "0" strings as zero time - Null values as zero time - Numeric strings (e.g., "1609459200") MarshalJSON returns: - 0 for zero time - Unix timestamp for non-zero time Includes comprehensive tests for round-trip marshaling and struct embedding scenarios. Closes: kanboard-api-25y
This commit is contained in:
parent
a56456cc00
commit
dbac08ac1e
3 changed files with 268 additions and 1 deletions
203
timestamp_test.go
Normal file
203
timestamp_test.go
Normal file
|
|
@ -0,0 +1,203 @@
|
|||
package kanboard
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestTimestamp_UnmarshalJSON_Integer(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
input string
|
||||
expected time.Time
|
||||
}{
|
||||
{
|
||||
name: "positive unix timestamp",
|
||||
input: "1609459200",
|
||||
expected: time.Unix(1609459200, 0), // 2021-01-01 00:00:00 UTC
|
||||
},
|
||||
{
|
||||
name: "zero",
|
||||
input: "0",
|
||||
expected: time.Time{},
|
||||
},
|
||||
{
|
||||
name: "recent timestamp",
|
||||
input: "1704067200",
|
||||
expected: time.Unix(1704067200, 0), // 2024-01-01 00:00:00 UTC
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
var ts Timestamp
|
||||
if err := json.Unmarshal([]byte(tt.input), &ts); err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
if !ts.Time.Equal(tt.expected) {
|
||||
t.Errorf("expected %v, got %v", tt.expected, ts.Time)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestTimestamp_UnmarshalJSON_String(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
input string
|
||||
expected time.Time
|
||||
}{
|
||||
{
|
||||
name: "empty string",
|
||||
input: `""`,
|
||||
expected: time.Time{},
|
||||
},
|
||||
{
|
||||
name: "zero string",
|
||||
input: `"0"`,
|
||||
expected: time.Time{},
|
||||
},
|
||||
{
|
||||
name: "numeric string",
|
||||
input: `"1609459200"`,
|
||||
expected: time.Unix(1609459200, 0),
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
var ts Timestamp
|
||||
if err := json.Unmarshal([]byte(tt.input), &ts); err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
if !ts.Time.Equal(tt.expected) {
|
||||
t.Errorf("expected %v, got %v", tt.expected, ts.Time)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestTimestamp_UnmarshalJSON_Null(t *testing.T) {
|
||||
var ts Timestamp
|
||||
if err := json.Unmarshal([]byte("null"), &ts); err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
if !ts.IsZero() {
|
||||
t.Errorf("expected zero time for null, got %v", ts.Time)
|
||||
}
|
||||
}
|
||||
|
||||
func TestTimestamp_MarshalJSON(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
ts Timestamp
|
||||
expected string
|
||||
}{
|
||||
{
|
||||
name: "zero time",
|
||||
ts: Timestamp{},
|
||||
expected: "0",
|
||||
},
|
||||
{
|
||||
name: "positive timestamp",
|
||||
ts: Timestamp{Time: time.Unix(1609459200, 0)},
|
||||
expected: "1609459200",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
data, err := json.Marshal(tt.ts)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
if string(data) != tt.expected {
|
||||
t.Errorf("expected %s, got %s", tt.expected, string(data))
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestTimestamp_RoundTrip(t *testing.T) {
|
||||
// Test that marshal/unmarshal produces the same result
|
||||
original := Timestamp{Time: time.Unix(1609459200, 0)}
|
||||
|
||||
data, err := json.Marshal(original)
|
||||
if err != nil {
|
||||
t.Fatalf("marshal error: %v", err)
|
||||
}
|
||||
|
||||
var parsed Timestamp
|
||||
if err := json.Unmarshal(data, &parsed); err != nil {
|
||||
t.Fatalf("unmarshal error: %v", err)
|
||||
}
|
||||
|
||||
if !original.Time.Equal(parsed.Time) {
|
||||
t.Errorf("round trip failed: %v != %v", original.Time, parsed.Time)
|
||||
}
|
||||
}
|
||||
|
||||
func TestTimestamp_InStruct(t *testing.T) {
|
||||
// Test Timestamp as part of a struct (simulating API response)
|
||||
type Task struct {
|
||||
ID int `json:"id"`
|
||||
DateCreation Timestamp `json:"date_creation"`
|
||||
DateDue Timestamp `json:"date_due"`
|
||||
}
|
||||
|
||||
jsonData := `{"id":42,"date_creation":1609459200,"date_due":0}`
|
||||
|
||||
var task Task
|
||||
if err := json.Unmarshal([]byte(jsonData), &task); err != nil {
|
||||
t.Fatalf("unmarshal error: %v", err)
|
||||
}
|
||||
|
||||
if task.ID != 42 {
|
||||
t.Errorf("expected ID=42, got %d", task.ID)
|
||||
}
|
||||
if task.DateCreation.IsZero() {
|
||||
t.Error("DateCreation should not be zero")
|
||||
}
|
||||
if !task.DateDue.IsZero() {
|
||||
t.Error("DateDue should be zero")
|
||||
}
|
||||
}
|
||||
|
||||
func TestTimestamp_InStructWithStringTimestamp(t *testing.T) {
|
||||
// Test with string timestamps (Kanboard sometimes returns these)
|
||||
type Task struct {
|
||||
ID int `json:"id"`
|
||||
DateCreation Timestamp `json:"date_creation"`
|
||||
DateDue Timestamp `json:"date_due"`
|
||||
}
|
||||
|
||||
jsonData := `{"id":42,"date_creation":"1609459200","date_due":""}`
|
||||
|
||||
var task Task
|
||||
if err := json.Unmarshal([]byte(jsonData), &task); err != nil {
|
||||
t.Fatalf("unmarshal error: %v", err)
|
||||
}
|
||||
|
||||
if task.ID != 42 {
|
||||
t.Errorf("expected ID=42, got %d", task.ID)
|
||||
}
|
||||
if task.DateCreation.IsZero() {
|
||||
t.Error("DateCreation should not be zero")
|
||||
}
|
||||
if !task.DateDue.IsZero() {
|
||||
t.Error("DateDue should be zero")
|
||||
}
|
||||
}
|
||||
|
||||
func TestTimestamp_IsZero(t *testing.T) {
|
||||
var zero Timestamp
|
||||
if !zero.IsZero() {
|
||||
t.Error("default Timestamp should be zero")
|
||||
}
|
||||
|
||||
nonZero := Timestamp{Time: time.Unix(1609459200, 0)}
|
||||
if nonZero.IsZero() {
|
||||
t.Error("non-zero Timestamp should not be zero")
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue