package godo import ( "encoding/json" "fmt" "io/ioutil" "net/http" "reflect" "strings" "testing" ) func TestProjects_List(t *testing.T) { setup() defer teardown() expectedProjects := []Project{ { ID: "project-1", Name: "project-1", }, { ID: "project-2", Name: "project-2", }, } mux.HandleFunc("/v2/projects", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodGet) resp, _ := json.Marshal(expectedProjects) fmt.Fprint(w, fmt.Sprintf(`{"projects":%s, "meta": {"total": 2}}`, string(resp))) }) projects, resp, err := client.Projects.List(ctx, nil) if err != nil { t.Errorf("Projects.List returned error: %v", err) } if !reflect.DeepEqual(projects, expectedProjects) { t.Errorf("Projects.List returned projects %+v, expected %+v", projects, expectedProjects) } expectedMeta := &Meta{Total: 2} if !reflect.DeepEqual(resp.Meta, expectedMeta) { t.Errorf("Projects.List returned meta %+v, expected %+v", resp.Meta, expectedMeta) } } func TestProjects_ListWithMultiplePages(t *testing.T) { setup() defer teardown() mockResp := ` { "projects": [ { "uuid": "project-1", "name": "project-1" }, { "uuid": "project-2", "name": "project-2" } ], "links": { "pages": { "next": "http://example.com/v2/projects?page=2" } } }` mux.HandleFunc("/v2/projects", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodGet) fmt.Fprint(w, mockResp) }) _, resp, err := client.Projects.List(ctx, nil) if err != nil { t.Errorf("Projects.List returned error: %v", err) } checkCurrentPage(t, resp, 1) } func TestProjects_ListWithPageNumber(t *testing.T) { setup() defer teardown() mockResp := ` { "projects": [ { "uuid": "project-1", "name": "project-1" }, { "uuid": "project-2", "name": "project-2" } ], "links": { "pages": { "next": "http://example.com/v2/projects?page=3", "prev": "http://example.com/v2/projects?page=1", "last": "http://example.com/v2/projects?page=3", "first": "http://example.com/v2/projects?page=1" } } }` mux.HandleFunc("/v2/projects", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodGet) fmt.Fprint(w, mockResp) }) _, resp, err := client.Projects.List(ctx, &ListOptions{Page: 2}) if err != nil { t.Errorf("Projects.List returned error: %v", err) } checkCurrentPage(t, resp, 2) } func TestProjects_GetDefault(t *testing.T) { setup() defer teardown() project := &Project{ ID: "project-1", Name: "project-1", } mux.HandleFunc("/v2/projects/default", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodGet) resp, _ := json.Marshal(project) fmt.Fprint(w, fmt.Sprintf(`{"project":%s}`, string(resp))) }) resp, _, err := client.Projects.GetDefault(ctx) if err != nil { t.Errorf("Projects.GetDefault returned error: %v", err) } if !reflect.DeepEqual(resp, project) { t.Errorf("Projects.GetDefault returned %+v, expected %+v", resp, project) } } func TestProjects_GetWithUUID(t *testing.T) { setup() defer teardown() project := &Project{ ID: "project-1", Name: "project-1", } mux.HandleFunc("/v2/projects/project-1", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodGet) resp, _ := json.Marshal(project) fmt.Fprint(w, fmt.Sprintf(`{"project":%s}`, string(resp))) }) resp, _, err := client.Projects.Get(ctx, "project-1") if err != nil { t.Errorf("Projects.Get returned error: %v", err) } if !reflect.DeepEqual(resp, project) { t.Errorf("Projects.Get returned %+v, expected %+v", resp, project) } } func TestProjects_Create(t *testing.T) { setup() defer teardown() createRequest := &CreateProjectRequest{ Name: "my project", Description: "for my stuff", Purpose: "Just trying out DigitalOcean", Environment: "Production", } createResp := &Project{ ID: "project-id", Name: createRequest.Name, Description: createRequest.Description, Purpose: createRequest.Purpose, Environment: createRequest.Environment, } mux.HandleFunc("/v2/projects", func(w http.ResponseWriter, r *http.Request) { v := new(CreateProjectRequest) err := json.NewDecoder(r.Body).Decode(v) if err != nil { t.Fatalf("decode json: %v", err) } testMethod(t, r, http.MethodPost) if !reflect.DeepEqual(v, createRequest) { t.Errorf("Request body = %+v, expected %+v", v, createRequest) } resp, _ := json.Marshal(createResp) fmt.Fprintf(w, fmt.Sprintf(`{"project":%s}`, string(resp))) }) project, _, err := client.Projects.Create(ctx, createRequest) if err != nil { t.Errorf("Projects.Create returned error: %v", err) } if !reflect.DeepEqual(project, createResp) { t.Errorf("Projects.Create returned %+v, expected %+v", project, createResp) } } func TestProjects_UpdateWithOneAttribute(t *testing.T) { setup() defer teardown() updateRequest := &UpdateProjectRequest{ Name: "my-great-project", } updateResp := &Project{ ID: "project-id", Name: updateRequest.Name.(string), Description: "some-other-description", Purpose: "some-other-purpose", Environment: "some-other-env", IsDefault: false, } mux.HandleFunc("/v2/projects/project-1", func(w http.ResponseWriter, r *http.Request) { reqBytes, respErr := ioutil.ReadAll(r.Body) if respErr != nil { t.Error("projects mock didn't work") } req := strings.TrimSuffix(string(reqBytes), "\n") expectedReq := `{"name":"my-great-project","description":null,"purpose":null,"environment":null,"is_default":null}` if req != expectedReq { t.Errorf("projects req didn't match up:\n expected %+v\n got %+v\n", expectedReq, req) } resp, _ := json.Marshal(updateResp) fmt.Fprintf(w, fmt.Sprintf(`{"project":%s}`, string(resp))) }) project, _, err := client.Projects.Update(ctx, "project-1", updateRequest) if err != nil { t.Errorf("Projects.Update returned error: %v", err) } if !reflect.DeepEqual(project, updateResp) { t.Errorf("Projects.Update returned %+v, expected %+v", project, updateResp) } } func TestProjects_UpdateWithAllAttributes(t *testing.T) { setup() defer teardown() updateRequest := &UpdateProjectRequest{ Name: "my-great-project", Description: "some-description", Purpose: "some-purpose", Environment: "some-env", IsDefault: true, } updateResp := &Project{ ID: "project-id", Name: updateRequest.Name.(string), Description: updateRequest.Description.(string), Purpose: updateRequest.Purpose.(string), Environment: updateRequest.Environment.(string), IsDefault: updateRequest.IsDefault.(bool), } mux.HandleFunc("/v2/projects/project-1", func(w http.ResponseWriter, r *http.Request) { reqBytes, respErr := ioutil.ReadAll(r.Body) if respErr != nil { t.Error("projects mock didn't work") } req := strings.TrimSuffix(string(reqBytes), "\n") expectedReq := `{"name":"my-great-project","description":"some-description","purpose":"some-purpose","environment":"some-env","is_default":true}` if req != expectedReq { t.Errorf("projects req didn't match up:\n expected %+v\n got %+v\n", expectedReq, req) } resp, _ := json.Marshal(updateResp) fmt.Fprintf(w, fmt.Sprintf(`{"project":%s}`, string(resp))) }) project, _, err := client.Projects.Update(ctx, "project-1", updateRequest) if err != nil { t.Errorf("Projects.Update returned error: %v", err) } if !reflect.DeepEqual(project, updateResp) { t.Errorf("Projects.Update returned %+v, expected %+v", project, updateResp) } } func TestProjects_Destroy(t *testing.T) { setup() defer teardown() mux.HandleFunc("/v2/projects/project-1", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodDelete) }) _, err := client.Projects.Delete(ctx, "project-1") if err != nil { t.Errorf("Projects.Delete returned error: %v", err) } } func TestProjects_ListResources(t *testing.T) { setup() defer teardown() expectedResources := []ProjectResource{ { URN: "do:droplet:1", AssignedAt: "2018-09-27 00:00:00", Links: &ProjectResourceLinks{ Self: "http://example.com/v2/droplets/1", }, }, { URN: "do:floatingip:1.2.3.4", AssignedAt: "2018-09-27 00:00:00", Links: &ProjectResourceLinks{ Self: "http://example.com/v2/floating_ips/1.2.3.4", }, }, } mux.HandleFunc("/v2/projects/project-1/resources", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodGet) resp, _ := json.Marshal(expectedResources) fmt.Fprint(w, fmt.Sprintf(`{"resources":%s, "meta": {"total": 2}}`, string(resp))) }) resources, resp, err := client.Projects.ListResources(ctx, "project-1", nil) if err != nil { t.Errorf("Projects.List returned error: %v", err) } if !reflect.DeepEqual(resources, expectedResources) { t.Errorf("Projects.ListResources returned resources %+v, expected %+v", resources, expectedResources) } expectedMeta := &Meta{Total: 2} if !reflect.DeepEqual(resp.Meta, expectedMeta) { t.Errorf("Projects.ListResources returned meta %+v, expected %+v", resp.Meta, expectedMeta) } } func TestProjects_ListResourcesWithMultiplePages(t *testing.T) { setup() defer teardown() mockResp := ` { "resources": [ { "urn": "do:droplet:1", "assigned_at": "2018-09-27 00:00:00", "links": { "self": "http://example.com/v2/droplets/1" } }, { "urn": "do:floatingip:1.2.3.4", "assigned_at": "2018-09-27 00:00:00", "links": { "self": "http://example.com/v2/floating_ips/1.2.3.4" } } ], "links": { "pages": { "next": "http://example.com/v2/projects/project-1/resources?page=2" } } }` mux.HandleFunc("/v2/projects/project-1/resources", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodGet) fmt.Fprint(w, mockResp) }) _, resp, err := client.Projects.ListResources(ctx, "project-1", nil) if err != nil { t.Errorf("Projects.ListResources returned error: %v", err) } checkCurrentPage(t, resp, 1) } func TestProjects_ListResourcesWithPageNumber(t *testing.T) { setup() defer teardown() mockResp := ` { "resources": [ { "urn": "do:droplet:1", "assigned_at": "2018-09-27 00:00:00", "links": { "self": "http://example.com/v2/droplets/1" } }, { "urn": "do:floatingip:1.2.3.4", "assigned_at": "2018-09-27 00:00:00", "links": { "self": "http://example.com/v2/floating_ips/1.2.3.4" } } ], "links": { "pages": { "next": "http://example.com/v2/projects/project-1/resources?page=3", "prev": "http://example.com/v2/projects/project-1/resources?page=1", "last": "http://example.com/v2/projects/project-1/resources?page=3", "first": "http://example.com/v2/projects/project-1/resources?page=1" } } }` mux.HandleFunc("/v2/projects/project-1/resources", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodGet) fmt.Fprint(w, mockResp) }) _, resp, err := client.Projects.ListResources(ctx, "project-1", &ListOptions{Page: 2}) if err != nil { t.Errorf("Projects.ListResources returned error: %v", err) } checkCurrentPage(t, resp, 2) } func TestProjects_AssignFleetResourcesWithTypes(t *testing.T) { setup() defer teardown() assignableResources := []interface{}{ &Droplet{ID: 1234}, &FloatingIP{IP: "1.2.3.4"}, } mockResp := ` { "resources": [ { "urn": "do:droplet:1234", "assigned_at": "2018-09-27 00:00:00", "links": { "self": "http://example.com/v2/droplets/1" } }, { "urn": "do:floatingip:1.2.3.4", "assigned_at": "2018-09-27 00:00:00", "links": { "self": "http://example.com/v2/floating_ips/1.2.3.4" } } ] }` mux.HandleFunc("/v2/projects/project-1/resources", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodPost) reqBytes, respErr := ioutil.ReadAll(r.Body) if respErr != nil { t.Error("projects mock didn't work") } req := strings.TrimSuffix(string(reqBytes), "\n") expectedReq := `{"resources":["do:droplet:1234","do:floatingip:1.2.3.4"]}` if req != expectedReq { t.Errorf("projects assign req didn't match up:\n expected %+v\n got %+v\n", expectedReq, req) } fmt.Fprint(w, mockResp) }) _, _, err := client.Projects.AssignResources(ctx, "project-1", assignableResources...) if err != nil { t.Errorf("Projects.AssignResources returned error: %v", err) } } func TestProjects_AssignFleetResourcesWithStrings(t *testing.T) { setup() defer teardown() assignableResources := []interface{}{ "do:droplet:1234", "do:floatingip:1.2.3.4", } mockResp := ` { "resources": [ { "urn": "do:droplet:1234", "assigned_at": "2018-09-27 00:00:00", "links": { "self": "http://example.com/v2/droplets/1" } }, { "urn": "do:floatingip:1.2.3.4", "assigned_at": "2018-09-27 00:00:00", "links": { "self": "http://example.com/v2/floating_ips/1.2.3.4" } } ] }` mux.HandleFunc("/v2/projects/project-1/resources", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodPost) reqBytes, respErr := ioutil.ReadAll(r.Body) if respErr != nil { t.Error("projects mock didn't work") } req := strings.TrimSuffix(string(reqBytes), "\n") expectedReq := `{"resources":["do:droplet:1234","do:floatingip:1.2.3.4"]}` if req != expectedReq { t.Errorf("projects assign req didn't match up:\n expected %+v\n got %+v\n", expectedReq, req) } fmt.Fprint(w, mockResp) }) _, _, err := client.Projects.AssignResources(ctx, "project-1", assignableResources...) if err != nil { t.Errorf("Projects.AssignResources returned error: %v", err) } } func TestProjects_AssignFleetResourcesWithStringsAndTypes(t *testing.T) { setup() defer teardown() assignableResources := []interface{}{ "do:droplet:1234", &FloatingIP{IP: "1.2.3.4"}, } mockResp := ` { "resources": [ { "urn": "do:droplet:1234", "assigned_at": "2018-09-27 00:00:00", "links": { "self": "http://example.com/v2/droplets/1" } }, { "urn": "do:floatingip:1.2.3.4", "assigned_at": "2018-09-27 00:00:00", "links": { "self": "http://example.com/v2/floating_ips/1.2.3.4" } } ] }` mux.HandleFunc("/v2/projects/project-1/resources", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, http.MethodPost) reqBytes, respErr := ioutil.ReadAll(r.Body) if respErr != nil { t.Error("projects mock didn't work") } req := strings.TrimSuffix(string(reqBytes), "\n") expectedReq := `{"resources":["do:droplet:1234","do:floatingip:1.2.3.4"]}` if req != expectedReq { t.Errorf("projects assign req didn't match up:\n expected %+v\n got %+v\n", expectedReq, req) } fmt.Fprint(w, mockResp) }) _, _, err := client.Projects.AssignResources(ctx, "project-1", assignableResources...) if err != nil { t.Errorf("Projects.AssignResources returned error: %v", err) } } func TestProjects_AssignFleetResourcesWithTypeWithoutURNReturnsError(t *testing.T) { setup() defer teardown() type fakeType struct{} assignableResources := []interface{}{ fakeType{}, } _, _, err := client.Projects.AssignResources(ctx, "project-1", assignableResources...) if err == nil { t.Errorf("expected Projects.AssignResources to error, but it did not") } if err.Error() != "godo.fakeType must either be a string or have a valid URN method" { t.Errorf("Projects.AssignResources returned the wrong error: %v", err) } }