From 165f6e234f8413acac3b44e309d5cd2dfb7a9f0c Mon Sep 17 00:00:00 2001 From: petruki <31597636+petruki@users.noreply.github.com> Date: Thu, 25 Jun 2026 21:01:42 -0700 Subject: [PATCH] feat: added strategy input APIs --- README.md | 11 +- execution_logger_test.go | 30 ++ local_test.go | 287 ++++++++++++++++--- remote_test.go | 29 ++ switcher.go | 48 +++- testdata/snapshots/default_date_only.json | 51 ++++ testdata/snapshots/default_numeric_only.json | 99 +++++++ testdata/snapshots/default_payload_only.json | 39 +++ testdata/snapshots/default_regex_only.json | 63 ++++ testdata/snapshots/default_time_only.json | 51 ++++ 10 files changed, 654 insertions(+), 54 deletions(-) create mode 100644 testdata/snapshots/default_date_only.json create mode 100644 testdata/snapshots/default_numeric_only.json create mode 100644 testdata/snapshots/default_payload_only.json create mode 100644 testdata/snapshots/default_regex_only.json create mode 100644 testdata/snapshots/default_time_only.json diff --git a/README.md b/README.md index 4afff25..7c1edbc 100644 --- a/README.md +++ b/README.md @@ -298,7 +298,7 @@ Load validation data separately, useful for complex applications: ```go prepared := client.GetSwitcher(""). - CheckValue("USER_123") + Check(client.StrategyValue, "USER_123") if err := prepared.Prepare("USER_FEATURE"); err != nil { panic(err) @@ -321,8 +321,12 @@ Chain multiple validation strategies for comprehensive feature control: ```go isEnabled, err := client.GetSwitcher("PREMIUM_FEATURES"). CheckValue("premium_user"). + CheckNumeric("42"). + CheckDate("2026-06-24"). + CheckTime("09:30"). + CheckPayload(`{"tier":"premium","account":{"region":"us"}}`). CheckNetwork("192.168.1.0/24"). - DefaultResult(true). + CheckRegex(`premium_[a-z]+`). Throttle(time.Second). IsOn() @@ -335,6 +339,9 @@ if isEnabled { } ``` +Supported convenience helpers map to the same generic entry-point: `CheckValue`, `CheckNumeric`, +`CheckDate`, `CheckTime`, `CheckPayload`, `CheckNetwork`, and `CheckRegex`. + ### Error Handling Subscribe to error notifications for robust error management: diff --git a/execution_logger_test.go b/execution_logger_test.go index 28bd440..63ea3d2 100644 --- a/execution_logger_test.go +++ b/execution_logger_test.go @@ -77,6 +77,36 @@ func TestExecutionLogger(t *testing.T) { assert.Equal(t, ExecutionEntry{}, logged) }) + t.Run("should log generic strategy checks and retrieve them by switcher", func(t *testing.T) { + server := newRemoteTestServer(t, remoteTestHandlers{ + authStatus: http.StatusOK, + authBody: map[string]any{"token": "[token]", "exp": time.Now().Add(time.Hour).Unix()}, + criteriaStatus: http.StatusOK, + criteriaBody: map[string]any{"result": true}, + }) + defer server.Close() + + client := NewClient(Context{ + Domain: "My Domain", + URL: server.URL, + APIKey: "[YOUR_API_KEY]", + Component: "MyApp", + Options: ContextOptions{ + Logger: true, + }, + }) + + got, err := client.GetSwitcher("MY_SWITCHER").Check(StrategyRegex, "USER_11").IsOn() + + assert.NoError(t, err) + assert.True(t, got) + + logged := client.GetExecution(client.GetSwitcher("MY_SWITCHER").Check(StrategyRegex, "USER_11")) + assert.Equal(t, "MY_SWITCHER", logged.Key) + assert.Equal(t, []ExecutionInput{{Strategy: StrategyRegex, Input: "USER_11"}}, logged.Inputs) + assert.True(t, logged.Response.Result) + }) + t.Run("should return empty execution when the logged entry has inputs and the lookup has none", func(t *testing.T) { server := newRemoteTestServer(t, remoteTestHandlers{ authStatus: http.StatusOK, diff --git a/local_test.go b/local_test.go index d6b39aa..22155af 100644 --- a/local_test.go +++ b/local_test.go @@ -7,7 +7,7 @@ import ( "github.com/stretchr/testify/assert" ) -func TestSwitcherLocalEvaluation(t *testing.T) { +func TestSwitcherLocalEvaluationCommonBehavior(t *testing.T) { t.Run("should use local snapshot to evaluate a switcher without strategies", func(t *testing.T) { useLocalSnapshotFixture(t, "default") @@ -26,103 +26,126 @@ func TestSwitcherLocalEvaluation(t *testing.T) { assert.True(t, got) }) - t.Run("should return disabled when a value strategy does not receive any input", func(t *testing.T) { - useLocalSnapshotFixture(t, "default_value_only") + t.Run("should return disabled when the domain is deactivated", func(t *testing.T) { + useLocalSnapshotFixture(t, "default_disabled") - got, err := GetSwitcher("FF2FOR2020").IsOnWithDetails() + got, err := GetSwitcher("FEATURE").IsOnWithDetails() assert.NoError(t, err) assert.False(t, got.Result) - assert.Equal(t, "Strategy 'VALUE_VALIDATION' did not receive any input", got.Reason) + assert.Equal(t, "Domain is disabled", got.Reason) }) - t.Run("should return enabled when value EXIST matches the input", func(t *testing.T) { - useLocalSnapshotFixture(t, "default_value_only") + t.Run("should return disabled when the group is deactivated", func(t *testing.T) { + useLocalSnapshotFixture(t, "default") - got, err := GetSwitcher("VALUE_EXIST").CheckValue("guest").IsOn() + got, err := GetSwitcher("FF2FOR2040").IsOnWithDetails() + + assert.NoError(t, err) + assert.False(t, got.Result) + assert.Equal(t, "Group disabled", got.Reason) + }) + + t.Run("should return disabled when the config is deactivated", func(t *testing.T) { + useLocalSnapshotFixture(t, "default") + + got, err := GetSwitcher("FF2FOR2031").IsOnWithDetails() + + assert.NoError(t, err) + assert.False(t, got.Result) + assert.Equal(t, "Config disabled", got.Reason) + }) + + t.Run("should return enabled when the strategy is deactivated", func(t *testing.T) { + useLocalSnapshotFixture(t, "default") + + got, err := GetSwitcher("FF2FOR2021").CheckNetwork("10.0.0.3").IsOn() assert.NoError(t, err) assert.True(t, got) }) - t.Run("should return disabled when value EXIST does not match the input", func(t *testing.T) { - useLocalSnapshotFixture(t, "default_value_only") + t.Run("should return disabled when relay is enabled and relay restriction is active", func(t *testing.T) { + useLocalSnapshotFixture(t, "default") - got, err := GetSwitcher("VALUE_EXIST").CheckValue("anonymous").IsOnWithDetails() + got, err := GetSwitcher("USECASE103").IsOnWithDetails() assert.NoError(t, err) assert.False(t, got.Result) - assert.Equal(t, "Strategy 'VALUE_VALIDATION' does not agree", got.Reason) + assert.Equal(t, "Config has relay enabled", got.Reason) }) +} - t.Run("should return disabled when operation is invalid for the strategy", func(t *testing.T) { +func TestSwitcherLocalEvaluationValueStrategies(t *testing.T) { + t.Run("should use local snapshot to evaluate a switcher with the generic check API", func(t *testing.T) { useLocalSnapshotFixture(t, "default_value_only") - got, err := GetSwitcher("HAS_ALL").CheckValue("anonymous").IsOnWithDetails() + got, err := GetSwitcher("FF2FOR2020").Check(StrategyValue, "Japan").IsOn() assert.NoError(t, err) - assert.False(t, got.Result) - assert.Equal(t, "Strategy 'VALUE_VALIDATION' does not agree", got.Reason) + assert.True(t, got) }) - t.Run("should return disabled when strategy input does not match snapshot settings", func(t *testing.T) { + t.Run("should return disabled when a value strategy does not receive any input", func(t *testing.T) { useLocalSnapshotFixture(t, "default_value_only") - got, err := GetSwitcher("FF2FOR2020").CheckNetwork("10.0.0.3").IsOnWithDetails() + got, err := GetSwitcher("FF2FOR2020").IsOnWithDetails() assert.NoError(t, err) assert.False(t, got.Result) - assert.Equal(t, "Strategy 'VALUE_VALIDATION' does not agree", got.Reason) + assert.Equal(t, "Strategy 'VALUE_VALIDATION' did not receive any input", got.Reason) }) - t.Run("should return enabled when value EQUAL matches the input", func(t *testing.T) { + t.Run("should return enabled when value EXIST matches the input", func(t *testing.T) { useLocalSnapshotFixture(t, "default_value_only") - got, err := GetSwitcher("VALUE_EQUAL").CheckValue("pro-user").IsOn() + got, err := GetSwitcher("VALUE_EXIST").CheckValue("guest").IsOn() assert.NoError(t, err) assert.True(t, got) }) - t.Run("should return disabled when the domain is deactivated", func(t *testing.T) { - useLocalSnapshotFixture(t, "default_disabled") + t.Run("should return disabled when value EXIST does not match the input", func(t *testing.T) { + useLocalSnapshotFixture(t, "default_value_only") - got, err := GetSwitcher("FEATURE").IsOnWithDetails() + got, err := GetSwitcher("VALUE_EXIST").CheckValue("anonymous").IsOnWithDetails() assert.NoError(t, err) assert.False(t, got.Result) - assert.Equal(t, "Domain is disabled", got.Reason) + assert.Equal(t, "Strategy 'VALUE_VALIDATION' does not agree", got.Reason) }) - t.Run("should return disabled when the group is deactivated", func(t *testing.T) { - useLocalSnapshotFixture(t, "default") + t.Run("should return disabled when operation is invalid for the strategy", func(t *testing.T) { + useLocalSnapshotFixture(t, "default_value_only") - got, err := GetSwitcher("FF2FOR2040").IsOnWithDetails() + got, err := GetSwitcher("HAS_ALL").CheckValue("anonymous").IsOnWithDetails() assert.NoError(t, err) assert.False(t, got.Result) - assert.Equal(t, "Group disabled", got.Reason) + assert.Equal(t, "Strategy 'VALUE_VALIDATION' does not agree", got.Reason) }) - t.Run("should return disabled when the config is deactivated", func(t *testing.T) { - useLocalSnapshotFixture(t, "default") + t.Run("should return disabled when strategy input does not match snapshot settings", func(t *testing.T) { + useLocalSnapshotFixture(t, "default_value_only") - got, err := GetSwitcher("FF2FOR2031").IsOnWithDetails() + got, err := GetSwitcher("FF2FOR2020").CheckNetwork("10.0.0.3").IsOnWithDetails() assert.NoError(t, err) assert.False(t, got.Result) - assert.Equal(t, "Config disabled", got.Reason) + assert.Equal(t, "Strategy 'VALUE_VALIDATION' does not agree", got.Reason) }) - t.Run("should return enabled when the strategy is deactivated", func(t *testing.T) { - useLocalSnapshotFixture(t, "default") + t.Run("should return enabled when value EQUAL matches the input", func(t *testing.T) { + useLocalSnapshotFixture(t, "default_value_only") - got, err := GetSwitcher("FF2FOR2021").CheckNetwork("10.0.0.3").IsOn() + got, err := GetSwitcher("VALUE_EQUAL").CheckValue("pro-user").IsOn() assert.NoError(t, err) assert.True(t, got) }) +} +func TestSwitcherLocalEvaluationNetworkStrategies(t *testing.T) { t.Run("should return enabled when the network input is inside a CIDR range", func(t *testing.T) { useLocalSnapshotFixture(t, "default_network_only") @@ -179,17 +202,199 @@ func TestSwitcherLocalEvaluation(t *testing.T) { assert.False(t, got.Result) assert.Equal(t, "Strategy 'NETWORK_VALIDATION' does not agree", got.Reason) }) +} - t.Run("should return disabled when relay is enabled and relay restriction is active", func(t *testing.T) { - useLocalSnapshotFixture(t, "default") +func TestSwitcherLocalEvaluationNumericStrategies(t *testing.T) { + t.Run("should return enabled when numeric EXIST matches the input", func(t *testing.T) { + useLocalSnapshotFixture(t, "default_numeric_only") - got, err := GetSwitcher("USECASE103").IsOnWithDetails() + got, err := GetSwitcher("NUMERIC_EXIST").CheckNumeric("3").IsOn() assert.NoError(t, err) - assert.False(t, got.Result) - assert.Equal(t, "Config has relay enabled", got.Reason) + assert.True(t, got) + }) + + t.Run("should return enabled when numeric NOT_EXIST does not match the input", func(t *testing.T) { + useLocalSnapshotFixture(t, "default_numeric_only") + + got, err := GetSwitcher("NUMERIC_NOT_EXIST").CheckNumeric("2").IsOn() + + assert.NoError(t, err) + assert.True(t, got) + }) + + t.Run("should return enabled when numeric EQUAL matches the input", func(t *testing.T) { + useLocalSnapshotFixture(t, "default_numeric_only") + + got, err := GetSwitcher("NUMERIC_EQUAL").CheckNumeric("7").IsOn() + + assert.NoError(t, err) + assert.True(t, got) }) + t.Run("should return enabled when numeric NOT_EQUAL differs from the input", func(t *testing.T) { + useLocalSnapshotFixture(t, "default_numeric_only") + + got, err := GetSwitcher("NUMERIC_NOT_EQUAL").CheckNumeric("8").IsOn() + + assert.NoError(t, err) + assert.True(t, got) + }) + + t.Run("should return enabled when numeric GREATER matches the input", func(t *testing.T) { + useLocalSnapshotFixture(t, "default_numeric_only") + + got, err := GetSwitcher("NUMERIC_GREATER").CheckNumeric("11").IsOn() + + assert.NoError(t, err) + assert.True(t, got) + }) + + t.Run("should return enabled when numeric LOWER matches the input", func(t *testing.T) { + useLocalSnapshotFixture(t, "default_numeric_only") + + got, err := GetSwitcher("NUMERIC_LOWER").CheckNumeric("9").IsOn() + + assert.NoError(t, err) + assert.True(t, got) + }) + + t.Run("should return enabled when numeric BETWEEN matches the input", func(t *testing.T) { + useLocalSnapshotFixture(t, "default_numeric_only") + + got, err := GetSwitcher("NUMERIC_BETWEEN").CheckNumeric("15").IsOn() + + assert.NoError(t, err) + assert.True(t, got) + }) +} + +func TestSwitcherLocalEvaluationDateStrategies(t *testing.T) { + t.Run("should return enabled when date LOWER matches the input", func(t *testing.T) { + useLocalSnapshotFixture(t, "default_date_only") + + got, err := GetSwitcher("DATE_LOWER").CheckDate("2019-11-30").IsOn() + + assert.NoError(t, err) + assert.True(t, got) + }) + + t.Run("should return enabled when date GREATER matches the input", func(t *testing.T) { + useLocalSnapshotFixture(t, "default_date_only") + + got, err := GetSwitcher("DATE_GREATER").CheckDate("2019-12-02").IsOn() + + assert.NoError(t, err) + assert.True(t, got) + }) + + t.Run("should return enabled when date BETWEEN matches the input", func(t *testing.T) { + useLocalSnapshotFixture(t, "default_date_only") + + got, err := GetSwitcher("DATE_BETWEEN").CheckDate("2019-12-03").IsOn() + + assert.NoError(t, err) + assert.True(t, got) + }) +} + +func TestSwitcherLocalEvaluationTimeStrategies(t *testing.T) { + t.Run("should return enabled when time LOWER matches the input", func(t *testing.T) { + useLocalSnapshotFixture(t, "default_time_only") + + got, err := GetSwitcher("TIME_LOWER").CheckTime("06:00").IsOn() + + assert.NoError(t, err) + assert.True(t, got) + }) + + t.Run("should return enabled when time GREATER matches the input", func(t *testing.T) { + useLocalSnapshotFixture(t, "default_time_only") + + got, err := GetSwitcher("TIME_GREATER").CheckTime("10:00").IsOn() + + assert.NoError(t, err) + assert.True(t, got) + }) + + t.Run("should return enabled when time BETWEEN matches the input", func(t *testing.T) { + useLocalSnapshotFixture(t, "default_time_only") + + got, err := GetSwitcher("TIME_BETWEEN").CheckTime("09:00").IsOn() + + assert.NoError(t, err) + assert.True(t, got) + }) +} + +func TestSwitcherLocalEvaluationPayloadStrategies(t *testing.T) { + t.Run("should return enabled when payload HAS_ONE matches the input", func(t *testing.T) { + useLocalSnapshotFixture(t, "default_payload_only") + + got, err := GetSwitcher("PAYLOAD_HAS_ONE").CheckPayload(`{"id":"1","order":{"qty":2}}`).IsOn() + + assert.NoError(t, err) + assert.True(t, got) + }) + + t.Run("should return enabled when payload HAS_ALL matches the input", func(t *testing.T) { + useLocalSnapshotFixture(t, "default_payload_only") + + got, err := GetSwitcher("PAYLOAD_HAS_ALL").CheckPayload(`{"id":"1","user":{"role":"admin"}}`).IsOn() + + assert.NoError(t, err) + assert.True(t, got) + }) +} + +func TestSwitcherLocalEvaluationRegexStrategies(t *testing.T) { + t.Run("should return enabled when regex EXIST matches the input", func(t *testing.T) { + useLocalSnapshotFixture(t, "default_regex_only") + + got, err := GetSwitcher("REGEX_EXIST").CheckRegex("USER_11").IsOn() + + assert.NoError(t, err) + assert.True(t, got) + }) + + t.Run("should return enabled when regex NOT_EXIST does not match the input", func(t *testing.T) { + useLocalSnapshotFixture(t, "default_regex_only") + + got, err := GetSwitcher("REGEX_NOT_EXIST").CheckRegex("USER_123").IsOn() + + assert.NoError(t, err) + assert.True(t, got) + }) + + t.Run("should return enabled when regex EQUAL matches the input", func(t *testing.T) { + useLocalSnapshotFixture(t, "default_regex_only") + + got, err := GetSwitcher("REGEX_EQUAL").CheckRegex("USER_11").IsOn() + + assert.NoError(t, err) + assert.True(t, got) + }) + + t.Run("should return enabled when regex NOT_EQUAL differs from the input", func(t *testing.T) { + useLocalSnapshotFixture(t, "default_regex_only") + + got, err := GetSwitcher("REGEX_NOT_EQUAL").CheckRegex("USER_123").IsOn() + + assert.NoError(t, err) + assert.True(t, got) + }) + + t.Run("should use the generic check API with non-value strategies", func(t *testing.T) { + useLocalSnapshotFixture(t, "default_regex_only") + + got, err := GetSwitcher("REGEX_EQUAL").Check(StrategyRegex, "USER_11").IsOn() + + assert.NoError(t, err) + assert.True(t, got) + }) +} + +func TestSwitcherLocalEvaluationErrors(t *testing.T) { t.Run("should return an error when the key is not found in the snapshot", func(t *testing.T) { useLocalSnapshotFixture(t, "default") diff --git a/remote_test.go b/remote_test.go index 7233ac2..927991d 100644 --- a/remote_test.go +++ b/remote_test.go @@ -64,6 +64,35 @@ func TestSwitcherRemoteEvaluation(t *testing.T) { }, captured) }) + t.Run("should send generic strategy inputs to the remote criteria endpoint", func(t *testing.T) { + var captured map[string]any + server := newRemoteTestServer(t, remoteTestHandlers{ + authStatus: http.StatusOK, + authBody: map[string]any{"token": "[token]", "exp": time.Now().Add(time.Hour).Unix()}, + criteriaStatus: http.StatusOK, + criteriaBody: map[string]any{"result": true}, + onCriteriaRequest: func(body map[string]any, _ *http.Request) { + captured = body + }, + }) + defer server.Close() + + client := newRemoteTestClient(server.URL) + + got, err := client.GetSwitcher("MY_SWITCHER").Check(StrategyNumeric, "7").IsOn() + + assert.NoError(t, err) + assert.True(t, got) + assert.Equal(t, map[string]any{ + "entry": []any{ + map[string]any{ + "strategy": StrategyNumeric, + "input": "7", + }, + }, + }, captured) + }) + t.Run("should return response from the remote API without requesting details", func(t *testing.T) { server := newRemoteTestServer(t, remoteTestHandlers{ authStatus: http.StatusOK, diff --git a/switcher.go b/switcher.go index 3f0e3a3..e75ca7d 100644 --- a/switcher.go +++ b/switcher.go @@ -62,32 +62,58 @@ func (s *Switcher) Validate() error { return nil } -// CheckValue appends a value-based strategy input to this Switcher and returns the Switcher +// Check appends or replaces a strategy input on this Switcher and returns the Switcher // to allow method chaining (fluent API). -func (s *Switcher) CheckValue(input string) *Switcher { +func (s *Switcher) Check(strategy, input string) *Switcher { s.mu.Lock() defer s.mu.Unlock() s.entries = upsertEntry(s.entries, criteriaEntry{ - Strategy: StrategyValue, + Strategy: strategy, Input: input, }) return s } +// CheckValue appends a value-based strategy input to this Switcher and returns the Switcher +// to allow method chaining (fluent API). +func (s *Switcher) CheckValue(input string) *Switcher { + return s.Check(StrategyValue, input) +} + +// CheckNumeric appends a numeric strategy input to this Switcher and returns the Switcher +// for chaining. +func (s *Switcher) CheckNumeric(input string) *Switcher { + return s.Check(StrategyNumeric, input) +} + +// CheckDate appends a date strategy input to this Switcher and returns the Switcher for chaining. +func (s *Switcher) CheckDate(input string) *Switcher { + return s.Check(StrategyDate, input) +} + +// CheckTime appends a time strategy input to this Switcher and returns the Switcher for chaining. +func (s *Switcher) CheckTime(input string) *Switcher { + return s.Check(StrategyTime, input) +} + +// CheckPayload appends a payload strategy input to this Switcher and returns the Switcher +// for chaining. +func (s *Switcher) CheckPayload(input string) *Switcher { + return s.Check(StrategyPayload, input) +} + // CheckNetwork appends a network-based strategy input (e.g., IP or CIDR) to this Switcher // and returns the Switcher for chaining. func (s *Switcher) CheckNetwork(input string) *Switcher { - s.mu.Lock() - defer s.mu.Unlock() - - s.entries = upsertEntry(s.entries, criteriaEntry{ - Strategy: StrategyNetwork, - Input: input, - }) + return s.Check(StrategyNetwork, input) +} - return s +// CheckRegex appends a regex strategy input to this Switcher and returns the Switcher +// for chaining. +func (s *Switcher) CheckRegex(input string) *Switcher { + return s.Check(StrategyRegex, input) } // Throttle enables stale-while-revalidate behavior for this Switcher. When enabled the SDK diff --git a/testdata/snapshots/default_date_only.json b/testdata/snapshots/default_date_only.json new file mode 100644 index 0000000..4d4b638 --- /dev/null +++ b/testdata/snapshots/default_date_only.json @@ -0,0 +1,51 @@ +{ + "domain": { + "name": "Business", + "activated": true, + "version": 1, + "group": [ + { + "name": "Date tests", + "activated": true, + "config": [ + { + "key": "DATE_LOWER", + "activated": true, + "strategies": [ + { + "strategy": "DATE_VALIDATION", + "activated": true, + "operation": "LOWER", + "values": ["2019-12-01"] + } + ] + }, + { + "key": "DATE_GREATER", + "activated": true, + "strategies": [ + { + "strategy": "DATE_VALIDATION", + "activated": true, + "operation": "GREATER", + "values": ["2019-12-01"] + } + ] + }, + { + "key": "DATE_BETWEEN", + "activated": true, + "strategies": [ + { + "strategy": "DATE_VALIDATION", + "activated": true, + "operation": "BETWEEN", + "values": ["2019-12-01", "2019-12-05"] + } + ] + } + ] + } + ] + } +} diff --git a/testdata/snapshots/default_numeric_only.json b/testdata/snapshots/default_numeric_only.json new file mode 100644 index 0000000..b9cbe11 --- /dev/null +++ b/testdata/snapshots/default_numeric_only.json @@ -0,0 +1,99 @@ +{ + "domain": { + "name": "Business", + "activated": true, + "version": 1, + "group": [ + { + "name": "Numeric tests", + "activated": true, + "config": [ + { + "key": "NUMERIC_EXIST", + "activated": true, + "strategies": [ + { + "strategy": "NUMERIC_VALIDATION", + "activated": true, + "operation": "EXIST", + "values": ["1", "3"] + } + ] + }, + { + "key": "NUMERIC_NOT_EXIST", + "activated": true, + "strategies": [ + { + "strategy": "NUMERIC_VALIDATION", + "activated": true, + "operation": "NOT_EXIST", + "values": ["1", "3"] + } + ] + }, + { + "key": "NUMERIC_EQUAL", + "activated": true, + "strategies": [ + { + "strategy": "NUMERIC_VALIDATION", + "activated": true, + "operation": "EQUAL", + "values": ["7"] + } + ] + }, + { + "key": "NUMERIC_NOT_EQUAL", + "activated": true, + "strategies": [ + { + "strategy": "NUMERIC_VALIDATION", + "activated": true, + "operation": "NOT_EQUAL", + "values": ["7"] + } + ] + }, + { + "key": "NUMERIC_GREATER", + "activated": true, + "strategies": [ + { + "strategy": "NUMERIC_VALIDATION", + "activated": true, + "operation": "GREATER", + "values": ["10"] + } + ] + }, + { + "key": "NUMERIC_LOWER", + "activated": true, + "strategies": [ + { + "strategy": "NUMERIC_VALIDATION", + "activated": true, + "operation": "LOWER", + "values": ["10"] + } + ] + }, + { + "key": "NUMERIC_BETWEEN", + "activated": true, + "strategies": [ + { + "strategy": "NUMERIC_VALIDATION", + "activated": true, + "operation": "BETWEEN", + "values": ["10", "20"] + } + ] + } + ] + } + ] + } +} diff --git a/testdata/snapshots/default_payload_only.json b/testdata/snapshots/default_payload_only.json new file mode 100644 index 0000000..0e798ca --- /dev/null +++ b/testdata/snapshots/default_payload_only.json @@ -0,0 +1,39 @@ +{ + "domain": { + "name": "Business", + "activated": true, + "version": 1, + "group": [ + { + "name": "Payload tests", + "activated": true, + "config": [ + { + "key": "PAYLOAD_HAS_ONE", + "activated": true, + "strategies": [ + { + "strategy": "PAYLOAD_VALIDATION", + "activated": true, + "operation": "HAS_ONE", + "values": ["missing", "order.qty"] + } + ] + }, + { + "key": "PAYLOAD_HAS_ALL", + "activated": true, + "strategies": [ + { + "strategy": "PAYLOAD_VALIDATION", + "activated": true, + "operation": "HAS_ALL", + "values": ["id", "user", "user.role"] + } + ] + } + ] + } + ] + } +} diff --git a/testdata/snapshots/default_regex_only.json b/testdata/snapshots/default_regex_only.json new file mode 100644 index 0000000..903e89c --- /dev/null +++ b/testdata/snapshots/default_regex_only.json @@ -0,0 +1,63 @@ +{ + "domain": { + "name": "Business", + "activated": true, + "version": 1, + "group": [ + { + "name": "Regex tests", + "activated": true, + "config": [ + { + "key": "REGEX_EXIST", + "activated": true, + "strategies": [ + { + "strategy": "REGEX_VALIDATION", + "activated": true, + "operation": "EXIST", + "values": ["\\bUSER_[0-9]{1,2}\\b"] + } + ] + }, + { + "key": "REGEX_NOT_EXIST", + "activated": true, + "strategies": [ + { + "strategy": "REGEX_VALIDATION", + "activated": true, + "operation": "NOT_EXIST", + "values": ["\\bUSER_[0-9]{1,2}\\b"] + } + ] + }, + { + "key": "REGEX_EQUAL", + "activated": true, + "strategies": [ + { + "strategy": "REGEX_VALIDATION", + "activated": true, + "operation": "EQUAL", + "values": ["USER_[0-9]{1,2}"] + } + ] + }, + { + "key": "REGEX_NOT_EQUAL", + "activated": true, + "strategies": [ + { + "strategy": "REGEX_VALIDATION", + "activated": true, + "operation": "NOT_EQUAL", + "values": ["USER_[0-9]{1,2}"] + } + ] + } + ] + } + ] + } +} diff --git a/testdata/snapshots/default_time_only.json b/testdata/snapshots/default_time_only.json new file mode 100644 index 0000000..62b442e --- /dev/null +++ b/testdata/snapshots/default_time_only.json @@ -0,0 +1,51 @@ +{ + "domain": { + "name": "Business", + "activated": true, + "version": 1, + "group": [ + { + "name": "Time tests", + "activated": true, + "config": [ + { + "key": "TIME_LOWER", + "activated": true, + "strategies": [ + { + "strategy": "TIME_VALIDATION", + "activated": true, + "operation": "LOWER", + "values": ["08:00"] + } + ] + }, + { + "key": "TIME_GREATER", + "activated": true, + "strategies": [ + { + "strategy": "TIME_VALIDATION", + "activated": true, + "operation": "GREATER", + "values": ["08:00"] + } + ] + }, + { + "key": "TIME_BETWEEN", + "activated": true, + "strategies": [ + { + "strategy": "TIME_VALIDATION", + "activated": true, + "operation": "BETWEEN", + "values": ["08:00", "10:00"] + } + ] + } + ] + } + ] + } +}