* Move the server daemons into their own package With this change the internal/server package now is dedicated to the server domain (workers and controllers) and not to the server daemons. The hope is that this will make it easier to keep the daemons decoupled from the representation of the servers in the database.
478 lines
11 KiB
Go
478 lines
11 KiB
Go
package handlers
|
|
|
|
import (
|
|
"errors"
|
|
"testing"
|
|
|
|
pbs "github.com/hashicorp/boundary/internal/gen/controller/api/services"
|
|
pb "github.com/hashicorp/boundary/sdk/pbs/controller/api/resources/users"
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
"google.golang.org/protobuf/types/known/fieldmaskpb"
|
|
"google.golang.org/protobuf/types/known/timestamppb"
|
|
"google.golang.org/protobuf/types/known/wrapperspb"
|
|
)
|
|
|
|
func errorIncludesFields(t *testing.T, err error, wantFields []string) {
|
|
t.Helper()
|
|
var apiErr *ApiError
|
|
require.True(t, errors.As(err, &apiErr))
|
|
var gotFields []string
|
|
for _, d := range apiErr.Inner.GetDetails().GetRequestFields() {
|
|
gotFields = append(gotFields, d.GetName())
|
|
}
|
|
assert.ElementsMatch(t, gotFields, wantFields)
|
|
}
|
|
|
|
// Throughout this test we will use the User requests, but we could use any request
|
|
// message. User was picked arbitrarily.
|
|
|
|
func TestValidId(t *testing.T) {
|
|
assert.True(t, ValidId(Id("prefix_somerandomid"), "prefix"))
|
|
assert.True(t, ValidId(Id("prefix_somerandomid"), "notprefix", "prefix"))
|
|
assert.True(t, ValidId(Id("prefix_short"), "prefix"))
|
|
assert.True(t, ValidId(Id("prefix_thisisalongidentifierwhichstillworks"), "prefix"))
|
|
|
|
assert.False(t, ValidId(Id("prefixsomerandomid"), "prefix"))
|
|
assert.False(t, ValidId(Id("prefixsomerandomid"), "prefix", "alsobadprefix"))
|
|
assert.False(t, ValidId(Id("prefix_this has spaces"), "prefix"))
|
|
assert.False(t, ValidId(Id("prefix_includes-dash"), "prefix"))
|
|
assert.False(t, ValidId(Id("prefix_other@strange!characters"), "prefix"))
|
|
assert.False(t, ValidId(Id("prefix_short"), "short"))
|
|
}
|
|
|
|
func TestValidNameDescription(t *testing.T) {
|
|
assert.True(t, ValidNameDescription("foobar"))
|
|
assert.False(t, ValidNameDescription("foo\u200Bbar"))
|
|
}
|
|
|
|
func TestValidateGetRequest(t *testing.T) {
|
|
cases := []struct {
|
|
name string
|
|
prefix string
|
|
req GetRequest
|
|
valFn CustomValidatorFunc
|
|
badFields []string
|
|
}{
|
|
{
|
|
name: "noopvalidator no error",
|
|
prefix: "prefix",
|
|
req: &pbs.GetUserRequest{
|
|
Id: "prefix_someidentifier",
|
|
},
|
|
valFn: NoopValidatorFn,
|
|
},
|
|
{
|
|
name: "noopvalidator bad prefix",
|
|
prefix: "bad",
|
|
req: &pbs.GetUserRequest{
|
|
Id: "prefix_someidentifier",
|
|
},
|
|
valFn: NoopValidatorFn,
|
|
badFields: []string{"id"},
|
|
},
|
|
{
|
|
name: "custom field error",
|
|
prefix: "prefix",
|
|
req: &pbs.GetUserRequest{
|
|
Id: "prefix_someidentifier",
|
|
},
|
|
valFn: func() map[string]string {
|
|
return map[string]string{"test": "test"}
|
|
},
|
|
badFields: []string{"test"},
|
|
},
|
|
{
|
|
name: "both custom error and prefix",
|
|
prefix: "bad",
|
|
req: &pbs.GetUserRequest{
|
|
Id: "prefix_someidentifier",
|
|
},
|
|
valFn: func() map[string]string {
|
|
return map[string]string{"test": "test"}
|
|
},
|
|
badFields: []string{"id", "test"},
|
|
},
|
|
}
|
|
|
|
for _, tc := range cases {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
err := ValidateGetRequest(tc.valFn, tc.req, tc.prefix)
|
|
if len(tc.badFields) == 0 {
|
|
if !assert.NoError(t, err) {
|
|
errorIncludesFields(t, err, []string{})
|
|
}
|
|
return
|
|
}
|
|
errorIncludesFields(t, err, tc.badFields)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestValidateDeleteRequest(t *testing.T) {
|
|
cases := []struct {
|
|
name string
|
|
prefix string
|
|
req DeleteRequest
|
|
valFn CustomValidatorFunc
|
|
badFields []string
|
|
}{
|
|
{
|
|
name: "noopvalidator no error",
|
|
prefix: "prefix",
|
|
req: &pbs.DeleteUserRequest{
|
|
Id: "prefix_someidentifier",
|
|
},
|
|
valFn: NoopValidatorFn,
|
|
},
|
|
{
|
|
name: "noopvalidator bad prefix",
|
|
prefix: "bad",
|
|
req: &pbs.DeleteUserRequest{
|
|
Id: "prefix_someidentifier",
|
|
},
|
|
valFn: NoopValidatorFn,
|
|
badFields: []string{"id"},
|
|
},
|
|
{
|
|
name: "custom field error",
|
|
prefix: "prefix",
|
|
req: &pbs.DeleteUserRequest{
|
|
Id: "prefix_someidentifier",
|
|
},
|
|
valFn: func() map[string]string {
|
|
return map[string]string{"test": "test"}
|
|
},
|
|
badFields: []string{"test"},
|
|
},
|
|
{
|
|
name: "both custom error and prefix",
|
|
prefix: "bad",
|
|
req: &pbs.DeleteUserRequest{
|
|
Id: "prefix_someidentifier",
|
|
},
|
|
valFn: func() map[string]string {
|
|
return map[string]string{"test": "test"}
|
|
},
|
|
badFields: []string{"id", "test"},
|
|
},
|
|
}
|
|
|
|
for _, tc := range cases {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
err := ValidateDeleteRequest(tc.valFn, tc.req, tc.prefix)
|
|
if len(tc.badFields) == 0 {
|
|
if !assert.NoError(t, err) {
|
|
errorIncludesFields(t, err, []string{})
|
|
}
|
|
return
|
|
}
|
|
errorIncludesFields(t, err, tc.badFields)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestValidateCreateRequest(t *testing.T) {
|
|
cases := []struct {
|
|
name string
|
|
item ApiResource
|
|
valFn CustomValidatorFunc
|
|
badFields []string
|
|
}{
|
|
{
|
|
name: "valid",
|
|
item: &pb.User{},
|
|
valFn: NoopValidatorFn,
|
|
},
|
|
{
|
|
name: "disallow set id",
|
|
item: &pb.User{
|
|
Id: "anything",
|
|
},
|
|
valFn: NoopValidatorFn,
|
|
badFields: []string{"id"},
|
|
},
|
|
{
|
|
name: "disallow set created",
|
|
item: &pb.User{
|
|
CreatedTime: timestamppb.Now(),
|
|
},
|
|
valFn: NoopValidatorFn,
|
|
badFields: []string{"created_time"},
|
|
},
|
|
{
|
|
name: "disallow set updated",
|
|
item: &pb.User{
|
|
UpdatedTime: timestamppb.Now(),
|
|
},
|
|
valFn: NoopValidatorFn,
|
|
badFields: []string{"updated_time"},
|
|
},
|
|
{
|
|
name: "disallow set version",
|
|
item: &pb.User{
|
|
Version: 4,
|
|
},
|
|
valFn: NoopValidatorFn,
|
|
badFields: []string{"version"},
|
|
},
|
|
{
|
|
name: "custom validator error",
|
|
item: &pb.User{},
|
|
valFn: func() map[string]string {
|
|
return map[string]string{"test": "test"}
|
|
},
|
|
badFields: []string{"test"},
|
|
},
|
|
{
|
|
name: "disallow several fields",
|
|
item: &pb.User{
|
|
Id: "anything",
|
|
CreatedTime: timestamppb.Now(),
|
|
Version: 4,
|
|
},
|
|
valFn: func() map[string]string {
|
|
return map[string]string{"test": "test"}
|
|
},
|
|
badFields: []string{"id", "created_time", "version", "test"},
|
|
},
|
|
{
|
|
name: "good name",
|
|
item: &pb.User{
|
|
Name: wrapperspb.String("foobar"),
|
|
},
|
|
valFn: NoopValidatorFn,
|
|
},
|
|
{
|
|
name: "bad name",
|
|
item: &pb.User{
|
|
Name: wrapperspb.String("foo\u200Bbar"),
|
|
},
|
|
valFn: NoopValidatorFn,
|
|
badFields: []string{"name"},
|
|
},
|
|
{
|
|
name: "good description",
|
|
item: &pb.User{
|
|
Description: wrapperspb.String("foobar"),
|
|
},
|
|
valFn: NoopValidatorFn,
|
|
},
|
|
{
|
|
name: "bad description",
|
|
item: &pb.User{
|
|
Description: wrapperspb.String("foo\u200Bbar"),
|
|
},
|
|
valFn: NoopValidatorFn,
|
|
badFields: []string{"description"},
|
|
},
|
|
}
|
|
|
|
for _, tc := range cases {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
err := ValidateCreateRequest(tc.item, tc.valFn)
|
|
if len(tc.badFields) == 0 {
|
|
if !assert.NoError(t, err) {
|
|
errorIncludesFields(t, err, []string{})
|
|
}
|
|
return
|
|
}
|
|
errorIncludesFields(t, err, tc.badFields)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestValidateUpdateRequest(t *testing.T) {
|
|
cases := []struct {
|
|
name string
|
|
prefix string
|
|
req UpdateRequest
|
|
item ApiResource
|
|
valFn CustomValidatorFunc
|
|
badFields []string
|
|
}{
|
|
{
|
|
name: "valid",
|
|
prefix: "prefix",
|
|
req: &pbs.UpdateUserRequest{
|
|
Id: "prefix_something",
|
|
UpdateMask: &fieldmaskpb.FieldMask{Paths: []string{"anything"}},
|
|
},
|
|
item: &pb.User{
|
|
Version: 1,
|
|
},
|
|
valFn: NoopValidatorFn,
|
|
},
|
|
{
|
|
name: "missing mask",
|
|
prefix: "prefix",
|
|
req: &pbs.UpdateUserRequest{
|
|
Id: "prefix_something",
|
|
},
|
|
item: &pb.User{
|
|
Version: 1,
|
|
},
|
|
valFn: NoopValidatorFn,
|
|
badFields: []string{"update_mask"},
|
|
},
|
|
{
|
|
name: "bad id",
|
|
prefix: "prefix",
|
|
req: &pbs.UpdateUserRequest{
|
|
Id: "mismatched_prefix",
|
|
UpdateMask: &fieldmaskpb.FieldMask{Paths: []string{"anything"}},
|
|
},
|
|
item: &pb.User{
|
|
Version: 1,
|
|
},
|
|
valFn: NoopValidatorFn,
|
|
badFields: []string{"id"},
|
|
},
|
|
{
|
|
name: "good name",
|
|
prefix: "prefix",
|
|
req: &pbs.UpdateUserRequest{
|
|
Id: "prefix_something",
|
|
UpdateMask: &fieldmaskpb.FieldMask{Paths: []string{"anything"}},
|
|
},
|
|
item: &pb.User{
|
|
Version: 1,
|
|
Name: wrapperspb.String("foobar"),
|
|
},
|
|
valFn: NoopValidatorFn,
|
|
},
|
|
{
|
|
name: "bad name",
|
|
prefix: "prefix",
|
|
req: &pbs.UpdateUserRequest{
|
|
Id: "prefix_something",
|
|
UpdateMask: &fieldmaskpb.FieldMask{Paths: []string{"anything"}},
|
|
},
|
|
item: &pb.User{
|
|
Version: 1,
|
|
Name: wrapperspb.String("foo\u200Bbar"),
|
|
},
|
|
valFn: NoopValidatorFn,
|
|
badFields: []string{"name"},
|
|
},
|
|
{
|
|
name: "good description",
|
|
prefix: "prefix",
|
|
req: &pbs.UpdateUserRequest{
|
|
Id: "prefix_something",
|
|
UpdateMask: &fieldmaskpb.FieldMask{Paths: []string{"anything"}},
|
|
},
|
|
item: &pb.User{
|
|
Version: 1,
|
|
Description: wrapperspb.String("foobar"),
|
|
},
|
|
valFn: NoopValidatorFn,
|
|
},
|
|
{
|
|
name: "bad description",
|
|
prefix: "prefix",
|
|
req: &pbs.UpdateUserRequest{
|
|
Id: "prefix_something",
|
|
UpdateMask: &fieldmaskpb.FieldMask{Paths: []string{"anything"}},
|
|
},
|
|
item: &pb.User{
|
|
Version: 1,
|
|
Description: wrapperspb.String("foo\u200Bbar"),
|
|
},
|
|
valFn: NoopValidatorFn,
|
|
badFields: []string{"description"},
|
|
},
|
|
{
|
|
name: "missing version",
|
|
prefix: "prefix",
|
|
req: &pbs.UpdateUserRequest{
|
|
Id: "prefix_something",
|
|
UpdateMask: &fieldmaskpb.FieldMask{Paths: []string{"anything"}},
|
|
},
|
|
item: &pb.User{},
|
|
valFn: NoopValidatorFn,
|
|
badFields: []string{"version"},
|
|
},
|
|
{
|
|
name: "bad create time",
|
|
prefix: "prefix",
|
|
req: &pbs.UpdateUserRequest{
|
|
Id: "prefix_something",
|
|
UpdateMask: &fieldmaskpb.FieldMask{Paths: []string{"anything"}},
|
|
},
|
|
item: &pb.User{
|
|
Version: 1,
|
|
CreatedTime: timestamppb.Now(),
|
|
},
|
|
valFn: NoopValidatorFn,
|
|
badFields: []string{"created_time"},
|
|
},
|
|
{
|
|
name: "bad updated time",
|
|
prefix: "prefix",
|
|
req: &pbs.UpdateUserRequest{
|
|
Id: "prefix_something",
|
|
UpdateMask: &fieldmaskpb.FieldMask{Paths: []string{"anything"}},
|
|
},
|
|
item: &pb.User{
|
|
Version: 1,
|
|
UpdatedTime: timestamppb.Now(),
|
|
},
|
|
valFn: NoopValidatorFn,
|
|
badFields: []string{"updated_time"},
|
|
},
|
|
{
|
|
name: "bad defined id on item",
|
|
prefix: "prefix",
|
|
req: &pbs.UpdateUserRequest{
|
|
Id: "prefix_something",
|
|
UpdateMask: &fieldmaskpb.FieldMask{Paths: []string{"anything"}},
|
|
},
|
|
item: &pb.User{
|
|
Id: "prefix_something",
|
|
Version: 1,
|
|
},
|
|
valFn: NoopValidatorFn,
|
|
badFields: []string{"id"},
|
|
},
|
|
{
|
|
name: "custom validator error",
|
|
prefix: "prefix",
|
|
req: &pbs.UpdateUserRequest{
|
|
Id: "prefix_something",
|
|
UpdateMask: &fieldmaskpb.FieldMask{Paths: []string{"anything"}},
|
|
},
|
|
item: &pb.User{
|
|
Version: 1,
|
|
},
|
|
valFn: func() map[string]string {
|
|
return map[string]string{
|
|
"test": "test",
|
|
}
|
|
},
|
|
badFields: []string{"test"},
|
|
},
|
|
{
|
|
name: "multiple",
|
|
prefix: "prefix",
|
|
req: &pbs.UpdateUserRequest{},
|
|
item: &pb.User{},
|
|
valFn: NoopValidatorFn,
|
|
badFields: []string{"id", "update_mask", "version"},
|
|
},
|
|
}
|
|
|
|
for _, tc := range cases {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
err := ValidateUpdateRequest(tc.req, tc.item, tc.valFn, tc.prefix)
|
|
if len(tc.badFields) == 0 {
|
|
if !assert.NoError(t, err) {
|
|
errorIncludesFields(t, err, []string{})
|
|
}
|
|
return
|
|
}
|
|
errorIncludesFields(t, err, tc.badFields)
|
|
})
|
|
}
|
|
}
|