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) } }