gitlab.com/SkynetLabs/skyd@v1.6.9/skymodules/skyfilereader_test.go (about)

     1  package skymodules
     2  
     3  import (
     4  	"bytes"
     5  	"context"
     6  	"fmt"
     7  	"io"
     8  	"io/ioutil"
     9  	"mime/multipart"
    10  	"reflect"
    11  	"testing"
    12  	"time"
    13  
    14  	"gitlab.com/NebulousLabs/errors"
    15  	"gitlab.com/NebulousLabs/fastrand"
    16  )
    17  
    18  // TestSkyfileReader verifies the functionality of the SkyfileReader.
    19  func TestSkyfileReader(t *testing.T) {
    20  	t.Run("Basic", testSkyfileReaderBasic)
    21  	t.Run("ReadBuffer", testSkyfileReaderReadBuffer)
    22  	t.Run("MetadataTimeout", testSkyfileReaderMetadataTimeout)
    23  }
    24  
    25  // testSkyfileReaderBasic verifies the basic use case of the SkyfileReader
    26  func testSkyfileReaderBasic(t *testing.T) {
    27  	t.Parallel()
    28  
    29  	// create upload parameters
    30  	sup := SkyfileUploadParameters{
    31  		Filename: t.Name(),
    32  		Mode:     DefaultFilePerm,
    33  	}
    34  
    35  	// create a reader
    36  	dataLen := fastrand.Intn(1000) + 11
    37  	data := fastrand.Bytes(dataLen)
    38  	reader := bytes.NewReader(data)
    39  	sfReader := NewSkyfileReader(reader, sup)
    40  
    41  	// read 1 byte
    42  	peek := make([]byte, 1)
    43  	n, err := sfReader.Read(peek)
    44  	if err != nil {
    45  		t.Fatal(err)
    46  	}
    47  	if n != 1 || !bytes.Equal(peek, data[:1]) {
    48  		t.Fatal("unexpected read")
    49  	}
    50  
    51  	// read another 9 bytes
    52  	next := make([]byte, 9)
    53  	n, err = sfReader.Read(next)
    54  	if err != nil {
    55  		t.Fatal(err)
    56  	}
    57  	if n != 9 || !bytes.Equal(next, data[1:10]) {
    58  		t.Fatal("unexpected read")
    59  	}
    60  
    61  	// read the remaining bytes
    62  	remainingLen := dataLen - 10
    63  	next = make([]byte, remainingLen)
    64  	n, err = sfReader.Read(next)
    65  	if err != nil {
    66  		t.Fatal(err)
    67  	}
    68  	if n != remainingLen || !bytes.Equal(next, data[10:]) {
    69  		t.Fatal("unexpected read")
    70  	}
    71  
    72  	// read again, expect EOF
    73  	n, err = sfReader.Read(next)
    74  	if err != io.EOF {
    75  		t.Fatal(err, n)
    76  	}
    77  
    78  	// fetch the metadata from the reader
    79  	metadata, err := sfReader.SkyfileMetadata(context.Background())
    80  	if err != nil {
    81  		t.Fatal(err)
    82  	}
    83  
    84  	// check against what we expect it to be
    85  	if !reflect.DeepEqual(metadata, SkyfileMetadata{
    86  		Filename: sup.Filename,
    87  		Mode:     sup.Mode,
    88  		Length:   uint64(dataLen),
    89  	}) {
    90  		t.Fatal("unexpected metadata", metadata)
    91  	}
    92  }
    93  
    94  // testSkyfileReaderReadBuffer verifies the functionality of the read buffer.
    95  func testSkyfileReaderReadBuffer(t *testing.T) {
    96  	t.Parallel()
    97  
    98  	// create upload parameters
    99  	sup := SkyfileUploadParameters{
   100  		Filename: t.Name(),
   101  		Mode:     DefaultFilePerm,
   102  	}
   103  
   104  	// create a reader
   105  	size := 100
   106  	data := fastrand.Bytes(size)
   107  	reader := bytes.NewReader(data)
   108  	sfReader := NewSkyfileReader(reader, sup)
   109  
   110  	// read some data
   111  	buf := make([]byte, 40)
   112  	n, err := io.ReadFull(sfReader, buf)
   113  	if err != nil {
   114  		t.Fatal(err)
   115  	}
   116  	if n != 40 {
   117  		t.Fatal("unexpected read")
   118  	}
   119  
   120  	// set that data is read buffer
   121  	sfReader.SetReadBuffer(buf)
   122  
   123  	// read the rest of the data
   124  	rest := make([]byte, 100)
   125  	_, err = sfReader.Read(rest)
   126  	if err != nil {
   127  		t.Fatal(err)
   128  	}
   129  
   130  	// read again, expect EOF
   131  	_, err = sfReader.Read(make([]byte, 1))
   132  	if err != io.EOF {
   133  		t.Fatal(err, n)
   134  	}
   135  
   136  	// verify we have just read all data, including the buffer
   137  	if !bytes.Equal(rest, data) {
   138  		t.Fatal("unexpected read")
   139  	}
   140  
   141  	// fetch the metadata from the reader
   142  	metadata, err := sfReader.SkyfileMetadata(context.Background())
   143  	if err != nil {
   144  		t.Fatal(err)
   145  	}
   146  
   147  	// check against what we expect it to be
   148  	if !reflect.DeepEqual(metadata, SkyfileMetadata{
   149  		Filename: sup.Filename,
   150  		Mode:     sup.Mode,
   151  		Length:   uint64(size),
   152  	}) {
   153  		t.Fatal("unexpected metadata", metadata)
   154  	}
   155  }
   156  
   157  // testSkyfileReaderMetadataTimeout verifies metadata returns on timeout,
   158  // potentially before the reader is fully read
   159  func testSkyfileReaderMetadataTimeout(t *testing.T) {
   160  	// Since this is a timeout test only run with long test
   161  	if testing.Short() {
   162  		t.SkipNow()
   163  	}
   164  	t.Parallel()
   165  
   166  	// create upload parameters
   167  	sup := SkyfileUploadParameters{
   168  		Filename: t.Name(),
   169  		Mode:     DefaultFilePerm,
   170  	}
   171  
   172  	// create a reader
   173  	dataLen := fastrand.Intn(1000) + 10
   174  	data := fastrand.Bytes(dataLen)
   175  	reader := bytes.NewReader(data)
   176  	sfReader := NewSkyfileReader(reader, sup)
   177  
   178  	// read less than dataLen
   179  	read := make([]byte, dataLen/2)
   180  	_, err := sfReader.Read(read)
   181  	if err != nil {
   182  		t.Fatal(err)
   183  	}
   184  
   185  	// cancel the context in a goroutine
   186  	ctx, cancel := context.WithCancel(context.Background())
   187  	go func() {
   188  		time.Sleep(time.Second)
   189  		cancel()
   190  	}()
   191  
   192  	// fetch the metadata from the reader
   193  	metadata, err := sfReader.SkyfileMetadata(ctx)
   194  	if !errors.Contains(err, ErrSkyfileMetadataUnavailable) {
   195  		t.Fatal(err)
   196  	}
   197  	if !reflect.DeepEqual(metadata, SkyfileMetadata{}) {
   198  		t.Fatal("unexpected metadata", metadata)
   199  	}
   200  }
   201  
   202  // TestSkyfileMultipartReader verifies the functionality of the
   203  // SkyfileMultipartReader.
   204  func TestSkyfileMultipartReader(t *testing.T) {
   205  	t.Run("Basic", testSkyfileMultipartReaderBasic)
   206  	t.Run("IllegalFormName", testSkyfileMultipartReaderIllegalFormName)
   207  	t.Run("EmptyFilename", testSkyfileMultipartReaderEmptyFilename)
   208  	t.Run("RandomReadSize", testSkyfileMultipartReaderRandomReadSize)
   209  	t.Run("ReadBuffer", testSkyfileMultipartReaderReadBuffer)
   210  	t.Run("MetadataTimeout", testSkyfileMultipartReaderMetadataTimeout)
   211  }
   212  
   213  // testSkyfileMultipartReaderBasic verifies the basic use case of a skyfile
   214  // multipart reader, reading out the exact parts.
   215  func testSkyfileMultipartReaderBasic(t *testing.T) {
   216  	t.Parallel()
   217  
   218  	// create upload parameters
   219  	sup := SkyfileUploadParameters{
   220  		Filename: t.Name(),
   221  		Mode:     DefaultFilePerm,
   222  	}
   223  
   224  	// create a multipart writer
   225  	buffer := new(bytes.Buffer)
   226  	writer := multipart.NewWriter(buffer)
   227  
   228  	// prepare random file data
   229  	data1 := fastrand.Bytes(10)
   230  	data2 := fastrand.Bytes(20)
   231  
   232  	// write the multipart files
   233  	off := uint64(0)
   234  	md1, err1 := AddMultipartFile(writer, data1, "files[]", "part1", 0600, &off)
   235  	md2, err2 := AddMultipartFile(writer, data2, "files[]", "part2", 0600, &off)
   236  	if errors.Compose(err1, err2) != nil {
   237  		t.Fatal("unexpected")
   238  	}
   239  
   240  	// close the writer
   241  	err := writer.Close()
   242  	if err != nil {
   243  		t.Fatal(err)
   244  	}
   245  
   246  	// turn it into a skyfile reader
   247  	reader := bytes.NewReader(buffer.Bytes())
   248  	multipartReader := multipart.NewReader(reader, writer.Boundary())
   249  	sfReader := NewSkyfileMultipartReader(multipartReader, sup)
   250  
   251  	// verify we can read part 1
   252  	part1Data := make([]byte, 10)
   253  	n, err := sfReader.Read(part1Data)
   254  	if err != nil {
   255  		t.Fatal(err)
   256  	}
   257  	if n != 10 || !bytes.Equal(part1Data, data1) {
   258  		t.Fatal("unexpected read", n)
   259  	}
   260  
   261  	// verify we can read part 2
   262  	part2Data := make([]byte, 20)
   263  	n, err = sfReader.Read(part2Data)
   264  	if err != nil {
   265  		t.Fatal(err)
   266  	}
   267  	if n != 20 || !bytes.Equal(part2Data, data2) {
   268  		t.Fatal("unexpected read", n)
   269  	}
   270  
   271  	// we have to simulate a consecutive read to mimic not knowing how many
   272  	// parts the request contains, only then will the metadata be released
   273  	sfReader.Read(make([]byte, 1))
   274  
   275  	// fetch the metadata from the reader
   276  	metadata, err := sfReader.SkyfileMetadata(context.Background())
   277  	if err != nil {
   278  		t.Fatal(err)
   279  	}
   280  
   281  	part1Meta, ok := metadata.Subfiles["part1"]
   282  	if !ok || !reflect.DeepEqual(part1Meta, md1) {
   283  		t.Fatal("unexpected metadata")
   284  	}
   285  
   286  	part2Meta, ok := metadata.Subfiles["part2"]
   287  	if !ok || !reflect.DeepEqual(part2Meta, md2) {
   288  		t.Fatal("unexpected metadata")
   289  	}
   290  }
   291  
   292  // testSkyfileMultipartReaderIllegalFormName verifies the reader returns an
   293  // error if the given form name is not one of the allowed values.
   294  func testSkyfileMultipartReaderIllegalFormName(t *testing.T) {
   295  	t.Parallel()
   296  
   297  	// create upload parameters
   298  	sup := SkyfileUploadParameters{
   299  		Filename: t.Name(),
   300  		Mode:     DefaultFilePerm,
   301  	}
   302  
   303  	// create a multipart writer
   304  	buffer := new(bytes.Buffer)
   305  	writer := multipart.NewWriter(buffer)
   306  
   307  	// prepare random file data
   308  	data := fastrand.Bytes(10)
   309  
   310  	// write the multipart files
   311  	off := uint64(0)
   312  	_, err := AddMultipartFile(writer, data, "part", "part", 0600, &off)
   313  	if err != nil {
   314  		t.Fatal("unexpected")
   315  	}
   316  
   317  	// close the writer
   318  	err = writer.Close()
   319  	if err != nil {
   320  		t.Fatal(err)
   321  	}
   322  
   323  	// turn it into a skyfile reader
   324  	reader := bytes.NewReader(buffer.Bytes())
   325  	multipartReader := multipart.NewReader(reader, writer.Boundary())
   326  	sfReader := NewSkyfileMultipartReader(multipartReader, sup)
   327  
   328  	// verify we
   329  	_, err = ioutil.ReadAll(sfReader)
   330  	if !errors.Contains(err, ErrIllegalFormName) {
   331  		t.Fatalf("expected ErrIllegalFormName error, instead err was '%v'", err)
   332  	}
   333  }
   334  
   335  // testSkyfileMultipartReaderRandomReadSize creates a random multipart request
   336  // and reads the entire request using random read sizes.
   337  func testSkyfileMultipartReaderRandomReadSize(t *testing.T) {
   338  	t.Parallel()
   339  
   340  	// create upload parameters
   341  	sup := SkyfileUploadParameters{
   342  		Filename: t.Name(),
   343  		Mode:     DefaultFilePerm,
   344  	}
   345  
   346  	// create a multipart writer
   347  	buffer := new(bytes.Buffer)
   348  	writer := multipart.NewWriter(buffer)
   349  
   350  	// prepare some random data
   351  	randomParts := 3 //fastrand.Intn(10) + 3
   352  	randomPartsData := make([][]byte, randomParts)
   353  	for i := 0; i < randomParts; i++ {
   354  		randomPartLen := fastrand.Intn(10) + 1
   355  		randomPartsData[i] = fastrand.Bytes(randomPartLen)
   356  	}
   357  
   358  	// write the multipart files
   359  	off := uint64(0)
   360  	for i, data := range randomPartsData {
   361  		filename := fmt.Sprintf("file%d", i)
   362  		_, err := AddMultipartFile(writer, data, "files[]", filename, 644, &off)
   363  		if err != nil {
   364  			t.Fatal(err)
   365  		}
   366  	}
   367  
   368  	// close the writer
   369  	err := writer.Close()
   370  	if err != nil {
   371  		t.Fatal(err)
   372  	}
   373  
   374  	// turn it into a skyfile reader
   375  	reader := bytes.NewReader(buffer.Bytes())
   376  	multipartReader := multipart.NewReader(reader, writer.Boundary())
   377  	sfReader := NewSkyfileMultipartReader(multipartReader, sup)
   378  
   379  	// concat all data
   380  	expected := make([]byte, 0)
   381  	for _, data := range randomPartsData {
   382  		expected = append(expected, data...)
   383  	}
   384  
   385  	// read, randomly, until all data is read
   386  	actual := make([]byte, 0)
   387  	maxLen := len(expected) / 3
   388  	for {
   389  		randomLength := fastrand.Intn(maxLen) + 1
   390  
   391  		data := make([]byte, randomLength)
   392  		n, err := sfReader.Read(data)
   393  
   394  		actual = append(actual, data[:n]...)
   395  		if errors.Contains(err, io.EOF) {
   396  			break
   397  		}
   398  	}
   399  
   400  	// verify we've read all of the data
   401  	if !bytes.Equal(actual, expected) {
   402  		t.Fatal("unexpected data")
   403  	}
   404  
   405  	// fetch the metadata from the reader
   406  	metadata, err := sfReader.SkyfileMetadata(context.Background())
   407  	if err != nil {
   408  		t.Fatal(err)
   409  	}
   410  
   411  	// verify the metadata is properly read
   412  	if len(metadata.Subfiles) != randomParts {
   413  		t.Fatal("unexpected amount of metadata")
   414  	}
   415  
   416  	currOffset := 0
   417  	for i, data := range randomPartsData {
   418  		filename := fmt.Sprintf("file%d", i)
   419  		metadata, ok := metadata.Subfiles[filename]
   420  		if !ok {
   421  			t.Fatal("metadata not found")
   422  		}
   423  		if metadata.Len != uint64(len(data)) {
   424  			t.Fatal("unexpected len")
   425  		}
   426  		if metadata.Offset != uint64(currOffset) {
   427  			t.Fatal("unexpected offset")
   428  		}
   429  		currOffset += len(data)
   430  	}
   431  }
   432  
   433  // testSkyfileMultipartReaderEmptyFilename verifies the reader returns an error
   434  // if the filename is empty.
   435  func testSkyfileMultipartReaderEmptyFilename(t *testing.T) {
   436  	t.Parallel()
   437  
   438  	// create upload parameters
   439  	sup := SkyfileUploadParameters{
   440  		Filename: t.Name(),
   441  		Mode:     DefaultFilePerm,
   442  	}
   443  
   444  	// create a multipart writer
   445  	buffer := new(bytes.Buffer)
   446  	writer := multipart.NewWriter(buffer)
   447  
   448  	// prepare random file data
   449  	data := fastrand.Bytes(10)
   450  
   451  	// write the multipart files
   452  	off := uint64(0)
   453  	_, err := AddMultipartFile(writer, data, "file", "", 0600, &off)
   454  	if err != nil {
   455  		t.Fatal("unexpected")
   456  	}
   457  
   458  	// close the writer
   459  	err = writer.Close()
   460  	if err != nil {
   461  		t.Fatal(err)
   462  	}
   463  
   464  	// turn it into a skyfile reader
   465  	reader := bytes.NewReader(buffer.Bytes())
   466  	multipartReader := multipart.NewReader(reader, writer.Boundary())
   467  	sfReader := NewSkyfileMultipartReader(multipartReader, sup)
   468  
   469  	// verify we get ErrEmptyFilename if we do not provide a filename
   470  	_, err = ioutil.ReadAll(sfReader)
   471  	if !errors.Contains(err, ErrEmptyFilename) {
   472  		t.Fatalf("expected ErrEmptyFilename error, instead err was '%v'", err)
   473  	}
   474  }
   475  
   476  // testSkyfileMultipartReaderReadBuffer verifies the functionality of the read
   477  // buffer.
   478  func testSkyfileMultipartReaderReadBuffer(t *testing.T) {
   479  	t.Parallel()
   480  
   481  	// create upload parameters
   482  	sup := SkyfileUploadParameters{
   483  		Filename: t.Name(),
   484  		Mode:     DefaultFilePerm,
   485  	}
   486  
   487  	// create a multipart writer
   488  	buffer := new(bytes.Buffer)
   489  	writer := multipart.NewWriter(buffer)
   490  
   491  	// prepare random file data
   492  	data1 := fastrand.Bytes(10)
   493  	data2 := fastrand.Bytes(20)
   494  
   495  	// write the multipart files
   496  	off := uint64(0)
   497  	_, err1 := AddMultipartFile(writer, data1, "files[]", "part1", 0600, &off)
   498  	_, err2 := AddMultipartFile(writer, data2, "files[]", "part2", 0600, &off)
   499  	if errors.Compose(err1, err2) != nil {
   500  		t.Fatal("unexpected")
   501  	}
   502  
   503  	// close the writer
   504  	err := writer.Close()
   505  	if err != nil {
   506  		t.Fatal(err)
   507  	}
   508  
   509  	// turn it into a skyfile reader
   510  	reader := bytes.NewReader(buffer.Bytes())
   511  	multipartReader := multipart.NewReader(reader, writer.Boundary())
   512  	sfReader := NewSkyfileMultipartReader(multipartReader, sup)
   513  
   514  	// read 5 bytes
   515  	data := make([]byte, 5)
   516  	_, err = sfReader.Read(data)
   517  	if err != nil {
   518  		t.Fatal(err)
   519  	}
   520  
   521  	// set them as buffer
   522  	sfReader.SetReadBuffer(data)
   523  
   524  	// read 20 bytes and compare them to what we expect to receive
   525  	expected := append(data1, data2[:10]...)
   526  	data = make([]byte, 20)
   527  	_, err = sfReader.Read(data)
   528  	if err != nil {
   529  		t.Fatal(err)
   530  	}
   531  	if !bytes.Equal(expected, data) {
   532  		t.Fatal("unexpected")
   533  	}
   534  }
   535  
   536  // testSkyfileMultipartReaderMetadataTimeout verifies metadata returns on
   537  // timeout, potentially before the reader is fully read
   538  func testSkyfileMultipartReaderMetadataTimeout(t *testing.T) {
   539  	// Since this is a timeout test only run with long test
   540  	if testing.Short() {
   541  		t.SkipNow()
   542  	}
   543  	t.Parallel()
   544  
   545  	// create upload parameters
   546  	sup := SkyfileUploadParameters{
   547  		Filename: t.Name(),
   548  		Mode:     DefaultFilePerm,
   549  	}
   550  
   551  	// create a multipart writer
   552  	buffer := new(bytes.Buffer)
   553  	writer := multipart.NewWriter(buffer)
   554  
   555  	// prepare random file data
   556  	dataLen := fastrand.Intn(100) + 10
   557  	data := fastrand.Bytes(dataLen)
   558  
   559  	// write the multipart files
   560  	off := uint64(0)
   561  	_, err := AddMultipartFile(writer, data, "files[]", "part", 0600, &off)
   562  	if err != nil {
   563  		t.Fatal("unexpected")
   564  	}
   565  
   566  	// close the writer
   567  	err = writer.Close()
   568  	if err != nil {
   569  		t.Fatal(err)
   570  	}
   571  
   572  	// turn it into a skyfile reader
   573  	reader := bytes.NewReader(buffer.Bytes())
   574  	multipartReader := multipart.NewReader(reader, writer.Boundary())
   575  	sfReader := NewSkyfileMultipartReader(multipartReader, sup)
   576  
   577  	// read less than dataLen
   578  	read := make([]byte, dataLen/2)
   579  	_, err = sfReader.Read(read)
   580  	if err != nil {
   581  		t.Fatal(err)
   582  	}
   583  
   584  	// cancel the context in a goroutine
   585  	ctx, cancel := context.WithCancel(context.Background())
   586  	go func() {
   587  		time.Sleep(time.Second)
   588  		cancel()
   589  	}()
   590  
   591  	// fetch the metadata from the reader
   592  	metadata, err := sfReader.SkyfileMetadata(ctx)
   593  	if !errors.Contains(err, ErrSkyfileMetadataUnavailable) {
   594  		t.Fatal(err)
   595  	}
   596  	if !reflect.DeepEqual(metadata, SkyfileMetadata{}) {
   597  		t.Fatal("unexpected metadata", metadata)
   598  	}
   599  }