* carve out scope into a separate branch for a PR * refactor getOpts to be private * narrowed to just the scope proto * wrap in a transaction * remove user from scopes PR * make scope type immutable * refactored with more referential integrity (thanks mike) * refactor based on pair programming with Mike * better VetForWrite that supports db.Options but that requires GetOpts and Options to be exported. * stop printing out all the db logs * refactor withScope to be private * refactor the VetForWrite a bit based on Todd's PR comment * fix typo Co-authored-by: Michael Gaffney <mgaffney@users.noreply.github.com> * fix ctx propagation and Scope returns * fix ctx propagation and use a const for std retry count * change Std to Crud * fix ctx propagation * make sure update with field masks doesn't update a field not in the mask and force a read after update. * add description * remove comment about future * clean error strings * clean up enum string rep * stdMeta * fix return value to be just nil * fix returns and errors * update test to match new error string * change len of domain to 24 * provide common function for new public ids * use common db.NewPublicId() and provide the correct prefix * move list out of CrudActions and provide a CrudlActions shortcut as well * some comments about the purpose of VetForWrite() * move to random 10 * Adding project API interface. * Creation of Project handler and Tests * Move to using the iam objects. * Setting up testify framework for mocking and assertions. * Fix iam options typo references. * Adding tests for CreateProject and UpdateProject. This also adds some additional argument validation. * Merging in PR 40 * Renamed fakeRepo to mockRepo * Changing around TODOs to capture current plans with the code. * Remove the repo interface and use a real DB for tests. * Fixed a few tests. * Adding checks for properly formatted identifiers. * Updating code to account for recent merges. Relaxing some restrictions on what an id can look like. * Added small comment explaining why RegisterGrpcGatewayer interface exists. * Move project service up a call level. Long term I think it would make sense to define these services even higher up the call stack, potentially at the initialization of the controller server, near where the DB is initialized. * Making these fields align with those in the resources under hosts/... * Hard code the service initialization in the handler registration code. * Regenerate project related code proto code. * Removing type field for project. * Adds basic project handling in CLI/SDK (#47) * Adds SDK for creating and reading projects * Reorganizes a bit * Adds CLI for creating and reading projects * Fixes some some generation bugs * Adds redirect support for default org ID * Fix protobuf import references being moved. * Update client test to look for "orgs" and "projects" instead of "org" and "project" * Update expected segments test * Fix setAddr handling when default organization is in use * Set client in target after create or read * Adding functionality and tests for update masks. * Additional tests for update. Disable update non existant project test. * Make not providing an update mask be an invalid argument. * Make empty update mask paths and no mutable fields in the path an InvalidArgument error. * Fix get Project test. * Adding back in the Update a non existant project test case. * Adding seperate path structured tests. * Adding delete from repository API call. * Update calls to LookupScope and DeleteScope. * Pass in only what is needed for interfacing with the repo. * Creating delete sdk methods for the first time. * Create delete generation functionality in go sdk. * Use assert for tests instead of if statements. Co-authored-by: Jim Lambert <jimlambrt@Jims-MBP-3.home> Co-authored-by: Jim <jlambert@hashicorp.com> Co-authored-by: Michael Gaffney <mgaffney@users.noreply.github.com> Co-authored-by: Jeff Mitchell <jeffrey.mitchell@gmail.com>
558 lines
16 KiB
Go
558 lines
16 KiB
Go
package projects_test
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"strings"
|
|
"testing"
|
|
|
|
"github.com/golang/protobuf/proto"
|
|
"github.com/golang/protobuf/ptypes"
|
|
"github.com/golang/protobuf/ptypes/wrappers"
|
|
"github.com/hashicorp/watchtower/internal/db"
|
|
pb "github.com/hashicorp/watchtower/internal/gen/controller/api/resources/scopes"
|
|
pbs "github.com/hashicorp/watchtower/internal/gen/controller/api/services"
|
|
"github.com/hashicorp/watchtower/internal/iam"
|
|
"github.com/hashicorp/watchtower/internal/servers/controller/handlers/projects"
|
|
"google.golang.org/genproto/protobuf/field_mask"
|
|
"google.golang.org/grpc/codes"
|
|
"google.golang.org/grpc/status"
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
)
|
|
|
|
func createDefaultProjectAndRepo(t *testing.T) (*iam.Scope, *iam.Repository) {
|
|
t.Helper()
|
|
cleanup, conn, _ := db.TestSetup(t, "postgres")
|
|
t.Cleanup(func() {
|
|
conn.Close()
|
|
cleanup()
|
|
})
|
|
rw := db.New(conn)
|
|
wrap := db.TestWrapper(t)
|
|
repo, err := iam.NewRepository(rw, rw, wrap)
|
|
assert.Nil(t, err, "Unable to create new repo")
|
|
|
|
// Create a default org and project for our tests.
|
|
o, err := iam.NewOrganization(iam.WithName("default"))
|
|
if err != nil {
|
|
t.Fatalf("Could not get new org: %v", err)
|
|
}
|
|
oRes, err := repo.CreateScope(context.Background(), o)
|
|
if err != nil {
|
|
t.Fatalf("Could not create org scope: %v", err)
|
|
}
|
|
|
|
p, err := iam.NewProject(oRes.GetPublicId(), iam.WithName("default"), iam.WithDescription("default"))
|
|
if err != nil {
|
|
t.Fatalf("Could not get new project: %v", err)
|
|
}
|
|
pRes, err := repo.CreateScope(context.Background(), p)
|
|
if err != nil {
|
|
t.Fatalf("Could not create project scope: %v", err)
|
|
}
|
|
|
|
return pRes, repo
|
|
}
|
|
|
|
func TestGet(t *testing.T) {
|
|
proj, repo := createDefaultProjectAndRepo(t)
|
|
toMerge := &pbs.GetProjectRequest{
|
|
OrgId: proj.GetParentId(),
|
|
Id: proj.GetPublicId(),
|
|
}
|
|
|
|
pProject := &pb.Project{
|
|
Id: proj.GetPublicId(),
|
|
Name: &wrappers.StringValue{Value: proj.GetName()},
|
|
Description: &wrappers.StringValue{Value: proj.GetDescription()},
|
|
CreatedTime: proj.CreateTime.GetTimestamp(),
|
|
UpdatedTime: proj.UpdateTime.GetTimestamp(),
|
|
}
|
|
|
|
cases := []struct {
|
|
name string
|
|
req *pbs.GetProjectRequest
|
|
res *pbs.GetProjectResponse
|
|
errCode codes.Code
|
|
}{
|
|
{
|
|
name: "Get an Existing Project",
|
|
req: &pbs.GetProjectRequest{Id: proj.GetPublicId()},
|
|
res: &pbs.GetProjectResponse{Item: pProject},
|
|
errCode: codes.OK,
|
|
},
|
|
{
|
|
name: "Get a non existant Host Catalog",
|
|
req: &pbs.GetProjectRequest{Id: "p_DoesntExis"},
|
|
res: nil,
|
|
// This will be fixed with PR 42
|
|
errCode: codes.NotFound,
|
|
},
|
|
{
|
|
name: "Wrong id prefix",
|
|
req: &pbs.GetProjectRequest{Id: "j_1234567890"},
|
|
res: nil,
|
|
// This will be fixed with PR 42
|
|
errCode: codes.InvalidArgument,
|
|
},
|
|
{
|
|
name: "space in id",
|
|
req: &pbs.GetProjectRequest{Id: "p_1 23456789"},
|
|
res: nil,
|
|
// This will be fixed with PR 42
|
|
errCode: codes.InvalidArgument,
|
|
},
|
|
}
|
|
for _, tc := range cases {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
assert := assert.New(t)
|
|
req := proto.Clone(toMerge).(*pbs.GetProjectRequest)
|
|
proto.Merge(req, tc.req)
|
|
|
|
s := projects.NewService(repo)
|
|
|
|
got, gErr := s.GetProject(context.Background(), req)
|
|
assert.Equal(tc.errCode, status.Code(gErr), "GetProject(%+v) got error %v, wanted %v", req, gErr, tc.errCode)
|
|
assert.True(proto.Equal(got, tc.res), "GetProject(%q) got response %q, wanted %q", req, got, tc.res)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestDelete(t *testing.T) {
|
|
proj, repo := createDefaultProjectAndRepo(t)
|
|
|
|
proj2, err := iam.NewProject(proj.GetParentId())
|
|
if err != nil {
|
|
t.Fatalf("Couldn't allocate a second project: %v", err)
|
|
}
|
|
proj2, err = repo.CreateScope(context.Background(), proj2)
|
|
if err != nil {
|
|
t.Fatalf("Couldn't persist a second project %v", err)
|
|
}
|
|
|
|
s := projects.NewService(repo)
|
|
|
|
cases := []struct {
|
|
name string
|
|
req *pbs.DeleteProjectRequest
|
|
res *pbs.DeleteProjectResponse
|
|
errCode codes.Code
|
|
}{
|
|
{
|
|
name: "Delete an Existing Project",
|
|
req: &pbs.DeleteProjectRequest{
|
|
OrgId: proj2.GetParentId(),
|
|
Id: proj2.GetPublicId(),
|
|
},
|
|
res: &pbs.DeleteProjectResponse{
|
|
Existed: true,
|
|
},
|
|
errCode: codes.OK,
|
|
},
|
|
{
|
|
name: "Delete bad project id Project",
|
|
req: &pbs.DeleteProjectRequest{
|
|
OrgId: proj2.GetParentId(),
|
|
Id: "p_doesntexis",
|
|
},
|
|
res: &pbs.DeleteProjectResponse{
|
|
Existed: false,
|
|
},
|
|
errCode: codes.OK,
|
|
},
|
|
{
|
|
name: "Delete bad org id Project",
|
|
req: &pbs.DeleteProjectRequest{
|
|
OrgId: "o_doesntexis",
|
|
Id: proj2.GetPublicId(),
|
|
},
|
|
res: &pbs.DeleteProjectResponse{
|
|
Existed: false,
|
|
},
|
|
errCode: codes.OK,
|
|
},
|
|
{
|
|
name: "Bad org formatting",
|
|
req: &pbs.DeleteProjectRequest{
|
|
OrgId: "bad_format",
|
|
Id: proj2.GetPublicId(),
|
|
},
|
|
res: nil,
|
|
errCode: codes.InvalidArgument,
|
|
},
|
|
{
|
|
name: "Bad Project Id formatting",
|
|
req: &pbs.DeleteProjectRequest{
|
|
OrgId: proj2.GetParentId(),
|
|
Id: "bad_format",
|
|
},
|
|
res: nil,
|
|
errCode: codes.InvalidArgument,
|
|
},
|
|
}
|
|
for _, tc := range cases {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
assert := assert.New(t)
|
|
got, gErr := s.DeleteProject(context.Background(), tc.req)
|
|
assert.Equal(tc.errCode, status.Code(gErr), "DeleteProject(%+v) got error %v, wanted %v", tc.req, gErr, tc.errCode)
|
|
assert.EqualValuesf(tc.res, got, "DeleteProject(%q) got response %q, wanted %q", tc.req, got, tc.res)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestDelete_twice(t *testing.T) {
|
|
assert := assert.New(t)
|
|
proj, repo := createDefaultProjectAndRepo(t)
|
|
|
|
s := projects.NewService(repo)
|
|
req := &pbs.DeleteProjectRequest{
|
|
OrgId: proj.GetParentId(),
|
|
Id: proj.GetPublicId(),
|
|
}
|
|
got, gErr := s.DeleteProject(context.Background(), req)
|
|
assert.NoError(gErr, "First attempt")
|
|
assert.True(got.GetExisted(), "Expected existed to be true for the first delete.")
|
|
got, gErr = s.DeleteProject(context.Background(), req)
|
|
assert.NoError(gErr, "Second attempt")
|
|
assert.False(got.GetExisted(), "Expected existed to be false for the second delete.")
|
|
}
|
|
|
|
func TestCreate(t *testing.T) {
|
|
defaultProj, repo := createDefaultProjectAndRepo(t)
|
|
defaultProjCreated, err := ptypes.Timestamp(defaultProj.GetCreateTime().GetTimestamp())
|
|
if err != nil {
|
|
t.Fatalf("Error converting proto to timestamp: %v", err)
|
|
}
|
|
toMerge := &pbs.CreateProjectRequest{
|
|
OrgId: defaultProj.GetParentId(),
|
|
}
|
|
|
|
cases := []struct {
|
|
name string
|
|
req *pbs.CreateProjectRequest
|
|
res *pbs.CreateProjectResponse
|
|
errCode codes.Code
|
|
}{
|
|
{
|
|
name: "Create a valid Project",
|
|
req: &pbs.CreateProjectRequest{Item: &pb.Project{
|
|
Name: &wrappers.StringValue{Value: "name"},
|
|
Description: &wrappers.StringValue{Value: "desc"},
|
|
}},
|
|
res: &pbs.CreateProjectResponse{
|
|
Uri: fmt.Sprintf("orgs/%s/projects/p_", defaultProj.GetParentId()),
|
|
Item: &pb.Project{
|
|
Name: &wrappers.StringValue{Value: "name"},
|
|
Description: &wrappers.StringValue{Value: "desc"},
|
|
},
|
|
},
|
|
errCode: codes.OK,
|
|
},
|
|
{
|
|
name: "Can't specify Id",
|
|
req: &pbs.CreateProjectRequest{Item: &pb.Project{
|
|
Id: "not allowed to be set",
|
|
}},
|
|
res: nil,
|
|
errCode: codes.InvalidArgument,
|
|
},
|
|
{
|
|
name: "Can't specify Created Time",
|
|
req: &pbs.CreateProjectRequest{Item: &pb.Project{
|
|
CreatedTime: ptypes.TimestampNow(),
|
|
}},
|
|
res: nil,
|
|
errCode: codes.InvalidArgument,
|
|
},
|
|
{
|
|
name: "Can't specify Update Time",
|
|
req: &pbs.CreateProjectRequest{Item: &pb.Project{
|
|
UpdatedTime: ptypes.TimestampNow(),
|
|
}},
|
|
res: nil,
|
|
errCode: codes.InvalidArgument,
|
|
},
|
|
}
|
|
for _, tc := range cases {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
assert := assert.New(t)
|
|
req := proto.Clone(toMerge).(*pbs.CreateProjectRequest)
|
|
proto.Merge(req, tc.req)
|
|
|
|
s := projects.NewService(repo)
|
|
|
|
got, gErr := s.CreateProject(context.Background(), req)
|
|
assert.Equal(tc.errCode, status.Code(gErr), "CreateProject(%+v) got error %v, wanted %v", req, gErr, tc.errCode)
|
|
if got != nil {
|
|
strings.HasPrefix(got.GetUri(), tc.res.Uri)
|
|
strings.HasPrefix(got.GetItem().GetId(), "p_")
|
|
gotCreateTime, err := ptypes.Timestamp(got.GetItem().GetCreatedTime())
|
|
if err != nil {
|
|
t.Fatalf("Error converting proto to timestamp: %v", err)
|
|
}
|
|
gotUpdateTime, err := ptypes.Timestamp(got.GetItem().GetUpdatedTime())
|
|
if err != nil {
|
|
t.Fatalf("Error converting proto to timestamp: %v", err)
|
|
}
|
|
// Verify it is a project created after the test setup's default project
|
|
assert.True(gotCreateTime.After(defaultProjCreated), "New project should have been created after default project. Was created %v, which is after %v", gotCreateTime, defaultProjCreated)
|
|
assert.True(gotUpdateTime.After(defaultProjCreated), "New project should have been updated after default project. Was updated %v, which is after %v", gotUpdateTime, defaultProjCreated)
|
|
|
|
// Clear all values which are hard to compare against.
|
|
got.Uri, tc.res.Uri = "", ""
|
|
got.Item.Id, tc.res.Item.Id = "", ""
|
|
got.Item.CreatedTime, got.Item.UpdatedTime, tc.res.Item.CreatedTime, tc.res.Item.UpdatedTime = nil, nil, nil, nil
|
|
}
|
|
assert.True(proto.Equal(got, tc.res), "CreateProject(%q) got response %q, wanted %q", req, got, tc.res)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestUpdate(t *testing.T) {
|
|
proj, repo := createDefaultProjectAndRepo(t)
|
|
tested := projects.NewService(repo)
|
|
|
|
var err error
|
|
resetProject := func() {
|
|
if proj, _, err = repo.UpdateScope(context.Background(), proj, []string{"Name", "Description"}); err != nil {
|
|
t.Fatalf("Failed to reset the project")
|
|
}
|
|
}
|
|
|
|
projCreated, err := ptypes.Timestamp(proj.GetCreateTime().GetTimestamp())
|
|
if err != nil {
|
|
t.Fatalf("Error converting proto to timestamp: %v", err)
|
|
}
|
|
toMerge := &pbs.UpdateProjectRequest{
|
|
OrgId: proj.GetParentId(),
|
|
Id: proj.GetPublicId(),
|
|
}
|
|
|
|
cases := []struct {
|
|
name string
|
|
req *pbs.UpdateProjectRequest
|
|
res *pbs.UpdateProjectResponse
|
|
errCode codes.Code
|
|
}{
|
|
{
|
|
name: "Update an Existing Project",
|
|
req: &pbs.UpdateProjectRequest{
|
|
UpdateMask: &field_mask.FieldMask{
|
|
Paths: []string{"name", "description"},
|
|
},
|
|
Item: &pb.Project{
|
|
Name: &wrappers.StringValue{Value: "new"},
|
|
Description: &wrappers.StringValue{Value: "desc"},
|
|
},
|
|
},
|
|
res: &pbs.UpdateProjectResponse{
|
|
Item: &pb.Project{
|
|
Id: proj.GetPublicId(),
|
|
Name: &wrappers.StringValue{Value: "new"},
|
|
Description: &wrappers.StringValue{Value: "desc"},
|
|
CreatedTime: proj.GetCreateTime().GetTimestamp(),
|
|
},
|
|
},
|
|
errCode: codes.OK,
|
|
},
|
|
{
|
|
name: "Multiple Paths in single string",
|
|
req: &pbs.UpdateProjectRequest{
|
|
UpdateMask: &field_mask.FieldMask{
|
|
Paths: []string{"name,description"},
|
|
},
|
|
Item: &pb.Project{
|
|
Name: &wrappers.StringValue{Value: "new"},
|
|
Description: &wrappers.StringValue{Value: "desc"},
|
|
},
|
|
},
|
|
res: &pbs.UpdateProjectResponse{
|
|
Item: &pb.Project{
|
|
Id: proj.GetPublicId(),
|
|
Name: &wrappers.StringValue{Value: "new"},
|
|
Description: &wrappers.StringValue{Value: "desc"},
|
|
CreatedTime: proj.GetCreateTime().GetTimestamp(),
|
|
},
|
|
},
|
|
errCode: codes.OK,
|
|
},
|
|
{
|
|
name: "No Update Mask Is Invalid Argument",
|
|
req: &pbs.UpdateProjectRequest{
|
|
Item: &pb.Project{
|
|
Name: &wrappers.StringValue{Value: "updated name"},
|
|
Description: &wrappers.StringValue{Value: "updated desc"},
|
|
},
|
|
},
|
|
errCode: codes.InvalidArgument,
|
|
},
|
|
{
|
|
name: "No Paths in Mask Is Invalid Argument",
|
|
req: &pbs.UpdateProjectRequest{
|
|
UpdateMask: &field_mask.FieldMask{Paths: []string{}},
|
|
Item: &pb.Project{
|
|
Name: &wrappers.StringValue{Value: "updated name"},
|
|
Description: &wrappers.StringValue{Value: "updated desc"},
|
|
},
|
|
},
|
|
errCode: codes.InvalidArgument,
|
|
},
|
|
{
|
|
name: "Only non-existant paths in Mask Is Invalid Argument",
|
|
req: &pbs.UpdateProjectRequest{
|
|
UpdateMask: &field_mask.FieldMask{Paths: []string{"nonexistant_field"}},
|
|
Item: &pb.Project{
|
|
Name: &wrappers.StringValue{Value: "updated name"},
|
|
Description: &wrappers.StringValue{Value: "updated desc"},
|
|
},
|
|
},
|
|
errCode: codes.InvalidArgument,
|
|
},
|
|
{
|
|
name: "Unset Name",
|
|
req: &pbs.UpdateProjectRequest{
|
|
UpdateMask: &field_mask.FieldMask{
|
|
Paths: []string{"name"},
|
|
},
|
|
Item: &pb.Project{
|
|
Description: &wrappers.StringValue{Value: "ignored"},
|
|
},
|
|
},
|
|
res: &pbs.UpdateProjectResponse{
|
|
Item: &pb.Project{
|
|
Id: proj.GetPublicId(),
|
|
Description: &wrappers.StringValue{Value: "default"},
|
|
CreatedTime: proj.GetCreateTime().GetTimestamp(),
|
|
},
|
|
},
|
|
errCode: codes.OK,
|
|
},
|
|
{
|
|
name: "Update Only Name",
|
|
req: &pbs.UpdateProjectRequest{
|
|
UpdateMask: &field_mask.FieldMask{
|
|
Paths: []string{"name"},
|
|
},
|
|
Item: &pb.Project{
|
|
Name: &wrappers.StringValue{Value: "updated"},
|
|
Description: &wrappers.StringValue{Value: "ignored"},
|
|
},
|
|
},
|
|
res: &pbs.UpdateProjectResponse{
|
|
Item: &pb.Project{
|
|
Id: proj.GetPublicId(),
|
|
Name: &wrappers.StringValue{Value: "updated"},
|
|
Description: &wrappers.StringValue{Value: "default"},
|
|
CreatedTime: proj.GetCreateTime().GetTimestamp(),
|
|
},
|
|
},
|
|
errCode: codes.OK,
|
|
},
|
|
{
|
|
name: "Update Only Description",
|
|
req: &pbs.UpdateProjectRequest{
|
|
UpdateMask: &field_mask.FieldMask{
|
|
Paths: []string{"description"},
|
|
},
|
|
Item: &pb.Project{
|
|
Name: &wrappers.StringValue{Value: "ignored"},
|
|
Description: &wrappers.StringValue{Value: "notignored"},
|
|
},
|
|
},
|
|
res: &pbs.UpdateProjectResponse{
|
|
Item: &pb.Project{
|
|
Id: proj.GetPublicId(),
|
|
Name: &wrappers.StringValue{Value: "default"},
|
|
Description: &wrappers.StringValue{Value: "notignored"},
|
|
CreatedTime: proj.GetCreateTime().GetTimestamp(),
|
|
},
|
|
},
|
|
errCode: codes.OK,
|
|
},
|
|
{
|
|
name: "Update a Non Existing Project",
|
|
req: &pbs.UpdateProjectRequest{
|
|
Id: "p_DoesntExis",
|
|
UpdateMask: &field_mask.FieldMask{
|
|
Paths: []string{"description"},
|
|
},
|
|
Item: &pb.Project{
|
|
Name: &wrappers.StringValue{Value: "new"},
|
|
Description: &wrappers.StringValue{Value: "desc"},
|
|
},
|
|
},
|
|
errCode: codes.Unknown,
|
|
},
|
|
{
|
|
name: "Cant change Id",
|
|
req: &pbs.UpdateProjectRequest{
|
|
Id: proj.GetPublicId(),
|
|
UpdateMask: &field_mask.FieldMask{
|
|
Paths: []string{"id"},
|
|
},
|
|
Item: &pb.Project{
|
|
Id: "p_somethinge",
|
|
Name: &wrappers.StringValue{Value: "new"},
|
|
Description: &wrappers.StringValue{Value: "new desc"},
|
|
}},
|
|
res: nil,
|
|
errCode: codes.InvalidArgument,
|
|
},
|
|
{
|
|
name: "Cant specify Created Time",
|
|
req: &pbs.UpdateProjectRequest{
|
|
UpdateMask: &field_mask.FieldMask{
|
|
Paths: []string{"created_time"},
|
|
},
|
|
Item: &pb.Project{
|
|
CreatedTime: ptypes.TimestampNow(),
|
|
},
|
|
},
|
|
res: nil,
|
|
errCode: codes.InvalidArgument,
|
|
},
|
|
{
|
|
name: "Cant specify Updated Time",
|
|
req: &pbs.UpdateProjectRequest{
|
|
UpdateMask: &field_mask.FieldMask{
|
|
Paths: []string{"updated_time"},
|
|
},
|
|
Item: &pb.Project{
|
|
UpdatedTime: ptypes.TimestampNow(),
|
|
},
|
|
},
|
|
res: nil,
|
|
errCode: codes.InvalidArgument,
|
|
},
|
|
}
|
|
for _, tc := range cases {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
defer resetProject()
|
|
assert := assert.New(t)
|
|
req := proto.Clone(toMerge).(*pbs.UpdateProjectRequest)
|
|
proto.Merge(req, tc.req)
|
|
|
|
got, gErr := tested.UpdateProject(context.Background(), req)
|
|
assert.Equal(tc.errCode, status.Code(gErr), "UpdateProject(%+v) got error %v, wanted %v", req, gErr, tc.errCode)
|
|
|
|
if got != nil {
|
|
assert.NotNilf(tc.res, "Expected UpdateProject response to be nil, but was %v", got)
|
|
gotUpdateTime, err := ptypes.Timestamp(got.GetItem().GetUpdatedTime())
|
|
if err != nil {
|
|
t.Fatalf("Error converting proto to timestamp: %v", err)
|
|
}
|
|
// Verify it is a project updated after it was created
|
|
// TODO: This is currently failing.
|
|
//assert.True(gotUpdateTime.After(projCreated), "Updated project should have been updated after it's creation. Was updated %v, which is after %v", gotUpdateTime, projCreated)
|
|
_ = gotUpdateTime
|
|
_ = projCreated
|
|
|
|
// Clear all values which are hard to compare against.
|
|
got.Item.UpdatedTime, tc.res.Item.UpdatedTime = nil, nil
|
|
}
|
|
assert.True(proto.Equal(got, tc.res), "UpdateProject(%q) got response %q, wanted %q", req, got, tc.res)
|
|
})
|
|
}
|
|
}
|