github.com/artpar/rclone@v1.67.3/cmd/serve/restic/restic_test.go (about) 1 // Serve restic tests set up a server and run the integration tests 2 // for restic against it. 3 4 package restic 5 6 import ( 7 "context" 8 "errors" 9 "net/http" 10 "net/http/httptest" 11 "os" 12 "os/exec" 13 "testing" 14 15 _ "github.com/artpar/rclone/backend/all" 16 "github.com/artpar/rclone/cmd" 17 "github.com/artpar/rclone/fs" 18 "github.com/artpar/rclone/fstest" 19 "github.com/stretchr/testify/assert" 20 "github.com/stretchr/testify/require" 21 ) 22 23 const ( 24 testBindAddress = "localhost:0" 25 resticSource = "../../../../../restic/restic" 26 ) 27 28 func newOpt() Options { 29 opt := DefaultOpt 30 opt.HTTP.ListenAddr = []string{testBindAddress} 31 return opt 32 } 33 34 // TestRestic runs the restic server then runs the unit tests for the 35 // restic remote against it. 36 // 37 // Requires the restic source code in the location indicated by resticSource. 38 func TestResticIntegration(t *testing.T) { 39 ctx := context.Background() 40 _, err := os.Stat(resticSource) 41 if err != nil { 42 t.Skipf("Skipping test as restic source not found: %v", err) 43 } 44 45 opt := newOpt() 46 47 fstest.Initialise() 48 49 fremote, _, clean, err := fstest.RandomRemote() 50 assert.NoError(t, err) 51 defer clean() 52 53 err = fremote.Mkdir(context.Background(), "") 54 assert.NoError(t, err) 55 56 // Start the server 57 s, err := newServer(ctx, fremote, &opt) 58 require.NoError(t, err) 59 testURL := s.Server.URLs()[0] 60 defer func() { 61 _ = s.Shutdown() 62 }() 63 64 // Change directory to run the tests 65 err = os.Chdir(resticSource) 66 require.NoError(t, err, "failed to cd to restic source code") 67 68 // Run the restic tests 69 runTests := func(path string) { 70 args := []string{"test", "./internal/backend/rest", "-run", "TestBackendRESTExternalServer", "-count=1"} 71 if testing.Verbose() { 72 args = append(args, "-v") 73 } 74 cmd := exec.Command("go", args...) 75 cmd.Env = append(os.Environ(), 76 "RESTIC_TEST_REST_REPOSITORY=rest:"+testURL+path, 77 "GO111MODULE=on", 78 ) 79 out, err := cmd.CombinedOutput() 80 if len(out) != 0 { 81 t.Logf("\n----------\n%s----------\n", string(out)) 82 } 83 assert.NoError(t, err, "Running restic integration tests") 84 } 85 86 // Run the tests with no path 87 runTests("") 88 //... and again with a path 89 runTests("potato/sausage/") 90 91 } 92 93 func TestMakeRemote(t *testing.T) { 94 for _, test := range []struct { 95 in, want string 96 }{ 97 {"/", ""}, 98 {"/data", "data"}, 99 {"/data/", "data"}, 100 {"/data/1", "data/1"}, 101 {"/data/12", "data/12/12"}, 102 {"/data/123", "data/12/123"}, 103 {"/data/123/", "data/12/123"}, 104 {"/keys", "keys"}, 105 {"/keys/1", "keys/1"}, 106 {"/keys/12", "keys/12"}, 107 {"/keys/123", "keys/123"}, 108 } { 109 r := httptest.NewRequest("GET", test.in, nil) 110 w := httptest.NewRecorder() 111 next := http.HandlerFunc(func(_ http.ResponseWriter, request *http.Request) { 112 remote, ok := request.Context().Value(ContextRemoteKey).(string) 113 assert.True(t, ok, "Failed to get remote from context") 114 assert.Equal(t, test.want, remote, test.in) 115 }) 116 got := WithRemote(next) 117 got.ServeHTTP(w, r) 118 } 119 } 120 121 type listErrorFs struct { 122 fs.Fs 123 } 124 125 func (f *listErrorFs) List(ctx context.Context, dir string) (entries fs.DirEntries, err error) { 126 return fs.DirEntries{}, errors.New("oops") 127 } 128 129 func TestListErrors(t *testing.T) { 130 ctx := context.Background() 131 // setup rclone with a local backend in a temporary directory 132 tempdir := t.TempDir() 133 opt := newOpt() 134 135 // make a new file system in the temp dir 136 f := &listErrorFs{Fs: cmd.NewFsSrc([]string{tempdir})} 137 s, err := newServer(ctx, f, &opt) 138 require.NoError(t, err) 139 router := s.Server.Router() 140 141 req := newRequest(t, "GET", "/test/snapshots/", nil) 142 checkRequest(t, router.ServeHTTP, req, []wantFunc{wantCode(http.StatusInternalServerError)}) 143 } 144 145 type newObjectErrorFs struct { 146 fs.Fs 147 err error 148 } 149 150 func (f *newObjectErrorFs) NewObject(ctx context.Context, remote string) (fs.Object, error) { 151 return nil, f.err 152 } 153 154 func TestServeErrors(t *testing.T) { 155 ctx := context.Background() 156 // setup rclone with a local backend in a temporary directory 157 tempdir := t.TempDir() 158 opt := newOpt() 159 160 // make a new file system in the temp dir 161 f := &newObjectErrorFs{Fs: cmd.NewFsSrc([]string{tempdir})} 162 s, err := newServer(ctx, f, &opt) 163 require.NoError(t, err) 164 router := s.Server.Router() 165 166 f.err = errors.New("oops") 167 req := newRequest(t, "GET", "/test/config", nil) 168 checkRequest(t, router.ServeHTTP, req, []wantFunc{wantCode(http.StatusInternalServerError)}) 169 170 f.err = fs.ErrorObjectNotFound 171 checkRequest(t, router.ServeHTTP, req, []wantFunc{wantCode(http.StatusNotFound)}) 172 }