storj.io/uplink@v1.13.0/private/storage/streams/splitter/splitter_test.go (about)

     1  // Copyright (C) 2023 Storj Labs, Inc.
     2  // See LICENSE for copying information.
     3  
     4  package splitter
     5  
     6  import (
     7  	"context"
     8  	"fmt"
     9  	"io"
    10  	"testing"
    11  
    12  	"github.com/stretchr/testify/require"
    13  	"github.com/zeebo/errs"
    14  
    15  	"storj.io/common/storj"
    16  )
    17  
    18  func TestSplitter(t *testing.T) {
    19  	ctx := context.Background()
    20  
    21  	opts := Options{
    22  		Split:   20,
    23  		Minimum: 10,
    24  		Params: storj.EncryptionParameters{
    25  			CipherSuite: storj.EncAESGCM,
    26  			BlockSize:   21, // 16 bytes of padding + 5 bytes of plaintext
    27  		},
    28  		Key:        new(storj.Key),
    29  		PartNumber: 1,
    30  	}
    31  
    32  	canceled := errs.New("canceled")
    33  
    34  	type result struct {
    35  		kind     string
    36  		plain    int64
    37  		enc      int64
    38  		err      error
    39  		position int
    40  	}
    41  
    42  	type test struct {
    43  		name    string
    44  		write   int64
    45  		finish  error
    46  		results []result
    47  	}
    48  
    49  	readResult := func(splitter *Splitter) (res result, ok bool) {
    50  		seg, err := splitter.Next(ctx)
    51  		if err != nil {
    52  			return result{"error", 0, 0, err, 0}, false
    53  		} else if seg == nil {
    54  			return result{"done", 0, 0, nil, 0}, false
    55  		}
    56  
    57  		_ = seg.Begin() // ensure this can even be called
    58  
    59  		n, err := io.Copy(io.Discard, seg.Reader())
    60  		seg.DoneReading(nil)
    61  
    62  		if err != nil {
    63  			return result{"seg_error", 0, 0, err, 0}, false
    64  		}
    65  
    66  		data, err := seg.EncryptETag([]byte("some etag")) // ensure this can even be called
    67  		require.NoError(t, err)
    68  		require.NotNil(t, data)
    69  
    70  		info := seg.Finalize()
    71  		require.Equal(t, n, info.EncryptedSize)
    72  
    73  		return result{
    74  			kind:     map[bool]string{true: "inline", false: "remote"}[seg.Inline()],
    75  			plain:    info.PlainSize,
    76  			enc:      info.EncryptedSize,
    77  			err:      err,
    78  			position: int(seg.Position().Index),
    79  		}, true
    80  	}
    81  
    82  	cases := []test{
    83  		{"Basic", 45, nil, []result{
    84  			{"remote", 20, 21 * (4 + 1), nil, 0},
    85  			{"remote", 20, 21 * (4 + 1), nil, 1},
    86  			{"inline", 5, 5 + 16, nil, 2},
    87  			{"done", 0, 0, nil, 0},
    88  		}},
    89  
    90  		{"Aligned", 40, nil, []result{
    91  			{"remote", 20, 21 * (4 + 1), nil, 0},
    92  			{"remote", 20, 21 * (4 + 1), nil, 1},
    93  			{"done", 0, 0, nil, 0},
    94  		}},
    95  
    96  		{"Inline", 5, nil, []result{
    97  			{"inline", 5, 5 + 16, nil, 0},
    98  			{"done", 0, 0, nil, 0},
    99  		}},
   100  
   101  		{"Inline_Aligned", 10, nil, []result{
   102  			{"inline", 10, 10 + 16, nil, 0},
   103  			{"done", 0, 0, nil, 0},
   104  		}},
   105  
   106  		{"Zero", 0, nil, []result{
   107  			{"inline", 0, 0, nil, 0},
   108  			{"done", 0, 0, nil, 0},
   109  		}},
   110  
   111  		{"Error_Inline", 45, canceled, []result{
   112  			{"remote", 20, 21 * (4 + 1), nil, 0},
   113  			{"remote", 20, 21 * (4 + 1), nil, 1},
   114  			{"error", 0, 0, canceled, 0},
   115  		}},
   116  
   117  		{"Error_Buffer", 55, canceled, []result{
   118  			{"remote", 20, 21 * (4 + 1), nil, 0},
   119  			{"remote", 20, 21 * (4 + 1), nil, 1},
   120  			{"seg_error", 0, 0, canceled, 0},
   121  		}},
   122  
   123  		{"Error_Aligned", 30, canceled, []result{
   124  			{"remote", 20, 21 * (4 + 1), nil, 0},
   125  			{"error", 0, 0, canceled, 0},
   126  		}},
   127  	}
   128  
   129  	for _, tc := range cases {
   130  		tc := tc
   131  		t.Run(tc.name, func(t *testing.T) {
   132  			splitter, err := New(opts)
   133  			require.NoError(t, err)
   134  
   135  			go func() {
   136  				n, err := io.CopyN(randomWriter{splitter}, emptyReader{}, tc.write)
   137  				splitter.Finish(tc.finish)
   138  				if n != tc.write || err != nil {
   139  					panic(fmt.Sprintln("not enough bytes written or error:", n, tc.write, err))
   140  				}
   141  			}()
   142  
   143  			var results []result
   144  			for {
   145  				res, ok := readResult(splitter)
   146  				results = append(results, res)
   147  				if !ok {
   148  					break
   149  				}
   150  			}
   151  			require.Equal(t, tc.results, results)
   152  		})
   153  	}
   154  }