github.com/ethersphere/bee/v2@v2.2.0/pkg/cac/cac_test.go (about) 1 // Copyright 2021 The Swarm Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package cac_test 6 7 import ( 8 "bytes" 9 "encoding/binary" 10 "encoding/hex" 11 "errors" 12 "math/rand" 13 "testing" 14 15 "github.com/ethersphere/bee/v2/pkg/cac" 16 "github.com/ethersphere/bee/v2/pkg/swarm" 17 "github.com/ethersphere/bee/v2/pkg/util/testutil" 18 ) 19 20 func TestNew(t *testing.T) { 21 t.Parallel() 22 23 data := []byte("greaterthanspan") 24 bmtHashOfData := "27913f1bdb6e8e52cbd5a5fd4ab577c857287edf6969b41efe926b51de0f4f23" 25 address := swarm.MustParseHexAddress(bmtHashOfData) 26 dataWithSpan := dataWithSpan(data) 27 28 c, err := cac.New(data) 29 if err != nil { 30 t.Fatal(err) 31 } 32 33 if !c.Address().Equal(address) { 34 t.Fatalf("address mismatch. got %s want %s", c.Address().String(), address.String()) 35 } 36 37 if !bytes.Equal(c.Data(), dataWithSpan) { 38 t.Fatalf("chunk data mismatch. got %x want %x", c.Data(), dataWithSpan) 39 } 40 } 41 42 func TestNewWithDataSpan(t *testing.T) { 43 t.Parallel() 44 45 data := []byte("greaterthanspan") 46 bmtHashOfData := "95022e6af5c6d6a564ee55a67f8455a3e18c511b5697c932d9e44f07f2fb8c53" 47 address := swarm.MustParseHexAddress(bmtHashOfData) 48 49 c, err := cac.NewWithDataSpan(data) 50 if err != nil { 51 t.Fatal(err) 52 } 53 54 if !c.Address().Equal(address) { 55 t.Fatalf("address mismatch. got %s want %s", c.Address().String(), address.String()) 56 } 57 58 if !bytes.Equal(c.Data(), data) { 59 t.Fatalf("chunk data mismatch. got %x want %x", c.Data(), data) 60 } 61 } 62 63 func TestChunkInvariantsNew(t *testing.T) { 64 t.Parallel() 65 66 for _, cc := range []struct { 67 name string 68 data []byte 69 wantErr error 70 }{ 71 { 72 name: "nil", 73 data: nil, 74 wantErr: nil, 75 }, 76 { 77 name: "zero data", 78 data: []byte{}, 79 wantErr: nil, 80 }, 81 { 82 name: "too large data chunk", 83 data: testutil.RandBytes(t, swarm.ChunkSize+1), 84 wantErr: cac.ErrChunkDataLarge, 85 }, 86 { 87 name: "ok", 88 data: testutil.RandBytes(t, rand.Intn(swarm.ChunkSize)+1), 89 wantErr: nil, 90 }, 91 } { 92 cc := cc 93 t.Run(cc.name, func(t *testing.T) { 94 t.Parallel() 95 96 _, err := cac.New(cc.data) 97 if !errors.Is(err, cc.wantErr) { 98 t.Fatalf("got %v want %v", err, cc.wantErr) 99 } 100 }) 101 } 102 } 103 104 func TestChunkInvariantsNewWithDataSpan(t *testing.T) { 105 t.Parallel() 106 107 for _, cc := range []struct { 108 name string 109 data []byte 110 wantErr error 111 }{ 112 { 113 name: "nil", 114 data: nil, 115 wantErr: cac.ErrChunkSpanShort, 116 }, 117 { 118 name: "zero data", 119 data: []byte{}, 120 wantErr: cac.ErrChunkSpanShort, 121 }, 122 { 123 name: "small data", 124 data: make([]byte, swarm.SpanSize), 125 wantErr: nil, 126 }, 127 { 128 name: "too large data chunk", 129 data: testutil.RandBytes(t, swarm.ChunkSize+swarm.SpanSize+1), 130 wantErr: cac.ErrChunkDataLarge, 131 }, 132 { 133 name: "ok", 134 data: dataWithSpan(testutil.RandBytes(t, rand.Intn(swarm.ChunkSize)+1)), 135 wantErr: nil, 136 }, 137 } { 138 cc := cc 139 t.Run(cc.name, func(t *testing.T) { 140 t.Parallel() 141 142 _, err := cac.NewWithDataSpan(cc.data) 143 if !errors.Is(err, cc.wantErr) { 144 t.Fatalf("got %v want %v", err, cc.wantErr) 145 } 146 }) 147 } 148 } 149 150 // TestValid checks whether a chunk is a valid content-addressed chunk 151 func TestValid(t *testing.T) { 152 t.Parallel() 153 154 data := "foo" 155 bmtHashOfData := "2387e8e7d8a48c2a9339c97c1dc3461a9a7aa07e994c5cb8b38fd7c1b3e6ea48" 156 address := swarm.MustParseHexAddress(bmtHashOfData) 157 158 ch := swarm.NewChunk(address, dataWithSpan([]byte(data))) 159 assertValidChunk(t, ch, true) 160 161 ch, _ = cac.New([]byte("Digital Freedom Now")) 162 assertValidChunk(t, ch, true) 163 } 164 165 // TestInvalid checks whether a chunk is not a valid content-addressed chunk 166 func TestInvalid(t *testing.T) { 167 t.Parallel() 168 169 // Generates a chunk with the given data. No validation is performed here, 170 // the chunks are create as it is. 171 chunker := func(addr string, dataBytes []byte) swarm.Chunk { 172 addrBytes, _ := hex.DecodeString(addr) 173 address := swarm.NewAddress(addrBytes) 174 return swarm.NewChunk(address, dataBytes) 175 } 176 177 for _, tc := range []struct { 178 name string 179 chunk swarm.Chunk 180 }{ 181 { 182 name: "wrong address", 183 chunk: chunker( 184 "0000e8e7d8a48c2a9339c97c1dc3461a9a7aa07e994c5cb8b38fd7c1b3e6ea48", 185 dataWithSpan([]byte("foo")), 186 ), 187 }, 188 { 189 name: "empty address", 190 chunk: chunker("", dataWithSpan([]byte("foo"))), 191 }, 192 { 193 name: "zero data", 194 chunk: chunker("anything", []byte{}), 195 }, 196 { 197 name: "nil data", 198 chunk: chunker("anything", nil), 199 }, 200 { 201 name: "small data", 202 chunk: chunker( 203 "6251dbc53257832ae80d0e9f1cc41bd54d5b6c704c9c7349709c07fefef0aea6", 204 []byte("small"), 205 ), 206 }, 207 { 208 name: "small data (upper edge case)", 209 chunk: chunker( 210 "6251dbc53257832ae80d0e9f1cc41bd54d5b6c704c9c7349709c07fefef0aea6", 211 dataWithSpan(nil), 212 ), 213 }, 214 { 215 name: "large data", 216 chunk: chunker( 217 "ffd70157e48063fc33c97a050f7f640233bf646cc98d9524c6b92bcf3ab56f83", 218 testutil.RandBytes(t, swarm.ChunkSize+swarm.SpanSize+1), 219 ), 220 }, 221 } { 222 tc := tc 223 t.Run(tc.name, func(t *testing.T) { 224 t.Parallel() 225 226 assertValidChunk(t, tc.chunk, false) 227 }) 228 } 229 } 230 231 func assertValidChunk(t *testing.T, ch swarm.Chunk, expectValid bool) { 232 t.Helper() 233 234 isValid := cac.Valid(ch) 235 if expectValid && !isValid { 236 t.Fatalf("data '%x' should not have validated to hash '%s'", ch.Data(), ch.Address()) 237 } else if !expectValid && isValid { 238 t.Fatalf("data '%x' should have validated to hash '%s'", ch.Data(), ch.Address()) 239 } 240 } 241 242 // dataWithSpan appends span to given input data 243 func dataWithSpan(inputData []byte) []byte { 244 dataLength := len(inputData) 245 dataBytes := make([]byte, swarm.SpanSize+dataLength) 246 binary.LittleEndian.PutUint64(dataBytes, uint64(dataLength)) 247 copy(dataBytes[swarm.SpanSize:], inputData) 248 return dataBytes 249 }