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 }