2017-08-01 13:38:29 +02:00
// +build go1.8
2017-06-19 18:36:14 +02:00
package http
import (
"fmt"
"io/ioutil"
"net/http"
"net/http/httptest"
"net/url"
"os"
"path/filepath"
"sort"
"testing"
"time"
"github.com/ncw/rclone/fs"
2018-01-12 17:30:54 +01:00
"github.com/ncw/rclone/fs/config"
2018-05-14 19:06:57 +02:00
"github.com/ncw/rclone/fs/config/configmap"
2017-06-19 18:36:14 +02:00
"github.com/ncw/rclone/fstest"
2018-01-11 17:29:20 +01:00
"github.com/ncw/rclone/lib/rest"
2017-06-19 18:36:14 +02:00
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
var (
remoteName = "TestHTTP"
testPath = "test"
filesPath = filepath . Join ( testPath , "files" )
)
// prepareServer the test server and return a function to tidy it up afterwards
2018-05-14 19:06:57 +02:00
func prepareServer ( t * testing . T ) ( configmap . Simple , func ( ) ) {
2017-06-19 18:36:14 +02:00
// file server for test/files
fileServer := http . FileServer ( http . Dir ( filesPath ) )
// Make the test server
ts := httptest . NewServer ( fileServer )
// Configure the remote
2018-01-12 17:30:54 +01:00
config . LoadConfig ( )
2017-06-19 18:36:14 +02:00
// fs.Config.LogLevel = fs.LogLevelDebug
// fs.Config.DumpHeaders = true
// fs.Config.DumpBodies = true
2018-05-14 19:06:57 +02:00
// config.FileSet(remoteName, "type", "http")
// config.FileSet(remoteName, "url", ts.URL)
m := configmap . Simple {
"type" : "http" ,
"url" : ts . URL ,
}
2017-06-19 18:36:14 +02:00
// return a function to tidy up
2018-05-14 19:06:57 +02:00
return m , ts . Close
2017-06-19 18:36:14 +02:00
}
// prepare the test server and return a function to tidy it up afterwards
func prepare ( t * testing . T ) ( fs . Fs , func ( ) ) {
2018-05-14 19:06:57 +02:00
m , tidy := prepareServer ( t )
2017-06-19 18:36:14 +02:00
// Instantiate it
2018-05-14 19:06:57 +02:00
f , err := NewFs ( remoteName , "" , m )
2017-06-19 18:36:14 +02:00
require . NoError ( t , err )
return f , tidy
}
func testListRoot ( t * testing . T , f fs . Fs ) {
entries , err := f . List ( "" )
require . NoError ( t , err )
sort . Sort ( entries )
require . Equal ( t , 4 , len ( entries ) )
e := entries [ 0 ]
assert . Equal ( t , "four" , e . Remote ( ) )
2017-06-30 14:37:29 +02:00
assert . Equal ( t , int64 ( - 1 ) , e . Size ( ) )
_ , ok := e . ( fs . Directory )
2017-06-19 18:36:14 +02:00
assert . True ( t , ok )
e = entries [ 1 ]
2017-08-02 14:19:36 +02:00
assert . Equal ( t , "one%.txt" , e . Remote ( ) )
2017-06-19 18:36:14 +02:00
assert . Equal ( t , int64 ( 6 ) , e . Size ( ) )
_ , ok = e . ( * Object )
assert . True ( t , ok )
e = entries [ 2 ]
assert . Equal ( t , "three" , e . Remote ( ) )
2017-06-30 14:37:29 +02:00
assert . Equal ( t , int64 ( - 1 ) , e . Size ( ) )
_ , ok = e . ( fs . Directory )
2017-06-19 18:36:14 +02:00
assert . True ( t , ok )
e = entries [ 3 ]
assert . Equal ( t , "two.html" , e . Remote ( ) )
assert . Equal ( t , int64 ( 7 ) , e . Size ( ) )
_ , ok = e . ( * Object )
assert . True ( t , ok )
}
func TestListRoot ( t * testing . T ) {
f , tidy := prepare ( t )
defer tidy ( )
testListRoot ( t , f )
}
func TestListSubDir ( t * testing . T ) {
f , tidy := prepare ( t )
defer tidy ( )
entries , err := f . List ( "three" )
require . NoError ( t , err )
sort . Sort ( entries )
assert . Equal ( t , 1 , len ( entries ) )
e := entries [ 0 ]
assert . Equal ( t , "three/underthree.txt" , e . Remote ( ) )
assert . Equal ( t , int64 ( 9 ) , e . Size ( ) )
_ , ok := e . ( * Object )
assert . True ( t , ok )
}
func TestNewObject ( t * testing . T ) {
f , tidy := prepare ( t )
defer tidy ( )
2017-08-01 00:15:31 +02:00
o , err := f . NewObject ( "four/under four.txt" )
2017-06-19 18:36:14 +02:00
require . NoError ( t , err )
2017-08-01 00:15:31 +02:00
assert . Equal ( t , "four/under four.txt" , o . Remote ( ) )
2017-06-19 18:36:14 +02:00
assert . Equal ( t , int64 ( 9 ) , o . Size ( ) )
_ , ok := o . ( * Object )
assert . True ( t , ok )
// Test the time is correct on the object
tObj := o . ModTime ( )
2017-08-01 00:15:31 +02:00
fi , err := os . Stat ( filepath . Join ( filesPath , "four" , "under four.txt" ) )
2017-06-19 18:36:14 +02:00
require . NoError ( t , err )
tFile := fi . ModTime ( )
dt , ok := fstest . CheckTimeEqualWithPrecision ( tObj , tFile , time . Second )
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 ) )
2018-12-04 18:40:44 +01:00
// check object not found
o , err = f . NewObject ( "not found.txt" )
assert . Nil ( t , o )
assert . Equal ( t , fs . ErrorObjectNotFound , err )
2017-06-19 18:36:14 +02:00
}
func TestOpen ( t * testing . T ) {
f , tidy := prepare ( t )
defer tidy ( )
2017-08-01 00:15:31 +02:00
o , err := f . NewObject ( "four/under four.txt" )
2017-06-19 18:36:14 +02:00
require . NoError ( t , err )
// Test normal read
fd , err := o . Open ( )
require . NoError ( t , err )
data , err := ioutil . ReadAll ( fd )
2018-05-04 16:19:50 +02:00
require . NoError ( t , err )
2017-06-19 18:36:14 +02:00
require . NoError ( t , fd . Close ( ) )
assert . Equal ( t , "beetroot\n" , string ( data ) )
// Test with range request
fd , err = o . Open ( & fs . RangeOption { Start : 1 , End : 5 } )
require . NoError ( t , err )
data , err = ioutil . ReadAll ( fd )
2018-05-04 16:19:50 +02:00
require . NoError ( t , err )
2017-06-19 18:36:14 +02:00
require . NoError ( t , fd . Close ( ) )
assert . Equal ( t , "eetro" , string ( data ) )
}
func TestMimeType ( t * testing . T ) {
f , tidy := prepare ( t )
defer tidy ( )
2017-08-01 00:15:31 +02:00
o , err := f . NewObject ( "four/under four.txt" )
2017-06-19 18:36:14 +02:00
require . NoError ( t , err )
do , ok := o . ( fs . MimeTyper )
require . True ( t , ok )
assert . Equal ( t , "text/plain; charset=utf-8" , do . MimeType ( ) )
}
func TestIsAFileRoot ( t * testing . T ) {
2018-05-14 19:06:57 +02:00
m , tidy := prepareServer ( t )
2017-06-19 18:36:14 +02:00
defer tidy ( )
2018-05-14 19:06:57 +02:00
f , err := NewFs ( remoteName , "one%.txt" , m )
2017-06-19 18:36:14 +02:00
assert . Equal ( t , err , fs . ErrorIsFile )
testListRoot ( t , f )
}
func TestIsAFileSubDir ( t * testing . T ) {
2018-05-14 19:06:57 +02:00
m , tidy := prepareServer ( t )
2017-06-19 18:36:14 +02:00
defer tidy ( )
2018-05-14 19:06:57 +02:00
f , err := NewFs ( remoteName , "three/underthree.txt" , m )
2017-06-19 18:36:14 +02:00
assert . Equal ( t , err , fs . ErrorIsFile )
entries , err := f . List ( "" )
require . NoError ( t , err )
sort . Sort ( entries )
assert . Equal ( t , 1 , len ( entries ) )
e := entries [ 0 ]
assert . Equal ( t , "underthree.txt" , e . Remote ( ) )
assert . Equal ( t , int64 ( 9 ) , e . Size ( ) )
_ , ok := e . ( * Object )
assert . True ( t , ok )
}
func TestParseName ( t * testing . T ) {
for i , test := range [ ] struct {
2018-02-14 12:26:37 +01:00
base string
val string
wantErr error
want string
2017-06-19 18:36:14 +02:00
} {
2018-02-14 12:26:37 +01:00
{ "http://example.com/" , "potato" , nil , "potato" } ,
{ "http://example.com/dir/" , "potato" , nil , "potato" } ,
{ "http://example.com/dir/" , "potato?download=true" , errFoundQuestionMark , "" } ,
{ "http://example.com/dir/" , "../dir/potato" , nil , "potato" } ,
{ "http://example.com/dir/" , ".." , errNotUnderRoot , "" } ,
{ "http://example.com/dir/" , "http://example.com/" , errNotUnderRoot , "" } ,
{ "http://example.com/dir/" , "http://example.com/dir/" , errNameIsEmpty , "" } ,
{ "http://example.com/dir/" , "http://example.com/dir/potato" , nil , "potato" } ,
{ "http://example.com/dir/" , "https://example.com/dir/potato" , errSchemeMismatch , "" } ,
{ "http://example.com/dir/" , "http://notexample.com/dir/potato" , errHostMismatch , "" } ,
{ "http://example.com/dir/" , "/dir/" , errNameIsEmpty , "" } ,
{ "http://example.com/dir/" , "/dir/potato" , nil , "potato" } ,
{ "http://example.com/dir/" , "subdir/potato" , errNameContainsSlash , "" } ,
{ "http://example.com/dir/" , "With percent %25.txt" , nil , "With percent %.txt" } ,
{ "http://example.com/dir/" , "With colon :" , errURLJoinFailed , "" } ,
{ "http://example.com/dir/" , rest . URLPathEscape ( "With colon :" ) , nil , "With colon :" } ,
{ "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)/" } ,
2017-06-19 18:36:14 +02:00
} {
u , err := url . Parse ( test . base )
require . NoError ( t , err )
2018-02-14 12:26:37 +01:00
got , gotErr := parseName ( u , test . val )
2017-06-19 18:36:14 +02:00
what := fmt . Sprintf ( "test %d base=%q, val=%q" , i , test . base , test . val )
2018-02-14 12:26:37 +01:00
assert . Equal ( t , test . wantErr , gotErr , what )
2017-06-19 18:36:14 +02:00
assert . Equal ( t , test . want , got , what )
}
}
// Load HTML from the file given and parse it, checking it against the entries passed in
func parseHTML ( t * testing . T , name string , base string , want [ ] string ) {
in , err := os . Open ( filepath . Join ( testPath , "index_files" , name ) )
require . NoError ( t , err )
defer func ( ) {
require . NoError ( t , in . Close ( ) )
} ( )
if base == "" {
base = "http://example.com/"
}
u , err := url . Parse ( base )
require . NoError ( t , err )
entries , err := parse ( u , in )
require . NoError ( t , err )
assert . Equal ( t , want , entries )
}
func TestParseEmpty ( t * testing . T ) {
parseHTML ( t , "empty.html" , "" , [ ] string ( nil ) )
}
func TestParseApache ( t * testing . T ) {
parseHTML ( t , "apache.html" , "http://example.com/nick/pub/" , [ ] string {
"SWIG-embed.tar.gz" ,
"avi2dvd.pl" ,
"cambert.exe" ,
"cambert.gz" ,
"fedora_demo.gz" ,
"gchq-challenge/" ,
"mandelterm/" ,
"pgp-key.txt" ,
"pymath/" ,
"rclone" ,
"readdir.exe" ,
"rush_hour_solver_cut_down.py" ,
"snake-puzzle/" ,
"stressdisk/" ,
"timer-test" ,
"words-to-regexp.pl" ,
2017-08-02 14:19:36 +02:00
"Now 100% better.mp3" ,
"Now better.mp3" ,
2017-06-19 18:36:14 +02:00
} )
}
func TestParseMemstore ( t * testing . T ) {
parseHTML ( t , "memstore.html" , "" , [ ] string {
"test/" ,
"v1.35/" ,
"v1.36-01-g503cd84/" ,
"rclone-beta-latest-freebsd-386.zip" ,
"rclone-beta-latest-freebsd-amd64.zip" ,
"rclone-beta-latest-windows-amd64.zip" ,
} )
}
func TestParseNginx ( t * testing . T ) {
parseHTML ( t , "nginx.html" , "" , [ ] string {
"deltas/" ,
"objects/" ,
"refs/" ,
"state/" ,
"config" ,
"summary" ,
} )
}
func TestParseCaddy ( t * testing . T ) {
parseHTML ( t , "caddy.html" , "" , [ ] string {
"mimetype.zip" ,
"rclone-delete-empty-dirs.py" ,
"rclone-show-empty-dirs.py" ,
"stat-windows-386.zip" ,
"v1.36-155-gcf29ee8b-team-driveβ/" ,
"v1.36-156-gca76b3fb-team-driveβ/" ,
"v1.36-156-ge1f0e0f5-team-driveβ/" ,
"v1.36-22-g06ea13a-ssh-agentβ/" ,
} )
}