consul/agent/grpc-external/services/resource/mutate_and_validate_test.go

213 lines
6.9 KiB
Go

// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: BUSL-1.1
package resource_test
import (
"fmt"
"testing"
"github.com/stretchr/testify/require"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
svc "github.com/hashicorp/consul/agent/grpc-external/services/resource"
svctest "github.com/hashicorp/consul/agent/grpc-external/services/resource/testing"
"github.com/hashicorp/consul/internal/resource/demo"
"github.com/hashicorp/consul/proto-public/pbresource"
pbdemov2 "github.com/hashicorp/consul/proto/private/pbdemo/v2"
"github.com/hashicorp/consul/proto/private/prototest"
)
func TestMutateAndValidate_InputValidation(t *testing.T) {
run := func(t *testing.T, client pbresource.ResourceServiceClient, tc resourceValidTestCase) {
artist, err := demo.GenerateV2Artist()
require.NoError(t, err)
recordLabel, err := demo.GenerateV1RecordLabel("looney-tunes")
require.NoError(t, err)
req := &pbresource.MutateAndValidateRequest{Resource: tc.modFn(artist, recordLabel)}
_, err = client.MutateAndValidate(testContext(t), req)
require.Error(t, err)
require.Equal(t, codes.InvalidArgument.String(), status.Code(err).String())
require.ErrorContains(t, err, tc.errContains)
}
for _, v2tenancy := range []bool{false, true} {
t.Run(fmt.Sprintf("v2tenancy %v", v2tenancy), func(t *testing.T) {
client := svctest.NewResourceServiceBuilder().
WithRegisterFns(demo.RegisterTypes).
WithV2Tenancy(v2tenancy).
Run(t)
for desc, tc := range resourceValidTestCases(t) {
t.Run(desc, func(t *testing.T) {
run(t, client, tc)
})
}
})
}
}
func TestMutateAndValidate_OwnerValidation(t *testing.T) {
run := func(t *testing.T, client pbresource.ResourceServiceClient, tc ownerValidTestCase) {
artist, err := demo.GenerateV2Artist()
require.NoError(t, err)
album, err := demo.GenerateV2Album(artist.Id)
require.NoError(t, err)
tc.modFn(album)
_, err = client.MutateAndValidate(testContext(t), &pbresource.MutateAndValidateRequest{Resource: album})
require.Error(t, err)
require.Equal(t, codes.InvalidArgument.String(), status.Code(err).String())
require.ErrorContains(t, err, tc.errorContains)
}
for _, v2tenancy := range []bool{false, true} {
t.Run(fmt.Sprintf("v2tenancy %v", v2tenancy), func(t *testing.T) {
client := svctest.NewResourceServiceBuilder().
WithRegisterFns(demo.RegisterTypes).
WithV2Tenancy(v2tenancy).
Run(t)
for desc, tc := range ownerValidationTestCases(t) {
t.Run(desc, func(t *testing.T) {
run(t, client, tc)
})
}
})
}
}
func TestMutateAndValidate_TypeNotFound(t *testing.T) {
run := func(t *testing.T, client pbresource.ResourceServiceClient) {
res, err := demo.GenerateV2Artist()
require.NoError(t, err)
_, err = client.MutateAndValidate(testContext(t), &pbresource.MutateAndValidateRequest{Resource: res})
require.Error(t, err)
require.Equal(t, codes.InvalidArgument.String(), status.Code(err).String())
require.Contains(t, err.Error(), "resource type demo.v2.Artist not registered")
}
for _, v2tenancy := range []bool{false, true} {
t.Run(fmt.Sprintf("v2tenancy %v", v2tenancy), func(t *testing.T) {
client := svctest.NewResourceServiceBuilder().WithV2Tenancy(v2tenancy).Run(t)
run(t, client)
})
}
}
func TestMutateAndValidate_Success(t *testing.T) {
run := func(t *testing.T, client pbresource.ResourceServiceClient, tc mavOrWriteSuccessTestCase) {
recordLabel, err := demo.GenerateV1RecordLabel("looney-tunes")
require.NoError(t, err)
artist, err := demo.GenerateV2Artist()
require.NoError(t, err)
rsp, err := client.MutateAndValidate(testContext(t), &pbresource.MutateAndValidateRequest{Resource: tc.modFn(artist, recordLabel)})
require.NoError(t, err)
prototest.AssertDeepEqual(t, tc.expectedTenancy, rsp.Resource.Id.Tenancy)
}
for _, v2tenancy := range []bool{false, true} {
t.Run(fmt.Sprintf("v2tenancy %v", v2tenancy), func(t *testing.T) {
client := svctest.NewResourceServiceBuilder().
WithRegisterFns(demo.RegisterTypes).
WithV2Tenancy(v2tenancy).
Run(t)
for desc, tc := range mavOrWriteSuccessTestCases(t) {
t.Run(desc, func(t *testing.T) {
run(t, client, tc)
})
}
})
}
}
func TestMutateAndValidate_Mutate(t *testing.T) {
for _, v2tenancy := range []bool{false, true} {
t.Run(fmt.Sprintf("v2tenancy %v", v2tenancy), func(t *testing.T) {
client := svctest.NewResourceServiceBuilder().
WithRegisterFns(demo.RegisterTypes).
WithV2Tenancy(v2tenancy).
Run(t)
artist, err := demo.GenerateV2Artist()
require.NoError(t, err)
artistData := &pbdemov2.Artist{}
artist.Data.UnmarshalTo(artistData)
require.NoError(t, err)
// mutate hook sets genre to disco when unspecified
artistData.Genre = pbdemov2.Genre_GENRE_UNSPECIFIED
artist.Data.MarshalFrom(artistData)
require.NoError(t, err)
rsp, err := client.MutateAndValidate(testContext(t), &pbresource.MutateAndValidateRequest{Resource: artist})
require.NoError(t, err)
// verify mutate hook set genre to disco
require.NoError(t, rsp.Resource.Data.UnmarshalTo(artistData))
require.Equal(t, pbdemov2.Genre_GENRE_DISCO, artistData.Genre)
})
}
}
func TestMutateAndValidate_Tenancy_NotFound(t *testing.T) {
for desc, tc := range mavOrWriteTenancyNotFoundTestCases(t) {
t.Run(desc, func(t *testing.T) {
client := svctest.NewResourceServiceBuilder().
WithV2Tenancy(true).
WithRegisterFns(demo.RegisterTypes).
Run(t)
recordLabel, err := demo.GenerateV1RecordLabel("looney-tunes")
require.NoError(t, err)
artist, err := demo.GenerateV2Artist()
require.NoError(t, err)
_, err = client.MutateAndValidate(testContext(t), &pbresource.MutateAndValidateRequest{Resource: tc.modFn(artist, recordLabel)})
require.Error(t, err)
require.Equal(t, codes.InvalidArgument.String(), status.Code(err).String())
require.Contains(t, err.Error(), tc.errContains)
})
}
}
func TestMutateAndValidate_TenancyMarkedForDeletion_Fails(t *testing.T) {
for desc, tc := range mavOrWriteTenancyMarkedForDeletionTestCases(t) {
t.Run(desc, func(t *testing.T) {
server := testServer(t)
client := testClient(t, server)
demo.RegisterTypes(server.Registry)
recordLabel, err := demo.GenerateV1RecordLabel("looney-tunes")
require.NoError(t, err)
recordLabel.Id.Tenancy.Partition = "ap1"
artist, err := demo.GenerateV2Artist()
require.NoError(t, err)
artist.Id.Tenancy.Partition = "ap1"
artist.Id.Tenancy.Namespace = "ns1"
mockTenancyBridge := &svc.MockTenancyBridge{}
mockTenancyBridge.On("PartitionExists", "ap1").Return(true, nil)
mockTenancyBridge.On("NamespaceExists", "ap1", "ns1").Return(true, nil)
server.TenancyBridge = mockTenancyBridge
_, err = client.MutateAndValidate(testContext(t), &pbresource.MutateAndValidateRequest{Resource: tc.modFn(artist, recordLabel, mockTenancyBridge)})
require.Error(t, err)
require.Equal(t, codes.InvalidArgument.String(), status.Code(err).String())
require.Contains(t, err.Error(), tc.errContains)
})
}
}