VMware vSphere Integrated Containers provider (#206)
* Add Virtual Kubelet provider for VIC Initial virtual kubelet provider for VMware VIC. This provider currently handles creating and starting of a pod VM via the VIC portlayer and persona server. Image store handling via the VIC persona server. This provider currently requires the feature/wolfpack branch of VIC. * Added pod stop and delete. Also added node capacity. Added the ability to stop and delete pod VMs via VIC. Also retrieve node capacity information from the VCH. * Cleanup and readme file Some file clean up and added a Readme.md markdown file for the VIC provider. * Cleaned up errors, added function comments, moved operation code 1. Cleaned up error handling. Set standard for creating errors. 2. Added method prototype comments for all interface functions. 3. Moved PodCreator, PodStarter, PodStopper, and PodDeleter to a new folder. * Add mocking code and unit tests for podcache, podcreator, and podstarter Used the unit test framework used in VIC to handle assertions in the provider's unit test. Mocking code generated using OSS project mockery, which is compatible with the testify assertion framework. * Vendored packages for the VIC provider Requires feature/wolfpack branch of VIC and a few specific commit sha of projects used within VIC. * Implementation of POD Stopper and Deleter unit tests (#4) * Updated files for initial PR
This commit is contained in:
19
vendor/github.com/go-openapi/runtime/middleware/denco/LICENSE
generated
vendored
Executable file
19
vendor/github.com/go-openapi/runtime/middleware/denco/LICENSE
generated
vendored
Executable file
@@ -0,0 +1,19 @@
|
||||
Copyright (c) 2014 Naoya Inada <naoina@kuune.org>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
180
vendor/github.com/go-openapi/runtime/middleware/denco/README.md
generated
vendored
Executable file
180
vendor/github.com/go-openapi/runtime/middleware/denco/README.md
generated
vendored
Executable file
@@ -0,0 +1,180 @@
|
||||
# Denco [](https://travis-ci.org/naoina/denco)
|
||||
|
||||
The fast and flexible HTTP request router for [Go](http://golang.org).
|
||||
|
||||
Denco is based on Double-Array implementation of [Kocha-urlrouter](https://github.com/naoina/kocha-urlrouter).
|
||||
However, Denco is optimized and some features added.
|
||||
|
||||
## Features
|
||||
|
||||
* Fast (See [go-http-routing-benchmark](https://github.com/naoina/go-http-routing-benchmark))
|
||||
* [URL patterns](#url-patterns) (`/foo/:bar` and `/foo/*wildcard`)
|
||||
* Small (but enough) URL router API
|
||||
* HTTP request multiplexer like `http.ServeMux`
|
||||
|
||||
## Installation
|
||||
|
||||
go get -u github.com/go-openapi/runtime/middleware/denco
|
||||
|
||||
## Using as HTTP request multiplexer
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"net/http"
|
||||
|
||||
"github.com/go-openapi/runtime/middleware/denco"
|
||||
)
|
||||
|
||||
func Index(w http.ResponseWriter, r *http.Request, params denco.Params) {
|
||||
fmt.Fprintf(w, "Welcome to Denco!\n")
|
||||
}
|
||||
|
||||
func User(w http.ResponseWriter, r *http.Request, params denco.Params) {
|
||||
fmt.Fprintf(w, "Hello %s!\n", params.Get("name"))
|
||||
}
|
||||
|
||||
func main() {
|
||||
mux := denco.NewMux()
|
||||
handler, err := mux.Build([]denco.Handler{
|
||||
mux.GET("/", Index),
|
||||
mux.GET("/user/:name", User),
|
||||
mux.POST("/user/:name", User),
|
||||
})
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
log.Fatal(http.ListenAndServe(":8080", handler))
|
||||
}
|
||||
```
|
||||
|
||||
## Using as URL router
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/go-openapi/runtime/middleware/denco"
|
||||
)
|
||||
|
||||
type route struct {
|
||||
name string
|
||||
}
|
||||
|
||||
func main() {
|
||||
router := denco.New()
|
||||
router.Build([]denco.Record{
|
||||
{"/", &route{"root"}},
|
||||
{"/user/:id", &route{"user"}},
|
||||
{"/user/:name/:id", &route{"username"}},
|
||||
{"/static/*filepath", &route{"static"}},
|
||||
})
|
||||
|
||||
data, params, found := router.Lookup("/")
|
||||
// print `&main.route{name:"root"}, denco.Params(nil), true`.
|
||||
fmt.Printf("%#v, %#v, %#v\n", data, params, found)
|
||||
|
||||
data, params, found = router.Lookup("/user/hoge")
|
||||
// print `&main.route{name:"user"}, denco.Params{denco.Param{Name:"id", Value:"hoge"}}, true`.
|
||||
fmt.Printf("%#v, %#v, %#v\n", data, params, found)
|
||||
|
||||
data, params, found = router.Lookup("/user/hoge/7")
|
||||
// print `&main.route{name:"username"}, denco.Params{denco.Param{Name:"name", Value:"hoge"}, denco.Param{Name:"id", Value:"7"}}, true`.
|
||||
fmt.Printf("%#v, %#v, %#v\n", data, params, found)
|
||||
|
||||
data, params, found = router.Lookup("/static/path/to/file")
|
||||
// print `&main.route{name:"static"}, denco.Params{denco.Param{Name:"filepath", Value:"path/to/file"}}, true`.
|
||||
fmt.Printf("%#v, %#v, %#v\n", data, params, found)
|
||||
}
|
||||
```
|
||||
|
||||
See [Godoc](http://godoc.org/github.com/go-openapi/runtime/middleware/denco) for more details.
|
||||
|
||||
## Getting the value of path parameter
|
||||
|
||||
You can get the value of path parameter by 2 ways.
|
||||
|
||||
1. Using [`denco.Params.Get`](http://godoc.org/github.com/go-openapi/runtime/middleware/denco#Params.Get) method
|
||||
2. Find by loop
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/go-openapi/runtime/middleware/denco"
|
||||
)
|
||||
|
||||
func main() {
|
||||
router := denco.New()
|
||||
if err := router.Build([]denco.Record{
|
||||
{"/user/:name/:id", "route1"},
|
||||
}); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
// 1. Using denco.Params.Get method.
|
||||
_, params, _ := router.Lookup("/user/alice/1")
|
||||
name := params.Get("name")
|
||||
if name != "" {
|
||||
fmt.Printf("Hello %s.\n", name) // prints "Hello alice.".
|
||||
}
|
||||
|
||||
// 2. Find by loop.
|
||||
for _, param := range params {
|
||||
if param.Name == "name" {
|
||||
fmt.Printf("Hello %s.\n", name) // prints "Hello alice.".
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## URL patterns
|
||||
|
||||
Denco's route matching strategy is "most nearly matching".
|
||||
|
||||
When routes `/:name` and `/alice` have been built, URI `/alice` matches the route `/alice`, not `/:name`.
|
||||
Because URI `/alice` is more match with the route `/alice` than `/:name`.
|
||||
|
||||
For more example, when routes below have been built:
|
||||
|
||||
```
|
||||
/user/alice
|
||||
/user/:name
|
||||
/user/:name/:id
|
||||
/user/alice/:id
|
||||
/user/:id/bob
|
||||
```
|
||||
|
||||
Routes matching are:
|
||||
|
||||
```
|
||||
/user/alice => "/user/alice" (no match with "/user/:name")
|
||||
/user/bob => "/user/:name"
|
||||
/user/naoina/1 => "/user/:name/1"
|
||||
/user/alice/1 => "/user/alice/:id" (no match with "/user/:name/:id")
|
||||
/user/1/bob => "/user/:id/bob" (no match with "/user/:name/:id")
|
||||
/user/alice/bob => "/user/alice/:id" (no match with "/user/:name/:id" and "/user/:id/bob")
|
||||
```
|
||||
|
||||
## Limitation
|
||||
|
||||
Denco has some limitations below.
|
||||
|
||||
* Number of param records (such as `/:name`) must be less than 2^22
|
||||
* Number of elements of internal slice must be less than 2^22
|
||||
|
||||
## Benchmarks
|
||||
|
||||
cd $GOPATH/github.com/go-openapi/runtime/middleware/denco
|
||||
go test -bench . -benchmem
|
||||
|
||||
## License
|
||||
|
||||
Denco is licensed under the MIT License.
|
||||
452
vendor/github.com/go-openapi/runtime/middleware/denco/router.go
generated
vendored
Executable file
452
vendor/github.com/go-openapi/runtime/middleware/denco/router.go
generated
vendored
Executable file
@@ -0,0 +1,452 @@
|
||||
// Package denco provides fast URL router.
|
||||
package denco
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sort"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const (
|
||||
// ParamCharacter is a special character for path parameter.
|
||||
ParamCharacter = ':'
|
||||
|
||||
// WildcardCharacter is a special character for wildcard path parameter.
|
||||
WildcardCharacter = '*'
|
||||
|
||||
// TerminationCharacter is a special character for end of path.
|
||||
TerminationCharacter = '#'
|
||||
|
||||
// MaxSize is max size of records and internal slice.
|
||||
MaxSize = (1 << 22) - 1
|
||||
)
|
||||
|
||||
// Router represents a URL router.
|
||||
type Router struct {
|
||||
// SizeHint expects the maximum number of path parameters in records to Build.
|
||||
// SizeHint will be used to determine the capacity of the memory to allocate.
|
||||
// By default, SizeHint will be determined from given records to Build.
|
||||
SizeHint int
|
||||
|
||||
static map[string]interface{}
|
||||
param *doubleArray
|
||||
}
|
||||
|
||||
// New returns a new Router.
|
||||
func New() *Router {
|
||||
return &Router{
|
||||
SizeHint: -1,
|
||||
static: make(map[string]interface{}),
|
||||
param: newDoubleArray(),
|
||||
}
|
||||
}
|
||||
|
||||
// Lookup returns data and path parameters that associated with path.
|
||||
// params is a slice of the Param that arranged in the order in which parameters appeared.
|
||||
// e.g. when built routing path is "/path/to/:id/:name" and given path is "/path/to/1/alice". params order is [{"id": "1"}, {"name": "alice"}], not [{"name": "alice"}, {"id": "1"}].
|
||||
func (rt *Router) Lookup(path string) (data interface{}, params Params, found bool) {
|
||||
if data, found := rt.static[path]; found {
|
||||
return data, nil, true
|
||||
}
|
||||
if len(rt.param.node) == 1 {
|
||||
return nil, nil, false
|
||||
}
|
||||
nd, params, found := rt.param.lookup(path, make([]Param, 0, rt.SizeHint), 1)
|
||||
if !found {
|
||||
return nil, nil, false
|
||||
}
|
||||
for i := 0; i < len(params); i++ {
|
||||
params[i].Name = nd.paramNames[i]
|
||||
}
|
||||
return nd.data, params, true
|
||||
}
|
||||
|
||||
// Build builds URL router from records.
|
||||
func (rt *Router) Build(records []Record) error {
|
||||
statics, params := makeRecords(records)
|
||||
if len(params) > MaxSize {
|
||||
return fmt.Errorf("denco: too many records")
|
||||
}
|
||||
if rt.SizeHint < 0 {
|
||||
rt.SizeHint = 0
|
||||
for _, p := range params {
|
||||
size := 0
|
||||
for _, k := range p.Key {
|
||||
if k == ParamCharacter || k == WildcardCharacter {
|
||||
size++
|
||||
}
|
||||
}
|
||||
if size > rt.SizeHint {
|
||||
rt.SizeHint = size
|
||||
}
|
||||
}
|
||||
}
|
||||
for _, r := range statics {
|
||||
rt.static[r.Key] = r.Value
|
||||
}
|
||||
if err := rt.param.build(params, 1, 0, make(map[int]struct{})); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Param represents name and value of path parameter.
|
||||
type Param struct {
|
||||
Name string
|
||||
Value string
|
||||
}
|
||||
|
||||
// Params represents the name and value of path parameters.
|
||||
type Params []Param
|
||||
|
||||
// Get gets the first value associated with the given name.
|
||||
// If there are no values associated with the key, Get returns "".
|
||||
func (ps Params) Get(name string) string {
|
||||
for _, p := range ps {
|
||||
if p.Name == name {
|
||||
return p.Value
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
type doubleArray struct {
|
||||
bc []baseCheck
|
||||
node []*node
|
||||
}
|
||||
|
||||
func newDoubleArray() *doubleArray {
|
||||
return &doubleArray{
|
||||
bc: []baseCheck{0},
|
||||
node: []*node{nil}, // A start index is adjusting to 1 because 0 will be used as a mark of non-existent node.
|
||||
}
|
||||
}
|
||||
|
||||
// baseCheck contains BASE, CHECK and Extra flags.
|
||||
// From the top, 22bits of BASE, 2bits of Extra flags and 8bits of CHECK.
|
||||
//
|
||||
// BASE (22bit) | Extra flags (2bit) | CHECK (8bit)
|
||||
// |----------------------|--|--------|
|
||||
// 32 10 8 0
|
||||
type baseCheck uint32
|
||||
|
||||
func (bc baseCheck) Base() int {
|
||||
return int(bc >> 10)
|
||||
}
|
||||
|
||||
func (bc *baseCheck) SetBase(base int) {
|
||||
*bc |= baseCheck(base) << 10
|
||||
}
|
||||
|
||||
func (bc baseCheck) Check() byte {
|
||||
return byte(bc)
|
||||
}
|
||||
|
||||
func (bc *baseCheck) SetCheck(check byte) {
|
||||
*bc |= baseCheck(check)
|
||||
}
|
||||
|
||||
func (bc baseCheck) IsEmpty() bool {
|
||||
return bc&0xfffffcff == 0
|
||||
}
|
||||
|
||||
func (bc baseCheck) IsSingleParam() bool {
|
||||
return bc¶mTypeSingle == paramTypeSingle
|
||||
}
|
||||
|
||||
func (bc baseCheck) IsWildcardParam() bool {
|
||||
return bc¶mTypeWildcard == paramTypeWildcard
|
||||
}
|
||||
|
||||
func (bc baseCheck) IsAnyParam() bool {
|
||||
return bc¶mTypeAny != 0
|
||||
}
|
||||
|
||||
func (bc *baseCheck) SetSingleParam() {
|
||||
*bc |= (1 << 8)
|
||||
}
|
||||
|
||||
func (bc *baseCheck) SetWildcardParam() {
|
||||
*bc |= (1 << 9)
|
||||
}
|
||||
|
||||
const (
|
||||
paramTypeSingle = 0x0100
|
||||
paramTypeWildcard = 0x0200
|
||||
paramTypeAny = 0x0300
|
||||
)
|
||||
|
||||
func (da *doubleArray) lookup(path string, params []Param, idx int) (*node, []Param, bool) {
|
||||
indices := make([]uint64, 0, 1)
|
||||
for i := 0; i < len(path); i++ {
|
||||
if da.bc[idx].IsAnyParam() {
|
||||
indices = append(indices, (uint64(i)<<32)|(uint64(idx)&0xffffffff))
|
||||
}
|
||||
c := path[i]
|
||||
if idx = nextIndex(da.bc[idx].Base(), c); idx >= len(da.bc) || da.bc[idx].Check() != c {
|
||||
goto BACKTRACKING
|
||||
}
|
||||
}
|
||||
if next := nextIndex(da.bc[idx].Base(), TerminationCharacter); next < len(da.bc) && da.bc[next].Check() == TerminationCharacter {
|
||||
return da.node[da.bc[next].Base()], params, true
|
||||
}
|
||||
BACKTRACKING:
|
||||
for j := len(indices) - 1; j >= 0; j-- {
|
||||
i, idx := int(indices[j]>>32), int(indices[j]&0xffffffff)
|
||||
if da.bc[idx].IsSingleParam() {
|
||||
idx := nextIndex(da.bc[idx].Base(), ParamCharacter)
|
||||
if idx >= len(da.bc) {
|
||||
break
|
||||
}
|
||||
next := NextSeparator(path, i)
|
||||
params := append(params, Param{Value: path[i:next]})
|
||||
if nd, params, found := da.lookup(path[next:], params, idx); found {
|
||||
return nd, params, true
|
||||
}
|
||||
}
|
||||
if da.bc[idx].IsWildcardParam() {
|
||||
idx := nextIndex(da.bc[idx].Base(), WildcardCharacter)
|
||||
params := append(params, Param{Value: path[i:]})
|
||||
return da.node[da.bc[idx].Base()], params, true
|
||||
}
|
||||
}
|
||||
return nil, nil, false
|
||||
}
|
||||
|
||||
// build builds double-array from records.
|
||||
func (da *doubleArray) build(srcs []*record, idx, depth int, usedBase map[int]struct{}) error {
|
||||
sort.Stable(recordSlice(srcs))
|
||||
base, siblings, leaf, err := da.arrange(srcs, idx, depth, usedBase)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if leaf != nil {
|
||||
nd, err := makeNode(leaf)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
da.bc[idx].SetBase(len(da.node))
|
||||
da.node = append(da.node, nd)
|
||||
}
|
||||
for _, sib := range siblings {
|
||||
da.setCheck(nextIndex(base, sib.c), sib.c)
|
||||
}
|
||||
for _, sib := range siblings {
|
||||
records := srcs[sib.start:sib.end]
|
||||
switch sib.c {
|
||||
case ParamCharacter:
|
||||
for _, r := range records {
|
||||
next := NextSeparator(r.Key, depth+1)
|
||||
name := r.Key[depth+1 : next]
|
||||
r.paramNames = append(r.paramNames, name)
|
||||
r.Key = r.Key[next:]
|
||||
}
|
||||
da.bc[idx].SetSingleParam()
|
||||
if err := da.build(records, nextIndex(base, sib.c), 0, usedBase); err != nil {
|
||||
return err
|
||||
}
|
||||
case WildcardCharacter:
|
||||
r := records[0]
|
||||
name := r.Key[depth+1 : len(r.Key)-1]
|
||||
r.paramNames = append(r.paramNames, name)
|
||||
r.Key = ""
|
||||
da.bc[idx].SetWildcardParam()
|
||||
if err := da.build(records, nextIndex(base, sib.c), 0, usedBase); err != nil {
|
||||
return err
|
||||
}
|
||||
default:
|
||||
if err := da.build(records, nextIndex(base, sib.c), depth+1, usedBase); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// setBase sets BASE.
|
||||
func (da *doubleArray) setBase(i, base int) {
|
||||
da.bc[i].SetBase(base)
|
||||
}
|
||||
|
||||
// setCheck sets CHECK.
|
||||
func (da *doubleArray) setCheck(i int, check byte) {
|
||||
da.bc[i].SetCheck(check)
|
||||
}
|
||||
|
||||
// findEmptyIndex returns an index of unused BASE/CHECK node.
|
||||
func (da *doubleArray) findEmptyIndex(start int) int {
|
||||
i := start
|
||||
for ; i < len(da.bc); i++ {
|
||||
if da.bc[i].IsEmpty() {
|
||||
break
|
||||
}
|
||||
}
|
||||
return i
|
||||
}
|
||||
|
||||
// findBase returns good BASE.
|
||||
func (da *doubleArray) findBase(siblings []sibling, start int, usedBase map[int]struct{}) (base int) {
|
||||
for idx, firstChar := start+1, siblings[0].c; ; idx = da.findEmptyIndex(idx + 1) {
|
||||
base = nextIndex(idx, firstChar)
|
||||
if _, used := usedBase[base]; used {
|
||||
continue
|
||||
}
|
||||
i := 0
|
||||
for ; i < len(siblings); i++ {
|
||||
next := nextIndex(base, siblings[i].c)
|
||||
if len(da.bc) <= next {
|
||||
da.bc = append(da.bc, make([]baseCheck, next-len(da.bc)+1)...)
|
||||
}
|
||||
if !da.bc[next].IsEmpty() {
|
||||
break
|
||||
}
|
||||
}
|
||||
if i == len(siblings) {
|
||||
break
|
||||
}
|
||||
}
|
||||
usedBase[base] = struct{}{}
|
||||
return base
|
||||
}
|
||||
|
||||
func (da *doubleArray) arrange(records []*record, idx, depth int, usedBase map[int]struct{}) (base int, siblings []sibling, leaf *record, err error) {
|
||||
siblings, leaf, err = makeSiblings(records, depth)
|
||||
if err != nil {
|
||||
return -1, nil, nil, err
|
||||
}
|
||||
if len(siblings) < 1 {
|
||||
return -1, nil, leaf, nil
|
||||
}
|
||||
base = da.findBase(siblings, idx, usedBase)
|
||||
if base > MaxSize {
|
||||
return -1, nil, nil, fmt.Errorf("denco: too many elements of internal slice")
|
||||
}
|
||||
da.setBase(idx, base)
|
||||
return base, siblings, leaf, err
|
||||
}
|
||||
|
||||
// node represents a node of Double-Array.
|
||||
type node struct {
|
||||
data interface{}
|
||||
|
||||
// Names of path parameters.
|
||||
paramNames []string
|
||||
}
|
||||
|
||||
// makeNode returns a new node from record.
|
||||
func makeNode(r *record) (*node, error) {
|
||||
dups := make(map[string]bool)
|
||||
for _, name := range r.paramNames {
|
||||
if dups[name] {
|
||||
return nil, fmt.Errorf("denco: path parameter `%v' is duplicated in the key `%v'", name, r.Key)
|
||||
}
|
||||
dups[name] = true
|
||||
}
|
||||
return &node{data: r.Value, paramNames: r.paramNames}, nil
|
||||
}
|
||||
|
||||
// sibling represents an intermediate data of build for Double-Array.
|
||||
type sibling struct {
|
||||
// An index of start of duplicated characters.
|
||||
start int
|
||||
|
||||
// An index of end of duplicated characters.
|
||||
end int
|
||||
|
||||
// A character of sibling.
|
||||
c byte
|
||||
}
|
||||
|
||||
// nextIndex returns a next index of array of BASE/CHECK.
|
||||
func nextIndex(base int, c byte) int {
|
||||
return base ^ int(c)
|
||||
}
|
||||
|
||||
// makeSiblings returns slice of sibling.
|
||||
func makeSiblings(records []*record, depth int) (sib []sibling, leaf *record, err error) {
|
||||
var (
|
||||
pc byte
|
||||
n int
|
||||
)
|
||||
for i, r := range records {
|
||||
if len(r.Key) <= depth {
|
||||
leaf = r
|
||||
continue
|
||||
}
|
||||
c := r.Key[depth]
|
||||
switch {
|
||||
case pc < c:
|
||||
sib = append(sib, sibling{start: i, c: c})
|
||||
case pc == c:
|
||||
continue
|
||||
default:
|
||||
return nil, nil, fmt.Errorf("denco: BUG: routing table hasn't been sorted")
|
||||
}
|
||||
if n > 0 {
|
||||
sib[n-1].end = i
|
||||
}
|
||||
pc = c
|
||||
n++
|
||||
}
|
||||
if n == 0 {
|
||||
return nil, leaf, nil
|
||||
}
|
||||
sib[n-1].end = len(records)
|
||||
return sib, leaf, nil
|
||||
}
|
||||
|
||||
// Record represents a record data for router construction.
|
||||
type Record struct {
|
||||
// Key for router construction.
|
||||
Key string
|
||||
|
||||
// Result value for Key.
|
||||
Value interface{}
|
||||
}
|
||||
|
||||
// NewRecord returns a new Record.
|
||||
func NewRecord(key string, value interface{}) Record {
|
||||
return Record{
|
||||
Key: key,
|
||||
Value: value,
|
||||
}
|
||||
}
|
||||
|
||||
// record represents a record that use to build the Double-Array.
|
||||
type record struct {
|
||||
Record
|
||||
paramNames []string
|
||||
}
|
||||
|
||||
// makeRecords returns the records that use to build Double-Arrays.
|
||||
func makeRecords(srcs []Record) (statics, params []*record) {
|
||||
spChars := string([]byte{ParamCharacter, WildcardCharacter})
|
||||
termChar := string(TerminationCharacter)
|
||||
for _, r := range srcs {
|
||||
if strings.ContainsAny(r.Key, spChars) {
|
||||
r.Key += termChar
|
||||
params = append(params, &record{Record: r})
|
||||
} else {
|
||||
statics = append(statics, &record{Record: r})
|
||||
}
|
||||
}
|
||||
return statics, params
|
||||
}
|
||||
|
||||
// recordSlice represents a slice of Record for sort and implements the sort.Interface.
|
||||
type recordSlice []*record
|
||||
|
||||
// Len implements the sort.Interface.Len.
|
||||
func (rs recordSlice) Len() int {
|
||||
return len(rs)
|
||||
}
|
||||
|
||||
// Less implements the sort.Interface.Less.
|
||||
func (rs recordSlice) Less(i, j int) bool {
|
||||
return rs[i].Key < rs[j].Key
|
||||
}
|
||||
|
||||
// Swap implements the sort.Interface.Swap.
|
||||
func (rs recordSlice) Swap(i, j int) {
|
||||
rs[i], rs[j] = rs[j], rs[i]
|
||||
}
|
||||
178
vendor/github.com/go-openapi/runtime/middleware/denco/router_bench_test.go
generated
vendored
Executable file
178
vendor/github.com/go-openapi/runtime/middleware/denco/router_bench_test.go
generated
vendored
Executable file
@@ -0,0 +1,178 @@
|
||||
package denco_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/rand"
|
||||
"fmt"
|
||||
"math/big"
|
||||
"testing"
|
||||
|
||||
"github.com/go-openapi/runtime/middleware/denco"
|
||||
)
|
||||
|
||||
func BenchmarkRouterLookupStatic100(b *testing.B) {
|
||||
benchmarkRouterLookupStatic(b, 100)
|
||||
}
|
||||
|
||||
func BenchmarkRouterLookupStatic300(b *testing.B) {
|
||||
benchmarkRouterLookupStatic(b, 300)
|
||||
}
|
||||
|
||||
func BenchmarkRouterLookupStatic700(b *testing.B) {
|
||||
benchmarkRouterLookupStatic(b, 700)
|
||||
}
|
||||
|
||||
func BenchmarkRouterLookupSingleParam100(b *testing.B) {
|
||||
records := makeTestSingleParamRecords(100)
|
||||
benchmarkRouterLookupSingleParam(b, records)
|
||||
}
|
||||
|
||||
func BenchmarkRouterLookupSingleParam300(b *testing.B) {
|
||||
records := makeTestSingleParamRecords(300)
|
||||
benchmarkRouterLookupSingleParam(b, records)
|
||||
}
|
||||
|
||||
func BenchmarkRouterLookupSingleParam700(b *testing.B) {
|
||||
records := makeTestSingleParamRecords(700)
|
||||
benchmarkRouterLookupSingleParam(b, records)
|
||||
}
|
||||
|
||||
func BenchmarkRouterLookupSingle2Param100(b *testing.B) {
|
||||
records := makeTestSingle2ParamRecords(100)
|
||||
benchmarkRouterLookupSingleParam(b, records)
|
||||
}
|
||||
|
||||
func BenchmarkRouterLookupSingle2Param300(b *testing.B) {
|
||||
records := makeTestSingle2ParamRecords(300)
|
||||
benchmarkRouterLookupSingleParam(b, records)
|
||||
}
|
||||
|
||||
func BenchmarkRouterLookupSingle2Param700(b *testing.B) {
|
||||
records := makeTestSingle2ParamRecords(700)
|
||||
benchmarkRouterLookupSingleParam(b, records)
|
||||
}
|
||||
|
||||
func BenchmarkRouterBuildStatic100(b *testing.B) {
|
||||
records := makeTestStaticRecords(100)
|
||||
benchmarkRouterBuild(b, records)
|
||||
}
|
||||
|
||||
func BenchmarkRouterBuildStatic300(b *testing.B) {
|
||||
records := makeTestStaticRecords(300)
|
||||
benchmarkRouterBuild(b, records)
|
||||
}
|
||||
|
||||
func BenchmarkRouterBuildStatic700(b *testing.B) {
|
||||
records := makeTestStaticRecords(700)
|
||||
benchmarkRouterBuild(b, records)
|
||||
}
|
||||
|
||||
func BenchmarkRouterBuildSingleParam100(b *testing.B) {
|
||||
records := makeTestSingleParamRecords(100)
|
||||
benchmarkRouterBuild(b, records)
|
||||
}
|
||||
|
||||
func BenchmarkRouterBuildSingleParam300(b *testing.B) {
|
||||
records := makeTestSingleParamRecords(300)
|
||||
benchmarkRouterBuild(b, records)
|
||||
}
|
||||
|
||||
func BenchmarkRouterBuildSingleParam700(b *testing.B) {
|
||||
records := makeTestSingleParamRecords(700)
|
||||
benchmarkRouterBuild(b, records)
|
||||
}
|
||||
|
||||
func BenchmarkRouterBuildSingle2Param100(b *testing.B) {
|
||||
records := makeTestSingle2ParamRecords(100)
|
||||
benchmarkRouterBuild(b, records)
|
||||
}
|
||||
|
||||
func BenchmarkRouterBuildSingle2Param300(b *testing.B) {
|
||||
records := makeTestSingle2ParamRecords(300)
|
||||
benchmarkRouterBuild(b, records)
|
||||
}
|
||||
|
||||
func BenchmarkRouterBuildSingle2Param700(b *testing.B) {
|
||||
records := makeTestSingle2ParamRecords(700)
|
||||
benchmarkRouterBuild(b, records)
|
||||
}
|
||||
|
||||
func benchmarkRouterLookupStatic(b *testing.B, n int) {
|
||||
b.StopTimer()
|
||||
router := denco.New()
|
||||
records := makeTestStaticRecords(n)
|
||||
if err := router.Build(records); err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
record := pickTestRecord(records)
|
||||
b.StartTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
if r, _, _ := router.Lookup(record.Key); r != record.Value {
|
||||
b.Fail()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func benchmarkRouterLookupSingleParam(b *testing.B, records []denco.Record) {
|
||||
router := denco.New()
|
||||
if err := router.Build(records); err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
record := pickTestRecord(records)
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
if _, _, found := router.Lookup(record.Key); !found {
|
||||
b.Fail()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func benchmarkRouterBuild(b *testing.B, records []denco.Record) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
router := denco.New()
|
||||
if err := router.Build(records); err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func makeTestStaticRecords(n int) []denco.Record {
|
||||
records := make([]denco.Record, n)
|
||||
for i := 0; i < n; i++ {
|
||||
records[i] = denco.NewRecord("/"+randomString(50), fmt.Sprintf("testroute%d", i))
|
||||
}
|
||||
return records
|
||||
}
|
||||
|
||||
func makeTestSingleParamRecords(n int) []denco.Record {
|
||||
records := make([]denco.Record, n)
|
||||
for i := 0; i < len(records); i++ {
|
||||
records[i] = denco.NewRecord(fmt.Sprintf("/user%d/:name", i), fmt.Sprintf("testroute%d", i))
|
||||
}
|
||||
return records
|
||||
}
|
||||
|
||||
func makeTestSingle2ParamRecords(n int) []denco.Record {
|
||||
records := make([]denco.Record, n)
|
||||
for i := 0; i < len(records); i++ {
|
||||
records[i] = denco.NewRecord(fmt.Sprintf("/user%d/:name/comment/:id", i), fmt.Sprintf("testroute%d", i))
|
||||
}
|
||||
return records
|
||||
}
|
||||
|
||||
func pickTestRecord(records []denco.Record) denco.Record {
|
||||
return records[len(records)/2]
|
||||
}
|
||||
|
||||
func randomString(n int) string {
|
||||
const srcStrings = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789/"
|
||||
var buf bytes.Buffer
|
||||
for i := 0; i < n; i++ {
|
||||
num, err := rand.Int(rand.Reader, big.NewInt(int64(len(srcStrings)-1)))
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
buf.WriteByte(srcStrings[num.Int64()])
|
||||
}
|
||||
return buf.String()
|
||||
}
|
||||
524
vendor/github.com/go-openapi/runtime/middleware/denco/router_test.go
generated
vendored
Executable file
524
vendor/github.com/go-openapi/runtime/middleware/denco/router_test.go
generated
vendored
Executable file
@@ -0,0 +1,524 @@
|
||||
package denco_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"reflect"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/go-openapi/runtime/middleware/denco"
|
||||
)
|
||||
|
||||
func routes() []denco.Record {
|
||||
return []denco.Record{
|
||||
{"/", "testroute0"},
|
||||
{"/path/to/route", "testroute1"},
|
||||
{"/path/to/other", "testroute2"},
|
||||
{"/path/to/route/a", "testroute3"},
|
||||
{"/path/to/:param", "testroute4"},
|
||||
{"/gists/:param1/foo/:param2", "testroute12"},
|
||||
{"/gists/:param1/foo/bar", "testroute11"},
|
||||
{"/:param1/:param2/foo/:param3", "testroute13"},
|
||||
{"/path/to/wildcard/*routepath", "testroute5"},
|
||||
{"/path/to/:param1/:param2", "testroute6"},
|
||||
{"/path/to/:param1/sep/:param2", "testroute7"},
|
||||
{"/:year/:month/:day", "testroute8"},
|
||||
{"/user/:id", "testroute9"},
|
||||
{"/a/to/b/:param/*routepath", "testroute10"},
|
||||
}
|
||||
}
|
||||
|
||||
var realURIs = []denco.Record{
|
||||
{"/authorizations", "/authorizations"},
|
||||
{"/authorizations/:id", "/authorizations/:id"},
|
||||
{"/applications/:client_id/tokens/:access_token", "/applications/:client_id/tokens/:access_token"},
|
||||
{"/events", "/events"},
|
||||
{"/repos/:owner/:repo/events", "/repos/:owner/:repo/events"},
|
||||
{"/networks/:owner/:repo/events", "/networks/:owner/:repo/events"},
|
||||
{"/orgs/:org/events", "/orgs/:org/events"},
|
||||
{"/users/:user/received_events", "/users/:user/received_events"},
|
||||
{"/users/:user/received_events/public", "/users/:user/received_events/public"},
|
||||
{"/users/:user/events", "/users/:user/events"},
|
||||
{"/users/:user/events/public", "/users/:user/events/public"},
|
||||
{"/users/:user/events/orgs/:org", "/users/:user/events/orgs/:org"},
|
||||
{"/feeds", "/feeds"},
|
||||
{"/notifications", "/notifications"},
|
||||
{"/repos/:owner/:repo/notifications", "/repos/:owner/:repo/notifications"},
|
||||
{"/notifications/threads/:id", "/notifications/threads/:id"},
|
||||
{"/notifications/threads/:id/subscription", "/notifications/threads/:id/subscription"},
|
||||
{"/repos/:owner/:repo/stargazers", "/repos/:owner/:repo/stargazers"},
|
||||
{"/users/:user/starred", "/users/:user/starred"},
|
||||
{"/user/starred", "/user/starred"},
|
||||
{"/user/starred/:owner/:repo", "/user/starred/:owner/:repo"},
|
||||
{"/repos/:owner/:repo/subscribers", "/repos/:owner/:repo/subscribers"},
|
||||
{"/users/:user/subscriptions", "/users/:user/subscriptions"},
|
||||
{"/user/subscriptions", "/user/subscriptions"},
|
||||
{"/repos/:owner/:repo/subscription", "/repos/:owner/:repo/subscription"},
|
||||
{"/user/subscriptions/:owner/:repo", "/user/subscriptions/:owner/:repo"},
|
||||
{"/users/:user/gists", "/users/:user/gists"},
|
||||
{"/gists", "/gists"},
|
||||
{"/gists/:id", "/gists/:id"},
|
||||
{"/gists/:id/star", "/gists/:id/star"},
|
||||
{"/repos/:owner/:repo/git/blobs/:sha", "/repos/:owner/:repo/git/blobs/:sha"},
|
||||
{"/repos/:owner/:repo/git/commits/:sha", "/repos/:owner/:repo/git/commits/:sha"},
|
||||
{"/repos/:owner/:repo/git/refs", "/repos/:owner/:repo/git/refs"},
|
||||
{"/repos/:owner/:repo/git/tags/:sha", "/repos/:owner/:repo/git/tags/:sha"},
|
||||
{"/repos/:owner/:repo/git/trees/:sha", "/repos/:owner/:repo/git/trees/:sha"},
|
||||
{"/issues", "/issues"},
|
||||
{"/user/issues", "/user/issues"},
|
||||
{"/orgs/:org/issues", "/orgs/:org/issues"},
|
||||
{"/repos/:owner/:repo/issues", "/repos/:owner/:repo/issues"},
|
||||
{"/repos/:owner/:repo/issues/:number", "/repos/:owner/:repo/issues/:number"},
|
||||
{"/repos/:owner/:repo/assignees", "/repos/:owner/:repo/assignees"},
|
||||
{"/repos/:owner/:repo/assignees/:assignee", "/repos/:owner/:repo/assignees/:assignee"},
|
||||
{"/repos/:owner/:repo/issues/:number/comments", "/repos/:owner/:repo/issues/:number/comments"},
|
||||
{"/repos/:owner/:repo/issues/:number/events", "/repos/:owner/:repo/issues/:number/events"},
|
||||
{"/repos/:owner/:repo/labels", "/repos/:owner/:repo/labels"},
|
||||
{"/repos/:owner/:repo/labels/:name", "/repos/:owner/:repo/labels/:name"},
|
||||
{"/repos/:owner/:repo/issues/:number/labels", "/repos/:owner/:repo/issues/:number/labels"},
|
||||
{"/repos/:owner/:repo/milestones/:number/labels", "/repos/:owner/:repo/milestones/:number/labels"},
|
||||
{"/repos/:owner/:repo/milestones", "/repos/:owner/:repo/milestones"},
|
||||
{"/repos/:owner/:repo/milestones/:number", "/repos/:owner/:repo/milestones/:number"},
|
||||
{"/emojis", "/emojis"},
|
||||
{"/gitignore/templates", "/gitignore/templates"},
|
||||
{"/gitignore/templates/:name", "/gitignore/templates/:name"},
|
||||
{"/meta", "/meta"},
|
||||
{"/rate_limit", "/rate_limit"},
|
||||
{"/users/:user/orgs", "/users/:user/orgs"},
|
||||
{"/user/orgs", "/user/orgs"},
|
||||
{"/orgs/:org", "/orgs/:org"},
|
||||
{"/orgs/:org/members", "/orgs/:org/members"},
|
||||
{"/orgs/:org/members/:user", "/orgs/:org/members/:user"},
|
||||
{"/orgs/:org/public_members", "/orgs/:org/public_members"},
|
||||
{"/orgs/:org/public_members/:user", "/orgs/:org/public_members/:user"},
|
||||
{"/orgs/:org/teams", "/orgs/:org/teams"},
|
||||
{"/teams/:id", "/teams/:id"},
|
||||
{"/teams/:id/members", "/teams/:id/members"},
|
||||
{"/teams/:id/members/:user", "/teams/:id/members/:user"},
|
||||
{"/teams/:id/repos", "/teams/:id/repos"},
|
||||
{"/teams/:id/repos/:owner/:repo", "/teams/:id/repos/:owner/:repo"},
|
||||
{"/user/teams", "/user/teams"},
|
||||
{"/repos/:owner/:repo/pulls", "/repos/:owner/:repo/pulls"},
|
||||
{"/repos/:owner/:repo/pulls/:number", "/repos/:owner/:repo/pulls/:number"},
|
||||
{"/repos/:owner/:repo/pulls/:number/commits", "/repos/:owner/:repo/pulls/:number/commits"},
|
||||
{"/repos/:owner/:repo/pulls/:number/files", "/repos/:owner/:repo/pulls/:number/files"},
|
||||
{"/repos/:owner/:repo/pulls/:number/merge", "/repos/:owner/:repo/pulls/:number/merge"},
|
||||
{"/repos/:owner/:repo/pulls/:number/comments", "/repos/:owner/:repo/pulls/:number/comments"},
|
||||
{"/user/repos", "/user/repos"},
|
||||
{"/users/:user/repos", "/users/:user/repos"},
|
||||
{"/orgs/:org/repos", "/orgs/:org/repos"},
|
||||
{"/repositories", "/repositories"},
|
||||
{"/repos/:owner/:repo", "/repos/:owner/:repo"},
|
||||
{"/repos/:owner/:repo/contributors", "/repos/:owner/:repo/contributors"},
|
||||
{"/repos/:owner/:repo/languages", "/repos/:owner/:repo/languages"},
|
||||
{"/repos/:owner/:repo/teams", "/repos/:owner/:repo/teams"},
|
||||
{"/repos/:owner/:repo/tags", "/repos/:owner/:repo/tags"},
|
||||
{"/repos/:owner/:repo/branches", "/repos/:owner/:repo/branches"},
|
||||
{"/repos/:owner/:repo/branches/:branch", "/repos/:owner/:repo/branches/:branch"},
|
||||
{"/repos/:owner/:repo/collaborators", "/repos/:owner/:repo/collaborators"},
|
||||
{"/repos/:owner/:repo/collaborators/:user", "/repos/:owner/:repo/collaborators/:user"},
|
||||
{"/repos/:owner/:repo/comments", "/repos/:owner/:repo/comments"},
|
||||
{"/repos/:owner/:repo/commits/:sha/comments", "/repos/:owner/:repo/commits/:sha/comments"},
|
||||
{"/repos/:owner/:repo/comments/:id", "/repos/:owner/:repo/comments/:id"},
|
||||
{"/repos/:owner/:repo/commits", "/repos/:owner/:repo/commits"},
|
||||
{"/repos/:owner/:repo/commits/:sha", "/repos/:owner/:repo/commits/:sha"},
|
||||
{"/repos/:owner/:repo/readme", "/repos/:owner/:repo/readme"},
|
||||
{"/repos/:owner/:repo/keys", "/repos/:owner/:repo/keys"},
|
||||
{"/repos/:owner/:repo/keys/:id", "/repos/:owner/:repo/keys/:id"},
|
||||
{"/repos/:owner/:repo/downloads", "/repos/:owner/:repo/downloads"},
|
||||
{"/repos/:owner/:repo/downloads/:id", "/repos/:owner/:repo/downloads/:id"},
|
||||
{"/repos/:owner/:repo/forks", "/repos/:owner/:repo/forks"},
|
||||
{"/repos/:owner/:repo/hooks", "/repos/:owner/:repo/hooks"},
|
||||
{"/repos/:owner/:repo/hooks/:id", "/repos/:owner/:repo/hooks/:id"},
|
||||
{"/repos/:owner/:repo/releases", "/repos/:owner/:repo/releases"},
|
||||
{"/repos/:owner/:repo/releases/:id", "/repos/:owner/:repo/releases/:id"},
|
||||
{"/repos/:owner/:repo/releases/:id/assets", "/repos/:owner/:repo/releases/:id/assets"},
|
||||
{"/repos/:owner/:repo/stats/contributors", "/repos/:owner/:repo/stats/contributors"},
|
||||
{"/repos/:owner/:repo/stats/commit_activity", "/repos/:owner/:repo/stats/commit_activity"},
|
||||
{"/repos/:owner/:repo/stats/code_frequency", "/repos/:owner/:repo/stats/code_frequency"},
|
||||
{"/repos/:owner/:repo/stats/participation", "/repos/:owner/:repo/stats/participation"},
|
||||
{"/repos/:owner/:repo/stats/punch_card", "/repos/:owner/:repo/stats/punch_card"},
|
||||
{"/repos/:owner/:repo/statuses/:ref", "/repos/:owner/:repo/statuses/:ref"},
|
||||
{"/search/repositories", "/search/repositories"},
|
||||
{"/search/code", "/search/code"},
|
||||
{"/search/issues", "/search/issues"},
|
||||
{"/search/users", "/search/users"},
|
||||
{"/legacy/issues/search/:owner/:repository/:state/:keyword", "/legacy/issues/search/:owner/:repository/:state/:keyword"},
|
||||
{"/legacy/repos/search/:keyword", "/legacy/repos/search/:keyword"},
|
||||
{"/legacy/user/search/:keyword", "/legacy/user/search/:keyword"},
|
||||
{"/legacy/user/email/:email", "/legacy/user/email/:email"},
|
||||
{"/users/:user", "/users/:user"},
|
||||
{"/user", "/user"},
|
||||
{"/users", "/users"},
|
||||
{"/user/emails", "/user/emails"},
|
||||
{"/users/:user/followers", "/users/:user/followers"},
|
||||
{"/user/followers", "/user/followers"},
|
||||
{"/users/:user/following", "/users/:user/following"},
|
||||
{"/user/following", "/user/following"},
|
||||
{"/user/following/:user", "/user/following/:user"},
|
||||
{"/users/:user/following/:target_user", "/users/:user/following/:target_user"},
|
||||
{"/users/:user/keys", "/users/:user/keys"},
|
||||
{"/user/keys", "/user/keys"},
|
||||
{"/user/keys/:id", "/user/keys/:id"},
|
||||
{"/people/:userId", "/people/:userId"},
|
||||
{"/people", "/people"},
|
||||
{"/activities/:activityId/people/:collection", "/activities/:activityId/people/:collection"},
|
||||
{"/people/:userId/people/:collection", "/people/:userId/people/:collection"},
|
||||
{"/people/:userId/openIdConnect", "/people/:userId/openIdConnect"},
|
||||
{"/people/:userId/activities/:collection", "/people/:userId/activities/:collection"},
|
||||
{"/activities/:activityId", "/activities/:activityId"},
|
||||
{"/activities", "/activities"},
|
||||
{"/activities/:activityId/comments", "/activities/:activityId/comments"},
|
||||
{"/comments/:commentId", "/comments/:commentId"},
|
||||
{"/people/:userId/moments/:collection", "/people/:userId/moments/:collection"},
|
||||
}
|
||||
|
||||
type testcase struct {
|
||||
path string
|
||||
value interface{}
|
||||
params []denco.Param
|
||||
found bool
|
||||
}
|
||||
|
||||
func runLookupTest(t *testing.T, records []denco.Record, testcases []testcase) {
|
||||
r := denco.New()
|
||||
if err := r.Build(records); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
for _, testcase := range testcases {
|
||||
data, params, found := r.Lookup(testcase.path)
|
||||
if !reflect.DeepEqual(data, testcase.value) || !reflect.DeepEqual(params, denco.Params(testcase.params)) || !reflect.DeepEqual(found, testcase.found) {
|
||||
t.Errorf("Router.Lookup(%q) => (%#v, %#v, %#v), want (%#v, %#v, %#v)", testcase.path, data, params, found, testcase.value, denco.Params(testcase.params), testcase.found)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestRouter_Lookup(t *testing.T) {
|
||||
testcases := []testcase{
|
||||
{"/", "testroute0", nil, true},
|
||||
{"/gists/1323/foo/bar", "testroute11", []denco.Param{{"param1", "1323"}}, true},
|
||||
{"/gists/1323/foo/133", "testroute12", []denco.Param{{"param1", "1323"}, {"param2", "133"}}, true},
|
||||
{"/234/1323/foo/133", "testroute13", []denco.Param{{"param1", "234"}, {"param2", "1323"}, {"param3", "133"}}, true},
|
||||
{"/path/to/route", "testroute1", nil, true},
|
||||
{"/path/to/other", "testroute2", nil, true},
|
||||
{"/path/to/route/a", "testroute3", nil, true},
|
||||
{"/path/to/hoge", "testroute4", []denco.Param{{"param", "hoge"}}, true},
|
||||
{"/path/to/wildcard/some/params", "testroute5", []denco.Param{{"routepath", "some/params"}}, true},
|
||||
{"/path/to/o1/o2", "testroute6", []denco.Param{{"param1", "o1"}, {"param2", "o2"}}, true},
|
||||
{"/path/to/p1/sep/p2", "testroute7", []denco.Param{{"param1", "p1"}, {"param2", "p2"}}, true},
|
||||
{"/2014/01/06", "testroute8", []denco.Param{{"year", "2014"}, {"month", "01"}, {"day", "06"}}, true},
|
||||
{"/user/777", "testroute9", []denco.Param{{"id", "777"}}, true},
|
||||
{"/a/to/b/p1/some/wildcard/params", "testroute10", []denco.Param{{"param", "p1"}, {"routepath", "some/wildcard/params"}}, true},
|
||||
{"/missing", nil, nil, false},
|
||||
}
|
||||
runLookupTest(t, routes(), testcases)
|
||||
|
||||
records := []denco.Record{
|
||||
{"/", "testroute0"},
|
||||
{"/:b", "testroute1"},
|
||||
{"/*wildcard", "testroute2"},
|
||||
}
|
||||
testcases = []testcase{
|
||||
{"/", "testroute0", nil, true},
|
||||
{"/true", "testroute1", []denco.Param{{"b", "true"}}, true},
|
||||
{"/foo/bar", "testroute2", []denco.Param{{"wildcard", "foo/bar"}}, true},
|
||||
}
|
||||
runLookupTest(t, records, testcases)
|
||||
|
||||
records = []denco.Record{
|
||||
{"/networks/:owner/:repo/events", "testroute0"},
|
||||
{"/orgs/:org/events", "testroute1"},
|
||||
{"/notifications/threads/:id", "testroute2"},
|
||||
}
|
||||
testcases = []testcase{
|
||||
{"/networks/:owner/:repo/events", "testroute0", []denco.Param{{"owner", ":owner"}, {"repo", ":repo"}}, true},
|
||||
{"/orgs/:org/events", "testroute1", []denco.Param{{"org", ":org"}}, true},
|
||||
{"/notifications/threads/:id", "testroute2", []denco.Param{{"id", ":id"}}, true},
|
||||
}
|
||||
runLookupTest(t, records, testcases)
|
||||
|
||||
runLookupTest(t, []denco.Record{
|
||||
{"/", "route2"},
|
||||
}, []testcase{
|
||||
{"/user/alice", nil, nil, false},
|
||||
})
|
||||
|
||||
runLookupTest(t, []denco.Record{
|
||||
{"/user/:name", "route1"},
|
||||
}, []testcase{
|
||||
{"/", nil, nil, false},
|
||||
})
|
||||
|
||||
runLookupTest(t, []denco.Record{
|
||||
{"/*wildcard", "testroute0"},
|
||||
{"/a/:b", "testroute1"},
|
||||
}, []testcase{
|
||||
{"/a", "testroute0", []denco.Param{{"wildcard", "a"}}, true},
|
||||
})
|
||||
}
|
||||
|
||||
func TestRouter_Lookup_withManyRoutes(t *testing.T) {
|
||||
n := 1000
|
||||
rand.Seed(time.Now().UnixNano())
|
||||
records := make([]denco.Record, n)
|
||||
for i := 0; i < n; i++ {
|
||||
records[i] = denco.Record{Key: "/" + randomString(rand.Intn(50)+10), Value: fmt.Sprintf("route%d", i)}
|
||||
}
|
||||
router := denco.New()
|
||||
if err := router.Build(records); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
for _, r := range records {
|
||||
data, params, found := router.Lookup(r.Key)
|
||||
if !reflect.DeepEqual(data, r.Value) || len(params) != 0 || !reflect.DeepEqual(found, true) {
|
||||
t.Errorf("Router.Lookup(%q) => (%#v, %#v, %#v), want (%#v, %#v, %#v)", r.Key, data, len(params), found, r.Value, 0, true)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestRouter_Lookup_realURIs(t *testing.T) {
|
||||
testcases := []testcase{
|
||||
{"/authorizations", "/authorizations", nil, true},
|
||||
{"/authorizations/1", "/authorizations/:id", []denco.Param{{"id", "1"}}, true},
|
||||
{"/applications/1/tokens/zohRoo7e", "/applications/:client_id/tokens/:access_token", []denco.Param{{"client_id", "1"}, {"access_token", "zohRoo7e"}}, true},
|
||||
{"/events", "/events", nil, true},
|
||||
{"/repos/naoina/denco/events", "/repos/:owner/:repo/events", []denco.Param{{"owner", "naoina"}, {"repo", "denco"}}, true},
|
||||
{"/networks/naoina/denco/events", "/networks/:owner/:repo/events", []denco.Param{{"owner", "naoina"}, {"repo", "denco"}}, true},
|
||||
{"/orgs/something/events", "/orgs/:org/events", []denco.Param{{"org", "something"}}, true},
|
||||
{"/users/naoina/received_events", "/users/:user/received_events", []denco.Param{{"user", "naoina"}}, true},
|
||||
{"/users/naoina/received_events/public", "/users/:user/received_events/public", []denco.Param{{"user", "naoina"}}, true},
|
||||
{"/users/naoina/events", "/users/:user/events", []denco.Param{{"user", "naoina"}}, true},
|
||||
{"/users/naoina/events/public", "/users/:user/events/public", []denco.Param{{"user", "naoina"}}, true},
|
||||
{"/users/naoina/events/orgs/something", "/users/:user/events/orgs/:org", []denco.Param{{"user", "naoina"}, {"org", "something"}}, true},
|
||||
{"/feeds", "/feeds", nil, true},
|
||||
{"/notifications", "/notifications", nil, true},
|
||||
{"/repos/naoina/denco/notifications", "/repos/:owner/:repo/notifications", []denco.Param{{"owner", "naoina"}, {"repo", "denco"}}, true},
|
||||
{"/notifications/threads/1", "/notifications/threads/:id", []denco.Param{{"id", "1"}}, true},
|
||||
{"/notifications/threads/2/subscription", "/notifications/threads/:id/subscription", []denco.Param{{"id", "2"}}, true},
|
||||
{"/repos/naoina/denco/stargazers", "/repos/:owner/:repo/stargazers", []denco.Param{{"owner", "naoina"}, {"repo", "denco"}}, true},
|
||||
{"/users/naoina/starred", "/users/:user/starred", []denco.Param{{"user", "naoina"}}, true},
|
||||
{"/user/starred", "/user/starred", nil, true},
|
||||
{"/user/starred/naoina/denco", "/user/starred/:owner/:repo", []denco.Param{{"owner", "naoina"}, {"repo", "denco"}}, true},
|
||||
{"/repos/naoina/denco/subscribers", "/repos/:owner/:repo/subscribers", []denco.Param{{"owner", "naoina"}, {"repo", "denco"}}, true},
|
||||
{"/users/naoina/subscriptions", "/users/:user/subscriptions", []denco.Param{{"user", "naoina"}}, true},
|
||||
{"/user/subscriptions", "/user/subscriptions", nil, true},
|
||||
{"/repos/naoina/denco/subscription", "/repos/:owner/:repo/subscription", []denco.Param{{"owner", "naoina"}, {"repo", "denco"}}, true},
|
||||
{"/user/subscriptions/naoina/denco", "/user/subscriptions/:owner/:repo", []denco.Param{{"owner", "naoina"}, {"repo", "denco"}}, true},
|
||||
{"/users/naoina/gists", "/users/:user/gists", []denco.Param{{"user", "naoina"}}, true},
|
||||
{"/gists", "/gists", nil, true},
|
||||
{"/gists/1", "/gists/:id", []denco.Param{{"id", "1"}}, true},
|
||||
{"/gists/2/star", "/gists/:id/star", []denco.Param{{"id", "2"}}, true},
|
||||
{"/repos/naoina/denco/git/blobs/03c3bbc7f0d12268b9ca53d4fbfd8dc5ae5697b9", "/repos/:owner/:repo/git/blobs/:sha", []denco.Param{{"owner", "naoina"}, {"repo", "denco"}, {"sha", "03c3bbc7f0d12268b9ca53d4fbfd8dc5ae5697b9"}}, true},
|
||||
{"/repos/naoina/denco/git/commits/03c3bbc7f0d12268b9ca53d4fbfd8dc5ae5697b9", "/repos/:owner/:repo/git/commits/:sha", []denco.Param{{"owner", "naoina"}, {"repo", "denco"}, {"sha", "03c3bbc7f0d12268b9ca53d4fbfd8dc5ae5697b9"}}, true},
|
||||
{"/repos/naoina/denco/git/refs", "/repos/:owner/:repo/git/refs", []denco.Param{{"owner", "naoina"}, {"repo", "denco"}}, true},
|
||||
{"/repos/naoina/denco/git/tags/03c3bbc7f0d12268b9ca53d4fbfd8dc5ae5697b9", "/repos/:owner/:repo/git/tags/:sha", []denco.Param{{"owner", "naoina"}, {"repo", "denco"}, {"sha", "03c3bbc7f0d12268b9ca53d4fbfd8dc5ae5697b9"}}, true},
|
||||
{"/repos/naoina/denco/git/trees/03c3bbc7f0d12268b9ca53d4fbfd8dc5ae5697b9", "/repos/:owner/:repo/git/trees/:sha", []denco.Param{{"owner", "naoina"}, {"repo", "denco"}, {"sha", "03c3bbc7f0d12268b9ca53d4fbfd8dc5ae5697b9"}}, true},
|
||||
{"/issues", "/issues", nil, true},
|
||||
{"/user/issues", "/user/issues", nil, true},
|
||||
{"/orgs/something/issues", "/orgs/:org/issues", []denco.Param{{"org", "something"}}, true},
|
||||
{"/repos/naoina/denco/issues", "/repos/:owner/:repo/issues", []denco.Param{{"owner", "naoina"}, {"repo", "denco"}}, true},
|
||||
{"/repos/naoina/denco/issues/1", "/repos/:owner/:repo/issues/:number", []denco.Param{{"owner", "naoina"}, {"repo", "denco"}, {"number", "1"}}, true},
|
||||
{"/repos/naoina/denco/assignees", "/repos/:owner/:repo/assignees", []denco.Param{{"owner", "naoina"}, {"repo", "denco"}}, true},
|
||||
{"/repos/naoina/denco/assignees/foo", "/repos/:owner/:repo/assignees/:assignee", []denco.Param{{"owner", "naoina"}, {"repo", "denco"}, {"assignee", "foo"}}, true},
|
||||
{"/repos/naoina/denco/issues/1/comments", "/repos/:owner/:repo/issues/:number/comments", []denco.Param{{"owner", "naoina"}, {"repo", "denco"}, {"number", "1"}}, true},
|
||||
{"/repos/naoina/denco/issues/1/events", "/repos/:owner/:repo/issues/:number/events", []denco.Param{{"owner", "naoina"}, {"repo", "denco"}, {"number", "1"}}, true},
|
||||
{"/repos/naoina/denco/labels", "/repos/:owner/:repo/labels", []denco.Param{{"owner", "naoina"}, {"repo", "denco"}}, true},
|
||||
{"/repos/naoina/denco/labels/bug", "/repos/:owner/:repo/labels/:name", []denco.Param{{"owner", "naoina"}, {"repo", "denco"}, {"name", "bug"}}, true},
|
||||
{"/repos/naoina/denco/issues/1/labels", "/repos/:owner/:repo/issues/:number/labels", []denco.Param{{"owner", "naoina"}, {"repo", "denco"}, {"number", "1"}}, true},
|
||||
{"/repos/naoina/denco/milestones/1/labels", "/repos/:owner/:repo/milestones/:number/labels", []denco.Param{{"owner", "naoina"}, {"repo", "denco"}, {"number", "1"}}, true},
|
||||
{"/repos/naoina/denco/milestones", "/repos/:owner/:repo/milestones", []denco.Param{{"owner", "naoina"}, {"repo", "denco"}}, true},
|
||||
{"/repos/naoina/denco/milestones/1", "/repos/:owner/:repo/milestones/:number", []denco.Param{{"owner", "naoina"}, {"repo", "denco"}, {"number", "1"}}, true},
|
||||
{"/emojis", "/emojis", nil, true},
|
||||
{"/gitignore/templates", "/gitignore/templates", nil, true},
|
||||
{"/gitignore/templates/Go", "/gitignore/templates/:name", []denco.Param{{"name", "Go"}}, true},
|
||||
{"/meta", "/meta", nil, true},
|
||||
{"/rate_limit", "/rate_limit", nil, true},
|
||||
{"/users/naoina/orgs", "/users/:user/orgs", []denco.Param{{"user", "naoina"}}, true},
|
||||
{"/user/orgs", "/user/orgs", nil, true},
|
||||
{"/orgs/something", "/orgs/:org", []denco.Param{{"org", "something"}}, true},
|
||||
{"/orgs/something/members", "/orgs/:org/members", []denco.Param{{"org", "something"}}, true},
|
||||
{"/orgs/something/members/naoina", "/orgs/:org/members/:user", []denco.Param{{"org", "something"}, {"user", "naoina"}}, true},
|
||||
{"/orgs/something/public_members", "/orgs/:org/public_members", []denco.Param{{"org", "something"}}, true},
|
||||
{"/orgs/something/public_members/naoina", "/orgs/:org/public_members/:user", []denco.Param{{"org", "something"}, {"user", "naoina"}}, true},
|
||||
{"/orgs/something/teams", "/orgs/:org/teams", []denco.Param{{"org", "something"}}, true},
|
||||
{"/teams/1", "/teams/:id", []denco.Param{{"id", "1"}}, true},
|
||||
{"/teams/2/members", "/teams/:id/members", []denco.Param{{"id", "2"}}, true},
|
||||
{"/teams/3/members/naoina", "/teams/:id/members/:user", []denco.Param{{"id", "3"}, {"user", "naoina"}}, true},
|
||||
{"/teams/4/repos", "/teams/:id/repos", []denco.Param{{"id", "4"}}, true},
|
||||
{"/teams/5/repos/naoina/denco", "/teams/:id/repos/:owner/:repo", []denco.Param{{"id", "5"}, {"owner", "naoina"}, {"repo", "denco"}}, true},
|
||||
{"/user/teams", "/user/teams", nil, true},
|
||||
{"/repos/naoina/denco/pulls", "/repos/:owner/:repo/pulls", []denco.Param{{"owner", "naoina"}, {"repo", "denco"}}, true},
|
||||
{"/repos/naoina/denco/pulls/1", "/repos/:owner/:repo/pulls/:number", []denco.Param{{"owner", "naoina"}, {"repo", "denco"}, {"number", "1"}}, true},
|
||||
{"/repos/naoina/denco/pulls/1/commits", "/repos/:owner/:repo/pulls/:number/commits", []denco.Param{{"owner", "naoina"}, {"repo", "denco"}, {"number", "1"}}, true},
|
||||
{"/repos/naoina/denco/pulls/1/files", "/repos/:owner/:repo/pulls/:number/files", []denco.Param{{"owner", "naoina"}, {"repo", "denco"}, {"number", "1"}}, true},
|
||||
{"/repos/naoina/denco/pulls/1/merge", "/repos/:owner/:repo/pulls/:number/merge", []denco.Param{{"owner", "naoina"}, {"repo", "denco"}, {"number", "1"}}, true},
|
||||
{"/repos/naoina/denco/pulls/1/comments", "/repos/:owner/:repo/pulls/:number/comments", []denco.Param{{"owner", "naoina"}, {"repo", "denco"}, {"number", "1"}}, true},
|
||||
{"/user/repos", "/user/repos", nil, true},
|
||||
{"/users/naoina/repos", "/users/:user/repos", []denco.Param{{"user", "naoina"}}, true},
|
||||
{"/orgs/something/repos", "/orgs/:org/repos", []denco.Param{{"org", "something"}}, true},
|
||||
{"/repositories", "/repositories", nil, true},
|
||||
{"/repos/naoina/denco", "/repos/:owner/:repo", []denco.Param{{"owner", "naoina"}, {"repo", "denco"}}, true},
|
||||
{"/repos/naoina/denco/contributors", "/repos/:owner/:repo/contributors", []denco.Param{{"owner", "naoina"}, {"repo", "denco"}}, true},
|
||||
{"/repos/naoina/denco/languages", "/repos/:owner/:repo/languages", []denco.Param{{"owner", "naoina"}, {"repo", "denco"}}, true},
|
||||
{"/repos/naoina/denco/teams", "/repos/:owner/:repo/teams", []denco.Param{{"owner", "naoina"}, {"repo", "denco"}}, true},
|
||||
{"/repos/naoina/denco/tags", "/repos/:owner/:repo/tags", []denco.Param{{"owner", "naoina"}, {"repo", "denco"}}, true},
|
||||
{"/repos/naoina/denco/branches", "/repos/:owner/:repo/branches", []denco.Param{{"owner", "naoina"}, {"repo", "denco"}}, true},
|
||||
{"/repos/naoina/denco/branches/master", "/repos/:owner/:repo/branches/:branch", []denco.Param{{"owner", "naoina"}, {"repo", "denco"}, {"branch", "master"}}, true},
|
||||
{"/repos/naoina/denco/collaborators", "/repos/:owner/:repo/collaborators", []denco.Param{{"owner", "naoina"}, {"repo", "denco"}}, true},
|
||||
{"/repos/naoina/denco/collaborators/something", "/repos/:owner/:repo/collaborators/:user", []denco.Param{{"owner", "naoina"}, {"repo", "denco"}, {"user", "something"}}, true},
|
||||
{"/repos/naoina/denco/comments", "/repos/:owner/:repo/comments", []denco.Param{{"owner", "naoina"}, {"repo", "denco"}}, true},
|
||||
{"/repos/naoina/denco/commits/03c3bbc7f0d12268b9ca53d4fbfd8dc5ae5697b9/comments", "/repos/:owner/:repo/commits/:sha/comments", []denco.Param{{"owner", "naoina"}, {"repo", "denco"}, {"sha", "03c3bbc7f0d12268b9ca53d4fbfd8dc5ae5697b9"}}, true},
|
||||
{"/repos/naoina/denco/comments/1", "/repos/:owner/:repo/comments/:id", []denco.Param{{"owner", "naoina"}, {"repo", "denco"}, {"id", "1"}}, true},
|
||||
{"/repos/naoina/denco/commits", "/repos/:owner/:repo/commits", []denco.Param{{"owner", "naoina"}, {"repo", "denco"}}, true},
|
||||
{"/repos/naoina/denco/commits/03c3bbc7f0d12268b9ca53d4fbfd8dc5ae5697b9", "/repos/:owner/:repo/commits/:sha", []denco.Param{{"owner", "naoina"}, {"repo", "denco"}, {"sha", "03c3bbc7f0d12268b9ca53d4fbfd8dc5ae5697b9"}}, true},
|
||||
{"/repos/naoina/denco/readme", "/repos/:owner/:repo/readme", []denco.Param{{"owner", "naoina"}, {"repo", "denco"}}, true},
|
||||
{"/repos/naoina/denco/keys", "/repos/:owner/:repo/keys", []denco.Param{{"owner", "naoina"}, {"repo", "denco"}}, true},
|
||||
{"/repos/naoina/denco/keys/1", "/repos/:owner/:repo/keys/:id", []denco.Param{{"owner", "naoina"}, {"repo", "denco"}, {"id", "1"}}, true},
|
||||
{"/repos/naoina/denco/downloads", "/repos/:owner/:repo/downloads", []denco.Param{{"owner", "naoina"}, {"repo", "denco"}}, true},
|
||||
{"/repos/naoina/denco/downloads/2", "/repos/:owner/:repo/downloads/:id", []denco.Param{{"owner", "naoina"}, {"repo", "denco"}, {"id", "2"}}, true},
|
||||
{"/repos/naoina/denco/forks", "/repos/:owner/:repo/forks", []denco.Param{{"owner", "naoina"}, {"repo", "denco"}}, true},
|
||||
{"/repos/naoina/denco/hooks", "/repos/:owner/:repo/hooks", []denco.Param{{"owner", "naoina"}, {"repo", "denco"}}, true},
|
||||
{"/repos/naoina/denco/hooks/2", "/repos/:owner/:repo/hooks/:id", []denco.Param{{"owner", "naoina"}, {"repo", "denco"}, {"id", "2"}}, true},
|
||||
{"/repos/naoina/denco/releases", "/repos/:owner/:repo/releases", []denco.Param{{"owner", "naoina"}, {"repo", "denco"}}, true},
|
||||
{"/repos/naoina/denco/releases/1", "/repos/:owner/:repo/releases/:id", []denco.Param{{"owner", "naoina"}, {"repo", "denco"}, {"id", "1"}}, true},
|
||||
{"/repos/naoina/denco/releases/1/assets", "/repos/:owner/:repo/releases/:id/assets", []denco.Param{{"owner", "naoina"}, {"repo", "denco"}, {"id", "1"}}, true},
|
||||
{"/repos/naoina/denco/stats/contributors", "/repos/:owner/:repo/stats/contributors", []denco.Param{{"owner", "naoina"}, {"repo", "denco"}}, true},
|
||||
{"/repos/naoina/denco/stats/commit_activity", "/repos/:owner/:repo/stats/commit_activity", []denco.Param{{"owner", "naoina"}, {"repo", "denco"}}, true},
|
||||
{"/repos/naoina/denco/stats/code_frequency", "/repos/:owner/:repo/stats/code_frequency", []denco.Param{{"owner", "naoina"}, {"repo", "denco"}}, true},
|
||||
{"/repos/naoina/denco/stats/participation", "/repos/:owner/:repo/stats/participation", []denco.Param{{"owner", "naoina"}, {"repo", "denco"}}, true},
|
||||
{"/repos/naoina/denco/stats/punch_card", "/repos/:owner/:repo/stats/punch_card", []denco.Param{{"owner", "naoina"}, {"repo", "denco"}}, true},
|
||||
{"/repos/naoina/denco/statuses/master", "/repos/:owner/:repo/statuses/:ref", []denco.Param{{"owner", "naoina"}, {"repo", "denco"}, {"ref", "master"}}, true},
|
||||
{"/search/repositories", "/search/repositories", nil, true},
|
||||
{"/search/code", "/search/code", nil, true},
|
||||
{"/search/issues", "/search/issues", nil, true},
|
||||
{"/search/users", "/search/users", nil, true},
|
||||
{"/legacy/issues/search/naoina/denco/closed/test", "/legacy/issues/search/:owner/:repository/:state/:keyword", []denco.Param{{"owner", "naoina"}, {"repository", "denco"}, {"state", "closed"}, {"keyword", "test"}}, true},
|
||||
{"/legacy/repos/search/test", "/legacy/repos/search/:keyword", []denco.Param{{"keyword", "test"}}, true},
|
||||
{"/legacy/user/search/test", "/legacy/user/search/:keyword", []denco.Param{{"keyword", "test"}}, true},
|
||||
{"/legacy/user/email/naoina@kuune.org", "/legacy/user/email/:email", []denco.Param{{"email", "naoina@kuune.org"}}, true},
|
||||
{"/users/naoina", "/users/:user", []denco.Param{{"user", "naoina"}}, true},
|
||||
{"/user", "/user", nil, true},
|
||||
{"/users", "/users", nil, true},
|
||||
{"/user/emails", "/user/emails", nil, true},
|
||||
{"/users/naoina/followers", "/users/:user/followers", []denco.Param{{"user", "naoina"}}, true},
|
||||
{"/user/followers", "/user/followers", nil, true},
|
||||
{"/users/naoina/following", "/users/:user/following", []denco.Param{{"user", "naoina"}}, true},
|
||||
{"/user/following", "/user/following", nil, true},
|
||||
{"/user/following/naoina", "/user/following/:user", []denco.Param{{"user", "naoina"}}, true},
|
||||
{"/users/naoina/following/target", "/users/:user/following/:target_user", []denco.Param{{"user", "naoina"}, {"target_user", "target"}}, true},
|
||||
{"/users/naoina/keys", "/users/:user/keys", []denco.Param{{"user", "naoina"}}, true},
|
||||
{"/user/keys", "/user/keys", nil, true},
|
||||
{"/user/keys/1", "/user/keys/:id", []denco.Param{{"id", "1"}}, true},
|
||||
{"/people/me", "/people/:userId", []denco.Param{{"userId", "me"}}, true},
|
||||
{"/people", "/people", nil, true},
|
||||
{"/activities/foo/people/vault", "/activities/:activityId/people/:collection", []denco.Param{{"activityId", "foo"}, {"collection", "vault"}}, true},
|
||||
{"/people/me/people/vault", "/people/:userId/people/:collection", []denco.Param{{"userId", "me"}, {"collection", "vault"}}, true},
|
||||
{"/people/me/openIdConnect", "/people/:userId/openIdConnect", []denco.Param{{"userId", "me"}}, true},
|
||||
{"/people/me/activities/vault", "/people/:userId/activities/:collection", []denco.Param{{"userId", "me"}, {"collection", "vault"}}, true},
|
||||
{"/activities/foo", "/activities/:activityId", []denco.Param{{"activityId", "foo"}}, true},
|
||||
{"/activities", "/activities", nil, true},
|
||||
{"/activities/foo/comments", "/activities/:activityId/comments", []denco.Param{{"activityId", "foo"}}, true},
|
||||
{"/comments/hoge", "/comments/:commentId", []denco.Param{{"commentId", "hoge"}}, true},
|
||||
{"/people/me/moments/vault", "/people/:userId/moments/:collection", []denco.Param{{"userId", "me"}, {"collection", "vault"}}, true},
|
||||
}
|
||||
runLookupTest(t, realURIs, testcases)
|
||||
}
|
||||
|
||||
func TestRouter_Build(t *testing.T) {
|
||||
// test for duplicate name of path parameters.
|
||||
func() {
|
||||
r := denco.New()
|
||||
if err := r.Build([]denco.Record{
|
||||
{"/:user/:id/:id", "testroute0"},
|
||||
{"/:user/:user/:id", "testroute0"},
|
||||
}); err == nil {
|
||||
t.Errorf("no error returned by duplicate name of path parameters")
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
func TestRouter_Build_withoutSizeHint(t *testing.T) {
|
||||
for _, v := range []struct {
|
||||
keys []string
|
||||
sizeHint int
|
||||
}{
|
||||
{[]string{"/user"}, 0},
|
||||
{[]string{"/user/:id"}, 1},
|
||||
{[]string{"/user/:id/post"}, 1},
|
||||
{[]string{"/user/:id/:group"}, 2},
|
||||
{[]string{"/user/:id/post/:cid"}, 2},
|
||||
{[]string{"/user/:id/post/:cid", "/admin/:id/post/:cid"}, 2},
|
||||
{[]string{"/user/:id", "/admin/:id/post/:cid"}, 2},
|
||||
{[]string{"/user/:id/post/:cid", "/admin/:id/post/:cid/:type"}, 3},
|
||||
} {
|
||||
r := denco.New()
|
||||
actual := r.SizeHint
|
||||
expect := -1
|
||||
if !reflect.DeepEqual(actual, expect) {
|
||||
t.Errorf(`before Build; Router.SizeHint => (%[1]T=%#[1]v); want (%[2]T=%#[2]v)`, actual, expect)
|
||||
}
|
||||
records := make([]denco.Record, len(v.keys))
|
||||
for i, k := range v.keys {
|
||||
records[i] = denco.Record{Key: k, Value: "value"}
|
||||
}
|
||||
if err := r.Build(records); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
actual = r.SizeHint
|
||||
expect = v.sizeHint
|
||||
if !reflect.DeepEqual(actual, expect) {
|
||||
t.Errorf(`Router.Build(%#v); Router.SizeHint => (%[2]T=%#[2]v); want (%[3]T=%#[3]v)`, records, actual, expect)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestRouter_Build_withSizeHint(t *testing.T) {
|
||||
for _, v := range []struct {
|
||||
key string
|
||||
sizeHint int
|
||||
expect int
|
||||
}{
|
||||
{"/user", 0, 0},
|
||||
{"/user", 1, 1},
|
||||
{"/user", 2, 2},
|
||||
{"/user/:id", 3, 3},
|
||||
{"/user/:id/:group", 0, 0},
|
||||
{"/user/:id/:group", 1, 1},
|
||||
} {
|
||||
r := denco.New()
|
||||
r.SizeHint = v.sizeHint
|
||||
records := []denco.Record{
|
||||
{v.key, "value"},
|
||||
}
|
||||
if err := r.Build(records); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
actual := r.SizeHint
|
||||
expect := v.expect
|
||||
if !reflect.DeepEqual(actual, expect) {
|
||||
t.Errorf(`Router.Build(%#v); Router.SizeHint => (%[2]T=%#[2]v); want (%[3]T=%#[3]v)`, records, actual, expect)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestParams_Get(t *testing.T) {
|
||||
params := denco.Params([]denco.Param{
|
||||
{"name1", "value1"},
|
||||
{"name2", "value2"},
|
||||
{"name3", "value3"},
|
||||
{"name1", "value4"},
|
||||
})
|
||||
for _, v := range []struct{ value, expected string }{
|
||||
{"name1", "value1"},
|
||||
{"name2", "value2"},
|
||||
{"name3", "value3"},
|
||||
{"name4", ""},
|
||||
} {
|
||||
actual := params.Get(v.value)
|
||||
expected := v.expected
|
||||
if !reflect.DeepEqual(actual, expected) {
|
||||
t.Errorf("Params.Get(%q) => %#v, want %#v", v.value, actual, expected)
|
||||
}
|
||||
}
|
||||
}
|
||||
106
vendor/github.com/go-openapi/runtime/middleware/denco/server.go
generated
vendored
Executable file
106
vendor/github.com/go-openapi/runtime/middleware/denco/server.go
generated
vendored
Executable file
@@ -0,0 +1,106 @@
|
||||
package denco
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
)
|
||||
|
||||
// Mux represents a multiplexer for HTTP request.
|
||||
type Mux struct{}
|
||||
|
||||
// NewMux returns a new Mux.
|
||||
func NewMux() *Mux {
|
||||
return &Mux{}
|
||||
}
|
||||
|
||||
// GET is shorthand of Mux.Handler("GET", path, handler).
|
||||
func (m *Mux) GET(path string, handler HandlerFunc) Handler {
|
||||
return m.Handler("GET", path, handler)
|
||||
}
|
||||
|
||||
// POST is shorthand of Mux.Handler("POST", path, handler).
|
||||
func (m *Mux) POST(path string, handler HandlerFunc) Handler {
|
||||
return m.Handler("POST", path, handler)
|
||||
}
|
||||
|
||||
// PUT is shorthand of Mux.Handler("PUT", path, handler).
|
||||
func (m *Mux) PUT(path string, handler HandlerFunc) Handler {
|
||||
return m.Handler("PUT", path, handler)
|
||||
}
|
||||
|
||||
// HEAD is shorthand of Mux.Handler("HEAD", path, handler).
|
||||
func (m *Mux) HEAD(path string, handler HandlerFunc) Handler {
|
||||
return m.Handler("HEAD", path, handler)
|
||||
}
|
||||
|
||||
// Handler returns a handler for HTTP method.
|
||||
func (m *Mux) Handler(method, path string, handler HandlerFunc) Handler {
|
||||
return Handler{
|
||||
Method: method,
|
||||
Path: path,
|
||||
Func: handler,
|
||||
}
|
||||
}
|
||||
|
||||
// Build builds a http.Handler.
|
||||
func (m *Mux) Build(handlers []Handler) (http.Handler, error) {
|
||||
recordMap := make(map[string][]Record)
|
||||
for _, h := range handlers {
|
||||
recordMap[h.Method] = append(recordMap[h.Method], NewRecord(h.Path, h.Func))
|
||||
}
|
||||
mux := newServeMux()
|
||||
for m, records := range recordMap {
|
||||
router := New()
|
||||
if err := router.Build(records); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
mux.routers[m] = router
|
||||
}
|
||||
return mux, nil
|
||||
}
|
||||
|
||||
// Handler represents a handler of HTTP request.
|
||||
type Handler struct {
|
||||
// Method is an HTTP method.
|
||||
Method string
|
||||
|
||||
// Path is a routing path for handler.
|
||||
Path string
|
||||
|
||||
// Func is a function of handler of HTTP request.
|
||||
Func HandlerFunc
|
||||
}
|
||||
|
||||
// The HandlerFunc type is aliased to type of handler function.
|
||||
type HandlerFunc func(w http.ResponseWriter, r *http.Request, params Params)
|
||||
|
||||
type serveMux struct {
|
||||
routers map[string]*Router
|
||||
}
|
||||
|
||||
func newServeMux() *serveMux {
|
||||
return &serveMux{
|
||||
routers: make(map[string]*Router),
|
||||
}
|
||||
}
|
||||
|
||||
// ServeHTTP implements http.Handler interface.
|
||||
func (mux *serveMux) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
handler, params := mux.handler(r.Method, r.URL.Path)
|
||||
handler(w, r, params)
|
||||
}
|
||||
|
||||
func (mux *serveMux) handler(method, path string) (HandlerFunc, []Param) {
|
||||
if router, found := mux.routers[method]; found {
|
||||
if handler, params, found := router.Lookup(path); found {
|
||||
return handler.(HandlerFunc), params
|
||||
}
|
||||
}
|
||||
return NotFound, nil
|
||||
}
|
||||
|
||||
// NotFound replies to the request with an HTTP 404 not found error.
|
||||
// NotFound is called when unknown HTTP method or a handler not found.
|
||||
// If you want to use the your own NotFound handler, please overwrite this variable.
|
||||
var NotFound = func(w http.ResponseWriter, r *http.Request, _ Params) {
|
||||
http.NotFound(w, r)
|
||||
}
|
||||
106
vendor/github.com/go-openapi/runtime/middleware/denco/server_test.go
generated
vendored
Executable file
106
vendor/github.com/go-openapi/runtime/middleware/denco/server_test.go
generated
vendored
Executable file
@@ -0,0 +1,106 @@
|
||||
package denco_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
|
||||
"github.com/go-openapi/runtime/middleware/denco"
|
||||
)
|
||||
|
||||
func testHandlerFunc(w http.ResponseWriter, r *http.Request, params denco.Params) {
|
||||
fmt.Fprintf(w, "method: %s, path: %s, params: %v", r.Method, r.URL.Path, params)
|
||||
}
|
||||
|
||||
func TestMux(t *testing.T) {
|
||||
mux := denco.NewMux()
|
||||
handler, err := mux.Build([]denco.Handler{
|
||||
mux.GET("/", testHandlerFunc),
|
||||
mux.GET("/user/:name", testHandlerFunc),
|
||||
mux.POST("/user/:name", testHandlerFunc),
|
||||
mux.HEAD("/user/:name", testHandlerFunc),
|
||||
mux.PUT("/user/:name", testHandlerFunc),
|
||||
mux.Handler("GET", "/user/handler", testHandlerFunc),
|
||||
mux.Handler("POST", "/user/handler", testHandlerFunc),
|
||||
{"PUT", "/user/inference", testHandlerFunc},
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
server := httptest.NewServer(handler)
|
||||
defer server.Close()
|
||||
|
||||
for _, v := range []struct {
|
||||
status int
|
||||
method, path, expected string
|
||||
}{
|
||||
{200, "GET", "/", "method: GET, path: /, params: []"},
|
||||
{200, "GET", "/user/alice", "method: GET, path: /user/alice, params: [{name alice}]"},
|
||||
{200, "POST", "/user/bob", "method: POST, path: /user/bob, params: [{name bob}]"},
|
||||
{200, "HEAD", "/user/alice", ""},
|
||||
{200, "PUT", "/user/bob", "method: PUT, path: /user/bob, params: [{name bob}]"},
|
||||
{404, "POST", "/", "404 page not found\n"},
|
||||
{404, "GET", "/unknown", "404 page not found\n"},
|
||||
{404, "POST", "/user/alice/1", "404 page not found\n"},
|
||||
{200, "GET", "/user/handler", "method: GET, path: /user/handler, params: []"},
|
||||
{200, "POST", "/user/handler", "method: POST, path: /user/handler, params: []"},
|
||||
{200, "PUT", "/user/inference", "method: PUT, path: /user/inference, params: []"},
|
||||
} {
|
||||
req, err := http.NewRequest(v.method, server.URL+v.path, nil)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
continue
|
||||
}
|
||||
res, err := http.DefaultClient.Do(req)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
continue
|
||||
}
|
||||
defer res.Body.Close()
|
||||
body, err := ioutil.ReadAll(res.Body)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
continue
|
||||
}
|
||||
actual := string(body)
|
||||
expected := v.expected
|
||||
if res.StatusCode != v.status || actual != expected {
|
||||
t.Errorf(`%s "%s" => %#v %#v, want %#v %#v`, v.method, v.path, res.StatusCode, actual, v.status, expected)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestNotFound(t *testing.T) {
|
||||
mux := denco.NewMux()
|
||||
handler, err := mux.Build([]denco.Handler{})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
server := httptest.NewServer(handler)
|
||||
defer server.Close()
|
||||
|
||||
origNotFound := denco.NotFound
|
||||
defer func() {
|
||||
denco.NotFound = origNotFound
|
||||
}()
|
||||
denco.NotFound = func(w http.ResponseWriter, r *http.Request, params denco.Params) {
|
||||
w.WriteHeader(http.StatusServiceUnavailable)
|
||||
fmt.Fprintf(w, "method: %s, path: %s, params: %v", r.Method, r.URL.Path, params)
|
||||
}
|
||||
res, err := http.Get(server.URL)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer res.Body.Close()
|
||||
body, err := ioutil.ReadAll(res.Body)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
actual := string(body)
|
||||
expected := "method: GET, path: /, params: []"
|
||||
if res.StatusCode != http.StatusServiceUnavailable || actual != expected {
|
||||
t.Errorf(`GET "/" => %#v %#v, want %#v %#v`, res.StatusCode, actual, http.StatusServiceUnavailable, expected)
|
||||
}
|
||||
}
|
||||
12
vendor/github.com/go-openapi/runtime/middleware/denco/util.go
generated
vendored
Executable file
12
vendor/github.com/go-openapi/runtime/middleware/denco/util.go
generated
vendored
Executable file
@@ -0,0 +1,12 @@
|
||||
package denco
|
||||
|
||||
// NextSeparator returns an index of next separator in path.
|
||||
func NextSeparator(path string, start int) int {
|
||||
for start < len(path) {
|
||||
if c := path[start]; c == '/' || c == TerminationCharacter {
|
||||
break
|
||||
}
|
||||
start++
|
||||
}
|
||||
return start
|
||||
}
|
||||
31
vendor/github.com/go-openapi/runtime/middleware/denco/util_test.go
generated
vendored
Executable file
31
vendor/github.com/go-openapi/runtime/middleware/denco/util_test.go
generated
vendored
Executable file
@@ -0,0 +1,31 @@
|
||||
package denco_test
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/go-openapi/runtime/middleware/denco"
|
||||
)
|
||||
|
||||
func TestNextSeparator(t *testing.T) {
|
||||
for _, testcase := range []struct {
|
||||
path string
|
||||
start int
|
||||
expected interface{}
|
||||
}{
|
||||
{"/path/to/route", 0, 0},
|
||||
{"/path/to/route", 1, 5},
|
||||
{"/path/to/route", 9, 14},
|
||||
{"/path.html", 1, 10},
|
||||
{"/foo/bar.html", 1, 4},
|
||||
{"/foo/bar.html/baz.png", 5, 13},
|
||||
{"/foo/bar.html/baz.png", 14, 21},
|
||||
{"path#", 0, 4},
|
||||
} {
|
||||
actual := denco.NextSeparator(testcase.path, testcase.start)
|
||||
expected := testcase.expected
|
||||
if !reflect.DeepEqual(actual, expected) {
|
||||
t.Errorf("path = %q, start = %v expect %v, but %v", testcase.path, testcase.start, expected, actual)
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user