From d94223d25d2da94cf320640fa69c3f81bc59fb83 Mon Sep 17 00:00:00 2001 From: Jeffy Mathew Date: Mon, 19 Aug 2024 10:35:57 +0200 Subject: [PATCH] refactor tests to tests/rate --- gateway/mw_rate_limiting_test.go | 217 ------------------------- tests/rate/endpoint_rate_limit_test.go | 211 ++++++++++++++++++++++++ 2 files changed, 211 insertions(+), 217 deletions(-) create mode 100644 tests/rate/endpoint_rate_limit_test.go diff --git a/gateway/mw_rate_limiting_test.go b/gateway/mw_rate_limiting_test.go index 3bd42bf19e9a..f0efcb4cd316 100644 --- a/gateway/mw_rate_limiting_test.go +++ b/gateway/mw_rate_limiting_test.go @@ -5,8 +5,6 @@ import ( "testing" "time" - "github.com/TykTechnologies/tyk/config" - "github.com/stretchr/testify/assert" "github.com/TykTechnologies/graphql-go-tools/pkg/graphql" @@ -396,218 +394,3 @@ func TestMwRateLimiting_CustomRatelimitKeyDRL(t *testing.T) { func TestMwRateLimiting_CustomRatelimitKeyNonTransactional(t *testing.T) { providerCustomRatelimitKey(t, "NonTransactional") } - -func rlTestRunnerProvider(t *testing.T, hashKey bool, hashAlgo string, limiter string) *Test { - ts := StartTest(func(globalConf *config.Config) { - globalConf.HashKeys = hashKey - globalConf.HashKeyFunction = hashAlgo - - switch limiter { - case "Redis": - globalConf.RateLimit.EnableRedisRollingLimiter = true - case "Sentinel": - globalConf.RateLimit.EnableSentinelRateLimiter = true - case "DRL": - globalConf.RateLimit.DRLEnableSentinelRateLimiter = true - case "NonTransactional": - globalConf.RateLimit.EnableNonTransactionalRateLimiter = true - default: - t.Fatal("There is no such a rate limiter:", limiter) - } - }) - - ok := ts.Gw.GlobalSessionManager.Store().DeleteAllKeys() - assert.True(t, ok) - - return ts -} - -func endpointRateLimitTestHelper(t *testing.T, limiter string, beforeFn func()) { - t.Helper() - type rlTestCase struct { - name string - hashKey bool - hashAlgo string - } - - var rlTestCases = []rlTestCase{ - { - name: "hash_key false", - hashKey: false, - }, - { - name: "hash_key true murmur64", - hashKey: true, - hashAlgo: "murmur64", - }, - { - name: "hash_key true murmur32", - hashKey: true, - hashAlgo: "murmur32", - }, - { - name: "hash_key true sha256", - hashKey: true, - hashAlgo: "sha256", - }, - } - - for _, tc := range rlTestCases { - tc := tc - t.Run(tc.name, func(t *testing.T) { - ts := StartTest(nil) - defer ts.Close() - - globalConf := ts.Gw.GetConfig() - - globalConf.HashKeys = tc.hashKey - globalConf.HashKeyFunction = tc.hashAlgo - - switch limiter { - case "Redis": - globalConf.RateLimit.EnableRedisRollingLimiter = true - case "Sentinel": - globalConf.RateLimit.EnableSentinelRateLimiter = true - case "DRL": - globalConf.RateLimit.DRLEnableSentinelRateLimiter = true - case "NonTransactional": - globalConf.RateLimit.EnableNonTransactionalRateLimiter = true - default: - t.Fatal("There is no such a rate limiter:", limiter) - } - - ts.Gw.SetConfig(globalConf) - - ok := ts.Gw.GlobalSessionManager.Store().DeleteAllKeys() - assert.True(t, ok) - - apis := ts.Gw.BuildAndLoadAPI(func(spec *APISpec) { - spec.UseKeylessAccess = false - spec.Proxy.ListenPath = "/api-1" - }, func(spec *APISpec) { - spec.UseKeylessAccess = false - spec.Proxy.ListenPath = "/api-2" - }, func(spec *APISpec) { - spec.UseKeylessAccess = false - spec.Proxy.ListenPath = "/api-3" - }) - - api1, api2, api3 := apis[0], apis[1], apis[2] - - _, endpointRLKey := ts.CreateSession(func(s *user.SessionState) { - s.Rate = 2 - s.Per = 1000 - s.AccessRights = map[string]user.AccessDefinition{ - api1.APIID: { - APIID: api1.APIID, - APIName: api1.Name, - Limit: user.APILimit{ - Rate: 3, - Per: 1000, - }, - Endpoints: []user.Endpoint{ - { - Path: "/get", - Methods: []user.EndpointMethod{ - { - Name: http.MethodGet, - Limit: user.EndpointMethodRateLimit{ - Rate: 5, - Per: 1000, - }, - }, - }, - }, - { - Path: "/post", - Methods: []user.EndpointMethod{ - { - Name: http.MethodPost, - Limit: user.EndpointMethodRateLimit{ - Rate: 4, - Per: 1000, - }, - }, - }, - }, - }, - }, - api2.APIID: { - APIID: api2.APIID, - APIName: api2.Name, - Limit: user.APILimit{ - Rate: 3, - Per: 1000, - }, - }, - api3.APIID: { - APIID: api3.APIID, - APIName: api3.Name, - }, - } - }) - - authHeaders := map[string]string{header.Authorization: endpointRLKey} - - _, _ = ts.Run(t, []test.TestCase{ - // first 3 calls should pass through for an endpoint that is not specified for api-1. - {Headers: authHeaders, Path: "/api-1", Code: http.StatusOK}, - {Headers: authHeaders, Path: "/api-1", Code: http.StatusOK}, - {Headers: authHeaders, Path: "/api-1", Code: http.StatusOK}, - - {BeforeFn: beforeFn, Headers: authHeaders, Path: "/api-1", Code: http.StatusTooManyRequests}, - - // GET /get endpoint should have separate RL counter for api-1. - {Headers: authHeaders, Path: "/api-1/get", Code: http.StatusOK}, - {Headers: authHeaders, Path: "/api-1/get", Code: http.StatusOK}, - {Headers: authHeaders, Path: "/api-1/get", Code: http.StatusOK}, - {Headers: authHeaders, Path: "/api-1/get", Code: http.StatusOK}, - {Headers: authHeaders, Path: "/api-1/get", Code: http.StatusOK}, - - {BeforeFn: beforeFn, Headers: authHeaders, Path: "/api-1/get", Code: http.StatusTooManyRequests}, - - // POST /post endpoint should have separate RL for api-1. - {Headers: authHeaders, Method: http.MethodPost, Path: "/api-1/post", Code: http.StatusOK}, - {Headers: authHeaders, Method: http.MethodPost, Path: "/api-1/post", Code: http.StatusOK}, - {Headers: authHeaders, Method: http.MethodPost, Path: "/api-1/post", Code: http.StatusOK}, - {Headers: authHeaders, Method: http.MethodPost, Path: "/api-1/post", Code: http.StatusOK}, - - {BeforeFn: beforeFn, Headers: authHeaders, Method: http.MethodPost, Path: "/api-1/post", Code: http.StatusTooManyRequests}, - - // GET /status/200 should use API level rate limit of api-1. - {Headers: authHeaders, Path: "/api-1/status/200", Code: http.StatusTooManyRequests}, - - // all endpoints should be using API level rate limit for api-2. - {Headers: authHeaders, Path: "/api-2/get", Code: http.StatusOK}, - {Headers: authHeaders, Method: http.MethodPost, Path: "/api-2/post", Code: http.StatusOK}, - {Headers: authHeaders, Path: "/api-2/status/200", Code: http.StatusOK}, - - {BeforeFn: beforeFn, Headers: authHeaders, Path: "/api-1/status/200", Code: http.StatusTooManyRequests}, - - // api-3 should be using global rate limit. - {Headers: authHeaders, Path: "/api-3/get", Code: http.StatusOK}, - {Headers: authHeaders, Method: http.MethodPost, Path: "/api-3/post", Code: http.StatusOK}, - {BeforeFn: beforeFn, Headers: authHeaders, Path: "/api-3/status/200", Code: http.StatusTooManyRequests}, - }...) - }) - } -} - -func TestMWRateLimiting_EndpointRL_NonTransactional(t *testing.T) { - endpointRateLimitTestHelper(t, "NonTransactional", nil) -} - -func TestMWRateLimiting_EndpointRL_Redis(t *testing.T) { - endpointRateLimitTestHelper(t, "Redis", nil) -} - -func TestMWRateLimiting_EndpointRL_Sentinel(t *testing.T) { - // add a small delay before expecting rate limit exceeded in sentinel rate limiter. - endpointRateLimitTestHelper(t, "Sentinel", func() { - time.Sleep(time.Millisecond * 5) - }) -} - -func TestMWRateLimiting_EndpointRL_DRL(t *testing.T) { - endpointRateLimitTestHelper(t, "DRL", nil) -} diff --git a/tests/rate/endpoint_rate_limit_test.go b/tests/rate/endpoint_rate_limit_test.go new file mode 100644 index 000000000000..4e0a4144111c --- /dev/null +++ b/tests/rate/endpoint_rate_limit_test.go @@ -0,0 +1,211 @@ +package rate_test + +import ( + "net/http" + "testing" + "time" + + "github.com/stretchr/testify/assert" + + . "github.com/TykTechnologies/tyk/gateway" + "github.com/TykTechnologies/tyk/header" + "github.com/TykTechnologies/tyk/test" + "github.com/TykTechnologies/tyk/user" +) + +func rlTestRunnerProvider(t *testing.T, hashKey bool, hashAlgo string, limiter string) *Test { + t.Helper() + ts := StartTest(nil) + defer ts.Close() + + globalConf := ts.Gw.GetConfig() + + globalConf.HashKeys = hashKey + globalConf.HashKeyFunction = hashAlgo + + switch limiter { + case "Redis": + globalConf.RateLimit.EnableRedisRollingLimiter = true + case "Sentinel": + globalConf.RateLimit.EnableSentinelRateLimiter = true + case "DRL": + globalConf.RateLimit.DRLEnableSentinelRateLimiter = true + case "NonTransactional": + globalConf.RateLimit.EnableNonTransactionalRateLimiter = true + default: + t.Fatal("There is no such a rate limiter:", limiter) + } + + ts.Gw.SetConfig(globalConf) + + ok := ts.Gw.GlobalSessionManager.Store().DeleteAllKeys() + assert.True(t, ok) + + return ts +} + +func endpointRateLimitTestHelper(t *testing.T, limiter string, beforeFn func()) { + t.Helper() + type rlTestCase struct { + name string + hashKey bool + hashAlgo string + } + + var rlTestCases = []rlTestCase{ + { + name: "hash_key false", + hashKey: false, + }, + { + name: "hash_key true murmur64", + hashKey: true, + hashAlgo: "murmur64", + }, + { + name: "hash_key true murmur32", + hashKey: true, + hashAlgo: "murmur32", + }, + { + name: "hash_key true sha256", + hashKey: true, + hashAlgo: "sha256", + }, + } + + for _, tc := range rlTestCases { + tc := tc + t.Run(tc.name, func(t *testing.T) { + ts := rlTestRunnerProvider(t, tc.hashKey, tc.hashAlgo, limiter) + defer ts.Close() + apis := ts.Gw.BuildAndLoadAPI(func(spec *APISpec) { + spec.UseKeylessAccess = false + spec.Proxy.ListenPath = "/api-1" + }, func(spec *APISpec) { + spec.UseKeylessAccess = false + spec.Proxy.ListenPath = "/api-2" + }, func(spec *APISpec) { + spec.UseKeylessAccess = false + spec.Proxy.ListenPath = "/api-3" + }) + + api1, api2, api3 := apis[0], apis[1], apis[2] + + _, endpointRLKey := ts.CreateSession(func(s *user.SessionState) { + s.Rate = 2 + s.Per = 1000 + s.AccessRights = map[string]user.AccessDefinition{ + api1.APIID: { + APIID: api1.APIID, + APIName: api1.Name, + Limit: user.APILimit{ + Rate: 3, + Per: 1000, + }, + Endpoints: []user.Endpoint{ + { + Path: "/get", + Methods: []user.EndpointMethod{ + { + Name: http.MethodGet, + Limit: user.EndpointMethodRateLimit{ + Rate: 5, + Per: 1000, + }, + }, + }, + }, + { + Path: "/post", + Methods: []user.EndpointMethod{ + { + Name: http.MethodPost, + Limit: user.EndpointMethodRateLimit{ + Rate: 4, + Per: 1000, + }, + }, + }, + }, + }, + }, + api2.APIID: { + APIID: api2.APIID, + APIName: api2.Name, + Limit: user.APILimit{ + Rate: 3, + Per: 1000, + }, + }, + api3.APIID: { + APIID: api3.APIID, + APIName: api3.Name, + }, + } + }) + + authHeaders := map[string]string{header.Authorization: endpointRLKey} + + _, _ = ts.Run(t, []test.TestCase{ + // first 3 calls should pass through for an endpoint that is not specified for api-1. + {Headers: authHeaders, Path: "/api-1", Code: http.StatusOK}, + {Headers: authHeaders, Path: "/api-1", Code: http.StatusOK}, + {Headers: authHeaders, Path: "/api-1", Code: http.StatusOK}, + + {BeforeFn: beforeFn, Headers: authHeaders, Path: "/api-1", Code: http.StatusTooManyRequests}, + + // GET /get endpoint should have separate RL counter for api-1. + {Headers: authHeaders, Path: "/api-1/get", Code: http.StatusOK}, + {Headers: authHeaders, Path: "/api-1/get", Code: http.StatusOK}, + {Headers: authHeaders, Path: "/api-1/get", Code: http.StatusOK}, + {Headers: authHeaders, Path: "/api-1/get", Code: http.StatusOK}, + {Headers: authHeaders, Path: "/api-1/get", Code: http.StatusOK}, + + {BeforeFn: beforeFn, Headers: authHeaders, Path: "/api-1/get", Code: http.StatusTooManyRequests}, + + // POST /post endpoint should have separate RL for api-1. + {Headers: authHeaders, Method: http.MethodPost, Path: "/api-1/post", Code: http.StatusOK}, + {Headers: authHeaders, Method: http.MethodPost, Path: "/api-1/post", Code: http.StatusOK}, + {Headers: authHeaders, Method: http.MethodPost, Path: "/api-1/post", Code: http.StatusOK}, + {Headers: authHeaders, Method: http.MethodPost, Path: "/api-1/post", Code: http.StatusOK}, + + {BeforeFn: beforeFn, Headers: authHeaders, Method: http.MethodPost, Path: "/api-1/post", Code: http.StatusTooManyRequests}, + + // GET /status/200 should use API level rate limit of api-1. + {Headers: authHeaders, Path: "/api-1/status/200", Code: http.StatusTooManyRequests}, + + // all endpoints should be using API level rate limit for api-2. + {Headers: authHeaders, Path: "/api-2/get", Code: http.StatusOK}, + {Headers: authHeaders, Method: http.MethodPost, Path: "/api-2/post", Code: http.StatusOK}, + {Headers: authHeaders, Path: "/api-2/status/200", Code: http.StatusOK}, + + {BeforeFn: beforeFn, Headers: authHeaders, Path: "/api-1/status/200", Code: http.StatusTooManyRequests}, + + // api-3 should be using global rate limit. + {Headers: authHeaders, Path: "/api-3/get", Code: http.StatusOK}, + {Headers: authHeaders, Method: http.MethodPost, Path: "/api-3/post", Code: http.StatusOK}, + {BeforeFn: beforeFn, Headers: authHeaders, Path: "/api-3/status/200", Code: http.StatusTooManyRequests}, + }...) + }) + } +} + +func TestMWRateLimiting_EndpointRL_NonTransactional(t *testing.T) { + endpointRateLimitTestHelper(t, "NonTransactional", nil) +} + +func TestMWRateLimiting_EndpointRL_Redis(t *testing.T) { + endpointRateLimitTestHelper(t, "Redis", nil) +} + +func TestMWRateLimiting_EndpointRL_Sentinel(t *testing.T) { + // add a small delay before expecting rate limit exceeded in sentinel rate limiter. + endpointRateLimitTestHelper(t, "Sentinel", func() { + time.Sleep(time.Millisecond * 5) + }) +} + +func TestMWRateLimiting_EndpointRL_DRL(t *testing.T) { + endpointRateLimitTestHelper(t, "DRL", nil) +}