mirror of
https://github.com/status-im/consul.git
synced 2025-02-11 21:27:20 +00:00
The peer name will eventually show up elsewhere in the resource. For now though this rips it out of where we don’t want it to be.
315 lines
11 KiB
Go
315 lines
11 KiB
Go
// Copyright (c) HashiCorp, Inc.
|
|
// SPDX-License-Identifier: BUSL-1.1
|
|
|
|
package resource_test
|
|
|
|
import (
|
|
"strings"
|
|
"testing"
|
|
|
|
"github.com/stretchr/testify/require"
|
|
"google.golang.org/grpc/codes"
|
|
"google.golang.org/protobuf/types/known/anypb"
|
|
|
|
svc "github.com/hashicorp/consul/agent/grpc-external/services/resource"
|
|
"github.com/hashicorp/consul/internal/resource"
|
|
"github.com/hashicorp/consul/proto-public/pbresource"
|
|
pbdemov2 "github.com/hashicorp/consul/proto/private/pbdemo/v2"
|
|
)
|
|
|
|
// Common test structs and test cases shared by the Write and MutateAndValidate RPCs
|
|
// only. These are not intended to be used by other tests.
|
|
|
|
type resourceValidTestCase struct {
|
|
modFn func(artist, recordLabel *pbresource.Resource) *pbresource.Resource
|
|
errContains string
|
|
}
|
|
|
|
func resourceValidTestCases(t *testing.T) map[string]resourceValidTestCase {
|
|
return map[string]resourceValidTestCase{
|
|
"no resource": {
|
|
modFn: func(_, _ *pbresource.Resource) *pbresource.Resource {
|
|
return nil
|
|
},
|
|
errContains: "resource is required",
|
|
},
|
|
"no id": {
|
|
modFn: func(artist, _ *pbresource.Resource) *pbresource.Resource {
|
|
artist.Id = nil
|
|
return artist
|
|
},
|
|
errContains: "resource.id is required",
|
|
},
|
|
"no type": {
|
|
modFn: func(artist, _ *pbresource.Resource) *pbresource.Resource {
|
|
artist.Id.Type = nil
|
|
return artist
|
|
},
|
|
errContains: "resource.id.type is required",
|
|
},
|
|
"no name": {
|
|
modFn: func(artist, _ *pbresource.Resource) *pbresource.Resource {
|
|
artist.Id.Name = ""
|
|
return artist
|
|
},
|
|
errContains: "resource.id.name invalid",
|
|
},
|
|
"name is mixed case": {
|
|
modFn: func(artist, _ *pbresource.Resource) *pbresource.Resource {
|
|
artist.Id.Name = "MixedCaseNotAllowed"
|
|
return artist
|
|
},
|
|
errContains: "resource.id.name invalid",
|
|
},
|
|
"name too long": {
|
|
modFn: func(artist, _ *pbresource.Resource) *pbresource.Resource {
|
|
artist.Id.Name = strings.Repeat("a", resource.MaxNameLength+1)
|
|
return artist
|
|
},
|
|
errContains: "resource.id.name invalid",
|
|
},
|
|
"wrong data type": {
|
|
modFn: func(artist, _ *pbresource.Resource) *pbresource.Resource {
|
|
var err error
|
|
artist.Data, err = anypb.New(&pbdemov2.Album{})
|
|
require.NoError(t, err)
|
|
return artist
|
|
},
|
|
errContains: "resource.data is of wrong type",
|
|
},
|
|
"partition is mixed case": {
|
|
modFn: func(artist, _ *pbresource.Resource) *pbresource.Resource {
|
|
artist.Id.Tenancy.Partition = "Default"
|
|
return artist
|
|
},
|
|
errContains: "resource.id.tenancy.partition invalid",
|
|
},
|
|
"partition too long": {
|
|
modFn: func(artist, _ *pbresource.Resource) *pbresource.Resource {
|
|
artist.Id.Tenancy.Partition = strings.Repeat("p", resource.MaxNameLength+1)
|
|
return artist
|
|
},
|
|
errContains: "resource.id.tenancy.partition invalid",
|
|
},
|
|
"namespace is mixed case": {
|
|
modFn: func(artist, _ *pbresource.Resource) *pbresource.Resource {
|
|
artist.Id.Tenancy.Namespace = "Default"
|
|
return artist
|
|
},
|
|
errContains: "resource.id.tenancy.namespace invalid",
|
|
},
|
|
"namespace too long": {
|
|
modFn: func(artist, _ *pbresource.Resource) *pbresource.Resource {
|
|
artist.Id.Tenancy.Namespace = strings.Repeat("n", resource.MaxNameLength+1)
|
|
return artist
|
|
},
|
|
errContains: "resource.id.tenancy.namespace invalid",
|
|
},
|
|
"fail validation hook": {
|
|
modFn: func(artist, _ *pbresource.Resource) *pbresource.Resource {
|
|
buffer := &pbdemov2.Artist{}
|
|
require.NoError(t, artist.Data.UnmarshalTo(buffer))
|
|
buffer.Name = "" // name cannot be empty
|
|
require.NoError(t, artist.Data.MarshalFrom(buffer))
|
|
return artist
|
|
},
|
|
errContains: "artist.name required",
|
|
},
|
|
"partition scope with non-empty namespace": {
|
|
modFn: func(_, recordLabel *pbresource.Resource) *pbresource.Resource {
|
|
recordLabel.Id.Tenancy.Namespace = "bogus"
|
|
return recordLabel
|
|
},
|
|
errContains: "cannot have a namespace",
|
|
},
|
|
}
|
|
}
|
|
|
|
type ownerValidTestCase struct {
|
|
modFn func(res *pbresource.Resource)
|
|
errorContains string
|
|
}
|
|
|
|
func ownerValidationTestCases(t *testing.T) map[string]ownerValidTestCase {
|
|
return map[string]ownerValidTestCase{
|
|
"no owner type": {
|
|
modFn: func(res *pbresource.Resource) { res.Owner.Type = nil },
|
|
errorContains: "resource.owner.type is required",
|
|
},
|
|
"no owner name": {
|
|
modFn: func(res *pbresource.Resource) { res.Owner.Name = "" },
|
|
errorContains: "resource.owner.name invalid",
|
|
},
|
|
"mixed case owner name": {
|
|
modFn: func(res *pbresource.Resource) { res.Owner.Name = strings.ToUpper(res.Owner.Name) },
|
|
errorContains: "resource.owner.name invalid",
|
|
},
|
|
"owner name too long": {
|
|
modFn: func(res *pbresource.Resource) {
|
|
res.Owner.Name = strings.Repeat("a", resource.MaxNameLength+1)
|
|
},
|
|
errorContains: "resource.owner.name invalid",
|
|
},
|
|
"owner partition is mixed case": {
|
|
modFn: func(res *pbresource.Resource) {
|
|
res.Owner.Tenancy.Partition = "Default"
|
|
},
|
|
errorContains: "resource.owner.tenancy.partition invalid",
|
|
},
|
|
"owner partition too long": {
|
|
modFn: func(res *pbresource.Resource) {
|
|
res.Owner.Tenancy.Partition = strings.Repeat("p", resource.MaxNameLength+1)
|
|
},
|
|
errorContains: "resource.owner.tenancy.partition invalid",
|
|
},
|
|
"owner namespace is mixed case": {
|
|
modFn: func(res *pbresource.Resource) {
|
|
res.Owner.Tenancy.Namespace = "Default"
|
|
},
|
|
errorContains: "resource.owner.tenancy.namespace invalid",
|
|
},
|
|
"owner namespace too long": {
|
|
modFn: func(res *pbresource.Resource) {
|
|
res.Owner.Tenancy.Namespace = strings.Repeat("n", resource.MaxNameLength+1)
|
|
},
|
|
errorContains: "resource.owner.tenancy.namespace invalid",
|
|
},
|
|
}
|
|
}
|
|
|
|
// Test case struct shared by MutateAndValidate and Write success test cases
|
|
type mavOrWriteSuccessTestCase struct {
|
|
modFn func(artist, recordLabel *pbresource.Resource) *pbresource.Resource
|
|
expectedTenancy *pbresource.Tenancy
|
|
}
|
|
|
|
// Test case struct shared by MutateAndValidate and Write success test cases
|
|
func mavOrWriteSuccessTestCases(t *testing.T) map[string]mavOrWriteSuccessTestCase {
|
|
return map[string]mavOrWriteSuccessTestCase{
|
|
"namespaced resource provides nonempty partition and namespace": {
|
|
modFn: func(artist, _ *pbresource.Resource) *pbresource.Resource {
|
|
return artist
|
|
},
|
|
expectedTenancy: resource.DefaultNamespacedTenancy(),
|
|
},
|
|
"namespaced resource inherits tokens partition when empty": {
|
|
modFn: func(artist, _ *pbresource.Resource) *pbresource.Resource {
|
|
artist.Id.Tenancy.Partition = ""
|
|
return artist
|
|
},
|
|
expectedTenancy: resource.DefaultNamespacedTenancy(),
|
|
},
|
|
"namespaced resource inherits tokens namespace when empty": {
|
|
modFn: func(artist, _ *pbresource.Resource) *pbresource.Resource {
|
|
artist.Id.Tenancy.Namespace = ""
|
|
return artist
|
|
},
|
|
expectedTenancy: resource.DefaultNamespacedTenancy(),
|
|
},
|
|
"namespaced resource inherits tokens partition and namespace when empty": {
|
|
modFn: func(artist, _ *pbresource.Resource) *pbresource.Resource {
|
|
artist.Id.Tenancy.Partition = ""
|
|
artist.Id.Tenancy.Namespace = ""
|
|
return artist
|
|
},
|
|
expectedTenancy: resource.DefaultNamespacedTenancy(),
|
|
},
|
|
"namespaced resource inherits tokens partition and namespace when tenancy nil": {
|
|
modFn: func(artist, _ *pbresource.Resource) *pbresource.Resource {
|
|
artist.Id.Tenancy = nil
|
|
return artist
|
|
},
|
|
expectedTenancy: resource.DefaultNamespacedTenancy(),
|
|
},
|
|
"partitioned resource provides nonempty partition": {
|
|
modFn: func(_, recordLabel *pbresource.Resource) *pbresource.Resource {
|
|
return recordLabel
|
|
},
|
|
expectedTenancy: resource.DefaultPartitionedTenancy(),
|
|
},
|
|
"partitioned resource inherits tokens partition when empty": {
|
|
modFn: func(_, recordLabel *pbresource.Resource) *pbresource.Resource {
|
|
recordLabel.Id.Tenancy.Partition = ""
|
|
return recordLabel
|
|
},
|
|
expectedTenancy: resource.DefaultPartitionedTenancy(),
|
|
},
|
|
"partitioned resource inherits tokens partition when tenancy nil": {
|
|
modFn: func(_, recordLabel *pbresource.Resource) *pbresource.Resource {
|
|
recordLabel.Id.Tenancy = nil
|
|
return recordLabel
|
|
},
|
|
expectedTenancy: resource.DefaultPartitionedTenancy(),
|
|
},
|
|
}
|
|
}
|
|
|
|
// Test case struct shared by MutateAndValidate and Write test cases where tenancy is not found
|
|
type mavOrWriteTenancyNotFoundTestCase map[string]struct {
|
|
modFn func(artist, recordLabel *pbresource.Resource) *pbresource.Resource
|
|
errCode codes.Code
|
|
errContains string
|
|
}
|
|
|
|
// Test case struct shared by MutateAndValidate and Write test cases where tenancy is not found
|
|
func mavOrWriteTenancyNotFoundTestCases(t *testing.T) mavOrWriteTenancyNotFoundTestCase {
|
|
return mavOrWriteTenancyNotFoundTestCase{
|
|
"namespaced resource provides nonexistant partition": {
|
|
modFn: func(artist, _ *pbresource.Resource) *pbresource.Resource {
|
|
artist.Id.Tenancy.Partition = "boguspartition"
|
|
return artist
|
|
},
|
|
errCode: codes.InvalidArgument,
|
|
errContains: "partition not found",
|
|
},
|
|
"namespaced resource provides nonexistant namespace": {
|
|
modFn: func(artist, _ *pbresource.Resource) *pbresource.Resource {
|
|
artist.Id.Tenancy.Namespace = "bogusnamespace"
|
|
return artist
|
|
},
|
|
errCode: codes.InvalidArgument,
|
|
errContains: "namespace not found",
|
|
},
|
|
"partitioned resource provides nonexistant partition": {
|
|
modFn: func(_, recordLabel *pbresource.Resource) *pbresource.Resource {
|
|
recordLabel.Id.Tenancy.Partition = "boguspartition"
|
|
return recordLabel
|
|
},
|
|
errCode: codes.InvalidArgument,
|
|
errContains: "partition not found",
|
|
},
|
|
}
|
|
}
|
|
|
|
type mavOrWriteTenancyMarkedForDeletionTestCase struct {
|
|
modFn func(artist, recordLabel *pbresource.Resource, mockTenancyBridge *svc.MockTenancyBridge) *pbresource.Resource
|
|
errContains string
|
|
}
|
|
|
|
func mavOrWriteTenancyMarkedForDeletionTestCases(t *testing.T) map[string]mavOrWriteTenancyMarkedForDeletionTestCase {
|
|
return map[string]mavOrWriteTenancyMarkedForDeletionTestCase{
|
|
"namespaced resources partition marked for deletion": {
|
|
modFn: func(artist, _ *pbresource.Resource, mockTenancyBridge *svc.MockTenancyBridge) *pbresource.Resource {
|
|
mockTenancyBridge.On("IsPartitionMarkedForDeletion", "ap1").Return(true, nil)
|
|
return artist
|
|
},
|
|
errContains: "tenancy marked for deletion",
|
|
},
|
|
"namespaced resources namespace marked for deletion": {
|
|
modFn: func(artist, _ *pbresource.Resource, mockTenancyBridge *svc.MockTenancyBridge) *pbresource.Resource {
|
|
mockTenancyBridge.On("IsPartitionMarkedForDeletion", "ap1").Return(false, nil)
|
|
mockTenancyBridge.On("IsNamespaceMarkedForDeletion", "ap1", "ns1").Return(true, nil)
|
|
return artist
|
|
},
|
|
errContains: "tenancy marked for deletion",
|
|
},
|
|
"partitioned resources partition marked for deletion": {
|
|
modFn: func(_, recordLabel *pbresource.Resource, mockTenancyBridge *svc.MockTenancyBridge) *pbresource.Resource {
|
|
mockTenancyBridge.On("IsPartitionMarkedForDeletion", "ap1").Return(true, nil)
|
|
return recordLabel
|
|
},
|
|
errContains: "tenancy marked for deletion",
|
|
},
|
|
}
|
|
}
|