github.com/10XDev/rclone@v1.52.3-0.20200626220027-16af9ab76b2a/backend/http/http_internal_test.go (about)

     1  package http
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"io/ioutil"
     7  	"net/http"
     8  	"net/http/httptest"
     9  	"net/url"
    10  	"os"
    11  	"path/filepath"
    12  	"sort"
    13  	"strings"
    14  	"testing"
    15  	"time"
    16  
    17  	"github.com/rclone/rclone/fs"
    18  	"github.com/rclone/rclone/fs/config"
    19  	"github.com/rclone/rclone/fs/config/configmap"
    20  	"github.com/rclone/rclone/fstest"
    21  	"github.com/rclone/rclone/lib/rest"
    22  	"github.com/stretchr/testify/assert"
    23  	"github.com/stretchr/testify/require"
    24  )
    25  
    26  var (
    27  	remoteName = "TestHTTP"
    28  	testPath   = "test"
    29  	filesPath  = filepath.Join(testPath, "files")
    30  	headers    = []string{"X-Potato", "sausage", "X-Rhubarb", "cucumber"}
    31  )
    32  
    33  // prepareServer the test server and return a function to tidy it up afterwards
    34  func prepareServer(t *testing.T) (configmap.Simple, func()) {
    35  	// file server for test/files
    36  	fileServer := http.FileServer(http.Dir(filesPath))
    37  
    38  	// test the headers are there then pass on to fileServer
    39  	handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
    40  		what := fmt.Sprintf("%s %s: Header ", r.Method, r.URL.Path)
    41  		assert.Equal(t, headers[1], r.Header.Get(headers[0]), what+headers[0])
    42  		assert.Equal(t, headers[3], r.Header.Get(headers[2]), what+headers[2])
    43  		fileServer.ServeHTTP(w, r)
    44  	})
    45  
    46  	// Make the test server
    47  	ts := httptest.NewServer(handler)
    48  
    49  	// Configure the remote
    50  	config.LoadConfig()
    51  	// fs.Config.LogLevel = fs.LogLevelDebug
    52  	// fs.Config.DumpHeaders = true
    53  	// fs.Config.DumpBodies = true
    54  	// config.FileSet(remoteName, "type", "http")
    55  	// config.FileSet(remoteName, "url", ts.URL)
    56  
    57  	m := configmap.Simple{
    58  		"type":    "http",
    59  		"url":     ts.URL,
    60  		"headers": strings.Join(headers, ","),
    61  	}
    62  
    63  	// return a function to tidy up
    64  	return m, ts.Close
    65  }
    66  
    67  // prepare the test server and return a function to tidy it up afterwards
    68  func prepare(t *testing.T) (fs.Fs, func()) {
    69  	m, tidy := prepareServer(t)
    70  
    71  	// Instantiate it
    72  	f, err := NewFs(remoteName, "", m)
    73  	require.NoError(t, err)
    74  
    75  	return f, tidy
    76  }
    77  
    78  func testListRoot(t *testing.T, f fs.Fs, noSlash bool) {
    79  	entries, err := f.List(context.Background(), "")
    80  	require.NoError(t, err)
    81  
    82  	sort.Sort(entries)
    83  
    84  	require.Equal(t, 4, len(entries))
    85  
    86  	e := entries[0]
    87  	assert.Equal(t, "four", e.Remote())
    88  	assert.Equal(t, int64(-1), e.Size())
    89  	_, ok := e.(fs.Directory)
    90  	assert.True(t, ok)
    91  
    92  	e = entries[1]
    93  	assert.Equal(t, "one%.txt", e.Remote())
    94  	assert.Equal(t, int64(6), e.Size())
    95  	_, ok = e.(*Object)
    96  	assert.True(t, ok)
    97  
    98  	e = entries[2]
    99  	assert.Equal(t, "three", e.Remote())
   100  	assert.Equal(t, int64(-1), e.Size())
   101  	_, ok = e.(fs.Directory)
   102  	assert.True(t, ok)
   103  
   104  	e = entries[3]
   105  	assert.Equal(t, "two.html", e.Remote())
   106  	if noSlash {
   107  		assert.Equal(t, int64(-1), e.Size())
   108  		_, ok = e.(fs.Directory)
   109  		assert.True(t, ok)
   110  	} else {
   111  		assert.Equal(t, int64(41), e.Size())
   112  		_, ok = e.(*Object)
   113  		assert.True(t, ok)
   114  	}
   115  }
   116  
   117  func TestListRoot(t *testing.T) {
   118  	f, tidy := prepare(t)
   119  	defer tidy()
   120  	testListRoot(t, f, false)
   121  }
   122  
   123  func TestListRootNoSlash(t *testing.T) {
   124  	f, tidy := prepare(t)
   125  	f.(*Fs).opt.NoSlash = true
   126  	defer tidy()
   127  
   128  	testListRoot(t, f, true)
   129  }
   130  
   131  func TestListSubDir(t *testing.T) {
   132  	f, tidy := prepare(t)
   133  	defer tidy()
   134  
   135  	entries, err := f.List(context.Background(), "three")
   136  	require.NoError(t, err)
   137  
   138  	sort.Sort(entries)
   139  
   140  	assert.Equal(t, 1, len(entries))
   141  
   142  	e := entries[0]
   143  	assert.Equal(t, "three/underthree.txt", e.Remote())
   144  	assert.Equal(t, int64(9), e.Size())
   145  	_, ok := e.(*Object)
   146  	assert.True(t, ok)
   147  }
   148  
   149  func TestNewObject(t *testing.T) {
   150  	f, tidy := prepare(t)
   151  	defer tidy()
   152  
   153  	o, err := f.NewObject(context.Background(), "four/under four.txt")
   154  	require.NoError(t, err)
   155  
   156  	assert.Equal(t, "four/under four.txt", o.Remote())
   157  	assert.Equal(t, int64(9), o.Size())
   158  	_, ok := o.(*Object)
   159  	assert.True(t, ok)
   160  
   161  	// Test the time is correct on the object
   162  
   163  	tObj := o.ModTime(context.Background())
   164  
   165  	fi, err := os.Stat(filepath.Join(filesPath, "four", "under four.txt"))
   166  	require.NoError(t, err)
   167  	tFile := fi.ModTime()
   168  
   169  	fstest.AssertTimeEqualWithPrecision(t, o.Remote(), tFile, tObj, time.Second)
   170  
   171  	// check object not found
   172  	o, err = f.NewObject(context.Background(), "not found.txt")
   173  	assert.Nil(t, o)
   174  	assert.Equal(t, fs.ErrorObjectNotFound, err)
   175  }
   176  
   177  func TestOpen(t *testing.T) {
   178  	f, tidy := prepare(t)
   179  	defer tidy()
   180  
   181  	o, err := f.NewObject(context.Background(), "four/under four.txt")
   182  	require.NoError(t, err)
   183  
   184  	// Test normal read
   185  	fd, err := o.Open(context.Background())
   186  	require.NoError(t, err)
   187  	data, err := ioutil.ReadAll(fd)
   188  	require.NoError(t, err)
   189  	require.NoError(t, fd.Close())
   190  	assert.Equal(t, "beetroot\n", string(data))
   191  
   192  	// Test with range request
   193  	fd, err = o.Open(context.Background(), &fs.RangeOption{Start: 1, End: 5})
   194  	require.NoError(t, err)
   195  	data, err = ioutil.ReadAll(fd)
   196  	require.NoError(t, err)
   197  	require.NoError(t, fd.Close())
   198  	assert.Equal(t, "eetro", string(data))
   199  }
   200  
   201  func TestMimeType(t *testing.T) {
   202  	f, tidy := prepare(t)
   203  	defer tidy()
   204  
   205  	o, err := f.NewObject(context.Background(), "four/under four.txt")
   206  	require.NoError(t, err)
   207  
   208  	do, ok := o.(fs.MimeTyper)
   209  	require.True(t, ok)
   210  	assert.Equal(t, "text/plain; charset=utf-8", do.MimeType(context.Background()))
   211  }
   212  
   213  func TestIsAFileRoot(t *testing.T) {
   214  	m, tidy := prepareServer(t)
   215  	defer tidy()
   216  
   217  	f, err := NewFs(remoteName, "one%.txt", m)
   218  	assert.Equal(t, err, fs.ErrorIsFile)
   219  
   220  	testListRoot(t, f, false)
   221  }
   222  
   223  func TestIsAFileSubDir(t *testing.T) {
   224  	m, tidy := prepareServer(t)
   225  	defer tidy()
   226  
   227  	f, err := NewFs(remoteName, "three/underthree.txt", m)
   228  	assert.Equal(t, err, fs.ErrorIsFile)
   229  
   230  	entries, err := f.List(context.Background(), "")
   231  	require.NoError(t, err)
   232  
   233  	sort.Sort(entries)
   234  
   235  	assert.Equal(t, 1, len(entries))
   236  
   237  	e := entries[0]
   238  	assert.Equal(t, "underthree.txt", e.Remote())
   239  	assert.Equal(t, int64(9), e.Size())
   240  	_, ok := e.(*Object)
   241  	assert.True(t, ok)
   242  }
   243  
   244  func TestParseName(t *testing.T) {
   245  	for i, test := range []struct {
   246  		base    string
   247  		val     string
   248  		wantErr error
   249  		want    string
   250  	}{
   251  		{"http://example.com/", "potato", nil, "potato"},
   252  		{"http://example.com/dir/", "potato", nil, "potato"},
   253  		{"http://example.com/dir/", "potato?download=true", errFoundQuestionMark, ""},
   254  		{"http://example.com/dir/", "../dir/potato", nil, "potato"},
   255  		{"http://example.com/dir/", "..", errNotUnderRoot, ""},
   256  		{"http://example.com/dir/", "http://example.com/", errNotUnderRoot, ""},
   257  		{"http://example.com/dir/", "http://example.com/dir/", errNameIsEmpty, ""},
   258  		{"http://example.com/dir/", "http://example.com/dir/potato", nil, "potato"},
   259  		{"http://example.com/dir/", "https://example.com/dir/potato", errSchemeMismatch, ""},
   260  		{"http://example.com/dir/", "http://notexample.com/dir/potato", errHostMismatch, ""},
   261  		{"http://example.com/dir/", "/dir/", errNameIsEmpty, ""},
   262  		{"http://example.com/dir/", "/dir/potato", nil, "potato"},
   263  		{"http://example.com/dir/", "subdir/potato", errNameContainsSlash, ""},
   264  		{"http://example.com/dir/", "With percent %25.txt", nil, "With percent %.txt"},
   265  		{"http://example.com/dir/", "With colon :", errURLJoinFailed, ""},
   266  		{"http://example.com/dir/", rest.URLPathEscape("With colon :"), nil, "With colon :"},
   267  		{"http://example.com/Dungeons%20%26%20Dragons/", "/Dungeons%20&%20Dragons/D%26D%20Basic%20%28Holmes%2C%20B%2C%20X%2C%20BECMI%29/", nil, "D&D Basic (Holmes, B, X, BECMI)/"},
   268  	} {
   269  		u, err := url.Parse(test.base)
   270  		require.NoError(t, err)
   271  		got, gotErr := parseName(u, test.val)
   272  		what := fmt.Sprintf("test %d base=%q, val=%q", i, test.base, test.val)
   273  		assert.Equal(t, test.wantErr, gotErr, what)
   274  		assert.Equal(t, test.want, got, what)
   275  	}
   276  }
   277  
   278  // Load HTML from the file given and parse it, checking it against the entries passed in
   279  func parseHTML(t *testing.T, name string, base string, want []string) {
   280  	in, err := os.Open(filepath.Join(testPath, "index_files", name))
   281  	require.NoError(t, err)
   282  	defer func() {
   283  		require.NoError(t, in.Close())
   284  	}()
   285  	if base == "" {
   286  		base = "http://example.com/"
   287  	}
   288  	u, err := url.Parse(base)
   289  	require.NoError(t, err)
   290  	entries, err := parse(u, in)
   291  	require.NoError(t, err)
   292  	assert.Equal(t, want, entries)
   293  }
   294  
   295  func TestParseEmpty(t *testing.T) {
   296  	parseHTML(t, "empty.html", "", []string(nil))
   297  }
   298  
   299  func TestParseApache(t *testing.T) {
   300  	parseHTML(t, "apache.html", "http://example.com/nick/pub/", []string{
   301  		"SWIG-embed.tar.gz",
   302  		"avi2dvd.pl",
   303  		"cambert.exe",
   304  		"cambert.gz",
   305  		"fedora_demo.gz",
   306  		"gchq-challenge/",
   307  		"mandelterm/",
   308  		"pgp-key.txt",
   309  		"pymath/",
   310  		"rclone",
   311  		"readdir.exe",
   312  		"rush_hour_solver_cut_down.py",
   313  		"snake-puzzle/",
   314  		"stressdisk/",
   315  		"timer-test",
   316  		"words-to-regexp.pl",
   317  		"Now 100% better.mp3",
   318  		"Now better.mp3",
   319  	})
   320  }
   321  
   322  func TestParseMemstore(t *testing.T) {
   323  	parseHTML(t, "memstore.html", "", []string{
   324  		"test/",
   325  		"v1.35/",
   326  		"v1.36-01-g503cd84/",
   327  		"rclone-beta-latest-freebsd-386.zip",
   328  		"rclone-beta-latest-freebsd-amd64.zip",
   329  		"rclone-beta-latest-windows-amd64.zip",
   330  	})
   331  }
   332  
   333  func TestParseNginx(t *testing.T) {
   334  	parseHTML(t, "nginx.html", "", []string{
   335  		"deltas/",
   336  		"objects/",
   337  		"refs/",
   338  		"state/",
   339  		"config",
   340  		"summary",
   341  	})
   342  }
   343  
   344  func TestParseCaddy(t *testing.T) {
   345  	parseHTML(t, "caddy.html", "", []string{
   346  		"mimetype.zip",
   347  		"rclone-delete-empty-dirs.py",
   348  		"rclone-show-empty-dirs.py",
   349  		"stat-windows-386.zip",
   350  		"v1.36-155-gcf29ee8b-team-driveβ/",
   351  		"v1.36-156-gca76b3fb-team-driveβ/",
   352  		"v1.36-156-ge1f0e0f5-team-driveβ/",
   353  		"v1.36-22-g06ea13a-ssh-agentβ/",
   354  	})
   355  }