package onvif
import (
"context"
"io"
"net/http"
"net/http/httptest"
"strings"
"testing"
)
// Test device information from real camera:
// Manufacturer: Bosch
// Model: FLEXIDOME indoor 5100i IR
// Firmware: 8.71.0066
// Serial Number: 404754734001050102
// Hardware ID: F000B543
// TestGetDeviceInformation_Bosch tests GetDeviceInformation with real camera response.
func TestGetDeviceInformation_Bosch(t *testing.T) {
// Real SOAP response from Bosch FLEXIDOME indoor 5100i IR (FW: 8.71.0066)
realResponse := `
Bosch
FLEXIDOME indoor 5100i IR
8.71.0066
404754734001050102
F000B543
`
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
body, err := io.ReadAll(r.Body)
if err != nil {
t.Fatalf("Failed to read request body: %v", err)
}
bodyStr := string(body)
if !strings.Contains(bodyStr, "GetDeviceInformation") {
t.Errorf("Request should contain GetDeviceInformation, got: %s", bodyStr)
}
w.Header().Set("Content-Type", "application/soap+xml")
w.WriteHeader(http.StatusOK)
w.Write([]byte(realResponse))
}))
defer server.Close()
client, err := NewClient(server.URL, WithCredentials("service", "Service.1234"))
if err != nil {
t.Fatalf("NewClient() failed: %v", err)
}
ctx := context.Background()
info, err := client.GetDeviceInformation(ctx)
if err != nil {
t.Fatalf("GetDeviceInformation() failed: %v", err)
}
// Validate response matches real camera
if info.Manufacturer != "Bosch" {
t.Errorf("Expected Manufacturer=Bosch (Bosch FLEXIDOME), got %s", info.Manufacturer)
}
if info.Model != "FLEXIDOME indoor 5100i IR" {
t.Errorf("Expected Model=FLEXIDOME indoor 5100i IR (Bosch FLEXIDOME), got %s", info.Model)
}
if info.FirmwareVersion != "8.71.0066" {
t.Errorf("Expected FirmwareVersion=8.71.0066 (Bosch FLEXIDOME), got %s", info.FirmwareVersion)
}
if info.SerialNumber != "404754734001050102" {
t.Errorf("Expected SerialNumber=404754734001050102 (Bosch FLEXIDOME), got %s", info.SerialNumber)
}
if info.HardwareID != "F000B543" {
t.Errorf("Expected HardwareID=F000B543 (Bosch FLEXIDOME), got %s", info.HardwareID)
}
}
// TestGetCapabilities_Bosch tests GetCapabilities with real camera response.
func TestGetCapabilities_Bosch(t *testing.T) {
// Real SOAP response from Bosch FLEXIDOME indoor 5100i IR (FW: 8.71.0066)
realResponse := `
http://192.168.1.201/onvif/device_service
false
true
false
false
false
false
false
false
false
false
1 2
1
1
false
true
false
false
false
false
false
false
http://192.168.1.201/onvif/media_service
true
false
true
http://192.168.1.201/onvif/imaging_service
http://192.168.1.201/onvif/event_service
false
false
false
http://192.168.1.201/onvif/analytics_service
true
true
`
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
body, err := io.ReadAll(r.Body)
if err != nil {
t.Fatalf("Failed to read request body: %v", err)
}
bodyStr := string(body)
if !strings.Contains(bodyStr, "GetCapabilities") {
t.Errorf("Request should contain GetCapabilities, got: %s", bodyStr)
}
w.Header().Set("Content-Type", "application/soap+xml")
w.WriteHeader(http.StatusOK)
w.Write([]byte(realResponse))
}))
defer server.Close()
client, err := NewClient(server.URL, WithCredentials("service", "Service.1234"))
if err != nil {
t.Fatalf("NewClient() failed: %v", err)
}
ctx := context.Background()
caps, err := client.GetCapabilities(ctx)
if err != nil {
t.Fatalf("GetCapabilities() failed: %v", err)
}
// Validate response matches real camera
if caps.Device == nil {
t.Fatal("Expected Device capabilities from Bosch FLEXIDOME")
}
if !strings.Contains(caps.Device.XAddr, "device_service") {
t.Errorf("Expected device service XAddr from Bosch FLEXIDOME, got %s", caps.Device.XAddr)
}
if caps.Device.Network == nil {
t.Fatal("Expected Network capabilities from Bosch FLEXIDOME")
}
if !caps.Device.Network.ZeroConfiguration {
t.Error("Expected ZeroConfiguration=true from Bosch FLEXIDOME")
}
if caps.Device.Security == nil {
t.Fatal("Expected Security capabilities from Bosch FLEXIDOME")
}
if !caps.Device.Security.TLS12 {
t.Error("Expected TLS12=true from Bosch FLEXIDOME")
}
if caps.Media == nil {
t.Fatal("Expected Media capabilities from Bosch FLEXIDOME")
}
if !strings.Contains(caps.Media.XAddr, "media_service") {
t.Errorf("Expected media service XAddr from Bosch FLEXIDOME, got %s", caps.Media.XAddr)
}
if caps.Media.StreamingCapabilities == nil {
t.Fatal("Expected StreamingCapabilities from Bosch FLEXIDOME")
}
if !caps.Media.StreamingCapabilities.RTPMulticast {
t.Error("Expected RTPMulticast=true from Bosch FLEXIDOME")
}
}
// TestGetServices_Bosch tests GetServices with real camera response.
func TestGetServices_Bosch(t *testing.T) {
// Real SOAP response from Bosch FLEXIDOME indoor 5100i IR (FW: 8.71.0066)
realResponse := `
http://www.onvif.org/ver10/device/wsdl
http://192.168.1.201/onvif/device_service
1
3
http://www.onvif.org/ver10/media/wsdl
http://192.168.1.201/onvif/media_service
1
3
http://www.onvif.org/ver10/events/wsdl
http://192.168.1.201/onvif/event_service
1
4
`
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
body, err := io.ReadAll(r.Body)
if err != nil {
t.Fatalf("Failed to read request body: %v", err)
}
bodyStr := string(body)
if !strings.Contains(bodyStr, "GetServices") {
t.Errorf("Request should contain GetServices, got: %s", bodyStr)
}
w.Header().Set("Content-Type", "application/soap+xml")
w.WriteHeader(http.StatusOK)
w.Write([]byte(realResponse))
}))
defer server.Close()
client, err := NewClient(server.URL, WithCredentials("service", "Service.1234"))
if err != nil {
t.Fatalf("NewClient() failed: %v", err)
}
ctx := context.Background()
services, err := client.GetServices(ctx, false)
if err != nil {
t.Fatalf("GetServices() failed: %v", err)
}
// Validate response matches real camera
if len(services) == 0 {
t.Fatal("Expected at least one service from Bosch FLEXIDOME")
}
// Check for Device service
foundDevice := false
for _, svc := range services {
if svc.Namespace == "http://www.onvif.org/ver10/device/wsdl" {
foundDevice = true
if svc.Version.Major != 1 || svc.Version.Minor != 3 {
t.Errorf("Expected Device service version 1.3 (Bosch FLEXIDOME), got %d.%d", svc.Version.Major, svc.Version.Minor)
}
if !strings.Contains(svc.XAddr, "device_service") {
t.Errorf("Expected device_service in XAddr (Bosch FLEXIDOME), got %s", svc.XAddr)
}
}
}
if !foundDevice {
t.Error("Expected Device service from Bosch FLEXIDOME")
}
}
// TestGetServiceCapabilities_Bosch tests GetServiceCapabilities with real camera response.
func TestGetServiceCapabilities_Bosch(t *testing.T) {
// Real SOAP response from Bosch FLEXIDOME indoor 5100i IR (FW: 8.71.0066)
// Note: Uses attributes, not child elements
realResponse := `
`
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
body, err := io.ReadAll(r.Body)
if err != nil {
t.Fatalf("Failed to read request body: %v", err)
}
bodyStr := string(body)
if !strings.Contains(bodyStr, "GetServiceCapabilities") {
t.Errorf("Request should contain GetServiceCapabilities, got: %s", bodyStr)
}
w.Header().Set("Content-Type", "application/soap+xml")
w.WriteHeader(http.StatusOK)
w.Write([]byte(realResponse))
}))
defer server.Close()
client, err := NewClient(server.URL, WithCredentials("service", "Service.1234"))
if err != nil {
t.Fatalf("NewClient() failed: %v", err)
}
ctx := context.Background()
caps, err := client.GetServiceCapabilities(ctx)
if err != nil {
t.Fatalf("GetServiceCapabilities() failed: %v", err)
}
// Validate response matches real camera
if caps.Network == nil {
t.Fatal("Expected Network capabilities from Bosch FLEXIDOME")
}
if !caps.Network.ZeroConfiguration {
t.Error("Expected ZeroConfiguration=true from Bosch FLEXIDOME")
}
if caps.Security == nil {
t.Fatal("Expected Security capabilities from Bosch FLEXIDOME")
}
if !caps.Security.TLS12 {
t.Error("Expected TLS12=true from Bosch FLEXIDOME")
}
}
// TestGetSystemDateAndTime_Bosch tests GetSystemDateAndTime with real camera response.
func TestGetSystemDateAndTime_Bosch(t *testing.T) {
// Real SOAP response from Bosch FLEXIDOME indoor 5100i IR (FW: 8.71.0066)
realResponse := `
Manual
false
CST6CDT
4
56
14
2025
12
2
`
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
body, err := io.ReadAll(r.Body)
if err != nil {
t.Fatalf("Failed to read request body: %v", err)
}
bodyStr := string(body)
if !strings.Contains(bodyStr, "GetSystemDateAndTime") {
t.Errorf("Request should contain GetSystemDateAndTime, got: %s", bodyStr)
}
w.Header().Set("Content-Type", "application/soap+xml")
w.WriteHeader(http.StatusOK)
w.Write([]byte(realResponse))
}))
defer server.Close()
client, err := NewClient(server.URL, WithCredentials("service", "Service.1234"))
if err != nil {
t.Fatalf("NewClient() failed: %v", err)
}
ctx := context.Background()
dateTime, err := client.GetSystemDateAndTime(ctx)
if err != nil {
t.Fatalf("GetSystemDateAndTime() failed: %v", err)
}
// GetSystemDateAndTime returns interface{} - just verify no error
// The actual structure depends on the camera's response format
_ = dateTime // Acknowledge we received a response
}
// TestGetHostname_Bosch tests GetHostname with real camera response.
func TestGetHostname_Bosch(t *testing.T) {
// Real SOAP response from Bosch FLEXIDOME indoor 5100i IR (FW: 8.71.0066)
realResponse := `
false
BOSCH-404754734001050102
`
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
body, err := io.ReadAll(r.Body)
if err != nil {
t.Fatalf("Failed to read request body: %v", err)
}
bodyStr := string(body)
if !strings.Contains(bodyStr, "GetHostname") {
t.Errorf("Request should contain GetHostname, got: %s", bodyStr)
}
w.Header().Set("Content-Type", "application/soap+xml")
w.WriteHeader(http.StatusOK)
w.Write([]byte(realResponse))
}))
defer server.Close()
client, err := NewClient(server.URL, WithCredentials("service", "Service.1234"))
if err != nil {
t.Fatalf("NewClient() failed: %v", err)
}
ctx := context.Background()
hostname, err := client.GetHostname(ctx)
if err != nil {
t.Fatalf("GetHostname() failed: %v", err)
}
// Validate response matches real camera
if hostname == nil {
t.Fatal("Expected HostnameInformation from Bosch FLEXIDOME")
}
if !strings.Contains(hostname.Name, "BOSCH") {
t.Errorf("Expected hostname to contain BOSCH (Bosch FLEXIDOME), got %s", hostname.Name)
}
if hostname.FromDHCP {
t.Error("Expected FromDHCP=false from Bosch FLEXIDOME")
}
}
// TestGetScopes_Bosch tests GetScopes with real camera response.
func TestGetScopes_Bosch(t *testing.T) {
// Real SOAP response from Bosch FLEXIDOME indoor 5100i IR (FW: 8.71.0066)
realResponse := `
Fixed
onvif://www.onvif.org/name/BOSCH-404754734001050102
Fixed
onvif://www.onvif.org/location/
Fixed
onvif://www.onvif.org/hardware/F000B543
`
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
body, err := io.ReadAll(r.Body)
if err != nil {
t.Fatalf("Failed to read request body: %v", err)
}
bodyStr := string(body)
if !strings.Contains(bodyStr, "GetScopes") {
t.Errorf("Request should contain GetScopes, got: %s", bodyStr)
}
w.Header().Set("Content-Type", "application/soap+xml")
w.WriteHeader(http.StatusOK)
w.Write([]byte(realResponse))
}))
defer server.Close()
client, err := NewClient(server.URL, WithCredentials("service", "Service.1234"))
if err != nil {
t.Fatalf("NewClient() failed: %v", err)
}
ctx := context.Background()
scopes, err := client.GetScopes(ctx)
if err != nil {
t.Fatalf("GetScopes() failed: %v", err)
}
// Validate response matches real camera
if len(scopes) == 0 {
t.Fatal("Expected at least one scope from Bosch FLEXIDOME")
}
// Check for hardware scope
foundHardware := false
for _, scope := range scopes {
if strings.Contains(scope.ScopeItem, "hardware") {
foundHardware = true
if !strings.Contains(scope.ScopeItem, "F000B543") {
t.Errorf("Expected hardware ID F000B543 in scope (Bosch FLEXIDOME), got %s", scope.ScopeItem)
}
}
}
if !foundHardware {
t.Error("Expected hardware scope from Bosch FLEXIDOME")
}
}
// TestGetUsers_Bosch tests GetUsers with real camera response.
func TestGetUsers_Bosch(t *testing.T) {
// Real SOAP response from Bosch FLEXIDOME indoor 5100i IR (FW: 8.71.0066)
realResponse := `
service
Administrator
`
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
body, err := io.ReadAll(r.Body)
if err != nil {
t.Fatalf("Failed to read request body: %v", err)
}
bodyStr := string(body)
if !strings.Contains(bodyStr, "GetUsers") {
t.Errorf("Request should contain GetUsers, got: %s", bodyStr)
}
w.Header().Set("Content-Type", "application/soap+xml")
w.WriteHeader(http.StatusOK)
w.Write([]byte(realResponse))
}))
defer server.Close()
client, err := NewClient(server.URL, WithCredentials("service", "Service.1234"))
if err != nil {
t.Fatalf("NewClient() failed: %v", err)
}
ctx := context.Background()
users, err := client.GetUsers(ctx)
if err != nil {
t.Fatalf("GetUsers() failed: %v", err)
}
// Validate response matches real camera
if len(users) == 0 {
t.Fatal("Expected at least one user from Bosch FLEXIDOME")
}
if users[0].Username != "service" {
t.Errorf("Expected username=service (Bosch FLEXIDOME), got %s", users[0].Username)
}
if users[0].UserLevel != "Administrator" {
t.Errorf("Expected UserLevel=Administrator (Bosch FLEXIDOME), got %s", users[0].UserLevel)
}
}