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