github.com/hellobchain/third_party@v0.0.0-20230331131523-deb0478a2e52/gin/githubapi_test.go (about) 1 // Copyright 2014 Manu Martinez-Almeida. All rights reserved. 2 // Use of this source code is governed by a MIT style 3 // license that can be found in the LICENSE file. 4 5 package gin 6 7 import ( 8 "bytes" 9 "fmt" 10 "math/rand" 11 "github.com/hellobchain/newcryptosm/http" 12 "github.com/hellobchain/newcryptosm/http/httptest" 13 "os" 14 "testing" 15 16 "github.com/stretchr/testify/assert" 17 ) 18 19 type route struct { 20 method string 21 path string 22 } 23 24 // http://developer.github.com/v3/ 25 var githubAPI = []route{ 26 // OAuth Authorizations 27 {http.MethodGet, "/authorizations"}, 28 {http.MethodGet, "/authorizations/:id"}, 29 {http.MethodPost, "/authorizations"}, 30 //{http.MethodPut, "/authorizations/clients/:client_id"}, 31 //{http.MethodPatch, "/authorizations/:id"}, 32 {http.MethodDelete, "/authorizations/:id"}, 33 {http.MethodGet, "/applications/:client_id/tokens/:access_token"}, 34 {http.MethodDelete, "/applications/:client_id/tokens"}, 35 {http.MethodDelete, "/applications/:client_id/tokens/:access_token"}, 36 37 // Activity 38 {http.MethodGet, "/events"}, 39 {http.MethodGet, "/repos/:owner/:repo/events"}, 40 {http.MethodGet, "/networks/:owner/:repo/events"}, 41 {http.MethodGet, "/orgs/:org/events"}, 42 {http.MethodGet, "/users/:user/received_events"}, 43 {http.MethodGet, "/users/:user/received_events/public"}, 44 {http.MethodGet, "/users/:user/events"}, 45 {http.MethodGet, "/users/:user/events/public"}, 46 {http.MethodGet, "/users/:user/events/orgs/:org"}, 47 {http.MethodGet, "/feeds"}, 48 {http.MethodGet, "/notifications"}, 49 {http.MethodGet, "/repos/:owner/:repo/notifications"}, 50 {http.MethodPut, "/notifications"}, 51 {http.MethodPut, "/repos/:owner/:repo/notifications"}, 52 {http.MethodGet, "/notifications/threads/:id"}, 53 //{http.MethodPatch, "/notifications/threads/:id"}, 54 {http.MethodGet, "/notifications/threads/:id/subscription"}, 55 {http.MethodPut, "/notifications/threads/:id/subscription"}, 56 {http.MethodDelete, "/notifications/threads/:id/subscription"}, 57 {http.MethodGet, "/repos/:owner/:repo/stargazers"}, 58 {http.MethodGet, "/users/:user/starred"}, 59 {http.MethodGet, "/user/starred"}, 60 {http.MethodGet, "/user/starred/:owner/:repo"}, 61 {http.MethodPut, "/user/starred/:owner/:repo"}, 62 {http.MethodDelete, "/user/starred/:owner/:repo"}, 63 {http.MethodGet, "/repos/:owner/:repo/subscribers"}, 64 {http.MethodGet, "/users/:user/subscriptions"}, 65 {http.MethodGet, "/user/subscriptions"}, 66 {http.MethodGet, "/repos/:owner/:repo/subscription"}, 67 {http.MethodPut, "/repos/:owner/:repo/subscription"}, 68 {http.MethodDelete, "/repos/:owner/:repo/subscription"}, 69 {http.MethodGet, "/user/subscriptions/:owner/:repo"}, 70 {http.MethodPut, "/user/subscriptions/:owner/:repo"}, 71 {http.MethodDelete, "/user/subscriptions/:owner/:repo"}, 72 73 // Gists 74 {http.MethodGet, "/users/:user/gists"}, 75 {http.MethodGet, "/gists"}, 76 //{http.MethodGet, "/gists/public"}, 77 //{http.MethodGet, "/gists/starred"}, 78 {http.MethodGet, "/gists/:id"}, 79 {http.MethodPost, "/gists"}, 80 //{http.MethodPatch, "/gists/:id"}, 81 {http.MethodPut, "/gists/:id/star"}, 82 {http.MethodDelete, "/gists/:id/star"}, 83 {http.MethodGet, "/gists/:id/star"}, 84 {http.MethodPost, "/gists/:id/forks"}, 85 {http.MethodDelete, "/gists/:id"}, 86 87 // Git Data 88 {http.MethodGet, "/repos/:owner/:repo/git/blobs/:sha"}, 89 {http.MethodPost, "/repos/:owner/:repo/git/blobs"}, 90 {http.MethodGet, "/repos/:owner/:repo/git/commits/:sha"}, 91 {http.MethodPost, "/repos/:owner/:repo/git/commits"}, 92 //{http.MethodGet, "/repos/:owner/:repo/git/refs/*ref"}, 93 {http.MethodGet, "/repos/:owner/:repo/git/refs"}, 94 {http.MethodPost, "/repos/:owner/:repo/git/refs"}, 95 //{http.MethodPatch, "/repos/:owner/:repo/git/refs/*ref"}, 96 //{http.MethodDelete, "/repos/:owner/:repo/git/refs/*ref"}, 97 {http.MethodGet, "/repos/:owner/:repo/git/tags/:sha"}, 98 {http.MethodPost, "/repos/:owner/:repo/git/tags"}, 99 {http.MethodGet, "/repos/:owner/:repo/git/trees/:sha"}, 100 {http.MethodPost, "/repos/:owner/:repo/git/trees"}, 101 102 // Issues 103 {http.MethodGet, "/issues"}, 104 {http.MethodGet, "/user/issues"}, 105 {http.MethodGet, "/orgs/:org/issues"}, 106 {http.MethodGet, "/repos/:owner/:repo/issues"}, 107 {http.MethodGet, "/repos/:owner/:repo/issues/:number"}, 108 {http.MethodPost, "/repos/:owner/:repo/issues"}, 109 //{http.MethodPatch, "/repos/:owner/:repo/issues/:number"}, 110 {http.MethodGet, "/repos/:owner/:repo/assignees"}, 111 {http.MethodGet, "/repos/:owner/:repo/assignees/:assignee"}, 112 {http.MethodGet, "/repos/:owner/:repo/issues/:number/comments"}, 113 //{http.MethodGet, "/repos/:owner/:repo/issues/comments"}, 114 //{http.MethodGet, "/repos/:owner/:repo/issues/comments/:id"}, 115 {http.MethodPost, "/repos/:owner/:repo/issues/:number/comments"}, 116 //{http.MethodPatch, "/repos/:owner/:repo/issues/comments/:id"}, 117 //{http.MethodDelete, "/repos/:owner/:repo/issues/comments/:id"}, 118 {http.MethodGet, "/repos/:owner/:repo/issues/:number/events"}, 119 //{http.MethodGet, "/repos/:owner/:repo/issues/events"}, 120 //{http.MethodGet, "/repos/:owner/:repo/issues/events/:id"}, 121 {http.MethodGet, "/repos/:owner/:repo/labels"}, 122 {http.MethodGet, "/repos/:owner/:repo/labels/:name"}, 123 {http.MethodPost, "/repos/:owner/:repo/labels"}, 124 //{http.MethodPatch, "/repos/:owner/:repo/labels/:name"}, 125 {http.MethodDelete, "/repos/:owner/:repo/labels/:name"}, 126 {http.MethodGet, "/repos/:owner/:repo/issues/:number/labels"}, 127 {http.MethodPost, "/repos/:owner/:repo/issues/:number/labels"}, 128 {http.MethodDelete, "/repos/:owner/:repo/issues/:number/labels/:name"}, 129 {http.MethodPut, "/repos/:owner/:repo/issues/:number/labels"}, 130 {http.MethodDelete, "/repos/:owner/:repo/issues/:number/labels"}, 131 {http.MethodGet, "/repos/:owner/:repo/milestones/:number/labels"}, 132 {http.MethodGet, "/repos/:owner/:repo/milestones"}, 133 {http.MethodGet, "/repos/:owner/:repo/milestones/:number"}, 134 {http.MethodPost, "/repos/:owner/:repo/milestones"}, 135 //{http.MethodPatch, "/repos/:owner/:repo/milestones/:number"}, 136 {http.MethodDelete, "/repos/:owner/:repo/milestones/:number"}, 137 138 // Miscellaneous 139 {http.MethodGet, "/emojis"}, 140 {http.MethodGet, "/gitignore/templates"}, 141 {http.MethodGet, "/gitignore/templates/:name"}, 142 {http.MethodPost, "/markdown"}, 143 {http.MethodPost, "/markdown/raw"}, 144 {http.MethodGet, "/meta"}, 145 {http.MethodGet, "/rate_limit"}, 146 147 // Organizations 148 {http.MethodGet, "/users/:user/orgs"}, 149 {http.MethodGet, "/user/orgs"}, 150 {http.MethodGet, "/orgs/:org"}, 151 //{http.MethodPatch, "/orgs/:org"}, 152 {http.MethodGet, "/orgs/:org/members"}, 153 {http.MethodGet, "/orgs/:org/members/:user"}, 154 {http.MethodDelete, "/orgs/:org/members/:user"}, 155 {http.MethodGet, "/orgs/:org/public_members"}, 156 {http.MethodGet, "/orgs/:org/public_members/:user"}, 157 {http.MethodPut, "/orgs/:org/public_members/:user"}, 158 {http.MethodDelete, "/orgs/:org/public_members/:user"}, 159 {http.MethodGet, "/orgs/:org/teams"}, 160 {http.MethodGet, "/teams/:id"}, 161 {http.MethodPost, "/orgs/:org/teams"}, 162 //{http.MethodPatch, "/teams/:id"}, 163 {http.MethodDelete, "/teams/:id"}, 164 {http.MethodGet, "/teams/:id/members"}, 165 {http.MethodGet, "/teams/:id/members/:user"}, 166 {http.MethodPut, "/teams/:id/members/:user"}, 167 {http.MethodDelete, "/teams/:id/members/:user"}, 168 {http.MethodGet, "/teams/:id/repos"}, 169 {http.MethodGet, "/teams/:id/repos/:owner/:repo"}, 170 {http.MethodPut, "/teams/:id/repos/:owner/:repo"}, 171 {http.MethodDelete, "/teams/:id/repos/:owner/:repo"}, 172 {http.MethodGet, "/user/teams"}, 173 174 // Pull Requests 175 {http.MethodGet, "/repos/:owner/:repo/pulls"}, 176 {http.MethodGet, "/repos/:owner/:repo/pulls/:number"}, 177 {http.MethodPost, "/repos/:owner/:repo/pulls"}, 178 //{http.MethodPatch, "/repos/:owner/:repo/pulls/:number"}, 179 {http.MethodGet, "/repos/:owner/:repo/pulls/:number/commits"}, 180 {http.MethodGet, "/repos/:owner/:repo/pulls/:number/files"}, 181 {http.MethodGet, "/repos/:owner/:repo/pulls/:number/merge"}, 182 {http.MethodPut, "/repos/:owner/:repo/pulls/:number/merge"}, 183 {http.MethodGet, "/repos/:owner/:repo/pulls/:number/comments"}, 184 //{http.MethodGet, "/repos/:owner/:repo/pulls/comments"}, 185 //{http.MethodGet, "/repos/:owner/:repo/pulls/comments/:number"}, 186 {http.MethodPut, "/repos/:owner/:repo/pulls/:number/comments"}, 187 //{http.MethodPatch, "/repos/:owner/:repo/pulls/comments/:number"}, 188 //{http.MethodDelete, "/repos/:owner/:repo/pulls/comments/:number"}, 189 190 // Repositories 191 {http.MethodGet, "/user/repos"}, 192 {http.MethodGet, "/users/:user/repos"}, 193 {http.MethodGet, "/orgs/:org/repos"}, 194 {http.MethodGet, "/repositories"}, 195 {http.MethodPost, "/user/repos"}, 196 {http.MethodPost, "/orgs/:org/repos"}, 197 {http.MethodGet, "/repos/:owner/:repo"}, 198 //{http.MethodPatch, "/repos/:owner/:repo"}, 199 {http.MethodGet, "/repos/:owner/:repo/contributors"}, 200 {http.MethodGet, "/repos/:owner/:repo/languages"}, 201 {http.MethodGet, "/repos/:owner/:repo/teams"}, 202 {http.MethodGet, "/repos/:owner/:repo/tags"}, 203 {http.MethodGet, "/repos/:owner/:repo/branches"}, 204 {http.MethodGet, "/repos/:owner/:repo/branches/:branch"}, 205 {http.MethodDelete, "/repos/:owner/:repo"}, 206 {http.MethodGet, "/repos/:owner/:repo/collaborators"}, 207 {http.MethodGet, "/repos/:owner/:repo/collaborators/:user"}, 208 {http.MethodPut, "/repos/:owner/:repo/collaborators/:user"}, 209 {http.MethodDelete, "/repos/:owner/:repo/collaborators/:user"}, 210 {http.MethodGet, "/repos/:owner/:repo/comments"}, 211 {http.MethodGet, "/repos/:owner/:repo/commits/:sha/comments"}, 212 {http.MethodPost, "/repos/:owner/:repo/commits/:sha/comments"}, 213 {http.MethodGet, "/repos/:owner/:repo/comments/:id"}, 214 //{http.MethodPatch, "/repos/:owner/:repo/comments/:id"}, 215 {http.MethodDelete, "/repos/:owner/:repo/comments/:id"}, 216 {http.MethodGet, "/repos/:owner/:repo/commits"}, 217 {http.MethodGet, "/repos/:owner/:repo/commits/:sha"}, 218 {http.MethodGet, "/repos/:owner/:repo/readme"}, 219 //{http.MethodGet, "/repos/:owner/:repo/contents/*path"}, 220 //{http.MethodPut, "/repos/:owner/:repo/contents/*path"}, 221 //{http.MethodDelete, "/repos/:owner/:repo/contents/*path"}, 222 //{http.MethodGet, "/repos/:owner/:repo/:archive_format/:ref"}, 223 {http.MethodGet, "/repos/:owner/:repo/keys"}, 224 {http.MethodGet, "/repos/:owner/:repo/keys/:id"}, 225 {http.MethodPost, "/repos/:owner/:repo/keys"}, 226 //{http.MethodPatch, "/repos/:owner/:repo/keys/:id"}, 227 {http.MethodDelete, "/repos/:owner/:repo/keys/:id"}, 228 {http.MethodGet, "/repos/:owner/:repo/downloads"}, 229 {http.MethodGet, "/repos/:owner/:repo/downloads/:id"}, 230 {http.MethodDelete, "/repos/:owner/:repo/downloads/:id"}, 231 {http.MethodGet, "/repos/:owner/:repo/forks"}, 232 {http.MethodPost, "/repos/:owner/:repo/forks"}, 233 {http.MethodGet, "/repos/:owner/:repo/hooks"}, 234 {http.MethodGet, "/repos/:owner/:repo/hooks/:id"}, 235 {http.MethodPost, "/repos/:owner/:repo/hooks"}, 236 //{http.MethodPatch, "/repos/:owner/:repo/hooks/:id"}, 237 {http.MethodPost, "/repos/:owner/:repo/hooks/:id/tests"}, 238 {http.MethodDelete, "/repos/:owner/:repo/hooks/:id"}, 239 {http.MethodPost, "/repos/:owner/:repo/merges"}, 240 {http.MethodGet, "/repos/:owner/:repo/releases"}, 241 {http.MethodGet, "/repos/:owner/:repo/releases/:id"}, 242 {http.MethodPost, "/repos/:owner/:repo/releases"}, 243 //{http.MethodPatch, "/repos/:owner/:repo/releases/:id"}, 244 {http.MethodDelete, "/repos/:owner/:repo/releases/:id"}, 245 {http.MethodGet, "/repos/:owner/:repo/releases/:id/assets"}, 246 {http.MethodGet, "/repos/:owner/:repo/stats/contributors"}, 247 {http.MethodGet, "/repos/:owner/:repo/stats/commit_activity"}, 248 {http.MethodGet, "/repos/:owner/:repo/stats/code_frequency"}, 249 {http.MethodGet, "/repos/:owner/:repo/stats/participation"}, 250 {http.MethodGet, "/repos/:owner/:repo/stats/punch_card"}, 251 {http.MethodGet, "/repos/:owner/:repo/statuses/:ref"}, 252 {http.MethodPost, "/repos/:owner/:repo/statuses/:ref"}, 253 254 // Search 255 {http.MethodGet, "/search/repositories"}, 256 {http.MethodGet, "/search/code"}, 257 {http.MethodGet, "/search/issues"}, 258 {http.MethodGet, "/search/users"}, 259 {http.MethodGet, "/legacy/issues/search/:owner/:repository/:state/:keyword"}, 260 {http.MethodGet, "/legacy/repos/search/:keyword"}, 261 {http.MethodGet, "/legacy/user/search/:keyword"}, 262 {http.MethodGet, "/legacy/user/email/:email"}, 263 264 // Users 265 {http.MethodGet, "/users/:user"}, 266 {http.MethodGet, "/user"}, 267 //{http.MethodPatch, "/user"}, 268 {http.MethodGet, "/users"}, 269 {http.MethodGet, "/user/emails"}, 270 {http.MethodPost, "/user/emails"}, 271 {http.MethodDelete, "/user/emails"}, 272 {http.MethodGet, "/users/:user/followers"}, 273 {http.MethodGet, "/user/followers"}, 274 {http.MethodGet, "/users/:user/following"}, 275 {http.MethodGet, "/user/following"}, 276 {http.MethodGet, "/user/following/:user"}, 277 {http.MethodGet, "/users/:user/following/:target_user"}, 278 {http.MethodPut, "/user/following/:user"}, 279 {http.MethodDelete, "/user/following/:user"}, 280 {http.MethodGet, "/users/:user/keys"}, 281 {http.MethodGet, "/user/keys"}, 282 {http.MethodGet, "/user/keys/:id"}, 283 {http.MethodPost, "/user/keys"}, 284 //{http.MethodPatch, "/user/keys/:id"}, 285 {http.MethodDelete, "/user/keys/:id"}, 286 } 287 288 func TestShouldBindUri(t *testing.T) { 289 DefaultWriter = os.Stdout 290 router := New() 291 292 type Person struct { 293 Name string `uri:"name" binding:"required"` 294 ID string `uri:"id" binding:"required"` 295 } 296 router.Handle(http.MethodGet, "/rest/:name/:id", func(c *Context) { 297 var person Person 298 assert.NoError(t, c.ShouldBindUri(&person)) 299 assert.True(t, "" != person.Name) 300 assert.True(t, "" != person.ID) 301 c.String(http.StatusOK, "ShouldBindUri test OK") 302 }) 303 304 path, _ := exampleFromPath("/rest/:name/:id") 305 w := performRequest(router, http.MethodGet, path) 306 assert.Equal(t, "ShouldBindUri test OK", w.Body.String()) 307 assert.Equal(t, http.StatusOK, w.Code) 308 } 309 310 func TestBindUri(t *testing.T) { 311 DefaultWriter = os.Stdout 312 router := New() 313 314 type Person struct { 315 Name string `uri:"name" binding:"required"` 316 ID string `uri:"id" binding:"required"` 317 } 318 router.Handle(http.MethodGet, "/rest/:name/:id", func(c *Context) { 319 var person Person 320 assert.NoError(t, c.BindUri(&person)) 321 assert.True(t, "" != person.Name) 322 assert.True(t, "" != person.ID) 323 c.String(http.StatusOK, "BindUri test OK") 324 }) 325 326 path, _ := exampleFromPath("/rest/:name/:id") 327 w := performRequest(router, http.MethodGet, path) 328 assert.Equal(t, "BindUri test OK", w.Body.String()) 329 assert.Equal(t, http.StatusOK, w.Code) 330 } 331 332 func TestBindUriError(t *testing.T) { 333 DefaultWriter = os.Stdout 334 router := New() 335 336 type Member struct { 337 Number string `uri:"num" binding:"required,uuid"` 338 } 339 router.Handle(http.MethodGet, "/new/rest/:num", func(c *Context) { 340 var m Member 341 assert.Error(t, c.BindUri(&m)) 342 }) 343 344 path1, _ := exampleFromPath("/new/rest/:num") 345 w1 := performRequest(router, http.MethodGet, path1) 346 assert.Equal(t, http.StatusBadRequest, w1.Code) 347 } 348 349 func TestRaceContextCopy(t *testing.T) { 350 DefaultWriter = os.Stdout 351 router := Default() 352 router.GET("/test/copy/race", func(c *Context) { 353 c.Set("1", 0) 354 c.Set("2", 0) 355 356 // Sending a copy of the Context to two separate routines 357 go readWriteKeys(c.Copy()) 358 go readWriteKeys(c.Copy()) 359 c.String(http.StatusOK, "run OK, no panics") 360 }) 361 w := performRequest(router, http.MethodGet, "/test/copy/race") 362 assert.Equal(t, "run OK, no panics", w.Body.String()) 363 } 364 365 func readWriteKeys(c *Context) { 366 for { 367 c.Set("1", rand.Int()) 368 c.Set("2", c.Value("1")) 369 } 370 } 371 372 func githubConfigRouter(router *Engine) { 373 for _, route := range githubAPI { 374 router.Handle(route.method, route.path, func(c *Context) { 375 output := make(map[string]string, len(c.Params)+1) 376 output["status"] = "good" 377 for _, param := range c.Params { 378 output[param.Key] = param.Value 379 } 380 c.JSON(http.StatusOK, output) 381 }) 382 } 383 } 384 385 func TestGithubAPI(t *testing.T) { 386 DefaultWriter = os.Stdout 387 router := New() 388 githubConfigRouter(router) 389 390 for _, route := range githubAPI { 391 path, values := exampleFromPath(route.path) 392 w := performRequest(router, route.method, path) 393 394 // TEST 395 assert.Contains(t, w.Body.String(), "\"status\":\"good\"") 396 for _, value := range values { 397 str := fmt.Sprintf("\"%s\":\"%s\"", value.Key, value.Value) 398 assert.Contains(t, w.Body.String(), str) 399 } 400 } 401 } 402 403 func exampleFromPath(path string) (string, Params) { 404 output := new(bytes.Buffer) 405 params := make(Params, 0, 6) 406 start := -1 407 for i, c := range path { 408 if c == ':' { 409 start = i + 1 410 } 411 if start >= 0 { 412 if c == '/' { 413 value := fmt.Sprint(rand.Intn(100000)) 414 params = append(params, Param{ 415 Key: path[start:i], 416 Value: value, 417 }) 418 output.WriteString(value) 419 output.WriteRune(c) 420 start = -1 421 } 422 } else { 423 output.WriteRune(c) 424 } 425 } 426 if start >= 0 { 427 value := fmt.Sprint(rand.Intn(100000)) 428 params = append(params, Param{ 429 Key: path[start:], 430 Value: value, 431 }) 432 output.WriteString(value) 433 } 434 435 return output.String(), params 436 } 437 438 func BenchmarkGithub(b *testing.B) { 439 router := New() 440 githubConfigRouter(router) 441 runRequest(b, router, http.MethodGet, "/legacy/issues/search/:owner/:repository/:state/:keyword") 442 } 443 444 func BenchmarkParallelGithub(b *testing.B) { 445 DefaultWriter = os.Stdout 446 router := New() 447 githubConfigRouter(router) 448 449 req, _ := http.NewRequest(http.MethodPost, "/repos/manucorporat/sse/git/blobs", nil) 450 451 b.RunParallel(func(pb *testing.PB) { 452 // Each goroutine has its own bytes.Buffer. 453 for pb.Next() { 454 w := httptest.NewRecorder() 455 router.ServeHTTP(w, req) 456 } 457 }) 458 } 459 460 func BenchmarkParallelGithubDefault(b *testing.B) { 461 DefaultWriter = os.Stdout 462 router := New() 463 githubConfigRouter(router) 464 465 req, _ := http.NewRequest(http.MethodPost, "/repos/manucorporat/sse/git/blobs", nil) 466 467 b.RunParallel(func(pb *testing.PB) { 468 // Each goroutine has its own bytes.Buffer. 469 for pb.Next() { 470 w := httptest.NewRecorder() 471 router.ServeHTTP(w, req) 472 } 473 }) 474 }