package discovery import ( "testing" ) // TestMediaServerFromDescription_WithCDS verifies that a description that // includes a ContentDirectory service produces a valid MediaServer (ok=true) // with all fields populated. func TestMediaServerFromDescription_WithCDS(t *testing.T) { // Use the canned XML defined in ssdp_test.go (same package). location := "http://192.0.2.1:49000/rootDesc.xml" desc, err := parseDescription([]byte(cannedDescriptionXML), location) if err != nil { t.Fatalf("parseDescription: %v", err) } srv, ok := mediaServerFromDescription(desc) if !ok { t.Fatal("mediaServerFromDescription: ok=false, want true") } if srv.UDN == "" { t.Error("UDN is empty") } if srv.FriendlyName == "" { t.Error("FriendlyName is empty") } if srv.CDSControlURL == "" { t.Error("CDSControlURL is empty") } // Control URL must be absolute. if !isAbsoluteURL(srv.CDSControlURL) { t.Errorf("CDSControlURL %q is not absolute", srv.CDSControlURL) } // Icon must be resolved. if srv.IconURL == "" { t.Error("IconURL is empty") } if !isAbsoluteURL(srv.IconURL) { t.Errorf("IconURL %q is not absolute", srv.IconURL) } // Address must be the host:port from the CDS control URL. // cannedDescriptionXML has URLBase http://192.0.2.1:49000 and CDS // controlURL /ctl/ContentDir, so Address = "192.0.2.1:49000". if srv.Address == "" { t.Error("Address is empty") } if srv.Address != "192.0.2.1:49000" { t.Errorf("Address = %q, want %q", srv.Address, "192.0.2.1:49000") } t.Logf("MediaServer: UDN=%q FriendlyName=%q CDSControlURL=%q IconURL=%q Address=%q", srv.UDN, srv.FriendlyName, srv.CDSControlURL, srv.IconURL, srv.Address) } // TestMediaServerFromDescription_WithoutCDS verifies that a description // without a ContentDirectory service returns ok=false. func TestMediaServerFromDescription_WithoutCDS(t *testing.T) { const xmlNoCDS = ` urn:schemas-upnp-org:device:MediaRenderer:1 SoundTouch 20 Bose SoundTouch 20 uuid:bose-st20-0001 urn:schemas-upnp-org:service:AVTransport:1 /ctl/AVTransport ` desc, err := parseDescription([]byte(xmlNoCDS), "http://192.0.2.10:8200/desc.xml") if err != nil { t.Fatalf("parseDescription: %v", err) } _, ok := mediaServerFromDescription(desc) if ok { t.Error("mediaServerFromDescription: ok=true, want false (no ContentDirectory)") } } // TestMediaServerFromDescription_Nil ensures a nil Description returns ok=false // without panicking. func TestMediaServerFromDescription_Nil(t *testing.T) { _, ok := mediaServerFromDescription(nil) if ok { t.Error("mediaServerFromDescription(nil): ok=true, want false") } } // TestMediaServerFromDescription_FlatServer verifies a flat description (no // sub-devices, CDS in root) maps correctly. func TestMediaServerFromDescription_FlatServer(t *testing.T) { const xmlFlat = ` http://198.51.100.20:8200 urn:schemas-upnp-org:device:MediaServer:1 MiniDLNA Justin Maggard MiniDLNA uuid:minidlna-0001 image/png 48 48 /icons/sm.png urn:schemas-upnp-org:service:ContentDirectory:1 /ctl/ContentDir ` desc, err := parseDescription([]byte(xmlFlat), "http://198.51.100.20:8200/rootDesc.xml") if err != nil { t.Fatalf("parseDescription: %v", err) } srv, ok := mediaServerFromDescription(desc) if !ok { t.Fatal("mediaServerFromDescription: ok=false, want true") } if srv.FriendlyName != "MiniDLNA" { t.Errorf("FriendlyName = %q, want %q", srv.FriendlyName, "MiniDLNA") } if srv.UDN != "uuid:minidlna-0001" { t.Errorf("UDN = %q, want %q", srv.UDN, "uuid:minidlna-0001") } wantCDS := "http://198.51.100.20:8200/ctl/ContentDir" if srv.CDSControlURL != wantCDS { t.Errorf("CDSControlURL = %q, want %q", srv.CDSControlURL, wantCDS) } wantIcon := "http://198.51.100.20:8200/icons/sm.png" if srv.IconURL != wantIcon { t.Errorf("IconURL = %q, want %q", srv.IconURL, wantIcon) } // Address must reflect the host:port of the CDS control URL. // URLBase is http://198.51.100.20:8200 and CDS controlURL is /ctl/ContentDir. if srv.Address != "198.51.100.20:8200" { t.Errorf("Address = %q, want %q", srv.Address, "198.51.100.20:8200") } } // isAbsoluteURL returns true when s starts with "http://" or "https://". func isAbsoluteURL(s string) bool { return len(s) > 7 && (s[:7] == "http://" || (len(s) > 8 && s[:8] == "https://")) }