github.com/ncw/rclone@v1.48.1-0.20190724201158-a35aa1360e3e/cmd/serve/restic/restic_appendonly_test.go (about)

     1  // +build go1.9
     2  
     3  package restic
     4  
     5  import (
     6  	"crypto/rand"
     7  	"encoding/hex"
     8  	"io"
     9  	"io/ioutil"
    10  	"net/http"
    11  	"os"
    12  	"strings"
    13  	"testing"
    14  
    15  	"github.com/ncw/rclone/cmd"
    16  	"github.com/ncw/rclone/cmd/serve/httplib/httpflags"
    17  	"github.com/stretchr/testify/require"
    18  )
    19  
    20  // createOverwriteDeleteSeq returns a sequence which will create a new file at
    21  // path, and then try to overwrite and delete it.
    22  func createOverwriteDeleteSeq(t testing.TB, path string) []TestRequest {
    23  	// add a file, try to overwrite and delete it
    24  	req := []TestRequest{
    25  		{
    26  			req:  newRequest(t, "GET", path, nil),
    27  			want: []wantFunc{wantCode(http.StatusNotFound)},
    28  		},
    29  		{
    30  			req:  newRequest(t, "POST", path, strings.NewReader("foobar test config")),
    31  			want: []wantFunc{wantCode(http.StatusOK)},
    32  		},
    33  		{
    34  			req: newRequest(t, "GET", path, nil),
    35  			want: []wantFunc{
    36  				wantCode(http.StatusOK),
    37  				wantBody("foobar test config"),
    38  			},
    39  		},
    40  		{
    41  			req:  newRequest(t, "POST", path, strings.NewReader("other config")),
    42  			want: []wantFunc{wantCode(http.StatusForbidden)},
    43  		},
    44  		{
    45  			req: newRequest(t, "GET", path, nil),
    46  			want: []wantFunc{
    47  				wantCode(http.StatusOK),
    48  				wantBody("foobar test config"),
    49  			},
    50  		},
    51  		{
    52  			req:  newRequest(t, "DELETE", path, nil),
    53  			want: []wantFunc{wantCode(http.StatusForbidden)},
    54  		},
    55  		{
    56  			req: newRequest(t, "GET", path, nil),
    57  			want: []wantFunc{
    58  				wantCode(http.StatusOK),
    59  				wantBody("foobar test config"),
    60  			},
    61  		},
    62  	}
    63  	return req
    64  }
    65  
    66  // TestResticHandler runs tests on the restic handler code, especially in append-only mode.
    67  func TestResticHandler(t *testing.T) {
    68  	buf := make([]byte, 32)
    69  	_, err := io.ReadFull(rand.Reader, buf)
    70  	require.NoError(t, err)
    71  	randomID := hex.EncodeToString(buf)
    72  
    73  	var tests = []struct {
    74  		seq []TestRequest
    75  	}{
    76  		{createOverwriteDeleteSeq(t, "/config")},
    77  		{createOverwriteDeleteSeq(t, "/data/"+randomID)},
    78  		{
    79  			// ensure we can add and remove lock files
    80  			[]TestRequest{
    81  				{
    82  					req:  newRequest(t, "GET", "/locks/"+randomID, nil),
    83  					want: []wantFunc{wantCode(http.StatusNotFound)},
    84  				},
    85  				{
    86  					req:  newRequest(t, "POST", "/locks/"+randomID, strings.NewReader("lock file")),
    87  					want: []wantFunc{wantCode(http.StatusOK)},
    88  				},
    89  				{
    90  					req: newRequest(t, "GET", "/locks/"+randomID, nil),
    91  					want: []wantFunc{
    92  						wantCode(http.StatusOK),
    93  						wantBody("lock file"),
    94  					},
    95  				},
    96  				{
    97  					req:  newRequest(t, "POST", "/locks/"+randomID, strings.NewReader("other lock file")),
    98  					want: []wantFunc{wantCode(http.StatusForbidden)},
    99  				},
   100  				{
   101  					req:  newRequest(t, "DELETE", "/locks/"+randomID, nil),
   102  					want: []wantFunc{wantCode(http.StatusOK)},
   103  				},
   104  				{
   105  					req:  newRequest(t, "GET", "/locks/"+randomID, nil),
   106  					want: []wantFunc{wantCode(http.StatusNotFound)},
   107  				},
   108  			},
   109  		},
   110  	}
   111  
   112  	// setup rclone with a local backend in a temporary directory
   113  	tempdir, err := ioutil.TempDir("", "rclone-restic-test-")
   114  	require.NoError(t, err)
   115  
   116  	// make sure the tempdir is properly removed
   117  	defer func() {
   118  		err := os.RemoveAll(tempdir)
   119  		require.NoError(t, err)
   120  	}()
   121  
   122  	// globally set append-only mode
   123  	prev := appendOnly
   124  	appendOnly = true
   125  	defer func() {
   126  		appendOnly = prev // reset when done
   127  	}()
   128  
   129  	// make a new file system in the temp dir
   130  	f := cmd.NewFsSrc([]string{tempdir})
   131  	srv := newServer(f, &httpflags.Opt)
   132  
   133  	// create the repo
   134  	checkRequest(t, srv.handler,
   135  		newRequest(t, "POST", "/?create=true", nil),
   136  		[]wantFunc{wantCode(http.StatusOK)})
   137  
   138  	for _, test := range tests {
   139  		t.Run("", func(t *testing.T) {
   140  			for i, seq := range test.seq {
   141  				t.Logf("request %v: %v %v", i, seq.req.Method, seq.req.URL.Path)
   142  				checkRequest(t, srv.handler, seq.req, seq.want)
   143  			}
   144  		})
   145  	}
   146  }