github.com/hechain20/hechain@v0.0.0-20220316014945-b544036ba106/core/ledger/kvledger/txmgmt/statedb/statecouchdb/couchdbutil_test.go (about)

     1  /*
     2  Copyright hechain. All Rights Reserved.
     3  
     4  SPDX-License-Identifier: Apache-2.0
     5  */
     6  
     7  package statecouchdb
     8  
     9  import (
    10  	"encoding/hex"
    11  	fmt "fmt"
    12  	"testing"
    13  
    14  	"github.com/hechain20/hechain/common/metrics/disabled"
    15  	"github.com/hechain20/hechain/common/util"
    16  	"github.com/stretchr/testify/require"
    17  )
    18  
    19  // Unit test of couch db util functionality
    20  func TestCreateCouchDBConnectionAndDB(t *testing.T) {
    21  	config := testConfig()
    22  	couchDBEnv.startCouchDB(t)
    23  	config.Address = couchDBEnv.couchAddress
    24  	defer couchDBEnv.cleanup(config)
    25  	database := "testcreatecouchdbconnectionanddb"
    26  	// create a new connection
    27  	couchInstance, err := createCouchInstance(config, &disabled.Provider{})
    28  	require.NoError(t, err, "Error when trying to CreateCouchInstance")
    29  
    30  	_, err = createCouchDatabase(couchInstance, database)
    31  	require.NoError(t, err, "Error when trying to CreateCouchDatabase")
    32  }
    33  
    34  // Unit test of couch db util functionality
    35  func TestNotCreateCouchGlobalChangesDB(t *testing.T) {
    36  	config := testConfig()
    37  	couchDBEnv.startCouchDB(t)
    38  	config.Address = couchDBEnv.couchAddress
    39  	defer couchDBEnv.cleanup(config)
    40  	config.CreateGlobalChangesDB = false
    41  	database := "_global_changes"
    42  
    43  	// create a new connection
    44  	couchInstance, err := createCouchInstance(config, &disabled.Provider{})
    45  	require.NoError(t, err, "Error when trying to CreateCouchInstance")
    46  
    47  	db := couchDatabase{couchInstance: couchInstance, dbName: database}
    48  
    49  	// Retrieve the info for the new database and make sure the name matches
    50  	_, _, errdb := db.getDatabaseInfo()
    51  	require.NotNil(t, errdb)
    52  }
    53  
    54  // Unit test of couch db util functionality
    55  func TestCreateCouchDBSystemDBs(t *testing.T) {
    56  	config := testConfig()
    57  	couchDBEnv.startCouchDB(t)
    58  	config.Address = couchDBEnv.couchAddress
    59  	defer couchDBEnv.cleanup(config)
    60  	config.CreateGlobalChangesDB = true
    61  
    62  	// create a new connection
    63  	couchInstance, err := createCouchInstance(config, &disabled.Provider{})
    64  
    65  	require.NoError(t, err, "Error when trying to CreateCouchInstance")
    66  
    67  	err = createSystemDatabasesIfNotExist(couchInstance)
    68  	require.NoError(t, err, "Error when trying to create system databases")
    69  
    70  	db := couchDatabase{couchInstance: couchInstance, dbName: "_users"}
    71  
    72  	// Retrieve the info for the new database and make sure the name matches
    73  	dbResp, _, errdb := db.getDatabaseInfo()
    74  	require.NoError(t, errdb, "Error when trying to retrieve _users database information")
    75  	require.Equal(t, "_users", dbResp.DbName)
    76  
    77  	db = couchDatabase{couchInstance: couchInstance, dbName: "_replicator"}
    78  
    79  	// Retrieve the info for the new database and make sure the name matches
    80  	dbResp, _, errdb = db.getDatabaseInfo()
    81  	require.NoError(t, errdb, "Error when trying to retrieve _replicator database information")
    82  	require.Equal(t, "_replicator", dbResp.DbName)
    83  
    84  	db = couchDatabase{couchInstance: couchInstance, dbName: "_global_changes"}
    85  
    86  	// Retrieve the info for the new database and make sure the name matches
    87  	dbResp, _, errdb = db.getDatabaseInfo()
    88  	require.NoError(t, errdb, "Error when trying to retrieve _global_changes database information")
    89  	require.Equal(t, "_global_changes", dbResp.DbName)
    90  }
    91  
    92  func TestDatabaseMapping(t *testing.T) {
    93  	// create a new instance and database object using a database name mixed case
    94  	_, err := mapAndValidateDatabaseName("testDB")
    95  	require.Error(t, err, "Error expected because the name contains capital letters")
    96  
    97  	// create a new instance and database object using a database name with special characters
    98  	_, err = mapAndValidateDatabaseName("test1234/1")
    99  	require.Error(t, err, "Error expected because the name contains illegal chars")
   100  
   101  	// create a new instance and database object using a database name with special characters
   102  	_, err = mapAndValidateDatabaseName("5test1234")
   103  	require.Error(t, err, "Error expected because the name starts with a number")
   104  
   105  	// create a new instance and database object using an empty string
   106  	_, err = mapAndValidateDatabaseName("")
   107  	require.Error(t, err, "Error should have been thrown for an invalid name")
   108  
   109  	_, err = mapAndValidateDatabaseName("a12345678901234567890123456789012345678901234" +
   110  		"56789012345678901234567890123456789012345678901234567890123456789012345678901234567890" +
   111  		"12345678901234567890123456789012345678901234567890123456789012345678901234567890123456" +
   112  		"78901234567890123456789012345678901234567890")
   113  	require.Error(t, err, "Error should have been thrown for an invalid name")
   114  
   115  	transformedName, err := mapAndValidateDatabaseName("test.my.db-1")
   116  	require.NoError(t, err, "")
   117  	require.Equal(t, "test$my$db-1", transformedName)
   118  }
   119  
   120  func TestConstructMetadataDBName(t *testing.T) {
   121  	// Allowed pattern for chainName: [a-z][a-z0-9.-]
   122  	chainName := "tob2g.y-z0f.qwp-rq5g4-ogid5g6oucyryg9sc16mz0t4vuake5q557esz7sn493nf0ghch0xih6dwuirokyoi4jvs67gh6r5v6mhz3-292un2-9egdcs88cstg3f7xa9m1i8v4gj0t3jedsm-woh3kgiqehwej6h93hdy5tr4v.1qmmqjzz0ox62k.507sh3fkw3-mfqh.ukfvxlm5szfbwtpfkd1r4j.cy8oft5obvwqpzjxb27xuw6"
   123  
   124  	truncatedChainName := "tob2g.y-z0f.qwp-rq5g4-ogid5g6oucyryg9sc16mz0t4vuak"
   125  	require.Equal(t, chainNameAllowedLength, len(truncatedChainName))
   126  
   127  	// <first 50 chars (i.e., chainNameAllowedLength) of chainName> + 1 char for '(' + <64 chars for SHA256 hash
   128  	// (hex encoding) of untruncated chainName> + 1 char for ')' + 1 char for '_' = 117 chars
   129  	hash := hex.EncodeToString(util.ComputeSHA256([]byte(chainName)))
   130  	expectedDBName := truncatedChainName + "(" + hash + ")" + "_"
   131  	expectedDBNameLength := 117
   132  
   133  	constructedDBName := constructMetadataDBName(chainName)
   134  	require.Equal(t, expectedDBNameLength, len(constructedDBName))
   135  	require.Equal(t, expectedDBName, constructedDBName)
   136  }
   137  
   138  func TestConstructedNamespaceDBName(t *testing.T) {
   139  	// === SCENARIO 1: chainName_ns$$coll ===
   140  
   141  	// Allowed pattern for chainName: [a-z][a-z0-9.-]
   142  	chainName := "tob2g.y-z0f.qwp-rq5g4-ogid5g6oucyryg9sc16mz0t4vuake5q557esz7sn493nf0ghch0xih6dwuirokyoi4jvs67gh6r5v6mhz3-292un2-9egdcs88cstg3f7xa9m1i8v4gj0t3jedsm-woh3kgiqehwej6h93hdy5tr4v.1qmmqjzz0ox62k.507sh3fkw3-mfqh.ukfvxlm5szfbwtpfkd1r4j.cy8oft5obvwqpzjxb27xuw6"
   143  
   144  	// Allowed pattern for namespace and collection: [a-zA-Z0-9_-]
   145  	ns := "wMCnSXiV9YoIqNQyNvFVTdM8XnUtvrOFFIWsKelmP5NEszmNLl8YhtOKbFu3P_NgwgsYF8PsfwjYCD8f1XRpANQLoErDHwLlweryqXeJ6vzT2x0pS_GwSx0m6tBI0zOmHQOq_2De8A87x6zUOPwufC2T6dkidFxiuq8Sey2-5vUo_iNKCij3WTeCnKx78PUIg_U1gp4_0KTvYVtRBRvH0kz5usizBxPaiFu3TPhB9XLviScvdUVSbSYJ0Z"
   146  	// first letter 'p' denotes private data namespace. We can use 'h' to denote hashed data namespace as defined in
   147  	// privacyenabledstate/common_storage_db.go
   148  	coll := "pvWjtfSTXVK8WJus5s6zWoMIciXd7qHRZIusF9SkOS6m8XuHCiJDE9cCRuVerq22Na8qBL2ywDGFpVMIuzfyEXLjeJb0mMuH4cwewT6r1INOTOSYwrikwOLlT_fl0V1L7IQEwUBB8WCvRqSdj6j5-E5aGul_pv_0UeCdwWiyA_GrZmP7ocLzfj2vP8btigrajqdH-irLO2ydEjQUAvf8fiuxru9la402KmKRy457GgI98UHoUdqV3f3FCdR"
   149  
   150  	truncatedChainName := "tob2g.y-z0f.qwp-rq5g4-ogid5g6oucyryg9sc16mz0t4vuak"
   151  	truncatedEscapedNs := "w$m$cn$s$xi$v9$yo$iq$n$qy$nv$f$v$td$m8$xn$utvr$o$f"
   152  	truncatedEscapedColl := "pv$wjtf$s$t$x$v$k8$w$jus5s6z$wo$m$ici$xd7q$h$r$z$i"
   153  	require.Equal(t, chainNameAllowedLength, len(truncatedChainName))
   154  	require.Equal(t, namespaceNameAllowedLength, len(truncatedEscapedNs))
   155  	require.Equal(t, collectionNameAllowedLength, len(truncatedEscapedColl))
   156  
   157  	untruncatedDBName := chainName + "_" + ns + "$$" + coll
   158  	hash := hex.EncodeToString(util.ComputeSHA256([]byte(untruncatedDBName)))
   159  	expectedDBName := truncatedChainName + "_" + truncatedEscapedNs + "$$" + truncatedEscapedColl + "(" + hash + ")"
   160  	// <first 50 chars (i.e., chainNameAllowedLength) of chainName> + 1 char for '_' + <first 50 chars
   161  	// (i.e., namespaceNameAllowedLength) of escaped namespace> + 2 chars for '$$' + <first 50 chars
   162  	// (i.e., collectionNameAllowedLength) of escaped collection> + 1 char for '(' + <64 chars for SHA256 hash
   163  	// (hex encoding) of untruncated chainName_ns$$coll> + 1 char for ')' = 219 chars
   164  	expectedDBNameLength := 219
   165  
   166  	namespace := ns + "$$" + coll
   167  	constructedDBName := constructNamespaceDBName(chainName, namespace)
   168  	require.Equal(t, expectedDBNameLength, len(constructedDBName))
   169  	require.Equal(t, expectedDBName, constructedDBName)
   170  
   171  	// === SCENARIO 2: chainName_ns ===
   172  
   173  	untruncatedDBName = chainName + "_" + ns
   174  	hash = hex.EncodeToString(util.ComputeSHA256([]byte(untruncatedDBName)))
   175  	expectedDBName = truncatedChainName + "_" + truncatedEscapedNs + "(" + hash + ")"
   176  	// <first 50 chars (i.e., chainNameAllowedLength) of chainName> + 1 char for '_' + <first 50 chars
   177  	// (i.e., namespaceNameAllowedLength) of escaped namespace> + 1 char for '(' + <64 chars for SHA256 hash
   178  	// (hex encoding) of untruncated chainName_ns> + 1 char for ')' = 167 chars
   179  	expectedDBNameLength = 167
   180  
   181  	namespace = ns
   182  	constructedDBName = constructNamespaceDBName(chainName, namespace)
   183  	require.Equal(t, expectedDBNameLength, len(constructedDBName))
   184  	require.Equal(t, expectedDBName, constructedDBName)
   185  }
   186  
   187  func TestDropApplicationDBs(t *testing.T) {
   188  	config := testConfig()
   189  	couchDBEnv.startCouchDB(t)
   190  	config.Address = couchDBEnv.couchAddress
   191  	defer couchDBEnv.cleanup(config)
   192  	database := "testdropapplicationdbs"
   193  
   194  	couchInstance, err := createCouchInstance(config, &disabled.Provider{})
   195  	require.NoError(t, err, "Error when trying to create couch instance")
   196  
   197  	numCouchdbs := 10
   198  	for i := 0; i < numCouchdbs; i++ {
   199  		db, err := createCouchDatabase(couchInstance, fmt.Sprintf("%s_%d", database, i))
   200  		require.NoErrorf(t, err, "Error when trying to create database %s", db.dbName)
   201  	}
   202  
   203  	dbs, err := couchInstance.retrieveApplicationDBNames()
   204  	require.NoError(t, err, "Error when retrieving application db names")
   205  	require.Equal(t, numCouchdbs, len(dbs), "Expected number of databases are not created")
   206  
   207  	err = DropApplicationDBs(config)
   208  	require.NoError(t, err, "Error when dropping all application dbs")
   209  
   210  	dbs, err = couchInstance.retrieveApplicationDBNames()
   211  	require.NoError(t, err, "Error when retrieving application db names")
   212  	require.Equal(t, 0, len(dbs), "Databases should be dropped")
   213  }
   214  
   215  func TestDropApplicationDBsWhenDBNotStarted(t *testing.T) {
   216  	config := testConfig()
   217  	config.MaxRetriesOnStartup = 1
   218  	config.Address = "127.0.0.1:5984"
   219  	err := DropApplicationDBs(config)
   220  	require.EqualError(t, err, `unable to connect to CouchDB, check the hostname and port: http error calling couchdb: Get "http://127.0.0.1:5984/": dial tcp 127.0.0.1:5984: connect: connection refused`)
   221  }