mirror of
https://github.com/hanzoai/tasks.git
synced 2026-10-04 02:04:03 +00:00
## What changed?
<!-- Describe what has changed in this PR -->
Remove license header from every file. Because it is really hard to
follow in this PR here is the summary:
1. License header is removed from all `.go` and `.proto` files
:fireworks::fireworks:🎆.
2. `LICENSE` file in the root directory has only Temporal and Uber
copyrights.
3. 5 other `LICENSE` files added to the packages which have copyrights
different from Temporal and Uber: Datadog, Xargin, "Mat Ryer, Tyler
Bunnell and contributors".
4. `license_file` flag is removed from all code generation tools.
5. `copyright_file` flag is removed from `go:generate mockgen`
directive.
6. All copyright related targets are removed from `Makefile`.
7. Updated Temporal copyright year to 2025 everywhere.
## Why?
<!-- Tell your future self why have you made these changes -->
I double checked with legal department that it is not needed to have
license header in every file. One file per repo is enough. I put all
copyrights to the root `LICENSE` file and removed header from all other
files. Also updated tools and `Makefile`.
646 lines
20 KiB
Go
646 lines
20 KiB
Go
package dynamicconfig_test
|
|
|
|
import (
|
|
"maps"
|
|
"sync"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/stretchr/testify/suite"
|
|
enumspb "go.temporal.io/api/enums/v1"
|
|
enumsspb "go.temporal.io/server/api/enums/v1"
|
|
"go.temporal.io/server/common/dynamicconfig"
|
|
"go.temporal.io/server/common/log"
|
|
"go.temporal.io/server/common/namespace"
|
|
)
|
|
|
|
const (
|
|
// dynamic config for tests
|
|
unknownKey = "unknownKey"
|
|
testGetPropertyKey = "testGetPropertyKey"
|
|
testCaseInsensitivePropertyKey = "testCaseInsensitivePropertyKey"
|
|
testGetIntPropertyKey = "testGetIntPropertyKey"
|
|
testGetFloat64PropertyKey = "testGetFloat64PropertyKey"
|
|
testGetDurationPropertyKey = "testGetDurationPropertyKey"
|
|
testGetBoolPropertyKey = "testGetBoolPropertyKey"
|
|
testGetStringPropertyKey = "testGetStringPropertyKey"
|
|
testGetMapPropertyKey = "testGetMapPropertyKey"
|
|
testGetTypedPropertyKey = "testGetTypedPropertyKey"
|
|
testGetIntPropertyFilteredByNamespaceKey = "testGetIntPropertyFilteredByNamespaceKey"
|
|
testGetDurationPropertyFilteredByNamespaceKey = "testGetDurationPropertyFilteredByNamespaceKey"
|
|
testGetIntPropertyFilteredByTaskQueueInfoKey = "testGetIntPropertyFilteredByTaskQueueInfoKey"
|
|
testGetDurationPropertyFilteredByTaskQueueInfoKey = "testGetDurationPropertyFilteredByTaskQueueInfoKey"
|
|
testGetDurationPropertyFilteredByTaskTypeKey = "testGetDurationPropertyFilteredByTaskTypeKey"
|
|
testGetDurationPropertyStructuredDefaults = "testGetDurationPropertyStructuredDefaults"
|
|
testGetBoolPropertyFilteredByNamespaceIDKey = "testGetBoolPropertyFilteredByNamespaceIDKey"
|
|
testGetBoolPropertyFilteredByTaskQueueInfoKey = "testGetBoolPropertyFilteredByTaskQueueInfoKey"
|
|
testGetStringPropertyFilteredByNamespaceIDKey = "testGetStringPropertyFilteredByNamespaceIDKey"
|
|
testGetIntPropertyFilteredByDestinationKey = "testGetIntPropertyFilteredByDestinationKey"
|
|
)
|
|
|
|
// Note: fileBasedClientSuite also heavily tests Collection, since some tests are easier with data
|
|
// provided from a file.
|
|
type collectionSuite struct {
|
|
suite.Suite
|
|
client *testSubscribableClient
|
|
cln *dynamicconfig.Collection
|
|
}
|
|
|
|
func TestCollectionSuite(t *testing.T) {
|
|
s := new(collectionSuite)
|
|
suite.Run(t, s)
|
|
}
|
|
|
|
func (s *collectionSuite) SetupSuite() {
|
|
s.client = newTestSubscribableClient()
|
|
logger := log.NewNoopLogger()
|
|
s.cln = dynamicconfig.NewCollection(s.client, logger)
|
|
s.cln.Start()
|
|
}
|
|
|
|
func (s *collectionSuite) TearDownSuite() {
|
|
s.cln.Stop()
|
|
}
|
|
|
|
func (s *collectionSuite) SetupTest() {
|
|
dynamicconfig.ResetRegistryForTest()
|
|
}
|
|
|
|
func (s *collectionSuite) TestGetIntProperty() {
|
|
setting := dynamicconfig.NewGlobalIntSetting(testGetIntPropertyKey, 10, "")
|
|
value := setting.Get(s.cln)
|
|
s.Equal(10, value())
|
|
s.client.SetValue(testGetIntPropertyKey, 50)
|
|
s.Equal(50, value())
|
|
s.client.SetValue(testGetIntPropertyKey, uint32(50000))
|
|
s.Equal(50000, value())
|
|
}
|
|
|
|
func (s *collectionSuite) TestGetIntPropertyFilteredByNamespace() {
|
|
setting := dynamicconfig.NewNamespaceIntSetting(testGetIntPropertyFilteredByNamespaceKey, 10, "")
|
|
namespace := "testNamespace"
|
|
value := setting.Get(s.cln)
|
|
s.Equal(10, value(namespace))
|
|
s.client.SetValue(testGetIntPropertyFilteredByNamespaceKey, 50)
|
|
s.Equal(50, value(namespace))
|
|
}
|
|
|
|
func (s *collectionSuite) TestGetStringPropertyFnFilteredByNamespace() {
|
|
namespace := "testNamespace"
|
|
value := dynamicconfig.DefaultEventEncoding.Get(s.cln)
|
|
// copied default value, change this if it changes
|
|
s.Equal(enumspb.ENCODING_TYPE_PROTO3.String(), value(namespace))
|
|
s.client.SetValue(dynamicconfig.DefaultEventEncoding.Key(), "efg")
|
|
s.Equal("efg", value(namespace))
|
|
}
|
|
|
|
func (s *collectionSuite) TestGetStringPropertyFnFilteredByNamespaceID() {
|
|
setting := dynamicconfig.NewNamespaceIDStringSetting(testGetStringPropertyFilteredByNamespaceIDKey, "abc", "")
|
|
namespaceID := namespace.ID("testNamespaceID")
|
|
value := setting.Get(s.cln)
|
|
s.Equal("abc", value(namespaceID))
|
|
s.client.SetValue(testGetStringPropertyFilteredByNamespaceIDKey, "efg")
|
|
s.Equal("efg", value(namespaceID))
|
|
}
|
|
|
|
func (s *collectionSuite) TestGetIntPropertyFilteredByTaskQueueInfo() {
|
|
setting := dynamicconfig.NewTaskQueueIntSetting(testGetIntPropertyFilteredByTaskQueueInfoKey, 10, "")
|
|
namespace := "testNamespace"
|
|
taskQueue := "testTaskQueue"
|
|
value := setting.Get(s.cln)
|
|
s.Equal(10, value(namespace, taskQueue, 0))
|
|
s.client.SetValue(testGetIntPropertyFilteredByTaskQueueInfoKey, 50)
|
|
s.Equal(50, value(namespace, taskQueue, 0))
|
|
}
|
|
|
|
func (s *collectionSuite) TestGetFloat64Property() {
|
|
setting := dynamicconfig.NewGlobalFloatSetting(testGetFloat64PropertyKey, 0.1, "")
|
|
value := setting.Get(s.cln)
|
|
s.InEpsilon(0.1, value(), 1e-10)
|
|
s.client.SetValue(testGetFloat64PropertyKey, 0.01)
|
|
s.InEpsilon(0.01, value(), 1e-10)
|
|
s.client.SetValue(testGetFloat64PropertyKey, int64(123456789))
|
|
s.InEpsilon(float64(123456789), value(), 1e-10)
|
|
}
|
|
|
|
func (s *collectionSuite) TestGetBoolProperty() {
|
|
setting := dynamicconfig.NewGlobalBoolSetting(testGetBoolPropertyKey, true, "")
|
|
value := setting.Get(s.cln)
|
|
s.Equal(true, value())
|
|
s.client.SetValue(testGetBoolPropertyKey, false)
|
|
s.Equal(false, value())
|
|
s.client.SetValue(testGetBoolPropertyKey, "false")
|
|
s.Equal(false, value())
|
|
}
|
|
|
|
func (s *collectionSuite) TestGetBoolPropertyFilteredByNamespaceID() {
|
|
setting := dynamicconfig.NewNamespaceIDBoolSetting(testGetBoolPropertyFilteredByNamespaceIDKey, true, "")
|
|
namespaceID := namespace.ID("testNamespaceID")
|
|
value := setting.Get(s.cln)
|
|
s.Equal(true, value(namespaceID))
|
|
s.client.SetValue(testGetBoolPropertyFilteredByNamespaceIDKey, false)
|
|
s.Equal(false, value(namespaceID))
|
|
}
|
|
|
|
func (s *collectionSuite) TestGetBoolPropertyFilteredByTaskQueueInfo() {
|
|
setting := dynamicconfig.NewTaskQueueBoolSetting(testGetBoolPropertyFilteredByTaskQueueInfoKey, false, "")
|
|
namespace := "testNamespace"
|
|
taskQueue := "testTaskQueue"
|
|
value := setting.Get(s.cln)
|
|
s.Equal(false, value(namespace, taskQueue, 0))
|
|
s.client.SetValue(testGetBoolPropertyFilteredByTaskQueueInfoKey, true)
|
|
s.Equal(true, value(namespace, taskQueue, 0))
|
|
}
|
|
|
|
func (s *collectionSuite) TestGetDurationProperty() {
|
|
setting := dynamicconfig.NewGlobalDurationSetting(testGetDurationPropertyKey, 1*time.Second, "")
|
|
value := setting.Get(s.cln)
|
|
s.Equal(time.Second, value())
|
|
s.client.SetValue(testGetDurationPropertyKey, time.Minute)
|
|
s.Equal(time.Minute, value())
|
|
s.client.SetValue(testGetDurationPropertyKey, 33)
|
|
s.Equal(33*time.Second, value())
|
|
s.client.SetValue(testGetDurationPropertyKey, int16(33))
|
|
s.Equal(33*time.Second, value())
|
|
s.client.SetValue(testGetDurationPropertyKey, "33")
|
|
s.Equal(33*time.Second, value())
|
|
s.client.SetValue(testGetDurationPropertyKey, "33h")
|
|
s.Equal(33*time.Hour, value())
|
|
s.client.SetValue(testGetDurationPropertyKey, float32(33.5))
|
|
s.Equal(33*time.Second+500*time.Millisecond, value())
|
|
}
|
|
|
|
func (s *collectionSuite) TestGetDurationPropertyFilteredByNamespace() {
|
|
setting := dynamicconfig.NewNamespaceDurationSetting(testGetDurationPropertyFilteredByNamespaceKey, time.Second, "")
|
|
namespace := "testNamespace"
|
|
value := setting.Get(s.cln)
|
|
s.Equal(time.Second, value(namespace))
|
|
s.client.SetValue(testGetDurationPropertyFilteredByNamespaceKey, time.Minute)
|
|
s.Equal(time.Minute, value(namespace))
|
|
}
|
|
|
|
func (s *collectionSuite) TestGetDurationPropertyFilteredByTaskQueueInfo() {
|
|
setting := dynamicconfig.NewTaskQueueDurationSetting(testGetDurationPropertyFilteredByTaskQueueInfoKey, time.Second, "")
|
|
namespace := "testNamespace"
|
|
taskQueue := "testTaskQueue"
|
|
value := setting.Get(s.cln)
|
|
s.Equal(time.Second, value(namespace, taskQueue, 0))
|
|
s.client.SetValue(testGetDurationPropertyFilteredByTaskQueueInfoKey, time.Minute)
|
|
s.Equal(time.Minute, value(namespace, taskQueue, 0))
|
|
}
|
|
|
|
func (s *collectionSuite) TestGetDurationPropertyFilteredByTaskType() {
|
|
setting := dynamicconfig.NewTaskTypeDurationSetting(testGetDurationPropertyFilteredByTaskTypeKey, time.Second, "")
|
|
taskType := enumsspb.TASK_TYPE_UNSPECIFIED
|
|
value := setting.Get(s.cln)
|
|
s.Equal(time.Second, value(taskType))
|
|
s.client.SetValue(testGetDurationPropertyFilteredByTaskTypeKey, time.Minute)
|
|
s.Equal(time.Minute, value(taskType))
|
|
}
|
|
|
|
func (s *collectionSuite) TestGetDurationPropertyStructuredDefaults() {
|
|
setting := dynamicconfig.NewTaskQueueDurationSettingWithConstrainedDefault(
|
|
testGetDurationPropertyStructuredDefaults,
|
|
[]dynamicconfig.TypedConstrainedValue[time.Duration]{
|
|
{
|
|
Constraints: dynamicconfig.Constraints{
|
|
Namespace: "ns2",
|
|
TaskQueueName: "tq2",
|
|
},
|
|
Value: 2 * time.Minute,
|
|
},
|
|
{
|
|
Constraints: dynamicconfig.Constraints{
|
|
TaskQueueName: "tq2",
|
|
},
|
|
Value: 5 * time.Minute,
|
|
},
|
|
{
|
|
Value: 7 * time.Minute,
|
|
},
|
|
},
|
|
"",
|
|
)
|
|
value := setting.Get(s.cln)
|
|
s.Equal(7*time.Minute, value("ns1", "tq1", 0))
|
|
s.Equal(7*time.Minute, value("ns2", "tq1", 0))
|
|
s.Equal(5*time.Minute, value("ns1", "tq2", 0))
|
|
s.Equal(2*time.Minute, value("ns2", "tq2", 0))
|
|
|
|
// user-set values should take precedence. defaults are included below in the interleaved
|
|
// precedence order to make the test easier to read
|
|
s.client.Set(testGetDurationPropertyStructuredDefaults, []dynamicconfig.ConstrainedValue{
|
|
{
|
|
Constraints: dynamicconfig.Constraints{
|
|
Namespace: "ns2",
|
|
TaskQueueName: "tq2",
|
|
},
|
|
Value: 2 * time.Second,
|
|
},
|
|
// {
|
|
// Constraints: Constraints{
|
|
// Namespace: "ns2",
|
|
// TaskQueueName: "tq2",
|
|
// },
|
|
// Value: 2 * time.Minute,
|
|
// },
|
|
// {
|
|
// Constraints: Constraints{
|
|
// TaskQueueName: "tq2",
|
|
// },
|
|
// Value: 5 * time.Minute,
|
|
// },
|
|
{
|
|
Constraints: dynamicconfig.Constraints{
|
|
Namespace: "ns1",
|
|
},
|
|
Value: 5 * time.Second,
|
|
},
|
|
{
|
|
Value: 7 * time.Second,
|
|
},
|
|
// {
|
|
// Value: 7 * time.Minute,
|
|
// },
|
|
})
|
|
|
|
s.Equal(5*time.Second, value("ns1", "tq1", 0))
|
|
s.Equal(7*time.Second, value("ns2", "tq1", 0))
|
|
s.Equal(5*time.Minute, value("ns1", "tq2", 0))
|
|
s.Equal(2*time.Second, value("ns2", "tq2", 0))
|
|
}
|
|
|
|
func (s *collectionSuite) TestGetMapProperty() {
|
|
def := map[string]interface{}{"testKey": 123}
|
|
setting := dynamicconfig.NewGlobalMapSetting(
|
|
testGetMapPropertyKey,
|
|
def,
|
|
"",
|
|
)
|
|
value := setting.Get(s.cln)
|
|
s.Equal(def, value())
|
|
val := maps.Clone(def)
|
|
val["testKey"] = "321"
|
|
s.client.SetValue(testGetMapPropertyKey, val)
|
|
s.Equal(val, value())
|
|
s.Equal("321", value()["testKey"])
|
|
}
|
|
|
|
func (s *collectionSuite) TestGetTyped() {
|
|
type myFancyType struct {
|
|
Number int
|
|
Names []string
|
|
}
|
|
def := myFancyType{28, []string{"global", "typed", "setting"}}
|
|
setting := dynamicconfig.NewGlobalTypedSettingWithConverter(
|
|
testGetTypedPropertyKey,
|
|
dynamicconfig.ConvertStructure(myFancyType{-3, nil}), // used if convert is called
|
|
def,
|
|
"",
|
|
)
|
|
get := setting.Get(s.cln)
|
|
|
|
s.Run("Default", func() {
|
|
s.Equal(def, get())
|
|
})
|
|
|
|
s.Run("Basic", func() {
|
|
// map[string]any is what the yaml library decodes arbitrary data into
|
|
s.client.SetValue(testGetTypedPropertyKey, map[string]any{
|
|
"Number": 39,
|
|
"Names": []string{"new", "names"},
|
|
})
|
|
s.Equal(myFancyType{
|
|
Number: 39,
|
|
Names: []string{"new", "names"},
|
|
}, get())
|
|
})
|
|
|
|
s.Run("CaseInsensitive", func() {
|
|
s.client.SetValue(testGetTypedPropertyKey, map[string]any{
|
|
"naMES": []string{"case", "insensitive"},
|
|
})
|
|
s.Equal(-3, get().Number) // note the convert default is used here
|
|
s.Equal([]string{"case", "insensitive"}, get().Names)
|
|
})
|
|
|
|
s.Run("WrongType", func() {
|
|
s.client.SetValue(testGetTypedPropertyKey, 200)
|
|
s.Equal(def, get())
|
|
})
|
|
}
|
|
|
|
func (s *collectionSuite) TestGetTypedSimpleList() {
|
|
def := []float64{1.5, 1.1, 2.6, 3.7, 6.3}
|
|
setting := dynamicconfig.NewGlobalTypedSettingWithConverter(
|
|
testGetTypedPropertyKey,
|
|
dynamicconfig.ConvertStructure([]float64(nil)),
|
|
def,
|
|
"",
|
|
)
|
|
get := setting.Get(s.cln)
|
|
|
|
s.Run("Default", func() {
|
|
s.Equal(def, get())
|
|
})
|
|
|
|
s.Run("Basic", func() {
|
|
s.client.SetValue(testGetTypedPropertyKey, []any{19.0, -2.0})
|
|
s.Equal([]float64{19.0, -2.0}, get())
|
|
})
|
|
|
|
s.Run("WrongType", func() {
|
|
s.client.SetValue(testGetTypedPropertyKey, []any{88.8, false, -5, "oops"})
|
|
s.Equal(def, get())
|
|
})
|
|
}
|
|
|
|
func (s *collectionSuite) TestGetTypedListOfStruct() {
|
|
type simple struct{ A, B int }
|
|
def := []simple{{1, 5}, {2, 9}}
|
|
setting := dynamicconfig.NewGlobalTypedSettingWithConverter(
|
|
testGetTypedPropertyKey,
|
|
dynamicconfig.ConvertStructure([]simple(nil)),
|
|
def,
|
|
"",
|
|
)
|
|
get := setting.Get(s.cln)
|
|
|
|
s.Run("Default", func() {
|
|
s.Equal(def, get())
|
|
})
|
|
|
|
s.Run("Basic", func() {
|
|
s.client.SetValue(testGetTypedPropertyKey, []any{
|
|
map[string]any{"A": 12, "B": 6},
|
|
map[string]any{"A": -23, "B": 0},
|
|
map[string]any{"B": 555, "C": "ignored"},
|
|
})
|
|
s.Equal([]simple{{12, 6}, {-23, 0}, {0, 555}}, get())
|
|
})
|
|
|
|
s.Run("WrongType", func() {
|
|
s.client.SetValue(testGetTypedPropertyKey, []any{
|
|
map[string]any{"A": false, "B": true},
|
|
})
|
|
s.Equal(def, get())
|
|
})
|
|
}
|
|
|
|
func (s *collectionSuite) TestGetTypedProtoEnum() {
|
|
def := enumspb.ARCHIVAL_STATE_UNSPECIFIED
|
|
setting := dynamicconfig.NewGlobalTypedSetting(
|
|
testGetTypedPropertyKey,
|
|
def,
|
|
"",
|
|
)
|
|
get := setting.Get(s.cln)
|
|
|
|
s.Run("Default", func() {
|
|
s.Equal(def, get())
|
|
})
|
|
|
|
s.Run("Basic", func() {
|
|
s.client.SetValue(testGetTypedPropertyKey, "ARCHIVAL_STATE_DISABLED")
|
|
s.Equal(enumspb.ARCHIVAL_STATE_DISABLED, get())
|
|
})
|
|
|
|
s.Run("CaseInsensitive", func() {
|
|
s.client.SetValue(testGetTypedPropertyKey, "archival_state_disabled")
|
|
s.Equal(enumspb.ARCHIVAL_STATE_DISABLED, get())
|
|
})
|
|
|
|
s.Run("NotFound", func() {
|
|
s.client.SetValue(testGetTypedPropertyKey, "some_other_string")
|
|
s.Equal(def, get())
|
|
})
|
|
|
|
s.Run("Int", func() {
|
|
s.client.SetValue(testGetTypedPropertyKey, 2)
|
|
s.Equal(enumspb.ARCHIVAL_STATE_ENABLED, get())
|
|
})
|
|
|
|
s.Run("WrongType", func() {
|
|
s.client.SetValue(testGetTypedPropertyKey, true)
|
|
s.Equal(def, get())
|
|
})
|
|
}
|
|
|
|
func (s *collectionSuite) TestGetIntPropertyFilteredByDestination() {
|
|
setting := dynamicconfig.NewDestinationIntSetting(testGetIntPropertyFilteredByDestinationKey, 10, "")
|
|
namespaceName := "testNamespace"
|
|
destination1 := "testDestination1"
|
|
destination2 := "testDestination2"
|
|
value := setting.Get(s.cln)
|
|
s.Equal(10, value(namespaceName, destination1))
|
|
s.client.Set(testGetIntPropertyFilteredByDestinationKey, []dynamicconfig.ConstrainedValue{
|
|
{
|
|
Constraints: dynamicconfig.Constraints{
|
|
Namespace: namespaceName,
|
|
Destination: destination1,
|
|
},
|
|
Value: 50,
|
|
},
|
|
{
|
|
Constraints: dynamicconfig.Constraints{
|
|
Namespace: namespaceName,
|
|
},
|
|
Value: 75,
|
|
},
|
|
{
|
|
Constraints: dynamicconfig.Constraints{
|
|
Destination: destination1,
|
|
},
|
|
Value: 90,
|
|
},
|
|
{
|
|
Constraints: dynamicconfig.Constraints{
|
|
Destination: destination2,
|
|
},
|
|
Value: 100,
|
|
},
|
|
})
|
|
s.Equal(50, value(namespaceName, destination1))
|
|
s.Equal(75, value(namespaceName, "testAnotherDestination"))
|
|
s.Equal(90, value("testAnotherNamespace", destination1))
|
|
s.Equal(100, value(namespaceName, destination2)) // priority: destination >>> namespace
|
|
s.Equal(10, value("testAnotherNamespace", "testAnotherDestination"))
|
|
}
|
|
|
|
type (
|
|
subscriptionSuite struct {
|
|
suite.Suite
|
|
client *testSubscribableClient
|
|
cln *dynamicconfig.Collection
|
|
}
|
|
|
|
testSubscribableClient struct {
|
|
lock sync.Mutex
|
|
m map[dynamicconfig.Key][]dynamicconfig.ConstrainedValue
|
|
subs []dynamicconfig.ClientUpdateFunc
|
|
}
|
|
)
|
|
|
|
var _ dynamicconfig.NotifyingClient = (*testSubscribableClient)(nil)
|
|
|
|
func TestSubscriptionSuite(t *testing.T) {
|
|
suite.Run(t, new(subscriptionSuite))
|
|
}
|
|
|
|
func (s *subscriptionSuite) SetupSuite() {
|
|
s.client = newTestSubscribableClient()
|
|
logger := log.NewNoopLogger()
|
|
s.cln = dynamicconfig.NewCollection(s.client, logger)
|
|
s.cln.Start()
|
|
}
|
|
|
|
func (s *subscriptionSuite) TearDownSuite() {
|
|
s.cln.Stop()
|
|
}
|
|
|
|
func (s *subscriptionSuite) SetupTest() {
|
|
dynamicconfig.ResetRegistryForTest()
|
|
}
|
|
|
|
func newTestSubscribableClient() *testSubscribableClient {
|
|
return &testSubscribableClient{
|
|
m: make(map[dynamicconfig.Key][]dynamicconfig.ConstrainedValue),
|
|
}
|
|
}
|
|
|
|
func (c *testSubscribableClient) Subscribe(f dynamicconfig.ClientUpdateFunc) func() {
|
|
c.lock.Lock()
|
|
defer c.lock.Unlock()
|
|
c.subs = append(c.subs, f)
|
|
return func() {} // ignore cancel
|
|
}
|
|
|
|
func (c *testSubscribableClient) GetValue(k dynamicconfig.Key) []dynamicconfig.ConstrainedValue {
|
|
c.lock.Lock()
|
|
defer c.lock.Unlock()
|
|
return c.m[k]
|
|
}
|
|
|
|
func (c *testSubscribableClient) SetValue(k dynamicconfig.Key, v any) {
|
|
c.Set(k, []dynamicconfig.ConstrainedValue{{Value: v}})
|
|
}
|
|
|
|
func (c *testSubscribableClient) Set(k dynamicconfig.Key, cvs []dynamicconfig.ConstrainedValue) {
|
|
c.lock.Lock()
|
|
defer c.lock.Unlock()
|
|
c.m[k] = cvs
|
|
for _, f := range c.subs {
|
|
f(map[dynamicconfig.Key][]dynamicconfig.ConstrainedValue{k: cvs})
|
|
}
|
|
}
|
|
|
|
func (s *subscriptionSuite) TestSubscriptionGlobal() {
|
|
setting := dynamicconfig.NewGlobalBoolSetting(testGetBoolPropertyKey, false, "")
|
|
|
|
vals := make(chan bool, 1)
|
|
cb := func(newVal bool) { vals <- newVal }
|
|
initial, cancel := setting.Subscribe(s.cln)(cb)
|
|
|
|
s.False(initial)
|
|
|
|
s.client.Set(setting.Key(), []dynamicconfig.ConstrainedValue{{Value: true}})
|
|
s.Require().Eventually(func() bool { return len(vals) == 1 }, time.Second, time.Millisecond)
|
|
s.True(<-vals)
|
|
|
|
s.client.Set(setting.Key(), nil) // back to default
|
|
s.Require().Eventually(func() bool { return len(vals) == 1 }, time.Second, time.Millisecond)
|
|
s.False(<-vals)
|
|
|
|
cancel()
|
|
|
|
s.client.Set(setting.Key(), []dynamicconfig.ConstrainedValue{{Value: true}})
|
|
// no update should be delivered
|
|
time.Sleep(10 * time.Millisecond)
|
|
s.Empty(vals, "should not deliver update")
|
|
}
|
|
|
|
func (s *subscriptionSuite) TestSubscriptionGlobal_DoesNotCallUnchanged() {
|
|
setting := dynamicconfig.NewGlobalBoolSetting(testGetBoolPropertyKey, true, "")
|
|
vals := make(chan bool, 1)
|
|
cb := func(newVal bool) { vals <- newVal }
|
|
initial, _ := setting.Subscribe(s.cln)(cb)
|
|
s.True(initial)
|
|
s.client.Set(setting.Key(), []dynamicconfig.ConstrainedValue{{Value: true}})
|
|
time.Sleep(10 * time.Millisecond)
|
|
s.Empty(vals, "should not deliver update")
|
|
}
|
|
|
|
func (s *subscriptionSuite) TestSubscriptionNamespace() {
|
|
setting := dynamicconfig.NewNamespaceIntSetting(testGetIntPropertyKey, 0, "")
|
|
|
|
s.client.Set(setting.Key(), []dynamicconfig.ConstrainedValue{
|
|
{Constraints: dynamicconfig.Constraints{Namespace: "ns1"}, Value: 1},
|
|
{Constraints: dynamicconfig.Constraints{Namespace: "ns3"}, Value: 3},
|
|
})
|
|
|
|
vals1 := make(chan int, 1)
|
|
init1, _ := setting.Subscribe(s.cln)("ns1", func(n int) { vals1 <- n })
|
|
vals2 := make(chan int, 1)
|
|
init2, _ := setting.Subscribe(s.cln)("ns2", func(n int) { vals2 <- n })
|
|
vals3 := make(chan int, 1)
|
|
init3, _ := setting.Subscribe(s.cln)("ns3", func(n int) { vals3 <- n })
|
|
|
|
s.Equal(1, init1)
|
|
s.Equal(0, init2)
|
|
s.Equal(3, init3)
|
|
|
|
// change ns3 to 33
|
|
s.client.Set(setting.Key(), []dynamicconfig.ConstrainedValue{
|
|
{Constraints: dynamicconfig.Constraints{Namespace: "ns1"}, Value: 1},
|
|
{Constraints: dynamicconfig.Constraints{Namespace: "ns3"}, Value: 33},
|
|
})
|
|
|
|
s.Require().Eventually(func() bool { return len(vals3) == 1 }, time.Second, time.Millisecond)
|
|
s.Equal(33, <-vals3)
|
|
s.Empty(vals1)
|
|
s.Empty(vals2)
|
|
|
|
// add ns2
|
|
s.client.Set(setting.Key(), []dynamicconfig.ConstrainedValue{
|
|
{Constraints: dynamicconfig.Constraints{Namespace: "ns1"}, Value: 1},
|
|
{Constraints: dynamicconfig.Constraints{Namespace: "ns2"}, Value: 2},
|
|
{Constraints: dynamicconfig.Constraints{Namespace: "ns3"}, Value: 33},
|
|
})
|
|
s.Require().Eventually(func() bool { return len(vals2) == 1 }, time.Second, time.Millisecond)
|
|
s.Equal(2, <-vals2)
|
|
s.Empty(vals1)
|
|
s.Empty(vals3)
|
|
|
|
// remove ns1 and ns3
|
|
s.client.Set(setting.Key(), []dynamicconfig.ConstrainedValue{
|
|
{Constraints: dynamicconfig.Constraints{Namespace: "ns2"}, Value: 2},
|
|
})
|
|
s.Require().Eventually(
|
|
func() bool { return len(vals1) == 1 && len(vals3) == 1 },
|
|
time.Second, time.Millisecond)
|
|
s.Equal(0, <-vals1)
|
|
s.Empty(vals2)
|
|
s.Equal(0, <-vals3)
|
|
}
|
|
|
|
func (s *subscriptionSuite) TestSubscriptionWithDefault() {
|
|
baseSetting := dynamicconfig.NewGlobalIntSetting(testGetIntPropertyKey, 0, "")
|
|
setting := baseSetting.WithDefault(100)
|
|
|
|
s.client.Set(setting.Key(), []dynamicconfig.ConstrainedValue{{Value: 50}})
|
|
|
|
vals := make(chan int, 1)
|
|
init, _ := setting.Subscribe(s.cln)(func(n int) { vals <- n })
|
|
s.Equal(50, init)
|
|
|
|
// remove, should get default
|
|
s.client.Set(setting.Key(), nil)
|
|
s.Require().Eventually(func() bool { return len(vals) == 1 }, time.Second, time.Millisecond)
|
|
s.Equal(100, <-vals)
|
|
|
|
// test nil callback
|
|
v, cancel := setting.Subscribe(s.cln)(nil)
|
|
s.Equal(100, v)
|
|
s.Nil(cancel)
|
|
}
|