diff --git a/internal/api/promote_downstream_v1alpha1_test.go b/internal/api/promote_downstream_v1alpha1_test.go index 1c30b91fc2..af37308696 100644 --- a/internal/api/promote_downstream_v1alpha1_test.go +++ b/internal/api/promote_downstream_v1alpha1_test.go @@ -30,6 +30,7 @@ func TestPromoteDownstream(t *testing.T) { }, }}, } + testCases := []struct { name string req *svcv1alpha1.PromoteDownstreamRequest @@ -74,8 +75,7 @@ func TestPromoteDownstream(t *testing.T) { _ *connect.Response[svcv1alpha1.PromoteDownstreamResponse], err error, ) { - require.Error(t, err) - require.Equal(t, "something went wrong", err.Error()) + require.ErrorContains(t, err, "something went wrong") }, }, { @@ -103,8 +103,7 @@ func TestPromoteDownstream(t *testing.T) { _ *connect.Response[svcv1alpha1.PromoteDownstreamResponse], err error, ) { - require.Error(t, err) - require.Equal(t, "get stage: something went wrong", err.Error()) + require.ErrorContains(t, err, "get stage: something went wrong") }, }, { @@ -174,8 +173,7 @@ func TestPromoteDownstream(t *testing.T) { _ *connect.Response[svcv1alpha1.PromoteDownstreamResponse], err error, ) { - require.Error(t, err) - require.Equal(t, "get freight: something went wrong", err.Error()) + require.ErrorContains(t, err, "get freight: something went wrong") }, }, { @@ -313,8 +311,7 @@ func TestPromoteDownstream(t *testing.T) { _ *connect.Response[svcv1alpha1.PromoteDownstreamResponse], err error, ) { - require.Error(t, err) - require.Equal(t, "find downstream stages: something went wrong", err.Error()) + require.ErrorContains(t, err, "find downstream stages: something went wrong") }, }, { @@ -438,8 +435,87 @@ func TestPromoteDownstream(t *testing.T) { _ *connect.Response[svcv1alpha1.PromoteDownstreamResponse], err error, ) { - require.Error(t, err) - require.Equal(t, "not authorized", err.Error()) + require.ErrorContains(t, err, "not authorized") + }, + }, + { + name: "error building Promotion", + req: &svcv1alpha1.PromoteDownstreamRequest{ + Project: "fake-project", + Stage: "fake-stage", + Freight: "fake-freight", + }, + server: &server{ + validateProjectExistsFn: func(context.Context, string) error { + return nil + }, + getStageFn: func( + context.Context, + client.Client, + types.NamespacedName, + ) (*kargoapi.Stage, error) { + return &kargoapi.Stage{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: "fake-project", + Name: "fake-stage", + }, + Spec: testStageSpec, + }, nil + }, + getFreightByNameOrAliasFn: func( + context.Context, + client.Client, + string, string, string, + ) (*kargoapi.Freight, error) { + return &kargoapi.Freight{ + Status: kargoapi.FreightStatus{ + VerifiedIn: map[string]kargoapi.VerifiedStage{ + "fake-stage": {}, + }, + }, + }, nil + }, + findDownstreamStagesFn: func( + context.Context, + *kargoapi.Stage, + kargoapi.FreightOrigin, + ) ([]kargoapi.Stage, error) { + return []kargoapi.Stage{ + { + Spec: kargoapi.StageSpec{ + PromotionTemplate: &kargoapi.PromotionTemplate{ + Spec: kargoapi.PromotionTemplateSpec{ + Steps: []kargoapi.PromotionStep{{}}, + }, + }, + }, + }, + }, nil + }, + authorizeFn: func( + context.Context, + string, + schema.GroupVersionResource, + string, + client.ObjectKey, + ) error { + return nil + }, + createPromotionFn: func( + context.Context, + client.Object, + ...client.CreateOption, + ) error { + return nil + }, + }, + assertions: func( + t *testing.T, + _ *fakeevent.EventRecorder, + _ *connect.Response[svcv1alpha1.PromoteDownstreamResponse], + err error, + ) { + require.ErrorContains(t, err, "stage is required") }, }, { @@ -472,6 +548,10 @@ func TestPromoteDownstream(t *testing.T) { string, string, string, ) (*kargoapi.Freight, error) { return &kargoapi.Freight{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: "fake-project", + Name: "fake-freight", + }, Status: kargoapi.FreightStatus{ VerifiedIn: map[string]kargoapi.VerifiedStage{ "fake-stage": {}, @@ -486,6 +566,10 @@ func TestPromoteDownstream(t *testing.T) { ) ([]kargoapi.Stage, error) { return []kargoapi.Stage{ { + ObjectMeta: metav1.ObjectMeta{ + Namespace: "fake-project", + Name: "fake-downstream-stage", + }, Spec: kargoapi.StageSpec{ PromotionTemplate: &kargoapi.PromotionTemplate{ Spec: kargoapi.PromotionTemplateSpec{ @@ -556,6 +640,10 @@ func TestPromoteDownstream(t *testing.T) { string, string, string, ) (*kargoapi.Freight, error) { return &kargoapi.Freight{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: "fake-project", + Name: "fake-freight", + }, Status: kargoapi.FreightStatus{ VerifiedIn: map[string]kargoapi.VerifiedStage{ "fake-stage": {}, @@ -570,6 +658,10 @@ func TestPromoteDownstream(t *testing.T) { ) ([]kargoapi.Stage, error) { return []kargoapi.Stage{ { + ObjectMeta: metav1.ObjectMeta{ + Namespace: "fake-project", + Name: "fake-downstream-stage", + }, Spec: kargoapi.StageSpec{ PromotionTemplate: &kargoapi.PromotionTemplate{ Spec: kargoapi.PromotionTemplateSpec{ diff --git a/internal/api/promote_to_stage_v1alpha1_test.go b/internal/api/promote_to_stage_v1alpha1_test.go index 99fb960a0f..bd1fdcb78f 100644 --- a/internal/api/promote_to_stage_v1alpha1_test.go +++ b/internal/api/promote_to_stage_v1alpha1_test.go @@ -8,6 +8,7 @@ import ( "connectrpc.com/connect" "github.com/stretchr/testify/require" corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/types" "sigs.k8s.io/controller-runtime/pkg/client" @@ -28,6 +29,11 @@ func TestPromoteToStage(t *testing.T) { Stages: []string{"fake-upstream-stage"}, }, }}, + PromotionTemplate: &kargoapi.PromotionTemplate{ + Spec: kargoapi.PromotionTemplateSpec{ + Steps: []kargoapi.PromotionStep{{}}, + }, + }, } testCases := []struct { name string @@ -74,8 +80,7 @@ func TestPromoteToStage(t *testing.T) { _ *connect.Response[svcv1alpha1.PromoteToStageResponse], err error, ) { - require.Error(t, err) - require.Equal(t, "something went wrong", err.Error()) + require.ErrorContains(t, err, "something went wrong") }, }, { @@ -103,8 +108,7 @@ func TestPromoteToStage(t *testing.T) { _ *connect.Response[svcv1alpha1.PromoteToStageResponse], err error, ) { - require.Error(t, err) - require.Equal(t, "get stage: something went wrong", err.Error()) + require.ErrorContains(t, err, "get stage: something went wrong") }, }, { @@ -174,8 +178,7 @@ func TestPromoteToStage(t *testing.T) { _ *connect.Response[svcv1alpha1.PromoteToStageResponse], err error, ) { - require.Error(t, err) - require.Equal(t, "get freight: something went wrong", err.Error()) + require.ErrorContains(t, err, "get freight: something went wrong") }, }, { @@ -313,12 +316,11 @@ func TestPromoteToStage(t *testing.T) { _ *connect.Response[svcv1alpha1.PromoteToStageResponse], err error, ) { - require.Error(t, err) - require.Equal(t, "not authorized", err.Error()) + require.Error(t, err, "not authorized") }, }, { - name: "error creating Promotion", + name: "error building Promotion", req: &svcv1alpha1.PromoteToStageRequest{ Project: "fake-project", Stage: "fake-stage", @@ -334,6 +336,10 @@ func TestPromoteToStage(t *testing.T) { types.NamespacedName, ) (*kargoapi.Stage, error) { return &kargoapi.Stage{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: "fake-project", + Name: "fake-stage", + }, Spec: testStageSpec, }, nil }, @@ -356,6 +362,64 @@ func TestPromoteToStage(t *testing.T) { ) error { return nil }, + }, + assertions: func( + t *testing.T, + _ *fakeevent.EventRecorder, + _ *connect.Response[svcv1alpha1.PromoteToStageResponse], + err error, + ) { + require.ErrorContains(t, err, "build promotion") + }, + }, + { + name: "error creating Promotion", + req: &svcv1alpha1.PromoteToStageRequest{ + Project: "fake-project", + Stage: "fake-stage", + Freight: "fake-freight", + }, + server: &server{ + validateProjectExistsFn: func(context.Context, string) error { + return nil + }, + getStageFn: func( + context.Context, + client.Client, + types.NamespacedName, + ) (*kargoapi.Stage, error) { + return &kargoapi.Stage{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: "fake-project", + Name: "fake-stage", + }, + Spec: testStageSpec, + }, nil + }, + getFreightByNameOrAliasFn: func( + context.Context, + client.Client, + string, string, string, + ) (*kargoapi.Freight, error) { + return &kargoapi.Freight{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: "fake-project", + Name: "fake-freight", + }, + }, nil + }, + isFreightAvailableFn: func(*kargoapi.Stage, *kargoapi.Freight) bool { + return true + }, + authorizeFn: func( + context.Context, + string, + schema.GroupVersionResource, + string, + client.ObjectKey, + ) error { + return nil + }, createPromotionFn: func( context.Context, client.Object, @@ -370,8 +434,7 @@ func TestPromoteToStage(t *testing.T) { _ *connect.Response[svcv1alpha1.PromoteToStageResponse], err error, ) { - require.Error(t, err) - require.Equal(t, "create promotion: something went wrong", err.Error()) + require.Error(t, err, "create promotion: something went wrong") }, }, { @@ -391,6 +454,10 @@ func TestPromoteToStage(t *testing.T) { types.NamespacedName, ) (*kargoapi.Stage, error) { return &kargoapi.Stage{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: "fake-project", + Name: "fake-stage", + }, Spec: testStageSpec, }, nil }, @@ -399,7 +466,12 @@ func TestPromoteToStage(t *testing.T) { client.Client, string, string, string, ) (*kargoapi.Freight, error) { - return &kargoapi.Freight{}, nil + return &kargoapi.Freight{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: "fake-project", + Name: "fake-freight", + }, + }, nil }, isFreightAvailableFn: func(*kargoapi.Stage, *kargoapi.Freight) bool { return true diff --git a/internal/controller/stages/regular_stages_test.go b/internal/controller/stages/regular_stages_test.go index 0d87a194be..ab038a6de0 100644 --- a/internal/controller/stages/regular_stages_test.go +++ b/internal/controller/stages/regular_stages_test.go @@ -4342,6 +4342,15 @@ func TestRegularStageReconciler_autoPromoteFreight(t *testing.T) { }, }, }, + PromotionTemplate: &kargoapi.PromotionTemplate{ + Spec: kargoapi.PromotionTemplateSpec{ + Steps: []kargoapi.PromotionStep{ + { + Uses: "fake-step", + }, + }, + }, + }, }, }, objects: []client.Object{ @@ -4555,6 +4564,15 @@ func TestRegularStageReconciler_autoPromoteFreight(t *testing.T) { }, }, }, + PromotionTemplate: &kargoapi.PromotionTemplate{ + Spec: kargoapi.PromotionTemplateSpec{ + Steps: []kargoapi.PromotionStep{ + { + Uses: "fake-step", + }, + }, + }, + }, }, }, objects: []client.Object{ @@ -4616,6 +4634,15 @@ func TestRegularStageReconciler_autoPromoteFreight(t *testing.T) { }, }, }, + PromotionTemplate: &kargoapi.PromotionTemplate{ + Spec: kargoapi.PromotionTemplateSpec{ + Steps: []kargoapi.PromotionStep{ + { + Uses: "fake-step", + }, + }, + }, + }, }, }, objects: []client.Object{ @@ -4693,6 +4720,15 @@ func TestRegularStageReconciler_autoPromoteFreight(t *testing.T) { }, }, }, + PromotionTemplate: &kargoapi.PromotionTemplate{ + Spec: kargoapi.PromotionTemplateSpec{ + Steps: []kargoapi.PromotionStep{ + { + Uses: "fake-step", + }, + }, + }, + }, }, }, objects: []client.Object{ @@ -4777,6 +4813,15 @@ func TestRegularStageReconciler_autoPromoteFreight(t *testing.T) { }, }, }, + PromotionTemplate: &kargoapi.PromotionTemplate{ + Spec: kargoapi.PromotionTemplateSpec{ + Steps: []kargoapi.PromotionStep{ + { + Uses: "fake-step", + }, + }, + }, + }, }, }, objects: []client.Object{ @@ -4842,6 +4887,15 @@ func TestRegularStageReconciler_autoPromoteFreight(t *testing.T) { }, }, }, + PromotionTemplate: &kargoapi.PromotionTemplate{ + Spec: kargoapi.PromotionTemplateSpec{ + Steps: []kargoapi.PromotionStep{ + { + Uses: "fake-step", + }, + }, + }, + }, }, }, objects: []client.Object{ @@ -4890,6 +4944,66 @@ func TestRegularStageReconciler_autoPromoteFreight(t *testing.T) { assert.Len(t, promoList.Items, 1) }, }, + { + name: "handles promotion build error", + stage: &kargoapi.Stage{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: "fake-project", + Name: "test-stage", + }, + Spec: kargoapi.StageSpec{ + RequestedFreight: []kargoapi.FreightRequest{ + { + Origin: kargoapi.FreightOrigin{ + Kind: kargoapi.FreightOriginKindWarehouse, + Name: "test-warehouse", + }, + Sources: kargoapi.FreightSources{ + Direct: true, + }, + }, + }, + // Missing template to build Promotion + PromotionTemplate: nil, + }, + }, + objects: []client.Object{ + &kargoapi.Project{ + ObjectMeta: metav1.ObjectMeta{ + Name: "fake-project", + }, + Spec: &kargoapi.ProjectSpec{ + PromotionPolicies: []kargoapi.PromotionPolicy{ + { + Stage: "test-stage", + AutoPromotionEnabled: true, + }, + }, + }, + }, + &kargoapi.Freight{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: "fake-project", + Name: "test-freight", + CreationTimestamp: metav1.Time{Time: now}, + }, + Origin: kargoapi.FreightOrigin{ + Kind: kargoapi.FreightOriginKindWarehouse, + Name: "test-warehouse", + }, + Status: kargoapi.FreightStatus{}, + }, + }, + assertions: func( + t *testing.T, + _ *fakeevent.EventRecorder, + _ client.Client, + _ kargoapi.StageStatus, + err error, + ) { + require.ErrorContains(t, err, "error building Promotion") + }, + }, { name: "handles promotion creation error", stage: &kargoapi.Stage{ @@ -4909,6 +5023,15 @@ func TestRegularStageReconciler_autoPromoteFreight(t *testing.T) { }, }, }, + PromotionTemplate: &kargoapi.PromotionTemplate{ + Spec: kargoapi.PromotionTemplateSpec{ + Steps: []kargoapi.PromotionStep{ + { + Uses: "fake-step", + }, + }, + }, + }, }, }, objects: []client.Object{