@@ -23,7 +23,9 @@ import (
2323 "github.com/stretchr/testify/assert"
2424 "github.com/stretchr/testify/require"
2525 "go.uber.org/mock/gomock"
26+ "google.golang.org/grpc/codes"
2627 "google.golang.org/grpc/metadata"
28+ "google.golang.org/grpc/status"
2729 "google.golang.org/protobuf/types/known/wrapperspb"
2830
2931 "github.com/bucketeer-io/bucketeer/v2/pkg/api/api"
@@ -619,6 +621,14 @@ func TestConvUpdateFeatureError(t *testing.T) {
619621 err := fs .convUpdateFeatureError (p .input )
620622 assert .Equal (t , p .expectedErr , err )
621623 }
624+
625+ // UpdateFeature relies on this conversion to surface domain validation
626+ // errors (e.g. variation value schema violations) as InvalidArgument
627+ // with structured details instead of Unknown without details.
628+ fs := & FeatureService {}
629+ schemaErr := fs .convUpdateFeatureError (pkgErr .NewErrorInvalidArgNotMatchFormat (
630+ pkgErr .FeaturePackageName , "feature: variation value does not match schema" , "VariationValueSchema" ))
631+ assert .Equal (t , codes .InvalidArgument , status .Code (schemaErr ))
622632}
623633
624634func TestEvaluateFeatures (t * testing.T ) {
@@ -3012,6 +3022,80 @@ func TestUpdateFeature(t *testing.T) {
30123022 },
30133023 expectedErr : statusInvalidArchive .Err (),
30143024 },
3025+ {
3026+ // Regression test: domain validation errors from the update
3027+ // transaction must surface as InvalidArgument with structured
3028+ // details, not as codes.Unknown (HTTP 500).
3029+ desc : "fail: variation value schema violation returns InvalidArgument" ,
3030+ setup : func (s * FeatureService ) {
3031+ vID1 := newUUID (t )
3032+ vID2 := newUUID (t )
3033+ s .experimentClient .(* exprclientmock.MockClient ).EXPECT ().ListExperiments (gomock .Any (), gomock .Any ()).Return (
3034+ & exprproto.ListExperimentsResponse {},
3035+ nil ,
3036+ )
3037+ s .environmentClient .(* envclientmock.MockClient ).EXPECT ().GetEnvironmentV2 (
3038+ gomock .Any (),
3039+ & envproto.GetEnvironmentV2Request {Id : "eid" },
3040+ ).Return (
3041+ & envproto.GetEnvironmentV2Response {Environment : & envproto.EnvironmentV2 {RequireComment : true }},
3042+ nil ,
3043+ )
3044+ // Run the real transaction callback so the domain error
3045+ // propagates through UpdateFeature's error conversion.
3046+ s .dbClient .(* databasemock.MockClient ).EXPECT ().RunInTransactionV2 (
3047+ gomock .Any (), gomock .Any (),
3048+ ).DoAndReturn (func (ctx context.Context , fn func (ctx context.Context ) error ) error {
3049+ return fn (ctx )
3050+ })
3051+ s .featureStorage .(* mock.MockFeatureStorage ).EXPECT ().ListFeatures (
3052+ gomock .Any (), gomock .Any (),
3053+ ).Return ([]* featureproto.Feature {
3054+ {
3055+ Id : "fid" ,
3056+ VariationType : featureproto .Feature_STRING ,
3057+ Variations : []* featureproto.Variation {
3058+ {
3059+ Id : vID1 ,
3060+ Value : "true" ,
3061+ Name : "true" ,
3062+ },
3063+ {
3064+ Id : vID2 ,
3065+ Value : "false" ,
3066+ Name : "false" ,
3067+ },
3068+ },
3069+ OffVariation : vID2 ,
3070+ DefaultStrategy : & featureproto.Strategy {
3071+ Type : featureproto .Strategy_FIXED ,
3072+ FixedStrategy : & featureproto.FixedStrategy {
3073+ Variation : vID1 ,
3074+ },
3075+ },
3076+ Tags : []string {"test" },
3077+ },
3078+ }, 0 , int64 (0 ), nil )
3079+ },
3080+ ctx : createContextWithToken (),
3081+ input : & featureproto.UpdateFeatureRequest {
3082+ EnvironmentId : "eid" ,
3083+ Comment : "comment" ,
3084+ Id : "fid" ,
3085+ // The existing variation values (true/false) are not in the
3086+ // enum, so the schema update must be rejected.
3087+ VariationValueSchema : & featureproto.VariationValueSchema {
3088+ Type : featureproto .VariationValueSchema_ENUM ,
3089+ Validator : & featureproto.VariationValueSchema_EnumValidator_ {
3090+ EnumValidator : & featureproto.VariationValueSchema_EnumValidator {
3091+ Values : []string {"a" , "b" },
3092+ },
3093+ },
3094+ },
3095+ },
3096+ expectedErr : api .NewGRPCStatus (pkgErr .NewErrorInvalidArgNotMatchFormat (
3097+ pkgErr .FeaturePackageName , "feature: variation value does not match schema" , "VariationValueSchema" )).Err (),
3098+ },
30153099 {
30163100 desc : "success" ,
30173101 setup : func (s * FeatureService ) {
0 commit comments