github.com/myafeier/fabric@v1.0.1-0.20170722181825-3a4b1f2bce86/core/chaincode/platforms/util/utils_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 util 18 19 import ( 20 "archive/tar" 21 "bytes" 22 "compress/gzip" 23 "encoding/hex" 24 "fmt" 25 "io" 26 "io/ioutil" 27 "math/rand" 28 "os" 29 "strings" 30 "testing" 31 "time" 32 33 "github.com/hyperledger/fabric/common/util" 34 "github.com/hyperledger/fabric/core/config" 35 cutil "github.com/hyperledger/fabric/core/container/util" 36 "github.com/spf13/viper" 37 "github.com/stretchr/testify/assert" 38 ) 39 40 // TestHashContentChange changes a random byte in a content and checks for hash change 41 func TestHashContentChange(t *testing.T) { 42 b := []byte("firstcontent") 43 hash := util.ComputeSHA256(b) 44 45 b2 := []byte("To be, or not to be- that is the question: Whether 'tis nobler in the mind to suffer The slings and arrows of outrageous fortune Or to take arms against a sea of troubles, And by opposing end them. To die- to sleep- No more; and by a sleep to say we end The heartache, and the thousand natural shocks That flesh is heir to. 'Tis a consummation Devoutly to be wish'd.") 46 47 h1 := ComputeHash(b2, hash) 48 49 r := rand.New(rand.NewSource(time.Now().UnixNano())) 50 randIndex := (int(r.Uint32())) % len(b2) 51 52 randByte := byte((int(r.Uint32())) % 128) 53 54 //make sure the two bytes are different 55 for { 56 if randByte != b2[randIndex] { 57 break 58 } 59 60 randByte = byte((int(r.Uint32())) % 128) 61 } 62 63 //change a random byte 64 b2[randIndex] = randByte 65 66 //this is the core hash func under test 67 h2 := ComputeHash(b2, hash) 68 69 //the two hashes should be different 70 if bytes.Compare(h1, h2) == 0 { 71 t.Error("Hash expected to be different but is same") 72 } 73 } 74 75 // TestHashLenChange changes a random length of a content and checks for hash change 76 func TestHashLenChange(t *testing.T) { 77 b := []byte("firstcontent") 78 hash := util.ComputeSHA256(b) 79 80 b2 := []byte("To be, or not to be-") 81 82 h1 := ComputeHash(b2, hash) 83 84 r := rand.New(rand.NewSource(time.Now().UnixNano())) 85 randIndex := (int(r.Uint32())) % len(b2) 86 87 b2 = b2[0:randIndex] 88 89 h2 := ComputeHash(b2, hash) 90 91 //hash should be different 92 if bytes.Compare(h1, h2) == 0 { 93 t.Error("Hash expected to be different but is same") 94 } 95 } 96 97 // TestHashOrderChange changes a order of hash computation over a list of lines and checks for hash change 98 func TestHashOrderChange(t *testing.T) { 99 b := []byte("firstcontent") 100 hash := util.ComputeSHA256(b) 101 102 b2 := [][]byte{[]byte("To be, or not to be- that is the question:"), 103 []byte("Whether 'tis nobler in the mind to suffer"), 104 []byte("The slings and arrows of outrageous fortune"), 105 []byte("Or to take arms against a sea of troubles,"), 106 []byte("And by opposing end them."), 107 []byte("To die- to sleep- No more; and by a sleep to say we end"), 108 []byte("The heartache, and the thousand natural shocks"), 109 []byte("That flesh is heir to."), 110 []byte("'Tis a consummation Devoutly to be wish'd.")} 111 h1 := hash 112 113 for _, l := range b2 { 114 h1 = ComputeHash(l, h1) 115 } 116 117 r := rand.New(rand.NewSource(time.Now().UnixNano())) 118 randIndex1 := (int(r.Uint32())) % len(b2) 119 randIndex2 := (int(r.Uint32())) % len(b2) 120 121 //make sure the two indeces are different 122 for { 123 if randIndex2 != randIndex1 { 124 break 125 } 126 127 randIndex2 = (int(r.Uint32())) % len(b2) 128 } 129 130 //switch two arbitrary lines 131 tmp := b2[randIndex2] 132 b2[randIndex2] = b2[randIndex1] 133 b2[randIndex1] = tmp 134 135 h2 := hash 136 for _, l := range b2 { 137 h2 = ComputeHash(l, hash) 138 } 139 140 //hash should be different 141 if bytes.Compare(h1, h2) == 0 { 142 t.Error("Hash expected to be different but is same") 143 } 144 } 145 146 // TestHashOverFiles computes hash over a directory and ensures it matches precomputed, hardcoded, hash 147 func TestHashOverFiles(t *testing.T) { 148 b := []byte("firstcontent") 149 hash := util.ComputeSHA256(b) 150 151 hash, err := HashFilesInDir(".", "hashtestfiles1", hash, nil) 152 153 if err != nil { 154 t.Fail() 155 t.Logf("error : %s", err) 156 } 157 158 //as long as no files under "hashtestfiles1" are changed, hash should always compute to the following 159 expectedHash := "0c92180028200dfabd08d606419737f5cdecfcbab403e3f0d79e8d949f4775bc" 160 161 computedHash := hex.EncodeToString(hash[:]) 162 163 if expectedHash != computedHash { 164 t.Error("Hash expected to be unchanged") 165 } 166 } 167 168 func TestHashDiffDir(t *testing.T) { 169 b := []byte("firstcontent") 170 hash := util.ComputeSHA256(b) 171 172 hash1, err := HashFilesInDir(".", "hashtestfiles1", hash, nil) 173 if err != nil { 174 t.Errorf("Error getting code %s", err) 175 } 176 hash2, err := HashFilesInDir(".", "hashtestfiles2", hash, nil) 177 if err != nil { 178 t.Errorf("Error getting code %s", err) 179 } 180 if bytes.Compare(hash1, hash2) == 0 { 181 t.Error("Hash should be different for 2 different remote repos") 182 } 183 } 184 185 func TestHashSameDir(t *testing.T) { 186 assert := assert.New(t) 187 188 b := []byte("firstcontent") 189 hash := util.ComputeSHA256(b) 190 hash1, err := HashFilesInDir(".", "hashtestfiles1", hash, nil) 191 assert.NoError(err, "Error getting code") 192 193 fname := os.TempDir() + "/hash.tar" 194 w, err := os.Create(fname) 195 if err != nil { 196 t.Fatal(err) 197 } 198 defer os.Remove(fname) 199 tw := tar.NewWriter(w) 200 defer w.Close() 201 defer tw.Close() 202 hash2, err := HashFilesInDir(".", "hashtestfiles1", hash, tw) 203 assert.NoError(err, "Error getting code") 204 205 assert.Equal(bytes.Compare(hash1, hash2), 0, 206 "Hash should be same across multiple downloads") 207 } 208 209 func TestHashBadWriter(t *testing.T) { 210 b := []byte("firstcontent") 211 hash := util.ComputeSHA256(b) 212 213 fname := os.TempDir() + "/hash.tar" 214 w, err := os.Create(fname) 215 if err != nil { 216 t.Fatal(err) 217 } 218 defer os.Remove(fname) 219 tw := tar.NewWriter(w) 220 defer w.Close() 221 tw.Close() 222 223 _, err = HashFilesInDir(".", "hashtestfiles1", hash, tw) 224 assert.Error(t, err, 225 "HashFilesInDir invoked with closed writer, should have failed") 226 } 227 228 // TestHashNonExistentDir tests HashFilesInDir with non existant directory 229 func TestHashNonExistentDir(t *testing.T) { 230 b := []byte("firstcontent") 231 hash := util.ComputeSHA256(b) 232 _, err := HashFilesInDir(".", "idontexist", hash, nil) 233 assert.Error(t, err, "Expected an error for non existent directory %s", "idontexist") 234 } 235 236 // TestIsCodeExist tests isCodeExist function 237 func TestIsCodeExist(t *testing.T) { 238 assert := assert.New(t) 239 path := os.TempDir() 240 err := IsCodeExist(path) 241 assert.NoError(err, 242 "%s directory exists, IsCodeExist should not have returned error: %v", 243 path, err) 244 245 dir, err := ioutil.TempDir(os.TempDir(), "iscodeexist") 246 assert.NoError(err) 247 defer os.RemoveAll(dir) 248 path = dir + "/blah" 249 err = IsCodeExist(path) 250 assert.Error(err, 251 "%s directory does not exist, IsCodeExist should have returned error", path) 252 253 f := createTempFile(t) 254 defer os.Remove(f) 255 err = IsCodeExist(f) 256 assert.Error(err, "%s is a file, IsCodeExist should have returned error", f) 257 } 258 259 // TestDockerBuild tests DockerBuild function 260 func TestDockerBuild(t *testing.T) { 261 assert := assert.New(t) 262 var err error 263 264 ldflags := "-linkmode external -extldflags '-static'" 265 codepackage := bytes.NewReader(getDeploymentPayload()) 266 binpackage := bytes.NewBuffer(nil) 267 if err != nil { 268 t.Fatal(err) 269 } 270 err = DockerBuild(DockerBuildOptions{ 271 Cmd: fmt.Sprintf("GOPATH=/chaincode/input:$GOPATH go build -ldflags \"%s\" -o /chaincode/output/chaincode helloworld", 272 ldflags), 273 InputStream: codepackage, 274 OutputStream: binpackage, 275 }) 276 assert.NoError(err, "DockerBuild failed") 277 } 278 279 func getDeploymentPayload() []byte { 280 var goprog = ` 281 package main 282 import "fmt" 283 func main() { 284 fmt.Println("Hello World") 285 } 286 ` 287 var zeroTime time.Time 288 payload := bytes.NewBufferString(goprog).Bytes() 289 inputbuf := bytes.NewBuffer(nil) 290 gw := gzip.NewWriter(inputbuf) 291 tw := tar.NewWriter(gw) 292 tw.WriteHeader(&tar.Header{ 293 Name: "src/helloworld/helloworld.go", 294 Size: int64(len(payload)), 295 Mode: 0600, 296 ModTime: zeroTime, 297 AccessTime: zeroTime, 298 ChangeTime: zeroTime, 299 }) 300 tw.Write(payload) 301 tw.Close() 302 gw.Close() 303 return inputbuf.Bytes() 304 } 305 306 func createTempFile(t *testing.T) string { 307 tmpfile, err := ioutil.TempFile("", "test") 308 if err != nil { 309 t.Fatal(err) 310 return "" 311 } 312 if err := tmpfile.Close(); err != nil { 313 t.Fatal(err) 314 } 315 return tmpfile.Name() 316 } 317 318 func TestDockerPull(t *testing.T) { 319 codepackage, output := io.Pipe() 320 go func() { 321 tw := tar.NewWriter(output) 322 323 tw.Close() 324 output.Close() 325 }() 326 327 binpackage := bytes.NewBuffer(nil) 328 329 // Perform a nop operation within a fixed target. We choose 1.0.0-alpha2 because we know it's 330 // published and available. Ideally we could choose something that we know is both multi-arch 331 // and ok to delete prior to executing DockerBuild. This would ensure that we exercise the 332 // image pull logic. However, no suitable target exists that meets all the criteria. Therefore 333 // we settle on using a known released image. We don't know if the image is already 334 // downloaded per se, and we don't want to explicitly delete this particular image first since 335 // it could be in use legitimately elsewhere. Instead, we just know that this should always 336 // work and call that "close enough". 337 // 338 // Future considerations: publish a known dummy image that is multi-arch and free to randomly 339 // delete, and use that here instead. 340 err := DockerBuild(DockerBuildOptions{ 341 Image: cutil.ParseDockerfileTemplate("hyperledger/fabric-ccenv:$(ARCH)-1.0.0-alpha2"), 342 Cmd: "/bin/true", 343 InputStream: codepackage, 344 OutputStream: binpackage, 345 }) 346 if err != nil { 347 t.Errorf("Error during build: %s", err) 348 } 349 } 350 351 func TestMain(m *testing.M) { 352 viper.SetConfigName("core") 353 viper.SetEnvPrefix("CORE") 354 config.AddDevConfigPath(nil) 355 viper.SetEnvKeyReplacer(strings.NewReplacer(".", "_")) 356 viper.AutomaticEnv() 357 if err := viper.ReadInConfig(); err != nil { 358 fmt.Printf("could not read config %s\n", err) 359 os.Exit(-1) 360 } 361 os.Exit(m.Run()) 362 }