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  }