diff --git a/core/rule/service.go b/core/rule/service.go index 645d65d3..392baa9e 100644 --- a/core/rule/service.go +++ b/core/rule/service.go @@ -127,13 +127,16 @@ func (s *Service) Upsert(ctx context.Context, rl *Rule) error { return err } - if prov.Type == provider.TypeCortex { + switch prov.Type { + case provider.TypeCortex: if err = PostRuleGroupWithCortex(ctx, s.cortexClient, s.templateService, rl.Namespace, rl.GroupName, ns.URN, rulesWithinGroup); err != nil { if err := s.repository.Rollback(ctx, err); err != nil { return err } return err } + default: + return errors.ErrInvalid.WithCausef("unknown provider type %s", prov.Type) } if err := s.repository.Commit(ctx); err != nil { @@ -168,8 +171,7 @@ func PostRuleGroupWithCortex(ctx context.Context, client CortexClient, templateS } if renderedBodyForThisGroup == "" { - err := client.DeleteRuleGroup(cortex.NewContext(ctx, tenantName), nspace, groupName) - if err != nil { + if err := client.DeleteRuleGroup(cortex.NewContext(ctx, tenantName), nspace, groupName); err != nil { if err.Error() == "requested resource not found" { return nil } diff --git a/core/rule/service_test.go b/core/rule/service_test.go index f64a3343..b3132c4d 100644 --- a/core/rule/service_test.go +++ b/core/rule/service_test.go @@ -18,7 +18,7 @@ func TestService_Upsert(t *testing.T) { type testCase struct { Description string Rule *rule.Rule - Setup func(*mocks.RuleRepository, *mocks.TemplateService, *mocks.NamespaceService, *mocks.ProviderService) + Setup func(*mocks.RuleRepository, *mocks.TemplateService, *mocks.NamespaceService, *mocks.ProviderService, *mocks.CortexClient) ErrString string } var ( @@ -38,7 +38,7 @@ func TestService_Upsert(t *testing.T) { }, }, }, - Setup: func(rr *mocks.RuleRepository, ts *mocks.TemplateService, ns *mocks.NamespaceService, ps *mocks.ProviderService) { + Setup: func(rr *mocks.RuleRepository, ts *mocks.TemplateService, ns *mocks.NamespaceService, ps *mocks.ProviderService, cc *mocks.CortexClient) { ts.EXPECT().GetByName(mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("string")).Return(nil, errors.New("some error")) }, ErrString: "some error", @@ -57,7 +57,7 @@ func TestService_Upsert(t *testing.T) { }, }, }, - Setup: func(rr *mocks.RuleRepository, ts *mocks.TemplateService, ns *mocks.NamespaceService, ps *mocks.ProviderService) { + Setup: func(rr *mocks.RuleRepository, ts *mocks.TemplateService, ns *mocks.NamespaceService, ps *mocks.ProviderService, cc *mocks.CortexClient) { ts.EXPECT().GetByName(mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("string")).Return(&template.Template{}, nil) ns.EXPECT().Get(mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("uint64")).Return(nil, errors.New("some error")) }, @@ -77,7 +77,7 @@ func TestService_Upsert(t *testing.T) { }, }, }, - Setup: func(rr *mocks.RuleRepository, ts *mocks.TemplateService, ns *mocks.NamespaceService, ps *mocks.ProviderService) { + Setup: func(rr *mocks.RuleRepository, ts *mocks.TemplateService, ns *mocks.NamespaceService, ps *mocks.ProviderService, cc *mocks.CortexClient) { ts.EXPECT().GetByName(mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("string")).Return(&template.Template{}, nil) ns.EXPECT().Get(mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("uint64")).Return(&namespace.Namespace{}, nil) ps.EXPECT().Get(mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("uint64")).Return(nil, errors.New("some error")) @@ -85,7 +85,7 @@ func TestService_Upsert(t *testing.T) { ErrString: "some error", }, { - Description: "should return error if list repository return error", + Description: "should return error if upsert repository return error", Rule: &rule.Rule{ Name: "foo", Namespace: "namespace", @@ -98,19 +98,20 @@ func TestService_Upsert(t *testing.T) { }, }, }, - Setup: func(rr *mocks.RuleRepository, ts *mocks.TemplateService, ns *mocks.NamespaceService, ps *mocks.ProviderService) { + Setup: func(rr *mocks.RuleRepository, ts *mocks.TemplateService, ns *mocks.NamespaceService, ps *mocks.ProviderService, cc *mocks.CortexClient) { ts.EXPECT().GetByName(mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("string")).Return(&template.Template{}, nil) ns.EXPECT().Get(mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("uint64")).Return(&namespace.Namespace{}, nil) ps.EXPECT().Get(mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("uint64")).Return(&provider.Provider{}, nil) rr.EXPECT().WithTransaction(ctx).Return(ctx) - rr.EXPECT().Upsert(mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("*rule.Rule")).Return(errors.New("some error")) - rr.EXPECT().Rollback(ctx, errors.New("some error")).Return(nil) + rr.EXPECT().Upsert(mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("*rule.Rule")).Return(nil) + rr.EXPECT().List(mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("rule.Filter")).Return(nil, errors.New("some error")) + rr.EXPECT().Rollback(ctx, mock.Anything).Return(nil) }, ErrString: "some error", }, { - Description: "should return error if upsert repository return error", + Description: "should return error if upsert repository return error and rollback error", Rule: &rule.Rule{ Name: "foo", Namespace: "namespace", @@ -123,7 +124,7 @@ func TestService_Upsert(t *testing.T) { }, }, }, - Setup: func(rr *mocks.RuleRepository, ts *mocks.TemplateService, ns *mocks.NamespaceService, ps *mocks.ProviderService) { + Setup: func(rr *mocks.RuleRepository, ts *mocks.TemplateService, ns *mocks.NamespaceService, ps *mocks.ProviderService, cc *mocks.CortexClient) { ts.EXPECT().GetByName(mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("string")).Return(&template.Template{}, nil) ns.EXPECT().Get(mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("uint64")).Return(&namespace.Namespace{}, nil) ps.EXPECT().Get(mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("uint64")).Return(&provider.Provider{}, nil) @@ -131,10 +132,60 @@ func TestService_Upsert(t *testing.T) { rr.EXPECT().WithTransaction(ctx).Return(ctx) rr.EXPECT().Upsert(mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("*rule.Rule")).Return(nil) rr.EXPECT().List(mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("rule.Filter")).Return(nil, errors.New("some error")) + rr.EXPECT().Rollback(ctx, mock.Anything).Return(errors.New("rollback error")) + }, + ErrString: "rollback error", + }, + { + Description: "should return error if list repository return error", + Rule: &rule.Rule{ + Name: "foo", + Namespace: "namespace", + Variables: []rule.RuleVariable{ + { + Name: "var1", + Type: "type", + Value: "value", + Description: "description", + }, + }, + }, + Setup: func(rr *mocks.RuleRepository, ts *mocks.TemplateService, ns *mocks.NamespaceService, ps *mocks.ProviderService, cc *mocks.CortexClient) { + ts.EXPECT().GetByName(mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("string")).Return(&template.Template{}, nil) + ns.EXPECT().Get(mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("uint64")).Return(&namespace.Namespace{}, nil) + ps.EXPECT().Get(mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("uint64")).Return(&provider.Provider{}, nil) + + rr.EXPECT().WithTransaction(ctx).Return(ctx) + rr.EXPECT().Upsert(mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("*rule.Rule")).Return(errors.New("some error")) rr.EXPECT().Rollback(ctx, errors.New("some error")).Return(nil) }, ErrString: "some error", }, + { + Description: "should return error if list repository return error and rollback error", + Rule: &rule.Rule{ + Name: "foo", + Namespace: "namespace", + Variables: []rule.RuleVariable{ + { + Name: "var1", + Type: "type", + Value: "value", + Description: "description", + }, + }, + }, + Setup: func(rr *mocks.RuleRepository, ts *mocks.TemplateService, ns *mocks.NamespaceService, ps *mocks.ProviderService, cc *mocks.CortexClient) { + ts.EXPECT().GetByName(mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("string")).Return(&template.Template{}, nil) + ns.EXPECT().Get(mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("uint64")).Return(&namespace.Namespace{}, nil) + ps.EXPECT().Get(mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("uint64")).Return(&provider.Provider{}, nil) + + rr.EXPECT().WithTransaction(ctx).Return(ctx) + rr.EXPECT().Upsert(mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("*rule.Rule")).Return(errors.New("some error")) + rr.EXPECT().Rollback(ctx, errors.New("some error")).Return(errors.New("rollback error")) + }, + ErrString: "rollback error", + }, { Description: "should return nil error if upsert repository success", Rule: &rule.Rule{ @@ -149,7 +200,7 @@ func TestService_Upsert(t *testing.T) { }, }, }, - Setup: func(rr *mocks.RuleRepository, ts *mocks.TemplateService, ns *mocks.NamespaceService, ps *mocks.ProviderService) { + Setup: func(rr *mocks.RuleRepository, ts *mocks.TemplateService, ns *mocks.NamespaceService, ps *mocks.ProviderService, cc *mocks.CortexClient) { ts.EXPECT().GetByName(mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("string")).Return(&template.Template{ Variables: []template.Variable{ { @@ -161,7 +212,9 @@ func TestService_Upsert(t *testing.T) { }, }, nil) ns.EXPECT().Get(mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("uint64")).Return(&namespace.Namespace{}, nil) - ps.EXPECT().Get(mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("uint64")).Return(&provider.Provider{}, nil) + ps.EXPECT().Get(mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("uint64")).Return(&provider.Provider{ + Type: provider.TypeCortex, + }, nil) rr.EXPECT().WithTransaction(ctx).Return(ctx) rr.EXPECT().Upsert(mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("*rule.Rule")).Return(nil) @@ -177,9 +230,108 @@ func TestService_Upsert(t *testing.T) { }, }, }}, nil) + cc.EXPECT().DeleteRuleGroup(mock.Anything, mock.AnythingOfType("string"), mock.AnythingOfType("string")).Return(nil) rr.EXPECT().Commit(ctx).Return(nil) }, }, + { + Description: "should return nil error if upsert repository success but post rule failed and rollback success", + Rule: &rule.Rule{ + Name: "foo", + Namespace: "namespace", + Variables: []rule.RuleVariable{ + { + Name: "var1", + Type: "type", + Value: "value", + Description: "description", + }, + }, + }, + Setup: func(rr *mocks.RuleRepository, ts *mocks.TemplateService, ns *mocks.NamespaceService, ps *mocks.ProviderService, cc *mocks.CortexClient) { + ts.EXPECT().GetByName(mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("string")).Return(&template.Template{ + Variables: []template.Variable{ + { + Name: "var1", + Type: "type", + Default: "value", + Description: "description", + }, + }, + }, nil) + ns.EXPECT().Get(mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("uint64")).Return(&namespace.Namespace{}, nil) + ps.EXPECT().Get(mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("uint64")).Return(&provider.Provider{ + Type: provider.TypeCortex, + }, nil) + + rr.EXPECT().WithTransaction(ctx).Return(ctx) + rr.EXPECT().Upsert(mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("*rule.Rule")).Return(nil) + rr.EXPECT().List(mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("rule.Filter")).Return([]rule.Rule{{ + Name: "foo", + Namespace: "namespace", + Variables: []rule.RuleVariable{ + { + Name: "var1", + Type: "type", + Value: "value", + Description: "description", + }, + }, + }}, nil) + cc.EXPECT().DeleteRuleGroup(mock.Anything, mock.AnythingOfType("string"), mock.AnythingOfType("string")).Return(errors.New("some error")) + rr.EXPECT().Rollback(ctx, mock.Anything).Return(nil) + }, + ErrString: "error calling cortex: some error", + }, + { + Description: "should return nil error if upsert repository success but post rule failed and rollback failed", + Rule: &rule.Rule{ + Name: "foo", + Namespace: "namespace", + Variables: []rule.RuleVariable{ + { + Name: "var1", + Type: "type", + Value: "value", + Description: "description", + }, + }, + }, + Setup: func(rr *mocks.RuleRepository, ts *mocks.TemplateService, ns *mocks.NamespaceService, ps *mocks.ProviderService, cc *mocks.CortexClient) { + ts.EXPECT().GetByName(mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("string")).Return(&template.Template{ + Variables: []template.Variable{ + { + Name: "var1", + Type: "type", + Default: "value", + Description: "description", + }, + }, + }, nil) + ns.EXPECT().Get(mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("uint64")).Return(&namespace.Namespace{}, nil) + ps.EXPECT().Get(mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("uint64")).Return(&provider.Provider{ + Type: provider.TypeCortex, + }, nil) + + rr.EXPECT().WithTransaction(ctx).Return(ctx) + rr.EXPECT().Upsert(mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("*rule.Rule")).Return(nil) + rr.EXPECT().List(mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("rule.Filter")).Return([]rule.Rule{{ + Name: "foo", + Namespace: "namespace", + Variables: []rule.RuleVariable{ + { + Name: "var1", + Type: "type", + Value: "value", + Description: "description", + }, + }, + }}, nil) + cc.EXPECT().DeleteRuleGroup(mock.Anything, mock.AnythingOfType("string"), mock.AnythingOfType("string")).Return(errors.New("some error")) + rr.EXPECT().Rollback(ctx, mock.Anything).Return(errors.New("rollback error")) + }, + ErrString: "rollback error", + }, { Description: "should return nil error if upsert repository success and rule has no variables", Rule: &rule.Rule{ @@ -187,7 +339,7 @@ func TestService_Upsert(t *testing.T) { Namespace: "namespace", Variables: []rule.RuleVariable{}, }, - Setup: func(rr *mocks.RuleRepository, ts *mocks.TemplateService, ns *mocks.NamespaceService, ps *mocks.ProviderService) { + Setup: func(rr *mocks.RuleRepository, ts *mocks.TemplateService, ns *mocks.NamespaceService, ps *mocks.ProviderService, cc *mocks.CortexClient) { ts.EXPECT().GetByName(mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("string")).Return(&template.Template{ Variables: []template.Variable{ { @@ -199,7 +351,9 @@ func TestService_Upsert(t *testing.T) { }, }, nil) ns.EXPECT().Get(mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("uint64")).Return(&namespace.Namespace{}, nil) - ps.EXPECT().Get(mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("uint64")).Return(&provider.Provider{}, nil) + ps.EXPECT().Get(mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("uint64")).Return(&provider.Provider{ + Type: provider.TypeCortex, + }, nil) rr.EXPECT().WithTransaction(ctx).Return(ctx) rr.EXPECT().Upsert(mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("*rule.Rule")).Return(nil) @@ -215,6 +369,7 @@ func TestService_Upsert(t *testing.T) { }, }, }}, nil) + cc.EXPECT().DeleteRuleGroup(mock.Anything, mock.AnythingOfType("string"), mock.AnythingOfType("string")).Return(nil) rr.EXPECT().Commit(ctx).Return(nil) }, }, @@ -232,7 +387,7 @@ func TestService_Upsert(t *testing.T) { }, }, }, - Setup: func(rr *mocks.RuleRepository, ts *mocks.TemplateService, ns *mocks.NamespaceService, ps *mocks.ProviderService) { + Setup: func(rr *mocks.RuleRepository, ts *mocks.TemplateService, ns *mocks.NamespaceService, ps *mocks.ProviderService, cc *mocks.CortexClient) { ts.EXPECT().GetByName(mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("string")).Return(&template.Template{ Variables: []template.Variable{ { @@ -244,7 +399,9 @@ func TestService_Upsert(t *testing.T) { }, }, nil) ns.EXPECT().Get(mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("uint64")).Return(&namespace.Namespace{}, nil) - ps.EXPECT().Get(mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("uint64")).Return(&provider.Provider{}, nil) + ps.EXPECT().Get(mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("uint64")).Return(&provider.Provider{ + Type: provider.TypeCortex, + }, nil) rr.EXPECT().WithTransaction(ctx).Return(ctx) rr.EXPECT().Upsert(mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("*rule.Rule")).Return(nil) @@ -260,6 +417,7 @@ func TestService_Upsert(t *testing.T) { }, }, }}, nil) + cc.EXPECT().DeleteRuleGroup(mock.Anything, mock.AnythingOfType("string"), mock.AnythingOfType("string")).Return(nil) rr.EXPECT().Commit(ctx).Return(errors.New("some commit error")) }, ErrString: "some commit error", @@ -274,10 +432,11 @@ func TestService_Upsert(t *testing.T) { templateServiceMock = new(mocks.TemplateService) namespaceServiceMock = new(mocks.NamespaceService) providerServiceMock = new(mocks.ProviderService) + cortexClientMock = new(mocks.CortexClient) ) - svc := rule.NewService(repositoryMock, templateServiceMock, namespaceServiceMock, providerServiceMock, nil) + svc := rule.NewService(repositoryMock, templateServiceMock, namespaceServiceMock, providerServiceMock, cortexClientMock) - tc.Setup(repositoryMock, templateServiceMock, namespaceServiceMock, providerServiceMock) + tc.Setup(repositoryMock, templateServiceMock, namespaceServiceMock, providerServiceMock, cortexClientMock) err := svc.Upsert(ctx, tc.Rule) if tc.ErrString != "" { @@ -290,6 +449,7 @@ func TestService_Upsert(t *testing.T) { templateServiceMock.AssertExpectations(t) namespaceServiceMock.AssertExpectations(t) providerServiceMock.AssertExpectations(t) + cortexClientMock.AssertExpectations(t) }) } } diff --git a/core/subscription/service_test.go b/core/subscription/service_test.go index e97860b9..8cfe0bf7 100644 --- a/core/subscription/service_test.go +++ b/core/subscription/service_test.go @@ -269,7 +269,7 @@ func TestService_Create(t *testing.T) { ErrString: "commit error", }, { - Description: "should return error if transaction rollback return error", + Description: "should return error if create repository return error and rollback return error", Setup: func(sr *mocks.SubscriptionRepository, ns *mocks.NamespaceService, ps *mocks.ProviderService, rs *mocks.ReceiverService, cc *mocks.CortexClient) { ns.EXPECT().Get(mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("uint64")).Return(&namespace.Namespace{}, nil) ps.EXPECT().Get(mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("uint64")).Return(&provider.Provider{}, nil) @@ -280,6 +280,32 @@ func TestService_Create(t *testing.T) { }, ErrString: "some rollback error", }, + { + Description: "should return error if sync to upstream return error and rollback success", + Setup: func(sr *mocks.SubscriptionRepository, ns *mocks.NamespaceService, ps *mocks.ProviderService, rs *mocks.ReceiverService, cc *mocks.CortexClient) { + ns.EXPECT().Get(mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("uint64")).Return(&namespace.Namespace{}, nil) + ps.EXPECT().Get(mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("uint64")).Return(&provider.Provider{}, nil) + + sr.EXPECT().WithTransaction(ctx).Return(ctx) + sr.EXPECT().Create(mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("*subscription.Subscription")).Return(nil) + sr.EXPECT().List(mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("subscription.Filter")).Return(nil, errors.New("some error")) + sr.EXPECT().Rollback(ctx, mock.Anything).Return(nil) + }, + ErrString: "some error", + }, + { + Description: "should return error if sync to upstream return error and rollback failed", + Setup: func(sr *mocks.SubscriptionRepository, ns *mocks.NamespaceService, ps *mocks.ProviderService, rs *mocks.ReceiverService, cc *mocks.CortexClient) { + ns.EXPECT().Get(mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("uint64")).Return(&namespace.Namespace{}, nil) + ps.EXPECT().Get(mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("uint64")).Return(&provider.Provider{}, nil) + + sr.EXPECT().WithTransaction(ctx).Return(ctx) + sr.EXPECT().Create(mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("*subscription.Subscription")).Return(nil) + sr.EXPECT().List(mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("subscription.Filter")).Return(nil, errors.New("some error")) + sr.EXPECT().Rollback(ctx, mock.Anything).Return(errors.New("some rollback error")) + }, + ErrString: "some rollback error", + }, } ) @@ -448,7 +474,7 @@ func TestService_Update(t *testing.T) { ErrString: "some error", }, { - Description: "should return error if transaction rollback return error", + Description: "should return error if update subscription return error and rollback return error", Setup: func(sr *mocks.SubscriptionRepository, ns *mocks.NamespaceService, ps *mocks.ProviderService, rs *mocks.ReceiverService, cc *mocks.CortexClient) { ns.EXPECT().Get(mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("uint64")).Return(&namespace.Namespace{}, nil) ps.EXPECT().Get(mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("uint64")).Return(&provider.Provider{}, nil) @@ -459,6 +485,32 @@ func TestService_Update(t *testing.T) { }, ErrString: "some rollback error", }, + { + Description: "should return error if sync to upstream return error and rollback success", + Setup: func(sr *mocks.SubscriptionRepository, ns *mocks.NamespaceService, ps *mocks.ProviderService, rs *mocks.ReceiverService, cc *mocks.CortexClient) { + ns.EXPECT().Get(mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("uint64")).Return(&namespace.Namespace{}, nil) + ps.EXPECT().Get(mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("uint64")).Return(&provider.Provider{}, nil) + + sr.EXPECT().WithTransaction(ctx).Return(ctx) + sr.EXPECT().Update(mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("*subscription.Subscription")).Return(nil) + sr.EXPECT().List(mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("subscription.Filter")).Return(nil, errors.New("some error")) + sr.EXPECT().Rollback(ctx, mock.Anything).Return(nil) + }, + ErrString: "some error", + }, + { + Description: "should return error if sync to upstream return error and rollback failed", + Setup: func(sr *mocks.SubscriptionRepository, ns *mocks.NamespaceService, ps *mocks.ProviderService, rs *mocks.ReceiverService, cc *mocks.CortexClient) { + ns.EXPECT().Get(mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("uint64")).Return(&namespace.Namespace{}, nil) + ps.EXPECT().Get(mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("uint64")).Return(&provider.Provider{}, nil) + + sr.EXPECT().WithTransaction(ctx).Return(ctx) + sr.EXPECT().Update(mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("*subscription.Subscription")).Return(nil) + sr.EXPECT().List(mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("subscription.Filter")).Return(nil, errors.New("some error")) + sr.EXPECT().Rollback(ctx, mock.Anything).Return(errors.New("some rollback error")) + }, + ErrString: "some rollback error", + }, { Description: "should return no error if update subscription return no error", Setup: func(sr *mocks.SubscriptionRepository, ns *mocks.NamespaceService, ps *mocks.ProviderService, rs *mocks.ReceiverService, cc *mocks.CortexClient) { @@ -630,7 +682,7 @@ func TestService_Delete(t *testing.T) { ErrString: "some error", }, { - Description: "should return error if transaction rollback return error", + Description: "should return error if delete subscription return error and rollback return error", Setup: func(sr *mocks.SubscriptionRepository, ns *mocks.NamespaceService, ps *mocks.ProviderService, rs *mocks.ReceiverService, cc *mocks.CortexClient) { sr.EXPECT().Get(mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("uint64")).Return(&subscription.Subscription{}, nil) ns.EXPECT().Get(mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("uint64")).Return(&namespace.Namespace{}, nil) @@ -642,6 +694,34 @@ func TestService_Delete(t *testing.T) { }, ErrString: "some rollback error", }, + { + Description: "should return error if sync to upstream return error and rollback success", + Setup: func(sr *mocks.SubscriptionRepository, ns *mocks.NamespaceService, ps *mocks.ProviderService, rs *mocks.ReceiverService, cc *mocks.CortexClient) { + sr.EXPECT().Get(mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("uint64")).Return(&subscription.Subscription{}, nil) + ns.EXPECT().Get(mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("uint64")).Return(&namespace.Namespace{}, nil) + ps.EXPECT().Get(mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("uint64")).Return(&provider.Provider{}, nil) + + sr.EXPECT().WithTransaction(ctx).Return(ctx) + sr.EXPECT().Delete(mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("uint64"), mock.AnythingOfType("uint64")).Return(nil) + sr.EXPECT().List(mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("subscription.Filter")).Return(nil, errors.New("some error")) + sr.EXPECT().Rollback(ctx, mock.Anything).Return(nil) + }, + ErrString: "some error", + }, + { + Description: "should return error if sync to upstream return error and rollback failed", + Setup: func(sr *mocks.SubscriptionRepository, ns *mocks.NamespaceService, ps *mocks.ProviderService, rs *mocks.ReceiverService, cc *mocks.CortexClient) { + sr.EXPECT().Get(mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("uint64")).Return(&subscription.Subscription{}, nil) + ns.EXPECT().Get(mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("uint64")).Return(&namespace.Namespace{}, nil) + ps.EXPECT().Get(mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("uint64")).Return(&provider.Provider{}, nil) + + sr.EXPECT().WithTransaction(ctx).Return(ctx) + sr.EXPECT().Delete(mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("uint64"), mock.AnythingOfType("uint64")).Return(nil) + sr.EXPECT().List(mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("subscription.Filter")).Return(nil, errors.New("some error")) + sr.EXPECT().Rollback(ctx, mock.Anything).Return(errors.New("some rollback error")) + }, + ErrString: "some rollback error", + }, { Description: "should return no error if delete subscription return no error", Setup: func(sr *mocks.SubscriptionRepository, ns *mocks.NamespaceService, ps *mocks.ProviderService, rs *mocks.ReceiverService, cc *mocks.CortexClient) { diff --git a/internal/store/postgres/rule_test.go b/internal/store/postgres/rule_test.go index 3c4aa996..5db0e023 100644 --- a/internal/store/postgres/rule_test.go +++ b/internal/store/postgres/rule_test.go @@ -320,6 +320,56 @@ func (s *RuleRepositoryTestSuite) TestUpsert() { } } +func (s *RuleRepositoryTestSuite) TestTransaction() { + s.Run("successfully commit transaction", func() { + ctx := s.repository.WithTransaction(context.Background()) + err := s.repository.Upsert(ctx, &rule.Rule{ + ID: 888, + Name: "test-commit", + Enabled: true, + GroupName: "group-name-1", + Namespace: "ns-test-commit", + Template: "template-name-1", + Variables: []rule.RuleVariable{}, + ProviderNamespace: 2, + }) + s.NoError(err) + + err = s.repository.Commit(ctx) + s.NoError(err) + + fetchedRules, err := s.repository.List(s.ctx, rule.Filter{ + Name: "test-commit", + }) + s.NoError(err) + s.Len(fetchedRules, 1) + }) + + s.Run("successfully rollback transaction", func() { + ctx := s.repository.WithTransaction(context.Background()) + err := s.repository.Upsert(ctx, &rule.Rule{ + ID: 999, + Name: "test-rollback", + Enabled: true, + GroupName: "group-name-1", + Namespace: "ns-test-rollback", + Template: "template-name-1", + Variables: []rule.RuleVariable{}, + ProviderNamespace: 2, + }) + s.NoError(err) + + err = s.repository.Rollback(ctx, nil) + s.NoError(err) + + fetchedRules, err := s.repository.List(s.ctx, rule.Filter{ + Name: "test-rollback", + }) + s.NoError(err) + s.Len(fetchedRules, 0) + }) +} + func TestRuleRepository(t *testing.T) { suite.Run(t, new(RuleRepositoryTestSuite)) } diff --git a/internal/store/postgres/subscription_test.go b/internal/store/postgres/subscription_test.go index af05f9d4..b7df4f2a 100644 --- a/internal/store/postgres/subscription_test.go +++ b/internal/store/postgres/subscription_test.go @@ -442,6 +442,74 @@ func (s *SubscriptionRepositoryTestSuite) TestDelete() { } } +func (s *SubscriptionRepositoryTestSuite) TestTransaction() { + s.Run("successfully commit transaction", func() { + ctx := s.repository.WithTransaction(context.Background()) + err := s.repository.Create(ctx, &subscription.Subscription{ + Namespace: 2, + URN: "foo-commit", + Match: map[string]string{ + "foo": "bar", + }, + Receivers: []subscription.Receiver{ + { + ID: 2, + Configuration: map[string]string{}, + }, + { + ID: 1, + Configuration: map[string]string{ + "channel_name": "test", + }, + }, + }, + }) + s.NoError(err) + + err = s.repository.Commit(ctx) + s.NoError(err) + + fetchedRules, err := s.repository.List(s.ctx, subscription.Filter{ + NamespaceID: 2, + }) + s.NoError(err) + s.Len(fetchedRules, 3) + }) + + s.Run("successfully rollback transaction", func() { + ctx := s.repository.WithTransaction(context.Background()) + err := s.repository.Create(ctx, &subscription.Subscription{ + Namespace: 2, + URN: "foo-rollback", + Match: map[string]string{ + "foo": "bar", + }, + Receivers: []subscription.Receiver{ + { + ID: 2, + Configuration: map[string]string{}, + }, + { + ID: 1, + Configuration: map[string]string{ + "channel_name": "test", + }, + }, + }, + }) + s.NoError(err) + + err = s.repository.Rollback(ctx, nil) + s.NoError(err) + + fetchedRules, err := s.repository.List(s.ctx, subscription.Filter{ + NamespaceID: 2, + }) + s.NoError(err) + s.Len(fetchedRules, 3) + }) +} + func TestSubscriptionRepository(t *testing.T) { suite.Run(t, new(SubscriptionRepositoryTestSuite)) }