1package godo 2 3import ( 4 "encoding/json" 5 "fmt" 6 "io/ioutil" 7 "net/http" 8 "reflect" 9 "strings" 10 "testing" 11) 12 13func TestProjects_List(t *testing.T) { 14 setup() 15 defer teardown() 16 17 expectedProjects := []Project{ 18 { 19 ID: "project-1", 20 Name: "project-1", 21 }, 22 { 23 ID: "project-2", 24 Name: "project-2", 25 }, 26 } 27 28 mux.HandleFunc("/v2/projects", func(w http.ResponseWriter, r *http.Request) { 29 testMethod(t, r, http.MethodGet) 30 resp, _ := json.Marshal(expectedProjects) 31 fmt.Fprint(w, fmt.Sprintf(`{"projects":%s, "meta": {"total": 2}}`, string(resp))) 32 }) 33 34 projects, resp, err := client.Projects.List(ctx, nil) 35 if err != nil { 36 t.Errorf("Projects.List returned error: %v", err) 37 } 38 39 if !reflect.DeepEqual(projects, expectedProjects) { 40 t.Errorf("Projects.List returned projects %+v, expected %+v", projects, expectedProjects) 41 } 42 43 expectedMeta := &Meta{Total: 2} 44 if !reflect.DeepEqual(resp.Meta, expectedMeta) { 45 t.Errorf("Projects.List returned meta %+v, expected %+v", resp.Meta, expectedMeta) 46 } 47} 48 49func TestProjects_ListWithMultiplePages(t *testing.T) { 50 setup() 51 defer teardown() 52 53 mockResp := ` 54 { 55 "projects": [ 56 { 57 "uuid": "project-1", 58 "name": "project-1" 59 }, 60 { 61 "uuid": "project-2", 62 "name": "project-2" 63 } 64 ], 65 "links": { 66 "pages": { 67 "next": "http://example.com/v2/projects?page=2" 68 } 69 } 70 }` 71 72 mux.HandleFunc("/v2/projects", func(w http.ResponseWriter, r *http.Request) { 73 testMethod(t, r, http.MethodGet) 74 fmt.Fprint(w, mockResp) 75 }) 76 77 _, resp, err := client.Projects.List(ctx, nil) 78 if err != nil { 79 t.Errorf("Projects.List returned error: %v", err) 80 } 81 82 checkCurrentPage(t, resp, 1) 83} 84 85func TestProjects_ListWithPageNumber(t *testing.T) { 86 setup() 87 defer teardown() 88 89 mockResp := ` 90 { 91 "projects": [ 92 { 93 "uuid": "project-1", 94 "name": "project-1" 95 }, 96 { 97 "uuid": "project-2", 98 "name": "project-2" 99 } 100 ], 101 "links": { 102 "pages": { 103 "next": "http://example.com/v2/projects?page=3", 104 "prev": "http://example.com/v2/projects?page=1", 105 "last": "http://example.com/v2/projects?page=3", 106 "first": "http://example.com/v2/projects?page=1" 107 } 108 } 109 }` 110 111 mux.HandleFunc("/v2/projects", func(w http.ResponseWriter, r *http.Request) { 112 testMethod(t, r, http.MethodGet) 113 fmt.Fprint(w, mockResp) 114 }) 115 116 _, resp, err := client.Projects.List(ctx, &ListOptions{Page: 2}) 117 if err != nil { 118 t.Errorf("Projects.List returned error: %v", err) 119 } 120 121 checkCurrentPage(t, resp, 2) 122} 123 124func TestProjects_GetDefault(t *testing.T) { 125 setup() 126 defer teardown() 127 128 project := &Project{ 129 ID: "project-1", 130 Name: "project-1", 131 } 132 133 mux.HandleFunc("/v2/projects/default", func(w http.ResponseWriter, r *http.Request) { 134 testMethod(t, r, http.MethodGet) 135 resp, _ := json.Marshal(project) 136 fmt.Fprint(w, fmt.Sprintf(`{"project":%s}`, string(resp))) 137 }) 138 139 resp, _, err := client.Projects.GetDefault(ctx) 140 if err != nil { 141 t.Errorf("Projects.GetDefault returned error: %v", err) 142 } 143 144 if !reflect.DeepEqual(resp, project) { 145 t.Errorf("Projects.GetDefault returned %+v, expected %+v", resp, project) 146 } 147} 148 149func TestProjects_GetWithUUID(t *testing.T) { 150 setup() 151 defer teardown() 152 153 project := &Project{ 154 ID: "project-1", 155 Name: "project-1", 156 } 157 158 mux.HandleFunc("/v2/projects/project-1", func(w http.ResponseWriter, r *http.Request) { 159 testMethod(t, r, http.MethodGet) 160 resp, _ := json.Marshal(project) 161 fmt.Fprint(w, fmt.Sprintf(`{"project":%s}`, string(resp))) 162 }) 163 164 resp, _, err := client.Projects.Get(ctx, "project-1") 165 if err != nil { 166 t.Errorf("Projects.Get returned error: %v", err) 167 } 168 169 if !reflect.DeepEqual(resp, project) { 170 t.Errorf("Projects.Get returned %+v, expected %+v", resp, project) 171 } 172} 173 174func TestProjects_Create(t *testing.T) { 175 setup() 176 defer teardown() 177 178 createRequest := &CreateProjectRequest{ 179 Name: "my project", 180 Description: "for my stuff", 181 Purpose: "Just trying out DigitalOcean", 182 Environment: "Production", 183 } 184 185 createResp := &Project{ 186 ID: "project-id", 187 Name: createRequest.Name, 188 Description: createRequest.Description, 189 Purpose: createRequest.Purpose, 190 Environment: createRequest.Environment, 191 } 192 193 mux.HandleFunc("/v2/projects", func(w http.ResponseWriter, r *http.Request) { 194 v := new(CreateProjectRequest) 195 err := json.NewDecoder(r.Body).Decode(v) 196 if err != nil { 197 t.Fatalf("decode json: %v", err) 198 } 199 200 testMethod(t, r, http.MethodPost) 201 if !reflect.DeepEqual(v, createRequest) { 202 t.Errorf("Request body = %+v, expected %+v", v, createRequest) 203 } 204 205 resp, _ := json.Marshal(createResp) 206 fmt.Fprintf(w, fmt.Sprintf(`{"project":%s}`, string(resp))) 207 }) 208 209 project, _, err := client.Projects.Create(ctx, createRequest) 210 if err != nil { 211 t.Errorf("Projects.Create returned error: %v", err) 212 } 213 214 if !reflect.DeepEqual(project, createResp) { 215 t.Errorf("Projects.Create returned %+v, expected %+v", project, createResp) 216 } 217} 218 219func TestProjects_UpdateWithOneAttribute(t *testing.T) { 220 setup() 221 defer teardown() 222 223 updateRequest := &UpdateProjectRequest{ 224 Name: "my-great-project", 225 } 226 updateResp := &Project{ 227 ID: "project-id", 228 Name: updateRequest.Name.(string), 229 Description: "some-other-description", 230 Purpose: "some-other-purpose", 231 Environment: "some-other-env", 232 IsDefault: false, 233 } 234 235 mux.HandleFunc("/v2/projects/project-1", func(w http.ResponseWriter, r *http.Request) { 236 reqBytes, respErr := ioutil.ReadAll(r.Body) 237 if respErr != nil { 238 t.Error("projects mock didn't work") 239 } 240 241 req := strings.TrimSuffix(string(reqBytes), "\n") 242 expectedReq := `{"name":"my-great-project","description":null,"purpose":null,"environment":null,"is_default":null}` 243 if req != expectedReq { 244 t.Errorf("projects req didn't match up:\n expected %+v\n got %+v\n", expectedReq, req) 245 } 246 247 resp, _ := json.Marshal(updateResp) 248 fmt.Fprintf(w, fmt.Sprintf(`{"project":%s}`, string(resp))) 249 }) 250 251 project, _, err := client.Projects.Update(ctx, "project-1", updateRequest) 252 if err != nil { 253 t.Errorf("Projects.Update returned error: %v", err) 254 } 255 if !reflect.DeepEqual(project, updateResp) { 256 t.Errorf("Projects.Update returned %+v, expected %+v", project, updateResp) 257 } 258} 259 260func TestProjects_UpdateWithAllAttributes(t *testing.T) { 261 setup() 262 defer teardown() 263 264 updateRequest := &UpdateProjectRequest{ 265 Name: "my-great-project", 266 Description: "some-description", 267 Purpose: "some-purpose", 268 Environment: "some-env", 269 IsDefault: true, 270 } 271 updateResp := &Project{ 272 ID: "project-id", 273 Name: updateRequest.Name.(string), 274 Description: updateRequest.Description.(string), 275 Purpose: updateRequest.Purpose.(string), 276 Environment: updateRequest.Environment.(string), 277 IsDefault: updateRequest.IsDefault.(bool), 278 } 279 280 mux.HandleFunc("/v2/projects/project-1", func(w http.ResponseWriter, r *http.Request) { 281 reqBytes, respErr := ioutil.ReadAll(r.Body) 282 if respErr != nil { 283 t.Error("projects mock didn't work") 284 } 285 286 req := strings.TrimSuffix(string(reqBytes), "\n") 287 expectedReq := `{"name":"my-great-project","description":"some-description","purpose":"some-purpose","environment":"some-env","is_default":true}` 288 if req != expectedReq { 289 t.Errorf("projects req didn't match up:\n expected %+v\n got %+v\n", expectedReq, req) 290 } 291 292 resp, _ := json.Marshal(updateResp) 293 fmt.Fprintf(w, fmt.Sprintf(`{"project":%s}`, string(resp))) 294 }) 295 296 project, _, err := client.Projects.Update(ctx, "project-1", updateRequest) 297 if err != nil { 298 t.Errorf("Projects.Update returned error: %v", err) 299 } 300 if !reflect.DeepEqual(project, updateResp) { 301 t.Errorf("Projects.Update returned %+v, expected %+v", project, updateResp) 302 } 303} 304 305func TestProjects_Destroy(t *testing.T) { 306 setup() 307 defer teardown() 308 309 mux.HandleFunc("/v2/projects/project-1", func(w http.ResponseWriter, r *http.Request) { 310 testMethod(t, r, http.MethodDelete) 311 }) 312 313 _, err := client.Projects.Delete(ctx, "project-1") 314 if err != nil { 315 t.Errorf("Projects.Delete returned error: %v", err) 316 } 317} 318 319func TestProjects_ListResources(t *testing.T) { 320 setup() 321 defer teardown() 322 323 expectedResources := []ProjectResource{ 324 { 325 URN: "do:droplet:1", 326 AssignedAt: "2018-09-27 00:00:00", 327 Links: &ProjectResourceLinks{ 328 Self: "http://example.com/v2/droplets/1", 329 }, 330 }, 331 { 332 URN: "do:floatingip:1.2.3.4", 333 AssignedAt: "2018-09-27 00:00:00", 334 Links: &ProjectResourceLinks{ 335 Self: "http://example.com/v2/floating_ips/1.2.3.4", 336 }, 337 }, 338 } 339 340 mux.HandleFunc("/v2/projects/project-1/resources", func(w http.ResponseWriter, r *http.Request) { 341 testMethod(t, r, http.MethodGet) 342 resp, _ := json.Marshal(expectedResources) 343 fmt.Fprint(w, fmt.Sprintf(`{"resources":%s, "meta": {"total": 2}}`, string(resp))) 344 }) 345 346 resources, resp, err := client.Projects.ListResources(ctx, "project-1", nil) 347 if err != nil { 348 t.Errorf("Projects.List returned error: %v", err) 349 } 350 351 if !reflect.DeepEqual(resources, expectedResources) { 352 t.Errorf("Projects.ListResources returned resources %+v, expected %+v", resources, expectedResources) 353 } 354 355 expectedMeta := &Meta{Total: 2} 356 if !reflect.DeepEqual(resp.Meta, expectedMeta) { 357 t.Errorf("Projects.ListResources returned meta %+v, expected %+v", resp.Meta, expectedMeta) 358 } 359} 360 361func TestProjects_ListResourcesWithMultiplePages(t *testing.T) { 362 setup() 363 defer teardown() 364 365 mockResp := ` 366 { 367 "resources": [ 368 { 369 "urn": "do:droplet:1", 370 "assigned_at": "2018-09-27 00:00:00", 371 "links": { 372 "self": "http://example.com/v2/droplets/1" 373 } 374 }, 375 { 376 "urn": "do:floatingip:1.2.3.4", 377 "assigned_at": "2018-09-27 00:00:00", 378 "links": { 379 "self": "http://example.com/v2/floating_ips/1.2.3.4" 380 } 381 } 382 ], 383 "links": { 384 "pages": { 385 "next": "http://example.com/v2/projects/project-1/resources?page=2" 386 } 387 } 388 }` 389 390 mux.HandleFunc("/v2/projects/project-1/resources", func(w http.ResponseWriter, r *http.Request) { 391 testMethod(t, r, http.MethodGet) 392 fmt.Fprint(w, mockResp) 393 }) 394 395 _, resp, err := client.Projects.ListResources(ctx, "project-1", nil) 396 if err != nil { 397 t.Errorf("Projects.ListResources returned error: %v", err) 398 } 399 400 checkCurrentPage(t, resp, 1) 401} 402 403func TestProjects_ListResourcesWithPageNumber(t *testing.T) { 404 setup() 405 defer teardown() 406 407 mockResp := ` 408 { 409 "resources": [ 410 { 411 "urn": "do:droplet:1", 412 "assigned_at": "2018-09-27 00:00:00", 413 "links": { 414 "self": "http://example.com/v2/droplets/1" 415 } 416 }, 417 { 418 "urn": "do:floatingip:1.2.3.4", 419 "assigned_at": "2018-09-27 00:00:00", 420 "links": { 421 "self": "http://example.com/v2/floating_ips/1.2.3.4" 422 } 423 } 424 ], 425 "links": { 426 "pages": { 427 "next": "http://example.com/v2/projects/project-1/resources?page=3", 428 "prev": "http://example.com/v2/projects/project-1/resources?page=1", 429 "last": "http://example.com/v2/projects/project-1/resources?page=3", 430 "first": "http://example.com/v2/projects/project-1/resources?page=1" 431 } 432 } 433 }` 434 435 mux.HandleFunc("/v2/projects/project-1/resources", func(w http.ResponseWriter, r *http.Request) { 436 testMethod(t, r, http.MethodGet) 437 fmt.Fprint(w, mockResp) 438 }) 439 440 _, resp, err := client.Projects.ListResources(ctx, "project-1", &ListOptions{Page: 2}) 441 if err != nil { 442 t.Errorf("Projects.ListResources returned error: %v", err) 443 } 444 445 checkCurrentPage(t, resp, 2) 446} 447 448func TestProjects_AssignFleetResourcesWithTypes(t *testing.T) { 449 setup() 450 defer teardown() 451 452 assignableResources := []interface{}{ 453 &Droplet{ID: 1234}, 454 &FloatingIP{IP: "1.2.3.4"}, 455 } 456 457 mockResp := ` 458 { 459 "resources": [ 460 { 461 "urn": "do:droplet:1234", 462 "assigned_at": "2018-09-27 00:00:00", 463 "links": { 464 "self": "http://example.com/v2/droplets/1" 465 } 466 }, 467 { 468 "urn": "do:floatingip:1.2.3.4", 469 "assigned_at": "2018-09-27 00:00:00", 470 "links": { 471 "self": "http://example.com/v2/floating_ips/1.2.3.4" 472 } 473 } 474 ] 475 }` 476 477 mux.HandleFunc("/v2/projects/project-1/resources", func(w http.ResponseWriter, r *http.Request) { 478 testMethod(t, r, http.MethodPost) 479 reqBytes, respErr := ioutil.ReadAll(r.Body) 480 if respErr != nil { 481 t.Error("projects mock didn't work") 482 } 483 484 req := strings.TrimSuffix(string(reqBytes), "\n") 485 expectedReq := `{"resources":["do:droplet:1234","do:floatingip:1.2.3.4"]}` 486 if req != expectedReq { 487 t.Errorf("projects assign req didn't match up:\n expected %+v\n got %+v\n", expectedReq, req) 488 } 489 490 fmt.Fprint(w, mockResp) 491 }) 492 493 _, _, err := client.Projects.AssignResources(ctx, "project-1", assignableResources...) 494 if err != nil { 495 t.Errorf("Projects.AssignResources returned error: %v", err) 496 } 497} 498 499func TestProjects_AssignFleetResourcesWithStrings(t *testing.T) { 500 setup() 501 defer teardown() 502 503 assignableResources := []interface{}{ 504 "do:droplet:1234", 505 "do:floatingip:1.2.3.4", 506 } 507 508 mockResp := ` 509 { 510 "resources": [ 511 { 512 "urn": "do:droplet:1234", 513 "assigned_at": "2018-09-27 00:00:00", 514 "links": { 515 "self": "http://example.com/v2/droplets/1" 516 } 517 }, 518 { 519 "urn": "do:floatingip:1.2.3.4", 520 "assigned_at": "2018-09-27 00:00:00", 521 "links": { 522 "self": "http://example.com/v2/floating_ips/1.2.3.4" 523 } 524 } 525 ] 526 }` 527 528 mux.HandleFunc("/v2/projects/project-1/resources", func(w http.ResponseWriter, r *http.Request) { 529 testMethod(t, r, http.MethodPost) 530 reqBytes, respErr := ioutil.ReadAll(r.Body) 531 if respErr != nil { 532 t.Error("projects mock didn't work") 533 } 534 535 req := strings.TrimSuffix(string(reqBytes), "\n") 536 expectedReq := `{"resources":["do:droplet:1234","do:floatingip:1.2.3.4"]}` 537 if req != expectedReq { 538 t.Errorf("projects assign req didn't match up:\n expected %+v\n got %+v\n", expectedReq, req) 539 } 540 541 fmt.Fprint(w, mockResp) 542 }) 543 544 _, _, err := client.Projects.AssignResources(ctx, "project-1", assignableResources...) 545 if err != nil { 546 t.Errorf("Projects.AssignResources returned error: %v", err) 547 } 548} 549 550func TestProjects_AssignFleetResourcesWithStringsAndTypes(t *testing.T) { 551 setup() 552 defer teardown() 553 554 assignableResources := []interface{}{ 555 "do:droplet:1234", 556 &FloatingIP{IP: "1.2.3.4"}, 557 } 558 559 mockResp := ` 560 { 561 "resources": [ 562 { 563 "urn": "do:droplet:1234", 564 "assigned_at": "2018-09-27 00:00:00", 565 "links": { 566 "self": "http://example.com/v2/droplets/1" 567 } 568 }, 569 { 570 "urn": "do:floatingip:1.2.3.4", 571 "assigned_at": "2018-09-27 00:00:00", 572 "links": { 573 "self": "http://example.com/v2/floating_ips/1.2.3.4" 574 } 575 } 576 ] 577 }` 578 579 mux.HandleFunc("/v2/projects/project-1/resources", func(w http.ResponseWriter, r *http.Request) { 580 testMethod(t, r, http.MethodPost) 581 reqBytes, respErr := ioutil.ReadAll(r.Body) 582 if respErr != nil { 583 t.Error("projects mock didn't work") 584 } 585 586 req := strings.TrimSuffix(string(reqBytes), "\n") 587 expectedReq := `{"resources":["do:droplet:1234","do:floatingip:1.2.3.4"]}` 588 if req != expectedReq { 589 t.Errorf("projects assign req didn't match up:\n expected %+v\n got %+v\n", expectedReq, req) 590 } 591 592 fmt.Fprint(w, mockResp) 593 }) 594 595 _, _, err := client.Projects.AssignResources(ctx, "project-1", assignableResources...) 596 if err != nil { 597 t.Errorf("Projects.AssignResources returned error: %v", err) 598 } 599} 600 601func TestProjects_AssignFleetResourcesWithTypeWithoutURNReturnsError(t *testing.T) { 602 setup() 603 defer teardown() 604 605 type fakeType struct{} 606 607 assignableResources := []interface{}{ 608 fakeType{}, 609 } 610 611 _, _, err := client.Projects.AssignResources(ctx, "project-1", assignableResources...) 612 if err == nil { 613 t.Errorf("expected Projects.AssignResources to error, but it did not") 614 } 615 616 if err.Error() != "godo.fakeType must either be a string or have a valid URN method" { 617 t.Errorf("Projects.AssignResources returned the wrong error: %v", err) 618 } 619} 620