Fix date parsing for Checkvist API format
Add custom APITime type with UnmarshalJSON to handle the non-standard
date format returned by the Checkvist API ("2026/01/14 16:07:31 +0000")
instead of the expected RFC3339 format.
Changes:
- Add APITime type with custom JSON marshaling/unmarshaling in models.go
- Replace time.Time with APITime for UpdatedAt/CreatedAt fields in
Checklist, Task, and Note structs
- Add NewAPITime constructor for convenience
- Update test fixtures to use real API date format
- Add comprehensive unit tests for APITime in models_test.go
Fixes: checkvist-api-4qn
This commit is contained in:
parent
fa6b93e564
commit
e4862b8e9b
12 changed files with 241 additions and 30 deletions
166
models_test.go
Normal file
166
models_test.go
Normal file
|
|
@ -0,0 +1,166 @@
|
|||
package checkvist
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestAPITime_UnmarshalJSON(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
input string
|
||||
wantErr bool
|
||||
check func(t *testing.T, got APITime)
|
||||
}{
|
||||
{
|
||||
name: "Checkvist API format",
|
||||
input: `"2026/01/14 16:07:31 +0000"`,
|
||||
wantErr: false,
|
||||
check: func(t *testing.T, got APITime) {
|
||||
if got.Year() != 2026 || got.Month() != 1 || got.Day() != 14 {
|
||||
t.Errorf("date mismatch: got %v", got.Time)
|
||||
}
|
||||
if got.Hour() != 16 || got.Minute() != 7 || got.Second() != 31 {
|
||||
t.Errorf("time mismatch: got %v", got.Time)
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "RFC3339 format",
|
||||
input: `"2026-01-14T10:00:00Z"`,
|
||||
wantErr: false,
|
||||
check: func(t *testing.T, got APITime) {
|
||||
if got.Year() != 2026 || got.Month() != 1 || got.Day() != 14 {
|
||||
t.Errorf("date mismatch: got %v", got.Time)
|
||||
}
|
||||
if got.Hour() != 10 || got.Minute() != 0 {
|
||||
t.Errorf("time mismatch: got %v", got.Time)
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "RFC3339 with timezone offset",
|
||||
input: `"2026-01-14T10:00:00+02:00"`,
|
||||
wantErr: false,
|
||||
check: func(t *testing.T, got APITime) {
|
||||
if got.Year() != 2026 || got.Month() != 1 || got.Day() != 14 {
|
||||
t.Errorf("date mismatch: got %v", got.Time)
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "empty string",
|
||||
input: `""`,
|
||||
wantErr: false,
|
||||
check: func(t *testing.T, got APITime) {
|
||||
if !got.IsZero() {
|
||||
t.Error("expected zero time for empty string")
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "null",
|
||||
input: `null`,
|
||||
wantErr: false,
|
||||
check: func(t *testing.T, got APITime) {
|
||||
if !got.IsZero() {
|
||||
t.Error("expected zero time for null")
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "invalid format",
|
||||
input: `"not a date"`,
|
||||
wantErr: true,
|
||||
check: nil,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
var got APITime
|
||||
err := json.Unmarshal([]byte(tt.input), &got)
|
||||
|
||||
if (err != nil) != tt.wantErr {
|
||||
t.Errorf("UnmarshalJSON() error = %v, wantErr %v", err, tt.wantErr)
|
||||
return
|
||||
}
|
||||
|
||||
if tt.check != nil {
|
||||
tt.check(t, got)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestAPITime_MarshalJSON(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
input APITime
|
||||
expected string
|
||||
}{
|
||||
{
|
||||
name: "normal time",
|
||||
input: NewAPITime(time.Date(2026, 1, 14, 10, 30, 0, 0, time.UTC)),
|
||||
expected: `"2026-01-14T10:30:00Z"`,
|
||||
},
|
||||
{
|
||||
name: "zero time",
|
||||
input: APITime{},
|
||||
expected: `null`,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
got, err := json.Marshal(tt.input)
|
||||
if err != nil {
|
||||
t.Fatalf("MarshalJSON() error = %v", err)
|
||||
}
|
||||
if string(got) != tt.expected {
|
||||
t.Errorf("MarshalJSON() = %s, want %s", got, tt.expected)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestAPITime_InStruct(t *testing.T) {
|
||||
// Test unmarshaling a struct with APITime fields using real API format
|
||||
jsonData := `{
|
||||
"id": 1,
|
||||
"name": "Test Checklist",
|
||||
"updated_at": "2026/01/14 16:07:31 +0000"
|
||||
}`
|
||||
|
||||
type testStruct struct {
|
||||
ID int `json:"id"`
|
||||
Name string `json:"name"`
|
||||
UpdatedAt APITime `json:"updated_at"`
|
||||
}
|
||||
|
||||
var result testStruct
|
||||
err := json.Unmarshal([]byte(jsonData), &result)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to unmarshal: %v", err)
|
||||
}
|
||||
|
||||
if result.UpdatedAt.Year() != 2026 {
|
||||
t.Errorf("expected year 2026, got %d", result.UpdatedAt.Year())
|
||||
}
|
||||
if result.UpdatedAt.Month() != 1 {
|
||||
t.Errorf("expected month 1, got %d", result.UpdatedAt.Month())
|
||||
}
|
||||
if result.UpdatedAt.Day() != 14 {
|
||||
t.Errorf("expected day 14, got %d", result.UpdatedAt.Day())
|
||||
}
|
||||
}
|
||||
|
||||
func TestNewAPITime(t *testing.T) {
|
||||
now := time.Now()
|
||||
apiTime := NewAPITime(now)
|
||||
|
||||
if !apiTime.Time.Equal(now) {
|
||||
t.Errorf("NewAPITime() did not preserve time: got %v, want %v", apiTime.Time, now)
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue