github.com/myafeier/fabric@v1.0.1-0.20170722181825-3a4b1f2bce86/orderer/common/ledger/file/impl_test.go (about)

     1  /*
     2  Copyright IBM Corp. 2016 All Rights Reserved.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8                   http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package fileledger
    18  
    19  import (
    20  	"errors"
    21  	"fmt"
    22  	"io/ioutil"
    23  	"os"
    24  	"testing"
    25  
    26  	"github.com/hyperledger/fabric/common/configtx/tool/provisional"
    27  	cl "github.com/hyperledger/fabric/common/ledger"
    28  	"github.com/hyperledger/fabric/orderer/common/ledger"
    29  	cb "github.com/hyperledger/fabric/protos/common"
    30  	ab "github.com/hyperledger/fabric/protos/orderer"
    31  	"github.com/hyperledger/fabric/protos/peer"
    32  	logging "github.com/op/go-logging"
    33  	"github.com/stretchr/testify/assert"
    34  	"github.com/stretchr/testify/mock"
    35  )
    36  
    37  var genesisBlock = cb.NewBlock(0, nil)
    38  
    39  func init() {
    40  	logging.SetLevel(logging.DEBUG, "")
    41  }
    42  
    43  type testEnv struct {
    44  	t        *testing.T
    45  	location string
    46  	flf      ledger.Factory
    47  }
    48  
    49  func initialize(t *testing.T) (*testEnv, *fileLedger) {
    50  	name, err := ioutil.TempDir("", "hyperledger_fabric")
    51  	assert.NoError(t, err, "Error creating temp dir: %s", err)
    52  
    53  	flf := New(name).(*fileLedgerFactory)
    54  	fl, err := flf.GetOrCreate(provisional.TestChainID)
    55  	assert.NoError(t, err, "Error GetOrCreate chain")
    56  
    57  	fl.Append(genesisBlock)
    58  	return &testEnv{location: name, t: t, flf: flf}, fl.(*fileLedger)
    59  }
    60  
    61  func (tev *testEnv) tearDown() {
    62  	tev.shutDown()
    63  	err := os.RemoveAll(tev.location)
    64  	if err != nil {
    65  		tev.t.Fatalf("Error tearing down env: %s", err)
    66  	}
    67  }
    68  
    69  func (tev *testEnv) shutDown() {
    70  	tev.flf.Close()
    71  }
    72  
    73  type mockBlockStore struct {
    74  	blockchainInfo             *cb.BlockchainInfo
    75  	resultsIterator            cl.ResultsIterator
    76  	block                      *cb.Block
    77  	envelope                   *cb.Envelope
    78  	txValidationCode           peer.TxValidationCode
    79  	defaultError               error
    80  	getBlockchainInfoError     error
    81  	retrieveBlockByNumberError error
    82  }
    83  
    84  func (mbs *mockBlockStore) AddBlock(block *cb.Block) error {
    85  	return mbs.defaultError
    86  }
    87  
    88  func (mbs *mockBlockStore) GetBlockchainInfo() (*cb.BlockchainInfo, error) {
    89  	return mbs.blockchainInfo, mbs.getBlockchainInfoError
    90  }
    91  
    92  func (mbs *mockBlockStore) RetrieveBlocks(startNum uint64) (cl.ResultsIterator, error) {
    93  	return mbs.resultsIterator, mbs.defaultError
    94  }
    95  
    96  func (mbs *mockBlockStore) RetrieveBlockByHash(blockHash []byte) (*cb.Block, error) {
    97  	return mbs.block, mbs.defaultError
    98  }
    99  
   100  func (mbs *mockBlockStore) RetrieveBlockByNumber(blockNum uint64) (*cb.Block, error) {
   101  	return mbs.block, mbs.retrieveBlockByNumberError
   102  }
   103  
   104  func (mbs *mockBlockStore) RetrieveTxByID(txID string) (*cb.Envelope, error) {
   105  	return mbs.envelope, mbs.defaultError
   106  }
   107  
   108  func (mbs *mockBlockStore) RetrieveTxByBlockNumTranNum(blockNum uint64, tranNum uint64) (*cb.Envelope, error) {
   109  	return mbs.envelope, mbs.defaultError
   110  }
   111  
   112  func (mbs *mockBlockStore) RetrieveBlockByTxID(txID string) (*cb.Block, error) {
   113  	return mbs.block, mbs.defaultError
   114  }
   115  
   116  func (mbs *mockBlockStore) RetrieveTxValidationCodeByTxID(txID string) (peer.TxValidationCode, error) {
   117  	return mbs.txValidationCode, mbs.defaultError
   118  }
   119  
   120  func (*mockBlockStore) Shutdown() {
   121  }
   122  
   123  type mockBlockStoreIterator struct {
   124  	mock.Mock
   125  }
   126  
   127  func (m *mockBlockStoreIterator) Next() (cl.QueryResult, error) {
   128  	args := m.Called()
   129  	return args.Get(0), args.Error(1)
   130  }
   131  
   132  func (m *mockBlockStoreIterator) Close() {
   133  	m.Called()
   134  }
   135  
   136  func TestInitialization(t *testing.T) {
   137  	tev, fl := initialize(t)
   138  	defer tev.tearDown()
   139  
   140  	assert.Equal(t, uint64(1), fl.Height(), "Block height should be 1")
   141  
   142  	block := ledger.GetBlock(fl, 0)
   143  	assert.NotNil(t, block, "Error retrieving genesis block")
   144  	assert.Equal(t, genesisBlock.Header.Hash(), block.Header.Hash(), "Block hashes did no match")
   145  }
   146  
   147  func TestReinitialization(t *testing.T) {
   148  	tev, ledger1 := initialize(t)
   149  	defer tev.tearDown()
   150  
   151  	// create a block to add to the ledger
   152  	b1 := ledger.CreateNextBlock(ledger1, []*cb.Envelope{&cb.Envelope{Payload: []byte("My Data")}})
   153  
   154  	// add the block to the ledger
   155  	ledger1.Append(b1)
   156  
   157  	fl, err := tev.flf.GetOrCreate(provisional.TestChainID)
   158  	ledger1, ok := fl.(*fileLedger)
   159  	assert.NoError(t, err, "Expected to sucessfully get test chain")
   160  	assert.Equal(t, 1, len(tev.flf.ChainIDs()), "Exptected not new chain to be created")
   161  	assert.True(t, ok, "Exptected type assertion to succeed")
   162  
   163  	// shutdown the ledger
   164  	ledger1.blockStore.Shutdown()
   165  
   166  	// shut down the ledger provider
   167  	tev.shutDown()
   168  
   169  	// re-initialize the ledger provider (not the test ledger itself!)
   170  	provider2 := New(tev.location)
   171  
   172  	// assert expected ledgers exist
   173  	chains := provider2.ChainIDs()
   174  	assert.Equal(t, 1, len(chains), "Should have recovered the chain")
   175  
   176  	// get the existing test chain ledger
   177  	ledger2, err := provider2.GetOrCreate(chains[0])
   178  	assert.NoError(t, err, "Unexpected error: %s", err)
   179  
   180  	fl = ledger2.(*fileLedger)
   181  	assert.Equal(t, uint64(2), fl.Height(), "Block height should be 2. Got %v", fl.Height())
   182  
   183  	block := ledger.GetBlock(fl, 1)
   184  	assert.NotNil(t, block, "Error retrieving block 1")
   185  	assert.Equal(t, b1.Header.Hash(), block.Header.Hash(), "Block hashes did no match")
   186  }
   187  
   188  func TestAddition(t *testing.T) {
   189  	tev, fl := initialize(t)
   190  	defer tev.tearDown()
   191  	info, _ := fl.blockStore.GetBlockchainInfo()
   192  	prevHash := info.CurrentBlockHash
   193  	fl.Append(ledger.CreateNextBlock(fl, []*cb.Envelope{&cb.Envelope{Payload: []byte("My Data")}}))
   194  	assert.Equal(t, uint64(2), fl.Height(), "Block height should be 2")
   195  
   196  	block := ledger.GetBlock(fl, 1)
   197  	assert.NotNil(t, block, "Error retrieving genesis block")
   198  	assert.Equal(t, prevHash, block.Header.PreviousHash, "Block hashes did no match")
   199  }
   200  
   201  func TestRetrieval(t *testing.T) {
   202  	tev, fl := initialize(t)
   203  	defer tev.tearDown()
   204  	fl.Append(ledger.CreateNextBlock(fl, []*cb.Envelope{&cb.Envelope{Payload: []byte("My Data")}}))
   205  	it, num := fl.Iterator(&ab.SeekPosition{Type: &ab.SeekPosition_Oldest{}})
   206  	defer it.Close()
   207  	assert.Zero(t, num, "Expected genesis block iterator, but got %d", num)
   208  
   209  	signal := it.ReadyChan()
   210  	select {
   211  	case <-signal:
   212  	default:
   213  		t.Fatalf("Should be ready for block read")
   214  	}
   215  
   216  	block, status := it.Next()
   217  	assert.Equal(t, cb.Status_SUCCESS, status, "Expected to successfully read the genesis block")
   218  	assert.Zero(t, block.Header.Number, "Expected to successfully retrieve the genesis block")
   219  
   220  	signal = it.ReadyChan()
   221  	select {
   222  	case <-signal:
   223  	default:
   224  		t.Fatalf("Should still be ready for block read")
   225  	}
   226  
   227  	block, status = it.Next()
   228  	assert.Equal(t, cb.Status_SUCCESS, status, "Expected to successfully read the second block")
   229  	assert.Equal(
   230  		t,
   231  		uint64(1),
   232  		block.Header.Number,
   233  		"Expected to successfully retrieve the second block but got block number %d", block.Header.Number)
   234  }
   235  
   236  func TestBlockedRetrieval(t *testing.T) {
   237  	tev, fl := initialize(t)
   238  	defer tev.tearDown()
   239  	it, num := fl.Iterator(&ab.SeekPosition{Type: &ab.SeekPosition_Specified{Specified: &ab.SeekSpecified{Number: 1}}})
   240  	defer it.Close()
   241  	if num != 1 {
   242  		t.Fatalf("Expected block iterator at 1, but got %d", num)
   243  	}
   244  	assert.Equal(t, uint64(1), num, "Expected block iterator at 1, but got %d", num)
   245  
   246  	signal := it.ReadyChan()
   247  	select {
   248  	case <-signal:
   249  		t.Fatalf("Should not be ready for block read")
   250  	default:
   251  	}
   252  
   253  	fl.Append(ledger.CreateNextBlock(fl, []*cb.Envelope{&cb.Envelope{Payload: []byte("My Data")}}))
   254  	select {
   255  	case <-signal:
   256  	default:
   257  		t.Fatalf("Should now be ready for block read")
   258  	}
   259  
   260  	block, status := it.Next()
   261  	assert.Equal(t, cb.Status_SUCCESS, status, "Expected to successfully read the second block")
   262  	assert.Equal(
   263  		t,
   264  		uint64(1),
   265  		block.Header.Number,
   266  		"Expected to successfully retrieve the second block but got block number %d", block.Header.Number)
   267  
   268  	go func() {
   269  		fl.Append(ledger.CreateNextBlock(fl, []*cb.Envelope{&cb.Envelope{Payload: []byte("My Data")}}))
   270  	}()
   271  	select {
   272  	case <-it.ReadyChan():
   273  		t.Fatalf("Should not be ready for block read")
   274  	default:
   275  		block, status = it.Next()
   276  		assert.Equal(t, cb.Status_SUCCESS, status, "Expected to successfully read the third block")
   277  		assert.Equal(t, uint64(2), block.Header.Number, "Expected to successfully retrieve the third block")
   278  	}
   279  }
   280  
   281  func TestBlockstoreError(t *testing.T) {
   282  	// Since this test only ensures failed GetBlockchainInfo
   283  	// is properly handled. We don't bother creating fully
   284  	// legit ledgers here (without genesis block).
   285  	{
   286  		fl := &fileLedger{
   287  			blockStore: &mockBlockStore{
   288  				blockchainInfo:         nil,
   289  				getBlockchainInfoError: fmt.Errorf("Error getting blockchain info"),
   290  			},
   291  			signal: make(chan struct{}),
   292  		}
   293  		assert.Panics(
   294  			t,
   295  			func() {
   296  				fl.Iterator(&ab.SeekPosition{Type: &ab.SeekPosition_Newest{}})
   297  			},
   298  			"Expected Iterator() to panic if blockstore operation fails")
   299  
   300  		assert.Panics(
   301  			t,
   302  			func() { fl.Height() },
   303  			"Expected Height() to panic if blockstore operation fails ")
   304  	}
   305  
   306  	{
   307  		fl := &fileLedger{
   308  			blockStore: &mockBlockStore{
   309  				blockchainInfo:             &cb.BlockchainInfo{Height: uint64(1)},
   310  				getBlockchainInfoError:     nil,
   311  				retrieveBlockByNumberError: fmt.Errorf("Error retrieving block by number"),
   312  			},
   313  			signal: make(chan struct{}),
   314  		}
   315  		it, _ := fl.Iterator(&ab.SeekPosition{Type: &ab.SeekPosition_Specified{Specified: &ab.SeekSpecified{Number: 42}}})
   316  		defer it.Close()
   317  		assert.IsType(
   318  			t,
   319  			&ledger.NotFoundErrorIterator{},
   320  			it,
   321  			"Expected Not Found Error if seek number is greater than ledger height")
   322  	}
   323  
   324  	{
   325  		resultsIterator := &mockBlockStoreIterator{}
   326  		resultsIterator.On("Next").Return(nil, errors.New("a mocked error"))
   327  		resultsIterator.On("Close").Return()
   328  		fl := &fileLedger{
   329  			blockStore: &mockBlockStore{
   330  				blockchainInfo:             &cb.BlockchainInfo{Height: uint64(1)},
   331  				getBlockchainInfoError:     nil,
   332  				retrieveBlockByNumberError: fmt.Errorf("Error retrieving block by number"),
   333  				resultsIterator:            resultsIterator,
   334  			},
   335  			signal: make(chan struct{}),
   336  		}
   337  		it, _ := fl.Iterator(&ab.SeekPosition{Type: &ab.SeekPosition_Oldest{}})
   338  		defer it.Close()
   339  		_, status := it.Next()
   340  		assert.Equal(t, cb.Status_SERVICE_UNAVAILABLE, status, "Expected service unavailable error")
   341  	}
   342  }