package tariff import ( "context" "errors" "sync" "testing" "time" "github.com/evcc-io/evcc/api" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) // retryTariff fails instantiation on demand type retryTariff struct { mu sync.Mutex err error calls int } // created returns the number of creation attempts func (t *retryTariff) created() int { t.mu.Lock() defer t.mu.Unlock() return t.calls } func (t *retryTariff) setErr(err error) { t.mu.Lock() defer t.mu.Unlock() t.err = err } func (t *retryTariff) Rates() (api.Rates, error) { t.mu.Lock() defer t.mu.Unlock() if t.err != nil { return nil, t.err } now := time.Now().Truncate(SlotDuration) return api.Rates{{Start: now, End: now.Add(SlotDuration), Value: 1}}, nil } func (t *retryTariff) Type() api.TariffType { return api.TariffTypePriceForecast } var retryable = new(retryTariff) func init() { registry.AddCtx("test-retry", func(context.Context, map[string]any) (api.Tariff, error) { retryable.mu.Lock() retryable.calls++ retryable.mu.Unlock() if _, err := retryable.Rates(); err != nil { return nil, err } return retryable, nil }) } func TestWrapperRetry(t *testing.T) { retryable.setErr(errors.New("unavailable")) _, err := NewFromConfig(context.TODO(), "test-retry", nil) require.Error(t, err) res := NewWrapper(context.TODO(), "test-retry", nil, err) w := res.(*Wrapper) // creation has just failed: no immediate retry assert.False(t, w.retriedAt.IsZero()) created := retryable.created() _, err = res.Rates() require.ErrorContains(t, err, "tariff not available") assert.Equal(t, api.TariffType(0), res.Type()) assert.Equal(t, created, retryable.created()) // tariff becomes available but retry interval not elapsed: still unavailable retryable.setErr(nil) _, err = res.Rates() require.Error(t, err) // retry interval elapsed: next call creates the tariff w.mu.Lock() w.retriedAt = time.Time{} w.mu.Unlock() rr, err := res.Rates() require.NoError(t, err) assert.Len(t, rr, 1) assert.Equal(t, api.TariffTypePriceForecast, res.Type()) // tariff fails at runtime: error passed through, no re-creation retryable.setErr(api.ErrOutdated) _, err = res.Rates() require.ErrorIs(t, err, api.ErrOutdated) assert.Same(t, retryable, w.tariff) }