package models import ( "encoding/xml" "testing" "time" ) func TestClockTime_UnmarshalXML(t *testing.T) { tests := []struct { name string xmlData string expected ClockTime }{ { name: "Basic clock time with UTC", xmlData: `2021-01-01 00:00:00`, expected: ClockTime{ Zone: "UTC", UTC: 1609459200, Value: "2021-01-01 00:00:00", }, }, { name: "Clock time with timezone", xmlData: `2021-01-01 00:00:00`, expected: ClockTime{ Zone: "America/New_York", UTC: 1609459200, Value: "2021-01-01 00:00:00", }, }, { name: "Clock time without UTC", xmlData: `2021-01-01 12:30:45`, expected: ClockTime{ Zone: "UTC", Value: "2021-01-01 12:30:45", }, }, { name: "Empty clock time", xmlData: ``, expected: ClockTime{}, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { var clockTime ClockTime err := xml.Unmarshal([]byte(tt.xmlData), &clockTime) if err != nil { t.Fatalf("Failed to unmarshal XML: %v", err) } if clockTime.Zone != tt.expected.Zone { t.Errorf("Expected Zone %q, got %q", tt.expected.Zone, clockTime.Zone) } if clockTime.UTC != tt.expected.UTC { t.Errorf("Expected UTC %d, got %d", tt.expected.UTC, clockTime.UTC) } if clockTime.Value != tt.expected.Value { t.Errorf("Expected Value %q, got %q", tt.expected.Value, clockTime.Value) } }) } } func TestClockTime_GetTime(t *testing.T) { tests := []struct { name string clockTime ClockTime wantTime time.Time wantErr bool }{ { name: "UTC timestamp", clockTime: ClockTime{ UTC: 1609459200, // 2021-01-01 00:00:00 UTC }, wantTime: time.Unix(1609459200, 0), wantErr: false, }, { name: "RFC3339 format", clockTime: ClockTime{ Value: "2021-01-01T12:00:00Z", }, wantTime: time.Date(2021, 1, 1, 12, 0, 0, 0, time.UTC), wantErr: false, }, { name: "Standard format", clockTime: ClockTime{ Value: "2021-01-01 12:00:00", }, wantTime: time.Date(2021, 1, 1, 12, 0, 0, 0, time.UTC), wantErr: false, }, { name: "Time only format", clockTime: ClockTime{ Value: "15:30:45", }, wantTime: time.Date(0, 1, 1, 15, 30, 45, 0, time.UTC), wantErr: false, }, { name: "Invalid format", clockTime: ClockTime{ Value: "invalid-time", }, wantErr: true, }, { name: "Empty clock time", clockTime: ClockTime{}, wantErr: true, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { gotTime, err := tt.clockTime.GetTime() if tt.wantErr { if err == nil { t.Errorf("Expected error, got none") } return } if err != nil { t.Errorf("Unexpected error: %v", err) return } if !gotTime.Equal(tt.wantTime) { t.Errorf("Expected time %v, got %v", tt.wantTime, gotTime) } }) } } func TestClockTime_GetUTC(t *testing.T) { clockTime := ClockTime{UTC: 1609459200} if got := clockTime.GetUTC(); got != 1609459200 { t.Errorf("Expected UTC %d, got %d", 1609459200, got) } } func TestClockTime_GetZone(t *testing.T) { clockTime := ClockTime{Zone: "America/New_York"} if got := clockTime.GetZone(); got != "America/New_York" { t.Errorf("Expected Zone %q, got %q", "America/New_York", got) } } func TestClockTime_GetTimeString(t *testing.T) { tests := []struct { name string clockTime ClockTime expected string }{ { name: "Valid UTC time", clockTime: ClockTime{ UTC: 1609459200, // 2021-01-01 00:00:00 UTC }, expected: time.Unix(1609459200, 0).UTC().Format("2006-01-02 15:04:05"), }, { name: "Value fallback", clockTime: ClockTime{ Value: "2021-12-25 15:30:00", }, expected: "2021-12-25 15:30:00", }, { name: "Invalid time returns value", clockTime: ClockTime{ Value: "invalid-time", }, expected: "invalid-time", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { if got := tt.clockTime.GetTimeString(); got != tt.expected { t.Errorf("Expected time string %q, got %q", tt.expected, got) } }) } } func TestClockTime_IsEmpty(t *testing.T) { tests := []struct { name string clockTime ClockTime expected bool }{ { name: "Empty clock time", clockTime: ClockTime{}, expected: true, }, { name: "Clock time with UTC", clockTime: ClockTime{ UTC: 1609459200, }, expected: false, }, { name: "Clock time with value", clockTime: ClockTime{ Value: "2021-01-01 00:00:00", }, expected: false, }, { name: "Clock time with both", clockTime: ClockTime{ UTC: 1609459200, Value: "2021-01-01 00:00:00", }, expected: false, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { if got := tt.clockTime.IsEmpty(); got != tt.expected { t.Errorf("Expected IsEmpty() %v, got %v", tt.expected, got) } }) } } func TestClockTime_SetTime(t *testing.T) { clockTime := ClockTime{} testTime := time.Date(2021, 1, 1, 12, 0, 0, 0, time.UTC) clockTime.SetTime(testTime) if clockTime.UTC != testTime.Unix() { t.Errorf("Expected UTC %d, got %d", testTime.Unix(), clockTime.UTC) } if clockTime.Value != "2021-01-01 12:00:00" { t.Errorf("Expected Value %q, got %q", "2021-01-01 12:00:00", clockTime.Value) } if clockTime.Zone != "UTC" { t.Errorf("Expected Zone %q, got %q", "UTC", clockTime.Zone) } } func TestClockTime_SetUTC(t *testing.T) { clockTime := ClockTime{} utcTimestamp := int64(1609459200) // 2021-01-01 00:00:00 UTC clockTime.SetUTC(utcTimestamp) if clockTime.UTC != utcTimestamp { t.Errorf("Expected UTC %d, got %d", utcTimestamp, clockTime.UTC) } expectedValue := time.Unix(utcTimestamp, 0).UTC().Format("2006-01-02 15:04:05") if clockTime.Value != expectedValue { t.Errorf("Expected Value %q, got %q", expectedValue, clockTime.Value) } } func TestNewClockTimeRequest(t *testing.T) { testTime := time.Date(2021, 1, 1, 12, 0, 0, 0, time.UTC) request := NewClockTimeRequest(testTime) if request.UTCTime != testTime.Unix() { t.Errorf("Expected UTCTime %d, got %d", testTime.Unix(), request.UTCTime) } } func TestNewClockTimeRequestUTC(t *testing.T) { utcTimestamp := int64(1609459200) // 2021-01-01 00:00:00 UTC request := NewClockTimeRequestUTC(utcTimestamp) if request.UTCTime != utcTimestamp { t.Errorf("Expected UTCTime %d, got %d", utcTimestamp, request.UTCTime) } } func TestClockTimeRequest_Validate(t *testing.T) { tests := []struct { name string request ClockTimeRequest wantErr bool }{ { name: "Valid UTC request", request: ClockTimeRequest{UTCTime: 1609459200}, wantErr: false, }, { name: "Empty request", request: ClockTimeRequest{}, wantErr: true, }, { name: "UTC too old", request: ClockTimeRequest{UTCTime: 946684799}, // Before year 2000 wantErr: true, }, { name: "UTC too far in future", request: ClockTimeRequest{UTCTime: 4102444801}, // After year 2100 wantErr: true, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { err := tt.request.Validate() if tt.wantErr && err == nil { t.Error("Expected error, got none") } if !tt.wantErr && err != nil { t.Errorf("Unexpected error: %v", err) } }) } } func TestClockTimeRequest_MarshalXML(t *testing.T) { request := ClockTimeRequest{UTCTime: 1609459200} data, err := xml.Marshal(request) if err != nil { t.Fatalf("Failed to marshal XML: %v", err) } // Must match the device's GET /clockTime response attribute name // — firmware 27 rejects `utc=` (no Time suffix) with "Error parsing request". expected := `` if string(data) != expected { t.Errorf("Expected XML %q, got %q", expected, string(data)) } }