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