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 }