github.com/kumasuke120/mockuma@v1.1.9/internal/server/matcher_test.go (about) 1 package server 2 3 import ( 4 "net/http/httptest" 5 "regexp" 6 "strings" 7 "testing" 8 9 "github.com/kumasuke120/mockuma/internal/mckmaps" 10 "github.com/kumasuke120/mockuma/internal/myhttp" 11 "github.com/kumasuke120/mockuma/internal/myjson" 12 "github.com/stretchr/testify/assert" 13 ) 14 15 var emptyJSONMatcher = myjson.MakeExtJSONMatcher(myjson.Object{}) 16 var mappings = &mckmaps.MockuMappings{ 17 Mappings: []*mckmaps.Mapping{ 18 { 19 URI: "/hello", 20 Method: myhttp.MethodPost, 21 Policies: []*mckmaps.Policy{newJSONPolicy(myhttp.StatusOK, "OK")}, 22 }, 23 { 24 URI: "/m", 25 Method: myhttp.MethodGet, 26 Policies: []*mckmaps.Policy{ 27 { 28 When: &mckmaps.When{ 29 Params: []*mckmaps.NameValuesPair{ 30 { 31 Name: "p1", 32 Values: []string{"v1"}, 33 }, 34 { 35 Name: "p2", 36 Values: []string{"v2", "v1"}, 37 }, 38 }, 39 }, 40 CmdType: mckmaps.CmdTypeForwards, 41 Forwards: &mckmaps.Forwards{ 42 Path: "http://localhost:8080", 43 }, 44 }, 45 { 46 When: &mckmaps.When{ 47 ParamRegexps: []*mckmaps.NameRegexpPair{ 48 { 49 Name: "r1", 50 Regexp: regexp.MustCompile("^\\d{3}$"), 51 }, 52 }, 53 }, 54 CmdType: mckmaps.CmdTypeReturns, 55 Returns: &mckmaps.Returns{ 56 StatusCode: myhttp.StatusOK, 57 Body: []byte(""), 58 }, 59 }, 60 { 61 When: &mckmaps.When{ 62 ParamJSONs: []*mckmaps.NameJSONPair{ 63 { 64 Name: "j", 65 JSON: myjson.MakeExtJSONMatcher(myjson.Object{}), 66 }, 67 }, 68 }, 69 }, 70 { 71 When: &mckmaps.When{ 72 Headers: []*mckmaps.NameValuesPair{ 73 { 74 Name: "X-H1", 75 Values: []string{"v1"}, 76 }, 77 { 78 Name: "X-H2", 79 Values: []string{"v2", "v1"}, 80 }, 81 }, 82 }, 83 }, 84 { 85 When: &mckmaps.When{ 86 HeaderRegexps: []*mckmaps.NameRegexpPair{ 87 { 88 Name: "X-R1", 89 Regexp: regexp.MustCompile("^\\d{3}$"), 90 }, 91 }, 92 }, 93 }, 94 { 95 When: &mckmaps.When{ 96 HeaderJSONs: []*mckmaps.NameJSONPair{ 97 { 98 Name: "X-J1", 99 JSON: myjson.MakeExtJSONMatcher(myjson.Object{}), 100 }, 101 }, 102 }, 103 }, 104 }, 105 }, 106 { 107 URI: "/m", 108 Method: myhttp.MethodPost, 109 Policies: []*mckmaps.Policy{ 110 { 111 When: &mckmaps.When{ 112 Body: []byte("123"), 113 }, 114 }, 115 { 116 When: &mckmaps.When{ 117 BodyRegexp: regexp.MustCompile("^\\d{3}$"), 118 }, 119 CmdType: mckmaps.CmdTypeReturns, 120 Returns: &mckmaps.Returns{ 121 StatusCode: myhttp.StatusOK, 122 Headers: []*mckmaps.NameValuesPair{ 123 { 124 Name: "Server", 125 Values: []string{"TEST/v1"}, 126 }, 127 }, 128 }, 129 }, 130 { 131 When: &mckmaps.When{ 132 BodyJSON: &emptyJSONMatcher, 133 }, 134 }, 135 }, 136 }, 137 { 138 URI: "/p/{0}/m{1}", 139 Method: myhttp.MethodPut, 140 Policies: []*mckmaps.Policy{ 141 { 142 When: &mckmaps.When{ 143 PathVars: []*mckmaps.NameValuesPair{ 144 { 145 Name: "0", 146 Values: []string{"v0"}, 147 }, 148 }, 149 PathVarRegexps: []*mckmaps.NameRegexpPair{ 150 { 151 Name: "1", 152 Regexp: regexp.MustCompile("^\\d+$"), 153 }, 154 }, 155 }, 156 }, 157 }, 158 }, 159 }, 160 Config: &mckmaps.Config{ 161 CORS: &mckmaps.CORSOptions{ 162 Enabled: false, 163 }, 164 }, 165 } 166 167 var mappingsWithCORS = &mckmaps.MockuMappings{ 168 Mappings: mappings.Mappings, 169 Config: &mckmaps.Config{ 170 CORS: &mckmaps.CORSOptions{ 171 Enabled: true, 172 AllowCredentials: true, 173 MaxAge: 1800, 174 AllowedOrigins: []string{"*"}, 175 AllowedMethods: []myhttp.HTTPMethod{ 176 myhttp.MethodGet, 177 myhttp.MethodPost, 178 myhttp.MethodHead, 179 myhttp.MethodOptions, 180 }, 181 AllowedHeaders: []string{ 182 myhttp.HeaderOrigin, 183 myhttp.HeaderAccept, 184 myhttp.HeaderXRequestWith, 185 myhttp.HeaderContentType, 186 myhttp.HeaderAccessControlRequestMethod, 187 myhttp.HeaderAccessControlRequestHeaders, 188 }, 189 ExposedHeaders: nil, 190 }, 191 MatchTrailingSlash: false, 192 }, 193 } 194 195 func TestNewPathMatcher(t *testing.T) { 196 matcher := newPathMatcher(mappings) 197 198 expectedDirectPath := map[string][]*mckmaps.Mapping{ 199 "/hello": {mappings.Mappings[0]}, 200 "/m": { 201 &mckmaps.Mapping{ 202 URI: "/m", 203 Method: myhttp.MethodGet, 204 Policies: mappings.Mappings[1].Policies, 205 }, 206 mappings.Mappings[2], 207 }, 208 } 209 assert.Equal(t, expectedDirectPath, matcher.directPath) 210 expectedPatternPath := map[*regexp.Regexp][]*mckmaps.Mapping{ 211 regexp.MustCompile("^/p/(?P<v0>.*?)/m(?P<v1>.*?)$"): { 212 mappings.Mappings[3], 213 }, 214 } 215 assert.Equal(t, formatRegexpKeyMap(expectedPatternPath), formatRegexpKeyMap(matcher.patternPath)) 216 } 217 218 func formatRegexpKeyMap(m map[*regexp.Regexp][]*mckmaps.Mapping) map[string][]*mckmaps.Mapping { 219 result := make(map[string][]*mckmaps.Mapping, len(m)) 220 for r, ms := range m { 221 result[r.String()] = ms 222 } 223 return result 224 } 225 226 func TestPathMatcher_matches(t *testing.T) { 227 //noinspection GoImportUsedAsName 228 assert := assert.New(t) 229 230 matcher := newPathMatcher(mappings) 231 bound := matcher.bind(httptest.NewRequest("POST", "/hello", nil)) 232 assert.True(bound.matches()) 233 assert.Equal(matchState(matchExact), bound.matchState) 234 assert.Equal(mappings.Mappings[0], bound.matchedMapping) 235 } 236 237 func TestPathMatcher_matchPolicy(t *testing.T) { 238 //noinspection GoImportUsedAsName 239 assert := assert.New(t) 240 241 matcher := newPathMatcher(mappings) 242 243 bound1 := matcher.bind(httptest.NewRequest("", "/m?p1=v1&p2=v1&p2=v2", nil)) 244 assert.True(bound1.matches()) 245 assert.Equal(matchState(matchExact), bound1.matchState) 246 assert.Equal(mappings.Mappings[1].Policies[0], bound1.matchPolicy()) 247 248 bound2 := matcher.bind(httptest.NewRequest("", "/m?r1=123", nil)) 249 assert.True(bound2.matches()) 250 assert.Equal(matchState(matchExact), bound2.matchState) 251 assert.Equal(mappings.Mappings[1].Policies[1], bound2.matchPolicy()) 252 bound2 = matcher.bind(httptest.NewRequest("", "/m?r1=12", nil)) 253 assert.True(bound2.matches()) 254 assert.Equal(matchState(matchExact), bound2.matchState) 255 assert.Equal(pNoPolicyMatched, bound2.matchPolicy()) 256 257 bound3 := matcher.bind(httptest.NewRequest("", "/m?j={}", nil)) 258 assert.True(bound3.matches()) 259 assert.Equal(matchState(matchExact), bound3.matchState) 260 assert.Equal(mappings.Mappings[1].Policies[2], bound3.matchPolicy()) 261 bound3 = matcher.bind(httptest.NewRequest("", "/m?j=120", nil)) 262 assert.True(bound3.matches()) 263 assert.Equal(matchState(matchExact), bound3.matchState) 264 assert.Equal(pNoPolicyMatched, bound3.matchPolicy()) 265 266 req4 := httptest.NewRequest("", "/m", nil) 267 req4.Header.Add("X-H1", "v1") 268 req4.Header.Add("X-H2", "v1") 269 req4.Header.Add("X-H2", "v2") 270 bound4 := matcher.bind(req4) 271 assert.True(bound4.matches()) 272 assert.Equal(matchState(matchExact), bound4.matchState) 273 assert.Equal(mappings.Mappings[1].Policies[3], bound4.matchPolicy()) 274 275 req5p1 := httptest.NewRequest("", "/m", nil) 276 req5p1.Header.Add("X-R1", "123") 277 bound5 := matcher.bind(req5p1) 278 assert.True(bound5.matches()) 279 assert.Equal(matchState(matchExact), bound5.matchState) 280 assert.Equal(mappings.Mappings[1].Policies[4], bound5.matchPolicy()) 281 req5p2 := httptest.NewRequest("", "/m", nil) 282 req5p2.Header.Add("X-R1", "12") 283 bound5 = matcher.bind(req5p2) 284 assert.True(bound5.matches()) 285 assert.Equal(matchState(matchExact), bound5.matchState) 286 assert.Equal(pNoPolicyMatched, bound5.matchPolicy()) 287 288 req6p1 := httptest.NewRequest("", "/m", nil) 289 req6p1.Header.Add("X-J1", "{}") 290 bound6 := matcher.bind(req6p1) 291 assert.True(bound6.matches()) 292 assert.Equal(matchState(matchExact), bound6.matchState) 293 assert.Equal(mappings.Mappings[1].Policies[5], bound6.matchPolicy()) 294 req6p2 := httptest.NewRequest("", "/m", nil) 295 req6p2.Header.Add("X-J1", "120") 296 bound6 = matcher.bind(req6p2) 297 assert.True(bound6.matches()) 298 assert.Equal(matchState(matchExact), bound6.matchState) 299 assert.Equal(pNoPolicyMatched, bound6.matchPolicy()) 300 301 bound7 := matcher.bind(httptest.NewRequest("POST", "/m", strings.NewReader("123"))) 302 assert.True(bound7.matches()) 303 assert.Equal(matchState(matchExact), bound7.matchState) 304 assert.Equal(mappings.Mappings[2].Policies[0], bound7.matchPolicy()) 305 306 bound8 := matcher.bind(httptest.NewRequest("POST", "/m", strings.NewReader("120"))) 307 assert.True(bound8.matches()) 308 assert.Equal(matchState(matchExact), bound8.matchState) 309 assert.Equal(mappings.Mappings[2].Policies[1], bound8.matchPolicy()) 310 bound8 = matcher.bind(httptest.NewRequest("POST", "/m", strings.NewReader("95"))) 311 assert.True(bound8.matches()) 312 assert.Equal(matchState(matchExact), bound8.matchState) 313 assert.Equal(pNoPolicyMatched, bound8.matchPolicy()) 314 315 bound9 := matcher.bind(httptest.NewRequest("POST", "/m", strings.NewReader("{}"))) 316 assert.True(bound9.matches()) 317 assert.Equal(matchState(matchExact), bound9.matchState) 318 assert.Equal(mappings.Mappings[2].Policies[2], bound9.matchPolicy()) 319 320 bound10 := matcher.bind(httptest.NewRequest("", "/hello", nil)) 321 assert.True(bound10.matches()) 322 assert.Equal(matchState(matchURI), bound10.matchState) 323 324 bound11 := matcher.bind(httptest.NewRequest("PUT", "/p/v0/m1", nil)) 325 assert.True(bound11.matches()) 326 assert.Equal(matchState(matchExact), bound11.matchState) 327 assert.Equal(mappings.Mappings[3].Policies[0], bound11.matchPolicy()) 328 329 bound12 := matcher.bind(httptest.NewRequest("PUT", "/p/v0/ma", nil)) 330 assert.True(bound12.matches()) 331 assert.Equal(matchState(matchExact), bound12.matchState) 332 assert.Equal(pNoPolicyMatched, bound12.matchPolicy()) 333 334 bound13 := matcher.bind(httptest.NewRequest("", "/p/v0/m1", nil)) 335 assert.True(bound13.matches()) 336 assert.Equal(matchState(matchURI), bound13.matchState) 337 338 bound14 := matcher.bind(httptest.NewRequest("PUT", "/p/v1/m1", nil)) 339 assert.True(bound14.matches()) 340 assert.Equal(matchState(matchExact), bound14.matchState) 341 assert.Equal(pNoPolicyMatched, bound14.matchPolicy()) 342 343 bound15 := matcher.bind(httptest.NewRequest("HEAD", "/m?r1=123", nil)) 344 assert.True(bound15.matches()) 345 assert.Equal(matchState(matchHead), bound15.matchState) 346 assert.Equal(mappings.Mappings[1].Policies[1], bound15.matchPolicy()) 347 }