github.com/minio/console@v1.4.1/replication/admin_api_int_replication_test.go (about) 1 // This file is part of MinIO Console Server 2 // Copyright (c) 2022 MinIO, Inc. 3 // 4 // This program is free software: you can redistribute it and/or modify 5 // it under the terms of the GNU Affero General Public License as published by 6 // the Free Software Foundation, either version 3 of the License, or 7 // (at your option) any later version. 8 // 9 // This program is distributed in the hope that it will be useful, 10 // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 // GNU Affero General Public License for more details. 13 // 14 // You should have received a copy of the GNU Affero General Public License 15 // along with this program. If not, see <http://www.gnu.org/licenses/>. 16 17 // These tests are for AdminAPI Tag based on swagger-console.yml 18 19 package replication 20 21 import ( 22 "bytes" 23 "encoding/json" 24 "fmt" 25 "io" 26 "log" 27 "net/http" 28 "testing" 29 "time" 30 31 "github.com/minio/console/models" 32 "github.com/stretchr/testify/assert" 33 ) 34 35 const apiURL = "http://localhost:9090/api/v1/admin/site-replication" 36 37 func makeExecuteReq(method string, body io.Reader) (*http.Response, error) { 38 client := &http.Client{ 39 Timeout: 10 * time.Second, 40 } 41 42 request, err := http.NewRequest( 43 method, 44 apiURL, 45 body, 46 ) 47 if err != nil { 48 return nil, err 49 } 50 request.Header.Add("Cookie", fmt.Sprintf("token=%s", token)) 51 request.Header.Add("Content-Type", "application/json") 52 response, err := client.Do(request) 53 if err != nil { 54 return nil, err 55 } 56 57 return response, nil 58 } 59 60 func AddSiteReplicationInfo(sites []map[string]interface{}) (int, error) { 61 requestDataJSON, _ := json.Marshal(sites) 62 requestDataBody := bytes.NewReader(requestDataJSON) 63 response, err := makeExecuteReq("POST", requestDataBody) 64 if err != nil { 65 log.Println(response) 66 return -1, err 67 } 68 if response != nil { 69 return response.StatusCode, nil 70 } 71 72 return -1, nil 73 } 74 75 func EditSiteReplicationInfo(editedSite map[string]interface{}) (int, error) { 76 requestDataJSON, _ := json.Marshal(editedSite) 77 requestDataBody := bytes.NewReader(requestDataJSON) 78 response, err := makeExecuteReq("PUT", requestDataBody) 79 if err != nil { 80 return -1, err 81 } 82 if response != nil { 83 return response.StatusCode, nil 84 } 85 86 return -1, nil 87 } 88 89 func DeleteSiteReplicationInfo(delReq map[string]interface{}) (int, error) { 90 requestDataJSON, _ := json.Marshal(delReq) 91 requestDataBody := bytes.NewReader(requestDataJSON) 92 response, err := makeExecuteReq("DELETE", requestDataBody) 93 if err != nil { 94 return -1, err 95 } 96 if response != nil { 97 return response.StatusCode, nil 98 } 99 100 return -1, nil 101 } 102 103 func TestGetSiteReplicationInfo(t *testing.T) { 104 assert := assert.New(t) 105 response, err := makeExecuteReq("GET", nil) 106 107 // defer response.Body.Close() 108 109 tgt := &models.SiteReplicationInfoResponse{} 110 json.NewDecoder(response.Body).Decode(tgt) 111 if err != nil { 112 log.Println(err) 113 return 114 } 115 if response != nil { 116 assert.Equal(200, response.StatusCode, "Status Code is incorrect") 117 } 118 } 119 120 func TestAddSiteReplicationInfo(t *testing.T) { 121 assert := assert.New(t) 122 fmt.Println("Add Site Replication") 123 124 sites := make([]map[string]interface{}, 2) 125 sites[0] = map[string]interface{}{ 126 "accessKey": "minioadmin", 127 "endpoint": "http://localhost:9000", 128 "secretKey": "minioadmin", 129 "name": "sitellhost9000", 130 } 131 sites[1] = map[string]interface{}{ 132 "accessKey": "minioadmin", 133 "endpoint": "http://minio1:9001", // Docker container . 134 // "endpoint": "http://localhost:9001", for local development 135 "secretKey": "minioadmin", 136 "name": "sitellhost9001", 137 } 138 139 errorSites1 := make([]map[string]interface{}, 2) 140 errorSites1[0] = map[string]interface{}{ 141 "accessKey": "minioadmin", 142 "endpoint": "http://localhost:9000", 143 "secretKey": "minioadmin", 144 "name": "sitellhost9000", 145 } 146 errorSites1[1] = map[string]interface{}{ 147 "accessKey": "minioadmin", 148 "endpoint": "http://non-existent:9001", 149 "secretKey": "minioadmin", 150 "name": "sitellhost9001", 151 } 152 153 errorSites2 := make([]map[string]interface{}, 2) 154 errorSites2[0] = map[string]interface{}{ 155 "accessKey": "minioadmin", 156 "endpoint": "http://localhost:9000", 157 "secretKey": "minioadmin", 158 "name": "sitellhost9000", 159 } 160 errorSites2[1] = map[string]interface{}{ 161 "accessKey": "minioadmin", 162 "endpoint": "http://localhost:9000", 163 "secretKey": "minioadmin", 164 "name": "sitellhost9000", 165 } 166 type args struct { 167 sites []map[string]interface{} 168 } 169 tests := []struct { 170 name string 171 args args 172 expStatusCode int 173 expectedError bool 174 }{ 175 // valid sites for replication config 176 { 177 name: "Add Two sites for Replication", 178 args: args{ 179 sites: sites, 180 }, 181 expStatusCode: 200, 182 expectedError: false, 183 }, 184 185 // context deadline error 186 { 187 name: "Add Invalid Site name for Replication", 188 args: args{ 189 sites: errorSites1, 190 }, 191 expStatusCode: 500, 192 expectedError: true, 193 }, 194 195 // site already added error 500 196 { 197 name: "Add same Site name for Replication", 198 args: args{ 199 sites: errorSites2, 200 }, 201 expStatusCode: 500, 202 expectedError: true, 203 }, 204 } 205 206 for _, tt := range tests { 207 t.Run(tt.name, func(_ *testing.T) { 208 actualStatusCode, err := AddSiteReplicationInfo(tt.args.sites) 209 fmt.Println("TestAddSiteReplicationInfo: ", actualStatusCode) 210 211 if tt.expStatusCode > 0 { 212 assert.Equal(tt.expStatusCode, actualStatusCode, "Expected Status Code is incorrect") 213 return 214 } 215 if tt.expectedError { 216 assert.NotEmpty(err) 217 return 218 } 219 }) 220 } 221 } 222 223 func TestEditSiteReplicationInfo(t *testing.T) { 224 assert := assert.New(t) 225 getResponse, err := makeExecuteReq("GET", nil) 226 if err != nil { 227 assert.Fail("Unable to get Site replication info") 228 } 229 getResObj := &models.SiteReplicationInfoResponse{} 230 json.NewDecoder(getResponse.Body).Decode(getResObj) 231 var secondDeploymentID string 232 fmt.Println("Edit Site Replication") 233 fmt.Println("Editing a valid site deployment id::", secondDeploymentID) 234 updatedSiteInfo := map[string]interface{}{ 235 "deploymentID": secondDeploymentID, 236 "endpoint": "http://minio2:9002", // replace it with docker name 237 // "endpoint": "http://localhost:9002", // local dev 238 "name": "sitellhost9002", 239 } 240 invalidUpdatedSiteInfo := map[string]interface{}{ 241 "deploymentID": secondDeploymentID, 242 "endpoint": "non-existenet:9002", 243 "name": "sitellhost9002", 244 } 245 invalidUpdatedSiteInfo1 := map[string]interface{}{} 246 tests := []struct { 247 name string 248 args map[string]interface{} 249 expStatusCode int 250 expectedError bool 251 }{ 252 { 253 name: "Edit an existing valid site", 254 args: updatedSiteInfo, 255 expStatusCode: -1, 256 expectedError: false, 257 }, 258 { 259 name: "Edit with an invalid site endpoint", 260 args: invalidUpdatedSiteInfo, 261 expStatusCode: 500, 262 expectedError: false, 263 }, 264 { 265 name: "Edit with an invalid empty site ", 266 args: invalidUpdatedSiteInfo1, 267 expStatusCode: 500, 268 expectedError: true, 269 }, 270 } 271 for _, tt := range tests { 272 t.Run(tt.name, func(_ *testing.T) { 273 actualStatusCode, err := EditSiteReplicationInfo(tt.args) 274 fmt.Println("TestEditSiteReplicationInfo: ", actualStatusCode) 275 if tt.expStatusCode > 0 { 276 assert.Equal(tt.expStatusCode, actualStatusCode, "Expected Status Code is incorrect") 277 return 278 } 279 if tt.expectedError { 280 assert.NotEmpty(err) 281 return 282 } 283 }) 284 } 285 } 286 287 func TestDeleteSiteReplicationInfo(t *testing.T) { 288 assert := assert.New(t) 289 delReq := map[string]interface{}{ 290 "all": true, 291 "sites": []string{ 292 "sitellhost9000", 293 "sitellhost9001", 294 "sitellhost9002", 295 }, 296 } 297 invalidDelReq := map[string]interface{}{ 298 "all": false, 299 "sites": []string{ 300 "sitellhost9000", 301 }, 302 } 303 invalidDelReq1 := map[string]interface{}{ 304 "all": true, 305 "sites": []string{}, 306 } 307 tests := []struct { 308 name string 309 args map[string]interface{} 310 expStatusCode int 311 expectedError bool 312 }{ 313 { 314 name: "Delete Valid Sites already added", 315 args: delReq, 316 expStatusCode: 204, 317 expectedError: false, 318 }, 319 { 320 name: "Delete Invalid Sites ", 321 args: invalidDelReq, 322 expStatusCode: 500, 323 expectedError: true, 324 }, 325 { 326 name: "Invalid delete request", 327 args: invalidDelReq1, 328 expStatusCode: 500, 329 expectedError: true, 330 }, 331 } 332 fmt.Println("Delete Site Replication") 333 for _, tt := range tests { 334 t.Run(tt.name, func(_ *testing.T) { 335 actualStatusCode, err := DeleteSiteReplicationInfo(tt.args) 336 fmt.Println("TestDeleteSiteReplicationInfo: ", actualStatusCode) 337 if tt.expStatusCode > 0 { 338 assert.Equal(tt.expStatusCode, actualStatusCode, "Expected Status Code is incorrect") 339 return 340 } 341 if tt.expectedError { 342 assert.NotEmpty(err) 343 return 344 } 345 }) 346 } 347 } 348 349 // Status API 350 351 func makeStatusExecuteReq(method string, url string) (*http.Response, error) { 352 client := &http.Client{ 353 Timeout: 10 * time.Second, 354 } 355 request, err := http.NewRequest( 356 method, 357 url, 358 nil, 359 ) 360 if err != nil { 361 return nil, err 362 } 363 request.Header.Add("Cookie", fmt.Sprintf("token=%s", token)) 364 request.Header.Add("Content-Type", "application/json") 365 response, err := client.Do(request) 366 if err != nil { 367 return nil, err 368 } 369 return response, nil 370 } 371 372 func TestGetSiteReplicationStatus(t *testing.T) { 373 assert := assert.New(t) 374 375 const ( 376 baseAPIURL = "http://localhost:9090/api/v1/admin/site-replication/status?" 377 lookupDefaultParams = "users=false&groups=false&buckets=false&policies=false&" 378 ) 379 380 tests := []struct { 381 name string 382 args string 383 expStatusCode int 384 expectedError bool 385 }{ 386 { 387 name: "Default replication status", 388 args: baseAPIURL + "users=true&groups=true&buckets=true&policies=true", 389 expStatusCode: 200, 390 expectedError: false, 391 }, 392 { 393 name: "Specific bucket lookup replication status", 394 args: baseAPIURL + lookupDefaultParams + "entityValue=test-bucket&entityType=bucket", 395 expStatusCode: 200, 396 expectedError: false, 397 }, 398 { 399 name: "Invalid specific bucket lookup replication status", 400 args: baseAPIURL + lookupDefaultParams + "entityValue=test-bucket-non-existent&entityType=bucket", 401 expStatusCode: 200, 402 expectedError: true, 403 }, 404 { 405 name: "Specific user lookup replication status", 406 args: baseAPIURL + lookupDefaultParams + "entityValue=test-user&entityType=user", 407 expStatusCode: 200, 408 expectedError: false, 409 }, 410 { 411 name: "Invalid user lookup replication status", 412 args: baseAPIURL + lookupDefaultParams + "entityValue=test-user-non-existent&entityType=user", 413 expStatusCode: 200, 414 expectedError: false, 415 }, 416 { 417 name: "Specific group lookup replication status", 418 args: baseAPIURL + lookupDefaultParams + "entityValue=test-group&entityType=group", 419 expStatusCode: 200, 420 expectedError: false, 421 }, 422 { 423 name: "Invalid group lookup replication status", 424 args: baseAPIURL + lookupDefaultParams + "entityValue=test-group-non-existent&entityType=group", 425 expStatusCode: 200, 426 expectedError: false, 427 }, 428 { 429 name: "Specific policy replication status", 430 args: baseAPIURL + lookupDefaultParams + "entityValue=consoleAdmin&entityType=policy", 431 expStatusCode: 200, 432 expectedError: false, 433 }, 434 { 435 name: "Invalid policy replication status", 436 args: baseAPIURL + lookupDefaultParams + "entityValue=test-policies&entityType=policy", 437 expStatusCode: 200, 438 expectedError: false, 439 }, 440 } 441 442 for ti, tt := range tests { 443 t.Run(tt.name, func(_ *testing.T) { 444 response, err := makeStatusExecuteReq("GET", tt.args) 445 tgt := &models.SiteReplicationStatusResponse{} 446 json.NewDecoder(response.Body).Decode(tgt) 447 log.Println(err, response) 448 if err != nil { 449 log.Println(err) 450 451 if tt.expectedError { 452 assert.NotEmpty(err) 453 } 454 return 455 } 456 if response != nil { 457 assert.Equal(tt.expStatusCode, response.StatusCode, "Status Code for", ti) 458 } 459 }) 460 } 461 }