|
| 1 | +/* |
| 2 | +Copyright IBM Corp. All Rights Reserved. |
| 3 | +
|
| 4 | +SPDX-License-Identifier: Apache-2.0 |
| 5 | +*/ |
| 6 | + |
| 7 | +package config_test |
| 8 | + |
| 9 | +import ( |
| 10 | + "testing" |
| 11 | + "time" |
| 12 | + |
| 13 | + "github.com/LFDT-Panurus/panurus/token/services/network/fabric/config" |
| 14 | + "github.com/stretchr/testify/assert" |
| 15 | +) |
| 16 | + |
| 17 | +// mapConfigService is a driver.ConfigService backed by two maps: whatever the test |
| 18 | +// puts in them is "set", everything else reads as a zero value, which is what an |
| 19 | +// absent YAML key looks like. |
| 20 | +type mapConfigService struct { |
| 21 | + ints map[string]int |
| 22 | + durations map[string]time.Duration |
| 23 | +} |
| 24 | + |
| 25 | +func (c *mapConfigService) GetInt(key string) int { return c.ints[key] } |
| 26 | +func (c *mapConfigService) GetDuration(key string) time.Duration { return c.durations[key] } |
| 27 | +func (c *mapConfigService) GetString(string) string { return "" } |
| 28 | +func (c *mapConfigService) GetBool(string) bool { return false } |
| 29 | +func (c *mapConfigService) GetStringSlice(string) []string { return nil } |
| 30 | +func (c *mapConfigService) IsSet(string) bool { return false } |
| 31 | +func (c *mapConfigService) UnmarshalKey(string, any) error { return nil } |
| 32 | +func (c *mapConfigService) ConfigFileUsed() string { return "" } |
| 33 | +func (c *mapConfigService) GetPath(string) string { return "" } |
| 34 | +func (c *mapConfigService) TranslatePath(path string) string { return path } |
| 35 | + |
| 36 | +// TestLedgerInfoRetryDefaults pins the defaults an unconfigured deployment gets. |
| 37 | +// The attempt budget matters beyond taste: nothing retries the block scan, so a |
| 38 | +// height that stays unreadable costs the channel its block-based finality until |
| 39 | +// the process restarts, and the budget has to outlast a peer restart. |
| 40 | +func TestLedgerInfoRetryDefaults(t *testing.T) { |
| 41 | + c := config.NewListenerManagerConfig(&mapConfigService{}) |
| 42 | + |
| 43 | + assert.Equal(t, config.DefaultDeliveryLedgerInfoAttempts, c.DeliveryLedgerInfoAttempts()) |
| 44 | + assert.Equal(t, config.DefaultDeliveryLedgerInfoRetryDelay, c.DeliveryLedgerInfoRetryDelay()) |
| 45 | + assert.GreaterOrEqual(t, totalRetryWait(c.DeliveryLedgerInfoAttempts(), c.DeliveryLedgerInfoRetryDelay()), 20*time.Second, |
| 46 | + "the default budget must outlast a peer restart, not just a dropped packet") |
| 47 | +} |
| 48 | + |
| 49 | +// TestLedgerInfoRetryReadsConfiguredValues covers the point of the exercise: an |
| 50 | +// operator can shorten or lengthen the budget without a rebuild. |
| 51 | +func TestLedgerInfoRetryReadsConfiguredValues(t *testing.T) { |
| 52 | + c := config.NewListenerManagerConfig(&mapConfigService{ |
| 53 | + ints: map[string]int{config.DeliveryLedgerInfoAttempts: 12}, |
| 54 | + durations: map[string]time.Duration{config.DeliveryLedgerInfoRetryDelay: 250 * time.Millisecond}, |
| 55 | + }) |
| 56 | + |
| 57 | + assert.Equal(t, 12, c.DeliveryLedgerInfoAttempts()) |
| 58 | + assert.Equal(t, 250*time.Millisecond, c.DeliveryLedgerInfoRetryDelay()) |
| 59 | +} |
| 60 | + |
| 61 | +// TestLedgerInfoRetryRejectsNonPositiveValues covers the values a hand-edited YAML |
| 62 | +// can hold: 0 attempts would refuse every scan and a negative delay would busy |
| 63 | +// loop, so both fall back to the default rather than being honoured. |
| 64 | +func TestLedgerInfoRetryRejectsNonPositiveValues(t *testing.T) { |
| 65 | + for _, attempts := range []int{0, -1} { |
| 66 | + c := config.NewListenerManagerConfig(&mapConfigService{ints: map[string]int{config.DeliveryLedgerInfoAttempts: attempts}}) |
| 67 | + assert.Equal(t, config.DefaultDeliveryLedgerInfoAttempts, c.DeliveryLedgerInfoAttempts(), "attempts %d must not be honoured", attempts) |
| 68 | + } |
| 69 | + |
| 70 | + for _, delay := range []time.Duration{0, -time.Second} { |
| 71 | + c := config.NewListenerManagerConfig(&mapConfigService{durations: map[string]time.Duration{config.DeliveryLedgerInfoRetryDelay: delay}}) |
| 72 | + assert.Equal(t, config.DefaultDeliveryLedgerInfoRetryDelay, c.DeliveryLedgerInfoRetryDelay(), "delay %v must not be honoured", delay) |
| 73 | + } |
| 74 | +} |
| 75 | + |
| 76 | +// TestStringReportsLedgerInfoBudget keeps the startup log line informative: the |
| 77 | +// budget is otherwise invisible to an operator diagnosing a missing finality. |
| 78 | +func TestStringReportsLedgerInfoBudget(t *testing.T) { |
| 79 | + s := config.NewListenerManagerConfig(&mapConfigService{}).String() |
| 80 | + |
| 81 | + assert.Contains(t, s, "ledgerInfo:") |
| 82 | +} |
| 83 | + |
| 84 | +// totalRetryWait sums the doubling schedule ledgerHeight sleeps through: one wait |
| 85 | +// less than the number of attempts, each twice the previous. |
| 86 | +func totalRetryWait(attempts int, first time.Duration) time.Duration { |
| 87 | + var total time.Duration |
| 88 | + for i, delay := 0, first; i < attempts-1; i, delay = i+1, delay*2 { |
| 89 | + total += delay |
| 90 | + } |
| 91 | + |
| 92 | + return total |
| 93 | +} |
0 commit comments