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  }