Files
go2rtc/internal/homekit/onvif_motion_test.go
T
Sergey Krashevich 8a21809f18 feat(homekit): add ONVIF motion detection support
- implement ONVIF motion watcher to handle motion events
- add configuration options for motion hold time and ONVIF URL
- remap motion mode from "onvif" to "api" for compatibility
- log ONVIF motion watcher activity for better debugging

feat(onvif): implement event subscription for motion detection
- create PullPoint subscription to receive motion events
- implement methods for pulling messages and renewing subscriptions
- handle event requests and responses specific to motion detection

test(onvif): add unit tests for motion event parsing and subscription
- create tests for parsing various motion event XML responses
- verify correct handling of multiple notifications and edge cases
- test resolving event addresses for ONVIF clients

fix(hksv): improve motion detection logging
- log warnings when accessory or character not found during motion detection
- log number of listeners notified during motion state changes

feat(hap): add listener count method
- introduce method to retrieve the number of listeners for a character

feat(onvif): enhance ONVIF client with event URL handling
- extract event URL from ONVIF device response for subscription management
2026-03-09 13:06:57 +03:00

100 lines
2.3 KiB
Go

package homekit
import (
"errors"
"testing"
"time"
"github.com/AlexxIT/go2rtc/pkg/hksv"
"github.com/rs/zerolog"
)
func TestOnvifMotionWatcherConnectAndPollRenewsBeforeLeaseExpires(t *testing.T) {
start := time.Unix(0, 0)
now := start
stopErr := errors.New("stop pull loop")
sub := &fakeOnvifPullPoint{
t: t,
now: &now,
pullErrAt: 3,
pullErr: stopErr,
}
w := newOnvifMotionWatcher(&hksv.Server{}, "onvif://camera", 30*time.Second, zerolog.Nop())
w.now = func() time.Time { return now }
w.newPullPoint = func(rawURL string, timeout time.Duration) (onvifPullPoint, error) {
if rawURL != "onvif://camera" {
t.Fatalf("unexpected ONVIF URL: %s", rawURL)
}
if timeout != 60*time.Second {
t.Fatalf("unexpected subscription timeout: %v", timeout)
}
return sub, nil
}
err := w.connectAndPoll()
if !errors.Is(err, stopErr) {
t.Fatalf("expected %v, got %v", stopErr, err)
}
wantPulls := []time.Duration{30 * time.Second, 20 * time.Second, 30 * time.Second}
if len(sub.pullTimeouts) != len(wantPulls) {
t.Fatalf("unexpected pull count: got %d want %d", len(sub.pullTimeouts), len(wantPulls))
}
for i, want := range wantPulls {
if sub.pullTimeouts[i] != want {
t.Fatalf("pull %d timeout mismatch: got %v want %v", i+1, sub.pullTimeouts[i], want)
}
}
if sub.renewCalls != 1 {
t.Fatalf("expected 1 renew call, got %d", sub.renewCalls)
}
if !sub.unsubscribed {
t.Fatal("expected unsubscribe on exit")
}
}
type fakeOnvifPullPoint struct {
t *testing.T
now *time.Time
pullTimeouts []time.Duration
renewCalls int
unsubscribed bool
pullErrAt int
pullErr error
}
func (f *fakeOnvifPullPoint) PullMessages(timeout time.Duration, limit int) ([]byte, error) {
if limit != 10 {
f.t.Fatalf("unexpected message limit: %d", limit)
}
f.pullTimeouts = append(f.pullTimeouts, timeout)
*f.now = f.now.Add(timeout)
if f.pullErrAt > 0 && len(f.pullTimeouts) == f.pullErrAt {
return nil, f.pullErr
}
return []byte(`<tev:PullMessagesResponse xmlns:tev="http://www.onvif.org/ver10/events/wsdl"/>`), nil
}
func (f *fakeOnvifPullPoint) Renew(timeout time.Duration) error {
if timeout != 60*time.Second {
f.t.Fatalf("unexpected renew timeout: %v", timeout)
}
f.renewCalls++
return nil
}
func (f *fakeOnvifPullPoint) Unsubscribe() error {
f.unsubscribed = true
return nil
}