package onvif import ( "net/url" "testing" ) func TestParseMotionEvents_CellMotionDetector_True(t *testing.T) { // Dahua-style CellMotionDetector/Motion with IsMotion=true xml := ` tns1:RuleEngine/CellMotionDetector/Motion ` motion, found := ParseMotionEvents([]byte(xml)) if !found { t.Fatal("expected found=true") } if !motion { t.Fatal("expected motion=true") } } func TestParseMotionEvents_CellMotionDetector_False(t *testing.T) { xml := ` tns1:RuleEngine/CellMotionDetector/Motion ` motion, found := ParseMotionEvents([]byte(xml)) if !found { t.Fatal("expected found=true") } if motion { t.Fatal("expected motion=false") } } func TestParseMotionEvents_VideoSourceMotionAlarm_True(t *testing.T) { // Hikvision-style VideoSource/MotionAlarm with State=true xml := ` tns1:VideoSource/MotionAlarm ` motion, found := ParseMotionEvents([]byte(xml)) if !found { t.Fatal("expected found=true") } if !motion { t.Fatal("expected motion=true") } } func TestParseMotionEvents_VideoSourceMotionAlarm_False(t *testing.T) { xml := ` tns1:VideoSource/MotionAlarm ` motion, found := ParseMotionEvents([]byte(xml)) if !found { t.Fatal("expected found=true") } if motion { t.Fatal("expected motion=false") } } func TestParseMotionEvents_NoMotionTopic(t *testing.T) { // Response with a non-motion topic (e.g., tampering) xml := ` tns1:VideoSource/ImageTooBlurry/ImagingService ` _, found := ParseMotionEvents([]byte(xml)) if found { t.Fatal("expected found=false for non-motion topic") } } func TestParseMotionEvents_EmptyResponse(t *testing.T) { // PullMessages response with no notifications (timeout, no events) xml := ` 2025-01-15T10:00:00Z 2025-01-15T10:01:00Z ` _, found := ParseMotionEvents([]byte(xml)) if found { t.Fatal("expected found=false for empty response") } } func TestParseMotionEvents_MotionRegionDetector(t *testing.T) { xml := ` tns1:RuleEngine/MotionRegionDetector/Motion ` motion, found := ParseMotionEvents([]byte(xml)) if !found { t.Fatal("expected found=true") } if !motion { t.Fatal("expected motion=true for Value=1") } } func TestParseMotionEvents_MultipleNotifications(t *testing.T) { // Multiple notifications: first motion=true, then motion=false. Should return last. xml := ` tns1:RuleEngine/CellMotionDetector/Motion tns1:RuleEngine/CellMotionDetector/Motion ` motion, found := ParseMotionEvents([]byte(xml)) if !found { t.Fatal("expected found=true") } if motion { t.Fatal("expected motion=false (last notification)") } } func TestResolveEventAddress_RelativePath(t *testing.T) { u, _ := url.Parse("http://camera.example/onvif/device_service") client := &Client{url: u} got := client.resolveEventAddress("/onvif/Subscription?Idx=1") want := "http://camera.example/onvif/Subscription?Idx=1" if got != want { t.Fatalf("unexpected resolved address: got %q want %q", got, want) } } func TestResolveEventAddress_DockerInternalIP(t *testing.T) { u, _ := url.Parse("http://localhost:18080/onvif/device_service") client := &Client{url: u} got := client.resolveEventAddress("http://172.17.0.2:8080/onvif/events_service?sub=1") want := "http://localhost:18080/onvif/events_service?sub=1" if got != want { t.Fatalf("unexpected resolved address: got %q want %q", got, want) } }