github.com/Tyktechnologies/tyk@v2.9.5+incompatible/gateway/mw_organization_activity_test.go (about)

     1  // +build !race
     2  
     3  package gateway
     4  
     5  import (
     6  	"net/http"
     7  	"testing"
     8  	"time"
     9  
    10  	uuid "github.com/satori/go.uuid"
    11  
    12  	"github.com/TykTechnologies/tyk/config"
    13  	"github.com/TykTechnologies/tyk/test"
    14  )
    15  
    16  func testPrepareProcessRequestQuotaLimit(tb testing.TB, ts *Test, data map[string]interface{}) {
    17  	// load API
    18  	orgID := "test-org-" + uuid.NewV4().String()
    19  	BuildAndLoadAPI(func(spec *APISpec) {
    20  		spec.UseKeylessAccess = true
    21  		spec.OrgID = orgID
    22  		spec.Proxy.ListenPath = "/"
    23  	})
    24  
    25  	data["org_id"] = orgID
    26  
    27  	// create org key with quota
    28  	ts.Run(tb, test.TestCase{
    29  		Path:      "/tyk/org/keys/" + orgID + "?reset_quota=1",
    30  		AdminAuth: true,
    31  		Method:    http.MethodPost,
    32  		Code:      http.StatusOK,
    33  		Data:      data,
    34  	})
    35  }
    36  
    37  func TestProcessRequestLiveQuotaLimit(t *testing.T) {
    38  	// setup global config
    39  	globalConf := config.Global()
    40  	globalConf.EnforceOrgQuotas = true
    41  	globalConf.ExperimentalProcessOrgOffThread = false
    42  	config.SetGlobal(globalConf)
    43  
    44  	// run test server
    45  	ts := StartTest()
    46  	defer ts.Close()
    47  
    48  	// load API
    49  	testPrepareProcessRequestQuotaLimit(
    50  		t,
    51  		ts,
    52  		map[string]interface{}{
    53  			"quota_max":          10,
    54  			"quota_remaining":    10,
    55  			"quota_renewal_rate": 3,
    56  		},
    57  	)
    58  
    59  	t.Run("Process request live with quota", func(t *testing.T) {
    60  		// 1st ten requests within quota
    61  		for i := 0; i < 10; i++ {
    62  			ts.Run(t, test.TestCase{
    63  				Code: http.StatusOK,
    64  			})
    65  		}
    66  
    67  		// next request should fail with 403 as it is out of quota
    68  		ts.Run(t, test.TestCase{
    69  			Code: http.StatusForbidden,
    70  		})
    71  
    72  		// wait for renewal
    73  		time.Sleep(4 * time.Second)
    74  
    75  		// next one should be OK
    76  		ts.Run(t, test.TestCase{
    77  			Code: http.StatusOK,
    78  		})
    79  	})
    80  }
    81  
    82  func BenchmarkProcessRequestLiveQuotaLimit(b *testing.B) {
    83  	b.ReportAllocs()
    84  
    85  	// setup global config
    86  	globalConf := config.Global()
    87  	globalConf.EnforceOrgQuotas = true
    88  	globalConf.ExperimentalProcessOrgOffThread = false
    89  	config.SetGlobal(globalConf)
    90  
    91  	defer ResetTestConfig()
    92  
    93  	// run test server
    94  	ts := StartTest()
    95  	defer ts.Close()
    96  
    97  	// load API
    98  	testPrepareProcessRequestQuotaLimit(
    99  		b,
   100  		ts,
   101  		map[string]interface{}{
   102  			"quota_max":          100000000,
   103  			"quota_remaining":    100000000,
   104  			"quota_renewal_rate": 300,
   105  		},
   106  	)
   107  
   108  	for i := 0; i < b.N; i++ {
   109  		ts.Run(b, test.TestCase{
   110  			Code: http.StatusOK,
   111  		})
   112  	}
   113  }
   114  
   115  func TestProcessRequestOffThreadQuotaLimit(t *testing.T) {
   116  	// run test server
   117  	ts := StartTest()
   118  	defer ts.Close()
   119  
   120  	// setup global config
   121  	globalConf := config.Global()
   122  	globalConf.EnforceOrgQuotas = true
   123  	globalConf.ExperimentalProcessOrgOffThread = true
   124  	config.SetGlobal(globalConf)
   125  	defer ResetTestConfig()
   126  
   127  	// load API
   128  	testPrepareProcessRequestQuotaLimit(
   129  		t,
   130  		ts,
   131  		map[string]interface{}{
   132  			"quota_max":          10,
   133  			"quota_remaining":    10,
   134  			"quota_renewal_rate": 2,
   135  		},
   136  	)
   137  
   138  	t.Run("Process request off thread with quota", func(t *testing.T) {
   139  		// at least first 10 requests within quota should be OK
   140  		for i := 0; i < 10; i++ {
   141  			ts.Run(t, test.TestCase{
   142  				Code: http.StatusOK,
   143  			})
   144  		}
   145  
   146  		// some of next request should fail with 403 as it is out of quota
   147  		failed := false
   148  		i := 0
   149  		for i = 0; i < 11; i++ {
   150  			res, _ := ts.Run(t, test.TestCase{})
   151  			res.Body.Close()
   152  			if res.StatusCode == http.StatusForbidden {
   153  				failed = true
   154  				break
   155  			}
   156  		}
   157  		if !failed {
   158  			t.Error("Requests don't fail after quota exceeded")
   159  		} else {
   160  			t.Logf("Failed with 403 after %d requests over quota", i)
   161  		}
   162  
   163  		// wait for renewal
   164  		time.Sleep(4 * time.Second)
   165  
   166  		// next 10 requests should be OK again
   167  		ok := false
   168  		for i = 0; i < 9; i++ {
   169  			res, _ := ts.Run(t, test.TestCase{Delay: 10 * time.Millisecond})
   170  			res.Body.Close()
   171  			if res.StatusCode == http.StatusOK {
   172  				ok = true
   173  				break
   174  			}
   175  		}
   176  		if !ok {
   177  			t.Error("Requests still failing after quota renewal")
   178  		} else {
   179  			t.Logf("Started responding with 200 after %d requests after quota renewal", i)
   180  		}
   181  	})
   182  }
   183  
   184  func BenchmarkProcessRequestOffThreadQuotaLimit(b *testing.B) {
   185  	b.ReportAllocs()
   186  
   187  	// setup global config
   188  	globalConf := config.Global()
   189  	globalConf.EnforceOrgQuotas = true
   190  	globalConf.ExperimentalProcessOrgOffThread = true
   191  	config.SetGlobal(globalConf)
   192  
   193  	defer ResetTestConfig()
   194  
   195  	// run test server
   196  	ts := StartTest()
   197  	defer ts.Close()
   198  
   199  	// load API
   200  	testPrepareProcessRequestQuotaLimit(
   201  		b,
   202  		ts,
   203  		map[string]interface{}{
   204  			"quota_max":          100000000,
   205  			"quota_remaining":    100000000,
   206  			"quota_renewal_rate": 300,
   207  		},
   208  	)
   209  
   210  	for i := 0; i < b.N; i++ {
   211  		ts.Run(b, test.TestCase{
   212  			Code: http.StatusOK,
   213  		})
   214  	}
   215  }
   216  
   217  func TestProcessRequestLiveRedisRollingLimiter(t *testing.T) {
   218  	// setup global config
   219  	globalConf := config.Global()
   220  	globalConf.EnforceOrgQuotas = true
   221  	globalConf.EnableRedisRollingLimiter = true
   222  	globalConf.ExperimentalProcessOrgOffThread = false
   223  	config.SetGlobal(globalConf)
   224  	defer ResetTestConfig()
   225  
   226  	// run test server
   227  	ts := StartTest()
   228  	defer ts.Close()
   229  
   230  	// load API
   231  	testPrepareProcessRequestQuotaLimit(
   232  		t,
   233  		ts,
   234  		map[string]interface{}{
   235  			"quota_max": -1,
   236  			"rate":      10,
   237  			"per":       1,
   238  		},
   239  	)
   240  
   241  	t.Run("Process request live with rate limit", func(t *testing.T) {
   242  		// ten requests per sec should be OK
   243  		for i := 0; i < 10; i++ {
   244  			ts.Run(t, test.TestCase{
   245  				Code: http.StatusOK,
   246  			})
   247  		}
   248  
   249  		// wait for next time window
   250  		time.Sleep(2 * time.Second)
   251  
   252  		// try to run over rate limit
   253  		reqNum := 1
   254  		for {
   255  			resp, _ := ts.Run(t, test.TestCase{})
   256  			resp.Body.Close()
   257  			if resp.StatusCode == http.StatusForbidden {
   258  				break
   259  			}
   260  			reqNum++
   261  		}
   262  
   263  		if reqNum < 10 {
   264  			t.Errorf("Started failing too early after %d requests", reqNum)
   265  		} else {
   266  			t.Logf("Started failing after %d requests over limit", reqNum-10)
   267  		}
   268  	})
   269  }
   270  
   271  func BenchmarkProcessRequestLiveRedisRollingLimiter(b *testing.B) {
   272  	b.ReportAllocs()
   273  
   274  	// setup global config
   275  	globalConf := config.Global()
   276  	globalConf.EnforceOrgQuotas = true
   277  	globalConf.EnableRedisRollingLimiter = true
   278  	globalConf.ExperimentalProcessOrgOffThread = false
   279  	config.SetGlobal(globalConf)
   280  
   281  	defer ResetTestConfig()
   282  
   283  	// run test server
   284  	ts := StartTest()
   285  	defer ts.Close()
   286  
   287  	// load API
   288  	testPrepareProcessRequestQuotaLimit(
   289  		b,
   290  		ts,
   291  		map[string]interface{}{
   292  			"quota_max": -1,
   293  			"rate":      10000,
   294  			"per":       1,
   295  		},
   296  	)
   297  
   298  	for i := 0; i < b.N; i++ {
   299  		ts.Run(b, test.TestCase{
   300  			Code: http.StatusOK,
   301  		})
   302  	}
   303  }
   304  
   305  func TestProcessRequestOffThreadRedisRollingLimiter(t *testing.T) {
   306  	// setup global config
   307  	globalConf := config.Global()
   308  	globalConf.EnforceOrgQuotas = true
   309  	globalConf.EnableRedisRollingLimiter = true
   310  	globalConf.ExperimentalProcessOrgOffThread = true
   311  	config.SetGlobal(globalConf)
   312  	defer ResetTestConfig()
   313  
   314  	// run test server
   315  	ts := StartTest()
   316  	defer ts.Close()
   317  
   318  	// load API
   319  	testPrepareProcessRequestQuotaLimit(
   320  		t,
   321  		ts,
   322  		map[string]interface{}{
   323  			"quota_max": -1,
   324  			"rate":      10,
   325  			"per":       1,
   326  		},
   327  	)
   328  
   329  	t.Run("Process request off thread with rate limit", func(t *testing.T) {
   330  		// ten requests per sec should be OK
   331  		for i := 0; i < 10; i++ {
   332  			ts.Run(t, test.TestCase{
   333  				Code: http.StatusOK,
   334  			})
   335  		}
   336  
   337  		// wait for next time window
   338  		time.Sleep(2 * time.Second)
   339  
   340  		// try to run over rate limit
   341  		reqNum := 1
   342  		for {
   343  			resp, _ := ts.Run(t, test.TestCase{})
   344  			resp.Body.Close()
   345  			if resp.StatusCode == http.StatusForbidden {
   346  				break
   347  			}
   348  			reqNum++
   349  		}
   350  
   351  		if reqNum < 10 {
   352  			t.Errorf("Started failing too early after %d requests", reqNum)
   353  		} else {
   354  			t.Logf("Started failing after %d requests over limit", reqNum-10)
   355  		}
   356  	})
   357  }
   358  
   359  func BenchmarkProcessRequestOffThreadRedisRollingLimiter(b *testing.B) {
   360  	b.ReportAllocs()
   361  
   362  	// setup global config
   363  	globalConf := config.Global()
   364  	globalConf.EnforceOrgQuotas = true
   365  	globalConf.EnableRedisRollingLimiter = true
   366  	globalConf.ExperimentalProcessOrgOffThread = true
   367  	config.SetGlobal(globalConf)
   368  
   369  	// run test server
   370  	ts := StartTest()
   371  	defer ts.Close()
   372  
   373  	// load API
   374  	testPrepareProcessRequestQuotaLimit(
   375  		b,
   376  		ts,
   377  		map[string]interface{}{
   378  			"quota_max": -1,
   379  			"rate":      10000,
   380  			"per":       1,
   381  		},
   382  	)
   383  
   384  	for i := 0; i < b.N; i++ {
   385  		ts.Run(b, test.TestCase{
   386  			Code: http.StatusOK,
   387  		})
   388  	}
   389  }