393 lines
10 KiB
Go
393 lines
10 KiB
Go
//go:build real_camera
|
|
|
|
package onvif
|
|
|
|
import (
|
|
"context"
|
|
"os"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/0x524a/onvif-go"
|
|
)
|
|
|
|
// getTestCredentials returns ONVIF credentials from environment variables.
|
|
// Required environment variables:
|
|
// - ONVIF_ENDPOINT: Camera endpoint URL (e.g., http://192.168.1.201/onvif/device_service)
|
|
// - ONVIF_USERNAME: ONVIF username
|
|
// - ONVIF_PASSWORD: ONVIF password
|
|
func getTestCredentials(t *testing.T) (endpoint, username, password string) {
|
|
endpoint = os.Getenv("ONVIF_ENDPOINT")
|
|
username = os.Getenv("ONVIF_USERNAME")
|
|
password = os.Getenv("ONVIF_PASSWORD")
|
|
|
|
if endpoint == "" || username == "" || password == "" {
|
|
t.Skip("ONVIF credentials not configured. Set ONVIF_ENDPOINT, ONVIF_USERNAME, and ONVIF_PASSWORD environment variables.")
|
|
}
|
|
|
|
return endpoint, username, password
|
|
}
|
|
|
|
// TestEnhancedDeviceFeatures tests new Device service methods with real camera data
|
|
// Based on test results from Bosch FLEXIDOME indoor 5100i IR (8.71.0066)
|
|
func TestEnhancedDeviceFeatures(t *testing.T) {
|
|
endpoint, username, password := getTestCredentials(t)
|
|
|
|
// Create client with test credentials
|
|
client, err := onvif.NewClient(
|
|
endpoint,
|
|
onvif.WithCredentials(username, password),
|
|
onvif.WithTimeout(30*time.Second),
|
|
)
|
|
if err != nil {
|
|
t.Fatalf("Failed to create client: %v", err)
|
|
}
|
|
|
|
ctx := context.Background()
|
|
|
|
t.Run("GetHostname", func(t *testing.T) {
|
|
hostname, err := client.GetHostname(ctx)
|
|
if err != nil {
|
|
t.Fatalf("GetHostname failed: %v", err)
|
|
}
|
|
|
|
// Bosch camera has hostname configuration
|
|
if hostname == nil {
|
|
t.Fatal("Expected hostname information, got nil")
|
|
}
|
|
|
|
t.Logf("Hostname: FromDHCP=%v, Name=%q", hostname.FromDHCP, hostname.Name)
|
|
})
|
|
|
|
t.Run("GetDNS", func(t *testing.T) {
|
|
dns, err := client.GetDNS(ctx)
|
|
if err != nil {
|
|
t.Fatalf("GetDNS failed: %v", err)
|
|
}
|
|
|
|
if dns == nil {
|
|
t.Fatal("Expected DNS information, got nil")
|
|
}
|
|
|
|
// Bosch camera uses DHCP for DNS
|
|
if !dns.FromDHCP {
|
|
t.Logf("Note: Camera not using DHCP for DNS")
|
|
}
|
|
|
|
// Should have at least one DNS server
|
|
if len(dns.DNSFromDHCP) == 0 && len(dns.DNSManual) == 0 {
|
|
t.Error("Expected at least one DNS server")
|
|
}
|
|
|
|
t.Logf("DNS: FromDHCP=%v, Servers=%d (DHCP) + %d (Manual)",
|
|
dns.FromDHCP, len(dns.DNSFromDHCP), len(dns.DNSManual))
|
|
})
|
|
|
|
t.Run("GetNTP", func(t *testing.T) {
|
|
ntp, err := client.GetNTP(ctx)
|
|
if err != nil {
|
|
t.Fatalf("GetNTP failed: %v", err)
|
|
}
|
|
|
|
if ntp == nil {
|
|
t.Fatal("Expected NTP information, got nil")
|
|
}
|
|
|
|
// Bosch camera uses DHCP for NTP
|
|
if !ntp.FromDHCP {
|
|
t.Logf("Note: Camera not using DHCP for NTP")
|
|
}
|
|
|
|
t.Logf("NTP: FromDHCP=%v, Servers=%d (DHCP) + %d (Manual)",
|
|
ntp.FromDHCP, len(ntp.NTPFromDHCP), len(ntp.NTPManual))
|
|
})
|
|
|
|
t.Run("GetNetworkInterfaces", func(t *testing.T) {
|
|
interfaces, err := client.GetNetworkInterfaces(ctx)
|
|
if err != nil {
|
|
t.Fatalf("GetNetworkInterfaces failed: %v", err)
|
|
}
|
|
|
|
// Bosch camera has 1 network interface
|
|
if len(interfaces) == 0 {
|
|
t.Fatal("Expected at least one network interface")
|
|
}
|
|
|
|
iface := interfaces[0]
|
|
if iface.Token == "" {
|
|
t.Error("Expected interface to have token")
|
|
}
|
|
|
|
if iface.Info.Name == "" {
|
|
t.Error("Expected interface to have name")
|
|
}
|
|
|
|
if iface.Info.HwAddress == "" {
|
|
t.Error("Expected interface to have hardware address")
|
|
}
|
|
|
|
// Bosch camera has MTU of 1514
|
|
if iface.Info.MTU == 0 {
|
|
t.Error("Expected interface to have MTU")
|
|
}
|
|
|
|
t.Logf("Interface: Token=%s, Name=%s, HwAddr=%s, MTU=%d",
|
|
iface.Token, iface.Info.Name, iface.Info.HwAddress, iface.Info.MTU)
|
|
|
|
if iface.IPv4 != nil {
|
|
t.Logf(" IPv4: Enabled=%v, DHCP=%v",
|
|
iface.IPv4.Enabled, iface.IPv4.Config.DHCP)
|
|
}
|
|
})
|
|
|
|
t.Run("GetScopes", func(t *testing.T) {
|
|
scopes, err := client.GetScopes(ctx)
|
|
if err != nil {
|
|
t.Fatalf("GetScopes failed: %v", err)
|
|
}
|
|
|
|
// Bosch camera has 8 scopes
|
|
if len(scopes) == 0 {
|
|
t.Fatal("Expected at least one scope")
|
|
}
|
|
|
|
// Check for expected scopes
|
|
foundManufacturer := false
|
|
foundType := false
|
|
foundProfiles := 0
|
|
|
|
for _, scope := range scopes {
|
|
if scope.ScopeItem == "onvif://www.onvif.org/name/Bosch" {
|
|
foundManufacturer = true
|
|
}
|
|
if scope.ScopeItem == "onvif://www.onvif.org/type/Network_Video_Transmitter" {
|
|
foundType = true
|
|
}
|
|
// Count ONVIF profiles
|
|
if len(scope.ScopeItem) > 30 && scope.ScopeItem[:30] == "onvif://www.onvif.org/Profile/" {
|
|
foundProfiles++
|
|
}
|
|
}
|
|
|
|
if !foundManufacturer {
|
|
t.Error("Expected to find manufacturer scope")
|
|
}
|
|
if !foundType {
|
|
t.Error("Expected to find device type scope")
|
|
}
|
|
|
|
t.Logf("Scopes: Total=%d, Manufacturer=%v, Type=%v, Profiles=%d",
|
|
len(scopes), foundManufacturer, foundType, foundProfiles)
|
|
})
|
|
|
|
t.Run("GetUsers", func(t *testing.T) {
|
|
users, err := client.GetUsers(ctx)
|
|
if err != nil {
|
|
t.Fatalf("GetUsers failed: %v", err)
|
|
}
|
|
|
|
// Bosch camera has 3 users
|
|
if len(users) == 0 {
|
|
t.Fatal("Expected at least one user")
|
|
}
|
|
|
|
// Verify user levels
|
|
userLevels := make(map[string]int)
|
|
for _, user := range users {
|
|
if user.Username == "" {
|
|
t.Error("Expected user to have username")
|
|
}
|
|
if user.UserLevel == "" {
|
|
t.Error("Expected user to have level")
|
|
}
|
|
userLevels[user.UserLevel]++
|
|
}
|
|
|
|
t.Logf("Users: Total=%d, Administrator=%d, Operator=%d, User=%d",
|
|
len(users),
|
|
userLevels["Administrator"],
|
|
userLevels["Operator"],
|
|
userLevels["User"])
|
|
})
|
|
}
|
|
|
|
// TestEnhancedMediaFeatures tests new Media service methods
|
|
func TestEnhancedMediaFeatures(t *testing.T) {
|
|
endpoint, username, password := getTestCredentials(t)
|
|
|
|
client, err := onvif.NewClient(
|
|
endpoint,
|
|
onvif.WithCredentials(username, password),
|
|
onvif.WithTimeout(30*time.Second),
|
|
)
|
|
if err != nil {
|
|
t.Fatalf("Failed to create client: %v", err)
|
|
}
|
|
|
|
ctx := context.Background()
|
|
|
|
// Initialize to get media endpoint
|
|
if err := client.Initialize(ctx); err != nil {
|
|
t.Logf("Warning: Initialize failed: %v", err)
|
|
}
|
|
|
|
t.Run("GetVideoSources", func(t *testing.T) {
|
|
sources, err := client.GetVideoSources(ctx)
|
|
if err != nil {
|
|
t.Fatalf("GetVideoSources failed: %v", err)
|
|
}
|
|
|
|
// Bosch camera has 1 video source
|
|
if len(sources) == 0 {
|
|
t.Fatal("Expected at least one video source")
|
|
}
|
|
|
|
source := sources[0]
|
|
if source.Token == "" {
|
|
t.Error("Expected source to have token")
|
|
}
|
|
|
|
// Bosch camera supports 30fps
|
|
if source.Framerate == 0 {
|
|
t.Error("Expected source to have framerate")
|
|
}
|
|
|
|
// Bosch camera has 1920x1080 resolution
|
|
if source.Resolution == nil {
|
|
t.Error("Expected source to have resolution")
|
|
} else {
|
|
if source.Resolution.Width == 0 || source.Resolution.Height == 0 {
|
|
t.Error("Expected valid resolution dimensions")
|
|
}
|
|
t.Logf("Video Source: Token=%s, Framerate=%.1ffps, Resolution=%dx%d",
|
|
source.Token, source.Framerate,
|
|
source.Resolution.Width, source.Resolution.Height)
|
|
}
|
|
})
|
|
|
|
t.Run("GetAudioSources", func(t *testing.T) {
|
|
sources, err := client.GetAudioSources(ctx)
|
|
if err != nil {
|
|
t.Fatalf("GetAudioSources failed: %v", err)
|
|
}
|
|
|
|
// Bosch camera has 1 audio source with 2 channels
|
|
if len(sources) == 0 {
|
|
t.Fatal("Expected at least one audio source")
|
|
}
|
|
|
|
source := sources[0]
|
|
if source.Token == "" {
|
|
t.Error("Expected source to have token")
|
|
}
|
|
|
|
t.Logf("Audio Source: Token=%s, Channels=%d",
|
|
source.Token, source.Channels)
|
|
})
|
|
|
|
t.Run("GetAudioOutputs", func(t *testing.T) {
|
|
outputs, err := client.GetAudioOutputs(ctx)
|
|
if err != nil {
|
|
t.Fatalf("GetAudioOutputs failed: %v", err)
|
|
}
|
|
|
|
// Bosch camera has 1 audio output
|
|
if len(outputs) == 0 {
|
|
t.Fatal("Expected at least one audio output")
|
|
}
|
|
|
|
output := outputs[0]
|
|
if output.Token == "" {
|
|
t.Error("Expected output to have token")
|
|
}
|
|
|
|
t.Logf("Audio Output: Token=%s", output.Token)
|
|
})
|
|
}
|
|
|
|
// TestEnhancedImagingFeatures tests new Imaging service methods
|
|
func TestEnhancedImagingFeatures(t *testing.T) {
|
|
endpoint, username, password := getTestCredentials(t)
|
|
|
|
client, err := onvif.NewClient(
|
|
endpoint,
|
|
onvif.WithCredentials(username, password),
|
|
onvif.WithTimeout(30*time.Second),
|
|
)
|
|
if err != nil {
|
|
t.Fatalf("Failed to create client: %v", err)
|
|
}
|
|
|
|
ctx := context.Background()
|
|
|
|
// Initialize to get imaging endpoint
|
|
if err := client.Initialize(ctx); err != nil {
|
|
t.Logf("Warning: Initialize failed: %v", err)
|
|
}
|
|
|
|
// Get video source token
|
|
sources, err := client.GetVideoSources(ctx)
|
|
if err != nil || len(sources) == 0 {
|
|
t.Skip("No video sources available for imaging tests")
|
|
}
|
|
|
|
videoSourceToken := sources[0].Token
|
|
|
|
t.Run("GetOptions", func(t *testing.T) {
|
|
options, err := client.GetOptions(ctx, videoSourceToken)
|
|
if err != nil {
|
|
t.Fatalf("GetOptions failed: %v", err)
|
|
}
|
|
|
|
if options == nil {
|
|
t.Fatal("Expected imaging options, got nil")
|
|
}
|
|
|
|
// Bosch camera supports brightness (0-255)
|
|
if options.Brightness != nil {
|
|
if options.Brightness.Min > options.Brightness.Max {
|
|
t.Error("Expected Min <= Max for brightness")
|
|
}
|
|
t.Logf("Brightness range: %.0f - %.0f",
|
|
options.Brightness.Min, options.Brightness.Max)
|
|
}
|
|
|
|
// Bosch camera supports color saturation (0-255)
|
|
if options.ColorSaturation != nil {
|
|
if options.ColorSaturation.Min > options.ColorSaturation.Max {
|
|
t.Error("Expected Min <= Max for color saturation")
|
|
}
|
|
t.Logf("ColorSaturation range: %.0f - %.0f",
|
|
options.ColorSaturation.Min, options.ColorSaturation.Max)
|
|
}
|
|
|
|
// Bosch camera supports contrast (0-255)
|
|
if options.Contrast != nil {
|
|
if options.Contrast.Min > options.Contrast.Max {
|
|
t.Error("Expected Min <= Max for contrast")
|
|
}
|
|
t.Logf("Contrast range: %.0f - %.0f",
|
|
options.Contrast.Min, options.Contrast.Max)
|
|
}
|
|
})
|
|
|
|
t.Run("GetMoveOptions", func(t *testing.T) {
|
|
moveOptions, err := client.GetMoveOptions(ctx, videoSourceToken)
|
|
if err != nil {
|
|
t.Fatalf("GetMoveOptions failed: %v", err)
|
|
}
|
|
|
|
if moveOptions == nil {
|
|
t.Fatal("Expected move options, got nil")
|
|
}
|
|
|
|
// Log available move options
|
|
hasAbsolute := moveOptions.Absolute != nil
|
|
hasRelative := moveOptions.Relative != nil
|
|
hasContinuous := moveOptions.Continuous != nil
|
|
|
|
t.Logf("Move Options: Absolute=%v, Relative=%v, Continuous=%v",
|
|
hasAbsolute, hasRelative, hasContinuous)
|
|
})
|
|
}
|