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 }