github.com/go-chef/chef@v0.30.1/cookbook_download_test.go (about) 1 // 2 // Author:: Salim Afiune <afiune@chef.io> 3 // 4 5 package chef 6 7 import ( 8 "fmt" 9 "net/http" 10 "os" 11 "path" 12 "testing" 13 14 "github.com/stretchr/testify/assert" 15 ) 16 17 const emptyCookbookResponseFile = "test/empty_cookbook.json" 18 19 func TestCookbooksDownloadThatDoesNotExist(t *testing.T) { 20 setup() 21 defer teardown() 22 23 mux.HandleFunc("/cookbooks/foo/2.1.0", func(w http.ResponseWriter, r *http.Request) { 24 http.Error(w, "Not Found", 404) 25 }) 26 27 err := client.Cookbooks.Download("foo", "2.1.0") 28 if assert.NotNil(t, err) { 29 assert.Contains(t, err.Error(), "404") 30 } 31 } 32 33 func TestCookbooksDownloadCorrectsLatestVersion(t *testing.T) { 34 setup() 35 defer teardown() 36 37 mux.HandleFunc("/cookbooks/foo/_latest", func(w http.ResponseWriter, r *http.Request) { 38 http.Error(w, "Not Found", 404) 39 }) 40 41 err := client.Cookbooks.Download("foo", "") 42 if assert.NotNil(t, err) { 43 assert.Contains(t, err.Error(), "404") 44 } 45 46 err = client.Cookbooks.Download("foo", "latest") 47 if assert.NotNil(t, err) { 48 assert.Contains(t, err.Error(), "404") 49 } 50 51 err = client.Cookbooks.Download("foo", "_latest") 52 if assert.NotNil(t, err) { 53 assert.Contains(t, err.Error(), "404") 54 } 55 } 56 57 func TestCookbooksDownloadEmptyWithVersion(t *testing.T) { 58 setup() 59 defer teardown() 60 61 cbookResp, err := os.ReadFile(emptyCookbookResponseFile) 62 if err != nil { 63 t.Error(err) 64 } 65 66 mux.HandleFunc("/cookbooks/foo/0.2.0", func(w http.ResponseWriter, r *http.Request) { 67 fmt.Fprintf(w, string(cbookResp)) 68 }) 69 70 err = client.Cookbooks.Download("foo", "0.2.0") 71 assert.Nil(t, err) 72 } 73 74 func cookbookData() string { 75 return ` 76 77 { 78 "version": "0.2.1", 79 "name": "foo-0.2.1", 80 "cookbook_name": "foo", 81 "frozen?": false, 82 "chef_type": "cookbook_version", 83 "json_class": "Chef::CookbookVersion", 84 "attributes": [], 85 "definitions": [], 86 "files": [], 87 "libraries": [], 88 "providers": [], 89 "recipes": [ 90 { 91 "name": "default.rb", 92 "path": "recipes/default.rb", 93 "checksum": "8e751ed8663cb9b97499956b6a20b0de", 94 "specificity": "default", 95 "url": "` + server.URL + `/bookshelf/foo/default_rb" 96 } 97 ], 98 "resources": [], 99 "root_files": [ 100 { 101 "name": "metadata.rb", 102 "path": "metadata.rb", 103 "checksum": "6607f3131919e82dc4ba4c026fcfee9f", 104 "specificity": "default", 105 "url": "` + server.URL + `/bookshelf/foo/metadata_rb" 106 } 107 ], 108 "templates": [], 109 "metadata": {}, 110 "access": {} 111 } ` 112 } 113 114 func TestCookbooksDownloadTo(t *testing.T) { 115 setup() 116 defer teardown() 117 118 mockedCookbookResponseFile := cookbookData() 119 tempDir, err := os.MkdirTemp("", "foo-cookbook") 120 if err != nil { 121 t.Error(err) 122 } 123 defer os.RemoveAll(tempDir) // clean up 124 125 mux.HandleFunc("/cookbooks/foo/0.2.1", func(w http.ResponseWriter, r *http.Request) { 126 fmt.Fprintf(w, string(mockedCookbookResponseFile)) 127 }) 128 mux.HandleFunc("/bookshelf/foo/metadata_rb", func(w http.ResponseWriter, r *http.Request) { 129 fmt.Fprintf(w, "name 'foo'") 130 }) 131 mux.HandleFunc("/bookshelf/foo/default_rb", func(w http.ResponseWriter, r *http.Request) { 132 fmt.Fprintf(w, "log 'this is a resource'") 133 }) 134 135 err = client.Cookbooks.DownloadTo("foo", "0.2.1", tempDir) 136 assert.Nil(t, err) 137 138 var ( 139 cookbookPath = path.Join(tempDir, "foo-0.2.1") 140 metadataPath = path.Join(cookbookPath, "metadata.rb") 141 recipesPath = path.Join(cookbookPath, "recipes") 142 defaultPath = path.Join(recipesPath, "default.rb") 143 ) 144 assert.DirExistsf(t, cookbookPath, "the cookbook directory should exist") 145 assert.DirExistsf(t, recipesPath, "the recipes directory should exist") 146 if assert.FileExistsf(t, metadataPath, "a metadata.rb file should exist") { 147 metadataBytes, err := os.ReadFile(metadataPath) 148 assert.Nil(t, err) 149 assert.Equal(t, "name 'foo'", string(metadataBytes)) 150 } 151 if assert.FileExistsf(t, defaultPath, "the default.rb recipes should exist") { 152 recipeBytes, err := os.ReadFile(defaultPath) 153 assert.Nil(t, err) 154 assert.Equal(t, "log 'this is a resource'", string(recipeBytes)) 155 } 156 157 } 158 159 func TestCookbooksDownloadTo_caching(t *testing.T) { 160 setup() 161 defer teardown() 162 163 mockedCookbookResponseFile := cookbookData() 164 tempDir, err := os.MkdirTemp("", "foo-cookbook") 165 if err != nil { 166 t.Error(err) 167 } 168 defer os.RemoveAll(tempDir) // clean up 169 170 mux.HandleFunc("/cookbooks/foo/0.2.1", func(w http.ResponseWriter, r *http.Request) { 171 fmt.Fprintf(w, string(mockedCookbookResponseFile)) 172 }) 173 mux.HandleFunc("/bookshelf/foo/metadata_rb", func(w http.ResponseWriter, r *http.Request) { 174 fmt.Fprintf(w, "name 'foo'") 175 }) 176 mux.HandleFunc("/bookshelf/foo/default_rb", func(w http.ResponseWriter, r *http.Request) { 177 fmt.Fprintf(w, "log 'this is a resource'") 178 }) 179 180 err = client.Cookbooks.DownloadTo("foo", "0.2.1", tempDir) 181 assert.Nil(t, err) 182 183 var ( 184 cookbookPath = path.Join(tempDir, "foo-0.2.1") 185 metadataPath = path.Join(cookbookPath, "metadata.rb") 186 recipesPath = path.Join(cookbookPath, "recipes") 187 defaultPath = path.Join(recipesPath, "default.rb") 188 ) 189 assert.DirExistsf(t, cookbookPath, "the cookbook directory should exist") 190 assert.DirExistsf(t, recipesPath, "the recipes directory should exist") 191 if assert.FileExistsf(t, metadataPath, "a metadata.rb file should exist") { 192 metadataBytes, err := os.ReadFile(metadataPath) 193 assert.Nil(t, err) 194 assert.Equal(t, "name 'foo'", string(metadataBytes)) 195 } 196 if assert.FileExistsf(t, defaultPath, "the default.rb recipes should exist") { 197 recipeBytes, err := os.ReadFile(defaultPath) 198 assert.Nil(t, err) 199 assert.Equal(t, "log 'this is a resource'", string(recipeBytes)) 200 } 201 202 // Capture the timestamps to ensure that on-redownload of unchanged cookook, 203 // they show no modification (using this as a proxy to determine whether 204 // the file has been re-downloaded). 205 defaultPathInfo, err := os.Stat(defaultPath) 206 assert.Nil(t, err) 207 208 metaDataInfo, err := os.Stat(metadataPath) 209 assert.Nil(t, err) 210 211 err = client.Cookbooks.DownloadTo("foo", "0.2.1", tempDir) 212 assert.Nil(t, err) 213 214 defaultPathNewInfo, err := os.Stat(defaultPath) 215 assert.Nil(t, err) 216 217 metaDataNewInfo, err := os.Stat(metadataPath) 218 assert.Nil(t, err) 219 220 err = client.Cookbooks.DownloadTo("foo", "0.2.1", tempDir) 221 assert.Nil(t, err) 222 223 // If the file was not re-downloaded, we would expect the timestamp 224 // to remain unchanged. 225 assert.Equal(t, defaultPathInfo.ModTime(), defaultPathNewInfo.ModTime()) 226 assert.Equal(t, metaDataInfo.ModTime(), metaDataNewInfo.ModTime()) 227 228 err = os.Truncate(metadataPath, 1) 229 assert.Nil(t, err) 230 231 err = os.Chtimes(metadataPath, metaDataInfo.ModTime(), metaDataInfo.ModTime()) 232 assert.Nil(t, err) 233 234 err = client.Cookbooks.DownloadTo("foo", "0.2.1", tempDir) 235 assert.Nil(t, err) 236 237 metaDataNewInfo, err = os.Stat(metadataPath) 238 assert.Nil(t, err) 239 240 assert.NotEqual(t, metaDataInfo.ModTime(), metaDataNewInfo.ModTime()) 241 242 // Finally, make sure the modified-and-replaced metadata.rb is matching 243 // what we expect after we have redownloaded the cookbook: 244 if assert.FileExistsf(t, metadataPath, "a metadata.rb file should exist") { 245 metadataBytes, err := os.ReadFile(metadataPath) 246 assert.Nil(t, err) 247 assert.Equal(t, "name 'foo'", string(metadataBytes)) 248 } 249 } 250 251 func TestVerifyMD5Checksum(t *testing.T) { 252 tempDir, err := os.MkdirTemp("", "md5-test") 253 if err != nil { 254 t.Error(err) 255 } 256 defer os.RemoveAll(tempDir) // clean up 257 258 var ( 259 // if someone changes the test data, 260 // you have to also update the below md5 sum 261 testData = []byte("hello\nchef\n") 262 filePath = path.Join(tempDir, "dat") 263 ) 264 err = os.WriteFile(filePath, testData, 0644) 265 assert.Nil(t, err) 266 assert.True(t, verifyMD5Checksum(filePath, "70bda176ac4db06f1f66f96ae0693be1")) 267 }