github.com/divyam234/rclone@v1.64.1/cmd/serve/dlna/dlna_test.go (about)

     1  package dlna
     2  
     3  import (
     4  	"bytes"
     5  	"context"
     6  	"fmt"
     7  	"html"
     8  	"io"
     9  	"net/http"
    10  	"os"
    11  	"strings"
    12  	"testing"
    13  
    14  	"github.com/anacrolix/dms/soap"
    15  
    16  	"github.com/divyam234/rclone/fs/config/configfile"
    17  	"github.com/divyam234/rclone/vfs"
    18  
    19  	_ "github.com/divyam234/rclone/backend/local"
    20  	"github.com/divyam234/rclone/cmd/serve/dlna/dlnaflags"
    21  	"github.com/divyam234/rclone/fs"
    22  	"github.com/stretchr/testify/assert"
    23  	"github.com/stretchr/testify/require"
    24  )
    25  
    26  var (
    27  	dlnaServer *server
    28  	baseURL    string
    29  )
    30  
    31  const (
    32  	testBindAddress = "localhost:0"
    33  )
    34  
    35  func startServer(t *testing.T, f fs.Fs) {
    36  	opt := dlnaflags.DefaultOpt
    37  	opt.ListenAddr = testBindAddress
    38  	var err error
    39  	dlnaServer, err = newServer(f, &opt)
    40  	assert.NoError(t, err)
    41  	assert.NoError(t, dlnaServer.Serve())
    42  	baseURL = "http://" + dlnaServer.HTTPConn.Addr().String()
    43  }
    44  
    45  func TestInit(t *testing.T) {
    46  	configfile.Install()
    47  
    48  	f, err := fs.NewFs(context.Background(), "testdata/files")
    49  	l, _ := f.List(context.Background(), "")
    50  	fmt.Println(l)
    51  	require.NoError(t, err)
    52  
    53  	startServer(t, f)
    54  }
    55  
    56  // Make sure that it serves rootDesc.xml (SCPD in uPnP parlance).
    57  func TestRootSCPD(t *testing.T) {
    58  	req, err := http.NewRequest("GET", baseURL+rootDescPath, nil)
    59  	require.NoError(t, err)
    60  	resp, err := http.DefaultClient.Do(req)
    61  	require.NoError(t, err)
    62  	assert.Equal(t, http.StatusOK, resp.StatusCode)
    63  	body, err := io.ReadAll(resp.Body)
    64  	require.NoError(t, err)
    65  	// Make sure that the SCPD contains a CDS service.
    66  	require.Contains(t, string(body),
    67  		"<serviceType>urn:schemas-upnp-org:service:ContentDirectory:1</serviceType>")
    68  	// Make sure that the SCPD contains a CM service.
    69  	require.Contains(t, string(body),
    70  		"<serviceType>urn:schemas-upnp-org:service:ConnectionManager:1</serviceType>")
    71  	// Ensure that the SCPD url is configured.
    72  	require.Regexp(t, "<SCPDURL>/.*</SCPDURL>", string(body))
    73  }
    74  
    75  // Make sure that it serves content from the remote.
    76  func TestServeContent(t *testing.T) {
    77  	req, err := http.NewRequest("GET", baseURL+resPath+"video.mp4", nil)
    78  	require.NoError(t, err)
    79  	resp, err := http.DefaultClient.Do(req)
    80  	require.NoError(t, err)
    81  	defer fs.CheckClose(resp.Body, &err)
    82  	assert.Equal(t, http.StatusOK, resp.StatusCode)
    83  	actualContents, err := io.ReadAll(resp.Body)
    84  	assert.NoError(t, err)
    85  
    86  	// Now compare the contents with the golden file.
    87  	node, err := dlnaServer.vfs.Stat("/video.mp4")
    88  	assert.NoError(t, err)
    89  	goldenFile := node.(*vfs.File)
    90  	goldenReader, err := goldenFile.Open(os.O_RDONLY)
    91  	assert.NoError(t, err)
    92  	defer fs.CheckClose(goldenReader, &err)
    93  	goldenContents, err := io.ReadAll(goldenReader)
    94  	assert.NoError(t, err)
    95  
    96  	require.Equal(t, goldenContents, actualContents)
    97  }
    98  
    99  // Check that ContentDirectory#Browse returns appropriate metadata on the root container.
   100  func TestContentDirectoryBrowseMetadata(t *testing.T) {
   101  	// Sample from: https://github.com/divyam234/rclone/issues/3253#issuecomment-524317469
   102  	req, err := http.NewRequest("POST", baseURL+serviceControlURL, strings.NewReader(`
   103  <?xml version="1.0" encoding="utf-8"?>
   104  <s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/"
   105              s:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
   106      <s:Body>
   107          <u:Browse xmlns:u="urn:schemas-upnp-org:service:ContentDirectory:1">
   108              <ObjectID>0</ObjectID>
   109              <BrowseFlag>BrowseMetadata</BrowseFlag>
   110              <Filter>*</Filter>
   111              <StartingIndex>0</StartingIndex>
   112              <RequestedCount>0</RequestedCount>
   113              <SortCriteria></SortCriteria>
   114          </u:Browse>
   115      </s:Body>
   116  </s:Envelope>`))
   117  	require.NoError(t, err)
   118  	req.Header.Set("SOAPACTION", `"urn:schemas-upnp-org:service:ContentDirectory:1#Browse"`)
   119  	resp, err := http.DefaultClient.Do(req)
   120  	require.NoError(t, err)
   121  	assert.Equal(t, http.StatusOK, resp.StatusCode)
   122  	body, err := io.ReadAll(resp.Body)
   123  	require.NoError(t, err)
   124  	// should contain an appropriate URN
   125  	require.Contains(t, string(body), "urn:schemas-upnp-org:service:ContentDirectory:1")
   126  	// expect a <container> element
   127  	require.Contains(t, string(body), html.EscapeString("<container "))
   128  	require.NotContains(t, string(body), html.EscapeString("<item "))
   129  	// if there is a childCount, it better not be zero
   130  	require.NotContains(t, string(body), html.EscapeString(" childCount=\"0\""))
   131  	// should have a dc:date element
   132  	require.Contains(t, string(body), html.EscapeString("<dc:date>"))
   133  }
   134  
   135  // Check that the X_MS_MediaReceiverRegistrar is faked out properly.
   136  func TestMediaReceiverRegistrarService(t *testing.T) {
   137  	env := soap.Envelope{
   138  		Body: soap.Body{
   139  			Action: []byte("RegisterDevice"),
   140  		},
   141  	}
   142  	req, err := http.NewRequest("POST", baseURL+serviceControlURL, bytes.NewReader(mustMarshalXML(env)))
   143  	require.NoError(t, err)
   144  	req.Header.Set("SOAPACTION", `"urn:microsoft.com:service:X_MS_MediaReceiverRegistrar:1#RegisterDevice"`)
   145  	resp, err := http.DefaultClient.Do(req)
   146  	require.NoError(t, err)
   147  	assert.Equal(t, http.StatusOK, resp.StatusCode)
   148  	body, err := io.ReadAll(resp.Body)
   149  	require.NoError(t, err)
   150  	require.Contains(t, string(body), "<RegistrationRespMsg>")
   151  }
   152  
   153  // Check that ContentDirectory#Browse returns the expected items.
   154  func TestContentDirectoryBrowseDirectChildren(t *testing.T) {
   155  	// First the root...
   156  	req, err := http.NewRequest("POST", baseURL+serviceControlURL, strings.NewReader(`
   157  <?xml version="1.0" encoding="utf-8"?>
   158  <s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/"
   159              s:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
   160      <s:Body>
   161          <u:Browse xmlns:u="urn:schemas-upnp-org:service:ContentDirectory:1">
   162              <ObjectID>0</ObjectID>
   163              <BrowseFlag>BrowseDirectChildren</BrowseFlag>
   164              <Filter>*</Filter>
   165              <StartingIndex>0</StartingIndex>
   166              <RequestedCount>0</RequestedCount>
   167              <SortCriteria></SortCriteria>
   168          </u:Browse>
   169      </s:Body>
   170  </s:Envelope>`))
   171  	require.NoError(t, err)
   172  	req.Header.Set("SOAPACTION", `"urn:schemas-upnp-org:service:ContentDirectory:1#Browse"`)
   173  	resp, err := http.DefaultClient.Do(req)
   174  	require.NoError(t, err)
   175  	assert.Equal(t, http.StatusOK, resp.StatusCode)
   176  	body, err := io.ReadAll(resp.Body)
   177  	require.NoError(t, err)
   178  	// expect video.mp4, video.srt, video.en.srt URLs to be in the DIDL
   179  	require.Contains(t, string(body), "/r/video.mp4")
   180  	require.Contains(t, string(body), "/r/video.srt")
   181  	require.Contains(t, string(body), "/r/video.en.srt")
   182  
   183  	// Then a subdirectory
   184  	req, err = http.NewRequest("POST", baseURL+serviceControlURL, strings.NewReader(`
   185  <?xml version="1.0" encoding="utf-8"?>
   186  <s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/"
   187              s:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
   188      <s:Body>
   189          <u:Browse xmlns:u="urn:schemas-upnp-org:service:ContentDirectory:1">
   190              <ObjectID>%2Fsubdir</ObjectID>
   191              <BrowseFlag>BrowseDirectChildren</BrowseFlag>
   192              <Filter>*</Filter>
   193              <StartingIndex>0</StartingIndex>
   194              <RequestedCount>0</RequestedCount>
   195              <SortCriteria></SortCriteria>
   196          </u:Browse>
   197      </s:Body>
   198  </s:Envelope>`))
   199  	require.NoError(t, err)
   200  	req.Header.Set("SOAPACTION", `"urn:schemas-upnp-org:service:ContentDirectory:1#Browse"`)
   201  	resp, err = http.DefaultClient.Do(req)
   202  	require.NoError(t, err)
   203  	assert.Equal(t, http.StatusOK, resp.StatusCode)
   204  	body, err = io.ReadAll(resp.Body)
   205  	require.NoError(t, err)
   206  	// expect video.mp4, video.srt, URLs to be in the DIDL
   207  	require.Contains(t, string(body), "/r/subdir/video.mp4")
   208  	require.Contains(t, string(body), "/r/subdir/video.srt")
   209  }