github.com/google/martian/v3@v3.3.3/trafficshape/handler_test.go (about)

     1  // Copyright 2015 Google Inc. All rights reserved.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package trafficshape
    16  
    17  import (
    18  	"bytes"
    19  	"fmt"
    20  	"net"
    21  	"net/http"
    22  	"net/http/httptest"
    23  	"testing"
    24  	"time"
    25  )
    26  
    27  func compareActions(testSlice []Action, refSlice []Action) (bool, string) {
    28  	if len(testSlice) != len(refSlice) {
    29  		return false, fmt.Sprintf("length: got %d, want %d", len(testSlice), len(refSlice))
    30  	}
    31  	for i, action := range refSlice {
    32  		failure := false
    33  		switch refAction := action.(type) {
    34  		case *Halt:
    35  			if testAction, ok := testSlice[i].(*Halt); ok {
    36  				if *testAction != *refAction {
    37  					failure = true
    38  				}
    39  			} else {
    40  				failure = true
    41  			}
    42  		case *CloseConnection:
    43  			if testAction, ok := testSlice[i].(*CloseConnection); ok {
    44  				if *testAction != *refAction {
    45  					failure = true
    46  				}
    47  			} else {
    48  				failure = true
    49  			}
    50  		case *ChangeBandwidth:
    51  			if testAction, ok := testSlice[i].(*ChangeBandwidth); ok {
    52  				if *testAction != *refAction {
    53  					failure = true
    54  				}
    55  			} else {
    56  				failure = true
    57  			}
    58  		}
    59  		if failure {
    60  			return false, fmt.Sprintf("Action %d: got %+v, want %+v", i, testSlice[i], action)
    61  		}
    62  	}
    63  	return true, ""
    64  }
    65  
    66  func TestHandlerIncorrectInputs(t *testing.T) {
    67  	l, err := net.Listen("tcp", "[::]:0")
    68  	if err != nil {
    69  		t.Fatalf("net.Listen(): got %v, want no error", err)
    70  	}
    71  
    72  	tt := []struct {
    73  		testcase string
    74  		body     string
    75  	}{
    76  		{
    77  			testcase: `overlapping throttle`,
    78  			body:     `{"trafficshape":{"shapes":[{"url_regex":"http://example/example","throttles":[{"bytes":"500-1000","bandwidth":100},{"bytes":"700-2000","bandwidth":100}],"close_connections":[{"byte":1078,"count":1}]}]}}`,
    79  		},
    80  		{
    81  			testcase: `negative bandwidth`,
    82  			body:     `{"trafficshape":{"shapes":[{"url_regex":"http://example/example","throttles":[{"bytes":"500-","bandwidth":abc}],"close_connections":[{"byte":1078,"count":1}]}]}}`,
    83  		},
    84  		{
    85  			testcase: `negative close byte`,
    86  			body:     `{"trafficshape":{"shapes":[{"url_regex":"http://example/example","throttles":[{"bytes":"500-1000","bandwidth":100}],"close_connections":[{"byte":-1,"count":1}]}]}}`,
    87  		},
    88  		{
    89  			testcase: `uncompiling regex`,
    90  			body:     `{"trafficshape":{"shapes":[{"url_regex":"http://example/example(","throttles":[{"bytes":"500-1000","bandwidth":100}],"close_connections":[{"byte":100,"count":1}]}]}}`,
    91  		},
    92  		{
    93  			testcase: `missing count`,
    94  			body:     `{"trafficshape":{"shapes":[{"url_regex":"http://example/example","throttles":[{"bytes":"500-1000","bandwidth":100}],"halts":[{"byte":100}]}]}}`,
    95  		},
    96  		{
    97  			testcase: `illformed byte range`,
    98  			body:     `{"trafficshape":{"shapes":[{"url_regex":"http://example/example","throttles":[{"bytes":"500--1000","bandwidth":100}],"close_connections":[{"byte":10,"count":1}]}]}}`,
    99  		},
   100  		{
   101  			testcase: `throttle end < start`,
   102  			body:     `{"trafficshape":{"shapes":[{"url_regex":"http://example/example","throttles":[{"bytes":"500-255","bandwidth":100}],"close_connections":[{"byte":100,"count":1}]}]}}`,
   103  		},
   104  		{
   105  			testcase: `missing comma`,
   106  			body:     `{"trafficshape":{"shapes":[{"url_regex":"http://example/example","throttles":[{"bytes":"500-1000","bandwidth":100}]"close_connections":[{"byte":100,"count":1}]}]}}`,
   107  		},
   108  		{
   109  			testcase: `missing regex`,
   110  			body:     `{"trafficshape":{"shapes":[{"throttles":[{"bytes":"500-1000","bandwidth":100}],"close_connections":[{"byte":-1,"count":1}]}]}}`,
   111  		},
   112  		{
   113  			testcase: `negative default bandwidth`,
   114  			body:     `{"trafficshape":{"default":{"bandwidth":{"up":-100000,"down":100000},"latency":1000},"shapes":[{"url_regex":"http://example/example","throttles":[{"bytes":"500-1000","bandwidth":100}]",close_connections":[{"byte":100,"count":1}]}]}}`,
   115  		},
   116  		{
   117  			testcase: `negative default latency`,
   118  			body:     `{"trafficshape":{"default":{"bandwidth":{"up":100000,"down":100000},"latency":-1000},"shapes":[{"url_regex":"http://example/example","throttles":[{"bytes":"500-1000","bandwidth":100}]",close_connections":[{"byte":100,"count":1}]}]}}`,
   119  		},
   120  	}
   121  
   122  	for i, tc := range tt {
   123  		t.Logf("case %d: %s", i+1, tc.testcase)
   124  
   125  		tsl := NewListener(l)
   126  		defer tsl.Close()
   127  
   128  		h := NewHandler(tsl)
   129  
   130  		req, err := http.NewRequest("POST", "test", bytes.NewBufferString(tc.body))
   131  		if err != nil {
   132  			t.Fatalf("%d. http.NewRequest(): got %v, want no error", i, err)
   133  		}
   134  		rw := httptest.NewRecorder()
   135  
   136  		h.ServeHTTP(rw, req)
   137  
   138  		if got := rw.Code; got != 400 {
   139  			t.Errorf("%d. rw.Code: got %d, want %d", i+1, got, 400)
   140  		}
   141  	}
   142  }
   143  
   144  func TestHandlerClear(t *testing.T) {
   145  
   146  	l, err := net.Listen("tcp", "[::]:0")
   147  	if err != nil {
   148  		t.Fatalf("net.Listen(): got %v, want no error", err)
   149  	}
   150  
   151  	tsl := NewListener(l)
   152  	defer tsl.Close()
   153  
   154  	h := NewHandler(tsl)
   155  	startTime := time.Now()
   156  	jsonString := `{"trafficshape":{}}`
   157  	req, err := http.NewRequest("POST", "test", bytes.NewBufferString(jsonString))
   158  	if err != nil {
   159  		t.Fatalf("http.NewRequest(): got %v, want no error", err)
   160  	}
   161  
   162  	rw := httptest.NewRecorder()
   163  
   164  	h.ServeHTTP(rw, req)
   165  
   166  	if got, want := rw.Code, 200; got != want {
   167  		t.Errorf(" rw.Code: got %d, want %d", got, want)
   168  	}
   169  
   170  	defaults := tsl.Defaults()
   171  
   172  	if got, want := defaults.Bandwidth.Down, DefaultBitrate/8; got != want {
   173  		t.Errorf("default downstream bandwidth: got %d, want %d", got, want)
   174  	}
   175  	if got, want := defaults.Latency, int64(0); got != want {
   176  		t.Errorf("default latency: got %d, want %d", got, want)
   177  	}
   178  
   179  	if got, want := tsl.WriteBucket.Capacity(), DefaultBitrate/8; got != want {
   180  		t.Errorf("tsl WriteBucket Capacity: got %d, want %d", got, want)
   181  	}
   182  
   183  	tsl.Shapes.RLock()
   184  	if got, want := len(tsl.Shapes.M), 0; got != want {
   185  		t.Errorf("length of shape map: got %d, want %d", got, want)
   186  	}
   187  	if modifiedTime := tsl.Shapes.LastModifiedTime; modifiedTime.Before(startTime) {
   188  		t.Errorf("modified time is before start time; should be after")
   189  	}
   190  	tsl.Shapes.RUnlock()
   191  }
   192  
   193  func TestHandlerActions(t *testing.T) {
   194  	l, err := net.Listen("tcp", "[::]:0")
   195  	if err != nil {
   196  		t.Fatalf("net.Listen(): got %v, want no error", err)
   197  	}
   198  
   199  	tt := []struct {
   200  		jsonString string
   201  		actions    []Action
   202  	}{
   203  		{
   204  			jsonString: `{"trafficshape":{"shapes":[{"url_regex":"http://example/example", "max_global_bandwidth":1000, "throttles":[{"bytes":"500-1000","bandwidth":100},{"bytes":"1000-2000","bandwidth":300},{"bytes":"2001-","bandwidth":400}],
   205  	"halts":[{"byte":530,"duration": 5, "count": 1}],"close_connections":[{"byte":1078,"count":1}]}]}}`,
   206  			actions: []Action{
   207  				&ChangeBandwidth{Byte: 500, Bandwidth: 100},
   208  				&Halt{Byte: 530, Duration: 5, Count: 1},
   209  				&ChangeBandwidth{Byte: 1000, Bandwidth: 300},
   210  				&CloseConnection{Byte: 1078, Count: 1},
   211  				&ChangeBandwidth{Byte: 2000, Bandwidth: 1000},
   212  				&ChangeBandwidth{Byte: 2001, Bandwidth: 400},
   213  			},
   214  		},
   215  		{
   216  			jsonString: `{"trafficshape":{"shapes":[{"url_regex":"http://example/example","throttles":[{"bytes":"-","bandwidth":100}],
   217  			"close_connections":[{"byte":100,"count":1}]}]}}`,
   218  			actions: []Action{
   219  				&ChangeBandwidth{Byte: 0, Bandwidth: 100},
   220  				&CloseConnection{Byte: 100, Count: 1},
   221  			},
   222  		},
   223  	}
   224  
   225  	for i, tc := range tt {
   226  		tsl := NewListener(l)
   227  		defer tsl.Close()
   228  
   229  		h := NewHandler(tsl)
   230  		startTime := time.Now()
   231  		req, err := http.NewRequest("POST", "test", bytes.NewBufferString(tc.jsonString))
   232  		if err != nil {
   233  			t.Fatalf("%d. http.NewRequest(): got %v, want no error", i, err)
   234  		}
   235  
   236  		rw := httptest.NewRecorder()
   237  
   238  		h.ServeHTTP(rw, req)
   239  
   240  		if got, want := rw.Code, 200; got != want {
   241  			t.Errorf("%d. rw.Code: got %d, want %d", i+1, got, want)
   242  		}
   243  
   244  		tsl.Shapes.RLock()
   245  		defer tsl.Shapes.RUnlock()
   246  		if got, want := len(tsl.Shapes.M), 1; got != want {
   247  			t.Errorf("tc.%d length of shape map: got %d, want %d", i+1, got, want)
   248  		}
   249  		tsl.Shapes.M["http://example/example"].RLock()
   250  		defer tsl.Shapes.M["http://example/example"].RUnlock()
   251  		if same, errStr := compareActions(tsl.Shapes.M["http://example/example"].Shape.Actions, tc.actions); !same {
   252  			t.Errorf(errStr)
   253  		}
   254  		if modifiedTime := tsl.Shapes.LastModifiedTime; modifiedTime.Before(startTime) {
   255  			t.Errorf("tc.%d modified time is before start time; should be after", i+1)
   256  		}
   257  	}
   258  }