diff --git a/internal/circuitgentest/circuittest_multi/aggregator.gen.go b/internal/circuitgentest/circuittest_multi/aggregator.gen.go new file mode 100644 index 0000000..23176f9 --- /dev/null +++ b/internal/circuitgentest/circuittest_multi/aggregator.gen.go @@ -0,0 +1,106 @@ +// Code generated by circuitgen tool. DO NOT EDIT + +package circuittest + +import ( + "context" + + "github.com/cep21/circuit" + "github.com/twitchtv/circuitgen/internal/circuitgentest" +) + +// CircuitWrapperAggregatorConfig contains configuration for CircuitWrapperAggregator. All fields are optional +type CircuitWrapperAggregatorConfig struct { + // ShouldSkipError determines whether an error should be skipped and have the circuit + // track the call as successful. This takes precedence over IsBadRequest + ShouldSkipError func(error) bool + + // IsBadRequest is an optional bad request checker. It is useful to not count user errors as faults + IsBadRequest func(error) bool + + // Prefix is prepended to all circuit names + Prefix string + + // Defaults are used for all created circuits. Per-circuit configs override this + Defaults circuit.Config + + // CircuitIncSum is the configuration used for the IncSum circuit. This overrides values set by Defaults + CircuitIncSum circuit.Config +} + +// CircuitWrapperAggregator is a circuit wrapper for *circuitgentest.Aggregator +type CircuitWrapperAggregator struct { + *circuitgentest.Aggregator + + // ShouldSkipError determines whether an error should be skipped and have the circuit + // track the call as successful. This takes precedence over IsBadRequest + ShouldSkipError func(error) bool + + // IsBadRequest checks whether to count a user error against the circuit. It is recommended to set this + IsBadRequest func(error) bool + + // CircuitIncSum is the circuit for method IncSum + CircuitIncSum *circuit.Circuit +} + +// NewCircuitWrapperAggregator creates a new circuit wrapper and initializes circuits +func NewCircuitWrapperAggregator( + manager *circuit.Manager, + embedded *circuitgentest.Aggregator, + conf CircuitWrapperAggregatorConfig, +) (*CircuitWrapperAggregator, error) { + if conf.ShouldSkipError == nil { + conf.ShouldSkipError = func(err error) bool { + return false + } + } + + if conf.IsBadRequest == nil { + conf.IsBadRequest = func(err error) bool { + return false + } + } + + w := &CircuitWrapperAggregator{ + Aggregator: embedded, + ShouldSkipError: conf.ShouldSkipError, + IsBadRequest: conf.IsBadRequest, + } + + var err error + w.CircuitIncSum, err = manager.CreateCircuit(conf.Prefix+"Aggregator.IncSum", conf.CircuitIncSum, conf.Defaults) + if err != nil { + return nil, err + } + + return w, nil +} + +// IncSum calls the embedded *circuitgentest.Aggregator's method IncSum with CircuitIncSum +func (w *CircuitWrapperAggregator) IncSum(ctx context.Context, p1 int) error { + var skippedErr error + + err := w.CircuitIncSum.Run(ctx, func(ctx context.Context) error { + err := w.Aggregator.IncSum(ctx, p1) + + if w.ShouldSkipError(err) { + skippedErr = err + return nil + } + + if w.IsBadRequest(err) { + return &circuit.SimpleBadRequest{Err: err} + } + return err + }) + + if skippedErr != nil { + err = skippedErr + } + + if berr, ok := err.(*circuit.SimpleBadRequest); ok { + err = berr.Err + } + + return err +} diff --git a/internal/circuitgentest/circuittest_multi/gen.go b/internal/circuitgentest/circuittest_multi/gen.go new file mode 100644 index 0000000..dc6e380 --- /dev/null +++ b/internal/circuitgentest/circuittest_multi/gen.go @@ -0,0 +1,20 @@ +// Copyright 2019 Twitch Interactive, Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"). You may not +// use this file except in compliance with the License. A copy of the License is +// located at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// or in the "license" file accompanying this file. This file is distributed on +// an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either +// express or implied. See the License for the specific language governing +// permissions and limitations under the License. + +package circuittest + +// Test generation in a separate package. gen_test.go contains comprehensive test on generated circuit wrappers. +// +// Disable goimports to catch any import bugs + +//go:generate circuitgen circuit --goimports=true --pkg ../ --name Publisher --name Aggregator --out ./ \ No newline at end of file diff --git a/internal/circuitgentest/circuittest_multi/gen_test.go b/internal/circuitgentest/circuittest_multi/gen_test.go new file mode 100644 index 0000000..5a0e209 --- /dev/null +++ b/internal/circuitgentest/circuittest_multi/gen_test.go @@ -0,0 +1,115 @@ +// Copyright 2019 Twitch Interactive, Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"). You may not +// use this file except in compliance with the License. A copy of the License is +// located at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// or in the "license" file accompanying this file. This file is distributed on +// an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either +// express or implied. See the License for the specific language governing +// permissions and limitations under the License. + +package circuittest + +import ( + "context" + "errors" + "testing" + "time" + + "github.com/cep21/circuit" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/mock" + "github.com/stretchr/testify/require" + "github.com/twitchtv/circuitgen/internal/circuitgentest" + "github.com/twitchtv/circuitgen/internal/circuitgentest/rep" +) + +// Thinner test of multi-gen interface +func TestPublisherInterface(t *testing.T) { + manager := &circuit.Manager{} + + m := &circuitgentest.MockPublisher{} + m.On("PublishWithResult", mock.Anything, mock.Anything).Return(nil, nil).Once() + m.On("Publish", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil, nil).Once() + m.On("Close", mock.Anything).Return(nil).Once() + + pubsub, err := NewCircuitWrapperPublisher(manager, m, CircuitWrapperPubsubConfig{}) + require.NoError(t, err) + require.NotNil(t, pubsub) + + // Check circuit names + names := circuitNames(manager) + require.Contains(t, names, "Publisher.PublishWithResult") + require.Contains(t, names, "Publisher.Publish") + + ctx := context.Background() + _, err = pubsub.Publish(ctx, map[circuitgentest.Seed][][]circuitgentest.Grant{}, circuitgentest.TopicsList{}) + require.NoError(t, err) + + _, err = pubsub.PublishWithResult(ctx, rep.PublishInput{}) + require.NoError(t, err) + + require.NoError(t, pubsub.Close()) + + // Check embedded called + m.AssertExpectations(t) +} + + +func TestAggregatorStruct(t *testing.T) { + manager := &circuit.Manager{} + agg := &circuitgentest.Aggregator{} + + incSumCounter := &runMetricsCounter{} + wrapperAgg, err := NewCircuitWrapperAggregator(manager, agg, CircuitWrapperAggregatorConfig{ + CircuitIncSum: circuit.Config{ + Metrics: circuit.MetricsCollectors{ + Run: []circuit.RunMetrics{incSumCounter}, + }, + }, + }) + require.NoError(t, err) + + err = wrapperAgg.IncSum(context.Background(), 10) + require.NoError(t, err) + require.Equal(t, 10, agg.Sum()) + require.Equal(t, 10, wrapperAgg.Sum()) + require.Equal(t, 1, incSumCounter.success) + + sumErr := errors.New("sum error") + agg.IncSumError = sumErr + err = wrapperAgg.IncSum(context.Background(), 10) + require.Equal(t, sumErr, err) + +} + +func circuitNames(m *circuit.Manager) []string { + names := make([]string, 0, len(m.AllCircuits())) + for _, circ := range m.AllCircuits() { + names = append(names, circ.Name()) + } + return names +} + +type runMetricsCounter struct { + success int + failure int + timeout int + badRequest int + interrupt int + concurrencyLimitReject int + shortCircuit int +} + +func (r *runMetricsCounter) Success(now time.Time, duration time.Duration) { r.success++ } +func (r *runMetricsCounter) ErrFailure(now time.Time, duration time.Duration) { r.failure++ } +func (r *runMetricsCounter) ErrTimeout(now time.Time, duration time.Duration) { r.timeout++ } +func (r *runMetricsCounter) ErrBadRequest(now time.Time, duration time.Duration) { r.badRequest++ } +func (r *runMetricsCounter) ErrInterrupt(now time.Time, duration time.Duration) { r.interrupt++ } +func (r *runMetricsCounter) ErrConcurrencyLimitReject(now time.Time) { r.concurrencyLimitReject++ } +func (r *runMetricsCounter) ErrShortCircuit(now time.Time) { r.shortCircuit++ } + +var _ circuit.RunMetrics = (*runMetricsCounter)(nil) diff --git a/internal/circuitgentest/circuittest_multi/publisher.gen.go b/internal/circuitgentest/circuittest_multi/publisher.gen.go new file mode 100644 index 0000000..52ce829 --- /dev/null +++ b/internal/circuitgentest/circuittest_multi/publisher.gen.go @@ -0,0 +1,153 @@ +// Code generated by circuitgen tool. DO NOT EDIT + +package circuittest + +import ( + "context" + + "github.com/cep21/circuit" + "github.com/twitchtv/circuitgen/internal/circuitgentest" + "github.com/twitchtv/circuitgen/internal/circuitgentest/model" + "github.com/twitchtv/circuitgen/internal/circuitgentest/rep" +) + +// CircuitWrapperPublisherConfig contains configuration for CircuitWrapperPublisher. All fields are optional +type CircuitWrapperPublisherConfig struct { + // ShouldSkipError determines whether an error should be skipped and have the circuit + // track the call as successful. This takes precedence over IsBadRequest + ShouldSkipError func(error) bool + + // IsBadRequest is an optional bad request checker. It is useful to not count user errors as faults + IsBadRequest func(error) bool + + // Prefix is prepended to all circuit names + Prefix string + + // Defaults are used for all created circuits. Per-circuit configs override this + Defaults circuit.Config + + // CircuitPublish is the configuration used for the Publish circuit. This overrides values set by Defaults + CircuitPublish circuit.Config + // CircuitPublishWithResult is the configuration used for the PublishWithResult circuit. This overrides values set by Defaults + CircuitPublishWithResult circuit.Config +} + +// CircuitWrapperPublisher is a circuit wrapper for circuitgentest.Publisher +type CircuitWrapperPublisher struct { + circuitgentest.Publisher + + // ShouldSkipError determines whether an error should be skipped and have the circuit + // track the call as successful. This takes precedence over IsBadRequest + ShouldSkipError func(error) bool + + // IsBadRequest checks whether to count a user error against the circuit. It is recommended to set this + IsBadRequest func(error) bool + + // CircuitPublish is the circuit for method Publish + CircuitPublish *circuit.Circuit + // CircuitPublishWithResult is the circuit for method PublishWithResult + CircuitPublishWithResult *circuit.Circuit +} + +// NewCircuitWrapperPublisher creates a new circuit wrapper and initializes circuits +func NewCircuitWrapperPublisher( + manager *circuit.Manager, + embedded circuitgentest.Publisher, + conf CircuitWrapperPublisherConfig, +) (*CircuitWrapperPublisher, error) { + if conf.ShouldSkipError == nil { + conf.ShouldSkipError = func(err error) bool { + return false + } + } + + if conf.IsBadRequest == nil { + conf.IsBadRequest = func(err error) bool { + return false + } + } + + w := &CircuitWrapperPublisher{ + Publisher: embedded, + ShouldSkipError: conf.ShouldSkipError, + IsBadRequest: conf.IsBadRequest, + } + + var err error + + w.CircuitPublish, err = manager.CreateCircuit(conf.Prefix+"Publisher.Publish", conf.CircuitPublish, conf.Defaults) + if err != nil { + return nil, err + } + + w.CircuitPublishWithResult, err = manager.CreateCircuit(conf.Prefix+"Publisher.PublishWithResult", conf.CircuitPublishWithResult, conf.Defaults) + if err != nil { + return nil, err + } + + return w, nil +} + +// Publish calls the embedded circuitgentest.Publisher's method Publish with CircuitPublish +func (w *CircuitWrapperPublisher) Publish(ctx context.Context, p1 map[circuitgentest.Seed][][]circuitgentest.Grant, p2 circuitgentest.TopicsList, p3 ...rep.PublishOption) (map[string]struct{}, error) { + var r0 map[string]struct{} + var skippedErr error + + err := w.CircuitPublish.Run(ctx, func(ctx context.Context) error { + var err error + r0, err = w.Publisher.Publish(ctx, p1, p2, p3...) + + if w.ShouldSkipError(err) { + skippedErr = err + return nil + } + + if w.IsBadRequest(err) { + return &circuit.SimpleBadRequest{Err: err} + } + return err + }) + + if skippedErr != nil { + err = skippedErr + } + + if berr, ok := err.(*circuit.SimpleBadRequest); ok { + err = berr.Err + } + + return r0, err +} + +// PublishWithResult calls the embedded circuitgentest.Publisher's method PublishWithResult with CircuitPublishWithResult +func (w *CircuitWrapperPublisher) PublishWithResult(ctx context.Context, p1 rep.PublishInput) (*model.Result, error) { + var r0 *model.Result + var skippedErr error + + err := w.CircuitPublishWithResult.Run(ctx, func(ctx context.Context) error { + var err error + r0, err = w.Publisher.PublishWithResult(ctx, p1) + + if w.ShouldSkipError(err) { + skippedErr = err + return nil + } + + if w.IsBadRequest(err) { + return &circuit.SimpleBadRequest{Err: err} + } + return err + }) + + if skippedErr != nil { + err = skippedErr + } + + if berr, ok := err.(*circuit.SimpleBadRequest); ok { + err = berr.Err + } + + return r0, err +} + +var _ circuitgentest.Publisher = (*CircuitWrapperPublisher)(nil)