commit
58d9ae90c0
965 changed files with 578137 additions and 211 deletions
|
@ -82,68 +82,45 @@ docker rm -f dex_postgres
|
|||
|
||||
## Vendoring dependencies
|
||||
|
||||
dex uses [godep](https://github.com/tools/godep) for vendoring external dependencies. This section details how to add and update those dependencies.
|
||||
dex uses [glide](https://github.com/Masterminds/glide) for vendoring external dependencies. This section details how to add and update those dependencies.
|
||||
|
||||
Before continuing, please ensure you have the **latest version** of godep available in your PATH.
|
||||
Before continuing, please ensure you have the **latest version** of glide available in your PATH.
|
||||
|
||||
```
|
||||
go get -u github.com/tools/godep
|
||||
go get -u github.com/Masterminds/glide
|
||||
```
|
||||
|
||||
### Preparing your GOPATH
|
||||
|
||||
Godep assumes code uses the [standard Go directory layout](https://golang.org/doc/code.html#Organization) with the GOPATH environment variable. Developers who use a different workflow (for instance, prefer working from `~/src/dex`) should see [rkt's documentation](https://github.com/coreos/rkt/blob/master/Documentation/hacking.md#having-the-right-directory-layout-ie-gopath) for workarounds.
|
||||
|
||||
Godep determines depdencies using the GOPATH, not the vendored code in the Godeps directory. The first step is to "restore" your GOPATH to match the vendored state of dex. From dex's top level directory run:
|
||||
|
||||
```
|
||||
godep restore -v
|
||||
```
|
||||
|
||||
Next, continue to either *Adding a new package* or *Updating an existing package*.
|
||||
|
||||
### Adding a new package
|
||||
|
||||
After adding a new `import` to dex source, godep will automatically detect it and update the vendored code. Once code changes are finalized, bring the dependency into your GOPATH and save the state:
|
||||
After adding a new `import` to dex source, use `glide get` to add the dependency to the `glide.yaml` and `glide.lock` files.
|
||||
|
||||
```
|
||||
go get github.com/mavricknz/ldap # Replace with your dependency.
|
||||
godep save ./...
|
||||
glide get -u -v -s github.com/godbus/dbus
|
||||
```
|
||||
|
||||
Note that dex does **not** rewrite import paths like other CoreOS projects.
|
||||
Note that __all of these flags are manditory__. This should add an entry to the glide files, add the package to the `vendor` directory, and remove nested `vendor` directories and version control information.
|
||||
|
||||
## Updating an existing package
|
||||
|
||||
After restoring your GOPATH, update the dependency in your GOPATH to the version you wish to check in.
|
||||
To update an existing package, edit the `glide.yaml` file to the desired verison (most likely a git hash), and run `glide update`.
|
||||
|
||||
```
|
||||
cd $GOPATH/src/github.com/lib/pq # Replace with your dependency.
|
||||
git checkout origin master
|
||||
{{ edit the entry in glide.yaml }}
|
||||
glide update -u -v -s github.com/lib/pq
|
||||
```
|
||||
|
||||
Then, move to dex's top level directory and run:
|
||||
|
||||
```
|
||||
godep update github.com/lib/pq
|
||||
```
|
||||
|
||||
To update a group of packages, use the `...` notation.
|
||||
|
||||
```
|
||||
godep update github.com/coreos/go-oidc/...
|
||||
```
|
||||
Like `glide get` all flags are manditory. If the update was successful, `glide.lock` will have been updated to reflect the changes to `glide.yaml` and the package will have been updated in `vendor`.
|
||||
|
||||
## Finalizing your change
|
||||
|
||||
Use git to ensure the Godeps directory has updated only your target packages.
|
||||
Use git to ensure the `vendor` directory has updated only your target packages, and that no other entries in `glide.yaml` and `glide.lock` have changed.
|
||||
|
||||
Changes to the Godeps directory should be added as a separate commit from other changes for readability:
|
||||
|
||||
```
|
||||
git status # make sure things look reasonable
|
||||
git add Godeps
|
||||
git commit -m "Godeps: updated postgres driver"
|
||||
git add vendor
|
||||
git commit -m "vendor: updated postgres driver"
|
||||
|
||||
# continue working
|
||||
|
||||
|
|
165
Godeps/Godeps.json
generated
165
Godeps/Godeps.json
generated
|
@ -1,165 +0,0 @@
|
|||
{
|
||||
"ImportPath": "github.com/coreos/dex",
|
||||
"GoVersion": "go1.6",
|
||||
"Packages": [
|
||||
"./..."
|
||||
],
|
||||
"Deps": [
|
||||
{
|
||||
"ImportPath": "github.com/PuerkitoBio/goquery",
|
||||
"Comment": "v0.3.2-79-gb444041",
|
||||
"Rev": "b4440419d81240f8451a505b2f806c853bc2befc"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/andybalholm/cascadia",
|
||||
"Rev": "6122e68c2642b7b75c538a63b15168c6c80fb757"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/coreos/go-oidc/http",
|
||||
"Rev": "6039032c0b15517897116d333ead8edf38792437"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/coreos/go-oidc/jose",
|
||||
"Rev": "6039032c0b15517897116d333ead8edf38792437"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/coreos/go-oidc/key",
|
||||
"Rev": "6039032c0b15517897116d333ead8edf38792437"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/coreos/go-oidc/oauth2",
|
||||
"Rev": "6039032c0b15517897116d333ead8edf38792437"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/coreos/go-oidc/oidc",
|
||||
"Rev": "6039032c0b15517897116d333ead8edf38792437"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/coreos/pkg/capnslog",
|
||||
"Rev": "fa94270d4bac0d8ae5dc6b71894e251aada93f74"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/coreos/pkg/flagutil",
|
||||
"Rev": "fa94270d4bac0d8ae5dc6b71894e251aada93f74"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/coreos/pkg/health",
|
||||
"Rev": "fa94270d4bac0d8ae5dc6b71894e251aada93f74"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/coreos/pkg/httputil",
|
||||
"Rev": "fa94270d4bac0d8ae5dc6b71894e251aada93f74"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/coreos/pkg/timeutil",
|
||||
"Rev": "fa94270d4bac0d8ae5dc6b71894e251aada93f74"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/go-gorp/gorp",
|
||||
"Comment": "v1.7-138-gc44345f",
|
||||
"Rev": "c44345f52fc81d27c47e6b592dedaf8c28a972eb"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/gorilla/handlers",
|
||||
"Rev": "60c7bfde3e33c201519a200a4507a158cc03a17b"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/inconshreveable/mousetrap",
|
||||
"Rev": "76626ae9c91c4f2a10f34cad8ce83ea42c93bb75"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/jonboulle/clockwork",
|
||||
"Rev": "3f831b65b61282ba6bece21b91beea2edc4c887a"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/julienschmidt/httprouter",
|
||||
"Comment": "v1.1",
|
||||
"Rev": "8c199fb6259ffc1af525cc3ad52ee60ba8359669"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/kylelemons/godebug/diff",
|
||||
"Rev": "808ac284003ce2b08ef590da08f95379e8a06936"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/kylelemons/godebug/pretty",
|
||||
"Rev": "808ac284003ce2b08ef590da08f95379e8a06936"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/lib/pq",
|
||||
"Rev": "7175accbed18058468c07811f76440d6e8d7cf19"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/mailgun/mailgun-go",
|
||||
"Rev": "9578dc67692294bb7e2a6f4b15dd18c97af19440"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/mattn/go-sqlite3",
|
||||
"Comment": "v1.1.0-25-g2513631",
|
||||
"Rev": "2513631704612107a1c8b1803fb8e6b3dda2230e"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/mbanzon/simplehttp",
|
||||
"Rev": "04c542e7ac706a25820090f274ea6a4f39a63326"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/pborman/uuid",
|
||||
"Rev": "ca53cad383cad2479bbba7f7a1a05797ec1386e4"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/rubenv/sql-migrate",
|
||||
"Rev": "53184e1edfb4f9655b0fa8dd2c23e7763f452bda"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/spf13/cobra",
|
||||
"Rev": "b3f29e98e63a3618988a231fd02b45c1e6369d4f"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/spf13/pflag",
|
||||
"Rev": "7f60f83a2c81bc3c3c0d5297f61ddfa68da9d3b7"
|
||||
},
|
||||
{
|
||||
"ImportPath": "golang.org/x/crypto/bcrypt",
|
||||
"Rev": "1fbbd62cfec66bd39d91e97749579579d4d3037e"
|
||||
},
|
||||
{
|
||||
"ImportPath": "golang.org/x/crypto/blowfish",
|
||||
"Rev": "1fbbd62cfec66bd39d91e97749579579d4d3037e"
|
||||
},
|
||||
{
|
||||
"ImportPath": "golang.org/x/net/html",
|
||||
"Rev": "dfe268fd2bb5c793f4c083803609fce9806c6f80"
|
||||
},
|
||||
{
|
||||
"ImportPath": "google.golang.org/api/google-api-go-generator",
|
||||
"Rev": "d3edb0282bde692467788c50070a9211afe75cf3"
|
||||
},
|
||||
{
|
||||
"ImportPath": "google.golang.org/api/googleapi",
|
||||
"Rev": "d3edb0282bde692467788c50070a9211afe75cf3"
|
||||
},
|
||||
{
|
||||
"ImportPath": "gopkg.in/alexcesaro/quotedprintable.v3",
|
||||
"Rev": "2caba252f4dc53eaf6b553000885530023f54623"
|
||||
},
|
||||
{
|
||||
"ImportPath": "gopkg.in/asn1-ber.v1",
|
||||
"Comment": "v1.1",
|
||||
"Rev": "4e86f4367175e39f69d9358a5f17b4dda270378d"
|
||||
},
|
||||
{
|
||||
"ImportPath": "gopkg.in/gomail.v2",
|
||||
"Comment": "2.0.0-2-gb1e5552",
|
||||
"Rev": "b1e55520bf557d8a614f1e1f493ce892c1b5e97e"
|
||||
},
|
||||
{
|
||||
"ImportPath": "gopkg.in/gorp.v1",
|
||||
"Comment": "v1.7.1",
|
||||
"Rev": "c87af80f3cc5036b55b83d77171e156791085e2e"
|
||||
},
|
||||
{
|
||||
"ImportPath": "gopkg.in/ldap.v2",
|
||||
"Comment": "v2.2",
|
||||
"Rev": "e9a325d64989e2844be629682cb085d2c58eef8d"
|
||||
}
|
||||
]
|
||||
}
|
5
Godeps/Readme
generated
5
Godeps/Readme
generated
|
@ -1,5 +0,0 @@
|
|||
This directory tree is generated automatically by godep.
|
||||
|
||||
Please do not edit.
|
||||
|
||||
See https://github.com/tools/godep for more information.
|
85
glide.lock
generated
Normal file
85
glide.lock
generated
Normal file
|
@ -0,0 +1,85 @@
|
|||
hash: b0a1c1b400f077dac572450cb9602aabba62d9b7c6fc8ef426c2093de7621e8c
|
||||
updated: 2016-04-08T11:43:54.191409294-07:00
|
||||
imports:
|
||||
- name: github.com/andybalholm/cascadia
|
||||
version: 6122e68c2642b7b75c538a63b15168c6c80fb757
|
||||
- name: github.com/coreos/go-oidc
|
||||
version: 6039032c0b15517897116d333ead8edf38792437
|
||||
subpackages:
|
||||
- http
|
||||
- jose
|
||||
- key
|
||||
- oauth2
|
||||
- oidc
|
||||
- name: github.com/coreos/pkg
|
||||
version: fa94270d4bac0d8ae5dc6b71894e251aada93f74
|
||||
subpackages:
|
||||
- capnslog
|
||||
- flagutil
|
||||
- health
|
||||
- httputil
|
||||
- timeutil
|
||||
- name: github.com/go-gorp/gorp
|
||||
version: c44345f52fc81d27c47e6b592dedaf8c28a972eb
|
||||
- name: github.com/gorilla/handlers
|
||||
version: 60c7bfde3e33c201519a200a4507a158cc03a17b
|
||||
- name: github.com/inconshreveable/mousetrap
|
||||
version: 76626ae9c91c4f2a10f34cad8ce83ea42c93bb75
|
||||
- name: github.com/jonboulle/clockwork
|
||||
version: 3f831b65b61282ba6bece21b91beea2edc4c887a
|
||||
- name: github.com/julienschmidt/httprouter
|
||||
version: 8c199fb6259ffc1af525cc3ad52ee60ba8359669
|
||||
- name: github.com/kylelemons/godebug
|
||||
version: 808ac284003ce2b08ef590da08f95379e8a06936
|
||||
subpackages:
|
||||
- diff
|
||||
- pretty
|
||||
- name: github.com/lib/pq
|
||||
version: 7175accbed18058468c07811f76440d6e8d7cf19
|
||||
subpackages:
|
||||
- oid
|
||||
- name: github.com/mailgun/mailgun-go
|
||||
version: 9578dc67692294bb7e2a6f4b15dd18c97af19440
|
||||
- name: github.com/mattn/go-sqlite3
|
||||
version: 2513631704612107a1c8b1803fb8e6b3dda2230e
|
||||
- name: github.com/mbanzon/simplehttp
|
||||
version: 04c542e7ac706a25820090f274ea6a4f39a63326
|
||||
- name: github.com/pborman/uuid
|
||||
version: ca53cad383cad2479bbba7f7a1a05797ec1386e4
|
||||
- name: github.com/PuerkitoBio/goquery
|
||||
version: b4440419d81240f8451a505b2f806c853bc2befc
|
||||
- name: github.com/rubenv/sql-migrate
|
||||
version: 53184e1edfb4f9655b0fa8dd2c23e7763f452bda
|
||||
subpackages:
|
||||
- sqlparse
|
||||
- name: github.com/spf13/cobra
|
||||
version: b3f29e98e63a3618988a231fd02b45c1e6369d4f
|
||||
- name: github.com/spf13/pflag
|
||||
version: 7f60f83a2c81bc3c3c0d5297f61ddfa68da9d3b7
|
||||
- name: golang.org/x/crypto
|
||||
version: 1fbbd62cfec66bd39d91e97749579579d4d3037e
|
||||
subpackages:
|
||||
- bcrypt
|
||||
- blowfish
|
||||
- name: golang.org/x/net
|
||||
version: dfe268fd2bb5c793f4c083803609fce9806c6f80
|
||||
subpackages:
|
||||
- html
|
||||
- html/atom
|
||||
- name: google.golang.org/api
|
||||
version: d3edb0282bde692467788c50070a9211afe75cf3
|
||||
subpackages:
|
||||
- google-api-go-generator
|
||||
- googleapi
|
||||
- googleapi/internal/uritemplates
|
||||
- name: gopkg.in/alexcesaro/quotedprintable.v3
|
||||
version: 2caba252f4dc53eaf6b553000885530023f54623
|
||||
- name: gopkg.in/asn1-ber.v1
|
||||
version: 4e86f4367175e39f69d9358a5f17b4dda270378d
|
||||
- name: gopkg.in/gomail.v2
|
||||
version: b1e55520bf557d8a614f1e1f493ce892c1b5e97e
|
||||
- name: gopkg.in/gorp.v1
|
||||
version: c87af80f3cc5036b55b83d77171e156791085e2e
|
||||
- name: gopkg.in/ldap.v2
|
||||
version: e9a325d64989e2844be629682cb085d2c58eef8d
|
||||
devImports: []
|
77
glide.yaml
Normal file
77
glide.yaml
Normal file
|
@ -0,0 +1,77 @@
|
|||
package: github.com/coreos/dex
|
||||
import:
|
||||
- package: github.com/PuerkitoBio/goquery
|
||||
version: b4440419d81240f8451a505b2f806c853bc2befc
|
||||
- package: github.com/andybalholm/cascadia
|
||||
version: 6122e68c2642b7b75c538a63b15168c6c80fb757
|
||||
- package: github.com/coreos/go-oidc
|
||||
version: 6039032c0b15517897116d333ead8edf38792437
|
||||
subpackages:
|
||||
- http
|
||||
- jose
|
||||
- key
|
||||
- oauth2
|
||||
- oidc
|
||||
- package: github.com/coreos/pkg
|
||||
version: fa94270d4bac0d8ae5dc6b71894e251aada93f74
|
||||
subpackages:
|
||||
- capnslog
|
||||
- flagutil
|
||||
- health
|
||||
- httputil
|
||||
- timeutil
|
||||
- package: github.com/go-gorp/gorp
|
||||
version: c44345f52fc81d27c47e6b592dedaf8c28a972eb
|
||||
- package: github.com/gorilla/handlers
|
||||
version: 60c7bfde3e33c201519a200a4507a158cc03a17b
|
||||
- package: github.com/inconshreveable/mousetrap
|
||||
version: 76626ae9c91c4f2a10f34cad8ce83ea42c93bb75
|
||||
- package: github.com/jonboulle/clockwork
|
||||
version: 3f831b65b61282ba6bece21b91beea2edc4c887a
|
||||
- package: github.com/julienschmidt/httprouter
|
||||
version: 8c199fb6259ffc1af525cc3ad52ee60ba8359669
|
||||
- package: github.com/kylelemons/godebug
|
||||
version: 808ac284003ce2b08ef590da08f95379e8a06936
|
||||
subpackages:
|
||||
- diff
|
||||
- pretty
|
||||
- package: github.com/lib/pq
|
||||
version: 7175accbed18058468c07811f76440d6e8d7cf19
|
||||
- package: github.com/mailgun/mailgun-go
|
||||
version: 9578dc67692294bb7e2a6f4b15dd18c97af19440
|
||||
- package: github.com/mattn/go-sqlite3
|
||||
version: 2513631704612107a1c8b1803fb8e6b3dda2230e
|
||||
- package: github.com/mbanzon/simplehttp
|
||||
version: 04c542e7ac706a25820090f274ea6a4f39a63326
|
||||
- package: github.com/pborman/uuid
|
||||
version: ca53cad383cad2479bbba7f7a1a05797ec1386e4
|
||||
- package: github.com/rubenv/sql-migrate
|
||||
version: 53184e1edfb4f9655b0fa8dd2c23e7763f452bda
|
||||
- package: github.com/spf13/cobra
|
||||
version: b3f29e98e63a3618988a231fd02b45c1e6369d4f
|
||||
- package: github.com/spf13/pflag
|
||||
version: 7f60f83a2c81bc3c3c0d5297f61ddfa68da9d3b7
|
||||
- package: golang.org/x/crypto
|
||||
version: 1fbbd62cfec66bd39d91e97749579579d4d3037e
|
||||
subpackages:
|
||||
- bcrypt
|
||||
- blowfish
|
||||
- package: golang.org/x/net
|
||||
version: dfe268fd2bb5c793f4c083803609fce9806c6f80
|
||||
subpackages:
|
||||
- html
|
||||
- package: google.golang.org/api
|
||||
version: d3edb0282bde692467788c50070a9211afe75cf3
|
||||
subpackages:
|
||||
- google-api-go-generator
|
||||
- googleapi
|
||||
- package: gopkg.in/alexcesaro/quotedprintable.v3
|
||||
version: 2caba252f4dc53eaf6b553000885530023f54623
|
||||
- package: gopkg.in/asn1-ber.v1
|
||||
version: 4e86f4367175e39f69d9358a5f17b4dda270378d
|
||||
- package: gopkg.in/gomail.v2
|
||||
version: b1e55520bf557d8a614f1e1f493ce892c1b5e97e
|
||||
- package: gopkg.in/gorp.v1
|
||||
version: c87af80f3cc5036b55b83d77171e156791085e2e
|
||||
- package: gopkg.in/ldap.v2
|
||||
version: e9a325d64989e2844be629682cb085d2c58eef8d
|
180
vendor/github.com/PuerkitoBio/goquery/array_test.go
generated
vendored
Normal file
180
vendor/github.com/PuerkitoBio/goquery/array_test.go
generated
vendored
Normal file
|
@ -0,0 +1,180 @@
|
|||
package goquery
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestFirst(t *testing.T) {
|
||||
sel := Doc().Find(".pvk-content").First()
|
||||
assertLength(t, sel.Nodes, 1)
|
||||
}
|
||||
|
||||
func TestFirstEmpty(t *testing.T) {
|
||||
sel := Doc().Find(".pvk-zzcontentzz").First()
|
||||
assertLength(t, sel.Nodes, 0)
|
||||
}
|
||||
|
||||
func TestFirstRollback(t *testing.T) {
|
||||
sel := Doc().Find(".pvk-content")
|
||||
sel2 := sel.First().End()
|
||||
assertEqual(t, sel, sel2)
|
||||
}
|
||||
|
||||
func TestLast(t *testing.T) {
|
||||
sel := Doc().Find(".pvk-content").Last()
|
||||
assertLength(t, sel.Nodes, 1)
|
||||
|
||||
// Should contain Footer
|
||||
foot := Doc().Find(".footer")
|
||||
if !sel.Contains(foot.Nodes[0]) {
|
||||
t.Error("Last .pvk-content should contain .footer.")
|
||||
}
|
||||
}
|
||||
|
||||
func TestLastEmpty(t *testing.T) {
|
||||
sel := Doc().Find(".pvk-zzcontentzz").Last()
|
||||
assertLength(t, sel.Nodes, 0)
|
||||
}
|
||||
|
||||
func TestLastRollback(t *testing.T) {
|
||||
sel := Doc().Find(".pvk-content")
|
||||
sel2 := sel.Last().End()
|
||||
assertEqual(t, sel, sel2)
|
||||
}
|
||||
|
||||
func TestEq(t *testing.T) {
|
||||
sel := Doc().Find(".pvk-content").Eq(1)
|
||||
assertLength(t, sel.Nodes, 1)
|
||||
}
|
||||
|
||||
func TestEqNegative(t *testing.T) {
|
||||
sel := Doc().Find(".pvk-content").Eq(-1)
|
||||
assertLength(t, sel.Nodes, 1)
|
||||
|
||||
// Should contain Footer
|
||||
foot := Doc().Find(".footer")
|
||||
if !sel.Contains(foot.Nodes[0]) {
|
||||
t.Error("Index -1 of .pvk-content should contain .footer.")
|
||||
}
|
||||
}
|
||||
|
||||
func TestEqEmpty(t *testing.T) {
|
||||
sel := Doc().Find("something_random_that_does_not_exists").Eq(0)
|
||||
assertLength(t, sel.Nodes, 0)
|
||||
}
|
||||
|
||||
func TestEqInvalidPositive(t *testing.T) {
|
||||
sel := Doc().Find(".pvk-content").Eq(3)
|
||||
assertLength(t, sel.Nodes, 0)
|
||||
}
|
||||
|
||||
func TestEqInvalidNegative(t *testing.T) {
|
||||
sel := Doc().Find(".pvk-content").Eq(-4)
|
||||
assertLength(t, sel.Nodes, 0)
|
||||
}
|
||||
|
||||
func TestEqRollback(t *testing.T) {
|
||||
sel := Doc().Find(".pvk-content")
|
||||
sel2 := sel.Eq(1).End()
|
||||
assertEqual(t, sel, sel2)
|
||||
}
|
||||
|
||||
func TestSlice(t *testing.T) {
|
||||
sel := Doc().Find(".pvk-content").Slice(0, 2)
|
||||
|
||||
assertLength(t, sel.Nodes, 2)
|
||||
}
|
||||
|
||||
func TestSliceOutOfBounds(t *testing.T) {
|
||||
defer assertPanic(t)
|
||||
Doc().Find(".pvk-content").Slice(2, 12)
|
||||
}
|
||||
|
||||
func TestNegativeSliceStart(t *testing.T) {
|
||||
sel := Doc().Find(".container-fluid").Slice(-2, 3)
|
||||
assertLength(t, sel.Nodes, 1)
|
||||
assertSelectionIs(t, sel.Eq(0), "#cf3")
|
||||
}
|
||||
|
||||
func TestNegativeSliceEnd(t *testing.T) {
|
||||
sel := Doc().Find(".container-fluid").Slice(1, -1)
|
||||
assertLength(t, sel.Nodes, 2)
|
||||
assertSelectionIs(t, sel.Eq(0), "#cf2")
|
||||
assertSelectionIs(t, sel.Eq(1), "#cf3")
|
||||
}
|
||||
|
||||
func TestNegativeSliceBoth(t *testing.T) {
|
||||
sel := Doc().Find(".container-fluid").Slice(-3, -1)
|
||||
assertLength(t, sel.Nodes, 2)
|
||||
assertSelectionIs(t, sel.Eq(0), "#cf2")
|
||||
assertSelectionIs(t, sel.Eq(1), "#cf3")
|
||||
}
|
||||
|
||||
func TestNegativeSliceOutOfBounds(t *testing.T) {
|
||||
defer assertPanic(t)
|
||||
Doc().Find(".container-fluid").Slice(-12, -7)
|
||||
}
|
||||
|
||||
func TestSliceRollback(t *testing.T) {
|
||||
sel := Doc().Find(".pvk-content")
|
||||
sel2 := sel.Slice(0, 2).End()
|
||||
assertEqual(t, sel, sel2)
|
||||
}
|
||||
|
||||
func TestGet(t *testing.T) {
|
||||
sel := Doc().Find(".pvk-content")
|
||||
node := sel.Get(1)
|
||||
if sel.Nodes[1] != node {
|
||||
t.Errorf("Expected node %v to be %v.", node, sel.Nodes[1])
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetNegative(t *testing.T) {
|
||||
sel := Doc().Find(".pvk-content")
|
||||
node := sel.Get(-3)
|
||||
if sel.Nodes[0] != node {
|
||||
t.Errorf("Expected node %v to be %v.", node, sel.Nodes[0])
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetInvalid(t *testing.T) {
|
||||
defer assertPanic(t)
|
||||
sel := Doc().Find(".pvk-content")
|
||||
sel.Get(129)
|
||||
}
|
||||
|
||||
func TestIndex(t *testing.T) {
|
||||
sel := Doc().Find(".pvk-content")
|
||||
if i := sel.Index(); i != 1 {
|
||||
t.Errorf("Expected index of 1, got %v.", i)
|
||||
}
|
||||
}
|
||||
|
||||
func TestIndexSelector(t *testing.T) {
|
||||
sel := Doc().Find(".hero-unit")
|
||||
if i := sel.IndexSelector("div"); i != 4 {
|
||||
t.Errorf("Expected index of 4, got %v.", i)
|
||||
}
|
||||
}
|
||||
|
||||
func TestIndexOfNode(t *testing.T) {
|
||||
sel := Doc().Find("div.pvk-gutter")
|
||||
if i := sel.IndexOfNode(sel.Nodes[1]); i != 1 {
|
||||
t.Errorf("Expected index of 1, got %v.", i)
|
||||
}
|
||||
}
|
||||
|
||||
func TestIndexOfNilNode(t *testing.T) {
|
||||
sel := Doc().Find("div.pvk-gutter")
|
||||
if i := sel.IndexOfNode(nil); i != -1 {
|
||||
t.Errorf("Expected index of -1, got %v.", i)
|
||||
}
|
||||
}
|
||||
|
||||
func TestIndexOfSelection(t *testing.T) {
|
||||
sel := Doc().Find("div")
|
||||
sel2 := Doc().Find(".hero-unit")
|
||||
if i := sel.IndexOfSelection(sel2); i != 4 {
|
||||
t.Errorf("Expected index of 4, got %v.", i)
|
||||
}
|
||||
}
|
112
vendor/github.com/PuerkitoBio/goquery/bench_array_test.go
generated
vendored
Normal file
112
vendor/github.com/PuerkitoBio/goquery/bench_array_test.go
generated
vendored
Normal file
|
@ -0,0 +1,112 @@
|
|||
package goquery
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func BenchmarkFirst(b *testing.B) {
|
||||
b.StopTimer()
|
||||
sel := DocB().Find("dd")
|
||||
b.StartTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
sel.First()
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkLast(b *testing.B) {
|
||||
b.StopTimer()
|
||||
sel := DocB().Find("dd")
|
||||
b.StartTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
sel.Last()
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkEq(b *testing.B) {
|
||||
b.StopTimer()
|
||||
sel := DocB().Find("dd")
|
||||
j := 0
|
||||
b.StartTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
sel.Eq(j)
|
||||
if j++; j >= sel.Length() {
|
||||
j = 0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkSlice(b *testing.B) {
|
||||
b.StopTimer()
|
||||
sel := DocB().Find("dd")
|
||||
j := 0
|
||||
b.StartTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
sel.Slice(j, j+4)
|
||||
if j++; j >= (sel.Length() - 4) {
|
||||
j = 0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkGet(b *testing.B) {
|
||||
b.StopTimer()
|
||||
sel := DocB().Find("dd")
|
||||
j := 0
|
||||
b.StartTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
sel.Get(j)
|
||||
if j++; j >= sel.Length() {
|
||||
j = 0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkIndex(b *testing.B) {
|
||||
var j int
|
||||
|
||||
b.StopTimer()
|
||||
sel := DocB().Find("#Main")
|
||||
b.StartTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
j = sel.Index()
|
||||
}
|
||||
b.Logf("Index=%d", j)
|
||||
}
|
||||
|
||||
func BenchmarkIndexSelector(b *testing.B) {
|
||||
var j int
|
||||
|
||||
b.StopTimer()
|
||||
sel := DocB().Find("#manual-nav dl dd:nth-child(1)")
|
||||
b.StartTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
j = sel.IndexSelector("dd")
|
||||
}
|
||||
b.Logf("IndexSelector=%d", j)
|
||||
}
|
||||
|
||||
func BenchmarkIndexOfNode(b *testing.B) {
|
||||
var j int
|
||||
|
||||
b.StopTimer()
|
||||
sel := DocB().Find("span a")
|
||||
sel2 := DocB().Find("span a:nth-child(3)")
|
||||
n := sel2.Get(0)
|
||||
b.StartTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
j = sel.IndexOfNode(n)
|
||||
}
|
||||
b.Logf("IndexOfNode=%d", j)
|
||||
}
|
||||
|
||||
func BenchmarkIndexOfSelection(b *testing.B) {
|
||||
var j int
|
||||
b.StopTimer()
|
||||
sel := DocB().Find("span a")
|
||||
sel2 := DocB().Find("span a:nth-child(3)")
|
||||
b.StartTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
j = sel.IndexOfSelection(sel2)
|
||||
}
|
||||
b.Logf("IndexOfSelection=%d", j)
|
||||
}
|
42
vendor/github.com/PuerkitoBio/goquery/bench_example_test.go
generated
vendored
Normal file
42
vendor/github.com/PuerkitoBio/goquery/bench_example_test.go
generated
vendored
Normal file
|
@ -0,0 +1,42 @@
|
|||
package goquery
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func BenchmarkMetalReviewExample(b *testing.B) {
|
||||
var n int
|
||||
var buf bytes.Buffer
|
||||
|
||||
b.StopTimer()
|
||||
doc := loadDoc("metalreview.html")
|
||||
b.StartTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
doc.Find(".slider-row:nth-child(1) .slider-item").Each(func(i int, s *Selection) {
|
||||
var band, title string
|
||||
var score float64
|
||||
var e error
|
||||
|
||||
n++
|
||||
// For each item found, get the band, title and score, and print it
|
||||
band = s.Find("strong").Text()
|
||||
title = s.Find("em").Text()
|
||||
if score, e = strconv.ParseFloat(s.Find(".score").Text(), 64); e != nil {
|
||||
// Not a valid float, ignore score
|
||||
if n <= 4 {
|
||||
buf.WriteString(fmt.Sprintf("Review %d: %s - %s.\n", i, band, title))
|
||||
}
|
||||
} else {
|
||||
// Print all, including score
|
||||
if n <= 4 {
|
||||
buf.WriteString(fmt.Sprintf("Review %d: %s - %s (%2.1f).\n", i, band, title, score))
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
b.Log(buf.String())
|
||||
b.Logf("MetalReviewExample=%d", n)
|
||||
}
|
72
vendor/github.com/PuerkitoBio/goquery/bench_expand_test.go
generated
vendored
Normal file
72
vendor/github.com/PuerkitoBio/goquery/bench_expand_test.go
generated
vendored
Normal file
|
@ -0,0 +1,72 @@
|
|||
package goquery
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func BenchmarkAdd(b *testing.B) {
|
||||
var n int
|
||||
|
||||
b.StopTimer()
|
||||
sel := DocB().Find("dd")
|
||||
b.StartTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
if n == 0 {
|
||||
n = sel.Add("h2[title]").Length()
|
||||
} else {
|
||||
sel.Add("h2[title]")
|
||||
}
|
||||
}
|
||||
b.Logf("Add=%d", n)
|
||||
}
|
||||
|
||||
func BenchmarkAddSelection(b *testing.B) {
|
||||
var n int
|
||||
|
||||
b.StopTimer()
|
||||
sel := DocB().Find("dd")
|
||||
sel2 := DocB().Find("h2[title]")
|
||||
b.StartTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
if n == 0 {
|
||||
n = sel.AddSelection(sel2).Length()
|
||||
} else {
|
||||
sel.AddSelection(sel2)
|
||||
}
|
||||
}
|
||||
b.Logf("AddSelection=%d", n)
|
||||
}
|
||||
|
||||
func BenchmarkAddNodes(b *testing.B) {
|
||||
var n int
|
||||
|
||||
b.StopTimer()
|
||||
sel := DocB().Find("dd")
|
||||
sel2 := DocB().Find("h2[title]")
|
||||
nodes := sel2.Nodes
|
||||
b.StartTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
if n == 0 {
|
||||
n = sel.AddNodes(nodes...).Length()
|
||||
} else {
|
||||
sel.AddNodes(nodes...)
|
||||
}
|
||||
}
|
||||
b.Logf("AddNodes=%d", n)
|
||||
}
|
||||
|
||||
func BenchmarkAndSelf(b *testing.B) {
|
||||
var n int
|
||||
|
||||
b.StopTimer()
|
||||
sel := DocB().Find("dd").Parent()
|
||||
b.StartTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
if n == 0 {
|
||||
n = sel.AndSelf().Length()
|
||||
} else {
|
||||
sel.AndSelf()
|
||||
}
|
||||
}
|
||||
b.Logf("AndSelf=%d", n)
|
||||
}
|
212
vendor/github.com/PuerkitoBio/goquery/bench_filter_test.go
generated
vendored
Normal file
212
vendor/github.com/PuerkitoBio/goquery/bench_filter_test.go
generated
vendored
Normal file
|
@ -0,0 +1,212 @@
|
|||
package goquery
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func BenchmarkFilter(b *testing.B) {
|
||||
var n int
|
||||
|
||||
b.StopTimer()
|
||||
sel := DocW().Find("li")
|
||||
b.StartTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
if n == 0 {
|
||||
n = sel.Filter(".toclevel-1").Length()
|
||||
} else {
|
||||
sel.Filter(".toclevel-1")
|
||||
}
|
||||
}
|
||||
b.Logf("Filter=%d", n)
|
||||
}
|
||||
|
||||
func BenchmarkNot(b *testing.B) {
|
||||
var n int
|
||||
|
||||
b.StopTimer()
|
||||
sel := DocW().Find("li")
|
||||
b.StartTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
if n == 0 {
|
||||
n = sel.Not(".toclevel-2").Length()
|
||||
} else {
|
||||
sel.Filter(".toclevel-2")
|
||||
}
|
||||
}
|
||||
b.Logf("Not=%d", n)
|
||||
}
|
||||
|
||||
func BenchmarkFilterFunction(b *testing.B) {
|
||||
var n int
|
||||
|
||||
b.StopTimer()
|
||||
sel := DocW().Find("li")
|
||||
f := func(i int, s *Selection) bool {
|
||||
return len(s.Get(0).Attr) > 0
|
||||
}
|
||||
b.StartTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
if n == 0 {
|
||||
n = sel.FilterFunction(f).Length()
|
||||
} else {
|
||||
sel.FilterFunction(f)
|
||||
}
|
||||
}
|
||||
b.Logf("FilterFunction=%d", n)
|
||||
}
|
||||
|
||||
func BenchmarkNotFunction(b *testing.B) {
|
||||
var n int
|
||||
|
||||
b.StopTimer()
|
||||
sel := DocW().Find("li")
|
||||
f := func(i int, s *Selection) bool {
|
||||
return len(s.Get(0).Attr) > 0
|
||||
}
|
||||
b.StartTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
if n == 0 {
|
||||
n = sel.NotFunction(f).Length()
|
||||
} else {
|
||||
sel.NotFunction(f)
|
||||
}
|
||||
}
|
||||
b.Logf("NotFunction=%d", n)
|
||||
}
|
||||
|
||||
func BenchmarkFilterNodes(b *testing.B) {
|
||||
var n int
|
||||
|
||||
b.StopTimer()
|
||||
sel := DocW().Find("li")
|
||||
sel2 := DocW().Find(".toclevel-2")
|
||||
nodes := sel2.Nodes
|
||||
b.StartTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
if n == 0 {
|
||||
n = sel.FilterNodes(nodes...).Length()
|
||||
} else {
|
||||
sel.FilterNodes(nodes...)
|
||||
}
|
||||
}
|
||||
b.Logf("FilterNodes=%d", n)
|
||||
}
|
||||
|
||||
func BenchmarkNotNodes(b *testing.B) {
|
||||
var n int
|
||||
|
||||
b.StopTimer()
|
||||
sel := DocW().Find("li")
|
||||
sel2 := DocW().Find(".toclevel-1")
|
||||
nodes := sel2.Nodes
|
||||
b.StartTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
if n == 0 {
|
||||
n = sel.NotNodes(nodes...).Length()
|
||||
} else {
|
||||
sel.NotNodes(nodes...)
|
||||
}
|
||||
}
|
||||
b.Logf("NotNodes=%d", n)
|
||||
}
|
||||
|
||||
func BenchmarkFilterSelection(b *testing.B) {
|
||||
var n int
|
||||
|
||||
b.StopTimer()
|
||||
sel := DocW().Find("li")
|
||||
sel2 := DocW().Find(".toclevel-2")
|
||||
b.StartTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
if n == 0 {
|
||||
n = sel.FilterSelection(sel2).Length()
|
||||
} else {
|
||||
sel.FilterSelection(sel2)
|
||||
}
|
||||
}
|
||||
b.Logf("FilterSelection=%d", n)
|
||||
}
|
||||
|
||||
func BenchmarkNotSelection(b *testing.B) {
|
||||
var n int
|
||||
|
||||
b.StopTimer()
|
||||
sel := DocW().Find("li")
|
||||
sel2 := DocW().Find(".toclevel-1")
|
||||
b.StartTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
if n == 0 {
|
||||
n = sel.NotSelection(sel2).Length()
|
||||
} else {
|
||||
sel.NotSelection(sel2)
|
||||
}
|
||||
}
|
||||
b.Logf("NotSelection=%d", n)
|
||||
}
|
||||
|
||||
func BenchmarkHas(b *testing.B) {
|
||||
var n int
|
||||
|
||||
b.StopTimer()
|
||||
sel := DocW().Find("h2")
|
||||
b.StartTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
if n == 0 {
|
||||
n = sel.Has(".editsection").Length()
|
||||
} else {
|
||||
sel.Has(".editsection")
|
||||
}
|
||||
}
|
||||
b.Logf("Has=%d", n)
|
||||
}
|
||||
|
||||
func BenchmarkHasNodes(b *testing.B) {
|
||||
var n int
|
||||
|
||||
b.StopTimer()
|
||||
sel := DocW().Find("li")
|
||||
sel2 := DocW().Find(".tocnumber")
|
||||
nodes := sel2.Nodes
|
||||
b.StartTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
if n == 0 {
|
||||
n = sel.HasNodes(nodes...).Length()
|
||||
} else {
|
||||
sel.HasNodes(nodes...)
|
||||
}
|
||||
}
|
||||
b.Logf("HasNodes=%d", n)
|
||||
}
|
||||
|
||||
func BenchmarkHasSelection(b *testing.B) {
|
||||
var n int
|
||||
|
||||
b.StopTimer()
|
||||
sel := DocW().Find("li")
|
||||
sel2 := DocW().Find(".tocnumber")
|
||||
b.StartTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
if n == 0 {
|
||||
n = sel.HasSelection(sel2).Length()
|
||||
} else {
|
||||
sel.HasSelection(sel2)
|
||||
}
|
||||
}
|
||||
b.Logf("HasSelection=%d", n)
|
||||
}
|
||||
|
||||
func BenchmarkEnd(b *testing.B) {
|
||||
var n int
|
||||
|
||||
b.StopTimer()
|
||||
sel := DocW().Find("li").Has(".tocnumber")
|
||||
b.StartTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
if n == 0 {
|
||||
n = sel.End().Length()
|
||||
} else {
|
||||
sel.End()
|
||||
}
|
||||
}
|
||||
b.Logf("End=%d", n)
|
||||
}
|
62
vendor/github.com/PuerkitoBio/goquery/bench_iteration_test.go
generated
vendored
Normal file
62
vendor/github.com/PuerkitoBio/goquery/bench_iteration_test.go
generated
vendored
Normal file
|
@ -0,0 +1,62 @@
|
|||
package goquery
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func BenchmarkEach(b *testing.B) {
|
||||
var tmp, n int
|
||||
|
||||
b.StopTimer()
|
||||
sel := DocW().Find("td")
|
||||
f := func(i int, s *Selection) {
|
||||
tmp++
|
||||
}
|
||||
b.StartTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
sel.Each(f)
|
||||
if n == 0 {
|
||||
n = tmp
|
||||
}
|
||||
}
|
||||
b.Logf("Each=%d", n)
|
||||
}
|
||||
|
||||
func BenchmarkMap(b *testing.B) {
|
||||
var tmp, n int
|
||||
|
||||
b.StopTimer()
|
||||
sel := DocW().Find("td")
|
||||
f := func(i int, s *Selection) string {
|
||||
tmp++
|
||||
return string(tmp)
|
||||
}
|
||||
b.StartTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
sel.Map(f)
|
||||
if n == 0 {
|
||||
n = tmp
|
||||
}
|
||||
}
|
||||
b.Logf("Map=%d", n)
|
||||
}
|
||||
|
||||
func BenchmarkEachWithBreak(b *testing.B) {
|
||||
var tmp, n int
|
||||
|
||||
b.StopTimer()
|
||||
sel := DocW().Find("td")
|
||||
f := func(i int, s *Selection) bool {
|
||||
tmp++
|
||||
return tmp < 10
|
||||
}
|
||||
b.StartTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
tmp = 0
|
||||
sel.EachWithBreak(f)
|
||||
if n == 0 {
|
||||
n = tmp
|
||||
}
|
||||
}
|
||||
b.Logf("Each=%d", n)
|
||||
}
|
47
vendor/github.com/PuerkitoBio/goquery/bench_property_test.go
generated
vendored
Normal file
47
vendor/github.com/PuerkitoBio/goquery/bench_property_test.go
generated
vendored
Normal file
|
@ -0,0 +1,47 @@
|
|||
package goquery
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func BenchmarkAttr(b *testing.B) {
|
||||
var s string
|
||||
|
||||
b.StopTimer()
|
||||
sel := DocW().Find("h1")
|
||||
b.StartTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
s, _ = sel.Attr("id")
|
||||
}
|
||||
b.Logf("Attr=%s", s)
|
||||
}
|
||||
|
||||
func BenchmarkText(b *testing.B) {
|
||||
b.StopTimer()
|
||||
sel := DocW().Find("h2")
|
||||
b.StartTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
sel.Text()
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkLength(b *testing.B) {
|
||||
var n int
|
||||
|
||||
b.StopTimer()
|
||||
sel := DocW().Find("h2")
|
||||
b.StartTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
n = sel.Length()
|
||||
}
|
||||
b.Logf("Length=%d", n)
|
||||
}
|
||||
|
||||
func BenchmarkHtml(b *testing.B) {
|
||||
b.StopTimer()
|
||||
sel := DocW().Find("h2")
|
||||
b.StartTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
sel.Html()
|
||||
}
|
||||
}
|
97
vendor/github.com/PuerkitoBio/goquery/bench_query_test.go
generated
vendored
Normal file
97
vendor/github.com/PuerkitoBio/goquery/bench_query_test.go
generated
vendored
Normal file
|
@ -0,0 +1,97 @@
|
|||
package goquery
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func BenchmarkIs(b *testing.B) {
|
||||
var y bool
|
||||
|
||||
b.StopTimer()
|
||||
sel := DocW().Find("li")
|
||||
b.StartTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
y = sel.Is(".toclevel-2")
|
||||
}
|
||||
b.Logf("Is=%v", y)
|
||||
}
|
||||
|
||||
func BenchmarkIsPositional(b *testing.B) {
|
||||
var y bool
|
||||
|
||||
b.StopTimer()
|
||||
sel := DocW().Find("li")
|
||||
b.StartTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
y = sel.Is("li:nth-child(2)")
|
||||
}
|
||||
b.Logf("IsPositional=%v", y)
|
||||
}
|
||||
|
||||
func BenchmarkIsFunction(b *testing.B) {
|
||||
var y bool
|
||||
|
||||
b.StopTimer()
|
||||
sel := DocW().Find(".toclevel-1")
|
||||
f := func(i int, s *Selection) bool {
|
||||
return i == 8
|
||||
}
|
||||
b.StartTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
y = sel.IsFunction(f)
|
||||
}
|
||||
b.Logf("IsFunction=%v", y)
|
||||
}
|
||||
|
||||
func BenchmarkIsSelection(b *testing.B) {
|
||||
var y bool
|
||||
|
||||
b.StopTimer()
|
||||
sel := DocW().Find("li")
|
||||
sel2 := DocW().Find(".toclevel-2")
|
||||
b.StartTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
y = sel.IsSelection(sel2)
|
||||
}
|
||||
b.Logf("IsSelection=%v", y)
|
||||
}
|
||||
|
||||
func BenchmarkIsNodes(b *testing.B) {
|
||||
var y bool
|
||||
|
||||
b.StopTimer()
|
||||
sel := DocW().Find("li")
|
||||
sel2 := DocW().Find(".toclevel-2")
|
||||
nodes := sel2.Nodes
|
||||
b.StartTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
y = sel.IsNodes(nodes...)
|
||||
}
|
||||
b.Logf("IsNodes=%v", y)
|
||||
}
|
||||
|
||||
func BenchmarkHasClass(b *testing.B) {
|
||||
var y bool
|
||||
|
||||
b.StopTimer()
|
||||
sel := DocW().Find("span")
|
||||
b.StartTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
y = sel.HasClass("official")
|
||||
}
|
||||
b.Logf("HasClass=%v", y)
|
||||
}
|
||||
|
||||
func BenchmarkContains(b *testing.B) {
|
||||
var y bool
|
||||
|
||||
b.StopTimer()
|
||||
sel := DocW().Find("span.url")
|
||||
sel2 := DocW().Find("a[rel=\"nofollow\"]")
|
||||
node := sel2.Nodes[0]
|
||||
b.StartTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
y = sel.Contains(node)
|
||||
}
|
||||
b.Logf("Contains=%v", y)
|
||||
}
|
716
vendor/github.com/PuerkitoBio/goquery/bench_traversal_test.go
generated
vendored
Normal file
716
vendor/github.com/PuerkitoBio/goquery/bench_traversal_test.go
generated
vendored
Normal file
|
@ -0,0 +1,716 @@
|
|||
package goquery
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func BenchmarkFind(b *testing.B) {
|
||||
var n int
|
||||
|
||||
for i := 0; i < b.N; i++ {
|
||||
if n == 0 {
|
||||
n = DocB().Find("dd").Length()
|
||||
|
||||
} else {
|
||||
DocB().Find("dd")
|
||||
}
|
||||
}
|
||||
b.Logf("Find=%d", n)
|
||||
}
|
||||
|
||||
func BenchmarkFindWithinSelection(b *testing.B) {
|
||||
var n int
|
||||
|
||||
b.StopTimer()
|
||||
sel := DocW().Find("ul")
|
||||
b.StartTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
if n == 0 {
|
||||
n = sel.Find("a[class]").Length()
|
||||
} else {
|
||||
sel.Find("a[class]")
|
||||
}
|
||||
}
|
||||
b.Logf("FindWithinSelection=%d", n)
|
||||
}
|
||||
|
||||
func BenchmarkFindSelection(b *testing.B) {
|
||||
var n int
|
||||
|
||||
b.StopTimer()
|
||||
sel := DocW().Find("ul")
|
||||
sel2 := DocW().Find("span")
|
||||
b.StartTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
if n == 0 {
|
||||
n = sel.FindSelection(sel2).Length()
|
||||
} else {
|
||||
sel.FindSelection(sel2)
|
||||
}
|
||||
}
|
||||
b.Logf("FindSelection=%d", n)
|
||||
}
|
||||
|
||||
func BenchmarkFindNodes(b *testing.B) {
|
||||
var n int
|
||||
|
||||
b.StopTimer()
|
||||
sel := DocW().Find("ul")
|
||||
sel2 := DocW().Find("span")
|
||||
nodes := sel2.Nodes
|
||||
b.StartTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
if n == 0 {
|
||||
n = sel.FindNodes(nodes...).Length()
|
||||
} else {
|
||||
sel.FindNodes(nodes...)
|
||||
}
|
||||
}
|
||||
b.Logf("FindNodes=%d", n)
|
||||
}
|
||||
|
||||
func BenchmarkContents(b *testing.B) {
|
||||
var n int
|
||||
|
||||
b.StopTimer()
|
||||
sel := DocW().Find(".toclevel-1")
|
||||
b.StartTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
if n == 0 {
|
||||
n = sel.Contents().Length()
|
||||
} else {
|
||||
sel.Contents()
|
||||
}
|
||||
}
|
||||
b.Logf("Contents=%d", n)
|
||||
}
|
||||
|
||||
func BenchmarkContentsFiltered(b *testing.B) {
|
||||
var n int
|
||||
|
||||
b.StopTimer()
|
||||
sel := DocW().Find(".toclevel-1")
|
||||
b.StartTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
if n == 0 {
|
||||
n = sel.ContentsFiltered("a[href=\"#Examples\"]").Length()
|
||||
} else {
|
||||
sel.ContentsFiltered("a[href=\"#Examples\"]")
|
||||
}
|
||||
}
|
||||
b.Logf("ContentsFiltered=%d", n)
|
||||
}
|
||||
|
||||
func BenchmarkChildren(b *testing.B) {
|
||||
var n int
|
||||
|
||||
b.StopTimer()
|
||||
sel := DocW().Find(".toclevel-2")
|
||||
b.StartTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
if n == 0 {
|
||||
n = sel.Children().Length()
|
||||
} else {
|
||||
sel.Children()
|
||||
}
|
||||
}
|
||||
b.Logf("Children=%d", n)
|
||||
}
|
||||
|
||||
func BenchmarkChildrenFiltered(b *testing.B) {
|
||||
var n int
|
||||
|
||||
b.StopTimer()
|
||||
sel := DocW().Find("h3")
|
||||
b.StartTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
if n == 0 {
|
||||
n = sel.ChildrenFiltered(".editsection").Length()
|
||||
} else {
|
||||
sel.ChildrenFiltered(".editsection")
|
||||
}
|
||||
}
|
||||
b.Logf("ChildrenFiltered=%d", n)
|
||||
}
|
||||
|
||||
func BenchmarkParent(b *testing.B) {
|
||||
var n int
|
||||
|
||||
b.StopTimer()
|
||||
sel := DocW().Find("li")
|
||||
b.StartTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
if n == 0 {
|
||||
n = sel.Parent().Length()
|
||||
} else {
|
||||
sel.Parent()
|
||||
}
|
||||
}
|
||||
b.Logf("Parent=%d", n)
|
||||
}
|
||||
|
||||
func BenchmarkParentFiltered(b *testing.B) {
|
||||
var n int
|
||||
|
||||
b.StopTimer()
|
||||
sel := DocW().Find("li")
|
||||
b.StartTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
if n == 0 {
|
||||
n = sel.ParentFiltered("ul[id]").Length()
|
||||
} else {
|
||||
sel.ParentFiltered("ul[id]")
|
||||
}
|
||||
}
|
||||
b.Logf("ParentFiltered=%d", n)
|
||||
}
|
||||
|
||||
func BenchmarkParents(b *testing.B) {
|
||||
var n int
|
||||
|
||||
b.StopTimer()
|
||||
sel := DocW().Find("th a")
|
||||
b.StartTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
if n == 0 {
|
||||
n = sel.Parents().Length()
|
||||
} else {
|
||||
sel.Parents()
|
||||
}
|
||||
}
|
||||
b.Logf("Parents=%d", n)
|
||||
}
|
||||
|
||||
func BenchmarkParentsFiltered(b *testing.B) {
|
||||
var n int
|
||||
|
||||
b.StopTimer()
|
||||
sel := DocW().Find("th a")
|
||||
b.StartTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
if n == 0 {
|
||||
n = sel.ParentsFiltered("tr").Length()
|
||||
} else {
|
||||
sel.ParentsFiltered("tr")
|
||||
}
|
||||
}
|
||||
b.Logf("ParentsFiltered=%d", n)
|
||||
}
|
||||
|
||||
func BenchmarkParentsUntil(b *testing.B) {
|
||||
var n int
|
||||
|
||||
b.StopTimer()
|
||||
sel := DocW().Find("th a")
|
||||
b.StartTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
if n == 0 {
|
||||
n = sel.ParentsUntil("table").Length()
|
||||
} else {
|
||||
sel.ParentsUntil("table")
|
||||
}
|
||||
}
|
||||
b.Logf("ParentsUntil=%d", n)
|
||||
}
|
||||
|
||||
func BenchmarkParentsUntilSelection(b *testing.B) {
|
||||
var n int
|
||||
|
||||
b.StopTimer()
|
||||
sel := DocW().Find("th a")
|
||||
sel2 := DocW().Find("#content")
|
||||
b.StartTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
if n == 0 {
|
||||
n = sel.ParentsUntilSelection(sel2).Length()
|
||||
} else {
|
||||
sel.ParentsUntilSelection(sel2)
|
||||
}
|
||||
}
|
||||
b.Logf("ParentsUntilSelection=%d", n)
|
||||
}
|
||||
|
||||
func BenchmarkParentsUntilNodes(b *testing.B) {
|
||||
var n int
|
||||
|
||||
b.StopTimer()
|
||||
sel := DocW().Find("th a")
|
||||
sel2 := DocW().Find("#content")
|
||||
nodes := sel2.Nodes
|
||||
b.StartTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
if n == 0 {
|
||||
n = sel.ParentsUntilNodes(nodes...).Length()
|
||||
} else {
|
||||
sel.ParentsUntilNodes(nodes...)
|
||||
}
|
||||
}
|
||||
b.Logf("ParentsUntilNodes=%d", n)
|
||||
}
|
||||
|
||||
func BenchmarkParentsFilteredUntil(b *testing.B) {
|
||||
var n int
|
||||
|
||||
b.StopTimer()
|
||||
sel := DocW().Find(".toclevel-1 a")
|
||||
b.StartTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
if n == 0 {
|
||||
n = sel.ParentsFilteredUntil(":nth-child(1)", "ul").Length()
|
||||
} else {
|
||||
sel.ParentsFilteredUntil(":nth-child(1)", "ul")
|
||||
}
|
||||
}
|
||||
b.Logf("ParentsFilteredUntil=%d", n)
|
||||
}
|
||||
|
||||
func BenchmarkParentsFilteredUntilSelection(b *testing.B) {
|
||||
var n int
|
||||
|
||||
b.StopTimer()
|
||||
sel := DocW().Find(".toclevel-1 a")
|
||||
sel2 := DocW().Find("ul")
|
||||
b.StartTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
if n == 0 {
|
||||
n = sel.ParentsFilteredUntilSelection(":nth-child(1)", sel2).Length()
|
||||
} else {
|
||||
sel.ParentsFilteredUntilSelection(":nth-child(1)", sel2)
|
||||
}
|
||||
}
|
||||
b.Logf("ParentsFilteredUntilSelection=%d", n)
|
||||
}
|
||||
|
||||
func BenchmarkParentsFilteredUntilNodes(b *testing.B) {
|
||||
var n int
|
||||
|
||||
b.StopTimer()
|
||||
sel := DocW().Find(".toclevel-1 a")
|
||||
sel2 := DocW().Find("ul")
|
||||
nodes := sel2.Nodes
|
||||
b.StartTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
if n == 0 {
|
||||
n = sel.ParentsFilteredUntilNodes(":nth-child(1)", nodes...).Length()
|
||||
} else {
|
||||
sel.ParentsFilteredUntilNodes(":nth-child(1)", nodes...)
|
||||
}
|
||||
}
|
||||
b.Logf("ParentsFilteredUntilNodes=%d", n)
|
||||
}
|
||||
|
||||
func BenchmarkSiblings(b *testing.B) {
|
||||
var n int
|
||||
|
||||
b.StopTimer()
|
||||
sel := DocW().Find("ul li:nth-child(1)")
|
||||
b.StartTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
if n == 0 {
|
||||
n = sel.Siblings().Length()
|
||||
} else {
|
||||
sel.Siblings()
|
||||
}
|
||||
}
|
||||
b.Logf("Siblings=%d", n)
|
||||
}
|
||||
|
||||
func BenchmarkSiblingsFiltered(b *testing.B) {
|
||||
var n int
|
||||
|
||||
b.StopTimer()
|
||||
sel := DocW().Find("ul li:nth-child(1)")
|
||||
b.StartTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
if n == 0 {
|
||||
n = sel.SiblingsFiltered("[class]").Length()
|
||||
} else {
|
||||
sel.SiblingsFiltered("[class]")
|
||||
}
|
||||
}
|
||||
b.Logf("SiblingsFiltered=%d", n)
|
||||
}
|
||||
|
||||
func BenchmarkNext(b *testing.B) {
|
||||
var n int
|
||||
|
||||
b.StopTimer()
|
||||
sel := DocW().Find("li:nth-child(1)")
|
||||
b.StartTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
if n == 0 {
|
||||
n = sel.Next().Length()
|
||||
} else {
|
||||
sel.Next()
|
||||
}
|
||||
}
|
||||
b.Logf("Next=%d", n)
|
||||
}
|
||||
|
||||
func BenchmarkNextFiltered(b *testing.B) {
|
||||
var n int
|
||||
|
||||
b.StopTimer()
|
||||
sel := DocW().Find("li:nth-child(1)")
|
||||
b.StartTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
if n == 0 {
|
||||
n = sel.NextFiltered("[class]").Length()
|
||||
} else {
|
||||
sel.NextFiltered("[class]")
|
||||
}
|
||||
}
|
||||
b.Logf("NextFiltered=%d", n)
|
||||
}
|
||||
|
||||
func BenchmarkNextAll(b *testing.B) {
|
||||
var n int
|
||||
|
||||
b.StopTimer()
|
||||
sel := DocW().Find("li:nth-child(3)")
|
||||
b.StartTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
if n == 0 {
|
||||
n = sel.NextAll().Length()
|
||||
} else {
|
||||
sel.NextAll()
|
||||
}
|
||||
}
|
||||
b.Logf("NextAll=%d", n)
|
||||
}
|
||||
|
||||
func BenchmarkNextAllFiltered(b *testing.B) {
|
||||
var n int
|
||||
|
||||
b.StopTimer()
|
||||
sel := DocW().Find("li:nth-child(3)")
|
||||
b.StartTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
if n == 0 {
|
||||
n = sel.NextAllFiltered("[class]").Length()
|
||||
} else {
|
||||
sel.NextAllFiltered("[class]")
|
||||
}
|
||||
}
|
||||
b.Logf("NextAllFiltered=%d", n)
|
||||
}
|
||||
|
||||
func BenchmarkPrev(b *testing.B) {
|
||||
var n int
|
||||
|
||||
b.StopTimer()
|
||||
sel := DocW().Find("li:last-child")
|
||||
b.StartTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
if n == 0 {
|
||||
n = sel.Prev().Length()
|
||||
} else {
|
||||
sel.Prev()
|
||||
}
|
||||
}
|
||||
b.Logf("Prev=%d", n)
|
||||
}
|
||||
|
||||
func BenchmarkPrevFiltered(b *testing.B) {
|
||||
var n int
|
||||
|
||||
b.StopTimer()
|
||||
sel := DocW().Find("li:last-child")
|
||||
b.StartTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
if n == 0 {
|
||||
n = sel.PrevFiltered("[class]").Length()
|
||||
} else {
|
||||
sel.PrevFiltered("[class]")
|
||||
}
|
||||
}
|
||||
// There is one more Prev li with a class, compared to Next li with a class
|
||||
// (confirmed by looking at the HTML, this is ok)
|
||||
b.Logf("PrevFiltered=%d", n)
|
||||
}
|
||||
|
||||
func BenchmarkPrevAll(b *testing.B) {
|
||||
var n int
|
||||
|
||||
b.StopTimer()
|
||||
sel := DocW().Find("li:nth-child(4)")
|
||||
b.StartTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
if n == 0 {
|
||||
n = sel.PrevAll().Length()
|
||||
} else {
|
||||
sel.PrevAll()
|
||||
}
|
||||
}
|
||||
b.Logf("PrevAll=%d", n)
|
||||
}
|
||||
|
||||
func BenchmarkPrevAllFiltered(b *testing.B) {
|
||||
var n int
|
||||
|
||||
b.StopTimer()
|
||||
sel := DocW().Find("li:nth-child(4)")
|
||||
b.StartTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
if n == 0 {
|
||||
n = sel.PrevAllFiltered("[class]").Length()
|
||||
} else {
|
||||
sel.PrevAllFiltered("[class]")
|
||||
}
|
||||
}
|
||||
b.Logf("PrevAllFiltered=%d", n)
|
||||
}
|
||||
|
||||
func BenchmarkNextUntil(b *testing.B) {
|
||||
var n int
|
||||
|
||||
b.StopTimer()
|
||||
sel := DocW().Find("li:first-child")
|
||||
b.StartTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
if n == 0 {
|
||||
n = sel.NextUntil(":nth-child(4)").Length()
|
||||
} else {
|
||||
sel.NextUntil(":nth-child(4)")
|
||||
}
|
||||
}
|
||||
b.Logf("NextUntil=%d", n)
|
||||
}
|
||||
|
||||
func BenchmarkNextUntilSelection(b *testing.B) {
|
||||
var n int
|
||||
|
||||
b.StopTimer()
|
||||
sel := DocW().Find("h2")
|
||||
sel2 := DocW().Find("ul")
|
||||
b.StartTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
if n == 0 {
|
||||
n = sel.NextUntilSelection(sel2).Length()
|
||||
} else {
|
||||
sel.NextUntilSelection(sel2)
|
||||
}
|
||||
}
|
||||
b.Logf("NextUntilSelection=%d", n)
|
||||
}
|
||||
|
||||
func BenchmarkNextUntilNodes(b *testing.B) {
|
||||
var n int
|
||||
|
||||
b.StopTimer()
|
||||
sel := DocW().Find("h2")
|
||||
sel2 := DocW().Find("p")
|
||||
nodes := sel2.Nodes
|
||||
b.StartTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
if n == 0 {
|
||||
n = sel.NextUntilNodes(nodes...).Length()
|
||||
} else {
|
||||
sel.NextUntilNodes(nodes...)
|
||||
}
|
||||
}
|
||||
b.Logf("NextUntilNodes=%d", n)
|
||||
}
|
||||
|
||||
func BenchmarkPrevUntil(b *testing.B) {
|
||||
var n int
|
||||
|
||||
b.StopTimer()
|
||||
sel := DocW().Find("li:last-child")
|
||||
b.StartTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
if n == 0 {
|
||||
n = sel.PrevUntil(":nth-child(4)").Length()
|
||||
} else {
|
||||
sel.PrevUntil(":nth-child(4)")
|
||||
}
|
||||
}
|
||||
b.Logf("PrevUntil=%d", n)
|
||||
}
|
||||
|
||||
func BenchmarkPrevUntilSelection(b *testing.B) {
|
||||
var n int
|
||||
|
||||
b.StopTimer()
|
||||
sel := DocW().Find("h2")
|
||||
sel2 := DocW().Find("ul")
|
||||
b.StartTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
if n == 0 {
|
||||
n = sel.PrevUntilSelection(sel2).Length()
|
||||
} else {
|
||||
sel.PrevUntilSelection(sel2)
|
||||
}
|
||||
}
|
||||
b.Logf("PrevUntilSelection=%d", n)
|
||||
}
|
||||
|
||||
func BenchmarkPrevUntilNodes(b *testing.B) {
|
||||
var n int
|
||||
|
||||
b.StopTimer()
|
||||
sel := DocW().Find("h2")
|
||||
sel2 := DocW().Find("p")
|
||||
nodes := sel2.Nodes
|
||||
b.StartTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
if n == 0 {
|
||||
n = sel.PrevUntilNodes(nodes...).Length()
|
||||
} else {
|
||||
sel.PrevUntilNodes(nodes...)
|
||||
}
|
||||
}
|
||||
b.Logf("PrevUntilNodes=%d", n)
|
||||
}
|
||||
|
||||
func BenchmarkNextFilteredUntil(b *testing.B) {
|
||||
var n int
|
||||
|
||||
b.StopTimer()
|
||||
sel := DocW().Find("h2")
|
||||
b.StartTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
if n == 0 {
|
||||
n = sel.NextFilteredUntil("p", "div").Length()
|
||||
} else {
|
||||
sel.NextFilteredUntil("p", "div")
|
||||
}
|
||||
}
|
||||
b.Logf("NextFilteredUntil=%d", n)
|
||||
}
|
||||
|
||||
func BenchmarkNextFilteredUntilSelection(b *testing.B) {
|
||||
var n int
|
||||
|
||||
b.StopTimer()
|
||||
sel := DocW().Find("h2")
|
||||
sel2 := DocW().Find("div")
|
||||
b.StartTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
if n == 0 {
|
||||
n = sel.NextFilteredUntilSelection("p", sel2).Length()
|
||||
} else {
|
||||
sel.NextFilteredUntilSelection("p", sel2)
|
||||
}
|
||||
}
|
||||
b.Logf("NextFilteredUntilSelection=%d", n)
|
||||
}
|
||||
|
||||
func BenchmarkNextFilteredUntilNodes(b *testing.B) {
|
||||
var n int
|
||||
|
||||
b.StopTimer()
|
||||
sel := DocW().Find("h2")
|
||||
sel2 := DocW().Find("div")
|
||||
nodes := sel2.Nodes
|
||||
b.StartTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
if n == 0 {
|
||||
n = sel.NextFilteredUntilNodes("p", nodes...).Length()
|
||||
} else {
|
||||
sel.NextFilteredUntilNodes("p", nodes...)
|
||||
}
|
||||
}
|
||||
b.Logf("NextFilteredUntilNodes=%d", n)
|
||||
}
|
||||
|
||||
func BenchmarkPrevFilteredUntil(b *testing.B) {
|
||||
var n int
|
||||
|
||||
b.StopTimer()
|
||||
sel := DocW().Find("h2")
|
||||
b.StartTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
if n == 0 {
|
||||
n = sel.PrevFilteredUntil("p", "div").Length()
|
||||
} else {
|
||||
sel.PrevFilteredUntil("p", "div")
|
||||
}
|
||||
}
|
||||
b.Logf("PrevFilteredUntil=%d", n)
|
||||
}
|
||||
|
||||
func BenchmarkPrevFilteredUntilSelection(b *testing.B) {
|
||||
var n int
|
||||
|
||||
b.StopTimer()
|
||||
sel := DocW().Find("h2")
|
||||
sel2 := DocW().Find("div")
|
||||
b.StartTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
if n == 0 {
|
||||
n = sel.PrevFilteredUntilSelection("p", sel2).Length()
|
||||
} else {
|
||||
sel.PrevFilteredUntilSelection("p", sel2)
|
||||
}
|
||||
}
|
||||
b.Logf("PrevFilteredUntilSelection=%d", n)
|
||||
}
|
||||
|
||||
func BenchmarkPrevFilteredUntilNodes(b *testing.B) {
|
||||
var n int
|
||||
|
||||
b.StopTimer()
|
||||
sel := DocW().Find("h2")
|
||||
sel2 := DocW().Find("div")
|
||||
nodes := sel2.Nodes
|
||||
b.StartTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
if n == 0 {
|
||||
n = sel.PrevFilteredUntilNodes("p", nodes...).Length()
|
||||
} else {
|
||||
sel.PrevFilteredUntilNodes("p", nodes...)
|
||||
}
|
||||
}
|
||||
b.Logf("PrevFilteredUntilNodes=%d", n)
|
||||
}
|
||||
|
||||
func BenchmarkClosest(b *testing.B) {
|
||||
var n int
|
||||
|
||||
b.StopTimer()
|
||||
sel := Doc().Find(".container-fluid")
|
||||
b.StartTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
if n == 0 {
|
||||
n = sel.Closest(".pvk-content").Length()
|
||||
} else {
|
||||
sel.Closest(".pvk-content")
|
||||
}
|
||||
}
|
||||
b.Logf("Closest=%d", n)
|
||||
}
|
||||
|
||||
func BenchmarkClosestSelection(b *testing.B) {
|
||||
var n int
|
||||
|
||||
b.StopTimer()
|
||||
sel := Doc().Find(".container-fluid")
|
||||
sel2 := Doc().Find(".pvk-content")
|
||||
b.StartTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
if n == 0 {
|
||||
n = sel.ClosestSelection(sel2).Length()
|
||||
} else {
|
||||
sel.ClosestSelection(sel2)
|
||||
}
|
||||
}
|
||||
b.Logf("ClosestSelection=%d", n)
|
||||
}
|
||||
|
||||
func BenchmarkClosestNodes(b *testing.B) {
|
||||
var n int
|
||||
|
||||
b.StopTimer()
|
||||
sel := Doc().Find(".container-fluid")
|
||||
nodes := Doc().Find(".pvk-content").Nodes
|
||||
b.StartTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
if n == 0 {
|
||||
n = sel.ClosestNodes(nodes...).Length()
|
||||
} else {
|
||||
sel.ClosestNodes(nodes...)
|
||||
}
|
||||
}
|
||||
b.Logf("ClosestNodes=%d", n)
|
||||
}
|
32
vendor/github.com/PuerkitoBio/goquery/example_test.go
generated
vendored
Normal file
32
vendor/github.com/PuerkitoBio/goquery/example_test.go
generated
vendored
Normal file
|
@ -0,0 +1,32 @@
|
|||
package goquery
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
|
||||
// In real use, this import would be required (not in this example, since it
|
||||
// is part of the goquery package)
|
||||
//"github.com/PuerkitoBio/goquery"
|
||||
)
|
||||
|
||||
// This example scrapes the reviews shown on the home page of metalsucks.net.
|
||||
func ExampleScrape_MetalSucks() {
|
||||
// Load the HTML document (in real use, the type would be *goquery.Document)
|
||||
doc, err := NewDocument("http://metalsucks.net")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
// Find the review items (the type of the Selection would be *goquery.Selection)
|
||||
doc.Find(".reviews-wrap article .review-rhs").Each(func(i int, s *Selection) {
|
||||
// For each item found, get the band and title
|
||||
band := s.Find("h3").Text()
|
||||
title := s.Find("i").Text()
|
||||
fmt.Printf("Review %d: %s - %s\n", i, band, title)
|
||||
})
|
||||
// To see the output of the Example while running the test suite (go test), simply
|
||||
// remove the leading "x" before Output on the next line. This will cause the
|
||||
// example to fail (all the "real" tests should pass).
|
||||
|
||||
// xOutput: voluntarily fail the Example output.
|
||||
}
|
68
vendor/github.com/PuerkitoBio/goquery/expand_test.go
generated
vendored
Normal file
68
vendor/github.com/PuerkitoBio/goquery/expand_test.go
generated
vendored
Normal file
|
@ -0,0 +1,68 @@
|
|||
package goquery
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestAdd(t *testing.T) {
|
||||
sel := Doc().Find("div.row-fluid").Add("a")
|
||||
assertLength(t, sel.Nodes, 19)
|
||||
}
|
||||
|
||||
func TestAddRollback(t *testing.T) {
|
||||
sel := Doc().Find(".pvk-content")
|
||||
sel2 := sel.Add("a").End()
|
||||
assertEqual(t, sel, sel2)
|
||||
}
|
||||
|
||||
func TestAddSelection(t *testing.T) {
|
||||
sel := Doc().Find("div.row-fluid")
|
||||
sel2 := Doc().Find("a")
|
||||
sel = sel.AddSelection(sel2)
|
||||
assertLength(t, sel.Nodes, 19)
|
||||
}
|
||||
|
||||
func TestAddSelectionNil(t *testing.T) {
|
||||
sel := Doc().Find("div.row-fluid")
|
||||
assertLength(t, sel.Nodes, 9)
|
||||
|
||||
sel = sel.AddSelection(nil)
|
||||
assertLength(t, sel.Nodes, 9)
|
||||
}
|
||||
|
||||
func TestAddSelectionRollback(t *testing.T) {
|
||||
sel := Doc().Find(".pvk-content")
|
||||
sel2 := sel.Find("a")
|
||||
sel2 = sel.AddSelection(sel2).End()
|
||||
assertEqual(t, sel, sel2)
|
||||
}
|
||||
|
||||
func TestAddNodes(t *testing.T) {
|
||||
sel := Doc().Find("div.pvk-gutter")
|
||||
sel2 := Doc().Find(".pvk-content")
|
||||
sel = sel.AddNodes(sel2.Nodes...)
|
||||
assertLength(t, sel.Nodes, 9)
|
||||
}
|
||||
|
||||
func TestAddNodesNone(t *testing.T) {
|
||||
sel := Doc().Find("div.pvk-gutter").AddNodes()
|
||||
assertLength(t, sel.Nodes, 6)
|
||||
}
|
||||
|
||||
func TestAddNodesRollback(t *testing.T) {
|
||||
sel := Doc().Find(".pvk-content")
|
||||
sel2 := sel.Find("a")
|
||||
sel2 = sel.AddNodes(sel2.Nodes...).End()
|
||||
assertEqual(t, sel, sel2)
|
||||
}
|
||||
|
||||
func TestAndSelf(t *testing.T) {
|
||||
sel := Doc().Find(".span12").Last().AndSelf()
|
||||
assertLength(t, sel.Nodes, 2)
|
||||
}
|
||||
|
||||
func TestAndSelfRollback(t *testing.T) {
|
||||
sel := Doc().Find(".pvk-content")
|
||||
sel2 := sel.Find("a").AndSelf().End().End()
|
||||
assertEqual(t, sel, sel2)
|
||||
}
|
191
vendor/github.com/PuerkitoBio/goquery/filter_test.go
generated
vendored
Normal file
191
vendor/github.com/PuerkitoBio/goquery/filter_test.go
generated
vendored
Normal file
|
@ -0,0 +1,191 @@
|
|||
package goquery
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestFilter(t *testing.T) {
|
||||
sel := Doc().Find(".span12").Filter(".alert")
|
||||
assertLength(t, sel.Nodes, 1)
|
||||
}
|
||||
|
||||
func TestFilterNone(t *testing.T) {
|
||||
sel := Doc().Find(".span12").Filter(".zzalert")
|
||||
assertLength(t, sel.Nodes, 0)
|
||||
}
|
||||
|
||||
func TestFilterRollback(t *testing.T) {
|
||||
sel := Doc().Find(".pvk-content")
|
||||
sel2 := sel.Filter(".alert").End()
|
||||
assertEqual(t, sel, sel2)
|
||||
}
|
||||
|
||||
func TestFilterFunction(t *testing.T) {
|
||||
sel := Doc().Find(".pvk-content").FilterFunction(func(i int, s *Selection) bool {
|
||||
return i > 0
|
||||
})
|
||||
assertLength(t, sel.Nodes, 2)
|
||||
}
|
||||
|
||||
func TestFilterFunctionRollback(t *testing.T) {
|
||||
sel := Doc().Find(".pvk-content")
|
||||
sel2 := sel.FilterFunction(func(i int, s *Selection) bool {
|
||||
return i > 0
|
||||
}).End()
|
||||
assertEqual(t, sel, sel2)
|
||||
}
|
||||
|
||||
func TestFilterNode(t *testing.T) {
|
||||
sel := Doc().Find(".pvk-content")
|
||||
sel2 := sel.FilterNodes(sel.Nodes[2])
|
||||
assertLength(t, sel2.Nodes, 1)
|
||||
}
|
||||
|
||||
func TestFilterNodeRollback(t *testing.T) {
|
||||
sel := Doc().Find(".pvk-content")
|
||||
sel2 := sel.FilterNodes(sel.Nodes[2]).End()
|
||||
assertEqual(t, sel, sel2)
|
||||
}
|
||||
|
||||
func TestFilterSelection(t *testing.T) {
|
||||
sel := Doc().Find(".link")
|
||||
sel2 := Doc().Find("a[ng-click]")
|
||||
sel3 := sel.FilterSelection(sel2)
|
||||
assertLength(t, sel3.Nodes, 1)
|
||||
}
|
||||
|
||||
func TestFilterSelectionRollback(t *testing.T) {
|
||||
sel := Doc().Find(".link")
|
||||
sel2 := Doc().Find("a[ng-click]")
|
||||
sel2 = sel.FilterSelection(sel2).End()
|
||||
assertEqual(t, sel, sel2)
|
||||
}
|
||||
|
||||
func TestFilterSelectionNil(t *testing.T) {
|
||||
var sel2 *Selection
|
||||
|
||||
sel := Doc().Find(".link")
|
||||
sel3 := sel.FilterSelection(sel2)
|
||||
assertLength(t, sel3.Nodes, 0)
|
||||
}
|
||||
|
||||
func TestNot(t *testing.T) {
|
||||
sel := Doc().Find(".span12").Not(".alert")
|
||||
assertLength(t, sel.Nodes, 1)
|
||||
}
|
||||
|
||||
func TestNotRollback(t *testing.T) {
|
||||
sel := Doc().Find(".span12")
|
||||
sel2 := sel.Not(".alert").End()
|
||||
assertEqual(t, sel, sel2)
|
||||
}
|
||||
|
||||
func TestNotNone(t *testing.T) {
|
||||
sel := Doc().Find(".span12").Not(".zzalert")
|
||||
assertLength(t, sel.Nodes, 2)
|
||||
}
|
||||
|
||||
func TestNotFunction(t *testing.T) {
|
||||
sel := Doc().Find(".pvk-content").NotFunction(func(i int, s *Selection) bool {
|
||||
return i > 0
|
||||
})
|
||||
assertLength(t, sel.Nodes, 1)
|
||||
}
|
||||
|
||||
func TestNotFunctionRollback(t *testing.T) {
|
||||
sel := Doc().Find(".pvk-content")
|
||||
sel2 := sel.NotFunction(func(i int, s *Selection) bool {
|
||||
return i > 0
|
||||
}).End()
|
||||
assertEqual(t, sel, sel2)
|
||||
}
|
||||
|
||||
func TestNotNode(t *testing.T) {
|
||||
sel := Doc().Find(".pvk-content")
|
||||
sel2 := sel.NotNodes(sel.Nodes[2])
|
||||
assertLength(t, sel2.Nodes, 2)
|
||||
}
|
||||
|
||||
func TestNotNodeRollback(t *testing.T) {
|
||||
sel := Doc().Find(".pvk-content")
|
||||
sel2 := sel.NotNodes(sel.Nodes[2]).End()
|
||||
assertEqual(t, sel, sel2)
|
||||
}
|
||||
|
||||
func TestNotSelection(t *testing.T) {
|
||||
sel := Doc().Find(".link")
|
||||
sel2 := Doc().Find("a[ng-click]")
|
||||
sel3 := sel.NotSelection(sel2)
|
||||
assertLength(t, sel3.Nodes, 6)
|
||||
}
|
||||
|
||||
func TestNotSelectionRollback(t *testing.T) {
|
||||
sel := Doc().Find(".link")
|
||||
sel2 := Doc().Find("a[ng-click]")
|
||||
sel2 = sel.NotSelection(sel2).End()
|
||||
assertEqual(t, sel, sel2)
|
||||
}
|
||||
|
||||
func TestIntersection(t *testing.T) {
|
||||
sel := Doc().Find(".pvk-gutter")
|
||||
sel2 := Doc().Find("div").Intersection(sel)
|
||||
assertLength(t, sel2.Nodes, 6)
|
||||
}
|
||||
|
||||
func TestIntersectionRollback(t *testing.T) {
|
||||
sel := Doc().Find(".pvk-gutter")
|
||||
sel2 := Doc().Find("div")
|
||||
sel2 = sel.Intersection(sel2).End()
|
||||
assertEqual(t, sel, sel2)
|
||||
}
|
||||
|
||||
func TestHas(t *testing.T) {
|
||||
sel := Doc().Find(".container-fluid").Has(".center-content")
|
||||
assertLength(t, sel.Nodes, 2)
|
||||
// Has() returns the high-level .container-fluid div, and the one that is the immediate parent of center-content
|
||||
}
|
||||
|
||||
func TestHasRollback(t *testing.T) {
|
||||
sel := Doc().Find(".container-fluid")
|
||||
sel2 := sel.Has(".center-content").End()
|
||||
assertEqual(t, sel, sel2)
|
||||
}
|
||||
|
||||
func TestHasNodes(t *testing.T) {
|
||||
sel := Doc().Find(".container-fluid")
|
||||
sel2 := Doc().Find(".center-content")
|
||||
sel = sel.HasNodes(sel2.Nodes...)
|
||||
assertLength(t, sel.Nodes, 2)
|
||||
// Has() returns the high-level .container-fluid div, and the one that is the immediate parent of center-content
|
||||
}
|
||||
|
||||
func TestHasNodesRollback(t *testing.T) {
|
||||
sel := Doc().Find(".container-fluid")
|
||||
sel2 := Doc().Find(".center-content")
|
||||
sel2 = sel.HasNodes(sel2.Nodes...).End()
|
||||
assertEqual(t, sel, sel2)
|
||||
}
|
||||
|
||||
func TestHasSelection(t *testing.T) {
|
||||
sel := Doc().Find("p")
|
||||
sel2 := Doc().Find("small")
|
||||
sel = sel.HasSelection(sel2)
|
||||
assertLength(t, sel.Nodes, 1)
|
||||
}
|
||||
|
||||
func TestHasSelectionRollback(t *testing.T) {
|
||||
sel := Doc().Find("p")
|
||||
sel2 := Doc().Find("small")
|
||||
sel2 = sel.HasSelection(sel2).End()
|
||||
assertEqual(t, sel, sel2)
|
||||
}
|
||||
|
||||
func TestEnd(t *testing.T) {
|
||||
sel := Doc().Find("p").Has("small").End()
|
||||
assertLength(t, sel.Nodes, 4)
|
||||
}
|
||||
|
||||
func TestEndToTop(t *testing.T) {
|
||||
sel := Doc().Find("p").Has("small").End().End().End()
|
||||
assertLength(t, sel.Nodes, 0)
|
||||
}
|
88
vendor/github.com/PuerkitoBio/goquery/iteration_test.go
generated
vendored
Normal file
88
vendor/github.com/PuerkitoBio/goquery/iteration_test.go
generated
vendored
Normal file
|
@ -0,0 +1,88 @@
|
|||
package goquery
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"golang.org/x/net/html"
|
||||
)
|
||||
|
||||
func TestEach(t *testing.T) {
|
||||
var cnt int
|
||||
|
||||
sel := Doc().Find(".hero-unit .row-fluid").Each(func(i int, n *Selection) {
|
||||
cnt++
|
||||
t.Logf("At index %v, node %v", i, n.Nodes[0].Data)
|
||||
}).Find("a")
|
||||
|
||||
if cnt != 4 {
|
||||
t.Errorf("Expected Each() to call function 4 times, got %v times.", cnt)
|
||||
}
|
||||
assertLength(t, sel.Nodes, 6)
|
||||
}
|
||||
|
||||
func TestEachWithBreak(t *testing.T) {
|
||||
var cnt int
|
||||
|
||||
sel := Doc().Find(".hero-unit .row-fluid").EachWithBreak(func(i int, n *Selection) bool {
|
||||
cnt++
|
||||
t.Logf("At index %v, node %v", i, n.Nodes[0].Data)
|
||||
return false
|
||||
}).Find("a")
|
||||
|
||||
if cnt != 1 {
|
||||
t.Errorf("Expected Each() to call function 1 time, got %v times.", cnt)
|
||||
}
|
||||
assertLength(t, sel.Nodes, 6)
|
||||
}
|
||||
|
||||
func TestEachEmptySelection(t *testing.T) {
|
||||
var cnt int
|
||||
|
||||
sel := Doc().Find("zzzz")
|
||||
sel.Each(func(i int, n *Selection) {
|
||||
cnt++
|
||||
})
|
||||
if cnt > 0 {
|
||||
t.Error("Expected Each() to not be called on empty Selection.")
|
||||
}
|
||||
sel2 := sel.Find("div")
|
||||
assertLength(t, sel2.Nodes, 0)
|
||||
}
|
||||
|
||||
func TestMap(t *testing.T) {
|
||||
sel := Doc().Find(".pvk-content")
|
||||
vals := sel.Map(func(i int, s *Selection) string {
|
||||
n := s.Get(0)
|
||||
if n.Type == html.ElementNode {
|
||||
return n.Data
|
||||
}
|
||||
return ""
|
||||
})
|
||||
for _, v := range vals {
|
||||
if v != "div" {
|
||||
t.Error("Expected Map array result to be all 'div's.")
|
||||
}
|
||||
}
|
||||
if len(vals) != 3 {
|
||||
t.Errorf("Expected Map array result to have a length of 3, found %v.", len(vals))
|
||||
}
|
||||
}
|
||||
|
||||
func TestForRange(t *testing.T) {
|
||||
sel := Doc().Find(".pvk-content")
|
||||
initLen := sel.Length()
|
||||
for i := range sel.Nodes {
|
||||
single := sel.Eq(i)
|
||||
//h, err := single.Html()
|
||||
//if err != nil {
|
||||
// t.Fatal(err)
|
||||
//}
|
||||
//fmt.Println(i, h)
|
||||
if single.Length() != 1 {
|
||||
t.Errorf("%d: expected length of 1, got %d", i, single.Length())
|
||||
}
|
||||
}
|
||||
if sel.Length() != initLen {
|
||||
t.Errorf("expected initial selection to still have length %d, got %d", initLen, sel.Length())
|
||||
}
|
||||
}
|
453
vendor/github.com/PuerkitoBio/goquery/manipulation_test.go
generated
vendored
Normal file
453
vendor/github.com/PuerkitoBio/goquery/manipulation_test.go
generated
vendored
Normal file
|
@ -0,0 +1,453 @@
|
|||
package goquery
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
const (
|
||||
wrapHtml = "<div id=\"ins\">test string<div><p><em><b></b></em></p></div></div>"
|
||||
)
|
||||
|
||||
func TestAfter(t *testing.T) {
|
||||
doc := Doc2Clone()
|
||||
doc.Find("#main").After("#nf6")
|
||||
|
||||
assertLength(t, doc.Find("#main #nf6").Nodes, 0)
|
||||
assertLength(t, doc.Find("#foot #nf6").Nodes, 0)
|
||||
assertLength(t, doc.Find("#main + #nf6").Nodes, 1)
|
||||
printSel(t, doc.Selection)
|
||||
}
|
||||
|
||||
func TestAfterMany(t *testing.T) {
|
||||
doc := Doc2Clone()
|
||||
doc.Find(".one").After("#nf6")
|
||||
|
||||
assertLength(t, doc.Find("#foot #nf6").Nodes, 1)
|
||||
assertLength(t, doc.Find("#main #nf6").Nodes, 1)
|
||||
assertLength(t, doc.Find(".one + #nf6").Nodes, 2)
|
||||
printSel(t, doc.Selection)
|
||||
}
|
||||
|
||||
func TestAfterWithRemoved(t *testing.T) {
|
||||
doc := Doc2Clone()
|
||||
s := doc.Find("#main").Remove()
|
||||
s.After("#nf6")
|
||||
|
||||
assertLength(t, s.Find("#nf6").Nodes, 0)
|
||||
assertLength(t, doc.Find("#nf6").Nodes, 0)
|
||||
printSel(t, doc.Selection)
|
||||
}
|
||||
|
||||
func TestAfterSelection(t *testing.T) {
|
||||
doc := Doc2Clone()
|
||||
doc.Find("#main").AfterSelection(doc.Find("#nf1, #nf2"))
|
||||
|
||||
assertLength(t, doc.Find("#main #nf1, #main #nf2").Nodes, 0)
|
||||
assertLength(t, doc.Find("#foot #nf1, #foot #nf2").Nodes, 0)
|
||||
assertLength(t, doc.Find("#main + #nf1, #nf1 + #nf2").Nodes, 2)
|
||||
printSel(t, doc.Selection)
|
||||
}
|
||||
|
||||
func TestAfterHtml(t *testing.T) {
|
||||
doc := Doc2Clone()
|
||||
doc.Find("#main").AfterHtml("<strong>new node</strong>")
|
||||
|
||||
assertLength(t, doc.Find("#main + strong").Nodes, 1)
|
||||
printSel(t, doc.Selection)
|
||||
}
|
||||
|
||||
func TestAppend(t *testing.T) {
|
||||
doc := Doc2Clone()
|
||||
doc.Find("#main").Append("#nf6")
|
||||
|
||||
assertLength(t, doc.Find("#foot #nf6").Nodes, 0)
|
||||
assertLength(t, doc.Find("#main #nf6").Nodes, 1)
|
||||
printSel(t, doc.Selection)
|
||||
}
|
||||
|
||||
func TestAppendBody(t *testing.T) {
|
||||
doc := Doc2Clone()
|
||||
doc.Find("body").Append("#nf6")
|
||||
|
||||
assertLength(t, doc.Find("#foot #nf6").Nodes, 0)
|
||||
assertLength(t, doc.Find("#main #nf6").Nodes, 0)
|
||||
assertLength(t, doc.Find("body > #nf6").Nodes, 1)
|
||||
printSel(t, doc.Selection)
|
||||
}
|
||||
|
||||
func TestAppendSelection(t *testing.T) {
|
||||
doc := Doc2Clone()
|
||||
doc.Find("#main").AppendSelection(doc.Find("#nf1, #nf2"))
|
||||
|
||||
assertLength(t, doc.Find("#foot #nf1").Nodes, 0)
|
||||
assertLength(t, doc.Find("#foot #nf2").Nodes, 0)
|
||||
assertLength(t, doc.Find("#main #nf1").Nodes, 1)
|
||||
assertLength(t, doc.Find("#main #nf2").Nodes, 1)
|
||||
printSel(t, doc.Selection)
|
||||
}
|
||||
|
||||
func TestAppendSelectionExisting(t *testing.T) {
|
||||
doc := Doc2Clone()
|
||||
doc.Find("#main").AppendSelection(doc.Find("#n1, #n2"))
|
||||
|
||||
assertClass(t, doc.Find("#main :nth-child(1)"), "three")
|
||||
assertClass(t, doc.Find("#main :nth-child(5)"), "one")
|
||||
assertClass(t, doc.Find("#main :nth-child(6)"), "two")
|
||||
printSel(t, doc.Selection)
|
||||
}
|
||||
|
||||
func TestAppendClone(t *testing.T) {
|
||||
doc := Doc2Clone()
|
||||
doc.Find("#n1").AppendSelection(doc.Find("#nf1").Clone())
|
||||
|
||||
assertLength(t, doc.Find("#foot #nf1").Nodes, 1)
|
||||
assertLength(t, doc.Find("#main #nf1").Nodes, 1)
|
||||
printSel(t, doc.Selection)
|
||||
}
|
||||
|
||||
func TestAppendHtml(t *testing.T) {
|
||||
doc := Doc2Clone()
|
||||
doc.Find("div").AppendHtml("<strong>new node</strong>")
|
||||
|
||||
assertLength(t, doc.Find("strong").Nodes, 14)
|
||||
printSel(t, doc.Selection)
|
||||
}
|
||||
|
||||
func TestBefore(t *testing.T) {
|
||||
doc := Doc2Clone()
|
||||
doc.Find("#main").Before("#nf6")
|
||||
|
||||
assertLength(t, doc.Find("#main #nf6").Nodes, 0)
|
||||
assertLength(t, doc.Find("#foot #nf6").Nodes, 0)
|
||||
assertLength(t, doc.Find("body > #nf6:first-child").Nodes, 1)
|
||||
printSel(t, doc.Selection)
|
||||
}
|
||||
|
||||
func TestBeforeWithRemoved(t *testing.T) {
|
||||
doc := Doc2Clone()
|
||||
s := doc.Find("#main").Remove()
|
||||
s.Before("#nf6")
|
||||
|
||||
assertLength(t, s.Find("#nf6").Nodes, 0)
|
||||
assertLength(t, doc.Find("#nf6").Nodes, 0)
|
||||
printSel(t, doc.Selection)
|
||||
}
|
||||
|
||||
func TestBeforeSelection(t *testing.T) {
|
||||
doc := Doc2Clone()
|
||||
doc.Find("#main").BeforeSelection(doc.Find("#nf1, #nf2"))
|
||||
|
||||
assertLength(t, doc.Find("#main #nf1, #main #nf2").Nodes, 0)
|
||||
assertLength(t, doc.Find("#foot #nf1, #foot #nf2").Nodes, 0)
|
||||
assertLength(t, doc.Find("body > #nf1:first-child, #nf1 + #nf2").Nodes, 2)
|
||||
printSel(t, doc.Selection)
|
||||
}
|
||||
|
||||
func TestBeforeHtml(t *testing.T) {
|
||||
doc := Doc2Clone()
|
||||
doc.Find("#main").BeforeHtml("<strong>new node</strong>")
|
||||
|
||||
assertLength(t, doc.Find("body > strong:first-child").Nodes, 1)
|
||||
printSel(t, doc.Selection)
|
||||
}
|
||||
|
||||
func TestEmpty(t *testing.T) {
|
||||
doc := Doc2Clone()
|
||||
s := doc.Find("#main").Empty()
|
||||
|
||||
assertLength(t, doc.Find("#main").Children().Nodes, 0)
|
||||
assertLength(t, s.Filter("div").Nodes, 6)
|
||||
printSel(t, doc.Selection)
|
||||
}
|
||||
|
||||
func TestPrepend(t *testing.T) {
|
||||
doc := Doc2Clone()
|
||||
doc.Find("#main").Prepend("#nf6")
|
||||
|
||||
assertLength(t, doc.Find("#foot #nf6").Nodes, 0)
|
||||
assertLength(t, doc.Find("#main #nf6:first-child").Nodes, 1)
|
||||
printSel(t, doc.Selection)
|
||||
}
|
||||
|
||||
func TestPrependBody(t *testing.T) {
|
||||
doc := Doc2Clone()
|
||||
doc.Find("body").Prepend("#nf6")
|
||||
|
||||
assertLength(t, doc.Find("#foot #nf6").Nodes, 0)
|
||||
assertLength(t, doc.Find("#main #nf6").Nodes, 0)
|
||||
assertLength(t, doc.Find("body > #nf6:first-child").Nodes, 1)
|
||||
printSel(t, doc.Selection)
|
||||
}
|
||||
|
||||
func TestPrependSelection(t *testing.T) {
|
||||
doc := Doc2Clone()
|
||||
doc.Find("#main").PrependSelection(doc.Find("#nf1, #nf2"))
|
||||
|
||||
assertLength(t, doc.Find("#foot #nf1").Nodes, 0)
|
||||
assertLength(t, doc.Find("#foot #nf2").Nodes, 0)
|
||||
assertLength(t, doc.Find("#main #nf1:first-child").Nodes, 1)
|
||||
assertLength(t, doc.Find("#main #nf2:nth-child(2)").Nodes, 1)
|
||||
printSel(t, doc.Selection)
|
||||
}
|
||||
|
||||
func TestPrependSelectionExisting(t *testing.T) {
|
||||
doc := Doc2Clone()
|
||||
doc.Find("#main").PrependSelection(doc.Find("#n5, #n6"))
|
||||
|
||||
assertClass(t, doc.Find("#main :nth-child(1)"), "five")
|
||||
assertClass(t, doc.Find("#main :nth-child(2)"), "six")
|
||||
assertClass(t, doc.Find("#main :nth-child(5)"), "three")
|
||||
assertClass(t, doc.Find("#main :nth-child(6)"), "four")
|
||||
printSel(t, doc.Selection)
|
||||
}
|
||||
|
||||
func TestPrependClone(t *testing.T) {
|
||||
doc := Doc2Clone()
|
||||
doc.Find("#n1").PrependSelection(doc.Find("#nf1").Clone())
|
||||
|
||||
assertLength(t, doc.Find("#foot #nf1:first-child").Nodes, 1)
|
||||
assertLength(t, doc.Find("#main #nf1:first-child").Nodes, 1)
|
||||
printSel(t, doc.Selection)
|
||||
}
|
||||
|
||||
func TestPrependHtml(t *testing.T) {
|
||||
doc := Doc2Clone()
|
||||
doc.Find("div").PrependHtml("<strong>new node</strong>")
|
||||
|
||||
assertLength(t, doc.Find("strong:first-child").Nodes, 14)
|
||||
printSel(t, doc.Selection)
|
||||
}
|
||||
|
||||
func TestRemove(t *testing.T) {
|
||||
doc := Doc2Clone()
|
||||
doc.Find("#nf1").Remove()
|
||||
|
||||
assertLength(t, doc.Find("#foot #nf1").Nodes, 0)
|
||||
printSel(t, doc.Selection)
|
||||
}
|
||||
|
||||
func TestRemoveAll(t *testing.T) {
|
||||
doc := Doc2Clone()
|
||||
doc.Find("*").Remove()
|
||||
|
||||
assertLength(t, doc.Find("*").Nodes, 0)
|
||||
printSel(t, doc.Selection)
|
||||
}
|
||||
|
||||
func TestRemoveRoot(t *testing.T) {
|
||||
doc := Doc2Clone()
|
||||
doc.Find("html").Remove()
|
||||
|
||||
assertLength(t, doc.Find("html").Nodes, 0)
|
||||
printSel(t, doc.Selection)
|
||||
}
|
||||
|
||||
func TestRemoveFiltered(t *testing.T) {
|
||||
doc := Doc2Clone()
|
||||
nf6 := doc.Find("#nf6")
|
||||
s := doc.Find("div").RemoveFiltered("#nf6")
|
||||
|
||||
assertLength(t, doc.Find("#nf6").Nodes, 0)
|
||||
assertLength(t, s.Nodes, 1)
|
||||
if nf6.Nodes[0] != s.Nodes[0] {
|
||||
t.Error("Removed node does not match original")
|
||||
}
|
||||
printSel(t, doc.Selection)
|
||||
}
|
||||
|
||||
func TestReplaceWith(t *testing.T) {
|
||||
doc := Doc2Clone()
|
||||
|
||||
doc.Find("#nf6").ReplaceWith("#main")
|
||||
assertLength(t, doc.Find("#foot #main:last-child").Nodes, 1)
|
||||
printSel(t, doc.Selection)
|
||||
|
||||
doc.Find("#foot").ReplaceWith("#main")
|
||||
assertLength(t, doc.Find("#foot").Nodes, 0)
|
||||
assertLength(t, doc.Find("#main").Nodes, 1)
|
||||
|
||||
printSel(t, doc.Selection)
|
||||
}
|
||||
|
||||
func TestReplaceWithHtml(t *testing.T) {
|
||||
doc := Doc2Clone()
|
||||
doc.Find("#main, #foot").ReplaceWithHtml("<div id=\"replace\"></div>")
|
||||
|
||||
assertLength(t, doc.Find("#replace").Nodes, 2)
|
||||
|
||||
printSel(t, doc.Selection)
|
||||
}
|
||||
|
||||
func TestReplaceWithSelection(t *testing.T) {
|
||||
doc := Doc2Clone()
|
||||
sel := doc.Find("#nf6").ReplaceWithSelection(doc.Find("#nf5"))
|
||||
|
||||
assertSelectionIs(t, sel, "#nf6")
|
||||
assertLength(t, doc.Find("#nf6").Nodes, 0)
|
||||
assertLength(t, doc.Find("#nf5").Nodes, 1)
|
||||
|
||||
printSel(t, doc.Selection)
|
||||
}
|
||||
|
||||
func TestUnwrap(t *testing.T) {
|
||||
doc := Doc2Clone()
|
||||
|
||||
doc.Find("#nf5").Unwrap()
|
||||
assertLength(t, doc.Find("#foot").Nodes, 0)
|
||||
assertLength(t, doc.Find("body > #nf1").Nodes, 1)
|
||||
assertLength(t, doc.Find("body > #nf5").Nodes, 1)
|
||||
|
||||
printSel(t, doc.Selection)
|
||||
|
||||
doc = Doc2Clone()
|
||||
|
||||
doc.Find("#nf5, #n1").Unwrap()
|
||||
assertLength(t, doc.Find("#foot").Nodes, 0)
|
||||
assertLength(t, doc.Find("#main").Nodes, 0)
|
||||
assertLength(t, doc.Find("body > #n1").Nodes, 1)
|
||||
assertLength(t, doc.Find("body > #nf5").Nodes, 1)
|
||||
|
||||
printSel(t, doc.Selection)
|
||||
}
|
||||
|
||||
func TestUnwrapBody(t *testing.T) {
|
||||
doc := Doc2Clone()
|
||||
|
||||
doc.Find("#main").Unwrap()
|
||||
assertLength(t, doc.Find("body").Nodes, 1)
|
||||
assertLength(t, doc.Find("body > #main").Nodes, 1)
|
||||
|
||||
printSel(t, doc.Selection)
|
||||
}
|
||||
|
||||
func TestUnwrapHead(t *testing.T) {
|
||||
doc := Doc2Clone()
|
||||
|
||||
doc.Find("title").Unwrap()
|
||||
assertLength(t, doc.Find("head").Nodes, 0)
|
||||
assertLength(t, doc.Find("head > title").Nodes, 0)
|
||||
assertLength(t, doc.Find("title").Nodes, 1)
|
||||
|
||||
printSel(t, doc.Selection)
|
||||
}
|
||||
|
||||
func TestUnwrapHtml(t *testing.T) {
|
||||
doc := Doc2Clone()
|
||||
|
||||
doc.Find("head").Unwrap()
|
||||
assertLength(t, doc.Find("html").Nodes, 0)
|
||||
assertLength(t, doc.Find("html head").Nodes, 0)
|
||||
assertLength(t, doc.Find("head").Nodes, 1)
|
||||
|
||||
printSel(t, doc.Selection)
|
||||
}
|
||||
|
||||
func TestWrap(t *testing.T) {
|
||||
doc := Doc2Clone()
|
||||
doc.Find("#nf1").Wrap("#nf2")
|
||||
nf1 := doc.Find("#foot #nf2 #nf1")
|
||||
assertLength(t, nf1.Nodes, 1)
|
||||
|
||||
nf2 := doc.Find("#nf2")
|
||||
assertLength(t, nf2.Nodes, 2)
|
||||
|
||||
printSel(t, doc.Selection)
|
||||
}
|
||||
|
||||
func TestWrapEmpty(t *testing.T) {
|
||||
doc := Doc2Clone()
|
||||
doc.Find("#nf1").Wrap("#doesnt-exist")
|
||||
|
||||
origHtml, _ := Doc2().Html()
|
||||
newHtml, _ := doc.Html()
|
||||
|
||||
if origHtml != newHtml {
|
||||
t.Error("Expected the two documents to be identical.")
|
||||
}
|
||||
|
||||
printSel(t, doc.Selection)
|
||||
}
|
||||
|
||||
func TestWrapHtml(t *testing.T) {
|
||||
doc := Doc2Clone()
|
||||
doc.Find(".odd").WrapHtml(wrapHtml)
|
||||
nf2 := doc.Find("#ins #nf2")
|
||||
assertLength(t, nf2.Nodes, 1)
|
||||
printSel(t, doc.Selection)
|
||||
}
|
||||
|
||||
func TestWrapSelection(t *testing.T) {
|
||||
doc := Doc2Clone()
|
||||
doc.Find("#nf1").WrapSelection(doc.Find("#nf2"))
|
||||
nf1 := doc.Find("#foot #nf2 #nf1")
|
||||
assertLength(t, nf1.Nodes, 1)
|
||||
|
||||
nf2 := doc.Find("#nf2")
|
||||
assertLength(t, nf2.Nodes, 2)
|
||||
|
||||
printSel(t, doc.Selection)
|
||||
}
|
||||
|
||||
func TestWrapAll(t *testing.T) {
|
||||
doc := Doc2Clone()
|
||||
doc.Find(".odd").WrapAll("#nf1")
|
||||
nf1 := doc.Find("#main #nf1")
|
||||
assertLength(t, nf1.Nodes, 1)
|
||||
|
||||
sel := nf1.Find("#n2 ~ #n4 ~ #n6 ~ #nf2 ~ #nf4 ~ #nf6")
|
||||
assertLength(t, sel.Nodes, 1)
|
||||
|
||||
printSel(t, doc.Selection)
|
||||
}
|
||||
|
||||
func TestWrapAllHtml(t *testing.T) {
|
||||
doc := Doc2Clone()
|
||||
doc.Find(".odd").WrapAllHtml(wrapHtml)
|
||||
nf1 := doc.Find("#main div#ins div p em b #n2 ~ #n4 ~ #n6 ~ #nf2 ~ #nf4 ~ #nf6")
|
||||
assertLength(t, nf1.Nodes, 1)
|
||||
printSel(t, doc.Selection)
|
||||
}
|
||||
|
||||
func TestWrapInnerNoContent(t *testing.T) {
|
||||
doc := Doc2Clone()
|
||||
doc.Find(".one").WrapInner(".two")
|
||||
|
||||
twos := doc.Find(".two")
|
||||
assertLength(t, twos.Nodes, 4)
|
||||
assertLength(t, doc.Find(".one .two").Nodes, 2)
|
||||
|
||||
printSel(t, doc.Selection)
|
||||
}
|
||||
|
||||
func TestWrapInnerWithContent(t *testing.T) {
|
||||
doc := Doc3Clone()
|
||||
doc.Find(".one").WrapInner(".two")
|
||||
|
||||
twos := doc.Find(".two")
|
||||
assertLength(t, twos.Nodes, 4)
|
||||
assertLength(t, doc.Find(".one .two").Nodes, 2)
|
||||
|
||||
printSel(t, doc.Selection)
|
||||
}
|
||||
|
||||
func TestWrapInnerNoWrapper(t *testing.T) {
|
||||
doc := Doc2Clone()
|
||||
doc.Find(".one").WrapInner(".not-exist")
|
||||
|
||||
twos := doc.Find(".two")
|
||||
assertLength(t, twos.Nodes, 2)
|
||||
assertLength(t, doc.Find(".one").Nodes, 2)
|
||||
assertLength(t, doc.Find(".one .two").Nodes, 0)
|
||||
|
||||
printSel(t, doc.Selection)
|
||||
}
|
||||
|
||||
func TestWrapInnerHtml(t *testing.T) {
|
||||
doc := Doc2Clone()
|
||||
doc.Find("#foot").WrapInnerHtml(wrapHtml)
|
||||
|
||||
foot := doc.Find("#foot div#ins div p em b #nf1 ~ #nf2 ~ #nf3")
|
||||
assertLength(t, foot.Nodes, 1)
|
||||
|
||||
printSel(t, doc.Selection)
|
||||
}
|
0
vendor/github.com/PuerkitoBio/goquery/misc/git/pre-commit
generated
vendored
Normal file → Executable file
0
vendor/github.com/PuerkitoBio/goquery/misc/git/pre-commit
generated
vendored
Normal file → Executable file
252
vendor/github.com/PuerkitoBio/goquery/property_test.go
generated
vendored
Normal file
252
vendor/github.com/PuerkitoBio/goquery/property_test.go
generated
vendored
Normal file
|
@ -0,0 +1,252 @@
|
|||
package goquery
|
||||
|
||||
import (
|
||||
"regexp"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestAttrExists(t *testing.T) {
|
||||
if val, ok := Doc().Find("a").Attr("href"); !ok {
|
||||
t.Error("Expected a value for the href attribute.")
|
||||
} else {
|
||||
t.Logf("Href of first anchor: %v.", val)
|
||||
}
|
||||
}
|
||||
|
||||
func TestAttrOr(t *testing.T) {
|
||||
if val := Doc().Find("a").AttrOr("fake-attribute", "alternative"); val != "alternative" {
|
||||
t.Error("Expected an alternative value for 'fake-attribute' attribute.")
|
||||
} else {
|
||||
t.Logf("Value returned for not existing attribute: %v.", val)
|
||||
}
|
||||
if val := Doc().Find("zz").AttrOr("fake-attribute", "alternative"); val != "alternative" {
|
||||
t.Error("Expected an alternative value for 'fake-attribute' on an empty selection.")
|
||||
} else {
|
||||
t.Logf("Value returned for empty selection: %v.", val)
|
||||
}
|
||||
}
|
||||
|
||||
func TestAttrNotExist(t *testing.T) {
|
||||
if val, ok := Doc().Find("div.row-fluid").Attr("href"); ok {
|
||||
t.Errorf("Expected no value for the href attribute, got %v.", val)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRemoveAttr(t *testing.T) {
|
||||
sel := Doc2Clone().Find("div")
|
||||
|
||||
sel.RemoveAttr("id")
|
||||
|
||||
_, ok := sel.Attr("id")
|
||||
if ok {
|
||||
t.Error("Expected there to be no id attributes set")
|
||||
}
|
||||
}
|
||||
|
||||
func TestSetAttr(t *testing.T) {
|
||||
sel := Doc2Clone().Find("#main")
|
||||
|
||||
sel.SetAttr("id", "not-main")
|
||||
|
||||
val, ok := sel.Attr("id")
|
||||
if !ok {
|
||||
t.Error("Expected an id attribute on main")
|
||||
}
|
||||
|
||||
if val != "not-main" {
|
||||
t.Errorf("Expected an attribute id to be not-main, got %s", val)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSetAttr2(t *testing.T) {
|
||||
sel := Doc2Clone().Find("#main")
|
||||
|
||||
sel.SetAttr("foo", "bar")
|
||||
|
||||
val, ok := sel.Attr("foo")
|
||||
if !ok {
|
||||
t.Error("Expected an 'foo' attribute on main")
|
||||
}
|
||||
|
||||
if val != "bar" {
|
||||
t.Errorf("Expected an attribute 'foo' to be 'bar', got '%s'", val)
|
||||
}
|
||||
}
|
||||
|
||||
func TestText(t *testing.T) {
|
||||
txt := Doc().Find("h1").Text()
|
||||
if strings.Trim(txt, " \n\r\t") != "Provok.in" {
|
||||
t.Errorf("Expected text to be Provok.in, found %s.", txt)
|
||||
}
|
||||
}
|
||||
|
||||
func TestText2(t *testing.T) {
|
||||
txt := Doc().Find(".hero-unit .container-fluid .row-fluid:nth-child(1)").Text()
|
||||
if ok, e := regexp.MatchString(`^\s+Provok\.in\s+Prove your point.\s+$`, txt); !ok || e != nil {
|
||||
t.Errorf("Expected text to be Provok.in Prove your point., found %s.", txt)
|
||||
if e != nil {
|
||||
t.Logf("Error: %s.", e.Error())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestText3(t *testing.T) {
|
||||
txt := Doc().Find(".pvk-gutter").First().Text()
|
||||
// There's an character in there...
|
||||
if ok, e := regexp.MatchString(`^[\s\x{00A0}]+$`, txt); !ok || e != nil {
|
||||
t.Errorf("Expected spaces, found <%v>.", txt)
|
||||
if e != nil {
|
||||
t.Logf("Error: %s.", e.Error())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestHtml(t *testing.T) {
|
||||
txt, e := Doc().Find("h1").Html()
|
||||
if e != nil {
|
||||
t.Errorf("Error: %s.", e)
|
||||
}
|
||||
|
||||
if ok, e := regexp.MatchString(`^\s*<a href="/">Provok<span class="green">\.</span><span class="red">i</span>n</a>\s*$`, txt); !ok || e != nil {
|
||||
t.Errorf("Unexpected HTML content, found %s.", txt)
|
||||
if e != nil {
|
||||
t.Logf("Error: %s.", e.Error())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestNbsp(t *testing.T) {
|
||||
src := `<p>Some text</p>`
|
||||
d, err := NewDocumentFromReader(strings.NewReader(src))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
txt := d.Find("p").Text()
|
||||
ix := strings.Index(txt, "\u00a0")
|
||||
if ix != 4 {
|
||||
t.Errorf("Text: expected a non-breaking space at index 4, got %d", ix)
|
||||
}
|
||||
|
||||
h, err := d.Find("p").Html()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
ix = strings.Index(h, "\u00a0")
|
||||
if ix != 4 {
|
||||
t.Errorf("Html: expected a non-breaking space at index 4, got %d", ix)
|
||||
}
|
||||
}
|
||||
|
||||
func TestAddClass(t *testing.T) {
|
||||
sel := Doc2Clone().Find("#main")
|
||||
sel.AddClass("main main main")
|
||||
|
||||
// Make sure that class was only added once
|
||||
if a, ok := sel.Attr("class"); !ok || a != "main" {
|
||||
t.Error("Expected #main to have class main")
|
||||
}
|
||||
}
|
||||
|
||||
func TestAddClassSimilar(t *testing.T) {
|
||||
sel := Doc2Clone().Find("#nf5")
|
||||
sel.AddClass("odd")
|
||||
|
||||
assertClass(t, sel, "odd")
|
||||
assertClass(t, sel, "odder")
|
||||
printSel(t, sel.Parent())
|
||||
}
|
||||
|
||||
func TestAddEmptyClass(t *testing.T) {
|
||||
sel := Doc2Clone().Find("#main")
|
||||
sel.AddClass("")
|
||||
|
||||
// Make sure that class was only added once
|
||||
if a, ok := sel.Attr("class"); ok {
|
||||
t.Errorf("Expected #main to not to have a class, have: %s", a)
|
||||
}
|
||||
}
|
||||
|
||||
func TestAddClasses(t *testing.T) {
|
||||
sel := Doc2Clone().Find("#main")
|
||||
sel.AddClass("a b")
|
||||
|
||||
// Make sure that class was only added once
|
||||
if !sel.HasClass("a") || !sel.HasClass("b") {
|
||||
t.Errorf("#main does not have classes")
|
||||
}
|
||||
}
|
||||
|
||||
func TestHasClass(t *testing.T) {
|
||||
sel := Doc().Find("div")
|
||||
if !sel.HasClass("span12") {
|
||||
t.Error("Expected at least one div to have class span12.")
|
||||
}
|
||||
}
|
||||
|
||||
func TestHasClassNone(t *testing.T) {
|
||||
sel := Doc().Find("h2")
|
||||
if sel.HasClass("toto") {
|
||||
t.Error("Expected h1 to have no class.")
|
||||
}
|
||||
}
|
||||
|
||||
func TestHasClassNotFirst(t *testing.T) {
|
||||
sel := Doc().Find(".alert")
|
||||
if !sel.HasClass("alert-error") {
|
||||
t.Error("Expected .alert to also have class .alert-error.")
|
||||
}
|
||||
}
|
||||
|
||||
func TestRemoveClass(t *testing.T) {
|
||||
sel := Doc2Clone().Find("#nf1")
|
||||
sel.RemoveClass("one row")
|
||||
|
||||
if !sel.HasClass("even") || sel.HasClass("one") || sel.HasClass("row") {
|
||||
classes, _ := sel.Attr("class")
|
||||
t.Error("Expected #nf1 to have class even, has ", classes)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRemoveClassSimilar(t *testing.T) {
|
||||
sel := Doc2Clone().Find("#nf5, #nf6")
|
||||
assertLength(t, sel.Nodes, 2)
|
||||
|
||||
sel.RemoveClass("odd")
|
||||
assertClass(t, sel.Eq(0), "odder")
|
||||
printSel(t, sel)
|
||||
}
|
||||
|
||||
func TestRemoveAllClasses(t *testing.T) {
|
||||
sel := Doc2Clone().Find("#nf1")
|
||||
sel.RemoveClass()
|
||||
|
||||
if a, ok := sel.Attr("class"); ok {
|
||||
t.Error("All classes were not removed, has ", a)
|
||||
}
|
||||
|
||||
sel = Doc2Clone().Find("#main")
|
||||
sel.RemoveClass()
|
||||
if a, ok := sel.Attr("class"); ok {
|
||||
t.Error("All classes were not removed, has ", a)
|
||||
}
|
||||
}
|
||||
|
||||
func TestToggleClass(t *testing.T) {
|
||||
sel := Doc2Clone().Find("#nf1")
|
||||
|
||||
sel.ToggleClass("one")
|
||||
if sel.HasClass("one") {
|
||||
t.Error("Expected #nf1 to not have class one")
|
||||
}
|
||||
|
||||
sel.ToggleClass("one")
|
||||
if !sel.HasClass("one") {
|
||||
t.Error("Expected #nf1 to have class one")
|
||||
}
|
||||
|
||||
sel.ToggleClass("one even row")
|
||||
if a, ok := sel.Attr("class"); ok {
|
||||
t.Errorf("Expected #nf1 to have no classes, have %q", a)
|
||||
}
|
||||
}
|
96
vendor/github.com/PuerkitoBio/goquery/query_test.go
generated
vendored
Normal file
96
vendor/github.com/PuerkitoBio/goquery/query_test.go
generated
vendored
Normal file
|
@ -0,0 +1,96 @@
|
|||
package goquery
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestIs(t *testing.T) {
|
||||
sel := Doc().Find(".footer p:nth-child(1)")
|
||||
if !sel.Is("p") {
|
||||
t.Error("Expected .footer p:nth-child(1) to be p.")
|
||||
}
|
||||
}
|
||||
|
||||
func TestIsPositional(t *testing.T) {
|
||||
sel := Doc().Find(".footer p:nth-child(2)")
|
||||
if !sel.Is("p:nth-child(2)") {
|
||||
t.Error("Expected .footer p:nth-child(2) to be p:nth-child(2).")
|
||||
}
|
||||
}
|
||||
|
||||
func TestIsPositionalNot(t *testing.T) {
|
||||
sel := Doc().Find(".footer p:nth-child(1)")
|
||||
if sel.Is("p:nth-child(2)") {
|
||||
t.Error("Expected .footer p:nth-child(1) NOT to be p:nth-child(2).")
|
||||
}
|
||||
}
|
||||
|
||||
func TestIsFunction(t *testing.T) {
|
||||
ok := Doc().Find("div").IsFunction(func(i int, s *Selection) bool {
|
||||
return s.HasClass("container-fluid")
|
||||
})
|
||||
|
||||
if !ok {
|
||||
t.Error("Expected some div to have a container-fluid class.")
|
||||
}
|
||||
}
|
||||
|
||||
func TestIsFunctionRollback(t *testing.T) {
|
||||
ok := Doc().Find("div").IsFunction(func(i int, s *Selection) bool {
|
||||
return s.HasClass("container-fluid")
|
||||
})
|
||||
|
||||
if !ok {
|
||||
t.Error("Expected some div to have a container-fluid class.")
|
||||
}
|
||||
}
|
||||
|
||||
func TestIsSelection(t *testing.T) {
|
||||
sel := Doc().Find("div")
|
||||
sel2 := Doc().Find(".pvk-gutter")
|
||||
|
||||
if !sel.IsSelection(sel2) {
|
||||
t.Error("Expected some div to have a pvk-gutter class.")
|
||||
}
|
||||
}
|
||||
|
||||
func TestIsSelectionNot(t *testing.T) {
|
||||
sel := Doc().Find("div")
|
||||
sel2 := Doc().Find("a")
|
||||
|
||||
if sel.IsSelection(sel2) {
|
||||
t.Error("Expected some div NOT to be an anchor.")
|
||||
}
|
||||
}
|
||||
|
||||
func TestIsNodes(t *testing.T) {
|
||||
sel := Doc().Find("div")
|
||||
sel2 := Doc().Find(".footer")
|
||||
|
||||
if !sel.IsNodes(sel2.Nodes[0]) {
|
||||
t.Error("Expected some div to have a footer class.")
|
||||
}
|
||||
}
|
||||
|
||||
func TestDocContains(t *testing.T) {
|
||||
sel := Doc().Find("h1")
|
||||
if !Doc().Contains(sel.Nodes[0]) {
|
||||
t.Error("Expected document to contain H1 tag.")
|
||||
}
|
||||
}
|
||||
|
||||
func TestSelContains(t *testing.T) {
|
||||
sel := Doc().Find(".row-fluid")
|
||||
sel2 := Doc().Find("a[ng-click]")
|
||||
if !sel.Contains(sel2.Nodes[0]) {
|
||||
t.Error("Expected .row-fluid to contain a[ng-click] tag.")
|
||||
}
|
||||
}
|
||||
|
||||
func TestSelNotContains(t *testing.T) {
|
||||
sel := Doc().Find("a.link")
|
||||
sel2 := Doc().Find("span")
|
||||
if sel.Contains(sel2.Nodes[0]) {
|
||||
t.Error("Expected a.link to NOT contain span tag.")
|
||||
}
|
||||
}
|
855
vendor/github.com/PuerkitoBio/goquery/testdata/gotesting.html
generated
vendored
Normal file
855
vendor/github.com/PuerkitoBio/goquery/testdata/gotesting.html
generated
vendored
Normal file
|
@ -0,0 +1,855 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
|
||||
|
||||
<title>testing - The Go Programming Language</title>
|
||||
|
||||
<link type="text/css" rel="stylesheet" href="/doc/style.css">
|
||||
<script type="text/javascript" src="/doc/godocs.js"></script>
|
||||
|
||||
<link rel="search" type="application/opensearchdescription+xml" title="godoc" href="/opensearch.xml" />
|
||||
|
||||
<script type="text/javascript">
|
||||
var _gaq = _gaq || [];
|
||||
_gaq.push(["_setAccount", "UA-11222381-2"]);
|
||||
_gaq.push(["_trackPageview"]);
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div id="topbar"><div class="container wide">
|
||||
|
||||
<form method="GET" action="/search">
|
||||
<div id="menu">
|
||||
<a href="/doc/">Documents</a>
|
||||
<a href="/ref/">References</a>
|
||||
<a href="/pkg/">Packages</a>
|
||||
<a href="/project/">The Project</a>
|
||||
<a href="/help/">Help</a>
|
||||
<input type="text" id="search" name="q" class="inactive" value="Search">
|
||||
</div>
|
||||
<div id="heading"><a href="/">The Go Programming Language</a></div>
|
||||
</form>
|
||||
|
||||
</div></div>
|
||||
|
||||
<div id="page" class="wide">
|
||||
|
||||
|
||||
<div id="plusone"><g:plusone size="small" annotation="none"></g:plusone></div>
|
||||
<h1>Package testing</h1>
|
||||
|
||||
|
||||
|
||||
|
||||
<div id="nav"></div>
|
||||
|
||||
|
||||
<!--
|
||||
Copyright 2009 The Go Authors. All rights reserved.
|
||||
Use of this source code is governed by a BSD-style
|
||||
license that can be found in the LICENSE file.
|
||||
-->
|
||||
|
||||
|
||||
<div id="short-nav">
|
||||
<dl>
|
||||
<dd><code>import "testing"</code></dd>
|
||||
</dl>
|
||||
<dl>
|
||||
<dd><a href="#overview" class="overviewLink">Overview</a></dd>
|
||||
<dd><a href="#index">Index</a></dd>
|
||||
|
||||
|
||||
<dd><a href="#subdirectories">Subdirectories</a></dd>
|
||||
|
||||
</dl>
|
||||
</div>
|
||||
<!-- The package's Name is printed as title by the top-level template -->
|
||||
<div id="overview" class="toggleVisible">
|
||||
<div class="collapsed">
|
||||
<h2 class="toggleButton" title="Click to show Overview section">Overview ▹</h2>
|
||||
</div>
|
||||
<div class="expanded">
|
||||
<h2 class="toggleButton" title="Click to hide Overview section">Overview ▾</h2>
|
||||
<p>
|
||||
Package testing provides support for automated testing of Go packages.
|
||||
It is intended to be used in concert with the “go test” command, which automates
|
||||
execution of any function of the form
|
||||
</p>
|
||||
<pre>func TestXxx(*testing.T)
|
||||
</pre>
|
||||
<p>
|
||||
where Xxx can be any alphanumeric string (but the first letter must not be in
|
||||
[a-z]) and serves to identify the test routine.
|
||||
These TestXxx routines should be declared within the package they are testing.
|
||||
</p>
|
||||
<p>
|
||||
Functions of the form
|
||||
</p>
|
||||
<pre>func BenchmarkXxx(*testing.B)
|
||||
</pre>
|
||||
<p>
|
||||
are considered benchmarks, and are executed by the "go test" command when
|
||||
the -test.bench flag is provided.
|
||||
</p>
|
||||
<p>
|
||||
A sample benchmark function looks like this:
|
||||
</p>
|
||||
<pre>func BenchmarkHello(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
fmt.Sprintf("hello")
|
||||
}
|
||||
}
|
||||
</pre>
|
||||
<p>
|
||||
The benchmark package will vary b.N until the benchmark function lasts
|
||||
long enough to be timed reliably. The output
|
||||
</p>
|
||||
<pre>testing.BenchmarkHello 10000000 282 ns/op
|
||||
</pre>
|
||||
<p>
|
||||
means that the loop ran 10000000 times at a speed of 282 ns per loop.
|
||||
</p>
|
||||
<p>
|
||||
If a benchmark needs some expensive setup before running, the timer
|
||||
may be stopped:
|
||||
</p>
|
||||
<pre>func BenchmarkBigLen(b *testing.B) {
|
||||
b.StopTimer()
|
||||
big := NewBig()
|
||||
b.StartTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
big.Len()
|
||||
}
|
||||
}
|
||||
</pre>
|
||||
<p>
|
||||
The package also runs and verifies example code. Example functions may
|
||||
include a concluding comment that begins with "Output:" and is compared with
|
||||
the standard output of the function when the tests are run, as in these
|
||||
examples of an example:
|
||||
</p>
|
||||
<pre>func ExampleHello() {
|
||||
fmt.Println("hello")
|
||||
// Output: hello
|
||||
}
|
||||
|
||||
func ExampleSalutations() {
|
||||
fmt.Println("hello, and")
|
||||
fmt.Println("goodbye")
|
||||
// Output:
|
||||
// hello, and
|
||||
// goodbye
|
||||
}
|
||||
</pre>
|
||||
<p>
|
||||
Example functions without output comments are compiled but not executed.
|
||||
</p>
|
||||
<p>
|
||||
The naming convention to declare examples for a function F, a type T and
|
||||
method M on type T are:
|
||||
</p>
|
||||
<pre>func ExampleF() { ... }
|
||||
func ExampleT() { ... }
|
||||
func ExampleT_M() { ... }
|
||||
</pre>
|
||||
<p>
|
||||
Multiple example functions for a type/function/method may be provided by
|
||||
appending a distinct suffix to the name. The suffix must start with a
|
||||
lower-case letter.
|
||||
</p>
|
||||
<pre>func ExampleF_suffix() { ... }
|
||||
func ExampleT_suffix() { ... }
|
||||
func ExampleT_M_suffix() { ... }
|
||||
</pre>
|
||||
<p>
|
||||
The entire test file is presented as the example when it contains a single
|
||||
example function, at least one other function, type, variable, or constant
|
||||
declaration, and no test or benchmark functions.
|
||||
</p>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<h2 id="index">Index</h2>
|
||||
<!-- Table of contents for API; must be named manual-nav to turn off auto nav. -->
|
||||
<div id="manual-nav">
|
||||
<dl>
|
||||
|
||||
|
||||
|
||||
|
||||
<dd><a href="#Main">func Main(matchString func(pat, str string) (bool, error), tests []InternalTest, benchmarks []InternalBenchmark, examples []InternalExample)</a></dd>
|
||||
|
||||
|
||||
<dd><a href="#RunBenchmarks">func RunBenchmarks(matchString func(pat, str string) (bool, error), benchmarks []InternalBenchmark)</a></dd>
|
||||
|
||||
|
||||
<dd><a href="#RunExamples">func RunExamples(matchString func(pat, str string) (bool, error), examples []InternalExample) (ok bool)</a></dd>
|
||||
|
||||
|
||||
<dd><a href="#RunTests">func RunTests(matchString func(pat, str string) (bool, error), tests []InternalTest) (ok bool)</a></dd>
|
||||
|
||||
|
||||
<dd><a href="#Short">func Short() bool</a></dd>
|
||||
|
||||
|
||||
|
||||
<dd><a href="#B">type B</a></dd>
|
||||
|
||||
|
||||
|
||||
<dd> <a href="#B.Error">func (c *B) Error(args ...interface{})</a></dd>
|
||||
|
||||
|
||||
<dd> <a href="#B.Errorf">func (c *B) Errorf(format string, args ...interface{})</a></dd>
|
||||
|
||||
|
||||
<dd> <a href="#B.Fail">func (c *B) Fail()</a></dd>
|
||||
|
||||
|
||||
<dd> <a href="#B.FailNow">func (c *B) FailNow()</a></dd>
|
||||
|
||||
|
||||
<dd> <a href="#B.Failed">func (c *B) Failed() bool</a></dd>
|
||||
|
||||
|
||||
<dd> <a href="#B.Fatal">func (c *B) Fatal(args ...interface{})</a></dd>
|
||||
|
||||
|
||||
<dd> <a href="#B.Fatalf">func (c *B) Fatalf(format string, args ...interface{})</a></dd>
|
||||
|
||||
|
||||
<dd> <a href="#B.Log">func (c *B) Log(args ...interface{})</a></dd>
|
||||
|
||||
|
||||
<dd> <a href="#B.Logf">func (c *B) Logf(format string, args ...interface{})</a></dd>
|
||||
|
||||
|
||||
<dd> <a href="#B.ResetTimer">func (b *B) ResetTimer()</a></dd>
|
||||
|
||||
|
||||
<dd> <a href="#B.SetBytes">func (b *B) SetBytes(n int64)</a></dd>
|
||||
|
||||
|
||||
<dd> <a href="#B.StartTimer">func (b *B) StartTimer()</a></dd>
|
||||
|
||||
|
||||
<dd> <a href="#B.StopTimer">func (b *B) StopTimer()</a></dd>
|
||||
|
||||
|
||||
|
||||
<dd><a href="#BenchmarkResult">type BenchmarkResult</a></dd>
|
||||
|
||||
|
||||
<dd> <a href="#Benchmark">func Benchmark(f func(b *B)) BenchmarkResult</a></dd>
|
||||
|
||||
|
||||
|
||||
<dd> <a href="#BenchmarkResult.NsPerOp">func (r BenchmarkResult) NsPerOp() int64</a></dd>
|
||||
|
||||
|
||||
<dd> <a href="#BenchmarkResult.String">func (r BenchmarkResult) String() string</a></dd>
|
||||
|
||||
|
||||
|
||||
<dd><a href="#InternalBenchmark">type InternalBenchmark</a></dd>
|
||||
|
||||
|
||||
|
||||
|
||||
<dd><a href="#InternalExample">type InternalExample</a></dd>
|
||||
|
||||
|
||||
|
||||
|
||||
<dd><a href="#InternalTest">type InternalTest</a></dd>
|
||||
|
||||
|
||||
|
||||
|
||||
<dd><a href="#T">type T</a></dd>
|
||||
|
||||
|
||||
|
||||
<dd> <a href="#T.Error">func (c *T) Error(args ...interface{})</a></dd>
|
||||
|
||||
|
||||
<dd> <a href="#T.Errorf">func (c *T) Errorf(format string, args ...interface{})</a></dd>
|
||||
|
||||
|
||||
<dd> <a href="#T.Fail">func (c *T) Fail()</a></dd>
|
||||
|
||||
|
||||
<dd> <a href="#T.FailNow">func (c *T) FailNow()</a></dd>
|
||||
|
||||
|
||||
<dd> <a href="#T.Failed">func (c *T) Failed() bool</a></dd>
|
||||
|
||||
|
||||
<dd> <a href="#T.Fatal">func (c *T) Fatal(args ...interface{})</a></dd>
|
||||
|
||||
|
||||
<dd> <a href="#T.Fatalf">func (c *T) Fatalf(format string, args ...interface{})</a></dd>
|
||||
|
||||
|
||||
<dd> <a href="#T.Log">func (c *T) Log(args ...interface{})</a></dd>
|
||||
|
||||
|
||||
<dd> <a href="#T.Logf">func (c *T) Logf(format string, args ...interface{})</a></dd>
|
||||
|
||||
|
||||
<dd> <a href="#T.Parallel">func (t *T) Parallel()</a></dd>
|
||||
|
||||
|
||||
|
||||
</dl>
|
||||
|
||||
|
||||
|
||||
|
||||
<h4>Package files</h4>
|
||||
<p>
|
||||
<span style="font-size:90%">
|
||||
|
||||
<a href="/src/pkg/testing/benchmark.go">benchmark.go</a>
|
||||
|
||||
<a href="/src/pkg/testing/example.go">example.go</a>
|
||||
|
||||
<a href="/src/pkg/testing/testing.go">testing.go</a>
|
||||
|
||||
</span>
|
||||
</p>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<h2 id="Main">func <a href="/src/pkg/testing/testing.go?s=9750:9890#L268">Main</a></h2>
|
||||
<pre>func Main(matchString func(pat, str string) (bool, error), tests []InternalTest, benchmarks []InternalBenchmark, examples []InternalExample)</pre>
|
||||
<p>
|
||||
An internal function but exported because it is cross-package; part of the implementation
|
||||
of the "go test" command.
|
||||
</p>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<h2 id="RunBenchmarks">func <a href="/src/pkg/testing/benchmark.go?s=5365:5464#L207">RunBenchmarks</a></h2>
|
||||
<pre>func RunBenchmarks(matchString func(pat, str string) (bool, error), benchmarks []InternalBenchmark)</pre>
|
||||
<p>
|
||||
An internal function but exported because it is cross-package; part of the implementation
|
||||
of the "go test" command.
|
||||
</p>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<h2 id="RunExamples">func <a href="/src/pkg/testing/example.go?s=314:417#L12">RunExamples</a></h2>
|
||||
<pre>func RunExamples(matchString func(pat, str string) (bool, error), examples []InternalExample) (ok bool)</pre>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<h2 id="RunTests">func <a href="/src/pkg/testing/testing.go?s=10486:10580#L297">RunTests</a></h2>
|
||||
<pre>func RunTests(matchString func(pat, str string) (bool, error), tests []InternalTest) (ok bool)</pre>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<h2 id="Short">func <a href="/src/pkg/testing/testing.go?s=4859:4876#L117">Short</a></h2>
|
||||
<pre>func Short() bool</pre>
|
||||
<p>
|
||||
Short reports whether the -test.short flag is set.
|
||||
</p>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<h2 id="B">type <a href="/src/pkg/testing/benchmark.go?s=743:872#L17">B</a></h2>
|
||||
<pre>type B struct {
|
||||
N int
|
||||
<span class="comment">// contains filtered or unexported fields</span>
|
||||
}</pre>
|
||||
<p>
|
||||
B is a type passed to Benchmark functions to manage benchmark
|
||||
timing and to specify the number of iterations to run.
|
||||
</p>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<h3 id="B.Error">func (*B) <a href="/src/pkg/testing/testing.go?s=8110:8153#L209">Error</a></h3>
|
||||
<pre>func (c *B) Error(args ...interface{})</pre>
|
||||
<p>
|
||||
Error is equivalent to Log() followed by Fail().
|
||||
</p>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<h3 id="B.Errorf">func (*B) <a href="/src/pkg/testing/testing.go?s=8253:8312#L215">Errorf</a></h3>
|
||||
<pre>func (c *B) Errorf(format string, args ...interface{})</pre>
|
||||
<p>
|
||||
Errorf is equivalent to Logf() followed by Fail().
|
||||
</p>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<h3 id="B.Fail">func (*B) <a href="/src/pkg/testing/testing.go?s=6270:6293#L163">Fail</a></h3>
|
||||
<pre>func (c *B) Fail()</pre>
|
||||
<p>
|
||||
Fail marks the function as having failed but continues execution.
|
||||
</p>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<h3 id="B.FailNow">func (*B) <a href="/src/pkg/testing/testing.go?s=6548:6574#L170">FailNow</a></h3>
|
||||
<pre>func (c *B) FailNow()</pre>
|
||||
<p>
|
||||
FailNow marks the function as having failed and stops its execution.
|
||||
Execution will continue at the next test or benchmark.
|
||||
</p>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<h3 id="B.Failed">func (*B) <a href="/src/pkg/testing/testing.go?s=6366:6396#L166">Failed</a></h3>
|
||||
<pre>func (c *B) Failed() bool</pre>
|
||||
<p>
|
||||
Failed returns whether the function has failed.
|
||||
</p>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<h3 id="B.Fatal">func (*B) <a href="/src/pkg/testing/testing.go?s=8420:8463#L221">Fatal</a></h3>
|
||||
<pre>func (c *B) Fatal(args ...interface{})</pre>
|
||||
<p>
|
||||
Fatal is equivalent to Log() followed by FailNow().
|
||||
</p>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<h3 id="B.Fatalf">func (*B) <a href="/src/pkg/testing/testing.go?s=8569:8628#L227">Fatalf</a></h3>
|
||||
<pre>func (c *B) Fatalf(format string, args ...interface{})</pre>
|
||||
<p>
|
||||
Fatalf is equivalent to Logf() followed by FailNow().
|
||||
</p>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<h3 id="B.Log">func (*B) <a href="/src/pkg/testing/testing.go?s=7763:7804#L202">Log</a></h3>
|
||||
<pre>func (c *B) Log(args ...interface{})</pre>
|
||||
<p>
|
||||
Log formats its arguments using default formatting, analogous to Println(),
|
||||
and records the text in the error log.
|
||||
</p>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<h3 id="B.Logf">func (*B) <a href="/src/pkg/testing/testing.go?s=7959:8016#L206">Logf</a></h3>
|
||||
<pre>func (c *B) Logf(format string, args ...interface{})</pre>
|
||||
<p>
|
||||
Logf formats its arguments according to the format, analogous to Printf(),
|
||||
and records the text in the error log.
|
||||
</p>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<h3 id="B.ResetTimer">func (*B) <a href="/src/pkg/testing/benchmark.go?s=1503:1527#L48">ResetTimer</a></h3>
|
||||
<pre>func (b *B) ResetTimer()</pre>
|
||||
<p>
|
||||
ResetTimer sets the elapsed benchmark time to zero.
|
||||
It does not affect whether the timer is running.
|
||||
</p>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<h3 id="B.SetBytes">func (*B) <a href="/src/pkg/testing/benchmark.go?s=1728:1757#L57">SetBytes</a></h3>
|
||||
<pre>func (b *B) SetBytes(n int64)</pre>
|
||||
<p>
|
||||
SetBytes records the number of bytes processed in a single operation.
|
||||
If this is called, the benchmark will report ns/op and MB/s.
|
||||
</p>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<h3 id="B.StartTimer">func (*B) <a href="/src/pkg/testing/benchmark.go?s=1047:1071#L29">StartTimer</a></h3>
|
||||
<pre>func (b *B) StartTimer()</pre>
|
||||
<p>
|
||||
StartTimer starts timing a test. This function is called automatically
|
||||
before a benchmark starts, but it can also used to resume timing after
|
||||
a call to StopTimer.
|
||||
</p>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<h3 id="B.StopTimer">func (*B) <a href="/src/pkg/testing/benchmark.go?s=1288:1311#L39">StopTimer</a></h3>
|
||||
<pre>func (b *B) StopTimer()</pre>
|
||||
<p>
|
||||
StopTimer stops timing a test. This can be used to pause the timer
|
||||
while performing complex initialization that you don't
|
||||
want to measure.
|
||||
</p>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<h2 id="BenchmarkResult">type <a href="/src/pkg/testing/benchmark.go?s=4206:4391#L165">BenchmarkResult</a></h2>
|
||||
<pre>type BenchmarkResult struct {
|
||||
N int <span class="comment">// The number of iterations.</span>
|
||||
T time.Duration <span class="comment">// The total time taken.</span>
|
||||
Bytes int64 <span class="comment">// Bytes processed in one iteration.</span>
|
||||
}</pre>
|
||||
<p>
|
||||
The results of a benchmark run.
|
||||
</p>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<h3 id="Benchmark">func <a href="/src/pkg/testing/benchmark.go?s=7545:7589#L275">Benchmark</a></h3>
|
||||
<pre>func Benchmark(f func(b *B)) BenchmarkResult</pre>
|
||||
<p>
|
||||
Benchmark benchmarks a single function. Useful for creating
|
||||
custom benchmarks that do not use the "go test" command.
|
||||
</p>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<h3 id="BenchmarkResult.NsPerOp">func (BenchmarkResult) <a href="/src/pkg/testing/benchmark.go?s=4393:4433#L171">NsPerOp</a></h3>
|
||||
<pre>func (r BenchmarkResult) NsPerOp() int64</pre>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<h3 id="BenchmarkResult.String">func (BenchmarkResult) <a href="/src/pkg/testing/benchmark.go?s=4677:4717#L185">String</a></h3>
|
||||
<pre>func (r BenchmarkResult) String() string</pre>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<h2 id="InternalBenchmark">type <a href="/src/pkg/testing/benchmark.go?s=555:618#L10">InternalBenchmark</a></h2>
|
||||
<pre>type InternalBenchmark struct {
|
||||
Name string
|
||||
F func(b *B)
|
||||
}</pre>
|
||||
<p>
|
||||
An internal type but exported because it is cross-package; part of the implementation
|
||||
of the "go test" command.
|
||||
</p>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<h2 id="InternalExample">type <a href="/src/pkg/testing/example.go?s=236:312#L6">InternalExample</a></h2>
|
||||
<pre>type InternalExample struct {
|
||||
Name string
|
||||
F func()
|
||||
Output string
|
||||
}</pre>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<h2 id="InternalTest">type <a href="/src/pkg/testing/testing.go?s=9065:9121#L241">InternalTest</a></h2>
|
||||
<pre>type InternalTest struct {
|
||||
Name string
|
||||
F func(*T)
|
||||
}</pre>
|
||||
<p>
|
||||
An internal type but exported because it is cross-package; part of the implementation
|
||||
of the "go test" command.
|
||||
</p>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<h2 id="T">type <a href="/src/pkg/testing/testing.go?s=6070:6199#L156">T</a></h2>
|
||||
<pre>type T struct {
|
||||
<span class="comment">// contains filtered or unexported fields</span>
|
||||
}</pre>
|
||||
<p>
|
||||
T is a type passed to Test functions to manage test state and support formatted test logs.
|
||||
Logs are accumulated during execution and dumped to standard error when done.
|
||||
</p>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<h3 id="T.Error">func (*T) <a href="/src/pkg/testing/testing.go?s=8110:8153#L209">Error</a></h3>
|
||||
<pre>func (c *T) Error(args ...interface{})</pre>
|
||||
<p>
|
||||
Error is equivalent to Log() followed by Fail().
|
||||
</p>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<h3 id="T.Errorf">func (*T) <a href="/src/pkg/testing/testing.go?s=8253:8312#L215">Errorf</a></h3>
|
||||
<pre>func (c *T) Errorf(format string, args ...interface{})</pre>
|
||||
<p>
|
||||
Errorf is equivalent to Logf() followed by Fail().
|
||||
</p>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<h3 id="T.Fail">func (*T) <a href="/src/pkg/testing/testing.go?s=6270:6293#L163">Fail</a></h3>
|
||||
<pre>func (c *T) Fail()</pre>
|
||||
<p>
|
||||
Fail marks the function as having failed but continues execution.
|
||||
</p>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<h3 id="T.FailNow">func (*T) <a href="/src/pkg/testing/testing.go?s=6548:6574#L170">FailNow</a></h3>
|
||||
<pre>func (c *T) FailNow()</pre>
|
||||
<p>
|
||||
FailNow marks the function as having failed and stops its execution.
|
||||
Execution will continue at the next test or benchmark.
|
||||
</p>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<h3 id="T.Failed">func (*T) <a href="/src/pkg/testing/testing.go?s=6366:6396#L166">Failed</a></h3>
|
||||
<pre>func (c *T) Failed() bool</pre>
|
||||
<p>
|
||||
Failed returns whether the function has failed.
|
||||
</p>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<h3 id="T.Fatal">func (*T) <a href="/src/pkg/testing/testing.go?s=8420:8463#L221">Fatal</a></h3>
|
||||
<pre>func (c *T) Fatal(args ...interface{})</pre>
|
||||
<p>
|
||||
Fatal is equivalent to Log() followed by FailNow().
|
||||
</p>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<h3 id="T.Fatalf">func (*T) <a href="/src/pkg/testing/testing.go?s=8569:8628#L227">Fatalf</a></h3>
|
||||
<pre>func (c *T) Fatalf(format string, args ...interface{})</pre>
|
||||
<p>
|
||||
Fatalf is equivalent to Logf() followed by FailNow().
|
||||
</p>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<h3 id="T.Log">func (*T) <a href="/src/pkg/testing/testing.go?s=7763:7804#L202">Log</a></h3>
|
||||
<pre>func (c *T) Log(args ...interface{})</pre>
|
||||
<p>
|
||||
Log formats its arguments using default formatting, analogous to Println(),
|
||||
and records the text in the error log.
|
||||
</p>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<h3 id="T.Logf">func (*T) <a href="/src/pkg/testing/testing.go?s=7959:8016#L206">Logf</a></h3>
|
||||
<pre>func (c *T) Logf(format string, args ...interface{})</pre>
|
||||
<p>
|
||||
Logf formats its arguments according to the format, analogous to Printf(),
|
||||
and records the text in the error log.
|
||||
</p>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<h3 id="T.Parallel">func (*T) <a href="/src/pkg/testing/testing.go?s=8809:8831#L234">Parallel</a></h3>
|
||||
<pre>func (t *T) Parallel()</pre>
|
||||
<p>
|
||||
Parallel signals that this test is to be run in parallel with (and only with)
|
||||
other parallel tests in this CPU group.
|
||||
</p>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<h2 id="subdirectories">Subdirectories</h2>
|
||||
|
||||
<table class="dir">
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
<th> </th>
|
||||
<th style="text-align: left; width: auto">Synopsis</th>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td><a href="..">..</a></td>
|
||||
</tr>
|
||||
|
||||
|
||||
|
||||
<tr>
|
||||
<td class="name"><a href="iotest">iotest</a></td>
|
||||
<td> </td>
|
||||
<td style="width: auto">Package iotest implements Readers and Writers useful mainly for testing.</td>
|
||||
</tr>
|
||||
|
||||
|
||||
|
||||
<tr>
|
||||
<td class="name"><a href="quick">quick</a></td>
|
||||
<td> </td>
|
||||
<td style="width: auto">Package quick implements utility functions to help with black box testing.</td>
|
||||
</tr>
|
||||
|
||||
|
||||
</table>
|
||||
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
<div id="footer">
|
||||
Build version go1.0.2.<br>
|
||||
Except as <a href="http://code.google.com/policies.html#restrictions">noted</a>,
|
||||
the content of this page is licensed under the
|
||||
Creative Commons Attribution 3.0 License,
|
||||
and code is licensed under a <a href="/LICENSE">BSD license</a>.<br>
|
||||
<a href="/doc/tos.html">Terms of Service</a> |
|
||||
<a href="http://www.google.com/intl/en/privacy/privacy-policy.html">Privacy Policy</a>
|
||||
</div>
|
||||
|
||||
<script type="text/javascript">
|
||||
(function() {
|
||||
var ga = document.createElement("script"); ga.type = "text/javascript"; ga.async = true;
|
||||
ga.src = ("https:" == document.location.protocol ? "https://ssl" : "http://www") + ".google-analytics.com/ga.js";
|
||||
var s = document.getElementsByTagName("script")[0]; s.parentNode.insertBefore(ga, s);
|
||||
})();
|
||||
</script>
|
||||
</body>
|
||||
<script type="text/javascript">
|
||||
(function() {
|
||||
var po = document.createElement('script'); po.type = 'text/javascript'; po.async = true;
|
||||
po.src = 'https://apis.google.com/js/plusone.js';
|
||||
var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(po, s);
|
||||
})();
|
||||
</script>
|
||||
</html>
|
||||
|
1214
vendor/github.com/PuerkitoBio/goquery/testdata/gowiki.html
generated
vendored
Normal file
1214
vendor/github.com/PuerkitoBio/goquery/testdata/gowiki.html
generated
vendored
Normal file
File diff suppressed because it is too large
Load diff
413
vendor/github.com/PuerkitoBio/goquery/testdata/metalreview.html
generated
vendored
Normal file
413
vendor/github.com/PuerkitoBio/goquery/testdata/metalreview.html
generated
vendored
Normal file
|
@ -0,0 +1,413 @@
|
|||
|
||||
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
||||
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" >
|
||||
<head><meta http-equiv="X-UA-Compatible" content="IE=8" />
|
||||
|
||||
<meta name="keywords" content="metal, reviews, metalreview, metalreviews, heavy, rock, review, music, blogs, forums, community" />
|
||||
<meta name="description" content="Critical heavy metal album and dvd reviews, written by professional writers. Large community with forums, blogs, photos and commenting system." />
|
||||
|
||||
<title>
|
||||
|
||||
|
||||
Metal Reviews, News, Blogs, Interviews and Community | Metal Review
|
||||
|
||||
|
||||
</title><link rel="stylesheet" type="text/css" href="/Content/Css/reset-fonts-grids.css" /><link rel="stylesheet" type="text/css" href="/Content/Css/base.css" /><link rel="stylesheet" type="text/css" href="/Content/Css/core.css" /><link rel="stylesheet" type="text/css" href="/Content/Css/wt-rotator.css" />
|
||||
<script src="/Scripts/jquery-1.4.2.min.js" type="text/javascript"></script>
|
||||
</head>
|
||||
<body>
|
||||
<script type="text/javascript">
|
||||
var _comscore = _comscore || [];
|
||||
_comscore.push({ c1: "2", c2: "9290245" });
|
||||
(function () {
|
||||
var s = document.createElement("script"), el = document.getElementsByTagName("script")[0]; s.async = true;
|
||||
s.src = (document.location.protocol == "https:" ? "https://sb" : "http://b") + ".scorecardresearch.com/beacon.js";
|
||||
el.parentNode.insertBefore(s, el);
|
||||
})();
|
||||
</script>
|
||||
<noscript>
|
||||
<img src="http://b.scorecardresearch.com/p?c1=2&c2=9290245&cv=2.0&cj=1" />
|
||||
</noscript>
|
||||
|
||||
|
||||
<div id="doc2" class="yui-t7">
|
||||
<div id="hd">
|
||||
|
||||
|
||||
<div id="main-logo"><a href="/" title="Home"><img src="/Content/Images/metal-review-logo.png" alt="Metal Review Home" border="0" /></a></div>
|
||||
<div id="leaderboard-banner">
|
||||
|
||||
<script language="javascript" type="text/javascript"><!--
|
||||
document.write('<scr' + 'ipt language="javascript1.1" src="http://adserver.adtechus.com/addyn/3.0/5110/73085/0/225/ADTECH;loc=100;target=_blank;key=key1+key2+key3+key4;grp=[group];misc=' + new Date().getTime() + '"></scri' + 'pt>');
|
||||
//-->
|
||||
</script>
|
||||
|
||||
<noscript>
|
||||
<a href="http://adserver.adtechus.com/adlink/3.0/5110/73085/0/225/ADTECH;loc=300;key=key1+key2+key3+key4;grp=[group]" target="_blank">
|
||||
<img src="http://adserver.adtechus.com/adserv/3.0/5110/73085/0/225/ADTECH;loc=300;key=key1+key2+key3+key4;grp=[group]" border="0" width="728" height="90" />
|
||||
</a>
|
||||
</noscript>
|
||||
</div>
|
||||
<div id="header-menu-container">
|
||||
<div id="header-menu">
|
||||
<a href="/reviews/browse">REVIEWS</a>
|
||||
<a href="http://community2.metalreview.com/blogs/editorials/default.aspx">FEATURES</a>
|
||||
<a href="/artists/browse">ARTISTS</a>
|
||||
<a href="/reviews/pipeline">PIPELINE</a>
|
||||
<a href="http://community2.metalreview.com/forums">FORUMS</a>
|
||||
<a href="http://community2.metalreview.com/blogs/">BLOGS</a>
|
||||
<a href="/aboutus">ABOUT US</a>
|
||||
</div>
|
||||
|
||||
<div id="sign-in"><a href="https://metalreview.com/account/signin">SIGN IN</a> | <a href="/account/register">JOIN US</a></div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<div id="bd">
|
||||
|
||||
<div id="yui-main">
|
||||
<div class="yui-b">
|
||||
<div class="yui-g">
|
||||
<div class="yui-u first">
|
||||
|
||||
|
||||
|
||||
<script src="/Scripts/jquery.wt-rotator.min.js" type="text/javascript"></script>
|
||||
<script src="/Scripts/jquery.wt-rotator-initialize.js" type="text/javascript"></script>
|
||||
<div id="review-showcase-wrapper">
|
||||
<h2 id="showcase-heading">Reviews</h2>
|
||||
<div id="review-showcase">
|
||||
<div class="container">
|
||||
<div class="wt-rotator">
|
||||
<a href="#"></a>
|
||||
<div class="desc">
|
||||
</div>
|
||||
<div class="preloader">
|
||||
</div>
|
||||
<div class="c-panel">
|
||||
<div class="buttons">
|
||||
<div class="prev-btn">
|
||||
</div>
|
||||
<div class="play-btn">
|
||||
</div>
|
||||
<div class="next-btn">
|
||||
</div>
|
||||
</div>
|
||||
<div class="thumbnails">
|
||||
<ul>
|
||||
|
||||
<li><a href="artist.photo?mrx=4641" title="Serpentine Path - Serpentine Path"></a><a href="/reviews/6844/serpentine-path-serpentine-path"></a>
|
||||
<p style="top: 130px; left: 22px; width: 305px; height:60px;">
|
||||
<img class="rotator-cover-art" src="album.cover?art=6844" alt='Serpentine Path - Serpentine Path' title='Serpentine Path - Serpentine Path' />
|
||||
<span class="title"><strong>Serpentine Path</strong></span><br />
|
||||
Serpentine Path<br />
|
||||
|
||||
</p>
|
||||
</li>
|
||||
|
||||
<li><a href="artist.photo?mrx=4635" title="Hunter's Ground - No God But the Wild"></a><a href="/reviews/6830/hunters-ground-no-god-but-the-wild"></a>
|
||||
<p style="top: 130px; left: 22px; width: 305px; height:60px;">
|
||||
<img class="rotator-cover-art" src="album.cover?art=6830" alt='Hunter's Ground - No God But the Wild' title='Hunter's Ground - No God But the Wild' />
|
||||
<span class="title"><strong>Hunter's Ground</strong></span><br />
|
||||
No God But the Wild<br />
|
||||
|
||||
</p>
|
||||
</li>
|
||||
|
||||
<li><a href="artist.photo?mrx=1035" title="Blut Aus Nord - 777 - Cosmosophy"></a><a href="/reviews/6829/blut-aus-nord-777---cosmosophy"></a>
|
||||
<p style="top: 130px; left: 22px; width: 305px; height:60px;">
|
||||
<img class="rotator-cover-art" src="album.cover?art=6829" alt='Blut Aus Nord - 777 - Cosmosophy' title='Blut Aus Nord - 777 - Cosmosophy' />
|
||||
<span class="title"><strong>Blut Aus Nord</strong></span><br />
|
||||
777 - Cosmosophy<br />
|
||||
<a href="/tags/10/black"><span class="tag">Black</span></a>
|
||||
</p>
|
||||
</li>
|
||||
|
||||
<li><a href="artist.photo?mrx=1217" title="Ufomammut - Oro: Opus Alter"></a><a href="/reviews/6835/ufomammut-oro--opus-alter"></a>
|
||||
<p style="top: 130px; left: 22px; width: 305px; height:60px;">
|
||||
<img class="rotator-cover-art" src="album.cover?art=6835" alt='Ufomammut - Oro: Opus Alter' title='Ufomammut - Oro: Opus Alter' />
|
||||
<span class="title"><strong>Ufomammut</strong></span><br />
|
||||
Oro: Opus Alter<br />
|
||||
<a href="/tags/2/doom"><span class="tag">Doom</span></a>
|
||||
</p>
|
||||
</li>
|
||||
|
||||
<li><a href="artist.photo?mrx=4590" title="Resurgency - False Enlightenment"></a><a href="/reviews/6746/resurgency-false-enlightenment"></a>
|
||||
<p style="top: 130px; left: 22px; width: 305px; height:60px;">
|
||||
<img class="rotator-cover-art" src="album.cover?art=6746" alt='Resurgency - False Enlightenment' title='Resurgency - False Enlightenment' />
|
||||
<span class="title"><strong>Resurgency</strong></span><br />
|
||||
False Enlightenment<br />
|
||||
<a href="/tags/1/death"><span class="tag">Death</span></a>
|
||||
</p>
|
||||
</li>
|
||||
|
||||
<li><a href="artist.photo?mrx=1360" title="Morgoth - Cursed to Live"></a><a href="/reviews/6800/morgoth-cursed-to-live"></a>
|
||||
<p style="top: 130px; left: 22px; width: 305px; height:60px;">
|
||||
<img class="rotator-cover-art" src="album.cover?art=6800" alt='Morgoth - Cursed to Live' title='Morgoth - Cursed to Live' />
|
||||
<span class="title"><strong>Morgoth</strong></span><br />
|
||||
Cursed to Live<br />
|
||||
<a href="/tags/1/death"><span class="tag">Death</span></a><a href="/tags/31/live"><span class="tag">Live</span></a>
|
||||
</p>
|
||||
</li>
|
||||
|
||||
<li><a href="artist.photo?mrx=3879" title="Krallice - Years Past Matter"></a><a href="/reviews/6853/krallice-years-past-matter"></a>
|
||||
<p style="top: 130px; left: 22px; width: 305px; height:60px;">
|
||||
<img class="rotator-cover-art" src="album.cover?art=6853" alt='Krallice - Years Past Matter' title='Krallice - Years Past Matter' />
|
||||
<span class="title"><strong>Krallice</strong></span><br />
|
||||
Years Past Matter<br />
|
||||
<a href="/tags/10/black"><span class="tag">Black</span></a>
|
||||
</p>
|
||||
</li>
|
||||
|
||||
<li><a href="artist.photo?mrx=4243" title="Murder Construct - Results"></a><a href="/reviews/6782/murder-construct-results"></a>
|
||||
<p style="top: 130px; left: 22px; width: 305px; height:60px;">
|
||||
<img class="rotator-cover-art" src="album.cover?art=6782" alt='Murder Construct - Results' title='Murder Construct - Results' />
|
||||
<span class="title"><strong>Murder Construct</strong></span><br />
|
||||
Results<br />
|
||||
<a href="/tags/13/grindcore"><span class="tag">Grindcore</span></a>
|
||||
</p>
|
||||
</li>
|
||||
|
||||
<li><a href="artist.photo?mrx=251" title="Grave - Endless Procession of Souls"></a><a href="/reviews/6834/grave-endless-procession-of-souls"></a>
|
||||
<p style="top: 130px; left: 22px; width: 305px; height:60px;">
|
||||
<img class="rotator-cover-art" src="album.cover?art=6834" alt='Grave - Endless Procession of Souls' title='Grave - Endless Procession of Souls' />
|
||||
<span class="title"><strong>Grave</strong></span><br />
|
||||
Endless Procession of Souls<br />
|
||||
<a href="/tags/1/death"><span class="tag">Death</span></a>
|
||||
</p>
|
||||
</li>
|
||||
|
||||
<li><a href="artist.photo?mrx=3508" title="Master - The New Elite"></a><a href="/reviews/6774/master-the-new-elite"></a>
|
||||
<p style="top: 130px; left: 22px; width: 305px; height:60px;">
|
||||
<img class="rotator-cover-art" src="album.cover?art=6774" alt='Master - The New Elite' title='Master - The New Elite' />
|
||||
<span class="title"><strong>Master</strong></span><br />
|
||||
The New Elite<br />
|
||||
<a href="/tags/1/death"><span class="tag">Death</span></a>
|
||||
</p>
|
||||
</li>
|
||||
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="showcase-all-artist-albums">
|
||||
<a href="/reviews/6844/serpentine-path-serpentine-path"><img src="album.cover?art=6844" alt="Serpentine Path - Serpentine Path" /></a><a href="/reviews/6830/hunters-ground-no-god-but-the-wild"><img src="album.cover?art=6830" alt="Hunter's Ground - No God But the Wild" /></a><a href="/reviews/6829/blut-aus-nord-777---cosmosophy"><img src="album.cover?art=6829" alt="Blut Aus Nord - 777 - Cosmosophy" /></a><a href="/reviews/6835/ufomammut-oro--opus-alter"><img src="album.cover?art=6835" alt="Ufomammut - Oro: Opus Alter" /></a><a href="/reviews/6746/resurgency-false-enlightenment"><img src="album.cover?art=6746" alt="Resurgency - False Enlightenment" /></a><a href="/reviews/6800/morgoth-cursed-to-live"><img src="album.cover?art=6800" alt="Morgoth - Cursed to Live" /></a><a href="/reviews/6853/krallice-years-past-matter"><img src="album.cover?art=6853" alt="Krallice - Years Past Matter" /></a><a href="/reviews/6782/murder-construct-results"><img src="album.cover?art=6782" alt="Murder Construct - Results" /></a><a href="/reviews/6834/grave-endless-procession-of-souls"><img src="album.cover?art=6834" alt="Grave - Endless Procession of Souls" /></a><a href="/reviews/6774/master-the-new-elite"><img src="album.cover?art=6774" alt="Master - The New Elite" /></a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="yui-u">
|
||||
|
||||
|
||||
|
||||
<div id="feature-feed">
|
||||
<h2>Features</h2>
|
||||
<div class="feature-feed-line"><a href="http://community2.metalreview.com/blogs/editorials/archive/2012/08/15/corsair-interview.aspx"><span class="feature-link"><strong>Release The SkyKrakken: Corsair Interview</strong></span></a><br /><span class="publish-date">8/15/2012 by JW</span><img align="left" src="http://community2.metalreview.com/cfs-file.ashx/__key/CommunityServer.Components.Avatars/00.00.00.21.25/4TMR3E1CWERK.jpg" alt="JW's Avatar" width="36px" height="40px" border="0" /></div>
|
||||
<div class="feature-feed-line"><a href="http://community2.metalreview.com/blogs/editorials/archive/2012/08/09/riffology-kreative-evolution-part-iii.aspx"><span class="feature-link"><strong>Riffology: Kreative Evolution, Part III</strong></span></a><br /><span class="publish-date">8/9/2012 by Achilles</span><img align="left" src="http://community2.metalreview.com/cfs-file.ashx/__key/CommunityServer.Components.Avatars/00.00.00.21.44/4THUGH622I68.jpg" alt="Achilles's Avatar" width="40px" height="39px" border="0" /></div>
|
||||
<div class="feature-feed-line"><a href="http://community2.metalreview.com/blogs/editorials/archive/2012/08/02/reverend-s-bazaar-don-t-trend-on-me.aspx"><span class="feature-link"><strong>Reverend's Bazaar - Don't Trend On Me </strong></span></a><br /><span class="publish-date">8/2/2012 by Reverend Campbell</span><img align="left" src="http://community2.metalreview.com/cfs-file.ashx/__key/CommunityServer.Components.Avatars/00.00.00.21.18/4TM06FD0ND4G.png" alt="Reverend Campbell's Avatar" width="34px" height="40px" border="0" /></div>
|
||||
<div class="feature-feed-line"><a href="http://community2.metalreview.com/blogs/editorials/archive/2012/08/01/grand-theft-metal-three-for-free.aspx"><span class="feature-link"><strong>Grand Theft Metal - Free Four All </strong></span></a><br /><span class="publish-date">8/2/2012 by Dave</span><img align="left" src="http://community2.metalreview.com/cfs-file.ashx/__key/CommunityServer.Components.Avatars/00.00.00.22.16/4TKJCJQ00VFO.jpg" alt="Dave's Avatar" width="33px" height="40px" border="0" /></div>
|
||||
<div class="feature-feed-line"><a href="http://community2.metalreview.com/blogs/editorials/archive/2012/07/29/monday-with-moonspell-the-interview.aspx"><span class="feature-link"><strong>A Monday with Moonspell: The Interview</strong></span></a><br /><span class="publish-date">7/29/2012 by raetamacue</span><img align="left" src="http://community2.metalreview.com/cfs-file.ashx/__key/CommunityServer.Components.Avatars/00.00.00.71.26/4TLIHKLUSXF4.jpg" alt="raetamacue's Avatar" width="37px" height="40px" border="0" /></div>
|
||||
<div class="feature-feed-line"><a href="http://community2.metalreview.com/blogs/editorials/archive/2012/07/26/riffology-kreative-evolution-part-ii.aspx"><span class="feature-link"><strong>Riffology: Kreative Evolution Part II</strong></span></a><br /><span class="publish-date">7/26/2012 by Achilles</span><img align="left" src="http://community2.metalreview.com/cfs-file.ashx/__key/CommunityServer.Components.Avatars/00.00.00.21.44/4THUGH622I68.jpg" alt="Achilles's Avatar" width="40px" height="39px" border="0" /></div>
|
||||
<div class="feature-feed-line"><a href="http://community2.metalreview.com/blogs/editorials/archive/2012/07/24/shadow-kingdom-records-giveaway.aspx"><span class="feature-link"><strong>WINNERS ANNOUNCED -- Shadow Kingdom Records Give...</strong></span></a><br /><span class="publish-date">7/24/2012 by Metal Review</span><img align="left" src="http://community2.metalreview.com/cfs-file.ashx/__key/CommunityServer.Components.Avatars/00.00.00.59.06/4TFD2N58B7BS.png" alt="Metal Review's Avatar" width="34px" height="40px" border="0" /></div>
|
||||
|
||||
<br />
|
||||
<a href="http://community2.metalreview.com/blogs/editorials/default.aspx"><strong>More Editorials</strong></a>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="yui-b">
|
||||
|
||||
|
||||
|
||||
<script src="/Scripts/jquery.cycle.all.min.js" type="text/javascript"></script>
|
||||
<div id="slider-next-button"><img id="slider-next" src="/Content/Images/Backgrounds/rotator-next-button.png" alt="Goto Next Group" title="Goto Next Group" /></div>
|
||||
<div id="slider-back-button"><img id="slider-back" src="/Content/Images/Backgrounds/rotator-back-button.png" alt="Goto Previous Group" title="Goto Previous Group" /></div>
|
||||
<div id="latest-reviews-slider">
|
||||
<div class="slider-row">
|
||||
<div class="slider-item"><a href="/reviews/6795/midnight-complete-and-total-hell"><img src="album.cover?art=6795" alt="Midnight Complete and Total Hell" /><br /><strong>Midnight</strong><br /><em>Complete and Total Hell</em></a><div class="score">8.5</div></div>
|
||||
<div class="slider-item"><a href="/reviews/6842/over-your-threshold-facticity"><img src="album.cover?art=6842" alt="Over Your Threshold Facticity" /><br /><strong>Over Your Threshold</strong><br /><em>Facticity</em></a><div class="score">6.0</div></div>
|
||||
<div class="slider-item"><a href="/reviews/6813/nuclear-death-terror-chaos-reigns"><img src="album.cover?art=6813" alt="Nuclear Death Terror Chaos Reigns" /><br /><strong>Nuclear Death Terror</strong><br /><em>Chaos Reigns</em></a><div class="score">7.5</div></div>
|
||||
<div class="slider-item"><a href="/reviews/6811/evoken-atra-mors"><img src="album.cover?art=6811" alt="Evoken Atra Mors" /><br /><strong>Evoken</strong><br /><em>Atra Mors</em></a><div class="score">9.5</div></div>
|
||||
<div class="slider-item"><a href="/reviews/6807/blacklodge-machination"><img src="album.cover?art=6807" alt="Blacklodge MachinatioN" /><br /><strong>Blacklodge</strong><br /><em>MachinatioN</em></a><div class="score">5.5</div></div>
|
||||
<div class="slider-item"><a href="/reviews/6832/prototype-catalyst"><img src="album.cover?art=6832" alt="Prototype Catalyst" /><br /><strong>Prototype</strong><br /><em>Catalyst</em></a><div class="score">8.0</div></div>
|
||||
<div class="slider-item"><a href="/reviews/6822/hypnosia-horror-infernal"><img src="album.cover?art=6822" alt="Hypnosia Horror Infernal" /><br /><strong>Hypnosia</strong><br /><em>Horror Infernal</em></a><div class="score">7.0</div></div>
|
||||
<div class="slider-item"><a href="/reviews/6787/om-advaitic-songs"><img src="album.cover?art=6787" alt="OM Advaitic Songs" /><br /><strong>OM</strong><br /><em>Advaitic Songs</em></a><div class="score">8.0</div></div>
|
||||
<div class="slider-item"><a href="/reviews/6765/afgrund-the-age-of-dumb"><img src="album.cover?art=6765" alt="Afgrund The Age Of Dumb" /><br /><strong>Afgrund</strong><br /><em>The Age Of Dumb</em></a><div class="score">8.5</div></div>
|
||||
<div class="slider-item"><a href="/reviews/6773/binah-hallucinating-in-resurrecture"><img src="album.cover?art=6773" alt="Binah Hallucinating in Resurrecture" /><br /><strong>Binah</strong><br /><em>Hallucinating in Resurrecture</em></a><div class="score">8.5</div></div>
|
||||
</div>
|
||||
<div class="slider-row">
|
||||
<div class="slider-item"><a href="/reviews/6802/deiphago-satan-alpha-omega"><img src="album.cover?art=6802" alt="Deiphago Satan Alpha Omega" /><br /><strong>Deiphago</strong><br /><em>Satan Alpha Omega</em></a><div class="score">8.0</div></div>
|
||||
<div class="slider-item"><a href="/reviews/6719/conan-monnos"><img src="album.cover?art=6719" alt="Conan Monnos" /><br /><strong>Conan</strong><br /><em>Monnos</em></a><div class="score">8.0</div></div>
|
||||
<div class="slider-item"><a href="/reviews/6702/alaric-alaric-atriarch---split-lp"><img src="album.cover?art=6702" alt="Alaric Alaric/Atriarch - Split LP" /><br /><strong>Alaric</strong><br /><em>Alaric/Atriarch - Split LP</em></a><div class="score">8.5</div></div>
|
||||
<div class="slider-item"><a href="/reviews/6780/coven-worship-new-gods-(reissue)"><img src="album.cover?art=6780" alt="Coven Worship New Gods (Reissue)" /><br /><strong>Coven</strong><br /><em>Worship New Gods (Reissue)</em></a><div class="score">5.0</div></div>
|
||||
<div class="slider-item"><a href="/reviews/6831/the-foreshadowing-second-world"><img src="album.cover?art=6831" alt="The Foreshadowing Second World" /><br /><strong>The Foreshadowing</strong><br /><em>Second World</em></a><div class="score">5.5</div></div>
|
||||
<div class="slider-item"><a href="/reviews/6815/nether-regions-into-the-breach"><img src="album.cover?art=6815" alt="Nether Regions Into The Breach" /><br /><strong>Nether Regions</strong><br /><em>Into The Breach</em></a><div class="score">7.0</div></div>
|
||||
<div class="slider-item"><a href="/reviews/6824/agalloch-faustian-echoes"><img src="album.cover?art=6824" alt="Agalloch Faustian Echoes" /><br /><strong>Agalloch</strong><br /><em>Faustian Echoes</em></a><div class="score">9.0</div></div>
|
||||
<div class="slider-item"><a href="/reviews/6805/a-forest-of-stars-a-shadowplay-for-yesterdays"><img src="album.cover?art=6805" alt="A Forest Of Stars A Shadowplay For Yesterdays" /><br /><strong>A Forest Of Stars</strong><br /><em>A Shadowplay For Yesterdays</em></a><div class="score">9.0</div></div>
|
||||
<div class="slider-item"><a href="/reviews/6763/de-profundis-the-emptiness-within"><img src="album.cover?art=6763" alt="De Profundis The Emptiness Within" /><br /><strong>De Profundis</strong><br /><em>The Emptiness Within</em></a><div class="score">7.5</div></div>
|
||||
<div class="slider-item"><a href="/reviews/6826/ozzy-osbourne-speak-of-the-devil"><img src="album.cover?art=6826" alt="Ozzy Osbourne Speak of the Devil" /><br /><strong>Ozzy Osbourne</strong><br /><em>Speak of the Devil</em></a><div class="score">7.5</div></div>
|
||||
</div>
|
||||
<div class="slider-row">
|
||||
<div class="slider-item"><a href="/reviews/6825/testament-dark-roots-of-earth"><img src="album.cover?art=6825" alt="Testament Dark Roots of Earth" /><br /><strong>Testament</strong><br /><em>Dark Roots of Earth</em></a><div class="score">8.0</div></div>
|
||||
<div class="slider-item"><a href="/reviews/6796/eagle-twin-the-feather-tipped-the-serpents-scale"><img src="album.cover?art=6796" alt="Eagle Twin The Feather Tipped The Serpent's Scale" /><br /><strong>Eagle Twin</strong><br /><em>The Feather Tipped The Serpent's Scale</em></a><div class="score">8.5</div></div>
|
||||
<div class="slider-item"><a href="/reviews/6609/king-forged-by-satans-doctrine"><img src="album.cover?art=6609" alt="King Forged by Satan's Doctrine" /><br /><strong>King</strong><br /><em>Forged by Satan's Doctrine</em></a><div class="score">5.5</div></div>
|
||||
<div class="slider-item"><a href="/reviews/6798/khors-wisdom-of-centuries"><img src="album.cover?art=6798" alt="Khors Wisdom of Centuries" /><br /><strong>Khors</strong><br /><em>Wisdom of Centuries</em></a><div class="score">8.5</div></div>
|
||||
<div class="slider-item"><a href="/reviews/6776/samothrace-reverence-to-stone"><img src="album.cover?art=6776" alt="Samothrace Reverence To Stone" /><br /><strong>Samothrace</strong><br /><em>Reverence To Stone</em></a><div class="score">8.0</div></div>
|
||||
<div class="slider-item"><a href="/reviews/6784/horseback-on-the-eclipse"><img src="album.cover?art=6784" alt="Horseback On the Eclipse" /><br /><strong>Horseback</strong><br /><em>On the Eclipse</em></a><div class="score">8.0</div></div>
|
||||
<div class="slider-item"><a href="/reviews/6690/incoming-cerebral-overdrive-le-stelle--a-voyage-adrift"><img src="album.cover?art=6690" alt="Incoming Cerebral Overdrive Le Stelle: A Voyage Adrift" /><br /><strong>Incoming Cerebral Overdrive</strong><br /><em>Le Stelle: A Voyage Adrift</em></a><div class="score">7.5</div></div>
|
||||
<div class="slider-item"><a href="/reviews/6658/struck-by-lightning-true-predation"><img src="album.cover?art=6658" alt="Struck By Lightning True Predation" /><br /><strong>Struck By Lightning</strong><br /><em>True Predation</em></a><div class="score">7.0</div></div>
|
||||
<div class="slider-item"><a href="/reviews/6772/offending-age-of-perversion"><img src="album.cover?art=6772" alt="Offending Age of Perversion" /><br /><strong>Offending</strong><br /><em>Age of Perversion</em></a><div class="score">7.5</div></div>
|
||||
<div class="slider-item"><a href="/reviews/6804/king-of-asgard----to-north"><img src="album.cover?art=6804" alt="King Of Asgard ...to North" /><br /><strong>King Of Asgard</strong><br /><em>...to North</em></a><div class="score">7.5</div></div>
|
||||
</div>
|
||||
<div class="slider-row">
|
||||
<div class="slider-item"><a href="/reviews/6783/burning-love-rotten-thing-to-say"><img src="album.cover?art=6783" alt="Burning Love Rotten Thing to Say" /><br /><strong>Burning Love</strong><br /><em>Rotten Thing to Say</em></a><div class="score">7.0</div></div>
|
||||
<div class="slider-item"><a href="/reviews/6770/high-on-fire-the-art-of-self-defense-(reissue)"><img src="album.cover?art=6770" alt="High On Fire The Art Of Self Defense (Reissue)" /><br /><strong>High On Fire</strong><br /><em>The Art Of Self Defense (Reissue)</em></a><div class="score">7.5</div></div>
|
||||
<div class="slider-item"><a href="/reviews/6660/horseback-half-blood"><img src="album.cover?art=6660" alt="Horseback Half Blood" /><br /><strong>Horseback</strong><br /><em>Half Blood</em></a><div class="score">6.5</div></div>
|
||||
<div class="slider-item"><a href="/reviews/6732/aldebaran-embracing-the-lightless-depths"><img src="album.cover?art=6732" alt="Aldebaran Embracing the Lightless Depths" /><br /><strong>Aldebaran</strong><br /><em>Embracing the Lightless Depths</em></a><div class="score">8.0</div></div>
|
||||
<div class="slider-item"><a href="/reviews/6778/tank-war-nation"><img src="album.cover?art=6778" alt="Tank War Nation" /><br /><strong>Tank</strong><br /><em>War Nation</em></a><div class="score">6.5</div></div>
|
||||
<div class="slider-item"><a href="/reviews/6793/satanic-bloodspraying-at-the-mercy-of-satan"><img src="album.cover?art=6793" alt="Satanic Bloodspraying At the Mercy of Satan" /><br /><strong>Satanic Bloodspraying</strong><br /><em>At the Mercy of Satan</em></a><div class="score">8.5</div></div>
|
||||
<div class="slider-item"><a href="/reviews/6791/from-ashes-rise-rejoice-the-end---rage-of-sanity"><img src="album.cover?art=6791" alt="From Ashes Rise Rejoice The End / Rage Of Sanity" /><br /><strong>From Ashes Rise</strong><br /><em>Rejoice The End / Rage Of Sanity</em></a><div class="score">8.0</div></div>
|
||||
<div class="slider-item"><a href="/reviews/6743/ereb-altor-gastrike"><img src="album.cover?art=6743" alt="Ereb Altor Gastrike" /><br /><strong>Ereb Altor</strong><br /><em>Gastrike</em></a><div class="score">8.0</div></div>
|
||||
<div class="slider-item"><a href="/reviews/6794/catheter-southwest-doom-violence"><img src="album.cover?art=6794" alt="Catheter Southwest Doom Violence" /><br /><strong>Catheter</strong><br /><em>Southwest Doom Violence</em></a><div class="score">7.0</div></div>
|
||||
<div class="slider-item"><a href="/reviews/6759/power-theory-an-axe-to-grind"><img src="album.cover?art=6759" alt="Power Theory An Axe to Grind" /><br /><strong>Power Theory</strong><br /><em>An Axe to Grind</em></a><div class="score">6.0</div></div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
<script type="text/javascript">
|
||||
$(document).ready(function () {
|
||||
$('#latest-reviews-slider').cycle({
|
||||
fx: 'scrollRight',
|
||||
speed: 'fast',
|
||||
timeout: 0,
|
||||
next: '#slider-next-button',
|
||||
prev: '#slider-back-button'
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
<div id="homepage-mid-horizontal-zone">
|
||||
<script language="javascript" type="text/javascript" src="http://metalreview.com/bannermgr/abm.aspx?z=1"></script>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
<div id="news-feed">
|
||||
<h2>News</h2><div class="news-feed-line"><a href="http://www.bravewords.com/news/190057" target="_blank"><span class="news-link"><strong><span class="new-news">NEW</span> CENTURIAN To Release Contra Rationem Album This Winter</strong></span></a><br /><span class="publish-date">9/12/2012</span></div><br />
|
||||
<div class="news-feed-line"><a href="http://www.bravewords.com/news/190056" target="_blank"><span class="news-link"><strong><span class="new-news">NEW</span> Southwest Terror Fest 2012 - Lineup Changes Announced</strong></span></a><br /><span class="publish-date">9/12/2012</span></div><br />
|
||||
<div class="news-feed-line"><a href="http://www.bravewords.com/news/190055" target="_blank"><span class="news-link"><strong><span class="new-news">NEW</span> ROB ZOMBIE Premiers The Lords Of Salem At TIFF; Q&A Video Posted</strong></span></a><br /><span class="publish-date">9/12/2012</span></div><br />
|
||||
<div class="news-feed-line"><a href="http://www.bravewords.com/news/190054" target="_blank"><span class="news-link"><strong><span class="new-news">NEW</span> THIN LIZZY Keyboardist Darren Wharton's DARE - Calm Before The Storm 2 Album Details Revealed</strong></span></a><br /><span class="publish-date">9/12/2012</span></div><br />
|
||||
<div class="news-feed-line"><a href="http://www.bravewords.com/news/190053" target="_blank"><span class="news-link"><strong><span class="new-news">NEW</span> Japan's LIV MOON To Release Fourth Album; Features Past/Present Members Of EUROPE, ANGRA, HAMMERFALL</strong></span></a><br /><span class="publish-date">9/12/2012</span></div><br />
|
||||
<div class="news-feed-line"><a href="http://www.bravewords.com/news/190052" target="_blank"><span class="news-link"><strong><span class="new-news">NEW</span> SLASH - Sydney Show To Premier This Friday, Free And In HD; Trailer Posted</strong></span></a><br /><span class="publish-date">9/12/2012</span></div><br />
|
||||
<div class="news-feed-line"><a href="http://www.bravewords.com/news/190051" target="_blank"><span class="news-link"><strong><span class="new-news">NEW</span> KHAØS - New Band Featuring Members Of OUTLOUD, TRIBAL, JORN And ELIS To Release New EP In October; Teaser Posted </strong></span></a><br /><span class="publish-date">9/12/2012</span></div><br />
|
||||
<div class="news-feed-line"><a href="http://www.bravewords.com/news/190050" target="_blank"><span class="news-link"><strong><span class="new-news">NEW</span> RECKLESS LOVE Confirm Guests For London Residency Shows In October</strong></span></a><br /><span class="publish-date">9/12/2012</span></div><br />
|
||||
<div class="news-feed-line"><a href="http://www.bravewords.com/news/190049" target="_blank"><span class="news-link"><strong>NASHVILLE PUSSY Add Dates In France, Sweden To European Tour Schedule; Bassist Karen Cuda Sidelined With Back Injury </strong></span></a><br /><span class="publish-date">9/12/2012</span></div><br />
|
||||
<div class="news-feed-line"><a href="http://www.bravewords.com/news/190048" target="_blank"><span class="news-link"><strong>CALIBAN Post Behind-The-Scenes Tour Footage</strong></span></a><br /><span class="publish-date">9/12/2012</span></div><br />
|
||||
<div class="news-feed-line"><a href="http://www.bravewords.com/news/190047" target="_blank"><span class="news-link"><strong>Ex-MERCYFUL FATE Drummer Kim Ruzz Forms New Band METALRUZZ</strong></span></a><br /><span class="publish-date">9/12/2012</span></div><br />
|
||||
<div class="news-feed-line"><a href="http://www.bravewords.com/news/190046" target="_blank"><span class="news-link"><strong>GRAVE Mainman On Endless Procession Of Souls - "These Are The Most ‘Song-Oriented’ Tracks We’ve Done In A Long Time"</strong></span></a><br /><span class="publish-date">9/12/2012</span></div><br />
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
<div id="lashes-feed">
|
||||
<h2>Lashes</h2>
|
||||
<div class="lashes-feed-line"><a href="/reviews/6855/katatonia-dead-end-kings#81760"><span class="new-lash">NEW</span> <span class="lashes-link"><strong>Katatonia - Dead End Kings</strong></span></a><br /><span class="publish-date">45 minutes ago by Chaosjunkie</span></div>
|
||||
<div class="lashes-feed-line"><a href="/reviews/6855/katatonia-dead-end-kings#81759"><span class="lashes-link"><strong>Katatonia - Dead End Kings</strong></span></a><br /><span class="publish-date">1 hour ago by Harry Dick Rotten</span></div>
|
||||
<div class="lashes-feed-line"><a href="/reviews/6746/resurgency-false-enlightenment#81758"><span class="lashes-link"><strong>Resurgency - False Enlightenment</strong></span></a><br /><span class="publish-date">3 hours ago by Anonymous</span></div>
|
||||
<div class="lashes-feed-line"><a href="/reviews/4095/witchcraft-the-alchemist#81757"><span class="lashes-link"><strong>Witchcraft - The Alchemist</strong></span></a><br /><span class="publish-date">5 hours ago by Luke_22</span></div>
|
||||
<div class="lashes-feed-line"><a href="/reviews/6855/katatonia-dead-end-kings#81756"><span class="lashes-link"><strong>Katatonia - Dead End Kings</strong></span></a><br /><span class="publish-date">9 hours ago by chaosjunkie</span></div>
|
||||
<div class="lashes-feed-line"><a href="/reviews/6855/katatonia-dead-end-kings#81755"><span class="lashes-link"><strong>Katatonia - Dead End Kings</strong></span></a><br /><span class="publish-date">10 hours ago by Compeller</span></div>
|
||||
<div class="lashes-feed-line"><a href="/reviews/6827/manetheren-time#81754"><span class="lashes-link"><strong>Manetheren - Time</strong></span></a><br /><span class="publish-date">10 hours ago by xpmule</span></div>
|
||||
<div class="lashes-feed-line"><a href="/reviews/6835/ufomammut-oro--opus-alter#81753"><span class="lashes-link"><strong>Ufomammut - Oro: Opus Alter</strong></span></a><br /><span class="publish-date">16 hours ago by Anonymous</span></div>
|
||||
<div class="lashes-feed-line"><a href="/reviews/6835/ufomammut-oro--opus-alter#81752"><span class="lashes-link"><strong>Ufomammut - Oro: Opus Alter</strong></span></a><br /><span class="publish-date">17 hours ago by Harry Dick Rotten</span></div>
|
||||
<div class="lashes-feed-line"><a href="/reviews/6855/katatonia-dead-end-kings#81751"><span class="lashes-link"><strong>Katatonia - Dead End Kings</strong></span></a><br /><span class="publish-date">yesterday by Chaosjunkie</span></div>
|
||||
<div class="lashes-feed-line"><a href="/reviews/6855/katatonia-dead-end-kings#81750"><span class="lashes-link"><strong>Katatonia - Dead End Kings</strong></span></a><br /><span class="publish-date">yesterday by Anonymous</span></div>
|
||||
<div class="lashes-feed-line"><a href="/reviews/6855/katatonia-dead-end-kings#81749"><span class="lashes-link"><strong>Katatonia - Dead End Kings</strong></span></a><br /><span class="publish-date">yesterday by Anonymous</span></div>
|
||||
<div class="lashes-feed-line"><a href="/reviews/6855/katatonia-dead-end-kings#81748"><span class="lashes-link"><strong>Katatonia - Dead End Kings</strong></span></a><br /><span class="publish-date">yesterday by Anonymous</span></div>
|
||||
<div class="lashes-feed-line"><a href="/reviews/6855/katatonia-dead-end-kings#81747"><span class="lashes-link"><strong>Katatonia - Dead End Kings</strong></span></a><br /><span class="publish-date">yesterday by frantic</span></div>
|
||||
<div class="lashes-feed-line"><a href="/reviews/6829/blut-aus-nord-777---cosmosophy#81746"><span class="lashes-link"><strong>Blut Aus Nord - 777 - Cosmosophy</strong></span></a><br /><span class="publish-date">yesterday by Dimensional Bleedthrough</span></div>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<div id="ft">
|
||||
|
||||
|
||||
<div id="template-footer">
|
||||
<div class="left-column">
|
||||
<ul>
|
||||
<li><a href="/">Home</a></li>
|
||||
<li><a href="/reviews/browse">Reviews</a></li>
|
||||
<li><a href="/tags">Genre Tags</a></li>
|
||||
<li><a href="http://community2.metalreview.com/blogs/editorials/default.aspx">Features</a></li>
|
||||
<li><a href="/artists/browse">Artists</a></li>
|
||||
<li><a href="/reviews/pipeline">Pipeline</a></li>
|
||||
<li><a href="http://community2.metalreview.com/forums">Forums</a></li>
|
||||
<li><a href="/aboutus">About Us</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="middle-column">
|
||||
<ul>
|
||||
<li><a href="/aboutus/disclaimer">Disclaimer</a></li>
|
||||
<li><a href="/aboutus/privacypolicy">Privacy Policy</a></li>
|
||||
<li><a href="/aboutus/advertising">Advertising</a></li>
|
||||
<li><a href="http://community2.metalreview.com/blogs/eminor/archive/2008/10/27/write-for-metal-review.aspx">Write For Us</a></li>
|
||||
<li><a href="/contactus">Contact Us</a></li>
|
||||
<li><a href="/contactus">Digital Promos</a></li>
|
||||
<li><a href="/contactus">Mailing Address</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="right-column">
|
||||
<ul>
|
||||
<li><a href="http://feeds.feedburner.com/metalreviews">Reviews RSS Feed</a></li>
|
||||
<li><a href="http://twitter.com/metalreview">Twitter</a></li>
|
||||
<li><a href="http://www.myspace.com/metalreviewdotcom">MySpace</a></li>
|
||||
<li><a href="http://www.last.fm/group/MetalReview.com">Last.fm</a></li>
|
||||
<li><a href="http://www.facebook.com/pages/MetalReviewcom/48371319443">Facebook</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="square-ad">
|
||||
|
||||
|
||||
<!--JavaScript Tag // Tag for network 5110: Fixion Media // Website: Metalreview // Page: ROS // Placement: ROS-Middle-300 x 250 (1127996) // created at: Oct 19, 2009 6:48:27 PM-->
|
||||
<script type="text/javascript" language="javascript"><!--
|
||||
document.write('<scr' + 'ipt language="javascript1.1" src="http://adserver.adtechus.com/addyn/3.0/5110/1127996/0/170/ADTECH;loc=100;target=_blank;key=key1+key2+key3+key4;grp=[group];misc=' + new Date().getTime() + '"></scri' + 'pt>');
|
||||
//-->
|
||||
</script><noscript><a href="http://adserver.adtechus.com/adlink/3.0/5110/1127996/0/170/ADTECH;loc=300;key=key1+key2+key3+key4;grp=[group]" target="_blank"><img src="http://adserver.adtechus.com/adserv/3.0/5110/1127996/0/170/ADTECH;loc=300;key=key1+key2+key3+key4;grp=[group]" border="0" width="300" height="250"></a></noscript>
|
||||
<!-- End of JavaScript Tag -->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script type="text/javascript">
|
||||
var gaJsHost = (("https:" == document.location.protocol) ? "https://ssl." : "http://www.");
|
||||
document.write(unescape("%3Cscript src='" + gaJsHost + "google-analytics.com/ga.js' type='text/javascript'%3E%3C/script%3E"));
|
||||
</script>
|
||||
|
||||
<script type="text/javascript">
|
||||
var pageTracker = _gat._getTracker("UA-3455310-1");
|
||||
pageTracker._initData();
|
||||
pageTracker._trackPageview();
|
||||
</script>
|
||||
|
||||
<!--JavaScript Tag // Tag for network 5110: Fixion Media // Website: Metalreview // Page: BACKGROUND ADS // Placement: BACKGROUND ADS-Top-1 x 1 (2186116) // created at: Aug 18, 2011 7:20:38 PM-->
|
||||
<script language="javascript"><!--
|
||||
document.write('<scr' + 'ipt language="javascript1.1" src="http://adserver.adtechus.com/addyn/3.0/5110/2186116/0/16/ADTECH;loc=100;target=_blank;key=key1+key2+key3+key4;grp=[group];misc=' + new Date().getTime() + '"></scri' + 'pt>');
|
||||
//-->
|
||||
</script><noscript><a href="http://adserver.adtechus.com/adlink/3.0/5110/2186116/0/16/ADTECH;loc=300;key=key1+key2+key3+key4;grp=[group]" target="_blank"><img src="http://adserver.adtechus.com/adserv/3.0/5110/2186116/0/16/ADTECH;loc=300;key=key1+key2+key3+key4;grp=[group]" border="0" width="1" height="1"></a></noscript>
|
||||
<!-- End of JavaScript Tag -->
|
||||
|
||||
</body>
|
||||
</html>
|
102
vendor/github.com/PuerkitoBio/goquery/testdata/page.html
generated
vendored
Normal file
102
vendor/github.com/PuerkitoBio/goquery/testdata/page.html
generated
vendored
Normal file
|
@ -0,0 +1,102 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en" ng-app="app">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
|
||||
<title>
|
||||
Provok.in
|
||||
</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta name="description" content="Provok.in - Prove your point. State an affirmation, back it up with evidence, unveil the truth.">
|
||||
<meta name="author" content="Martin Angers">
|
||||
<link href="http://fonts.googleapis.com/css?family=Belgrano" rel="stylesheet" type="text/css">
|
||||
<!--[if lt IE 9]><link href="http://fonts.googleapis.com/css?family=Belgrano" rel="stylesheet" type="text/css"><link href="http://fonts.googleapis.com/css?family=Belgrano:400italic" rel="stylesheet" type="text/css"><link href="http://fonts.googleapis.com/css?family=Belgrano:700" rel="stylesheet" type="text/css"><link href="http://fonts.googleapis.com/css?family=Belgrano:700italic" rel="stylesheet" type="text/css"><![endif]-->
|
||||
<link href="/css/pvk.min.css" rel="stylesheet" type="text/css">
|
||||
</head>
|
||||
<body>
|
||||
<div class="container-fluid" id="cf1">
|
||||
<div class="row-fluid">
|
||||
<div class="pvk-gutter">
|
||||
|
||||
</div>
|
||||
<div class="pvk-content" id="pc1">
|
||||
<div ng-controller="HeroCtrl" class="hero-unit">
|
||||
<div class="container-fluid" id="cf2">
|
||||
<div class="row-fluid" id="cf2-1">
|
||||
<div class="span12">
|
||||
<h1>
|
||||
<a href="/">Provok<span class="green">.</span><span class="red">i</span>n</a>
|
||||
</h1>
|
||||
<p>
|
||||
Prove your point.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row-fluid" id="cf2-2">
|
||||
<div class="span12 alert alert-error">
|
||||
<strong>Beta Version.</strong> Things may change. Or disappear. Or fail miserably. If it's the latter, <a href="https://github.com/PuerkitoBio/Provok.in-issues" target="_blank" class="link">please file an issue.</a>
|
||||
</div>
|
||||
</div>
|
||||
<div ng-cloak="" ng-show="isLoggedOut() && !hideLogin" class="row-fluid" id="cf2-3">
|
||||
<a ng-href="{{ROUTES.login}}" class="btn btn-primary">Sign in. Painless.</a> <span>or</span> <a ng-href="{{ROUTES.help}}" class="link">learn more about provok.in.</a>
|
||||
</div>
|
||||
<div ng-cloak="" ng-show="isLoggedIn()" class="row-fluid logged-in-state" id="cf2-4">
|
||||
<span>Welcome,</span> <a ng-href="{{ROUTES.profile}}" class="link">{{getUserName()}}</a> <span>(</span> <a ng-click="doLogout($event)" class="link">logout</a> <span>)</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="pvk-gutter">
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<div class="row-fluid">
|
||||
<div class="pvk-gutter">
|
||||
|
||||
</div>
|
||||
<div class="pvk-content" id="pc2">
|
||||
<div class="container-fluid" id="cf3">
|
||||
<div class="row-fluid">
|
||||
<div ng-cloak="" view-on-display="" ng-controller="MsgCtrl" ng-class="{'displayed': blockIsDisplayed}" class="message-box">
|
||||
<div ng-class="{'alert-info': isInfo, 'alert-error': !isInfo, 'displayed': isDisplayed}" class="alert">
|
||||
<a ng-click="hideMessage(true, $event)" class="close">×</a>
|
||||
<h4 class="alert-heading">
|
||||
{{ title }}
|
||||
</h4>
|
||||
<p>
|
||||
{{ message }}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="container-fluid" id="cf4">
|
||||
<div ng-controller="ShareCtrl" ng-hide="isHidden" class="row-fluid center-content"></div>
|
||||
</div>
|
||||
<div ng-view=""></div>
|
||||
</div>
|
||||
<div class="pvk-gutter">
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<div class="row-fluid">
|
||||
<div class="pvk-gutter">
|
||||
|
||||
</div>
|
||||
<div class="pvk-content">
|
||||
<div class="footer">
|
||||
<p>
|
||||
<a href="/" class="link">Home</a> <span>|</span> <a href="/about" class="link">About</a> <span>|</span> <a href="/help" class="link">Help</a>
|
||||
</p>
|
||||
<p>
|
||||
<small>© 2012 Martin Angers</small>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="pvk-gutter">
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
24
vendor/github.com/PuerkitoBio/goquery/testdata/page2.html
generated
vendored
Normal file
24
vendor/github.com/PuerkitoBio/goquery/testdata/page2.html
generated
vendored
Normal file
|
@ -0,0 +1,24 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Tests for siblings</title>
|
||||
</head>
|
||||
<BODY>
|
||||
<div id="main">
|
||||
<div id="n1" class="one even row"></div>
|
||||
<div id="n2" class="two odd row"></div>
|
||||
<div id="n3" class="three even row"></div>
|
||||
<div id="n4" class="four odd row"></div>
|
||||
<div id="n5" class="five even row"></div>
|
||||
<div id="n6" class="six odd row"></div>
|
||||
</div>
|
||||
<div id="foot">
|
||||
<div id="nf1" class="one even row"></div>
|
||||
<div id="nf2" class="two odd row"></div>
|
||||
<div id="nf3" class="three even row"></div>
|
||||
<div id="nf4" class="four odd row"></div>
|
||||
<div id="nf5" class="five even row odder"></div>
|
||||
<div id="nf6" class="six odd row"></div>
|
||||
</div>
|
||||
</BODY>
|
||||
</html>
|
24
vendor/github.com/PuerkitoBio/goquery/testdata/page3.html
generated
vendored
Normal file
24
vendor/github.com/PuerkitoBio/goquery/testdata/page3.html
generated
vendored
Normal file
|
@ -0,0 +1,24 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Tests for siblings</title>
|
||||
</head>
|
||||
<BODY>
|
||||
<div id="main">
|
||||
<div id="n1" class="one even row">hello</div>
|
||||
<div id="n2" class="two odd row"></div>
|
||||
<div id="n3" class="three even row"></div>
|
||||
<div id="n4" class="four odd row"></div>
|
||||
<div id="n5" class="five even row"></div>
|
||||
<div id="n6" class="six odd row"></div>
|
||||
</div>
|
||||
<div id="foot">
|
||||
<div id="nf1" class="one even row">text</div>
|
||||
<div id="nf2" class="two odd row"></div>
|
||||
<div id="nf3" class="three even row"></div>
|
||||
<div id="nf4" class="four odd row"></div>
|
||||
<div id="nf5" class="five even row odder"></div>
|
||||
<div id="nf6" class="six odd row"></div>
|
||||
</div>
|
||||
</BODY>
|
||||
</html>
|
697
vendor/github.com/PuerkitoBio/goquery/traversal_test.go
generated
vendored
Normal file
697
vendor/github.com/PuerkitoBio/goquery/traversal_test.go
generated
vendored
Normal file
|
@ -0,0 +1,697 @@
|
|||
package goquery
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestFind(t *testing.T) {
|
||||
sel := Doc().Find("div.row-fluid")
|
||||
assertLength(t, sel.Nodes, 9)
|
||||
}
|
||||
|
||||
func TestFindRollback(t *testing.T) {
|
||||
sel := Doc().Find("div.row-fluid")
|
||||
sel2 := sel.Find("a").End()
|
||||
assertEqual(t, sel, sel2)
|
||||
}
|
||||
|
||||
func TestFindNotSelf(t *testing.T) {
|
||||
sel := Doc().Find("h1").Find("h1")
|
||||
assertLength(t, sel.Nodes, 0)
|
||||
}
|
||||
|
||||
func TestFindInvalidSelector(t *testing.T) {
|
||||
defer assertPanic(t)
|
||||
Doc().Find(":+ ^")
|
||||
}
|
||||
|
||||
func TestChainedFind(t *testing.T) {
|
||||
sel := Doc().Find("div.hero-unit").Find(".row-fluid")
|
||||
assertLength(t, sel.Nodes, 4)
|
||||
}
|
||||
|
||||
func TestChildren(t *testing.T) {
|
||||
sel := Doc().Find(".pvk-content").Children()
|
||||
assertLength(t, sel.Nodes, 5)
|
||||
}
|
||||
|
||||
func TestChildrenRollback(t *testing.T) {
|
||||
sel := Doc().Find(".pvk-content")
|
||||
sel2 := sel.Children().End()
|
||||
assertEqual(t, sel, sel2)
|
||||
}
|
||||
|
||||
func TestContents(t *testing.T) {
|
||||
sel := Doc().Find(".pvk-content").Contents()
|
||||
assertLength(t, sel.Nodes, 13)
|
||||
}
|
||||
|
||||
func TestContentsRollback(t *testing.T) {
|
||||
sel := Doc().Find(".pvk-content")
|
||||
sel2 := sel.Contents().End()
|
||||
assertEqual(t, sel, sel2)
|
||||
}
|
||||
|
||||
func TestChildrenFiltered(t *testing.T) {
|
||||
sel := Doc().Find(".pvk-content").ChildrenFiltered(".hero-unit")
|
||||
assertLength(t, sel.Nodes, 1)
|
||||
}
|
||||
|
||||
func TestChildrenFilteredRollback(t *testing.T) {
|
||||
sel := Doc().Find(".pvk-content")
|
||||
sel2 := sel.ChildrenFiltered(".hero-unit").End()
|
||||
assertEqual(t, sel, sel2)
|
||||
}
|
||||
|
||||
func TestContentsFiltered(t *testing.T) {
|
||||
sel := Doc().Find(".pvk-content").ContentsFiltered(".hero-unit")
|
||||
assertLength(t, sel.Nodes, 1)
|
||||
}
|
||||
|
||||
func TestContentsFilteredRollback(t *testing.T) {
|
||||
sel := Doc().Find(".pvk-content")
|
||||
sel2 := sel.ContentsFiltered(".hero-unit").End()
|
||||
assertEqual(t, sel, sel2)
|
||||
}
|
||||
|
||||
func TestChildrenFilteredNone(t *testing.T) {
|
||||
sel := Doc().Find(".pvk-content").ChildrenFiltered("a.btn")
|
||||
assertLength(t, sel.Nodes, 0)
|
||||
}
|
||||
|
||||
func TestParent(t *testing.T) {
|
||||
sel := Doc().Find(".container-fluid").Parent()
|
||||
assertLength(t, sel.Nodes, 3)
|
||||
}
|
||||
|
||||
func TestParentRollback(t *testing.T) {
|
||||
sel := Doc().Find(".container-fluid")
|
||||
sel2 := sel.Parent().End()
|
||||
assertEqual(t, sel, sel2)
|
||||
}
|
||||
|
||||
func TestParentBody(t *testing.T) {
|
||||
sel := Doc().Find("body").Parent()
|
||||
assertLength(t, sel.Nodes, 1)
|
||||
}
|
||||
|
||||
func TestParentFiltered(t *testing.T) {
|
||||
sel := Doc().Find(".container-fluid").ParentFiltered(".hero-unit")
|
||||
assertLength(t, sel.Nodes, 1)
|
||||
assertClass(t, sel, "hero-unit")
|
||||
}
|
||||
|
||||
func TestParentFilteredRollback(t *testing.T) {
|
||||
sel := Doc().Find(".container-fluid")
|
||||
sel2 := sel.ParentFiltered(".hero-unit").End()
|
||||
assertEqual(t, sel, sel2)
|
||||
}
|
||||
|
||||
func TestParents(t *testing.T) {
|
||||
sel := Doc().Find(".container-fluid").Parents()
|
||||
assertLength(t, sel.Nodes, 8)
|
||||
}
|
||||
|
||||
func TestParentsOrder(t *testing.T) {
|
||||
sel := Doc().Find("#cf2").Parents()
|
||||
assertLength(t, sel.Nodes, 6)
|
||||
assertSelectionIs(t, sel, ".hero-unit", ".pvk-content", "div.row-fluid", "#cf1", "body", "html")
|
||||
}
|
||||
|
||||
func TestParentsRollback(t *testing.T) {
|
||||
sel := Doc().Find(".container-fluid")
|
||||
sel2 := sel.Parents().End()
|
||||
assertEqual(t, sel, sel2)
|
||||
}
|
||||
|
||||
func TestParentsFiltered(t *testing.T) {
|
||||
sel := Doc().Find(".container-fluid").ParentsFiltered("body")
|
||||
assertLength(t, sel.Nodes, 1)
|
||||
}
|
||||
|
||||
func TestParentsFilteredRollback(t *testing.T) {
|
||||
sel := Doc().Find(".container-fluid")
|
||||
sel2 := sel.ParentsFiltered("body").End()
|
||||
assertEqual(t, sel, sel2)
|
||||
}
|
||||
|
||||
func TestParentsUntil(t *testing.T) {
|
||||
sel := Doc().Find(".container-fluid").ParentsUntil("body")
|
||||
assertLength(t, sel.Nodes, 6)
|
||||
}
|
||||
|
||||
func TestParentsUntilRollback(t *testing.T) {
|
||||
sel := Doc().Find(".container-fluid")
|
||||
sel2 := sel.ParentsUntil("body").End()
|
||||
assertEqual(t, sel, sel2)
|
||||
}
|
||||
|
||||
func TestParentsUntilSelection(t *testing.T) {
|
||||
sel := Doc().Find(".container-fluid")
|
||||
sel2 := Doc().Find(".pvk-content")
|
||||
sel = sel.ParentsUntilSelection(sel2)
|
||||
assertLength(t, sel.Nodes, 3)
|
||||
}
|
||||
|
||||
func TestParentsUntilSelectionRollback(t *testing.T) {
|
||||
sel := Doc().Find(".container-fluid")
|
||||
sel2 := Doc().Find(".pvk-content")
|
||||
sel2 = sel.ParentsUntilSelection(sel2).End()
|
||||
assertEqual(t, sel, sel2)
|
||||
}
|
||||
|
||||
func TestParentsUntilNodes(t *testing.T) {
|
||||
sel := Doc().Find(".container-fluid")
|
||||
sel2 := Doc().Find(".pvk-content, .hero-unit")
|
||||
sel = sel.ParentsUntilNodes(sel2.Nodes...)
|
||||
assertLength(t, sel.Nodes, 2)
|
||||
}
|
||||
|
||||
func TestParentsUntilNodesRollback(t *testing.T) {
|
||||
sel := Doc().Find(".container-fluid")
|
||||
sel2 := Doc().Find(".pvk-content, .hero-unit")
|
||||
sel2 = sel.ParentsUntilNodes(sel2.Nodes...).End()
|
||||
assertEqual(t, sel, sel2)
|
||||
}
|
||||
|
||||
func TestParentsFilteredUntil(t *testing.T) {
|
||||
sel := Doc().Find(".container-fluid").ParentsFilteredUntil(".pvk-content", "body")
|
||||
assertLength(t, sel.Nodes, 2)
|
||||
}
|
||||
|
||||
func TestParentsFilteredUntilRollback(t *testing.T) {
|
||||
sel := Doc().Find(".container-fluid")
|
||||
sel2 := sel.ParentsFilteredUntil(".pvk-content", "body").End()
|
||||
assertEqual(t, sel, sel2)
|
||||
}
|
||||
|
||||
func TestParentsFilteredUntilSelection(t *testing.T) {
|
||||
sel := Doc().Find(".container-fluid")
|
||||
sel2 := Doc().Find(".row-fluid")
|
||||
sel = sel.ParentsFilteredUntilSelection("div", sel2)
|
||||
assertLength(t, sel.Nodes, 3)
|
||||
}
|
||||
|
||||
func TestParentsFilteredUntilSelectionRollback(t *testing.T) {
|
||||
sel := Doc().Find(".container-fluid")
|
||||
sel2 := Doc().Find(".row-fluid")
|
||||
sel2 = sel.ParentsFilteredUntilSelection("div", sel2).End()
|
||||
assertEqual(t, sel, sel2)
|
||||
}
|
||||
|
||||
func TestParentsFilteredUntilNodes(t *testing.T) {
|
||||
sel := Doc().Find(".container-fluid")
|
||||
sel2 := Doc().Find(".row-fluid")
|
||||
sel = sel.ParentsFilteredUntilNodes("body", sel2.Nodes...)
|
||||
assertLength(t, sel.Nodes, 1)
|
||||
}
|
||||
|
||||
func TestParentsFilteredUntilNodesRollback(t *testing.T) {
|
||||
sel := Doc().Find(".container-fluid")
|
||||
sel2 := Doc().Find(".row-fluid")
|
||||
sel2 = sel.ParentsFilteredUntilNodes("body", sel2.Nodes...).End()
|
||||
assertEqual(t, sel, sel2)
|
||||
}
|
||||
|
||||
func TestSiblings(t *testing.T) {
|
||||
sel := Doc().Find("h1").Siblings()
|
||||
assertLength(t, sel.Nodes, 1)
|
||||
}
|
||||
|
||||
func TestSiblingsRollback(t *testing.T) {
|
||||
sel := Doc().Find("h1")
|
||||
sel2 := sel.Siblings().End()
|
||||
assertEqual(t, sel, sel2)
|
||||
}
|
||||
|
||||
func TestSiblings2(t *testing.T) {
|
||||
sel := Doc().Find(".pvk-gutter").Siblings()
|
||||
assertLength(t, sel.Nodes, 9)
|
||||
}
|
||||
|
||||
func TestSiblings3(t *testing.T) {
|
||||
sel := Doc().Find("body>.container-fluid").Siblings()
|
||||
assertLength(t, sel.Nodes, 0)
|
||||
}
|
||||
|
||||
func TestSiblingsFiltered(t *testing.T) {
|
||||
sel := Doc().Find(".pvk-gutter").SiblingsFiltered(".pvk-content")
|
||||
assertLength(t, sel.Nodes, 3)
|
||||
}
|
||||
|
||||
func TestSiblingsFilteredRollback(t *testing.T) {
|
||||
sel := Doc().Find(".pvk-gutter")
|
||||
sel2 := sel.SiblingsFiltered(".pvk-content").End()
|
||||
assertEqual(t, sel, sel2)
|
||||
}
|
||||
|
||||
func TestNext(t *testing.T) {
|
||||
sel := Doc().Find("h1").Next()
|
||||
assertLength(t, sel.Nodes, 1)
|
||||
}
|
||||
|
||||
func TestNextRollback(t *testing.T) {
|
||||
sel := Doc().Find("h1")
|
||||
sel2 := sel.Next().End()
|
||||
assertEqual(t, sel, sel2)
|
||||
}
|
||||
|
||||
func TestNext2(t *testing.T) {
|
||||
sel := Doc().Find(".close").Next()
|
||||
assertLength(t, sel.Nodes, 1)
|
||||
}
|
||||
|
||||
func TestNextNone(t *testing.T) {
|
||||
sel := Doc().Find("small").Next()
|
||||
assertLength(t, sel.Nodes, 0)
|
||||
}
|
||||
|
||||
func TestNextFiltered(t *testing.T) {
|
||||
sel := Doc().Find(".container-fluid").NextFiltered("div")
|
||||
assertLength(t, sel.Nodes, 2)
|
||||
}
|
||||
|
||||
func TestNextFilteredRollback(t *testing.T) {
|
||||
sel := Doc().Find(".container-fluid")
|
||||
sel2 := sel.NextFiltered("div").End()
|
||||
assertEqual(t, sel, sel2)
|
||||
}
|
||||
|
||||
func TestNextFiltered2(t *testing.T) {
|
||||
sel := Doc().Find(".container-fluid").NextFiltered("[ng-view]")
|
||||
assertLength(t, sel.Nodes, 1)
|
||||
}
|
||||
|
||||
func TestPrev(t *testing.T) {
|
||||
sel := Doc().Find(".red").Prev()
|
||||
assertLength(t, sel.Nodes, 1)
|
||||
assertClass(t, sel, "green")
|
||||
}
|
||||
|
||||
func TestPrevRollback(t *testing.T) {
|
||||
sel := Doc().Find(".red")
|
||||
sel2 := sel.Prev().End()
|
||||
assertEqual(t, sel, sel2)
|
||||
}
|
||||
|
||||
func TestPrev2(t *testing.T) {
|
||||
sel := Doc().Find(".row-fluid").Prev()
|
||||
assertLength(t, sel.Nodes, 5)
|
||||
}
|
||||
|
||||
func TestPrevNone(t *testing.T) {
|
||||
sel := Doc().Find("h2").Prev()
|
||||
assertLength(t, sel.Nodes, 0)
|
||||
}
|
||||
|
||||
func TestPrevFiltered(t *testing.T) {
|
||||
sel := Doc().Find(".row-fluid").PrevFiltered(".row-fluid")
|
||||
assertLength(t, sel.Nodes, 5)
|
||||
}
|
||||
|
||||
func TestPrevFilteredRollback(t *testing.T) {
|
||||
sel := Doc().Find(".row-fluid")
|
||||
sel2 := sel.PrevFiltered(".row-fluid").End()
|
||||
assertEqual(t, sel, sel2)
|
||||
}
|
||||
|
||||
func TestNextAll(t *testing.T) {
|
||||
sel := Doc().Find("#cf2 div:nth-child(1)").NextAll()
|
||||
assertLength(t, sel.Nodes, 3)
|
||||
}
|
||||
|
||||
func TestNextAllRollback(t *testing.T) {
|
||||
sel := Doc().Find("#cf2 div:nth-child(1)")
|
||||
sel2 := sel.NextAll().End()
|
||||
assertEqual(t, sel, sel2)
|
||||
}
|
||||
|
||||
func TestNextAll2(t *testing.T) {
|
||||
sel := Doc().Find("div[ng-cloak]").NextAll()
|
||||
assertLength(t, sel.Nodes, 1)
|
||||
}
|
||||
|
||||
func TestNextAllNone(t *testing.T) {
|
||||
sel := Doc().Find(".footer").NextAll()
|
||||
assertLength(t, sel.Nodes, 0)
|
||||
}
|
||||
|
||||
func TestNextAllFiltered(t *testing.T) {
|
||||
sel := Doc().Find("#cf2 .row-fluid").NextAllFiltered("[ng-cloak]")
|
||||
assertLength(t, sel.Nodes, 2)
|
||||
}
|
||||
|
||||
func TestNextAllFilteredRollback(t *testing.T) {
|
||||
sel := Doc().Find("#cf2 .row-fluid")
|
||||
sel2 := sel.NextAllFiltered("[ng-cloak]").End()
|
||||
assertEqual(t, sel, sel2)
|
||||
}
|
||||
|
||||
func TestNextAllFiltered2(t *testing.T) {
|
||||
sel := Doc().Find(".close").NextAllFiltered("h4")
|
||||
assertLength(t, sel.Nodes, 1)
|
||||
}
|
||||
|
||||
func TestPrevAll(t *testing.T) {
|
||||
sel := Doc().Find("[ng-view]").PrevAll()
|
||||
assertLength(t, sel.Nodes, 2)
|
||||
}
|
||||
|
||||
func TestPrevAllOrder(t *testing.T) {
|
||||
sel := Doc().Find("[ng-view]").PrevAll()
|
||||
assertLength(t, sel.Nodes, 2)
|
||||
assertSelectionIs(t, sel, "#cf4", "#cf3")
|
||||
}
|
||||
|
||||
func TestPrevAllRollback(t *testing.T) {
|
||||
sel := Doc().Find("[ng-view]")
|
||||
sel2 := sel.PrevAll().End()
|
||||
assertEqual(t, sel, sel2)
|
||||
}
|
||||
|
||||
func TestPrevAll2(t *testing.T) {
|
||||
sel := Doc().Find(".pvk-gutter").PrevAll()
|
||||
assertLength(t, sel.Nodes, 6)
|
||||
}
|
||||
|
||||
func TestPrevAllFiltered(t *testing.T) {
|
||||
sel := Doc().Find(".pvk-gutter").PrevAllFiltered(".pvk-content")
|
||||
assertLength(t, sel.Nodes, 3)
|
||||
}
|
||||
|
||||
func TestPrevAllFilteredRollback(t *testing.T) {
|
||||
sel := Doc().Find(".pvk-gutter")
|
||||
sel2 := sel.PrevAllFiltered(".pvk-content").End()
|
||||
assertEqual(t, sel, sel2)
|
||||
}
|
||||
|
||||
func TestNextUntil(t *testing.T) {
|
||||
sel := Doc().Find(".alert a").NextUntil("p")
|
||||
assertLength(t, sel.Nodes, 1)
|
||||
assertSelectionIs(t, sel, "h4")
|
||||
}
|
||||
|
||||
func TestNextUntil2(t *testing.T) {
|
||||
sel := Doc().Find("#cf2-1").NextUntil("[ng-cloak]")
|
||||
assertLength(t, sel.Nodes, 1)
|
||||
assertSelectionIs(t, sel, "#cf2-2")
|
||||
}
|
||||
|
||||
func TestNextUntilOrder(t *testing.T) {
|
||||
sel := Doc().Find("#cf2-1").NextUntil("#cf2-4")
|
||||
assertLength(t, sel.Nodes, 2)
|
||||
assertSelectionIs(t, sel, "#cf2-2", "#cf2-3")
|
||||
}
|
||||
|
||||
func TestNextUntilRollback(t *testing.T) {
|
||||
sel := Doc().Find("#cf2-1")
|
||||
sel2 := sel.PrevUntil("#cf2-4").End()
|
||||
assertEqual(t, sel, sel2)
|
||||
}
|
||||
|
||||
func TestNextUntilSelection(t *testing.T) {
|
||||
sel := Doc2().Find("#n2")
|
||||
sel2 := Doc2().Find("#n4")
|
||||
sel2 = sel.NextUntilSelection(sel2)
|
||||
assertLength(t, sel2.Nodes, 1)
|
||||
assertSelectionIs(t, sel2, "#n3")
|
||||
}
|
||||
|
||||
func TestNextUntilSelectionRollback(t *testing.T) {
|
||||
sel := Doc2().Find("#n2")
|
||||
sel2 := Doc2().Find("#n4")
|
||||
sel2 = sel.NextUntilSelection(sel2).End()
|
||||
assertEqual(t, sel, sel2)
|
||||
}
|
||||
|
||||
func TestNextUntilNodes(t *testing.T) {
|
||||
sel := Doc2().Find("#n2")
|
||||
sel2 := Doc2().Find("#n5")
|
||||
sel2 = sel.NextUntilNodes(sel2.Nodes...)
|
||||
assertLength(t, sel2.Nodes, 2)
|
||||
assertSelectionIs(t, sel2, "#n3", "#n4")
|
||||
}
|
||||
|
||||
func TestNextUntilNodesRollback(t *testing.T) {
|
||||
sel := Doc2().Find("#n2")
|
||||
sel2 := Doc2().Find("#n5")
|
||||
sel2 = sel.NextUntilNodes(sel2.Nodes...).End()
|
||||
assertEqual(t, sel, sel2)
|
||||
}
|
||||
|
||||
func TestPrevUntil(t *testing.T) {
|
||||
sel := Doc().Find(".alert p").PrevUntil("a")
|
||||
assertLength(t, sel.Nodes, 1)
|
||||
assertSelectionIs(t, sel, "h4")
|
||||
}
|
||||
|
||||
func TestPrevUntil2(t *testing.T) {
|
||||
sel := Doc().Find("[ng-cloak]").PrevUntil(":not([ng-cloak])")
|
||||
assertLength(t, sel.Nodes, 1)
|
||||
assertSelectionIs(t, sel, "[ng-cloak]")
|
||||
}
|
||||
|
||||
func TestPrevUntilOrder(t *testing.T) {
|
||||
sel := Doc().Find("#cf2-4").PrevUntil("#cf2-1")
|
||||
assertLength(t, sel.Nodes, 2)
|
||||
assertSelectionIs(t, sel, "#cf2-3", "#cf2-2")
|
||||
}
|
||||
|
||||
func TestPrevUntilRollback(t *testing.T) {
|
||||
sel := Doc().Find("#cf2-4")
|
||||
sel2 := sel.PrevUntil("#cf2-1").End()
|
||||
assertEqual(t, sel, sel2)
|
||||
}
|
||||
|
||||
func TestPrevUntilSelection(t *testing.T) {
|
||||
sel := Doc2().Find("#n4")
|
||||
sel2 := Doc2().Find("#n2")
|
||||
sel2 = sel.PrevUntilSelection(sel2)
|
||||
assertLength(t, sel2.Nodes, 1)
|
||||
assertSelectionIs(t, sel2, "#n3")
|
||||
}
|
||||
|
||||
func TestPrevUntilSelectionRollback(t *testing.T) {
|
||||
sel := Doc2().Find("#n4")
|
||||
sel2 := Doc2().Find("#n2")
|
||||
sel2 = sel.PrevUntilSelection(sel2).End()
|
||||
assertEqual(t, sel, sel2)
|
||||
}
|
||||
|
||||
func TestPrevUntilNodes(t *testing.T) {
|
||||
sel := Doc2().Find("#n5")
|
||||
sel2 := Doc2().Find("#n2")
|
||||
sel2 = sel.PrevUntilNodes(sel2.Nodes...)
|
||||
assertLength(t, sel2.Nodes, 2)
|
||||
assertSelectionIs(t, sel2, "#n4", "#n3")
|
||||
}
|
||||
|
||||
func TestPrevUntilNodesRollback(t *testing.T) {
|
||||
sel := Doc2().Find("#n5")
|
||||
sel2 := Doc2().Find("#n2")
|
||||
sel2 = sel.PrevUntilNodes(sel2.Nodes...).End()
|
||||
assertEqual(t, sel, sel2)
|
||||
}
|
||||
|
||||
func TestNextFilteredUntil(t *testing.T) {
|
||||
sel := Doc2().Find(".two").NextFilteredUntil(".even", ".six")
|
||||
assertLength(t, sel.Nodes, 4)
|
||||
assertSelectionIs(t, sel, "#n3", "#n5", "#nf3", "#nf5")
|
||||
}
|
||||
|
||||
func TestNextFilteredUntilRollback(t *testing.T) {
|
||||
sel := Doc2().Find(".two")
|
||||
sel2 := sel.NextFilteredUntil(".even", ".six").End()
|
||||
assertEqual(t, sel, sel2)
|
||||
}
|
||||
|
||||
func TestNextFilteredUntilSelection(t *testing.T) {
|
||||
sel := Doc2().Find(".even")
|
||||
sel2 := Doc2().Find(".five")
|
||||
sel = sel.NextFilteredUntilSelection(".even", sel2)
|
||||
assertLength(t, sel.Nodes, 2)
|
||||
assertSelectionIs(t, sel, "#n3", "#nf3")
|
||||
}
|
||||
|
||||
func TestNextFilteredUntilSelectionRollback(t *testing.T) {
|
||||
sel := Doc2().Find(".even")
|
||||
sel2 := Doc2().Find(".five")
|
||||
sel3 := sel.NextFilteredUntilSelection(".even", sel2).End()
|
||||
assertEqual(t, sel, sel3)
|
||||
}
|
||||
|
||||
func TestNextFilteredUntilNodes(t *testing.T) {
|
||||
sel := Doc2().Find(".even")
|
||||
sel2 := Doc2().Find(".four")
|
||||
sel = sel.NextFilteredUntilNodes(".odd", sel2.Nodes...)
|
||||
assertLength(t, sel.Nodes, 4)
|
||||
assertSelectionIs(t, sel, "#n2", "#n6", "#nf2", "#nf6")
|
||||
}
|
||||
|
||||
func TestNextFilteredUntilNodesRollback(t *testing.T) {
|
||||
sel := Doc2().Find(".even")
|
||||
sel2 := Doc2().Find(".four")
|
||||
sel3 := sel.NextFilteredUntilNodes(".odd", sel2.Nodes...).End()
|
||||
assertEqual(t, sel, sel3)
|
||||
}
|
||||
|
||||
func TestPrevFilteredUntil(t *testing.T) {
|
||||
sel := Doc2().Find(".five").PrevFilteredUntil(".odd", ".one")
|
||||
assertLength(t, sel.Nodes, 4)
|
||||
assertSelectionIs(t, sel, "#n4", "#n2", "#nf4", "#nf2")
|
||||
}
|
||||
|
||||
func TestPrevFilteredUntilRollback(t *testing.T) {
|
||||
sel := Doc2().Find(".four")
|
||||
sel2 := sel.PrevFilteredUntil(".odd", ".one").End()
|
||||
assertEqual(t, sel, sel2)
|
||||
}
|
||||
|
||||
func TestPrevFilteredUntilSelection(t *testing.T) {
|
||||
sel := Doc2().Find(".odd")
|
||||
sel2 := Doc2().Find(".two")
|
||||
sel = sel.PrevFilteredUntilSelection(".odd", sel2)
|
||||
assertLength(t, sel.Nodes, 2)
|
||||
assertSelectionIs(t, sel, "#n4", "#nf4")
|
||||
}
|
||||
|
||||
func TestPrevFilteredUntilSelectionRollback(t *testing.T) {
|
||||
sel := Doc2().Find(".even")
|
||||
sel2 := Doc2().Find(".five")
|
||||
sel3 := sel.PrevFilteredUntilSelection(".even", sel2).End()
|
||||
assertEqual(t, sel, sel3)
|
||||
}
|
||||
|
||||
func TestPrevFilteredUntilNodes(t *testing.T) {
|
||||
sel := Doc2().Find(".even")
|
||||
sel2 := Doc2().Find(".four")
|
||||
sel = sel.PrevFilteredUntilNodes(".odd", sel2.Nodes...)
|
||||
assertLength(t, sel.Nodes, 2)
|
||||
assertSelectionIs(t, sel, "#n2", "#nf2")
|
||||
}
|
||||
|
||||
func TestPrevFilteredUntilNodesRollback(t *testing.T) {
|
||||
sel := Doc2().Find(".even")
|
||||
sel2 := Doc2().Find(".four")
|
||||
sel3 := sel.PrevFilteredUntilNodes(".odd", sel2.Nodes...).End()
|
||||
assertEqual(t, sel, sel3)
|
||||
}
|
||||
|
||||
func TestClosestItself(t *testing.T) {
|
||||
sel := Doc2().Find(".three")
|
||||
sel2 := sel.Closest(".row")
|
||||
assertLength(t, sel2.Nodes, sel.Length())
|
||||
assertSelectionIs(t, sel2, "#n3", "#nf3")
|
||||
}
|
||||
|
||||
func TestClosestNoDupes(t *testing.T) {
|
||||
sel := Doc().Find(".span12")
|
||||
sel2 := sel.Closest(".pvk-content")
|
||||
assertLength(t, sel2.Nodes, 1)
|
||||
assertClass(t, sel2, "pvk-content")
|
||||
}
|
||||
|
||||
func TestClosestNone(t *testing.T) {
|
||||
sel := Doc().Find("h4")
|
||||
sel2 := sel.Closest("a")
|
||||
assertLength(t, sel2.Nodes, 0)
|
||||
}
|
||||
|
||||
func TestClosestMany(t *testing.T) {
|
||||
sel := Doc().Find(".container-fluid")
|
||||
sel2 := sel.Closest(".pvk-content")
|
||||
assertLength(t, sel2.Nodes, 2)
|
||||
assertSelectionIs(t, sel2, "#pc1", "#pc2")
|
||||
}
|
||||
|
||||
func TestClosestRollback(t *testing.T) {
|
||||
sel := Doc().Find(".container-fluid")
|
||||
sel2 := sel.Closest(".pvk-content").End()
|
||||
assertEqual(t, sel, sel2)
|
||||
}
|
||||
|
||||
func TestClosestSelectionItself(t *testing.T) {
|
||||
sel := Doc2().Find(".three")
|
||||
sel2 := sel.ClosestSelection(Doc2().Find(".row"))
|
||||
assertLength(t, sel2.Nodes, sel.Length())
|
||||
}
|
||||
|
||||
func TestClosestSelectionNoDupes(t *testing.T) {
|
||||
sel := Doc().Find(".span12")
|
||||
sel2 := sel.ClosestSelection(Doc().Find(".pvk-content"))
|
||||
assertLength(t, sel2.Nodes, 1)
|
||||
assertClass(t, sel2, "pvk-content")
|
||||
}
|
||||
|
||||
func TestClosestSelectionNone(t *testing.T) {
|
||||
sel := Doc().Find("h4")
|
||||
sel2 := sel.ClosestSelection(Doc().Find("a"))
|
||||
assertLength(t, sel2.Nodes, 0)
|
||||
}
|
||||
|
||||
func TestClosestSelectionMany(t *testing.T) {
|
||||
sel := Doc().Find(".container-fluid")
|
||||
sel2 := sel.ClosestSelection(Doc().Find(".pvk-content"))
|
||||
assertLength(t, sel2.Nodes, 2)
|
||||
assertSelectionIs(t, sel2, "#pc1", "#pc2")
|
||||
}
|
||||
|
||||
func TestClosestSelectionRollback(t *testing.T) {
|
||||
sel := Doc().Find(".container-fluid")
|
||||
sel2 := sel.ClosestSelection(Doc().Find(".pvk-content")).End()
|
||||
assertEqual(t, sel, sel2)
|
||||
}
|
||||
|
||||
func TestClosestNodesItself(t *testing.T) {
|
||||
sel := Doc2().Find(".three")
|
||||
sel2 := sel.ClosestNodes(Doc2().Find(".row").Nodes...)
|
||||
assertLength(t, sel2.Nodes, sel.Length())
|
||||
}
|
||||
|
||||
func TestClosestNodesNoDupes(t *testing.T) {
|
||||
sel := Doc().Find(".span12")
|
||||
sel2 := sel.ClosestNodes(Doc().Find(".pvk-content").Nodes...)
|
||||
assertLength(t, sel2.Nodes, 1)
|
||||
assertClass(t, sel2, "pvk-content")
|
||||
}
|
||||
|
||||
func TestClosestNodesNone(t *testing.T) {
|
||||
sel := Doc().Find("h4")
|
||||
sel2 := sel.ClosestNodes(Doc().Find("a").Nodes...)
|
||||
assertLength(t, sel2.Nodes, 0)
|
||||
}
|
||||
|
||||
func TestClosestNodesMany(t *testing.T) {
|
||||
sel := Doc().Find(".container-fluid")
|
||||
sel2 := sel.ClosestNodes(Doc().Find(".pvk-content").Nodes...)
|
||||
assertLength(t, sel2.Nodes, 2)
|
||||
assertSelectionIs(t, sel2, "#pc1", "#pc2")
|
||||
}
|
||||
|
||||
func TestClosestNodesRollback(t *testing.T) {
|
||||
sel := Doc().Find(".container-fluid")
|
||||
sel2 := sel.ClosestNodes(Doc().Find(".pvk-content").Nodes...).End()
|
||||
assertEqual(t, sel, sel2)
|
||||
}
|
||||
|
||||
func TestIssue26(t *testing.T) {
|
||||
img1 := `<img src="assets/images/gallery/thumb-1.jpg" alt="150x150" />`
|
||||
img2 := `<img alt="150x150" src="assets/images/gallery/thumb-1.jpg" />`
|
||||
cases := []struct {
|
||||
s string
|
||||
l int
|
||||
}{
|
||||
{s: img1 + img2, l: 2},
|
||||
{s: img1, l: 1},
|
||||
{s: img2, l: 1},
|
||||
}
|
||||
for _, c := range cases {
|
||||
doc, err := NewDocumentFromReader(strings.NewReader(c.s))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
sel := doc.Find("img[src]")
|
||||
assertLength(t, sel.Nodes, c.l)
|
||||
}
|
||||
}
|
192
vendor/github.com/PuerkitoBio/goquery/type_test.go
generated
vendored
Normal file
192
vendor/github.com/PuerkitoBio/goquery/type_test.go
generated
vendored
Normal file
|
@ -0,0 +1,192 @@
|
|||
package goquery
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"golang.org/x/net/html"
|
||||
)
|
||||
|
||||
// Test helper functions and members
|
||||
var doc *Document
|
||||
var doc2 *Document
|
||||
var doc3 *Document
|
||||
var docB *Document
|
||||
var docW *Document
|
||||
|
||||
func Doc() *Document {
|
||||
if doc == nil {
|
||||
doc = loadDoc("page.html")
|
||||
}
|
||||
return doc
|
||||
}
|
||||
func DocClone() *Document {
|
||||
return CloneDocument(Doc())
|
||||
}
|
||||
func Doc2() *Document {
|
||||
if doc2 == nil {
|
||||
doc2 = loadDoc("page2.html")
|
||||
}
|
||||
return doc2
|
||||
}
|
||||
func Doc2Clone() *Document {
|
||||
return CloneDocument(Doc2())
|
||||
}
|
||||
func Doc3() *Document {
|
||||
if doc3 == nil {
|
||||
doc3 = loadDoc("page3.html")
|
||||
}
|
||||
return doc3
|
||||
}
|
||||
func Doc3Clone() *Document {
|
||||
return CloneDocument(Doc3())
|
||||
}
|
||||
func DocB() *Document {
|
||||
if docB == nil {
|
||||
docB = loadDoc("gotesting.html")
|
||||
}
|
||||
return docB
|
||||
}
|
||||
func DocBClone() *Document {
|
||||
return CloneDocument(DocB())
|
||||
}
|
||||
func DocW() *Document {
|
||||
if docW == nil {
|
||||
docW = loadDoc("gowiki.html")
|
||||
}
|
||||
return docW
|
||||
}
|
||||
func DocWClone() *Document {
|
||||
return CloneDocument(DocW())
|
||||
}
|
||||
|
||||
func assertLength(t *testing.T, nodes []*html.Node, length int) {
|
||||
if len(nodes) != length {
|
||||
t.Errorf("Expected %d nodes, found %d.", length, len(nodes))
|
||||
for i, n := range nodes {
|
||||
t.Logf("Node %d: %+v.", i, n)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func assertClass(t *testing.T, sel *Selection, class string) {
|
||||
if !sel.HasClass(class) {
|
||||
t.Errorf("Expected node to have class %s, found %+v.", class, sel.Get(0))
|
||||
}
|
||||
}
|
||||
|
||||
func assertPanic(t *testing.T) {
|
||||
if e := recover(); e == nil {
|
||||
t.Error("Expected a panic.")
|
||||
}
|
||||
}
|
||||
|
||||
func assertEqual(t *testing.T, s1 *Selection, s2 *Selection) {
|
||||
if s1 != s2 {
|
||||
t.Error("Expected selection objects to be the same.")
|
||||
}
|
||||
}
|
||||
|
||||
func assertSelectionIs(t *testing.T, sel *Selection, is ...string) {
|
||||
for i := 0; i < sel.Length(); i++ {
|
||||
if !sel.Eq(i).Is(is[i]) {
|
||||
t.Errorf("Expected node %d to be %s, found %+v", i, is[i], sel.Get(i))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func printSel(t *testing.T, sel *Selection) {
|
||||
if testing.Verbose() {
|
||||
h, err := sel.Html()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
t.Log(h)
|
||||
}
|
||||
}
|
||||
|
||||
func loadDoc(page string) *Document {
|
||||
var f *os.File
|
||||
var e error
|
||||
|
||||
if f, e = os.Open(fmt.Sprintf("./testdata/%s", page)); e != nil {
|
||||
panic(e.Error())
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
var node *html.Node
|
||||
if node, e = html.Parse(f); e != nil {
|
||||
panic(e.Error())
|
||||
}
|
||||
return NewDocumentFromNode(node)
|
||||
}
|
||||
|
||||
func TestNewDocument(t *testing.T) {
|
||||
if f, e := os.Open("./testdata/page.html"); e != nil {
|
||||
t.Error(e.Error())
|
||||
} else {
|
||||
defer f.Close()
|
||||
if node, e := html.Parse(f); e != nil {
|
||||
t.Error(e.Error())
|
||||
} else {
|
||||
doc = NewDocumentFromNode(node)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestNewDocumentFromReader(t *testing.T) {
|
||||
cases := []struct {
|
||||
src string
|
||||
err bool
|
||||
sel string
|
||||
cnt int
|
||||
}{
|
||||
0: {
|
||||
src: `
|
||||
<html>
|
||||
<head>
|
||||
<title>Test</title>
|
||||
<body>
|
||||
<h1>Hi</h1>
|
||||
</body>
|
||||
</html>`,
|
||||
sel: "h1",
|
||||
cnt: 1,
|
||||
},
|
||||
1: {
|
||||
// Actually pretty hard to make html.Parse return an error
|
||||
// based on content...
|
||||
src: `<html><body><aef<eqf>>>qq></body></ht>`,
|
||||
},
|
||||
}
|
||||
buf := bytes.NewBuffer(nil)
|
||||
|
||||
for i, c := range cases {
|
||||
buf.Reset()
|
||||
buf.WriteString(c.src)
|
||||
|
||||
d, e := NewDocumentFromReader(buf)
|
||||
if (e != nil) != c.err {
|
||||
if c.err {
|
||||
t.Errorf("[%d] - expected error, got none", i)
|
||||
} else {
|
||||
t.Errorf("[%d] - expected no error, got %s", i, e)
|
||||
}
|
||||
}
|
||||
if c.sel != "" {
|
||||
s := d.Find(c.sel)
|
||||
if s.Length() != c.cnt {
|
||||
t.Errorf("[%d] - expected %d nodes, found %d", i, c.cnt, s.Length())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestNewDocumentFromResponseNil(t *testing.T) {
|
||||
_, e := NewDocumentFromResponse(nil)
|
||||
if e == nil {
|
||||
t.Error("Expected error, got none")
|
||||
}
|
||||
}
|
0
vendor/github.com/andybalholm/cascadia/LICENSE
generated
vendored
Normal file → Executable file
0
vendor/github.com/andybalholm/cascadia/LICENSE
generated
vendored
Normal file → Executable file
53
vendor/github.com/andybalholm/cascadia/benchmark_test.go
generated
vendored
Normal file
53
vendor/github.com/andybalholm/cascadia/benchmark_test.go
generated
vendored
Normal file
|
@ -0,0 +1,53 @@
|
|||
package cascadia
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"golang.org/x/net/html"
|
||||
)
|
||||
|
||||
func MustParseHTML(doc string) *html.Node {
|
||||
dom, err := html.Parse(strings.NewReader(doc))
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return dom
|
||||
}
|
||||
|
||||
var selector = MustCompile(`div.matched`)
|
||||
var doc = `<!DOCTYPE html>
|
||||
<html>
|
||||
<body>
|
||||
<div class="matched">
|
||||
<div>
|
||||
<div class="matched"></div>
|
||||
<div class="matched"></div>
|
||||
<div class="matched"></div>
|
||||
<div class="matched"></div>
|
||||
<div class="matched"></div>
|
||||
<div class="matched"></div>
|
||||
<div class="matched"></div>
|
||||
<div class="matched"></div>
|
||||
<div class="matched"></div>
|
||||
<div class="matched"></div>
|
||||
<div class="matched"></div>
|
||||
<div class="matched"></div>
|
||||
<div class="matched"></div>
|
||||
<div class="matched"></div>
|
||||
<div class="matched"></div>
|
||||
<div class="matched"></div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
`
|
||||
var dom = MustParseHTML(doc)
|
||||
|
||||
func BenchmarkMatchAll(b *testing.B) {
|
||||
var matches []*html.Node
|
||||
for i := 0; i < b.N; i++ {
|
||||
matches = selector.MatchAll(dom)
|
||||
}
|
||||
_ = matches
|
||||
}
|
86
vendor/github.com/andybalholm/cascadia/parser_test.go
generated
vendored
Normal file
86
vendor/github.com/andybalholm/cascadia/parser_test.go
generated
vendored
Normal file
|
@ -0,0 +1,86 @@
|
|||
package cascadia
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
var identifierTests = map[string]string{
|
||||
"x": "x",
|
||||
"96": "",
|
||||
"-x": "-x",
|
||||
`r\e9 sumé`: "résumé",
|
||||
`a\"b`: `a"b`,
|
||||
}
|
||||
|
||||
func TestParseIdentifier(t *testing.T) {
|
||||
for source, want := range identifierTests {
|
||||
p := &parser{s: source}
|
||||
got, err := p.parseIdentifier()
|
||||
|
||||
if err != nil {
|
||||
if want == "" {
|
||||
// It was supposed to be an error.
|
||||
continue
|
||||
}
|
||||
t.Errorf("parsing %q: got error (%s), want %q", source, err, want)
|
||||
continue
|
||||
}
|
||||
|
||||
if want == "" {
|
||||
if err == nil {
|
||||
t.Errorf("parsing %q: got %q, want error", source, got)
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
if p.i < len(source) {
|
||||
t.Errorf("parsing %q: %d bytes left over", source, len(source)-p.i)
|
||||
continue
|
||||
}
|
||||
|
||||
if got != want {
|
||||
t.Errorf("parsing %q: got %q, want %q", source, got, want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var stringTests = map[string]string{
|
||||
`"x"`: "x",
|
||||
`'x'`: "x",
|
||||
`'x`: "",
|
||||
"'x\\\r\nx'": "xx",
|
||||
`"r\e9 sumé"`: "résumé",
|
||||
`"a\"b"`: `a"b`,
|
||||
}
|
||||
|
||||
func TestParseString(t *testing.T) {
|
||||
for source, want := range stringTests {
|
||||
p := &parser{s: source}
|
||||
got, err := p.parseString()
|
||||
|
||||
if err != nil {
|
||||
if want == "" {
|
||||
// It was supposed to be an error.
|
||||
continue
|
||||
}
|
||||
t.Errorf("parsing %q: got error (%s), want %q", source, err, want)
|
||||
continue
|
||||
}
|
||||
|
||||
if want == "" {
|
||||
if err == nil {
|
||||
t.Errorf("parsing %q: got %q, want error", source, got)
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
if p.i < len(source) {
|
||||
t.Errorf("parsing %q: %d bytes left over", source, len(source)-p.i)
|
||||
continue
|
||||
}
|
||||
|
||||
if got != want {
|
||||
t.Errorf("parsing %q: got %q, want %q", source, got, want)
|
||||
}
|
||||
}
|
||||
}
|
559
vendor/github.com/andybalholm/cascadia/selector_test.go
generated
vendored
Normal file
559
vendor/github.com/andybalholm/cascadia/selector_test.go
generated
vendored
Normal file
|
@ -0,0 +1,559 @@
|
|||
package cascadia
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"golang.org/x/net/html"
|
||||
)
|
||||
|
||||
type selectorTest struct {
|
||||
HTML, selector string
|
||||
results []string
|
||||
}
|
||||
|
||||
func nodeString(n *html.Node) string {
|
||||
switch n.Type {
|
||||
case html.TextNode:
|
||||
return n.Data
|
||||
case html.ElementNode:
|
||||
return html.Token{
|
||||
Type: html.StartTagToken,
|
||||
Data: n.Data,
|
||||
Attr: n.Attr,
|
||||
}.String()
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
var selectorTests = []selectorTest{
|
||||
{
|
||||
`<body><address>This address...</address></body>`,
|
||||
"address",
|
||||
[]string{
|
||||
"<address>",
|
||||
},
|
||||
},
|
||||
{
|
||||
`<html><head></head><body></body></html>`,
|
||||
"*",
|
||||
[]string{
|
||||
"",
|
||||
"<html>",
|
||||
"<head>",
|
||||
"<body>",
|
||||
},
|
||||
},
|
||||
{
|
||||
`<p id="foo"><p id="bar">`,
|
||||
"#foo",
|
||||
[]string{
|
||||
`<p id="foo">`,
|
||||
},
|
||||
},
|
||||
{
|
||||
`<ul><li id="t1"><p id="t1">`,
|
||||
"li#t1",
|
||||
[]string{
|
||||
`<li id="t1">`,
|
||||
},
|
||||
},
|
||||
{
|
||||
`<ol><li id="t4"><li id="t44">`,
|
||||
"*#t4",
|
||||
[]string{
|
||||
`<li id="t4">`,
|
||||
},
|
||||
},
|
||||
{
|
||||
`<ul><li class="t1"><li class="t2">`,
|
||||
".t1",
|
||||
[]string{
|
||||
`<li class="t1">`,
|
||||
},
|
||||
},
|
||||
{
|
||||
`<p class="t1 t2">`,
|
||||
"p.t1",
|
||||
[]string{
|
||||
`<p class="t1 t2">`,
|
||||
},
|
||||
},
|
||||
{
|
||||
`<div class="test">`,
|
||||
"div.teST",
|
||||
[]string{},
|
||||
},
|
||||
{
|
||||
`<p class="t1 t2">`,
|
||||
".t1.fail",
|
||||
[]string{},
|
||||
},
|
||||
{
|
||||
`<p class="t1 t2">`,
|
||||
"p.t1.t2",
|
||||
[]string{
|
||||
`<p class="t1 t2">`,
|
||||
},
|
||||
},
|
||||
{
|
||||
`<p><p title="title">`,
|
||||
"p[title]",
|
||||
[]string{
|
||||
`<p title="title">`,
|
||||
},
|
||||
},
|
||||
{
|
||||
`<address><address title="foo"><address title="bar">`,
|
||||
`address[title="foo"]`,
|
||||
[]string{
|
||||
`<address title="foo">`,
|
||||
},
|
||||
},
|
||||
{
|
||||
`<p title="tot foo bar">`,
|
||||
`[ title ~= foo ]`,
|
||||
[]string{
|
||||
`<p title="tot foo bar">`,
|
||||
},
|
||||
},
|
||||
{
|
||||
`<p title="hello world">`,
|
||||
`[title~="hello world"]`,
|
||||
[]string{},
|
||||
},
|
||||
{
|
||||
`<p lang="en"><p lang="en-gb"><p lang="enough"><p lang="fr-en">`,
|
||||
`[lang|="en"]`,
|
||||
[]string{
|
||||
`<p lang="en">`,
|
||||
`<p lang="en-gb">`,
|
||||
},
|
||||
},
|
||||
{
|
||||
`<p title="foobar"><p title="barfoo">`,
|
||||
`[title^="foo"]`,
|
||||
[]string{
|
||||
`<p title="foobar">`,
|
||||
},
|
||||
},
|
||||
{
|
||||
`<p title="foobar"><p title="barfoo">`,
|
||||
`[title$="bar"]`,
|
||||
[]string{
|
||||
`<p title="foobar">`,
|
||||
},
|
||||
},
|
||||
{
|
||||
`<p title="foobarufoo">`,
|
||||
`[title*="bar"]`,
|
||||
[]string{
|
||||
`<p title="foobarufoo">`,
|
||||
},
|
||||
},
|
||||
{
|
||||
`<p class="t1 t2">`,
|
||||
".t1:not(.t2)",
|
||||
[]string{},
|
||||
},
|
||||
{
|
||||
`<div class="t3">`,
|
||||
`div:not(.t1)`,
|
||||
[]string{
|
||||
`<div class="t3">`,
|
||||
},
|
||||
},
|
||||
{
|
||||
`<ol><li id=1><li id=2><li id=3></ol>`,
|
||||
`li:nth-child(odd)`,
|
||||
[]string{
|
||||
`<li id="1">`,
|
||||
`<li id="3">`,
|
||||
},
|
||||
},
|
||||
{
|
||||
`<ol><li id=1><li id=2><li id=3></ol>`,
|
||||
`li:nth-child(even)`,
|
||||
[]string{
|
||||
`<li id="2">`,
|
||||
},
|
||||
},
|
||||
{
|
||||
`<ol><li id=1><li id=2><li id=3></ol>`,
|
||||
`li:nth-child(-n+2)`,
|
||||
[]string{
|
||||
`<li id="1">`,
|
||||
`<li id="2">`,
|
||||
},
|
||||
},
|
||||
{
|
||||
`<ol><li id=1><li id=2><li id=3></ol>`,
|
||||
`li:nth-child(3n+1)`,
|
||||
[]string{
|
||||
`<li id="1">`,
|
||||
},
|
||||
},
|
||||
{
|
||||
`<ol><li id=1><li id=2><li id=3><li id=4></ol>`,
|
||||
`li:nth-last-child(odd)`,
|
||||
[]string{
|
||||
`<li id="2">`,
|
||||
`<li id="4">`,
|
||||
},
|
||||
},
|
||||
{
|
||||
`<ol><li id=1><li id=2><li id=3><li id=4></ol>`,
|
||||
`li:nth-last-child(even)`,
|
||||
[]string{
|
||||
`<li id="1">`,
|
||||
`<li id="3">`,
|
||||
},
|
||||
},
|
||||
{
|
||||
`<ol><li id=1><li id=2><li id=3><li id=4></ol>`,
|
||||
`li:nth-last-child(-n+2)`,
|
||||
[]string{
|
||||
`<li id="3">`,
|
||||
`<li id="4">`,
|
||||
},
|
||||
},
|
||||
{
|
||||
`<ol><li id=1><li id=2><li id=3><li id=4></ol>`,
|
||||
`li:nth-last-child(3n+1)`,
|
||||
[]string{
|
||||
`<li id="1">`,
|
||||
`<li id="4">`,
|
||||
},
|
||||
},
|
||||
{
|
||||
`<p>some text <span id="1">and a span</span><span id="2"> and another</span></p>`,
|
||||
`span:first-child`,
|
||||
[]string{
|
||||
`<span id="1">`,
|
||||
},
|
||||
},
|
||||
{
|
||||
`<span>a span</span> and some text`,
|
||||
`span:last-child`,
|
||||
[]string{
|
||||
`<span>`,
|
||||
},
|
||||
},
|
||||
{
|
||||
`<address></address><p id=1><p id=2>`,
|
||||
`p:nth-of-type(2)`,
|
||||
[]string{
|
||||
`<p id="2">`,
|
||||
},
|
||||
},
|
||||
{
|
||||
`<address></address><p id=1><p id=2></p><a>`,
|
||||
`p:nth-last-of-type(2)`,
|
||||
[]string{
|
||||
`<p id="1">`,
|
||||
},
|
||||
},
|
||||
{
|
||||
`<address></address><p id=1><p id=2></p><a>`,
|
||||
`p:last-of-type`,
|
||||
[]string{
|
||||
`<p id="2">`,
|
||||
},
|
||||
},
|
||||
{
|
||||
`<address></address><p id=1><p id=2></p><a>`,
|
||||
`p:first-of-type`,
|
||||
[]string{
|
||||
`<p id="1">`,
|
||||
},
|
||||
},
|
||||
{
|
||||
`<div><p id="1"></p><a></a></div><div><p id="2"></p></div>`,
|
||||
`p:only-child`,
|
||||
[]string{
|
||||
`<p id="2">`,
|
||||
},
|
||||
},
|
||||
{
|
||||
`<div><p id="1"></p><a></a></div><div><p id="2"></p><p id="3"></p></div>`,
|
||||
`p:only-of-type`,
|
||||
[]string{
|
||||
`<p id="1">`,
|
||||
},
|
||||
},
|
||||
{
|
||||
`<p id="1"><!-- --><p id="2">Hello<p id="3"><span>`,
|
||||
`:empty`,
|
||||
[]string{
|
||||
`<head>`,
|
||||
`<p id="1">`,
|
||||
`<span>`,
|
||||
},
|
||||
},
|
||||
{
|
||||
`<div><p id="1"><table><tr><td><p id="2"></table></div><p id="3">`,
|
||||
`div p`,
|
||||
[]string{
|
||||
`<p id="1">`,
|
||||
`<p id="2">`,
|
||||
},
|
||||
},
|
||||
{
|
||||
`<div><p id="1"><table><tr><td><p id="2"></table></div><p id="3">`,
|
||||
`div table p`,
|
||||
[]string{
|
||||
`<p id="2">`,
|
||||
},
|
||||
},
|
||||
{
|
||||
`<div><p id="1"><div><p id="2"></div><table><tr><td><p id="3"></table></div>`,
|
||||
`div > p`,
|
||||
[]string{
|
||||
`<p id="1">`,
|
||||
`<p id="2">`,
|
||||
},
|
||||
},
|
||||
{
|
||||
`<p id="1"><p id="2"></p><address></address><p id="3">`,
|
||||
`p ~ p`,
|
||||
[]string{
|
||||
`<p id="2">`,
|
||||
`<p id="3">`,
|
||||
},
|
||||
},
|
||||
{
|
||||
`<p id="1"></p>
|
||||
<!--comment-->
|
||||
<p id="2"></p><address></address><p id="3">`,
|
||||
`p + p`,
|
||||
[]string{
|
||||
`<p id="2">`,
|
||||
},
|
||||
},
|
||||
{
|
||||
`<ul><li></li><li></li></ul><p>`,
|
||||
`li, p`,
|
||||
[]string{
|
||||
"<li>",
|
||||
"<li>",
|
||||
"<p>",
|
||||
},
|
||||
},
|
||||
{
|
||||
`<p id="1"><p id="2"></p><address></address><p id="3">`,
|
||||
`p +/*This is a comment*/ p`,
|
||||
[]string{
|
||||
`<p id="2">`,
|
||||
},
|
||||
},
|
||||
{
|
||||
`<p>Text block that <span>wraps inner text</span> and continues</p>`,
|
||||
`p:contains("that wraps")`,
|
||||
[]string{
|
||||
`<p>`,
|
||||
},
|
||||
},
|
||||
{
|
||||
`<p>Text block that <span>wraps inner text</span> and continues</p>`,
|
||||
`p:containsOwn("that wraps")`,
|
||||
[]string{},
|
||||
},
|
||||
{
|
||||
`<p>Text block that <span>wraps inner text</span> and continues</p>`,
|
||||
`:containsOwn("inner")`,
|
||||
[]string{
|
||||
`<span>`,
|
||||
},
|
||||
},
|
||||
{
|
||||
`<p>Text block that <span>wraps inner text</span> and continues</p>`,
|
||||
`p:containsOwn("block")`,
|
||||
[]string{
|
||||
`<p>`,
|
||||
},
|
||||
},
|
||||
{
|
||||
`<div id="d1"><p id="p1"><span>text content</span></p></div><div id="d2"/>`,
|
||||
`div:has(#p1)`,
|
||||
[]string{
|
||||
`<div id="d1">`,
|
||||
},
|
||||
},
|
||||
{
|
||||
`<div id="d1"><p id="p1"><span>contents 1</span></p></div>
|
||||
<div id="d2"><p>contents <em>2</em></p></div>`,
|
||||
`div:has(:containsOwn("2"))`,
|
||||
[]string{
|
||||
`<div id="d2">`,
|
||||
},
|
||||
},
|
||||
{
|
||||
`<body><div id="d1"><p id="p1"><span>contents 1</span></p></div>
|
||||
<div id="d2"><p id="p2">contents <em>2</em></p></div></body>`,
|
||||
`body :has(:containsOwn("2"))`,
|
||||
[]string{
|
||||
`<div id="d2">`,
|
||||
`<p id="p2">`,
|
||||
},
|
||||
},
|
||||
{
|
||||
`<body><div id="d1"><p id="p1"><span>contents 1</span></p></div>
|
||||
<div id="d2"><p id="p2">contents <em>2</em></p></div></body>`,
|
||||
`body :haschild(:containsOwn("2"))`,
|
||||
[]string{
|
||||
`<p id="p2">`,
|
||||
},
|
||||
},
|
||||
{
|
||||
`<p id="p1">0123456789</p><p id="p2">abcdef</p><p id="p3">0123ABCD</p>`,
|
||||
`p:matches([\d])`,
|
||||
[]string{
|
||||
`<p id="p1">`,
|
||||
`<p id="p3">`,
|
||||
},
|
||||
},
|
||||
{
|
||||
`<p id="p1">0123456789</p><p id="p2">abcdef</p><p id="p3">0123ABCD</p>`,
|
||||
`p:matches([a-z])`,
|
||||
[]string{
|
||||
`<p id="p2">`,
|
||||
},
|
||||
},
|
||||
{
|
||||
`<p id="p1">0123456789</p><p id="p2">abcdef</p><p id="p3">0123ABCD</p>`,
|
||||
`p:matches([a-zA-Z])`,
|
||||
[]string{
|
||||
`<p id="p2">`,
|
||||
`<p id="p3">`,
|
||||
},
|
||||
},
|
||||
{
|
||||
`<p id="p1">0123456789</p><p id="p2">abcdef</p><p id="p3">0123ABCD</p>`,
|
||||
`p:matches([^\d])`,
|
||||
[]string{
|
||||
`<p id="p2">`,
|
||||
`<p id="p3">`,
|
||||
},
|
||||
},
|
||||
{
|
||||
`<p id="p1">0123456789</p><p id="p2">abcdef</p><p id="p3">0123ABCD</p>`,
|
||||
`p:matches(^(0|a))`,
|
||||
[]string{
|
||||
`<p id="p1">`,
|
||||
`<p id="p2">`,
|
||||
`<p id="p3">`,
|
||||
},
|
||||
},
|
||||
{
|
||||
`<p id="p1">0123456789</p><p id="p2">abcdef</p><p id="p3">0123ABCD</p>`,
|
||||
`p:matches(^\d+$)`,
|
||||
[]string{
|
||||
`<p id="p1">`,
|
||||
},
|
||||
},
|
||||
{
|
||||
`<p id="p1">0123456789</p><p id="p2">abcdef</p><p id="p3">0123ABCD</p>`,
|
||||
`p:not(:matches(^\d+$))`,
|
||||
[]string{
|
||||
`<p id="p2">`,
|
||||
`<p id="p3">`,
|
||||
},
|
||||
},
|
||||
{
|
||||
`<div><p id="p1">01234<em>567</em>89</p><div>`,
|
||||
`div :matchesOwn(^\d+$)`,
|
||||
[]string{
|
||||
`<p id="p1">`,
|
||||
`<em>`,
|
||||
},
|
||||
},
|
||||
{
|
||||
`<ul>
|
||||
<li><a id="a1" href="http://www.google.com/finance"/>
|
||||
<li><a id="a2" href="http://finance.yahoo.com/"/>
|
||||
<li><a id="a2" href="http://finance.untrusted.com/"/>
|
||||
<li><a id="a3" href="https://www.google.com/news"/>
|
||||
<li><a id="a4" href="http://news.yahoo.com"/>
|
||||
</ul>`,
|
||||
`[href#=(fina)]:not([href#=(\/\/[^\/]+untrusted)])`,
|
||||
[]string{
|
||||
`<a id="a1" href="http://www.google.com/finance">`,
|
||||
`<a id="a2" href="http://finance.yahoo.com/">`,
|
||||
},
|
||||
},
|
||||
{
|
||||
`<ul>
|
||||
<li><a id="a1" href="http://www.google.com/finance"/>
|
||||
<li><a id="a2" href="http://finance.yahoo.com/"/>
|
||||
<li><a id="a3" href="https://www.google.com/news"/>
|
||||
<li><a id="a4" href="http://news.yahoo.com"/>
|
||||
</ul>`,
|
||||
`[href#=(^https:\/\/[^\/]*\/?news)]`,
|
||||
[]string{
|
||||
`<a id="a3" href="https://www.google.com/news">`,
|
||||
},
|
||||
},
|
||||
{
|
||||
`<form>
|
||||
<label>Username <input type="text" name="username" /></label>
|
||||
<label>Password <input type="password" name="password" /></label>
|
||||
<label>Country
|
||||
<select name="country">
|
||||
<option value="ca">Canada</option>
|
||||
<option value="us">United States</option>
|
||||
</select>
|
||||
</label>
|
||||
<label>Bio <textarea name="bio"></textarea></label>
|
||||
<button>Sign up</button>
|
||||
</form>`,
|
||||
`:input`,
|
||||
[]string{
|
||||
`<input type="text" name="username">`,
|
||||
`<input type="password" name="password">`,
|
||||
`<select name="country">`,
|
||||
`<textarea name="bio">`,
|
||||
`<button>`,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
func TestSelectors(t *testing.T) {
|
||||
for _, test := range selectorTests {
|
||||
s, err := Compile(test.selector)
|
||||
if err != nil {
|
||||
t.Errorf("error compiling %q: %s", test.selector, err)
|
||||
continue
|
||||
}
|
||||
|
||||
doc, err := html.Parse(strings.NewReader(test.HTML))
|
||||
if err != nil {
|
||||
t.Errorf("error parsing %q: %s", test.HTML, err)
|
||||
continue
|
||||
}
|
||||
|
||||
matches := s.MatchAll(doc)
|
||||
if len(matches) != len(test.results) {
|
||||
t.Errorf("wanted %d elements, got %d instead", len(test.results), len(matches))
|
||||
continue
|
||||
}
|
||||
|
||||
for i, m := range matches {
|
||||
got := nodeString(m)
|
||||
if got != test.results[i] {
|
||||
t.Errorf("wanted %s, got %s instead", test.results[i], got)
|
||||
}
|
||||
}
|
||||
|
||||
firstMatch := s.MatchFirst(doc)
|
||||
if len(test.results) == 0 {
|
||||
if firstMatch != nil {
|
||||
t.Errorf("MatchFirst: want nil, got %s", nodeString(firstMatch))
|
||||
}
|
||||
} else {
|
||||
got := nodeString(firstMatch)
|
||||
if got != test.results[0] {
|
||||
t.Errorf("MatchFirst: want %s, got %s", test.results[0], got)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
2
vendor/github.com/coreos/go-oidc/.gitignore
generated
vendored
Normal file
2
vendor/github.com/coreos/go-oidc/.gitignore
generated
vendored
Normal file
|
@ -0,0 +1,2 @@
|
|||
/bin
|
||||
/gopath
|
16
vendor/github.com/coreos/go-oidc/.travis.yml
generated
vendored
Normal file
16
vendor/github.com/coreos/go-oidc/.travis.yml
generated
vendored
Normal file
|
@ -0,0 +1,16 @@
|
|||
language: go
|
||||
|
||||
go:
|
||||
- 1.4.3
|
||||
- 1.5.2
|
||||
|
||||
install:
|
||||
- go get -v -t ./...
|
||||
- go get golang.org/x/tools/cmd/cover
|
||||
- go get golang.org/x/tools/cmd/vet
|
||||
|
||||
script:
|
||||
- ./test
|
||||
|
||||
notifications:
|
||||
email: false
|
71
vendor/github.com/coreos/go-oidc/CONTRIBUTING.md
generated
vendored
Normal file
71
vendor/github.com/coreos/go-oidc/CONTRIBUTING.md
generated
vendored
Normal file
|
@ -0,0 +1,71 @@
|
|||
# How to Contribute
|
||||
|
||||
CoreOS projects are [Apache 2.0 licensed](LICENSE) and accept contributions via
|
||||
GitHub pull requests. This document outlines some of the conventions on
|
||||
development workflow, commit message formatting, contact points and other
|
||||
resources to make it easier to get your contribution accepted.
|
||||
|
||||
# Certificate of Origin
|
||||
|
||||
By contributing to this project you agree to the Developer Certificate of
|
||||
Origin (DCO). This document was created by the Linux Kernel community and is a
|
||||
simple statement that you, as a contributor, have the legal right to make the
|
||||
contribution. See the [DCO](DCO) file for details.
|
||||
|
||||
# Email and Chat
|
||||
|
||||
The project currently uses the general CoreOS email list and IRC channel:
|
||||
- Email: [coreos-dev](https://groups.google.com/forum/#!forum/coreos-dev)
|
||||
- IRC: #[coreos](irc://irc.freenode.org:6667/#coreos) IRC channel on freenode.org
|
||||
|
||||
Please avoid emailing maintainers found in the MAINTAINERS file directly. They
|
||||
are very busy and read the mailing lists.
|
||||
|
||||
## Getting Started
|
||||
|
||||
- Fork the repository on GitHub
|
||||
- Read the [README](README.md) for build and test instructions
|
||||
- Play with the project, submit bugs, submit patches!
|
||||
|
||||
## Contribution Flow
|
||||
|
||||
This is a rough outline of what a contributor's workflow looks like:
|
||||
|
||||
- Create a topic branch from where you want to base your work (usually master).
|
||||
- Make commits of logical units.
|
||||
- Make sure your commit messages are in the proper format (see below).
|
||||
- Push your changes to a topic branch in your fork of the repository.
|
||||
- Make sure the tests pass, and add any new tests as appropriate.
|
||||
- Submit a pull request to the original repository.
|
||||
|
||||
Thanks for your contributions!
|
||||
|
||||
### Format of the Commit Message
|
||||
|
||||
We follow a rough convention for commit messages that is designed to answer two
|
||||
questions: what changed and why. The subject line should feature the what and
|
||||
the body of the commit should describe the why.
|
||||
|
||||
```
|
||||
scripts: add the test-cluster command
|
||||
|
||||
this uses tmux to setup a test cluster that you can easily kill and
|
||||
start for debugging.
|
||||
|
||||
Fixes #38
|
||||
```
|
||||
|
||||
The format can be described more formally as follows:
|
||||
|
||||
```
|
||||
<subsystem>: <what changed>
|
||||
<BLANK LINE>
|
||||
<why this change was made>
|
||||
<BLANK LINE>
|
||||
<footer>
|
||||
```
|
||||
|
||||
The first line is the subject and should be no longer than 70 characters, the
|
||||
second line is always blank, and other lines should be wrapped at 80 characters.
|
||||
This allows the message to be easier to read on GitHub as well as in various
|
||||
git tools.
|
36
vendor/github.com/coreos/go-oidc/DCO
generated
vendored
Normal file
36
vendor/github.com/coreos/go-oidc/DCO
generated
vendored
Normal file
|
@ -0,0 +1,36 @@
|
|||
Developer Certificate of Origin
|
||||
Version 1.1
|
||||
|
||||
Copyright (C) 2004, 2006 The Linux Foundation and its contributors.
|
||||
660 York Street, Suite 102,
|
||||
San Francisco, CA 94110 USA
|
||||
|
||||
Everyone is permitted to copy and distribute verbatim copies of this
|
||||
license document, but changing it is not allowed.
|
||||
|
||||
|
||||
Developer's Certificate of Origin 1.1
|
||||
|
||||
By making a contribution to this project, I certify that:
|
||||
|
||||
(a) The contribution was created in whole or in part by me and I
|
||||
have the right to submit it under the open source license
|
||||
indicated in the file; or
|
||||
|
||||
(b) The contribution is based upon previous work that, to the best
|
||||
of my knowledge, is covered under an appropriate open source
|
||||
license and I have the right under that license to submit that
|
||||
work with modifications, whether created in whole or in part
|
||||
by me, under the same open source license (unless I am
|
||||
permitted to submit under a different license), as indicated
|
||||
in the file; or
|
||||
|
||||
(c) The contribution was provided directly to me by some other
|
||||
person who certified (a), (b) or (c) and I have not modified
|
||||
it.
|
||||
|
||||
(d) I understand and agree that this project and the contribution
|
||||
are public and that a record of the contribution (including all
|
||||
personal information I submit with it, including my sign-off) is
|
||||
maintained indefinitely and may be redistributed consistent with
|
||||
this project or the open source license(s) involved.
|
3
vendor/github.com/coreos/go-oidc/MAINTAINERS
generated
vendored
Normal file
3
vendor/github.com/coreos/go-oidc/MAINTAINERS
generated
vendored
Normal file
|
@ -0,0 +1,3 @@
|
|||
Bobby Rullo <bobby.rullo@coreos.com> (@bobbyrullo)
|
||||
Ed Rooth <ed.rooth@coreos.com> (@sym3tri)
|
||||
Eric Chiang <eric.chiang@coreos.com> (@ericchiang)
|
15
vendor/github.com/coreos/go-oidc/README.md
generated
vendored
Normal file
15
vendor/github.com/coreos/go-oidc/README.md
generated
vendored
Normal file
|
@ -0,0 +1,15 @@
|
|||
# go-oidc
|
||||
|
||||
[![GoDoc](https://godoc.org/github.com/coreos/go-oidc?status.svg)](https://godoc.org/github.com/coreos/go-oidc)
|
||||
[![Build Status](https://travis-ci.org/coreos/go-oidc.png?branch=master)](https://travis-ci.org/coreos/go-oidc)
|
||||
|
||||
go-oidc provides a comprehensive collection of golang libraries for other projects to implement [OpenID Connect (OIDC)][oidc] server and client components.
|
||||
|
||||
[oidc]: http://openid.net/connect
|
||||
|
||||
## package documentation
|
||||
|
||||
- [github.com/coreos/go-oidc/oidc](http://godoc.org/github.com/coreos/go-oidc/oidc) - OIDC client- and server-related components
|
||||
- [github.com/coreos/go-oidc/oauth2](http://godoc.org/github.com/coreos/go-oidc/oauth2) - OAuth2-specific code needed by the OIDC components
|
||||
- [github.com/coreos/go-oidc/jose](http://godoc.org/github.com/coreos/go-oidc/jose) - Javascript Object Signing and Encryption (JOSE) object ([JWS](https://tools.ietf.org/html/draft-ietf-jose-json-web-signature-41), [JWK](https://tools.ietf.org/html/draft-ietf-jose-json-web-key-41)) generation, validation and serialization
|
||||
- [github.com/coreos/go-oidc/key](http://godoc.org/github.com/coreos/go-oidc/key) - RSA key management for OIDC components
|
9
vendor/github.com/coreos/go-oidc/build
generated
vendored
Executable file
9
vendor/github.com/coreos/go-oidc/build
generated
vendored
Executable file
|
@ -0,0 +1,9 @@
|
|||
#!/bin/bash -e
|
||||
|
||||
GOBUILD="go build -a -installsuffix netgo -ldflags '-s'"
|
||||
|
||||
echo "building bin/oidc-example-app..."
|
||||
${GOBUILD} -o bin/oidc-example-app github.com/coreos/go-oidc/example/app
|
||||
echo "building bin/oidc-example-cli..."
|
||||
${GOBUILD} -o bin/oidc-example-cli github.com/coreos/go-oidc/example/cli
|
||||
echo "done"
|
162
vendor/github.com/coreos/go-oidc/example/app/main.go
generated
vendored
Normal file
162
vendor/github.com/coreos/go-oidc/example/app/main.go
generated
vendored
Normal file
|
@ -0,0 +1,162 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"flag"
|
||||
"fmt"
|
||||
"log"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/coreos/go-oidc/oidc"
|
||||
)
|
||||
|
||||
var (
|
||||
pathCallback = "/oauth2callback"
|
||||
defaultListenHost = "127.0.0.1:5555"
|
||||
)
|
||||
|
||||
func main() {
|
||||
log.SetOutput(os.Stderr)
|
||||
|
||||
fs := flag.NewFlagSet("oidc-example-app", flag.ExitOnError)
|
||||
listen := fs.String("listen", defaultListenHost, "serve traffic on this address (<host>:<port>)")
|
||||
redirectURL := fs.String("redirect-url", fmt.Sprintf("http://%s%s", defaultListenHost, pathCallback), "")
|
||||
clientID := fs.String("client-id", "", "")
|
||||
clientSecret := fs.String("client-secret", "", "")
|
||||
discovery := fs.String("discovery", "https://accounts.google.com", "")
|
||||
|
||||
if err := fs.Parse(os.Args[1:]); err != nil {
|
||||
log.Fatalf("failed parsing flags: %v", err)
|
||||
}
|
||||
|
||||
if *clientID == "" {
|
||||
log.Fatal("--client-id must be set")
|
||||
}
|
||||
|
||||
if *clientSecret == "" {
|
||||
log.Fatal("--client-secret must be set")
|
||||
}
|
||||
|
||||
_, _, err := net.SplitHostPort(*listen)
|
||||
if err != nil {
|
||||
log.Fatalf("unable to parse host:port from --listen flag: %v", err)
|
||||
}
|
||||
|
||||
cc := oidc.ClientCredentials{
|
||||
ID: *clientID,
|
||||
Secret: *clientSecret,
|
||||
}
|
||||
|
||||
log.Printf("fetching provider config from %s...", *discovery)
|
||||
|
||||
var cfg oidc.ProviderConfig
|
||||
for {
|
||||
cfg, err = oidc.FetchProviderConfig(http.DefaultClient, *discovery)
|
||||
if err == nil {
|
||||
break
|
||||
}
|
||||
|
||||
sleep := 3 * time.Second
|
||||
log.Printf("failed fetching provider config, trying again in %v: %v", sleep, err)
|
||||
time.Sleep(sleep)
|
||||
}
|
||||
|
||||
log.Printf("fetched provider config from %s: %#v", *discovery, cfg)
|
||||
|
||||
ccfg := oidc.ClientConfig{
|
||||
ProviderConfig: cfg,
|
||||
Credentials: cc,
|
||||
RedirectURL: *redirectURL,
|
||||
}
|
||||
|
||||
client, err := oidc.NewClient(ccfg)
|
||||
if err != nil {
|
||||
log.Fatalf("unable to create Client: %v", err)
|
||||
}
|
||||
|
||||
client.SyncProviderConfig(*discovery)
|
||||
|
||||
redirectURLParsed, err := url.Parse(*redirectURL)
|
||||
if err != nil {
|
||||
log.Fatalf("unable to parse url from --redirect-url flag: %v", err)
|
||||
}
|
||||
hdlr := NewClientHandler(client, *redirectURLParsed)
|
||||
httpsrv := &http.Server{
|
||||
Addr: fmt.Sprintf(*listen),
|
||||
Handler: hdlr,
|
||||
}
|
||||
|
||||
log.Printf("binding to %s...", httpsrv.Addr)
|
||||
log.Fatal(httpsrv.ListenAndServe())
|
||||
}
|
||||
|
||||
func NewClientHandler(c *oidc.Client, cbURL url.URL) http.Handler {
|
||||
mux := http.NewServeMux()
|
||||
mux.HandleFunc("/", handleIndex)
|
||||
mux.HandleFunc("/login", handleLoginFunc(c))
|
||||
mux.HandleFunc(pathCallback, handleCallbackFunc(c))
|
||||
return mux
|
||||
}
|
||||
|
||||
func handleIndex(w http.ResponseWriter, r *http.Request) {
|
||||
w.Write([]byte("<a href='/login'>login</a>"))
|
||||
}
|
||||
|
||||
func handleLoginFunc(c *oidc.Client) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
oac, err := c.OAuthClient()
|
||||
if err != nil {
|
||||
panic("unable to proceed")
|
||||
}
|
||||
|
||||
u, err := url.Parse(oac.AuthCodeURL("", "", ""))
|
||||
if err != nil {
|
||||
panic("unable to proceed")
|
||||
}
|
||||
http.Redirect(w, r, u.String(), http.StatusFound)
|
||||
}
|
||||
}
|
||||
|
||||
func handleCallbackFunc(c *oidc.Client) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
code := r.URL.Query().Get("code")
|
||||
if code == "" {
|
||||
writeError(w, http.StatusBadRequest, "code query param must be set")
|
||||
return
|
||||
}
|
||||
|
||||
tok, err := c.ExchangeAuthCode(code)
|
||||
if err != nil {
|
||||
writeError(w, http.StatusBadRequest, fmt.Sprintf("unable to verify auth code with issuer: %v", err))
|
||||
return
|
||||
}
|
||||
|
||||
claims, err := tok.Claims()
|
||||
if err != nil {
|
||||
writeError(w, http.StatusBadRequest, fmt.Sprintf("unable to construct claims: %v", err))
|
||||
return
|
||||
}
|
||||
|
||||
s := fmt.Sprintf("claims: %v", claims)
|
||||
w.Write([]byte(s))
|
||||
}
|
||||
}
|
||||
|
||||
func writeError(w http.ResponseWriter, code int, msg string) {
|
||||
e := struct {
|
||||
Error string `json:"error"`
|
||||
}{
|
||||
Error: msg,
|
||||
}
|
||||
b, err := json.Marshal(e)
|
||||
if err != nil {
|
||||
log.Printf("Failed marshaling %#v to JSON: %v", e, err)
|
||||
}
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.WriteHeader(code)
|
||||
w.Write(b)
|
||||
}
|
83
vendor/github.com/coreos/go-oidc/example/cli/main.go
generated
vendored
Normal file
83
vendor/github.com/coreos/go-oidc/example/cli/main.go
generated
vendored
Normal file
|
@ -0,0 +1,83 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/coreos/go-oidc/oidc"
|
||||
)
|
||||
|
||||
func main() {
|
||||
fs := flag.NewFlagSet("oidc-example-cli", flag.ExitOnError)
|
||||
clientID := fs.String("client-id", "", "")
|
||||
clientSecret := fs.String("client-secret", "", "")
|
||||
discovery := fs.String("discovery", "https://accounts.google.com", "")
|
||||
|
||||
if err := fs.Parse(os.Args[1:]); err != nil {
|
||||
fmt.Fprintln(os.Stderr, err.Error())
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
if *clientID == "" {
|
||||
fmt.Println("--client-id must be set")
|
||||
os.Exit(2)
|
||||
}
|
||||
|
||||
if *clientSecret == "" {
|
||||
fmt.Println("--client-secret must be set")
|
||||
os.Exit(2)
|
||||
}
|
||||
|
||||
cc := oidc.ClientCredentials{
|
||||
ID: *clientID,
|
||||
Secret: *clientSecret,
|
||||
}
|
||||
|
||||
fmt.Printf("fetching provider config from %s...", *discovery)
|
||||
|
||||
// NOTE: A real CLI would cache this config, or provide it via flags/config file.
|
||||
var cfg oidc.ProviderConfig
|
||||
var err error
|
||||
for {
|
||||
cfg, err = oidc.FetchProviderConfig(http.DefaultClient, *discovery)
|
||||
if err == nil {
|
||||
break
|
||||
}
|
||||
|
||||
sleep := 1 * time.Second
|
||||
fmt.Printf("failed fetching provider config, trying again in %v: %v\n", sleep, err)
|
||||
time.Sleep(sleep)
|
||||
}
|
||||
|
||||
fmt.Printf("fetched provider config from %s: %#v\n\n", *discovery, cfg)
|
||||
|
||||
ccfg := oidc.ClientConfig{
|
||||
ProviderConfig: cfg,
|
||||
Credentials: cc,
|
||||
}
|
||||
|
||||
client, err := oidc.NewClient(ccfg)
|
||||
if err != nil {
|
||||
fmt.Printf("unable to create Client: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
tok, err := client.ClientCredsToken([]string{"openid"})
|
||||
if err != nil {
|
||||
fmt.Printf("unable to verify auth code with issuer: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
fmt.Printf("got jwt: %v\n\n", tok.Encode())
|
||||
|
||||
claims, err := tok.Claims()
|
||||
if err != nil {
|
||||
fmt.Printf("unable to construct claims: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
fmt.Printf("got claims %#v...\n", claims)
|
||||
}
|
380
vendor/github.com/coreos/go-oidc/http/http_test.go
generated
vendored
Normal file
380
vendor/github.com/coreos/go-oidc/http/http_test.go
generated
vendored
Normal file
|
@ -0,0 +1,380 @@
|
|||
package http
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"net/url"
|
||||
"reflect"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestCacheControlMaxAgeSuccess(t *testing.T) {
|
||||
tests := []struct {
|
||||
hdr string
|
||||
wantAge time.Duration
|
||||
wantOK bool
|
||||
}{
|
||||
{"max-age=12", 12 * time.Second, true},
|
||||
{"max-age=-12", 0, false},
|
||||
{"max-age=0", 0, false},
|
||||
{"public, max-age=12", 12 * time.Second, true},
|
||||
{"public, max-age=40192, must-revalidate", 40192 * time.Second, true},
|
||||
{"public, not-max-age=12, must-revalidate", time.Duration(0), false},
|
||||
}
|
||||
|
||||
for i, tt := range tests {
|
||||
maxAge, ok, err := cacheControlMaxAge(tt.hdr)
|
||||
if err != nil {
|
||||
t.Errorf("case %d: err=%v", i, err)
|
||||
}
|
||||
if tt.wantAge != maxAge {
|
||||
t.Errorf("case %d: want=%d got=%d", i, tt.wantAge, maxAge)
|
||||
}
|
||||
if tt.wantOK != ok {
|
||||
t.Errorf("case %d: incorrect ok value: want=%t got=%t", i, tt.wantOK, ok)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestCacheControlMaxAgeFail(t *testing.T) {
|
||||
tests := []string{
|
||||
"max-age=aasdf",
|
||||
"max-age=",
|
||||
"max-age",
|
||||
}
|
||||
|
||||
for i, tt := range tests {
|
||||
_, ok, err := cacheControlMaxAge(tt)
|
||||
if ok {
|
||||
t.Errorf("case %d: want ok=false, got true", i)
|
||||
}
|
||||
if err == nil {
|
||||
t.Errorf("case %d: want non-nil err", i)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestMergeQuery(t *testing.T) {
|
||||
tests := []struct {
|
||||
u string
|
||||
q url.Values
|
||||
w string
|
||||
}{
|
||||
// No values
|
||||
{
|
||||
u: "http://example.com",
|
||||
q: nil,
|
||||
w: "http://example.com",
|
||||
},
|
||||
// No additional values
|
||||
{
|
||||
u: "http://example.com?foo=bar",
|
||||
q: nil,
|
||||
w: "http://example.com?foo=bar",
|
||||
},
|
||||
// Simple addition
|
||||
{
|
||||
u: "http://example.com",
|
||||
q: url.Values{
|
||||
"foo": []string{"bar"},
|
||||
},
|
||||
w: "http://example.com?foo=bar",
|
||||
},
|
||||
// Addition with existing values
|
||||
{
|
||||
u: "http://example.com?dog=boo",
|
||||
q: url.Values{
|
||||
"foo": []string{"bar"},
|
||||
},
|
||||
w: "http://example.com?dog=boo&foo=bar",
|
||||
},
|
||||
// Merge
|
||||
{
|
||||
u: "http://example.com?dog=boo",
|
||||
q: url.Values{
|
||||
"dog": []string{"elroy"},
|
||||
},
|
||||
w: "http://example.com?dog=boo&dog=elroy",
|
||||
},
|
||||
// Add and merge
|
||||
{
|
||||
u: "http://example.com?dog=boo",
|
||||
q: url.Values{
|
||||
"dog": []string{"elroy"},
|
||||
"foo": []string{"bar"},
|
||||
},
|
||||
w: "http://example.com?dog=boo&dog=elroy&foo=bar",
|
||||
},
|
||||
// Multivalue merge
|
||||
{
|
||||
u: "http://example.com?dog=boo",
|
||||
q: url.Values{
|
||||
"dog": []string{"elroy", "penny"},
|
||||
},
|
||||
w: "http://example.com?dog=boo&dog=elroy&dog=penny",
|
||||
},
|
||||
}
|
||||
|
||||
for i, tt := range tests {
|
||||
ur, err := url.Parse(tt.u)
|
||||
if err != nil {
|
||||
t.Errorf("case %d: failed parsing test url: %v, error: %v", i, tt.u, err)
|
||||
}
|
||||
|
||||
got := MergeQuery(*ur, tt.q)
|
||||
want, err := url.Parse(tt.w)
|
||||
if err != nil {
|
||||
t.Errorf("case %d: failed parsing want url: %v, error: %v", i, tt.w, err)
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(*want, got) {
|
||||
t.Errorf("case %d: want: %v, got: %v", i, *want, got)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestExpiresPass(t *testing.T) {
|
||||
tests := []struct {
|
||||
date string
|
||||
exp string
|
||||
wantTTL time.Duration
|
||||
wantOK bool
|
||||
}{
|
||||
// Expires and Date properly set
|
||||
{
|
||||
date: "Thu, 01 Dec 1983 22:00:00 GMT",
|
||||
exp: "Fri, 02 Dec 1983 01:00:00 GMT",
|
||||
wantTTL: 10800 * time.Second,
|
||||
wantOK: true,
|
||||
},
|
||||
// empty headers
|
||||
{
|
||||
date: "",
|
||||
exp: "",
|
||||
wantOK: false,
|
||||
},
|
||||
// lack of Expirs short-ciruits Date parsing
|
||||
{
|
||||
date: "foo",
|
||||
exp: "",
|
||||
wantOK: false,
|
||||
},
|
||||
// lack of Date short-ciruits Expires parsing
|
||||
{
|
||||
date: "",
|
||||
exp: "foo",
|
||||
wantOK: false,
|
||||
},
|
||||
// no Date
|
||||
{
|
||||
exp: "Thu, 01 Dec 1983 22:00:00 GMT",
|
||||
wantTTL: 0,
|
||||
wantOK: false,
|
||||
},
|
||||
// no Expires
|
||||
{
|
||||
date: "Thu, 01 Dec 1983 22:00:00 GMT",
|
||||
wantTTL: 0,
|
||||
wantOK: false,
|
||||
},
|
||||
// Expires < Date
|
||||
{
|
||||
date: "Fri, 02 Dec 1983 01:00:00 GMT",
|
||||
exp: "Thu, 01 Dec 1983 22:00:00 GMT",
|
||||
wantTTL: 0,
|
||||
wantOK: false,
|
||||
},
|
||||
}
|
||||
|
||||
for i, tt := range tests {
|
||||
ttl, ok, err := expires(tt.date, tt.exp)
|
||||
if err != nil {
|
||||
t.Errorf("case %d: err=%v", i, err)
|
||||
}
|
||||
if tt.wantTTL != ttl {
|
||||
t.Errorf("case %d: want=%d got=%d", i, tt.wantTTL, ttl)
|
||||
}
|
||||
if tt.wantOK != ok {
|
||||
t.Errorf("case %d: incorrect ok value: want=%t got=%t", i, tt.wantOK, ok)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestExpiresFail(t *testing.T) {
|
||||
tests := []struct {
|
||||
date string
|
||||
exp string
|
||||
}{
|
||||
// malformed Date header
|
||||
{
|
||||
date: "foo",
|
||||
exp: "Fri, 02 Dec 1983 01:00:00 GMT",
|
||||
},
|
||||
// malformed exp header
|
||||
{
|
||||
date: "Fri, 02 Dec 1983 01:00:00 GMT",
|
||||
exp: "bar",
|
||||
},
|
||||
}
|
||||
|
||||
for i, tt := range tests {
|
||||
_, _, err := expires(tt.date, tt.exp)
|
||||
if err == nil {
|
||||
t.Errorf("case %d: expected non-nil error", i)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestCacheablePass(t *testing.T) {
|
||||
tests := []struct {
|
||||
headers http.Header
|
||||
wantTTL time.Duration
|
||||
wantOK bool
|
||||
}{
|
||||
// valid Cache-Control
|
||||
{
|
||||
headers: http.Header{
|
||||
"Cache-Control": []string{"max-age=100"},
|
||||
},
|
||||
wantTTL: 100 * time.Second,
|
||||
wantOK: true,
|
||||
},
|
||||
// valid Date/Expires
|
||||
{
|
||||
headers: http.Header{
|
||||
"Date": []string{"Thu, 01 Dec 1983 22:00:00 GMT"},
|
||||
"Expires": []string{"Fri, 02 Dec 1983 01:00:00 GMT"},
|
||||
},
|
||||
wantTTL: 10800 * time.Second,
|
||||
wantOK: true,
|
||||
},
|
||||
// Cache-Control supersedes Date/Expires
|
||||
{
|
||||
headers: http.Header{
|
||||
"Cache-Control": []string{"max-age=100"},
|
||||
"Date": []string{"Thu, 01 Dec 1983 22:00:00 GMT"},
|
||||
"Expires": []string{"Fri, 02 Dec 1983 01:00:00 GMT"},
|
||||
},
|
||||
wantTTL: 100 * time.Second,
|
||||
wantOK: true,
|
||||
},
|
||||
// no caching headers
|
||||
{
|
||||
headers: http.Header{},
|
||||
wantOK: false,
|
||||
},
|
||||
}
|
||||
|
||||
for i, tt := range tests {
|
||||
ttl, ok, err := Cacheable(tt.headers)
|
||||
if err != nil {
|
||||
t.Errorf("case %d: err=%v", i, err)
|
||||
continue
|
||||
}
|
||||
if tt.wantTTL != ttl {
|
||||
t.Errorf("case %d: want=%d got=%d", i, tt.wantTTL, ttl)
|
||||
}
|
||||
if tt.wantOK != ok {
|
||||
t.Errorf("case %d: incorrect ok value: want=%t got=%t", i, tt.wantOK, ok)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestCacheableFail(t *testing.T) {
|
||||
tests := []http.Header{
|
||||
// invalid Cache-Control short-circuits
|
||||
http.Header{
|
||||
"Cache-Control": []string{"max-age"},
|
||||
"Date": []string{"Thu, 01 Dec 1983 22:00:00 GMT"},
|
||||
"Expires": []string{"Fri, 02 Dec 1983 01:00:00 GMT"},
|
||||
},
|
||||
// no Cache-Control, invalid Expires
|
||||
http.Header{
|
||||
"Date": []string{"Thu, 01 Dec 1983 22:00:00 GMT"},
|
||||
"Expires": []string{"boo"},
|
||||
},
|
||||
}
|
||||
|
||||
for i, tt := range tests {
|
||||
_, _, err := Cacheable(tt)
|
||||
if err == nil {
|
||||
t.Errorf("case %d: want non-nil err", i)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestNewResourceLocation(t *testing.T) {
|
||||
tests := []struct {
|
||||
ru *url.URL
|
||||
id string
|
||||
want string
|
||||
}{
|
||||
{
|
||||
ru: &url.URL{
|
||||
Scheme: "http",
|
||||
Host: "example.com",
|
||||
},
|
||||
id: "foo",
|
||||
want: "http://example.com/foo",
|
||||
},
|
||||
// https
|
||||
{
|
||||
ru: &url.URL{
|
||||
Scheme: "https",
|
||||
Host: "example.com",
|
||||
},
|
||||
id: "foo",
|
||||
want: "https://example.com/foo",
|
||||
},
|
||||
// with path
|
||||
{
|
||||
ru: &url.URL{
|
||||
Scheme: "http",
|
||||
Host: "example.com",
|
||||
Path: "one/two/three",
|
||||
},
|
||||
id: "foo",
|
||||
want: "http://example.com/one/two/three/foo",
|
||||
},
|
||||
// with fragment
|
||||
{
|
||||
ru: &url.URL{
|
||||
Scheme: "http",
|
||||
Host: "example.com",
|
||||
Fragment: "frag",
|
||||
},
|
||||
id: "foo",
|
||||
want: "http://example.com/foo",
|
||||
},
|
||||
// with query
|
||||
{
|
||||
ru: &url.URL{
|
||||
Scheme: "http",
|
||||
Host: "example.com",
|
||||
RawQuery: "dog=elroy",
|
||||
},
|
||||
id: "foo",
|
||||
want: "http://example.com/foo",
|
||||
},
|
||||
}
|
||||
|
||||
for i, tt := range tests {
|
||||
got := NewResourceLocation(tt.ru, tt.id)
|
||||
if tt.want != got {
|
||||
t.Errorf("case %d: want=%s, got=%s", i, tt.want, got)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestCopyRequest(t *testing.T) {
|
||||
r1, err := http.NewRequest("GET", "http://example.com", strings.NewReader("foo"))
|
||||
if err != nil {
|
||||
t.Fatalf("Unexpected error: %v", err)
|
||||
}
|
||||
|
||||
r2 := CopyRequest(r1)
|
||||
if !reflect.DeepEqual(r1, r2) {
|
||||
t.Fatalf("Result of CopyRequest incorrect: %#v != %#v", r1, r2)
|
||||
}
|
||||
}
|
49
vendor/github.com/coreos/go-oidc/http/url_test.go
generated
vendored
Normal file
49
vendor/github.com/coreos/go-oidc/http/url_test.go
generated
vendored
Normal file
|
@ -0,0 +1,49 @@
|
|||
package http
|
||||
|
||||
import (
|
||||
"net/url"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestParseNonEmptyURL(t *testing.T) {
|
||||
tests := []struct {
|
||||
u string
|
||||
ok bool
|
||||
}{
|
||||
{"", false},
|
||||
{"http://", false},
|
||||
{"example.com", false},
|
||||
{"example", false},
|
||||
{"http://example", true},
|
||||
{"http://example:1234", true},
|
||||
{"http://example.com", true},
|
||||
{"http://example.com:1234", true},
|
||||
}
|
||||
|
||||
for i, tt := range tests {
|
||||
u, err := ParseNonEmptyURL(tt.u)
|
||||
if err != nil {
|
||||
t.Logf("err: %v", err)
|
||||
if tt.ok {
|
||||
t.Errorf("case %d: unexpected error: %v", i, err)
|
||||
} else {
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
if !tt.ok {
|
||||
t.Errorf("case %d: expected error but got none", i)
|
||||
continue
|
||||
}
|
||||
|
||||
uu, err := url.Parse(tt.u)
|
||||
if err != nil {
|
||||
t.Errorf("case %d: unexpected error: %v", i, err)
|
||||
continue
|
||||
}
|
||||
|
||||
if uu.String() != u.String() {
|
||||
t.Errorf("case %d: incorrect url value, want: %q, got: %q", i, uu.String(), u.String())
|
||||
}
|
||||
}
|
||||
}
|
328
vendor/github.com/coreos/go-oidc/jose/claims_test.go
generated
vendored
Normal file
328
vendor/github.com/coreos/go-oidc/jose/claims_test.go
generated
vendored
Normal file
|
@ -0,0 +1,328 @@
|
|||
package jose
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestString(t *testing.T) {
|
||||
tests := []struct {
|
||||
cl Claims
|
||||
key string
|
||||
ok bool
|
||||
err bool
|
||||
val string
|
||||
}{
|
||||
// ok, no err, claim exists
|
||||
{
|
||||
cl: Claims{
|
||||
"foo": "bar",
|
||||
},
|
||||
key: "foo",
|
||||
val: "bar",
|
||||
ok: true,
|
||||
err: false,
|
||||
},
|
||||
// no claims
|
||||
{
|
||||
cl: Claims{},
|
||||
key: "foo",
|
||||
val: "",
|
||||
ok: false,
|
||||
err: false,
|
||||
},
|
||||
// missing claim
|
||||
{
|
||||
cl: Claims{
|
||||
"foo": "bar",
|
||||
},
|
||||
key: "xxx",
|
||||
val: "",
|
||||
ok: false,
|
||||
err: false,
|
||||
},
|
||||
// unparsable: type
|
||||
{
|
||||
cl: Claims{
|
||||
"foo": struct{}{},
|
||||
},
|
||||
key: "foo",
|
||||
val: "",
|
||||
ok: false,
|
||||
err: true,
|
||||
},
|
||||
// unparsable: nil value
|
||||
{
|
||||
cl: Claims{
|
||||
"foo": nil,
|
||||
},
|
||||
key: "foo",
|
||||
val: "",
|
||||
ok: false,
|
||||
err: true,
|
||||
},
|
||||
}
|
||||
|
||||
for i, tt := range tests {
|
||||
val, ok, err := tt.cl.StringClaim(tt.key)
|
||||
|
||||
if tt.err && err == nil {
|
||||
t.Errorf("case %d: want err=non-nil, got err=nil", i)
|
||||
} else if !tt.err && err != nil {
|
||||
t.Errorf("case %d: want err=nil, got err=%v", i, err)
|
||||
}
|
||||
|
||||
if tt.ok != ok {
|
||||
t.Errorf("case %d: want ok=%v, got ok=%v", i, tt.ok, ok)
|
||||
}
|
||||
|
||||
if tt.val != val {
|
||||
t.Errorf("case %d: want val=%v, got val=%v", i, tt.val, val)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestInt64(t *testing.T) {
|
||||
tests := []struct {
|
||||
cl Claims
|
||||
key string
|
||||
ok bool
|
||||
err bool
|
||||
val int64
|
||||
}{
|
||||
// ok, no err, claim exists
|
||||
{
|
||||
cl: Claims{
|
||||
"foo": int64(100),
|
||||
},
|
||||
key: "foo",
|
||||
val: int64(100),
|
||||
ok: true,
|
||||
err: false,
|
||||
},
|
||||
// no claims
|
||||
{
|
||||
cl: Claims{},
|
||||
key: "foo",
|
||||
val: 0,
|
||||
ok: false,
|
||||
err: false,
|
||||
},
|
||||
// missing claim
|
||||
{
|
||||
cl: Claims{
|
||||
"foo": "bar",
|
||||
},
|
||||
key: "xxx",
|
||||
val: 0,
|
||||
ok: false,
|
||||
err: false,
|
||||
},
|
||||
// unparsable: type
|
||||
{
|
||||
cl: Claims{
|
||||
"foo": struct{}{},
|
||||
},
|
||||
key: "foo",
|
||||
val: 0,
|
||||
ok: false,
|
||||
err: true,
|
||||
},
|
||||
// unparsable: nil value
|
||||
{
|
||||
cl: Claims{
|
||||
"foo": nil,
|
||||
},
|
||||
key: "foo",
|
||||
val: 0,
|
||||
ok: false,
|
||||
err: true,
|
||||
},
|
||||
}
|
||||
|
||||
for i, tt := range tests {
|
||||
val, ok, err := tt.cl.Int64Claim(tt.key)
|
||||
|
||||
if tt.err && err == nil {
|
||||
t.Errorf("case %d: want err=non-nil, got err=nil", i)
|
||||
} else if !tt.err && err != nil {
|
||||
t.Errorf("case %d: want err=nil, got err=%v", i, err)
|
||||
}
|
||||
|
||||
if tt.ok != ok {
|
||||
t.Errorf("case %d: want ok=%v, got ok=%v", i, tt.ok, ok)
|
||||
}
|
||||
|
||||
if tt.val != val {
|
||||
t.Errorf("case %d: want val=%v, got val=%v", i, tt.val, val)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestTime(t *testing.T) {
|
||||
now := time.Now().UTC()
|
||||
unixNow := now.Unix()
|
||||
|
||||
tests := []struct {
|
||||
cl Claims
|
||||
key string
|
||||
ok bool
|
||||
err bool
|
||||
val time.Time
|
||||
}{
|
||||
// ok, no err, claim exists
|
||||
{
|
||||
cl: Claims{
|
||||
"foo": unixNow,
|
||||
},
|
||||
key: "foo",
|
||||
val: time.Unix(now.Unix(), 0).UTC(),
|
||||
ok: true,
|
||||
err: false,
|
||||
},
|
||||
// no claims
|
||||
{
|
||||
cl: Claims{},
|
||||
key: "foo",
|
||||
val: time.Time{},
|
||||
ok: false,
|
||||
err: false,
|
||||
},
|
||||
// missing claim
|
||||
{
|
||||
cl: Claims{
|
||||
"foo": "bar",
|
||||
},
|
||||
key: "xxx",
|
||||
val: time.Time{},
|
||||
ok: false,
|
||||
err: false,
|
||||
},
|
||||
// unparsable: type
|
||||
{
|
||||
cl: Claims{
|
||||
"foo": struct{}{},
|
||||
},
|
||||
key: "foo",
|
||||
val: time.Time{},
|
||||
ok: false,
|
||||
err: true,
|
||||
},
|
||||
// unparsable: nil value
|
||||
{
|
||||
cl: Claims{
|
||||
"foo": nil,
|
||||
},
|
||||
key: "foo",
|
||||
val: time.Time{},
|
||||
ok: false,
|
||||
err: true,
|
||||
},
|
||||
}
|
||||
|
||||
for i, tt := range tests {
|
||||
val, ok, err := tt.cl.TimeClaim(tt.key)
|
||||
|
||||
if tt.err && err == nil {
|
||||
t.Errorf("case %d: want err=non-nil, got err=nil", i)
|
||||
} else if !tt.err && err != nil {
|
||||
t.Errorf("case %d: want err=nil, got err=%v", i, err)
|
||||
}
|
||||
|
||||
if tt.ok != ok {
|
||||
t.Errorf("case %d: want ok=%v, got ok=%v", i, tt.ok, ok)
|
||||
}
|
||||
|
||||
if tt.val != val {
|
||||
t.Errorf("case %d: want val=%v, got val=%v", i, tt.val, val)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestStringArray(t *testing.T) {
|
||||
tests := []struct {
|
||||
cl Claims
|
||||
key string
|
||||
ok bool
|
||||
err bool
|
||||
val []string
|
||||
}{
|
||||
// ok, no err, claim exists
|
||||
{
|
||||
cl: Claims{
|
||||
"foo": []string{"bar", "faf"},
|
||||
},
|
||||
key: "foo",
|
||||
val: []string{"bar", "faf"},
|
||||
ok: true,
|
||||
err: false,
|
||||
},
|
||||
// ok, no err, []interface{}
|
||||
{
|
||||
cl: Claims{
|
||||
"foo": []interface{}{"bar", "faf"},
|
||||
},
|
||||
key: "foo",
|
||||
val: []string{"bar", "faf"},
|
||||
ok: true,
|
||||
err: false,
|
||||
},
|
||||
// no claims
|
||||
{
|
||||
cl: Claims{},
|
||||
key: "foo",
|
||||
val: nil,
|
||||
ok: false,
|
||||
err: false,
|
||||
},
|
||||
// missing claim
|
||||
{
|
||||
cl: Claims{
|
||||
"foo": "bar",
|
||||
},
|
||||
key: "xxx",
|
||||
val: nil,
|
||||
ok: false,
|
||||
err: false,
|
||||
},
|
||||
// unparsable: type
|
||||
{
|
||||
cl: Claims{
|
||||
"foo": struct{}{},
|
||||
},
|
||||
key: "foo",
|
||||
val: nil,
|
||||
ok: false,
|
||||
err: true,
|
||||
},
|
||||
// unparsable: nil value
|
||||
{
|
||||
cl: Claims{
|
||||
"foo": nil,
|
||||
},
|
||||
key: "foo",
|
||||
val: nil,
|
||||
ok: false,
|
||||
err: true,
|
||||
},
|
||||
}
|
||||
|
||||
for i, tt := range tests {
|
||||
val, ok, err := tt.cl.StringsClaim(tt.key)
|
||||
|
||||
if tt.err && err == nil {
|
||||
t.Errorf("case %d: want err=non-nil, got err=nil", i)
|
||||
} else if !tt.err && err != nil {
|
||||
t.Errorf("case %d: want err=nil, got err=%v", i, err)
|
||||
}
|
||||
|
||||
if tt.ok != ok {
|
||||
t.Errorf("case %d: want ok=%v, got ok=%v", i, tt.ok, ok)
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(tt.val, val) {
|
||||
t.Errorf("case %d: want val=%v, got val=%v", i, tt.val, val)
|
||||
}
|
||||
}
|
||||
}
|
64
vendor/github.com/coreos/go-oidc/jose/jwk_test.go
generated
vendored
Normal file
64
vendor/github.com/coreos/go-oidc/jose/jwk_test.go
generated
vendored
Normal file
|
@ -0,0 +1,64 @@
|
|||
package jose
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestDecodeBase64URLPaddingOptional(t *testing.T) {
|
||||
tests := []struct {
|
||||
encoded string
|
||||
decoded string
|
||||
err bool
|
||||
}{
|
||||
{
|
||||
// With padding
|
||||
encoded: "VGVjdG9uaWM=",
|
||||
decoded: "Tectonic",
|
||||
},
|
||||
{
|
||||
// Without padding
|
||||
encoded: "VGVjdG9uaWM",
|
||||
decoded: "Tectonic",
|
||||
},
|
||||
{
|
||||
// Even More padding
|
||||
encoded: "VGVjdG9uaQ==",
|
||||
decoded: "Tectoni",
|
||||
},
|
||||
{
|
||||
// And take it away!
|
||||
encoded: "VGVjdG9uaQ",
|
||||
decoded: "Tectoni",
|
||||
},
|
||||
{
|
||||
// Too much padding.
|
||||
encoded: "VGVjdG9uaWNh=",
|
||||
decoded: "",
|
||||
err: true,
|
||||
},
|
||||
{
|
||||
// Too much padding.
|
||||
encoded: "VGVjdG9uaWNh=",
|
||||
decoded: "",
|
||||
err: true,
|
||||
},
|
||||
}
|
||||
|
||||
for i, tt := range tests {
|
||||
got, err := decodeBase64URLPaddingOptional(tt.encoded)
|
||||
if tt.err {
|
||||
if err == nil {
|
||||
t.Errorf("case %d: expected non-nil err", i)
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
t.Errorf("case %d: want nil err, got: %v", i, err)
|
||||
}
|
||||
|
||||
if string(got) != tt.decoded {
|
||||
t.Errorf("case %d: want=%q, got=%q", i, tt.decoded, got)
|
||||
}
|
||||
}
|
||||
}
|
74
vendor/github.com/coreos/go-oidc/jose/jws_test.go
generated
vendored
Normal file
74
vendor/github.com/coreos/go-oidc/jose/jws_test.go
generated
vendored
Normal file
|
@ -0,0 +1,74 @@
|
|||
package jose
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
type testCase struct{ t string }
|
||||
|
||||
var validInput []testCase
|
||||
|
||||
var invalidInput []testCase
|
||||
|
||||
func init() {
|
||||
validInput = []testCase{
|
||||
{
|
||||
"eyJ0eXAiOiJKV1QiLA0KICJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJqb2UiLA0KICJleHAiOjEzMDA4MTkzODAsDQogImh0dHA6Ly9leGFtcGxlLmNvbS9pc19yb290Ijp0cnVlfQ.dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk",
|
||||
},
|
||||
}
|
||||
|
||||
invalidInput = []testCase{
|
||||
// empty
|
||||
{
|
||||
"",
|
||||
},
|
||||
// undecodeable
|
||||
{
|
||||
"aaa.bbb.ccc",
|
||||
},
|
||||
// missing parts
|
||||
{
|
||||
"aaa",
|
||||
},
|
||||
// missing parts
|
||||
{
|
||||
"aaa.bbb",
|
||||
},
|
||||
// too many parts
|
||||
{
|
||||
"aaa.bbb.ccc.ddd",
|
||||
},
|
||||
// invalid header
|
||||
// EncodeHeader(map[string]string{"foo": "bar"})
|
||||
{
|
||||
"eyJmb28iOiJiYXIifQ.bbb.ccc",
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseJWS(t *testing.T) {
|
||||
for i, tt := range validInput {
|
||||
jws, err := ParseJWS(tt.t)
|
||||
if err != nil {
|
||||
t.Errorf("test: %d. expected: valid, actual: invalid", i)
|
||||
}
|
||||
|
||||
expectedHeader := strings.Split(tt.t, ".")[0]
|
||||
if jws.RawHeader != expectedHeader {
|
||||
t.Errorf("test: %d. expected: %s, actual: %s", i, expectedHeader, jws.RawHeader)
|
||||
}
|
||||
|
||||
expectedPayload := strings.Split(tt.t, ".")[1]
|
||||
if jws.RawPayload != expectedPayload {
|
||||
t.Errorf("test: %d. expected: %s, actual: %s", i, expectedPayload, jws.RawPayload)
|
||||
}
|
||||
}
|
||||
|
||||
for i, tt := range invalidInput {
|
||||
_, err := ParseJWS(tt.t)
|
||||
if err == nil {
|
||||
t.Errorf("test: %d. expected: invalid, actual: valid", i)
|
||||
}
|
||||
}
|
||||
}
|
94
vendor/github.com/coreos/go-oidc/jose/jwt_test.go
generated
vendored
Normal file
94
vendor/github.com/coreos/go-oidc/jose/jwt_test.go
generated
vendored
Normal file
|
@ -0,0 +1,94 @@
|
|||
package jose
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestParseJWT(t *testing.T) {
|
||||
tests := []struct {
|
||||
r string
|
||||
h JOSEHeader
|
||||
c Claims
|
||||
}{
|
||||
{
|
||||
// Example from JWT spec:
|
||||
// http://self-issued.info/docs/draft-ietf-oauth-json-web-token.html#ExampleJWT
|
||||
"eyJ0eXAiOiJKV1QiLA0KICJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJqb2UiLA0KICJleHAiOjEzMDA4MTkzODAsDQogImh0dHA6Ly9leGFtcGxlLmNvbS9pc19yb290Ijp0cnVlfQ.dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk",
|
||||
JOSEHeader{
|
||||
HeaderMediaType: "JWT",
|
||||
HeaderKeyAlgorithm: "HS256",
|
||||
},
|
||||
Claims{
|
||||
"iss": "joe",
|
||||
// NOTE: test numbers must be floats for equality checks to work since values are converted form interface{} to float64 by default.
|
||||
"exp": 1300819380.0,
|
||||
"http://example.com/is_root": true,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for i, tt := range tests {
|
||||
jwt, err := ParseJWT(tt.r)
|
||||
if err != nil {
|
||||
t.Errorf("raw token should parse. test: %d. expected: valid, actual: invalid. err=%v", i, err)
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(tt.h, jwt.Header) {
|
||||
t.Errorf("JOSE headers should match. test: %d. expected: %v, actual: %v", i, tt.h, jwt.Header)
|
||||
}
|
||||
|
||||
claims, err := jwt.Claims()
|
||||
if err != nil {
|
||||
t.Errorf("test: %d. expected: valid claim parsing. err=%v", i, err)
|
||||
}
|
||||
if !reflect.DeepEqual(tt.c, claims) {
|
||||
t.Errorf("claims should match. test: %d. expected: %v, actual: %v", i, tt.c, claims)
|
||||
}
|
||||
|
||||
enc := jwt.Encode()
|
||||
if enc != tt.r {
|
||||
t.Errorf("encoded jwt should match raw jwt. test: %d. expected: %v, actual: %v", i, tt.r, enc)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestNewJWTHeaderTyp(t *testing.T) {
|
||||
jwt, err := NewJWT(JOSEHeader{}, Claims{})
|
||||
if err != nil {
|
||||
t.Fatalf("Unexpected error: %v", err)
|
||||
}
|
||||
|
||||
want := "JWT"
|
||||
got := jwt.Header[HeaderMediaType]
|
||||
if want != got {
|
||||
t.Fatalf("Header %q incorrect: want=%s got=%s", HeaderMediaType, want, got)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestNewJWTHeaderKeyID(t *testing.T) {
|
||||
jwt, err := NewJWT(JOSEHeader{HeaderKeyID: "foo"}, Claims{})
|
||||
if err != nil {
|
||||
t.Fatalf("Unexpected error: %v", err)
|
||||
}
|
||||
|
||||
want := "foo"
|
||||
got, ok := jwt.KeyID()
|
||||
if !ok {
|
||||
t.Fatalf("KeyID not set")
|
||||
} else if want != got {
|
||||
t.Fatalf("KeyID incorrect: want=%s got=%s", want, got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestNewJWTHeaderKeyIDNotSet(t *testing.T) {
|
||||
jwt, err := NewJWT(JOSEHeader{}, Claims{})
|
||||
if err != nil {
|
||||
t.Fatalf("Unexpected error: %v", err)
|
||||
}
|
||||
|
||||
if _, ok := jwt.KeyID(); ok {
|
||||
t.Fatalf("KeyID set, but should not be")
|
||||
}
|
||||
}
|
85
vendor/github.com/coreos/go-oidc/jose/sig_hmac_test.go
generated
vendored
Normal file
85
vendor/github.com/coreos/go-oidc/jose/sig_hmac_test.go
generated
vendored
Normal file
|
@ -0,0 +1,85 @@
|
|||
package jose
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/base64"
|
||||
"testing"
|
||||
)
|
||||
|
||||
var hmacTestCases = []struct {
|
||||
data string
|
||||
sig string
|
||||
jwk JWK
|
||||
valid bool
|
||||
desc string
|
||||
}{
|
||||
{
|
||||
"test",
|
||||
"Aymga2LNFrM-tnkr6MYLFY2Jou46h2_Omogeu0iMCRQ=",
|
||||
JWK{
|
||||
ID: "fake-key",
|
||||
Alg: "HS256",
|
||||
Secret: []byte("secret"),
|
||||
},
|
||||
true,
|
||||
"valid case",
|
||||
},
|
||||
{
|
||||
"test",
|
||||
"Aymga2LNFrM-tnkr6MYLFY2Jou46h2_Omogeu0iMCRQ=",
|
||||
JWK{
|
||||
ID: "different-key",
|
||||
Alg: "HS256",
|
||||
Secret: []byte("secret"),
|
||||
},
|
||||
true,
|
||||
"invalid: different key, should not match",
|
||||
},
|
||||
{
|
||||
"test sig and non-matching data",
|
||||
"Aymga2LNFrM-tnkr6MYLFY2Jou46h2_Omogeu0iMCRQ=",
|
||||
JWK{
|
||||
ID: "fake-key",
|
||||
Alg: "HS256",
|
||||
Secret: []byte("secret"),
|
||||
},
|
||||
false,
|
||||
"invalid: sig and data should not match",
|
||||
},
|
||||
}
|
||||
|
||||
func TestVerify(t *testing.T) {
|
||||
for _, tt := range hmacTestCases {
|
||||
v, err := NewVerifierHMAC(tt.jwk)
|
||||
if err != nil {
|
||||
t.Errorf("should construct hmac verifier. test: %s. err=%v", tt.desc, err)
|
||||
}
|
||||
|
||||
decSig, _ := base64.URLEncoding.DecodeString(tt.sig)
|
||||
err = v.Verify(decSig, []byte(tt.data))
|
||||
if err == nil && !tt.valid {
|
||||
t.Errorf("verify failure. test: %s. expected: invalid, actual: valid.", tt.desc)
|
||||
}
|
||||
if err != nil && tt.valid {
|
||||
t.Errorf("verify failure. test: %s. expected: valid, actual: invalid. err=%v", tt.desc, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestSign(t *testing.T) {
|
||||
for _, tt := range hmacTestCases {
|
||||
s := NewSignerHMAC("test", tt.jwk.Secret)
|
||||
sig, err := s.Sign([]byte(tt.data))
|
||||
if err != nil {
|
||||
t.Errorf("sign failure. test: %s. err=%v", tt.desc, err)
|
||||
}
|
||||
|
||||
expSig, _ := base64.URLEncoding.DecodeString(tt.sig)
|
||||
if tt.valid && !bytes.Equal(sig, expSig) {
|
||||
t.Errorf("sign failure. test: %s. expected: %s, actual: %s.", tt.desc, tt.sig, base64.URLEncoding.EncodeToString(sig))
|
||||
}
|
||||
if !tt.valid && bytes.Equal(sig, expSig) {
|
||||
t.Errorf("sign failure. test: %s. expected: invalid signature.", tt.desc)
|
||||
}
|
||||
}
|
||||
}
|
68
vendor/github.com/coreos/go-oidc/key/key_test.go
generated
vendored
Normal file
68
vendor/github.com/coreos/go-oidc/key/key_test.go
generated
vendored
Normal file
|
@ -0,0 +1,68 @@
|
|||
package key
|
||||
|
||||
import (
|
||||
"crypto/rsa"
|
||||
"math/big"
|
||||
"reflect"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/coreos/go-oidc/jose"
|
||||
)
|
||||
|
||||
func TestPrivateRSAKeyJWK(t *testing.T) {
|
||||
n := big.NewInt(int64(17))
|
||||
if n == nil {
|
||||
panic("NewInt returned nil")
|
||||
}
|
||||
|
||||
k := &PrivateKey{
|
||||
KeyID: "foo",
|
||||
PrivateKey: &rsa.PrivateKey{
|
||||
PublicKey: rsa.PublicKey{N: n, E: 65537},
|
||||
},
|
||||
}
|
||||
|
||||
want := jose.JWK{
|
||||
ID: "foo",
|
||||
Type: "RSA",
|
||||
Alg: "RS256",
|
||||
Use: "sig",
|
||||
Modulus: n,
|
||||
Exponent: 65537,
|
||||
}
|
||||
|
||||
got := k.JWK()
|
||||
if !reflect.DeepEqual(want, got) {
|
||||
t.Fatalf("JWK mismatch: want=%#v got=%#v", want, got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestPublicKeySetKey(t *testing.T) {
|
||||
n := big.NewInt(int64(17))
|
||||
if n == nil {
|
||||
panic("NewInt returned nil")
|
||||
}
|
||||
|
||||
k := jose.JWK{
|
||||
ID: "foo",
|
||||
Type: "RSA",
|
||||
Alg: "RS256",
|
||||
Use: "sig",
|
||||
Modulus: n,
|
||||
Exponent: 65537,
|
||||
}
|
||||
now := time.Now().UTC()
|
||||
ks := NewPublicKeySet([]jose.JWK{k}, now)
|
||||
|
||||
want := &PublicKey{jwk: k}
|
||||
got := ks.Key("foo")
|
||||
if !reflect.DeepEqual(want, got) {
|
||||
t.Errorf("Unexpected response from PublicKeySet.Key: want=%#v got=%#v", want, got)
|
||||
}
|
||||
|
||||
got = ks.Key("bar")
|
||||
if got != nil {
|
||||
t.Errorf("Expected nil response from PublicKeySet.Key, got %#v", got)
|
||||
}
|
||||
}
|
225
vendor/github.com/coreos/go-oidc/key/manager_test.go
generated
vendored
Normal file
225
vendor/github.com/coreos/go-oidc/key/manager_test.go
generated
vendored
Normal file
|
@ -0,0 +1,225 @@
|
|||
package key
|
||||
|
||||
import (
|
||||
"crypto/rsa"
|
||||
"math/big"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/jonboulle/clockwork"
|
||||
|
||||
"github.com/coreos/go-oidc/jose"
|
||||
)
|
||||
|
||||
var (
|
||||
jwk1 jose.JWK
|
||||
jwk2 jose.JWK
|
||||
jwk3 jose.JWK
|
||||
)
|
||||
|
||||
func init() {
|
||||
jwk1 = jose.JWK{
|
||||
ID: "1",
|
||||
Type: "RSA",
|
||||
Alg: "RS256",
|
||||
Use: "sig",
|
||||
Modulus: big.NewInt(1),
|
||||
Exponent: 65537,
|
||||
}
|
||||
|
||||
jwk2 = jose.JWK{
|
||||
ID: "2",
|
||||
Type: "RSA",
|
||||
Alg: "RS256",
|
||||
Use: "sig",
|
||||
Modulus: big.NewInt(2),
|
||||
Exponent: 65537,
|
||||
}
|
||||
|
||||
jwk3 = jose.JWK{
|
||||
ID: "3",
|
||||
Type: "RSA",
|
||||
Alg: "RS256",
|
||||
Use: "sig",
|
||||
Modulus: big.NewInt(3),
|
||||
Exponent: 65537,
|
||||
}
|
||||
}
|
||||
|
||||
func generatePrivateKeyStatic(t *testing.T, idAndN int) *PrivateKey {
|
||||
n := big.NewInt(int64(idAndN))
|
||||
if n == nil {
|
||||
t.Fatalf("Call to NewInt(%d) failed", idAndN)
|
||||
}
|
||||
|
||||
pk := &rsa.PrivateKey{
|
||||
PublicKey: rsa.PublicKey{N: n, E: 65537},
|
||||
}
|
||||
|
||||
return &PrivateKey{
|
||||
KeyID: strconv.Itoa(idAndN),
|
||||
PrivateKey: pk,
|
||||
}
|
||||
}
|
||||
|
||||
func TestPrivateKeyManagerJWKsRotate(t *testing.T) {
|
||||
k1 := generatePrivateKeyStatic(t, 1)
|
||||
k2 := generatePrivateKeyStatic(t, 2)
|
||||
k3 := generatePrivateKeyStatic(t, 3)
|
||||
km := NewPrivateKeyManager()
|
||||
err := km.Set(&PrivateKeySet{
|
||||
keys: []*PrivateKey{k1, k2, k3},
|
||||
ActiveKeyID: k1.KeyID,
|
||||
expiresAt: time.Now().Add(time.Minute),
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("Unexpected error: %v", err)
|
||||
}
|
||||
|
||||
want := []jose.JWK{jwk1, jwk2, jwk3}
|
||||
got, err := km.JWKs()
|
||||
if err != nil {
|
||||
t.Fatalf("Unexpected error: %v", err)
|
||||
}
|
||||
if !reflect.DeepEqual(want, got) {
|
||||
t.Fatalf("JWK mismatch: want=%#v got=%#v", want, got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestPrivateKeyManagerSigner(t *testing.T) {
|
||||
k := generatePrivateKeyStatic(t, 13)
|
||||
|
||||
km := NewPrivateKeyManager()
|
||||
err := km.Set(&PrivateKeySet{
|
||||
keys: []*PrivateKey{k},
|
||||
ActiveKeyID: k.KeyID,
|
||||
expiresAt: time.Now().Add(time.Minute),
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("Unexpected error: %v", err)
|
||||
}
|
||||
|
||||
signer, err := km.Signer()
|
||||
if err != nil {
|
||||
t.Fatalf("Unexpected error: %v", err)
|
||||
}
|
||||
|
||||
wantID := "13"
|
||||
gotID := signer.ID()
|
||||
if wantID != gotID {
|
||||
t.Fatalf("Signer has incorrect ID: want=%s got=%s", wantID, gotID)
|
||||
}
|
||||
}
|
||||
|
||||
func TestPrivateKeyManagerHealthyFail(t *testing.T) {
|
||||
keyFixture := generatePrivateKeyStatic(t, 1)
|
||||
tests := []*privateKeyManager{
|
||||
// keySet nil
|
||||
&privateKeyManager{
|
||||
keySet: nil,
|
||||
clock: clockwork.NewRealClock(),
|
||||
},
|
||||
// zero keys
|
||||
&privateKeyManager{
|
||||
keySet: &PrivateKeySet{
|
||||
keys: []*PrivateKey{},
|
||||
expiresAt: time.Now().Add(time.Minute),
|
||||
},
|
||||
clock: clockwork.NewRealClock(),
|
||||
},
|
||||
// key set expired
|
||||
&privateKeyManager{
|
||||
keySet: &PrivateKeySet{
|
||||
keys: []*PrivateKey{keyFixture},
|
||||
expiresAt: time.Now().Add(-1 * time.Minute),
|
||||
},
|
||||
clock: clockwork.NewRealClock(),
|
||||
},
|
||||
}
|
||||
|
||||
for i, tt := range tests {
|
||||
if err := tt.Healthy(); err == nil {
|
||||
t.Errorf("case %d: nil error", i)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestPrivateKeyManagerHealthyFailsOtherMethods(t *testing.T) {
|
||||
km := NewPrivateKeyManager()
|
||||
if _, err := km.JWKs(); err == nil {
|
||||
t.Fatalf("Expected non-nil error")
|
||||
}
|
||||
if _, err := km.Signer(); err == nil {
|
||||
t.Fatalf("Expected non-nil error")
|
||||
}
|
||||
}
|
||||
|
||||
func TestPrivateKeyManagerExpiresAt(t *testing.T) {
|
||||
fc := clockwork.NewFakeClock()
|
||||
now := fc.Now().UTC()
|
||||
|
||||
k := generatePrivateKeyStatic(t, 17)
|
||||
km := &privateKeyManager{
|
||||
clock: fc,
|
||||
}
|
||||
|
||||
want := fc.Now().UTC()
|
||||
got := km.ExpiresAt()
|
||||
if want != got {
|
||||
t.Fatalf("Incorrect expiration time: want=%v got=%v", want, got)
|
||||
}
|
||||
|
||||
err := km.Set(&PrivateKeySet{
|
||||
keys: []*PrivateKey{k},
|
||||
ActiveKeyID: k.KeyID,
|
||||
expiresAt: now.Add(2 * time.Minute),
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("Unexpected error: %v", err)
|
||||
}
|
||||
|
||||
want = fc.Now().UTC().Add(2 * time.Minute)
|
||||
got = km.ExpiresAt()
|
||||
if want != got {
|
||||
t.Fatalf("Incorrect expiration time: want=%v got=%v", want, got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestPublicKeys(t *testing.T) {
|
||||
km := NewPrivateKeyManager()
|
||||
k1 := generatePrivateKeyStatic(t, 1)
|
||||
k2 := generatePrivateKeyStatic(t, 2)
|
||||
k3 := generatePrivateKeyStatic(t, 3)
|
||||
|
||||
tests := [][]*PrivateKey{
|
||||
[]*PrivateKey{k1},
|
||||
[]*PrivateKey{k1, k2},
|
||||
[]*PrivateKey{k1, k2, k3},
|
||||
}
|
||||
|
||||
for i, tt := range tests {
|
||||
ks := &PrivateKeySet{
|
||||
keys: tt,
|
||||
expiresAt: time.Now().Add(time.Hour),
|
||||
}
|
||||
km.Set(ks)
|
||||
|
||||
jwks, err := km.JWKs()
|
||||
if err != nil {
|
||||
t.Fatalf("Unexpected error: %v", err)
|
||||
}
|
||||
|
||||
pks := NewPublicKeySet(jwks, time.Now().Add(time.Hour))
|
||||
want := pks.Keys()
|
||||
got, err := km.PublicKeys()
|
||||
if err != nil {
|
||||
t.Fatalf("Unexpected error: %v", err)
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(want, got) {
|
||||
t.Errorf("case %d: Invalid public keys: want=%v got=%v", i, want, got)
|
||||
}
|
||||
}
|
||||
}
|
311
vendor/github.com/coreos/go-oidc/key/rotate_test.go
generated
vendored
Normal file
311
vendor/github.com/coreos/go-oidc/key/rotate_test.go
generated
vendored
Normal file
|
@ -0,0 +1,311 @@
|
|||
package key
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/jonboulle/clockwork"
|
||||
)
|
||||
|
||||
func generatePrivateKeySerialFunc(t *testing.T) GeneratePrivateKeyFunc {
|
||||
var n int
|
||||
return func() (*PrivateKey, error) {
|
||||
n++
|
||||
return generatePrivateKeyStatic(t, n), nil
|
||||
}
|
||||
}
|
||||
|
||||
func TestRotate(t *testing.T) {
|
||||
now := time.Now()
|
||||
k1 := generatePrivateKeyStatic(t, 1)
|
||||
k2 := generatePrivateKeyStatic(t, 2)
|
||||
k3 := generatePrivateKeyStatic(t, 3)
|
||||
|
||||
tests := []struct {
|
||||
start *PrivateKeySet
|
||||
key *PrivateKey
|
||||
keep int
|
||||
exp time.Time
|
||||
want *PrivateKeySet
|
||||
}{
|
||||
// start with nil keys
|
||||
{
|
||||
start: nil,
|
||||
key: k1,
|
||||
keep: 2,
|
||||
exp: now.Add(time.Second),
|
||||
want: &PrivateKeySet{
|
||||
keys: []*PrivateKey{k1},
|
||||
ActiveKeyID: k1.KeyID,
|
||||
expiresAt: now.Add(time.Second),
|
||||
},
|
||||
},
|
||||
// start with zero keys
|
||||
{
|
||||
start: &PrivateKeySet{},
|
||||
key: k1,
|
||||
keep: 2,
|
||||
exp: now.Add(time.Second),
|
||||
want: &PrivateKeySet{
|
||||
keys: []*PrivateKey{k1},
|
||||
ActiveKeyID: k1.KeyID,
|
||||
expiresAt: now.Add(time.Second),
|
||||
},
|
||||
},
|
||||
// add second key
|
||||
{
|
||||
start: &PrivateKeySet{
|
||||
keys: []*PrivateKey{k1},
|
||||
ActiveKeyID: k1.KeyID,
|
||||
expiresAt: now,
|
||||
},
|
||||
key: k2,
|
||||
keep: 2,
|
||||
exp: now.Add(time.Second),
|
||||
want: &PrivateKeySet{
|
||||
keys: []*PrivateKey{k2, k1},
|
||||
ActiveKeyID: k2.KeyID,
|
||||
expiresAt: now.Add(time.Second),
|
||||
},
|
||||
},
|
||||
// rotate in third key
|
||||
{
|
||||
start: &PrivateKeySet{
|
||||
keys: []*PrivateKey{k2, k1},
|
||||
ActiveKeyID: k2.KeyID,
|
||||
expiresAt: now,
|
||||
},
|
||||
key: k3,
|
||||
keep: 2,
|
||||
exp: now.Add(time.Second),
|
||||
want: &PrivateKeySet{
|
||||
keys: []*PrivateKey{k3, k2},
|
||||
ActiveKeyID: k3.KeyID,
|
||||
expiresAt: now.Add(time.Second),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for i, tt := range tests {
|
||||
repo := NewPrivateKeySetRepo()
|
||||
if tt.start != nil {
|
||||
err := repo.Set(tt.start)
|
||||
if err != nil {
|
||||
log.Fatalf("case %d: unexpected error: %v", i, err)
|
||||
}
|
||||
}
|
||||
|
||||
rotatePrivateKeys(repo, tt.key, tt.keep, tt.exp)
|
||||
got, err := repo.Get()
|
||||
if err != nil {
|
||||
t.Errorf("case %d: unexpected error: %v", i, err)
|
||||
continue
|
||||
}
|
||||
if !reflect.DeepEqual(tt.want, got) {
|
||||
t.Errorf("case %d: unexpected result: want=%#v got=%#v", i, tt.want, got)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestPrivateKeyRotatorRun(t *testing.T) {
|
||||
fc := clockwork.NewFakeClock()
|
||||
now := fc.Now().UTC()
|
||||
|
||||
k1 := generatePrivateKeyStatic(t, 1)
|
||||
k2 := generatePrivateKeyStatic(t, 2)
|
||||
k3 := generatePrivateKeyStatic(t, 3)
|
||||
k4 := generatePrivateKeyStatic(t, 4)
|
||||
|
||||
kRepo := NewPrivateKeySetRepo()
|
||||
krot := NewPrivateKeyRotator(kRepo, 4*time.Second)
|
||||
krot.clock = fc
|
||||
krot.generateKey = generatePrivateKeySerialFunc(t)
|
||||
|
||||
steps := []*PrivateKeySet{
|
||||
&PrivateKeySet{
|
||||
keys: []*PrivateKey{k1},
|
||||
ActiveKeyID: k1.KeyID,
|
||||
expiresAt: now.Add(4 * time.Second),
|
||||
},
|
||||
&PrivateKeySet{
|
||||
keys: []*PrivateKey{k2, k1},
|
||||
ActiveKeyID: k2.KeyID,
|
||||
expiresAt: now.Add(6 * time.Second),
|
||||
},
|
||||
&PrivateKeySet{
|
||||
keys: []*PrivateKey{k3, k2},
|
||||
ActiveKeyID: k3.KeyID,
|
||||
expiresAt: now.Add(8 * time.Second),
|
||||
},
|
||||
&PrivateKeySet{
|
||||
keys: []*PrivateKey{k4, k3},
|
||||
ActiveKeyID: k4.KeyID,
|
||||
expiresAt: now.Add(10 * time.Second),
|
||||
},
|
||||
}
|
||||
|
||||
stop := krot.Run()
|
||||
defer close(stop)
|
||||
|
||||
for i, st := range steps {
|
||||
// wait for the rotater to get sleepy
|
||||
fc.BlockUntil(1)
|
||||
|
||||
got, err := kRepo.Get()
|
||||
if err != nil {
|
||||
t.Fatalf("step %d: unexpected error: %v", i, err)
|
||||
}
|
||||
if !reflect.DeepEqual(st, got) {
|
||||
t.Fatalf("step %d: unexpected state: want=%#v got=%#v", i, st, got)
|
||||
}
|
||||
fc.Advance(2 * time.Second)
|
||||
}
|
||||
}
|
||||
|
||||
func TestPrivateKeyRotatorExpiresAt(t *testing.T) {
|
||||
fc := clockwork.NewFakeClock()
|
||||
krot := &PrivateKeyRotator{
|
||||
clock: fc,
|
||||
ttl: time.Minute,
|
||||
}
|
||||
got := krot.expiresAt()
|
||||
want := fc.Now().UTC().Add(time.Minute)
|
||||
if !reflect.DeepEqual(want, got) {
|
||||
t.Errorf("Incorrect expiration time: want=%v got=%v", want, got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestNextRotation(t *testing.T) {
|
||||
fc := clockwork.NewFakeClock()
|
||||
now := fc.Now().UTC()
|
||||
|
||||
tests := []struct {
|
||||
expiresAt time.Time
|
||||
ttl time.Duration
|
||||
numKeys int
|
||||
expected time.Duration
|
||||
}{
|
||||
{
|
||||
// closest to prod
|
||||
expiresAt: now.Add(time.Hour * 24),
|
||||
ttl: time.Hour * 24,
|
||||
numKeys: 2,
|
||||
expected: time.Hour * 12,
|
||||
},
|
||||
{
|
||||
expiresAt: now.Add(time.Hour * 2),
|
||||
ttl: time.Hour * 4,
|
||||
numKeys: 2,
|
||||
expected: 0,
|
||||
},
|
||||
{
|
||||
// No keys.
|
||||
expiresAt: now.Add(time.Hour * 2),
|
||||
ttl: time.Hour * 4,
|
||||
numKeys: 0,
|
||||
expected: 0,
|
||||
},
|
||||
{
|
||||
// Nil keyset.
|
||||
expiresAt: now.Add(time.Hour * 2),
|
||||
ttl: time.Hour * 4,
|
||||
numKeys: -1,
|
||||
expected: 0,
|
||||
},
|
||||
{
|
||||
// KeySet expired.
|
||||
expiresAt: now.Add(time.Hour * -2),
|
||||
ttl: time.Hour * 4,
|
||||
numKeys: 2,
|
||||
expected: 0,
|
||||
},
|
||||
{
|
||||
// Expiry past now + TTL
|
||||
expiresAt: now.Add(time.Hour * 5),
|
||||
ttl: time.Hour * 4,
|
||||
numKeys: 2,
|
||||
expected: 3 * time.Hour,
|
||||
},
|
||||
}
|
||||
|
||||
for i, tt := range tests {
|
||||
kRepo := NewPrivateKeySetRepo()
|
||||
krot := NewPrivateKeyRotator(kRepo, tt.ttl)
|
||||
krot.clock = fc
|
||||
pks := &PrivateKeySet{
|
||||
expiresAt: tt.expiresAt,
|
||||
}
|
||||
if tt.numKeys != -1 {
|
||||
for n := 0; n < tt.numKeys; n++ {
|
||||
pks.keys = append(pks.keys, generatePrivateKeyStatic(t, n))
|
||||
}
|
||||
err := kRepo.Set(pks)
|
||||
if err != nil {
|
||||
log.Fatalf("case %d: unexpected error: %v", i, err)
|
||||
}
|
||||
|
||||
}
|
||||
actual, err := krot.nextRotation()
|
||||
if err != nil {
|
||||
t.Errorf("case %d: error calling shouldRotate(): %v", i, err)
|
||||
}
|
||||
if actual != tt.expected {
|
||||
t.Errorf("case %d: actual == %v, want %v", i, actual, tt.expected)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestHealthy(t *testing.T) {
|
||||
fc := clockwork.NewFakeClock()
|
||||
now := fc.Now().UTC()
|
||||
|
||||
tests := []struct {
|
||||
expiresAt time.Time
|
||||
numKeys int
|
||||
expected error
|
||||
}{
|
||||
{
|
||||
expiresAt: now.Add(time.Hour),
|
||||
numKeys: 2,
|
||||
expected: nil,
|
||||
},
|
||||
{
|
||||
expiresAt: now.Add(time.Hour),
|
||||
numKeys: -1,
|
||||
expected: ErrorNoKeys,
|
||||
},
|
||||
{
|
||||
expiresAt: now.Add(time.Hour),
|
||||
numKeys: 0,
|
||||
expected: ErrorNoKeys,
|
||||
},
|
||||
{
|
||||
expiresAt: now.Add(-time.Hour),
|
||||
numKeys: 2,
|
||||
expected: ErrorPrivateKeysExpired,
|
||||
},
|
||||
}
|
||||
|
||||
for i, tt := range tests {
|
||||
kRepo := NewPrivateKeySetRepo()
|
||||
krot := NewPrivateKeyRotator(kRepo, time.Hour)
|
||||
krot.clock = fc
|
||||
pks := &PrivateKeySet{
|
||||
expiresAt: tt.expiresAt,
|
||||
}
|
||||
if tt.numKeys != -1 {
|
||||
for n := 0; n < tt.numKeys; n++ {
|
||||
pks.keys = append(pks.keys, generatePrivateKeyStatic(t, n))
|
||||
}
|
||||
err := kRepo.Set(pks)
|
||||
if err != nil {
|
||||
log.Fatalf("case %d: unexpected error: %v", i, err)
|
||||
}
|
||||
|
||||
}
|
||||
if err := krot.Healthy(); err != tt.expected {
|
||||
t.Errorf("case %d: got==%q, want==%q", i, err, tt.expected)
|
||||
}
|
||||
}
|
||||
}
|
214
vendor/github.com/coreos/go-oidc/key/sync_test.go
generated
vendored
Normal file
214
vendor/github.com/coreos/go-oidc/key/sync_test.go
generated
vendored
Normal file
|
@ -0,0 +1,214 @@
|
|||
package key
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"reflect"
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/jonboulle/clockwork"
|
||||
)
|
||||
|
||||
type staticReadableKeySetRepo struct {
|
||||
mu sync.RWMutex
|
||||
ks KeySet
|
||||
err error
|
||||
}
|
||||
|
||||
func (r *staticReadableKeySetRepo) Get() (KeySet, error) {
|
||||
r.mu.RLock()
|
||||
defer r.mu.RUnlock()
|
||||
return r.ks, r.err
|
||||
}
|
||||
|
||||
func (r *staticReadableKeySetRepo) set(ks KeySet, err error) {
|
||||
r.mu.Lock()
|
||||
defer r.mu.Unlock()
|
||||
r.ks, r.err = ks, err
|
||||
}
|
||||
|
||||
func TestKeySyncerSync(t *testing.T) {
|
||||
fc := clockwork.NewFakeClock()
|
||||
now := fc.Now().UTC()
|
||||
|
||||
k1 := generatePrivateKeyStatic(t, 1)
|
||||
k2 := generatePrivateKeyStatic(t, 2)
|
||||
k3 := generatePrivateKeyStatic(t, 3)
|
||||
|
||||
steps := []struct {
|
||||
fromKS KeySet
|
||||
fromErr error
|
||||
advance time.Duration
|
||||
want *PrivateKeySet
|
||||
}{
|
||||
// on startup, first sync should trigger within a second
|
||||
{
|
||||
fromKS: &PrivateKeySet{
|
||||
keys: []*PrivateKey{k1},
|
||||
ActiveKeyID: k1.KeyID,
|
||||
expiresAt: now.Add(10 * time.Second),
|
||||
},
|
||||
advance: time.Second,
|
||||
want: &PrivateKeySet{
|
||||
keys: []*PrivateKey{k1},
|
||||
ActiveKeyID: k1.KeyID,
|
||||
expiresAt: now.Add(10 * time.Second),
|
||||
},
|
||||
},
|
||||
// advance halfway into TTL, triggering sync
|
||||
{
|
||||
fromKS: &PrivateKeySet{
|
||||
keys: []*PrivateKey{k2, k1},
|
||||
ActiveKeyID: k2.KeyID,
|
||||
expiresAt: now.Add(15 * time.Second),
|
||||
},
|
||||
advance: 5 * time.Second,
|
||||
want: &PrivateKeySet{
|
||||
keys: []*PrivateKey{k2, k1},
|
||||
ActiveKeyID: k2.KeyID,
|
||||
expiresAt: now.Add(15 * time.Second),
|
||||
},
|
||||
},
|
||||
|
||||
// advance halfway into TTL, triggering sync that fails
|
||||
{
|
||||
fromErr: errors.New("fail!"),
|
||||
advance: 10 * time.Second,
|
||||
want: &PrivateKeySet{
|
||||
keys: []*PrivateKey{k2, k1},
|
||||
ActiveKeyID: k2.KeyID,
|
||||
expiresAt: now.Add(15 * time.Second),
|
||||
},
|
||||
},
|
||||
|
||||
// sync retries quickly, and succeeds with fixed data
|
||||
{
|
||||
fromKS: &PrivateKeySet{
|
||||
keys: []*PrivateKey{k3, k2, k1},
|
||||
ActiveKeyID: k3.KeyID,
|
||||
expiresAt: now.Add(25 * time.Second),
|
||||
},
|
||||
advance: 3 * time.Second,
|
||||
want: &PrivateKeySet{
|
||||
keys: []*PrivateKey{k3, k2, k1},
|
||||
ActiveKeyID: k3.KeyID,
|
||||
expiresAt: now.Add(25 * time.Second),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
from := &staticReadableKeySetRepo{}
|
||||
to := NewPrivateKeySetRepo()
|
||||
|
||||
syncer := NewKeySetSyncer(from, to)
|
||||
syncer.clock = fc
|
||||
stop := syncer.Run()
|
||||
defer close(stop)
|
||||
|
||||
for i, st := range steps {
|
||||
from.set(st.fromKS, st.fromErr)
|
||||
|
||||
fc.Advance(st.advance)
|
||||
fc.BlockUntil(1)
|
||||
|
||||
ks, err := to.Get()
|
||||
if err != nil {
|
||||
t.Fatalf("step %d: unable to get keys: %v", i, err)
|
||||
}
|
||||
if !reflect.DeepEqual(st.want, ks) {
|
||||
t.Fatalf("step %d: incorrect state: want=%#v got=%#v", i, st.want, ks)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestSync(t *testing.T) {
|
||||
fc := clockwork.NewFakeClock()
|
||||
now := fc.Now().UTC()
|
||||
|
||||
k1 := generatePrivateKeyStatic(t, 1)
|
||||
k2 := generatePrivateKeyStatic(t, 2)
|
||||
k3 := generatePrivateKeyStatic(t, 3)
|
||||
|
||||
tests := []struct {
|
||||
keySet *PrivateKeySet
|
||||
want time.Duration
|
||||
}{
|
||||
{
|
||||
keySet: &PrivateKeySet{
|
||||
keys: []*PrivateKey{k1},
|
||||
ActiveKeyID: k1.KeyID,
|
||||
expiresAt: now.Add(time.Minute),
|
||||
},
|
||||
want: time.Minute,
|
||||
},
|
||||
{
|
||||
keySet: &PrivateKeySet{
|
||||
keys: []*PrivateKey{k2, k1},
|
||||
ActiveKeyID: k2.KeyID,
|
||||
expiresAt: now.Add(time.Minute),
|
||||
},
|
||||
want: time.Minute,
|
||||
},
|
||||
{
|
||||
keySet: &PrivateKeySet{
|
||||
keys: []*PrivateKey{k3, k2, k1},
|
||||
ActiveKeyID: k2.KeyID,
|
||||
expiresAt: now.Add(time.Minute),
|
||||
},
|
||||
want: time.Minute,
|
||||
},
|
||||
{
|
||||
keySet: &PrivateKeySet{
|
||||
keys: []*PrivateKey{k2, k1},
|
||||
ActiveKeyID: k2.KeyID,
|
||||
expiresAt: now.Add(time.Hour),
|
||||
},
|
||||
want: time.Hour,
|
||||
},
|
||||
{
|
||||
keySet: &PrivateKeySet{
|
||||
keys: []*PrivateKey{k1},
|
||||
ActiveKeyID: k1.KeyID,
|
||||
expiresAt: now.Add(-time.Hour),
|
||||
},
|
||||
want: 0,
|
||||
},
|
||||
}
|
||||
|
||||
for i, tt := range tests {
|
||||
from := NewPrivateKeySetRepo()
|
||||
to := NewPrivateKeySetRepo()
|
||||
|
||||
err := from.Set(tt.keySet)
|
||||
if err != nil {
|
||||
t.Errorf("case %d: unexpected error: %v", i, err)
|
||||
continue
|
||||
}
|
||||
exp, err := syncKeySet(from, to, fc)
|
||||
if err != nil {
|
||||
t.Errorf("case %d: unexpected error: %v", i, err)
|
||||
continue
|
||||
}
|
||||
|
||||
if tt.want != exp {
|
||||
t.Errorf("case %d: want=%v got=%v", i, tt.want, exp)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestSyncFail(t *testing.T) {
|
||||
tests := []error{
|
||||
nil,
|
||||
errors.New("fail!"),
|
||||
}
|
||||
|
||||
for i, tt := range tests {
|
||||
from := &staticReadableKeySetRepo{ks: nil, err: tt}
|
||||
to := NewPrivateKeySetRepo()
|
||||
|
||||
if _, err := syncKeySet(from, to, clockwork.NewFakeClock()); err == nil {
|
||||
t.Errorf("case %d: expected non-nil error", i)
|
||||
}
|
||||
}
|
||||
}
|
435
vendor/github.com/coreos/go-oidc/oauth2/oauth2_test.go
generated
vendored
Normal file
435
vendor/github.com/coreos/go-oidc/oauth2/oauth2_test.go
generated
vendored
Normal file
|
@ -0,0 +1,435 @@
|
|||
package oauth2
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
phttp "github.com/coreos/go-oidc/http"
|
||||
)
|
||||
|
||||
func TestResponseTypesEqual(t *testing.T) {
|
||||
tests := []struct {
|
||||
r1, r2 string
|
||||
want bool
|
||||
}{
|
||||
{"code", "code", true},
|
||||
{"id_token", "code", false},
|
||||
{"code token", "token code", true},
|
||||
{"code token", "code token", true},
|
||||
{"foo", "bar code", false},
|
||||
{"code token id_token", "token id_token code", true},
|
||||
{"code token id_token", "token id_token code zoo", false},
|
||||
}
|
||||
|
||||
for i, tt := range tests {
|
||||
got1 := ResponseTypesEqual(tt.r1, tt.r2)
|
||||
got2 := ResponseTypesEqual(tt.r2, tt.r1)
|
||||
if got1 != got2 {
|
||||
t.Errorf("case %d: got different answers with different orders", i)
|
||||
}
|
||||
if tt.want != got1 {
|
||||
t.Errorf("case %d: want=%t, got=%t", i, tt.want, got1)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseAuthCodeRequest(t *testing.T) {
|
||||
tests := []struct {
|
||||
query url.Values
|
||||
wantACR AuthCodeRequest
|
||||
wantErr error
|
||||
}{
|
||||
// no redirect_uri
|
||||
{
|
||||
query: url.Values{
|
||||
"response_type": []string{"code"},
|
||||
"scope": []string{"foo bar baz"},
|
||||
"client_id": []string{"XXX"},
|
||||
"state": []string{"pants"},
|
||||
},
|
||||
wantACR: AuthCodeRequest{
|
||||
ResponseType: "code",
|
||||
ClientID: "XXX",
|
||||
Scope: []string{"foo", "bar", "baz"},
|
||||
State: "pants",
|
||||
RedirectURL: nil,
|
||||
},
|
||||
},
|
||||
|
||||
// with redirect_uri
|
||||
{
|
||||
query: url.Values{
|
||||
"response_type": []string{"code"},
|
||||
"redirect_uri": []string{"https://127.0.0.1:5555/callback?foo=bar"},
|
||||
"scope": []string{"foo bar baz"},
|
||||
"client_id": []string{"XXX"},
|
||||
"state": []string{"pants"},
|
||||
},
|
||||
wantACR: AuthCodeRequest{
|
||||
ResponseType: "code",
|
||||
ClientID: "XXX",
|
||||
Scope: []string{"foo", "bar", "baz"},
|
||||
State: "pants",
|
||||
RedirectURL: &url.URL{
|
||||
Scheme: "https",
|
||||
Host: "127.0.0.1:5555",
|
||||
Path: "/callback",
|
||||
RawQuery: "foo=bar",
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
// unsupported response_type doesn't trigger error
|
||||
{
|
||||
query: url.Values{
|
||||
"response_type": []string{"token"},
|
||||
"redirect_uri": []string{"https://127.0.0.1:5555/callback?foo=bar"},
|
||||
"scope": []string{"foo bar baz"},
|
||||
"client_id": []string{"XXX"},
|
||||
"state": []string{"pants"},
|
||||
},
|
||||
wantACR: AuthCodeRequest{
|
||||
ResponseType: "token",
|
||||
ClientID: "XXX",
|
||||
Scope: []string{"foo", "bar", "baz"},
|
||||
State: "pants",
|
||||
RedirectURL: &url.URL{
|
||||
Scheme: "https",
|
||||
Host: "127.0.0.1:5555",
|
||||
Path: "/callback",
|
||||
RawQuery: "foo=bar",
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
// unparseable redirect_uri
|
||||
{
|
||||
query: url.Values{
|
||||
"response_type": []string{"code"},
|
||||
"redirect_uri": []string{":"},
|
||||
"scope": []string{"foo bar baz"},
|
||||
"client_id": []string{"XXX"},
|
||||
"state": []string{"pants"},
|
||||
},
|
||||
wantACR: AuthCodeRequest{
|
||||
ResponseType: "code",
|
||||
ClientID: "XXX",
|
||||
Scope: []string{"foo", "bar", "baz"},
|
||||
State: "pants",
|
||||
},
|
||||
wantErr: NewError(ErrorInvalidRequest),
|
||||
},
|
||||
|
||||
// no client_id, redirect_uri not parsed
|
||||
{
|
||||
query: url.Values{
|
||||
"response_type": []string{"code"},
|
||||
"redirect_uri": []string{"https://127.0.0.1:5555/callback?foo=bar"},
|
||||
"scope": []string{"foo bar baz"},
|
||||
"client_id": []string{},
|
||||
"state": []string{"pants"},
|
||||
},
|
||||
wantACR: AuthCodeRequest{
|
||||
ResponseType: "code",
|
||||
ClientID: "",
|
||||
Scope: []string{"foo", "bar", "baz"},
|
||||
State: "pants",
|
||||
RedirectURL: nil,
|
||||
},
|
||||
wantErr: NewError(ErrorInvalidRequest),
|
||||
},
|
||||
}
|
||||
|
||||
for i, tt := range tests {
|
||||
got, err := ParseAuthCodeRequest(tt.query)
|
||||
if !reflect.DeepEqual(tt.wantErr, err) {
|
||||
t.Errorf("case %d: incorrect error value: want=%q got=%q", i, tt.wantErr, err)
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(tt.wantACR, got) {
|
||||
t.Errorf("case %d: incorrect AuthCodeRequest value: want=%#v got=%#v", i, tt.wantACR, got)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestClientCredsToken(t *testing.T) {
|
||||
hc := &phttp.RequestRecorder{Error: errors.New("error")}
|
||||
cfg := Config{
|
||||
Credentials: ClientCredentials{ID: "cid", Secret: "csecret"},
|
||||
Scope: []string{"foo-scope", "bar-scope"},
|
||||
TokenURL: "http://example.com/token",
|
||||
AuthMethod: AuthMethodClientSecretBasic,
|
||||
RedirectURL: "http://example.com/redirect",
|
||||
AuthURL: "http://example.com/auth",
|
||||
}
|
||||
|
||||
c, err := NewClient(hc, cfg)
|
||||
if err != nil {
|
||||
t.Errorf("unexpected error %v", err)
|
||||
}
|
||||
|
||||
scope := []string{"openid"}
|
||||
c.ClientCredsToken(scope)
|
||||
if hc.Request == nil {
|
||||
t.Error("request is empty")
|
||||
}
|
||||
|
||||
tu := hc.Request.URL.String()
|
||||
if cfg.TokenURL != tu {
|
||||
t.Errorf("wrong token url, want=%v, got=%v", cfg.TokenURL, tu)
|
||||
}
|
||||
|
||||
ct := hc.Request.Header.Get("Content-Type")
|
||||
if ct != "application/x-www-form-urlencoded" {
|
||||
t.Errorf("wrong content-type, want=application/x-www-form-urlencoded, got=%v", ct)
|
||||
}
|
||||
|
||||
cid, secret, ok := phttp.BasicAuth(hc.Request)
|
||||
if !ok {
|
||||
t.Error("unexpected error parsing basic auth")
|
||||
}
|
||||
|
||||
if cfg.Credentials.ID != cid {
|
||||
t.Errorf("wrong client ID, want=%v, got=%v", cfg.Credentials.ID, cid)
|
||||
}
|
||||
|
||||
if cfg.Credentials.Secret != secret {
|
||||
t.Errorf("wrong client secret, want=%v, got=%v", cfg.Credentials.Secret, secret)
|
||||
}
|
||||
|
||||
err = hc.Request.ParseForm()
|
||||
if err != nil {
|
||||
t.Error("unexpected error parsing form")
|
||||
}
|
||||
|
||||
gt := hc.Request.PostForm.Get("grant_type")
|
||||
if gt != GrantTypeClientCreds {
|
||||
t.Errorf("wrong grant_type, want=client_credentials, got=%v", gt)
|
||||
}
|
||||
|
||||
sc := strings.Split(hc.Request.PostForm.Get("scope"), " ")
|
||||
if !reflect.DeepEqual(scope, sc) {
|
||||
t.Errorf("wrong scope, want=%v, got=%v", scope, sc)
|
||||
}
|
||||
}
|
||||
|
||||
func TestNewAuthenticatedRequest(t *testing.T) {
|
||||
tests := []struct {
|
||||
authMethod string
|
||||
url string
|
||||
values url.Values
|
||||
}{
|
||||
{
|
||||
authMethod: AuthMethodClientSecretBasic,
|
||||
url: "http://example.com/token",
|
||||
values: url.Values{},
|
||||
},
|
||||
{
|
||||
authMethod: AuthMethodClientSecretPost,
|
||||
url: "http://example.com/token",
|
||||
values: url.Values{},
|
||||
},
|
||||
}
|
||||
|
||||
for i, tt := range tests {
|
||||
hc := &phttp.HandlerClient{}
|
||||
cfg := Config{
|
||||
Credentials: ClientCredentials{ID: "cid", Secret: "csecret"},
|
||||
Scope: []string{"foo-scope", "bar-scope"},
|
||||
TokenURL: "http://example.com/token",
|
||||
AuthURL: "http://example.com/auth",
|
||||
RedirectURL: "http://example.com/redirect",
|
||||
AuthMethod: tt.authMethod,
|
||||
}
|
||||
c, err := NewClient(hc, cfg)
|
||||
req, err := c.newAuthenticatedRequest(tt.url, tt.values)
|
||||
if err != nil {
|
||||
t.Errorf("case %d: unexpected error: %v", i, err)
|
||||
continue
|
||||
}
|
||||
err = req.ParseForm()
|
||||
if err != nil {
|
||||
t.Errorf("case %d: want nil err, got %v", i, err)
|
||||
}
|
||||
|
||||
if tt.authMethod == AuthMethodClientSecretBasic {
|
||||
cid, secret, ok := phttp.BasicAuth(req)
|
||||
if !ok {
|
||||
t.Errorf("case %d: !ok parsing Basic Auth headers", i)
|
||||
continue
|
||||
}
|
||||
if cid != cfg.Credentials.ID {
|
||||
t.Errorf("case %d: want CID == %q, got CID == %q", i, cfg.Credentials.ID, cid)
|
||||
}
|
||||
if secret != cfg.Credentials.Secret {
|
||||
t.Errorf("case %d: want secret == %q, got secret == %q", i, cfg.Credentials.Secret, secret)
|
||||
}
|
||||
} else if tt.authMethod == AuthMethodClientSecretPost {
|
||||
if req.PostFormValue("client_secret") != cfg.Credentials.Secret {
|
||||
t.Errorf("case %d: want client_secret == %q, got client_secret == %q",
|
||||
i, cfg.Credentials.Secret, req.PostFormValue("client_secret"))
|
||||
}
|
||||
}
|
||||
|
||||
for k, v := range tt.values {
|
||||
if !reflect.DeepEqual(v, req.PostForm[k]) {
|
||||
t.Errorf("case %d: key:%q want==%q, got==%q", i, k, v, req.PostForm[k])
|
||||
}
|
||||
}
|
||||
|
||||
if req.URL.String() != tt.url {
|
||||
t.Errorf("case %d: want URL==%q, got URL==%q", i, tt.url, req.URL.String())
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseTokenResponse(t *testing.T) {
|
||||
type response struct {
|
||||
body string
|
||||
contentType string
|
||||
statusCode int // defaults to http.StatusOK
|
||||
}
|
||||
tests := []struct {
|
||||
resp response
|
||||
wantResp TokenResponse
|
||||
wantError *Error
|
||||
}{
|
||||
{
|
||||
resp: response{
|
||||
body: "{ \"error\": \"invalid_client\", \"state\": \"foo\" }",
|
||||
contentType: "application/json",
|
||||
statusCode: http.StatusBadRequest,
|
||||
},
|
||||
wantError: &Error{Type: "invalid_client", State: "foo"},
|
||||
},
|
||||
{
|
||||
resp: response{
|
||||
body: "{ \"error\": \"invalid_request\", \"state\": \"bar\" }",
|
||||
contentType: "application/json",
|
||||
statusCode: http.StatusBadRequest,
|
||||
},
|
||||
wantError: &Error{Type: "invalid_request", State: "bar"},
|
||||
},
|
||||
{
|
||||
// Actual response from bitbucket
|
||||
resp: response{
|
||||
body: `{"error_description": "Invalid OAuth client credentials", "error": "unauthorized_client"}`,
|
||||
contentType: "application/json",
|
||||
statusCode: http.StatusBadRequest,
|
||||
},
|
||||
wantError: &Error{Type: "unauthorized_client", Description: "Invalid OAuth client credentials"},
|
||||
},
|
||||
{
|
||||
// Actual response from github
|
||||
resp: response{
|
||||
body: `error=incorrect_client_credentials&error_description=The+client_id+and%2For+client_secret+passed+are+incorrect.&error_uri=https%3A%2F%2Fdeveloper.github.com%2Fv3%2Foauth%2F%23incorrect-client-credentials`,
|
||||
contentType: "application/x-www-form-urlencoded; charset=utf-8",
|
||||
},
|
||||
wantError: &Error{Type: "incorrect_client_credentials", Description: "The client_id and/or client_secret passed are incorrect."},
|
||||
},
|
||||
{
|
||||
resp: response{
|
||||
body: `{"access_token":"e72e16c7e42f292c6912e7710c838347ae178b4a", "scope":"repo,gist", "token_type":"bearer"}`,
|
||||
contentType: "application/json",
|
||||
},
|
||||
wantResp: TokenResponse{
|
||||
AccessToken: "e72e16c7e42f292c6912e7710c838347ae178b4a",
|
||||
TokenType: "bearer",
|
||||
Scope: "repo,gist",
|
||||
},
|
||||
},
|
||||
{
|
||||
resp: response{
|
||||
body: `access_token=e72e16c7e42f292c6912e7710c838347ae178b4a&scope=user%2Cgist&token_type=bearer`,
|
||||
contentType: "application/x-www-form-urlencoded",
|
||||
},
|
||||
wantResp: TokenResponse{
|
||||
AccessToken: "e72e16c7e42f292c6912e7710c838347ae178b4a",
|
||||
TokenType: "bearer",
|
||||
Scope: "user,gist",
|
||||
},
|
||||
},
|
||||
{
|
||||
resp: response{
|
||||
body: `{"access_token":"foo","id_token":"bar","expires_in":200,"token_type":"bearer","refresh_token":"spam"}`,
|
||||
contentType: "application/json; charset=utf-8",
|
||||
},
|
||||
wantResp: TokenResponse{
|
||||
AccessToken: "foo",
|
||||
IDToken: "bar",
|
||||
Expires: 200,
|
||||
TokenType: "bearer",
|
||||
RefreshToken: "spam",
|
||||
},
|
||||
},
|
||||
{
|
||||
resp: response{
|
||||
body: `{"access_token":"foo","id_token":"bar","expires":200,"token_type":"bearer","refresh_token":"spam"}`,
|
||||
contentType: "application/json; charset=utf-8",
|
||||
},
|
||||
wantResp: TokenResponse{
|
||||
AccessToken: "foo",
|
||||
IDToken: "bar",
|
||||
Expires: 200,
|
||||
TokenType: "bearer",
|
||||
RefreshToken: "spam",
|
||||
},
|
||||
},
|
||||
{
|
||||
resp: response{
|
||||
body: `access_token=foo&id_token=bar&expires_in=200&token_type=bearer&refresh_token=spam`,
|
||||
contentType: "application/x-www-form-urlencoded",
|
||||
},
|
||||
wantResp: TokenResponse{
|
||||
AccessToken: "foo",
|
||||
IDToken: "bar",
|
||||
Expires: 200,
|
||||
TokenType: "bearer",
|
||||
RefreshToken: "spam",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for i, tt := range tests {
|
||||
r := &http.Response{
|
||||
StatusCode: http.StatusOK,
|
||||
Header: http.Header{
|
||||
"Content-Type": []string{tt.resp.contentType},
|
||||
"Content-Length": []string{strconv.Itoa(len([]byte(tt.resp.body)))},
|
||||
},
|
||||
Body: ioutil.NopCloser(strings.NewReader(tt.resp.body)),
|
||||
ContentLength: int64(len([]byte(tt.resp.body))),
|
||||
}
|
||||
if tt.resp.statusCode != 0 {
|
||||
r.StatusCode = tt.resp.statusCode
|
||||
}
|
||||
|
||||
result, err := parseTokenResponse(r)
|
||||
if err != nil {
|
||||
if tt.wantError == nil {
|
||||
t.Errorf("case %d: got error==%v", i, err)
|
||||
continue
|
||||
}
|
||||
if !reflect.DeepEqual(tt.wantError, err) {
|
||||
t.Errorf("case %d: want=%+v, got=%+v", i, tt.wantError, err)
|
||||
}
|
||||
} else {
|
||||
if tt.wantError != nil {
|
||||
t.Errorf("case %d: want error==%v, got==nil", i, tt.wantError)
|
||||
continue
|
||||
}
|
||||
// don't compare the raw body (it's really big and clogs error messages)
|
||||
result.RawBody = tt.wantResp.RawBody
|
||||
if !reflect.DeepEqual(tt.wantResp, result) {
|
||||
t.Errorf("case %d: want=%+v, got=%+v", i, tt.wantResp, result)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
81
vendor/github.com/coreos/go-oidc/oidc/client_race_test.go
generated
vendored
Normal file
81
vendor/github.com/coreos/go-oidc/oidc/client_race_test.go
generated
vendored
Normal file
|
@ -0,0 +1,81 @@
|
|||
// This file contains tests which depend on the race detector being enabled.
|
||||
// +build race
|
||||
|
||||
package oidc
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"net/url"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
type testProvider struct {
|
||||
baseURL *url.URL
|
||||
}
|
||||
|
||||
func (p *testProvider) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
if r.URL.Path != discoveryConfigPath {
|
||||
http.NotFound(w, r)
|
||||
return
|
||||
}
|
||||
|
||||
cfg := ProviderConfig{
|
||||
Issuer: p.baseURL,
|
||||
ExpiresAt: time.Now().Add(time.Second),
|
||||
}
|
||||
cfg = fillRequiredProviderFields(cfg)
|
||||
json.NewEncoder(w).Encode(&cfg)
|
||||
}
|
||||
|
||||
// This test fails by triggering the race detector, not by calling t.Error or t.Fatal.
|
||||
func TestProviderSyncRace(t *testing.T) {
|
||||
|
||||
prov := &testProvider{}
|
||||
|
||||
s := httptest.NewServer(prov)
|
||||
defer s.Close()
|
||||
u, err := url.Parse(s.URL)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
prov.baseURL = u
|
||||
|
||||
prevValue := minimumProviderConfigSyncInterval
|
||||
defer func() { minimumProviderConfigSyncInterval = prevValue }()
|
||||
|
||||
// Reduce the sync interval to increase the write frequencey.
|
||||
minimumProviderConfigSyncInterval = 5 * time.Millisecond
|
||||
|
||||
cliCfg := ClientConfig{
|
||||
HTTPClient: http.DefaultClient,
|
||||
}
|
||||
cli, err := NewClient(cliCfg)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
|
||||
if !cli.providerConfig.Get().Empty() {
|
||||
t.Errorf("want c.ProviderConfig == nil, got c.ProviderConfig=%#v")
|
||||
}
|
||||
|
||||
// SyncProviderConfig beings a goroutine which writes to the client's provider config.
|
||||
c := cli.SyncProviderConfig(s.URL)
|
||||
if cli.providerConfig.Get().Empty() {
|
||||
t.Errorf("want c.ProviderConfig != nil")
|
||||
}
|
||||
|
||||
defer func() {
|
||||
// stop the background process
|
||||
c <- struct{}{}
|
||||
}()
|
||||
|
||||
for i := 0; i < 10; i++ {
|
||||
time.Sleep(5 * time.Millisecond)
|
||||
// Creating an OAuth client reads from the provider config.
|
||||
cli.OAuthClient()
|
||||
}
|
||||
}
|
654
vendor/github.com/coreos/go-oidc/oidc/client_test.go
generated
vendored
Normal file
654
vendor/github.com/coreos/go-oidc/oidc/client_test.go
generated
vendored
Normal file
|
@ -0,0 +1,654 @@
|
|||
package oidc
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"net/mail"
|
||||
"net/url"
|
||||
"reflect"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/coreos/go-oidc/jose"
|
||||
"github.com/coreos/go-oidc/key"
|
||||
"github.com/coreos/go-oidc/oauth2"
|
||||
"github.com/kylelemons/godebug/pretty"
|
||||
)
|
||||
|
||||
func TestNewClientScopeDefault(t *testing.T) {
|
||||
tests := []struct {
|
||||
c ClientConfig
|
||||
e []string
|
||||
}{
|
||||
{
|
||||
// No scope
|
||||
c: ClientConfig{RedirectURL: "http://example.com/redirect"},
|
||||
e: DefaultScope,
|
||||
},
|
||||
{
|
||||
// Nil scope
|
||||
c: ClientConfig{RedirectURL: "http://example.com/redirect", Scope: nil},
|
||||
e: DefaultScope,
|
||||
},
|
||||
{
|
||||
// Empty scope
|
||||
c: ClientConfig{RedirectURL: "http://example.com/redirect", Scope: []string{}},
|
||||
e: []string{},
|
||||
},
|
||||
{
|
||||
// Custom scope equal to default
|
||||
c: ClientConfig{RedirectURL: "http://example.com/redirect", Scope: []string{"openid", "email", "profile"}},
|
||||
e: DefaultScope,
|
||||
},
|
||||
{
|
||||
// Custom scope not including defaults
|
||||
c: ClientConfig{RedirectURL: "http://example.com/redirect", Scope: []string{"foo", "bar"}},
|
||||
e: []string{"foo", "bar"},
|
||||
},
|
||||
{
|
||||
// Custom scopes overlapping with defaults
|
||||
c: ClientConfig{RedirectURL: "http://example.com/redirect", Scope: []string{"openid", "foo"}},
|
||||
e: []string{"openid", "foo"},
|
||||
},
|
||||
}
|
||||
|
||||
for i, tt := range tests {
|
||||
c, err := NewClient(tt.c)
|
||||
if err != nil {
|
||||
t.Errorf("case %d: unexpected error from NewClient: %v", i, err)
|
||||
continue
|
||||
}
|
||||
if !reflect.DeepEqual(tt.e, c.scope) {
|
||||
t.Errorf("case %d: want: %v, got: %v", i, tt.e, c.scope)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestHealthy(t *testing.T) {
|
||||
now := time.Now().UTC()
|
||||
|
||||
tests := []struct {
|
||||
p ProviderConfig
|
||||
h bool
|
||||
}{
|
||||
// all ok
|
||||
{
|
||||
p: ProviderConfig{
|
||||
Issuer: &url.URL{Scheme: "http", Host: "example.com"},
|
||||
ExpiresAt: now.Add(time.Hour),
|
||||
},
|
||||
h: true,
|
||||
},
|
||||
// zero-value ProviderConfig.ExpiresAt
|
||||
{
|
||||
p: ProviderConfig{
|
||||
Issuer: &url.URL{Scheme: "http", Host: "example.com"},
|
||||
},
|
||||
h: true,
|
||||
},
|
||||
// expired ProviderConfig
|
||||
{
|
||||
p: ProviderConfig{
|
||||
Issuer: &url.URL{Scheme: "http", Host: "example.com"},
|
||||
ExpiresAt: now.Add(time.Hour * -1),
|
||||
},
|
||||
h: false,
|
||||
},
|
||||
// empty ProviderConfig
|
||||
{
|
||||
p: ProviderConfig{},
|
||||
h: false,
|
||||
},
|
||||
}
|
||||
|
||||
for i, tt := range tests {
|
||||
c := &Client{providerConfig: newProviderConfigRepo(tt.p)}
|
||||
err := c.Healthy()
|
||||
want := tt.h
|
||||
got := (err == nil)
|
||||
|
||||
if want != got {
|
||||
t.Errorf("case %d: want: healthy=%v, got: healhty=%v, err: %v", i, want, got, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestClientKeysFuncAll(t *testing.T) {
|
||||
priv1, err := key.GeneratePrivateKey()
|
||||
if err != nil {
|
||||
t.Fatalf("failed to generate private key, error=%v", err)
|
||||
}
|
||||
|
||||
priv2, err := key.GeneratePrivateKey()
|
||||
if err != nil {
|
||||
t.Fatalf("failed to generate private key, error=%v", err)
|
||||
}
|
||||
|
||||
now := time.Now()
|
||||
future := now.Add(time.Hour)
|
||||
past := now.Add(-1 * time.Hour)
|
||||
|
||||
tests := []struct {
|
||||
keySet *key.PublicKeySet
|
||||
want []key.PublicKey
|
||||
}{
|
||||
// two keys, non-expired set
|
||||
{
|
||||
keySet: key.NewPublicKeySet([]jose.JWK{priv2.JWK(), priv1.JWK()}, future),
|
||||
want: []key.PublicKey{*key.NewPublicKey(priv2.JWK()), *key.NewPublicKey(priv1.JWK())},
|
||||
},
|
||||
|
||||
// no keys, non-expired set
|
||||
{
|
||||
keySet: key.NewPublicKeySet([]jose.JWK{}, future),
|
||||
want: []key.PublicKey{},
|
||||
},
|
||||
|
||||
// two keys, expired set
|
||||
{
|
||||
keySet: key.NewPublicKeySet([]jose.JWK{priv2.JWK(), priv1.JWK()}, past),
|
||||
want: []key.PublicKey{},
|
||||
},
|
||||
|
||||
// no keys, expired set
|
||||
{
|
||||
keySet: key.NewPublicKeySet([]jose.JWK{}, past),
|
||||
want: []key.PublicKey{},
|
||||
},
|
||||
}
|
||||
|
||||
for i, tt := range tests {
|
||||
var c Client
|
||||
c.keySet = *tt.keySet
|
||||
keysFunc := c.keysFuncAll()
|
||||
got := keysFunc()
|
||||
if !reflect.DeepEqual(tt.want, got) {
|
||||
t.Errorf("case %d: want=%#v got=%#v", i, tt.want, got)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestClientKeysFuncWithID(t *testing.T) {
|
||||
priv1, err := key.GeneratePrivateKey()
|
||||
if err != nil {
|
||||
t.Fatalf("failed to generate private key, error=%v", err)
|
||||
}
|
||||
|
||||
priv2, err := key.GeneratePrivateKey()
|
||||
if err != nil {
|
||||
t.Fatalf("failed to generate private key, error=%v", err)
|
||||
}
|
||||
|
||||
now := time.Now()
|
||||
future := now.Add(time.Hour)
|
||||
past := now.Add(-1 * time.Hour)
|
||||
|
||||
tests := []struct {
|
||||
keySet *key.PublicKeySet
|
||||
argID string
|
||||
want []key.PublicKey
|
||||
}{
|
||||
// two keys, match, non-expired set
|
||||
{
|
||||
keySet: key.NewPublicKeySet([]jose.JWK{priv2.JWK(), priv1.JWK()}, future),
|
||||
argID: priv2.ID(),
|
||||
want: []key.PublicKey{*key.NewPublicKey(priv2.JWK())},
|
||||
},
|
||||
|
||||
// two keys, no match, non-expired set
|
||||
{
|
||||
keySet: key.NewPublicKeySet([]jose.JWK{priv2.JWK(), priv1.JWK()}, future),
|
||||
argID: "XXX",
|
||||
want: []key.PublicKey{},
|
||||
},
|
||||
|
||||
// no keys, no match, non-expired set
|
||||
{
|
||||
keySet: key.NewPublicKeySet([]jose.JWK{}, future),
|
||||
argID: priv2.ID(),
|
||||
want: []key.PublicKey{},
|
||||
},
|
||||
|
||||
// two keys, match, expired set
|
||||
{
|
||||
keySet: key.NewPublicKeySet([]jose.JWK{priv2.JWK(), priv1.JWK()}, past),
|
||||
argID: priv2.ID(),
|
||||
want: []key.PublicKey{},
|
||||
},
|
||||
|
||||
// no keys, no match, expired set
|
||||
{
|
||||
keySet: key.NewPublicKeySet([]jose.JWK{}, past),
|
||||
argID: priv2.ID(),
|
||||
want: []key.PublicKey{},
|
||||
},
|
||||
}
|
||||
|
||||
for i, tt := range tests {
|
||||
var c Client
|
||||
c.keySet = *tt.keySet
|
||||
keysFunc := c.keysFuncWithID(tt.argID)
|
||||
got := keysFunc()
|
||||
if !reflect.DeepEqual(tt.want, got) {
|
||||
t.Errorf("case %d: want=%#v got=%#v", i, tt.want, got)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestClientMetadataValid(t *testing.T) {
|
||||
tests := []ClientMetadata{
|
||||
// one RedirectURL
|
||||
ClientMetadata{
|
||||
RedirectURIs: []url.URL{url.URL{Scheme: "http", Host: "example.com"}},
|
||||
},
|
||||
|
||||
// one RedirectURL w/ nonempty path
|
||||
ClientMetadata{
|
||||
RedirectURIs: []url.URL{url.URL{Scheme: "http", Host: "example.com", Path: "/foo"}},
|
||||
},
|
||||
|
||||
// two RedirectURIs
|
||||
ClientMetadata{
|
||||
RedirectURIs: []url.URL{
|
||||
url.URL{Scheme: "http", Host: "foo.example.com"},
|
||||
url.URL{Scheme: "http", Host: "bar.example.com"},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for i, tt := range tests {
|
||||
if err := tt.Valid(); err != nil {
|
||||
t.Errorf("case %d: unexpected error: %v", i, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestClientMetadataInvalid(t *testing.T) {
|
||||
tests := []ClientMetadata{
|
||||
// nil RedirectURls slice
|
||||
ClientMetadata{
|
||||
RedirectURIs: nil,
|
||||
},
|
||||
|
||||
// empty RedirectURIs slice
|
||||
ClientMetadata{
|
||||
RedirectURIs: []url.URL{},
|
||||
},
|
||||
|
||||
// empty url.URL
|
||||
ClientMetadata{
|
||||
RedirectURIs: []url.URL{url.URL{}},
|
||||
},
|
||||
|
||||
// empty url.URL following OK item
|
||||
ClientMetadata{
|
||||
RedirectURIs: []url.URL{url.URL{Scheme: "http", Host: "example.com"}, url.URL{}},
|
||||
},
|
||||
|
||||
// url.URL with empty Host
|
||||
ClientMetadata{
|
||||
RedirectURIs: []url.URL{url.URL{Scheme: "http", Host: ""}},
|
||||
},
|
||||
|
||||
// url.URL with empty Scheme
|
||||
ClientMetadata{
|
||||
RedirectURIs: []url.URL{url.URL{Scheme: "", Host: "example.com"}},
|
||||
},
|
||||
|
||||
// url.URL with non-HTTP(S) Scheme
|
||||
ClientMetadata{
|
||||
RedirectURIs: []url.URL{url.URL{Scheme: "tcp", Host: "127.0.0.1"}},
|
||||
},
|
||||
|
||||
// EncryptionEnc without EncryptionAlg
|
||||
ClientMetadata{
|
||||
RedirectURIs: []url.URL{url.URL{Scheme: "http", Host: "example.com"}},
|
||||
IDTokenResponseOptions: JWAOptions{
|
||||
EncryptionEnc: "A128CBC-HS256",
|
||||
},
|
||||
},
|
||||
|
||||
// List of URIs with one empty element
|
||||
ClientMetadata{
|
||||
RedirectURIs: []url.URL{url.URL{Scheme: "http", Host: "example.com"}},
|
||||
RequestURIs: []url.URL{
|
||||
url.URL{Scheme: "http", Host: "example.com"},
|
||||
url.URL{},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for i, tt := range tests {
|
||||
if err := tt.Valid(); err == nil {
|
||||
t.Errorf("case %d: expected non-nil error", i)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestChooseAuthMethod(t *testing.T) {
|
||||
tests := []struct {
|
||||
supported []string
|
||||
chosen string
|
||||
err bool
|
||||
}{
|
||||
{
|
||||
supported: []string{},
|
||||
chosen: oauth2.AuthMethodClientSecretBasic,
|
||||
},
|
||||
{
|
||||
supported: []string{oauth2.AuthMethodClientSecretBasic},
|
||||
chosen: oauth2.AuthMethodClientSecretBasic,
|
||||
},
|
||||
{
|
||||
supported: []string{oauth2.AuthMethodClientSecretPost},
|
||||
chosen: oauth2.AuthMethodClientSecretPost,
|
||||
},
|
||||
{
|
||||
supported: []string{oauth2.AuthMethodClientSecretPost, oauth2.AuthMethodClientSecretBasic},
|
||||
chosen: oauth2.AuthMethodClientSecretPost,
|
||||
},
|
||||
{
|
||||
supported: []string{oauth2.AuthMethodClientSecretBasic, oauth2.AuthMethodClientSecretPost},
|
||||
chosen: oauth2.AuthMethodClientSecretBasic,
|
||||
},
|
||||
{
|
||||
supported: []string{oauth2.AuthMethodClientSecretJWT, oauth2.AuthMethodClientSecretPost},
|
||||
chosen: oauth2.AuthMethodClientSecretPost,
|
||||
},
|
||||
{
|
||||
supported: []string{oauth2.AuthMethodClientSecretJWT},
|
||||
chosen: "",
|
||||
err: true,
|
||||
},
|
||||
}
|
||||
|
||||
for i, tt := range tests {
|
||||
cfg := ProviderConfig{
|
||||
TokenEndpointAuthMethodsSupported: tt.supported,
|
||||
}
|
||||
got, err := chooseAuthMethod(cfg)
|
||||
if tt.err {
|
||||
if err == nil {
|
||||
t.Errorf("case %d: expected non-nil err", i)
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
if got != tt.chosen {
|
||||
t.Errorf("case %d: want=%q, got=%q", i, tt.chosen, got)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestClientMetadataUnmarshal(t *testing.T) {
|
||||
tests := []struct {
|
||||
data string
|
||||
want ClientMetadata
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
`{"redirect_uris":["https://example.com"]}`,
|
||||
ClientMetadata{
|
||||
RedirectURIs: []url.URL{
|
||||
{Scheme: "https", Host: "example.com"},
|
||||
},
|
||||
},
|
||||
false,
|
||||
},
|
||||
{
|
||||
// redirect_uris required
|
||||
`{}`,
|
||||
ClientMetadata{},
|
||||
true,
|
||||
},
|
||||
{
|
||||
// must have at least one redirect_uris
|
||||
`{"redirect_uris":[]}`,
|
||||
ClientMetadata{},
|
||||
true,
|
||||
},
|
||||
{
|
||||
`{"redirect_uris":["https://example.com"],"contacts":["Ms. Foo <foo@example.com>"]}`,
|
||||
ClientMetadata{
|
||||
RedirectURIs: []url.URL{
|
||||
{Scheme: "https", Host: "example.com"},
|
||||
},
|
||||
Contacts: []mail.Address{
|
||||
{Name: "Ms. Foo", Address: "foo@example.com"},
|
||||
},
|
||||
},
|
||||
false,
|
||||
},
|
||||
{
|
||||
// invalid URI provided for field
|
||||
`{"redirect_uris":["https://example.com"],"logo_uri":"not a valid uri"}`,
|
||||
ClientMetadata{},
|
||||
true,
|
||||
},
|
||||
{
|
||||
// logo_uri can't be a list
|
||||
`{"redirect_uris":["https://example.com"],"logo_uri":["https://example.com/logo"]}`,
|
||||
ClientMetadata{},
|
||||
true,
|
||||
},
|
||||
{
|
||||
`{
|
||||
"redirect_uris":["https://example.com"],
|
||||
"userinfo_encrypted_response_alg":"RSA1_5",
|
||||
"userinfo_encrypted_response_enc":"A128CBC-HS256",
|
||||
"contacts": [
|
||||
"jane doe <jane.doe@example.com>", "john doe <john.doe@example.com>"
|
||||
]
|
||||
}`,
|
||||
ClientMetadata{
|
||||
RedirectURIs: []url.URL{
|
||||
{Scheme: "https", Host: "example.com"},
|
||||
},
|
||||
UserInfoResponseOptions: JWAOptions{
|
||||
EncryptionAlg: "RSA1_5",
|
||||
EncryptionEnc: "A128CBC-HS256",
|
||||
},
|
||||
Contacts: []mail.Address{
|
||||
{Name: "jane doe", Address: "jane.doe@example.com"},
|
||||
{Name: "john doe", Address: "john.doe@example.com"},
|
||||
},
|
||||
},
|
||||
false,
|
||||
},
|
||||
{
|
||||
// If encrypted_response_enc is provided encrypted_response_alg must also be.
|
||||
`{
|
||||
"redirect_uris":["https://example.com"],
|
||||
"userinfo_encrypted_response_enc":"A128CBC-HS256"
|
||||
}`,
|
||||
ClientMetadata{},
|
||||
true,
|
||||
},
|
||||
}
|
||||
for i, tt := range tests {
|
||||
var got ClientMetadata
|
||||
if err := got.UnmarshalJSON([]byte(tt.data)); err != nil {
|
||||
if !tt.wantErr {
|
||||
t.Errorf("case %d: unmarshal failed: %v", i, err)
|
||||
}
|
||||
continue
|
||||
}
|
||||
if tt.wantErr {
|
||||
t.Errorf("case %d: expected unmarshal to produce error", i)
|
||||
continue
|
||||
}
|
||||
|
||||
if diff := pretty.Compare(tt.want, got); diff != "" {
|
||||
t.Errorf("case %d: results not equal: %s", i, diff)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestClientMetadataMarshal(t *testing.T) {
|
||||
|
||||
tests := []struct {
|
||||
metadata ClientMetadata
|
||||
want string
|
||||
}{
|
||||
{
|
||||
ClientMetadata{
|
||||
RedirectURIs: []url.URL{
|
||||
{Scheme: "https", Host: "example.com", Path: "/callback"},
|
||||
},
|
||||
},
|
||||
`{"redirect_uris":["https://example.com/callback"]}`,
|
||||
},
|
||||
{
|
||||
ClientMetadata{
|
||||
RedirectURIs: []url.URL{
|
||||
{Scheme: "https", Host: "example.com", Path: "/callback"},
|
||||
},
|
||||
RequestObjectOptions: JWAOptions{
|
||||
EncryptionAlg: "RSA1_5",
|
||||
EncryptionEnc: "A128CBC-HS256",
|
||||
},
|
||||
},
|
||||
`{"redirect_uris":["https://example.com/callback"],"request_object_encryption_alg":"RSA1_5","request_object_encryption_enc":"A128CBC-HS256"}`,
|
||||
},
|
||||
}
|
||||
|
||||
for i, tt := range tests {
|
||||
got, err := json.Marshal(&tt.metadata)
|
||||
if err != nil {
|
||||
t.Errorf("case %d: failed to marshal metadata: %v", i, err)
|
||||
continue
|
||||
}
|
||||
if string(got) != tt.want {
|
||||
t.Errorf("case %d: marshaled string did not match expected string", i)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestClientMetadataMarshalRoundTrip(t *testing.T) {
|
||||
tests := []ClientMetadata{
|
||||
{
|
||||
RedirectURIs: []url.URL{
|
||||
{Scheme: "https", Host: "example.com", Path: "/callback"},
|
||||
},
|
||||
LogoURI: &url.URL{Scheme: "https", Host: "example.com", Path: "/logo"},
|
||||
RequestObjectOptions: JWAOptions{
|
||||
EncryptionAlg: "RSA1_5",
|
||||
EncryptionEnc: "A128CBC-HS256",
|
||||
},
|
||||
ApplicationType: "native",
|
||||
TokenEndpointAuthMethod: "client_secret_basic",
|
||||
},
|
||||
}
|
||||
|
||||
for i, want := range tests {
|
||||
data, err := json.Marshal(&want)
|
||||
if err != nil {
|
||||
t.Errorf("case %d: failed to marshal metadata: %v", i, err)
|
||||
continue
|
||||
}
|
||||
var got ClientMetadata
|
||||
if err := json.Unmarshal(data, &got); err != nil {
|
||||
t.Errorf("case %d: failed to unmarshal metadata: %v", i, err)
|
||||
continue
|
||||
}
|
||||
if diff := pretty.Compare(want, got); diff != "" {
|
||||
t.Errorf("case %d: struct did not survive a marshaling round trip: %s", i, diff)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestClientRegistrationResponseUnmarshal(t *testing.T) {
|
||||
tests := []struct {
|
||||
data string
|
||||
want ClientRegistrationResponse
|
||||
wantErr bool
|
||||
secretExpires bool
|
||||
}{
|
||||
{
|
||||
`{
|
||||
"client_id":"foo",
|
||||
"client_secret":"bar",
|
||||
"client_secret_expires_at": 1577858400,
|
||||
"redirect_uris":[
|
||||
"https://client.example.org/callback",
|
||||
"https://client.example.org/callback2"
|
||||
],
|
||||
"client_name":"my_example"
|
||||
}`,
|
||||
ClientRegistrationResponse{
|
||||
ClientID: "foo",
|
||||
ClientSecret: "bar",
|
||||
ClientSecretExpiresAt: time.Unix(1577858400, 0),
|
||||
ClientMetadata: ClientMetadata{
|
||||
RedirectURIs: []url.URL{
|
||||
{Scheme: "https", Host: "client.example.org", Path: "/callback"},
|
||||
{Scheme: "https", Host: "client.example.org", Path: "/callback2"},
|
||||
},
|
||||
ClientName: "my_example",
|
||||
},
|
||||
},
|
||||
false,
|
||||
true,
|
||||
},
|
||||
{
|
||||
`{
|
||||
"client_id":"foo",
|
||||
"client_secret_expires_at": 0,
|
||||
"redirect_uris":[
|
||||
"https://client.example.org/callback",
|
||||
"https://client.example.org/callback2"
|
||||
],
|
||||
"client_name":"my_example"
|
||||
}`,
|
||||
ClientRegistrationResponse{
|
||||
ClientID: "foo",
|
||||
ClientMetadata: ClientMetadata{
|
||||
RedirectURIs: []url.URL{
|
||||
{Scheme: "https", Host: "client.example.org", Path: "/callback"},
|
||||
{Scheme: "https", Host: "client.example.org", Path: "/callback2"},
|
||||
},
|
||||
ClientName: "my_example",
|
||||
},
|
||||
},
|
||||
false,
|
||||
false,
|
||||
},
|
||||
{
|
||||
// no client id
|
||||
`{
|
||||
"client_secret_expires_at": 0,
|
||||
"redirect_uris":[
|
||||
"https://client.example.org/callback",
|
||||
"https://client.example.org/callback2"
|
||||
],
|
||||
"client_name":"my_example"
|
||||
}`,
|
||||
ClientRegistrationResponse{},
|
||||
true,
|
||||
false,
|
||||
},
|
||||
}
|
||||
|
||||
for i, tt := range tests {
|
||||
var got ClientRegistrationResponse
|
||||
if err := json.Unmarshal([]byte(tt.data), &got); err != nil {
|
||||
if !tt.wantErr {
|
||||
t.Errorf("case %d: unmarshal failed: %v", i, err)
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
if tt.wantErr {
|
||||
t.Errorf("case %d: expected unmarshal to produce error", i)
|
||||
continue
|
||||
}
|
||||
|
||||
if diff := pretty.Compare(tt.want, got); diff != "" {
|
||||
t.Errorf("case %d: results not equal: %s", i, diff)
|
||||
}
|
||||
if tt.secretExpires && got.ClientSecretExpiresAt.IsZero() {
|
||||
t.Errorf("case %d: expected client_secret to expire, but it doesn't", i)
|
||||
} else if !tt.secretExpires && !got.ClientSecretExpiresAt.IsZero() {
|
||||
t.Errorf("case %d: expected client_secret to not expire, but it does", i)
|
||||
}
|
||||
}
|
||||
}
|
113
vendor/github.com/coreos/go-oidc/oidc/identity_test.go
generated
vendored
Normal file
113
vendor/github.com/coreos/go-oidc/oidc/identity_test.go
generated
vendored
Normal file
|
@ -0,0 +1,113 @@
|
|||
package oidc
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/coreos/go-oidc/jose"
|
||||
)
|
||||
|
||||
func TestIdentityFromClaims(t *testing.T) {
|
||||
tests := []struct {
|
||||
claims jose.Claims
|
||||
want Identity
|
||||
}{
|
||||
{
|
||||
claims: jose.Claims{
|
||||
"sub": "123850281",
|
||||
"name": "Elroy",
|
||||
"email": "elroy@example.com",
|
||||
"exp": float64(1.416935146e+09),
|
||||
},
|
||||
want: Identity{
|
||||
ID: "123850281",
|
||||
Name: "",
|
||||
Email: "elroy@example.com",
|
||||
ExpiresAt: time.Date(2014, time.November, 25, 17, 05, 46, 0, time.UTC),
|
||||
},
|
||||
},
|
||||
{
|
||||
claims: jose.Claims{
|
||||
"sub": "123850281",
|
||||
"name": "Elroy",
|
||||
"exp": float64(1.416935146e+09),
|
||||
},
|
||||
want: Identity{
|
||||
ID: "123850281",
|
||||
Name: "",
|
||||
Email: "",
|
||||
ExpiresAt: time.Date(2014, time.November, 25, 17, 05, 46, 0, time.UTC),
|
||||
},
|
||||
},
|
||||
{
|
||||
claims: jose.Claims{
|
||||
"sub": "123850281",
|
||||
"name": "Elroy",
|
||||
"email": "elroy@example.com",
|
||||
"exp": int64(1416935146),
|
||||
},
|
||||
want: Identity{
|
||||
ID: "123850281",
|
||||
Name: "",
|
||||
Email: "elroy@example.com",
|
||||
ExpiresAt: time.Date(2014, time.November, 25, 17, 05, 46, 0, time.UTC),
|
||||
},
|
||||
},
|
||||
{
|
||||
claims: jose.Claims{
|
||||
"sub": "123850281",
|
||||
"name": "Elroy",
|
||||
"email": "elroy@example.com",
|
||||
},
|
||||
want: Identity{
|
||||
ID: "123850281",
|
||||
Name: "",
|
||||
Email: "elroy@example.com",
|
||||
ExpiresAt: time.Time{},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for i, tt := range tests {
|
||||
got, err := IdentityFromClaims(tt.claims)
|
||||
if err != nil {
|
||||
t.Errorf("case %d: unexpected error: %v", i, err)
|
||||
continue
|
||||
}
|
||||
if !reflect.DeepEqual(tt.want, *got) {
|
||||
t.Errorf("case %d: want=%#v got=%#v", i, tt.want, *got)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestIdentityFromClaimsFail(t *testing.T) {
|
||||
tests := []jose.Claims{
|
||||
// sub incorrect type
|
||||
jose.Claims{
|
||||
"sub": 123,
|
||||
"name": "foo",
|
||||
"email": "elroy@example.com",
|
||||
},
|
||||
// email incorrect type
|
||||
jose.Claims{
|
||||
"sub": "123850281",
|
||||
"name": "Elroy",
|
||||
"email": false,
|
||||
},
|
||||
// exp incorrect type
|
||||
jose.Claims{
|
||||
"sub": "123850281",
|
||||
"name": "Elroy",
|
||||
"email": "elroy@example.com",
|
||||
"exp": "2014-11-25 18:05:46 +0000 UTC",
|
||||
},
|
||||
}
|
||||
|
||||
for i, tt := range tests {
|
||||
_, err := IdentityFromClaims(tt)
|
||||
if err == nil {
|
||||
t.Errorf("case %d: expected non-nil error", i)
|
||||
}
|
||||
}
|
||||
}
|
916
vendor/github.com/coreos/go-oidc/oidc/provider_test.go
generated
vendored
Normal file
916
vendor/github.com/coreos/go-oidc/oidc/provider_test.go
generated
vendored
Normal file
|
@ -0,0 +1,916 @@
|
|||
package oidc
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"net/url"
|
||||
"reflect"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/jonboulle/clockwork"
|
||||
"github.com/kylelemons/godebug/diff"
|
||||
"github.com/kylelemons/godebug/pretty"
|
||||
|
||||
phttp "github.com/coreos/go-oidc/http"
|
||||
"github.com/coreos/go-oidc/jose"
|
||||
"github.com/coreos/go-oidc/oauth2"
|
||||
)
|
||||
|
||||
func TestProviderConfigDefaults(t *testing.T) {
|
||||
var cfg ProviderConfig
|
||||
cfg = cfg.Defaults()
|
||||
tests := []struct {
|
||||
got, want []string
|
||||
name string
|
||||
}{
|
||||
{cfg.GrantTypesSupported, DefaultGrantTypesSupported, "grant types"},
|
||||
{cfg.ResponseModesSupported, DefaultResponseModesSupported, "response modes"},
|
||||
{cfg.ClaimTypesSupported, DefaultClaimTypesSupported, "claim types"},
|
||||
{
|
||||
cfg.TokenEndpointAuthMethodsSupported,
|
||||
DefaultTokenEndpointAuthMethodsSupported,
|
||||
"token endpoint auth methods",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
if diff := pretty.Compare(tt.want, tt.got); diff != "" {
|
||||
t.Errorf("%s: did not match %s", tt.name, diff)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestProviderConfigUnmarshal(t *testing.T) {
|
||||
|
||||
// helper for quickly creating uris
|
||||
uri := func(path string) *url.URL {
|
||||
return &url.URL{
|
||||
Scheme: "https",
|
||||
Host: "server.example.com",
|
||||
Path: path,
|
||||
}
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
data string
|
||||
want ProviderConfig
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
data: `{
|
||||
"issuer": "https://server.example.com",
|
||||
"authorization_endpoint": "https://server.example.com/connect/authorize",
|
||||
"token_endpoint": "https://server.example.com/connect/token",
|
||||
"token_endpoint_auth_methods_supported": ["client_secret_basic", "private_key_jwt"],
|
||||
"token_endpoint_auth_signing_alg_values_supported": ["RS256", "ES256"],
|
||||
"userinfo_endpoint": "https://server.example.com/connect/userinfo",
|
||||
"jwks_uri": "https://server.example.com/jwks.json",
|
||||
"registration_endpoint": "https://server.example.com/connect/register",
|
||||
"scopes_supported": [
|
||||
"openid", "profile", "email", "address", "phone", "offline_access"
|
||||
],
|
||||
"response_types_supported": [
|
||||
"code", "code id_token", "id_token", "id_token token"
|
||||
],
|
||||
"acr_values_supported": [
|
||||
"urn:mace:incommon:iap:silver", "urn:mace:incommon:iap:bronze"
|
||||
],
|
||||
"subject_types_supported": ["public", "pairwise"],
|
||||
"userinfo_signing_alg_values_supported": ["RS256", "ES256", "HS256"],
|
||||
"userinfo_encryption_alg_values_supported": ["RSA1_5", "A128KW"],
|
||||
"userinfo_encryption_enc_values_supported": ["A128CBC-HS256", "A128GCM"],
|
||||
"id_token_signing_alg_values_supported": ["RS256", "ES256", "HS256"],
|
||||
"id_token_encryption_alg_values_supported": ["RSA1_5", "A128KW"],
|
||||
"id_token_encryption_enc_values_supported": ["A128CBC-HS256", "A128GCM"],
|
||||
"request_object_signing_alg_values_supported": ["none", "RS256", "ES256"],
|
||||
"display_values_supported": ["page", "popup"],
|
||||
"claim_types_supported": ["normal", "distributed"],
|
||||
"claims_supported": [
|
||||
"sub", "iss", "auth_time", "acr", "name", "given_name",
|
||||
"family_name", "nickname", "profile", "picture", "website",
|
||||
"email", "email_verified", "locale", "zoneinfo",
|
||||
"http://example.info/claims/groups"
|
||||
],
|
||||
"claims_parameter_supported": true,
|
||||
"service_documentation": "https://server.example.com/connect/service_documentation.html",
|
||||
"ui_locales_supported": ["en-US", "en-GB", "en-CA", "fr-FR", "fr-CA"]
|
||||
}
|
||||
`,
|
||||
want: ProviderConfig{
|
||||
Issuer: &url.URL{Scheme: "https", Host: "server.example.com"},
|
||||
AuthEndpoint: uri("/connect/authorize"),
|
||||
TokenEndpoint: uri("/connect/token"),
|
||||
TokenEndpointAuthMethodsSupported: []string{
|
||||
oauth2.AuthMethodClientSecretBasic, oauth2.AuthMethodPrivateKeyJWT,
|
||||
},
|
||||
TokenEndpointAuthSigningAlgValuesSupported: []string{
|
||||
jose.AlgRS256, jose.AlgES256,
|
||||
},
|
||||
UserInfoEndpoint: uri("/connect/userinfo"),
|
||||
KeysEndpoint: uri("/jwks.json"),
|
||||
RegistrationEndpoint: uri("/connect/register"),
|
||||
ScopesSupported: []string{
|
||||
"openid", "profile", "email", "address", "phone", "offline_access",
|
||||
},
|
||||
ResponseTypesSupported: []string{
|
||||
oauth2.ResponseTypeCode, oauth2.ResponseTypeCodeIDToken,
|
||||
oauth2.ResponseTypeIDToken, oauth2.ResponseTypeIDTokenToken,
|
||||
},
|
||||
ACRValuesSupported: []string{
|
||||
"urn:mace:incommon:iap:silver", "urn:mace:incommon:iap:bronze",
|
||||
},
|
||||
SubjectTypesSupported: []string{
|
||||
SubjectTypePublic, SubjectTypePairwise,
|
||||
},
|
||||
UserInfoSigningAlgValues: []string{jose.AlgRS256, jose.AlgES256, jose.AlgHS256},
|
||||
UserInfoEncryptionAlgValues: []string{"RSA1_5", "A128KW"},
|
||||
UserInfoEncryptionEncValues: []string{"A128CBC-HS256", "A128GCM"},
|
||||
IDTokenSigningAlgValues: []string{jose.AlgRS256, jose.AlgES256, jose.AlgHS256},
|
||||
IDTokenEncryptionAlgValues: []string{"RSA1_5", "A128KW"},
|
||||
IDTokenEncryptionEncValues: []string{"A128CBC-HS256", "A128GCM"},
|
||||
ReqObjSigningAlgValues: []string{jose.AlgNone, jose.AlgRS256, jose.AlgES256},
|
||||
DisplayValuesSupported: []string{"page", "popup"},
|
||||
ClaimTypesSupported: []string{"normal", "distributed"},
|
||||
ClaimsSupported: []string{
|
||||
"sub", "iss", "auth_time", "acr", "name", "given_name",
|
||||
"family_name", "nickname", "profile", "picture", "website",
|
||||
"email", "email_verified", "locale", "zoneinfo",
|
||||
"http://example.info/claims/groups",
|
||||
},
|
||||
ClaimsParameterSupported: true,
|
||||
ServiceDocs: uri("/connect/service_documentation.html"),
|
||||
UILocalsSupported: []string{"en-US", "en-GB", "en-CA", "fr-FR", "fr-CA"},
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
// missing a lot of required field
|
||||
data: `{}`,
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
data: `{
|
||||
"issuer": "https://server.example.com",
|
||||
"authorization_endpoint": "https://server.example.com/connect/authorize",
|
||||
"token_endpoint": "https://server.example.com/connect/token",
|
||||
"jwks_uri": "https://server.example.com/jwks.json",
|
||||
"response_types_supported": [
|
||||
"code", "code id_token", "id_token", "id_token token"
|
||||
],
|
||||
"subject_types_supported": ["public", "pairwise"],
|
||||
"id_token_signing_alg_values_supported": ["RS256", "ES256", "HS256"]
|
||||
}
|
||||
`,
|
||||
want: ProviderConfig{
|
||||
Issuer: &url.URL{Scheme: "https", Host: "server.example.com"},
|
||||
AuthEndpoint: uri("/connect/authorize"),
|
||||
TokenEndpoint: uri("/connect/token"),
|
||||
KeysEndpoint: uri("/jwks.json"),
|
||||
ResponseTypesSupported: []string{
|
||||
oauth2.ResponseTypeCode, oauth2.ResponseTypeCodeIDToken,
|
||||
oauth2.ResponseTypeIDToken, oauth2.ResponseTypeIDTokenToken,
|
||||
},
|
||||
SubjectTypesSupported: []string{
|
||||
SubjectTypePublic, SubjectTypePairwise,
|
||||
},
|
||||
IDTokenSigningAlgValues: []string{jose.AlgRS256, jose.AlgES256, jose.AlgHS256},
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
// invalid scheme 'ftp://'
|
||||
data: `{
|
||||
"issuer": "https://server.example.com",
|
||||
"authorization_endpoint": "https://server.example.com/connect/authorize",
|
||||
"token_endpoint": "https://server.example.com/connect/token",
|
||||
"jwks_uri": "ftp://server.example.com/jwks.json",
|
||||
"response_types_supported": [
|
||||
"code", "code id_token", "id_token", "id_token token"
|
||||
],
|
||||
"subject_types_supported": ["public", "pairwise"],
|
||||
"id_token_signing_alg_values_supported": ["RS256", "ES256", "HS256"]
|
||||
}
|
||||
`,
|
||||
wantErr: true,
|
||||
},
|
||||
}
|
||||
for i, tt := range tests {
|
||||
var got ProviderConfig
|
||||
if err := json.Unmarshal([]byte(tt.data), &got); err != nil {
|
||||
if !tt.wantErr {
|
||||
t.Errorf("case %d: failed to unmarshal provider config: %v", i, err)
|
||||
}
|
||||
continue
|
||||
}
|
||||
if tt.wantErr {
|
||||
t.Errorf("case %d: expected error", i)
|
||||
continue
|
||||
}
|
||||
if diff := pretty.Compare(tt.want, got); diff != "" {
|
||||
t.Errorf("case %d: unmarshaled struct did not match expected %s", i, diff)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestProviderConfigMarshal(t *testing.T) {
|
||||
tests := []struct {
|
||||
cfg ProviderConfig
|
||||
want string
|
||||
}{
|
||||
{
|
||||
cfg: ProviderConfig{
|
||||
Issuer: &url.URL{Scheme: "https", Host: "auth.example.com"},
|
||||
AuthEndpoint: &url.URL{
|
||||
Scheme: "https", Host: "auth.example.com", Path: "/auth",
|
||||
},
|
||||
TokenEndpoint: &url.URL{
|
||||
Scheme: "https", Host: "auth.example.com", Path: "/token",
|
||||
},
|
||||
UserInfoEndpoint: &url.URL{
|
||||
Scheme: "https", Host: "auth.example.com", Path: "/userinfo",
|
||||
},
|
||||
KeysEndpoint: &url.URL{
|
||||
Scheme: "https", Host: "auth.example.com", Path: "/jwk",
|
||||
},
|
||||
ResponseTypesSupported: []string{oauth2.ResponseTypeCode},
|
||||
SubjectTypesSupported: []string{SubjectTypePublic},
|
||||
IDTokenSigningAlgValues: []string{jose.AlgRS256},
|
||||
},
|
||||
// spacing must match json.MarshalIndent(cfg, "", "\t")
|
||||
want: `{
|
||||
"issuer": "https://auth.example.com",
|
||||
"authorization_endpoint": "https://auth.example.com/auth",
|
||||
"token_endpoint": "https://auth.example.com/token",
|
||||
"userinfo_endpoint": "https://auth.example.com/userinfo",
|
||||
"jwks_uri": "https://auth.example.com/jwk",
|
||||
"response_types_supported": [
|
||||
"code"
|
||||
],
|
||||
"subject_types_supported": [
|
||||
"public"
|
||||
],
|
||||
"id_token_signing_alg_values_supported": [
|
||||
"RS256"
|
||||
]
|
||||
}`,
|
||||
},
|
||||
{
|
||||
cfg: ProviderConfig{
|
||||
Issuer: &url.URL{Scheme: "https", Host: "auth.example.com"},
|
||||
AuthEndpoint: &url.URL{
|
||||
Scheme: "https", Host: "auth.example.com", Path: "/auth",
|
||||
},
|
||||
TokenEndpoint: &url.URL{
|
||||
Scheme: "https", Host: "auth.example.com", Path: "/token",
|
||||
},
|
||||
UserInfoEndpoint: &url.URL{
|
||||
Scheme: "https", Host: "auth.example.com", Path: "/userinfo",
|
||||
},
|
||||
KeysEndpoint: &url.URL{
|
||||
Scheme: "https", Host: "auth.example.com", Path: "/jwk",
|
||||
},
|
||||
RegistrationEndpoint: &url.URL{
|
||||
Scheme: "https", Host: "auth.example.com", Path: "/register",
|
||||
},
|
||||
ScopesSupported: DefaultScope,
|
||||
ResponseTypesSupported: []string{oauth2.ResponseTypeCode},
|
||||
ResponseModesSupported: DefaultResponseModesSupported,
|
||||
GrantTypesSupported: []string{oauth2.GrantTypeAuthCode},
|
||||
SubjectTypesSupported: []string{SubjectTypePublic},
|
||||
IDTokenSigningAlgValues: []string{jose.AlgRS256},
|
||||
ServiceDocs: &url.URL{Scheme: "https", Host: "example.com", Path: "/docs"},
|
||||
},
|
||||
// spacing must match json.MarshalIndent(cfg, "", "\t")
|
||||
want: `{
|
||||
"issuer": "https://auth.example.com",
|
||||
"authorization_endpoint": "https://auth.example.com/auth",
|
||||
"token_endpoint": "https://auth.example.com/token",
|
||||
"userinfo_endpoint": "https://auth.example.com/userinfo",
|
||||
"jwks_uri": "https://auth.example.com/jwk",
|
||||
"registration_endpoint": "https://auth.example.com/register",
|
||||
"scopes_supported": [
|
||||
"openid",
|
||||
"email",
|
||||
"profile"
|
||||
],
|
||||
"response_types_supported": [
|
||||
"code"
|
||||
],
|
||||
"response_modes_supported": [
|
||||
"query",
|
||||
"fragment"
|
||||
],
|
||||
"grant_types_supported": [
|
||||
"authorization_code"
|
||||
],
|
||||
"subject_types_supported": [
|
||||
"public"
|
||||
],
|
||||
"id_token_signing_alg_values_supported": [
|
||||
"RS256"
|
||||
],
|
||||
"service_documentation": "https://example.com/docs"
|
||||
}`,
|
||||
},
|
||||
}
|
||||
|
||||
for i, tt := range tests {
|
||||
got, err := json.MarshalIndent(&tt.cfg, "", "\t")
|
||||
if err != nil {
|
||||
t.Errorf("case %d: failed to marshal config: %v", i, err)
|
||||
continue
|
||||
}
|
||||
if d := diff.Diff(string(got), string(tt.want)); d != "" {
|
||||
t.Errorf("case %d: expected did not match result: %s", i, d)
|
||||
}
|
||||
|
||||
var cfg ProviderConfig
|
||||
if err := json.Unmarshal(got, &cfg); err != nil {
|
||||
t.Errorf("case %d: could not unmarshal marshal response: %v", i, err)
|
||||
continue
|
||||
}
|
||||
|
||||
if d := pretty.Compare(tt.cfg, cfg); d != "" {
|
||||
t.Errorf("case %d: config did not survive JSON marshaling round trip: %s", i, d)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestProviderConfigSupports(t *testing.T) {
|
||||
tests := []struct {
|
||||
provider ProviderConfig
|
||||
client ClientMetadata
|
||||
fillRequiredProviderFields bool
|
||||
ok bool
|
||||
}{
|
||||
{
|
||||
provider: ProviderConfig{},
|
||||
client: ClientMetadata{
|
||||
RedirectURIs: []url.URL{
|
||||
{Scheme: "https", Host: "example.com", Path: "/callback"},
|
||||
},
|
||||
},
|
||||
fillRequiredProviderFields: true,
|
||||
ok: true,
|
||||
},
|
||||
{
|
||||
// invalid provider config
|
||||
provider: ProviderConfig{},
|
||||
client: ClientMetadata{
|
||||
RedirectURIs: []url.URL{
|
||||
{Scheme: "https", Host: "example.com", Path: "/callback"},
|
||||
},
|
||||
},
|
||||
fillRequiredProviderFields: false,
|
||||
ok: false,
|
||||
},
|
||||
{
|
||||
// invalid client config
|
||||
provider: ProviderConfig{},
|
||||
client: ClientMetadata{},
|
||||
fillRequiredProviderFields: true,
|
||||
ok: false,
|
||||
},
|
||||
}
|
||||
|
||||
for i, tt := range tests {
|
||||
if tt.fillRequiredProviderFields {
|
||||
tt.provider = fillRequiredProviderFields(tt.provider)
|
||||
}
|
||||
|
||||
err := tt.provider.Supports(tt.client)
|
||||
if err == nil && !tt.ok {
|
||||
t.Errorf("case %d: expected non-nil error", i)
|
||||
}
|
||||
if err != nil && tt.ok {
|
||||
t.Errorf("case %d: supports failed: %v", i, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func newValidProviderConfig() ProviderConfig {
|
||||
var cfg ProviderConfig
|
||||
return fillRequiredProviderFields(cfg)
|
||||
}
|
||||
|
||||
// fill a provider config with enough information to be valid
|
||||
func fillRequiredProviderFields(cfg ProviderConfig) ProviderConfig {
|
||||
if cfg.Issuer == nil {
|
||||
cfg.Issuer = &url.URL{Scheme: "https", Host: "auth.example.com"}
|
||||
}
|
||||
urlPath := func(path string) *url.URL {
|
||||
var u url.URL
|
||||
u = *cfg.Issuer
|
||||
u.Path = path
|
||||
return &u
|
||||
}
|
||||
cfg.AuthEndpoint = urlPath("/auth")
|
||||
cfg.TokenEndpoint = urlPath("/token")
|
||||
cfg.UserInfoEndpoint = urlPath("/userinfo")
|
||||
cfg.KeysEndpoint = urlPath("/jwk")
|
||||
cfg.ResponseTypesSupported = []string{oauth2.ResponseTypeCode}
|
||||
cfg.SubjectTypesSupported = []string{SubjectTypePublic}
|
||||
cfg.IDTokenSigningAlgValues = []string{jose.AlgRS256}
|
||||
return cfg
|
||||
}
|
||||
|
||||
type fakeProviderConfigGetterSetter struct {
|
||||
cfg *ProviderConfig
|
||||
getCount int
|
||||
setCount int
|
||||
}
|
||||
|
||||
func (g *fakeProviderConfigGetterSetter) Get() (ProviderConfig, error) {
|
||||
g.getCount++
|
||||
return *g.cfg, nil
|
||||
}
|
||||
|
||||
func (g *fakeProviderConfigGetterSetter) Set(cfg ProviderConfig) error {
|
||||
g.cfg = &cfg
|
||||
g.setCount++
|
||||
return nil
|
||||
}
|
||||
|
||||
type fakeProviderConfigHandler struct {
|
||||
cfg ProviderConfig
|
||||
maxAge time.Duration
|
||||
}
|
||||
|
||||
func (s *fakeProviderConfigHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
b, _ := json.Marshal(&s.cfg)
|
||||
if s.maxAge.Seconds() >= 0 {
|
||||
w.Header().Set("Cache-Control", fmt.Sprintf("public, max-age=%d", int(s.maxAge.Seconds())))
|
||||
}
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.Write(b)
|
||||
}
|
||||
|
||||
func TestProviderConfigRequiredFields(t *testing.T) {
|
||||
// Ensure provider metadata responses have all the required fields.
|
||||
// taken from https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderMetadata
|
||||
requiredFields := []string{
|
||||
"issuer",
|
||||
"authorization_endpoint",
|
||||
"token_endpoint", // "This is REQUIRED unless only the Implicit Flow is used."
|
||||
"jwks_uri",
|
||||
"response_types_supported",
|
||||
"subject_types_supported",
|
||||
"id_token_signing_alg_values_supported",
|
||||
}
|
||||
|
||||
svr := &fakeProviderConfigHandler{
|
||||
cfg: ProviderConfig{
|
||||
Issuer: &url.URL{Scheme: "http", Host: "example.com"},
|
||||
ExpiresAt: time.Now().Add(time.Minute),
|
||||
},
|
||||
maxAge: time.Minute,
|
||||
}
|
||||
svr.cfg = fillRequiredProviderFields(svr.cfg)
|
||||
s := httptest.NewServer(svr)
|
||||
defer s.Close()
|
||||
|
||||
resp, err := http.Get(s.URL + "/")
|
||||
if err != nil {
|
||||
t.Errorf("get: %v", err)
|
||||
return
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
var data map[string]interface{}
|
||||
if err := json.NewDecoder(resp.Body).Decode(&data); err != nil {
|
||||
t.Errorf("decode: %v", err)
|
||||
return
|
||||
}
|
||||
for _, field := range requiredFields {
|
||||
if _, ok := data[field]; !ok {
|
||||
t.Errorf("provider metadata does not have required field '%s'", field)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestHTTPProviderConfigGetter(t *testing.T) {
|
||||
svr := &fakeProviderConfigHandler{}
|
||||
hc := &phttp.HandlerClient{Handler: svr}
|
||||
fc := clockwork.NewFakeClock()
|
||||
now := fc.Now().UTC()
|
||||
|
||||
tests := []struct {
|
||||
dsc string
|
||||
age time.Duration
|
||||
cfg ProviderConfig
|
||||
ok bool
|
||||
}{
|
||||
// everything is good
|
||||
{
|
||||
dsc: "https://example.com",
|
||||
age: time.Minute,
|
||||
cfg: ProviderConfig{
|
||||
Issuer: &url.URL{Scheme: "https", Host: "example.com"},
|
||||
ExpiresAt: now.Add(time.Minute),
|
||||
},
|
||||
ok: true,
|
||||
},
|
||||
// iss and disco url differ by scheme only (how google works)
|
||||
{
|
||||
dsc: "https://example.com",
|
||||
age: time.Minute,
|
||||
cfg: ProviderConfig{
|
||||
Issuer: &url.URL{Scheme: "https", Host: "example.com"},
|
||||
ExpiresAt: now.Add(time.Minute),
|
||||
},
|
||||
ok: true,
|
||||
},
|
||||
// issuer and discovery URL mismatch
|
||||
{
|
||||
dsc: "https://foo.com",
|
||||
age: time.Minute,
|
||||
cfg: ProviderConfig{
|
||||
Issuer: &url.URL{Scheme: "https", Host: "example.com"},
|
||||
ExpiresAt: now.Add(time.Minute),
|
||||
},
|
||||
ok: false,
|
||||
},
|
||||
// missing cache header results in zero ExpiresAt
|
||||
{
|
||||
dsc: "https://example.com",
|
||||
age: -1,
|
||||
cfg: ProviderConfig{
|
||||
Issuer: &url.URL{Scheme: "https", Host: "example.com"},
|
||||
},
|
||||
ok: true,
|
||||
},
|
||||
}
|
||||
|
||||
for i, tt := range tests {
|
||||
tt.cfg = fillRequiredProviderFields(tt.cfg)
|
||||
svr.cfg = tt.cfg
|
||||
svr.maxAge = tt.age
|
||||
getter := NewHTTPProviderConfigGetter(hc, tt.dsc)
|
||||
getter.clock = fc
|
||||
|
||||
got, err := getter.Get()
|
||||
if err != nil {
|
||||
if tt.ok {
|
||||
t.Errorf("test %d: unexpected error: %v", i, err)
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
if !tt.ok {
|
||||
t.Errorf("test %d: expected error", i)
|
||||
continue
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(tt.cfg, got) {
|
||||
t.Errorf("test %d: want: %#v, got: %#v", i, tt.cfg, got)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestProviderConfigSyncerRun(t *testing.T) {
|
||||
c1 := &ProviderConfig{
|
||||
Issuer: &url.URL{Scheme: "https", Host: "example.com"},
|
||||
}
|
||||
c2 := &ProviderConfig{
|
||||
Issuer: &url.URL{Scheme: "https", Host: "example.com"},
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
first *ProviderConfig
|
||||
advance time.Duration
|
||||
second *ProviderConfig
|
||||
firstExp time.Duration
|
||||
secondExp time.Duration
|
||||
count int
|
||||
}{
|
||||
// exp is 10m, should have same config after 1s
|
||||
{
|
||||
first: c1,
|
||||
firstExp: time.Duration(10 * time.Minute),
|
||||
advance: time.Minute,
|
||||
second: c1,
|
||||
secondExp: time.Duration(10 * time.Minute),
|
||||
count: 1,
|
||||
},
|
||||
// exp is 10m, should have new config after 10/2 = 5m
|
||||
{
|
||||
first: c1,
|
||||
firstExp: time.Duration(10 * time.Minute),
|
||||
advance: time.Duration(5 * time.Minute),
|
||||
second: c2,
|
||||
secondExp: time.Duration(10 * time.Minute),
|
||||
count: 2,
|
||||
},
|
||||
// exp is 20m, should have new config after 20/2 = 10m
|
||||
{
|
||||
first: c1,
|
||||
firstExp: time.Duration(20 * time.Minute),
|
||||
advance: time.Duration(10 * time.Minute),
|
||||
second: c2,
|
||||
secondExp: time.Duration(30 * time.Minute),
|
||||
count: 2,
|
||||
},
|
||||
}
|
||||
|
||||
assertCfg := func(i int, to *fakeProviderConfigGetterSetter, want ProviderConfig) {
|
||||
got, err := to.Get()
|
||||
if err != nil {
|
||||
t.Fatalf("test %d: unable to get config: %v", i, err)
|
||||
}
|
||||
if !reflect.DeepEqual(want, got) {
|
||||
t.Fatalf("test %d: incorrect state:\nwant=%#v\ngot=%#v", i, want, got)
|
||||
}
|
||||
}
|
||||
|
||||
for i, tt := range tests {
|
||||
from := &fakeProviderConfigGetterSetter{}
|
||||
to := &fakeProviderConfigGetterSetter{}
|
||||
|
||||
fc := clockwork.NewFakeClock()
|
||||
now := fc.Now().UTC()
|
||||
syncer := NewProviderConfigSyncer(from, to)
|
||||
syncer.clock = fc
|
||||
|
||||
tt.first.ExpiresAt = now.Add(tt.firstExp)
|
||||
tt.second.ExpiresAt = now.Add(tt.secondExp)
|
||||
if err := from.Set(*tt.first); err != nil {
|
||||
t.Fatalf("test %d: unexpected error: %v", i, err)
|
||||
}
|
||||
|
||||
stop := syncer.Run()
|
||||
defer close(stop)
|
||||
fc.BlockUntil(1)
|
||||
|
||||
// first sync
|
||||
assertCfg(i, to, *tt.first)
|
||||
|
||||
if err := from.Set(*tt.second); err != nil {
|
||||
t.Fatalf("test %d: unexpected error: %v", i, err)
|
||||
}
|
||||
|
||||
fc.Advance(tt.advance)
|
||||
fc.BlockUntil(1)
|
||||
|
||||
// second sync
|
||||
assertCfg(i, to, *tt.second)
|
||||
|
||||
if tt.count != from.getCount {
|
||||
t.Fatalf("test %d: want: %v, got: %v", i, tt.count, from.getCount)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type staticProviderConfigGetter struct {
|
||||
cfg ProviderConfig
|
||||
err error
|
||||
}
|
||||
|
||||
func (g *staticProviderConfigGetter) Get() (ProviderConfig, error) {
|
||||
return g.cfg, g.err
|
||||
}
|
||||
|
||||
type staticProviderConfigSetter struct {
|
||||
cfg *ProviderConfig
|
||||
err error
|
||||
}
|
||||
|
||||
func (s *staticProviderConfigSetter) Set(cfg ProviderConfig) error {
|
||||
s.cfg = &cfg
|
||||
return s.err
|
||||
}
|
||||
|
||||
func TestProviderConfigSyncerSyncFailure(t *testing.T) {
|
||||
fc := clockwork.NewFakeClock()
|
||||
|
||||
tests := []struct {
|
||||
from *staticProviderConfigGetter
|
||||
to *staticProviderConfigSetter
|
||||
|
||||
// want indicates what ProviderConfig should be passed to Set.
|
||||
// If nil, the Set should not be called.
|
||||
want *ProviderConfig
|
||||
}{
|
||||
// generic Get failure
|
||||
{
|
||||
from: &staticProviderConfigGetter{err: errors.New("fail")},
|
||||
to: &staticProviderConfigSetter{},
|
||||
want: nil,
|
||||
},
|
||||
// generic Set failure
|
||||
{
|
||||
from: &staticProviderConfigGetter{cfg: ProviderConfig{ExpiresAt: fc.Now().Add(time.Minute)}},
|
||||
to: &staticProviderConfigSetter{err: errors.New("fail")},
|
||||
want: &ProviderConfig{ExpiresAt: fc.Now().Add(time.Minute)},
|
||||
},
|
||||
}
|
||||
|
||||
for i, tt := range tests {
|
||||
pcs := &ProviderConfigSyncer{
|
||||
from: tt.from,
|
||||
to: tt.to,
|
||||
clock: fc,
|
||||
}
|
||||
_, err := pcs.sync()
|
||||
if err == nil {
|
||||
t.Errorf("case %d: expected non-nil error", i)
|
||||
}
|
||||
if !reflect.DeepEqual(tt.want, tt.to.cfg) {
|
||||
t.Errorf("case %d: Set mismatch: want=%#v got=%#v", i, tt.want, tt.to.cfg)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestNextSyncAfter(t *testing.T) {
|
||||
fc := clockwork.NewFakeClock()
|
||||
|
||||
tests := []struct {
|
||||
exp time.Time
|
||||
want time.Duration
|
||||
}{
|
||||
{
|
||||
exp: fc.Now().Add(time.Hour),
|
||||
want: 30 * time.Minute,
|
||||
},
|
||||
// override large values with the maximum
|
||||
{
|
||||
exp: fc.Now().Add(168 * time.Hour), // one week
|
||||
want: 24 * time.Hour,
|
||||
},
|
||||
// override "now" values with the minimum
|
||||
{
|
||||
exp: fc.Now(),
|
||||
want: time.Minute,
|
||||
},
|
||||
// override negative values with the minimum
|
||||
{
|
||||
exp: fc.Now().Add(-1 * time.Minute),
|
||||
want: time.Minute,
|
||||
},
|
||||
// zero-value Time results in maximum sync interval
|
||||
{
|
||||
exp: time.Time{},
|
||||
want: 24 * time.Hour,
|
||||
},
|
||||
}
|
||||
|
||||
for i, tt := range tests {
|
||||
got := nextSyncAfter(tt.exp, fc)
|
||||
if tt.want != got {
|
||||
t.Errorf("case %d: want=%v got=%v", i, tt.want, got)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestProviderConfigEmpty(t *testing.T) {
|
||||
cfg := ProviderConfig{}
|
||||
if !cfg.Empty() {
|
||||
t.Fatalf("Empty provider config reports non-empty")
|
||||
}
|
||||
cfg = ProviderConfig{
|
||||
Issuer: &url.URL{Scheme: "https", Host: "example.com"},
|
||||
}
|
||||
if cfg.Empty() {
|
||||
t.Fatalf("Non-empty provider config reports empty")
|
||||
}
|
||||
}
|
||||
|
||||
func TestPCSStepAfter(t *testing.T) {
|
||||
pass := func() (time.Duration, error) { return 7 * time.Second, nil }
|
||||
fail := func() (time.Duration, error) { return 0, errors.New("fail") }
|
||||
|
||||
tests := []struct {
|
||||
stepper pcsStepper
|
||||
stepFunc pcsStepFunc
|
||||
want pcsStepper
|
||||
}{
|
||||
// good step results in retry at TTL
|
||||
{
|
||||
stepper: &pcsStepNext{},
|
||||
stepFunc: pass,
|
||||
want: &pcsStepNext{aft: 7 * time.Second},
|
||||
},
|
||||
|
||||
// good step after failed step results results in retry at TTL
|
||||
{
|
||||
stepper: &pcsStepRetry{aft: 2 * time.Second},
|
||||
stepFunc: pass,
|
||||
want: &pcsStepNext{aft: 7 * time.Second},
|
||||
},
|
||||
|
||||
// failed step results in a retry in 1s
|
||||
{
|
||||
stepper: &pcsStepNext{},
|
||||
stepFunc: fail,
|
||||
want: &pcsStepRetry{aft: time.Second},
|
||||
},
|
||||
|
||||
// failed retry backs off by a factor of 2
|
||||
{
|
||||
stepper: &pcsStepRetry{aft: time.Second},
|
||||
stepFunc: fail,
|
||||
want: &pcsStepRetry{aft: 2 * time.Second},
|
||||
},
|
||||
|
||||
// failed retry backs off by a factor of 2, up to 1m
|
||||
{
|
||||
stepper: &pcsStepRetry{aft: 32 * time.Second},
|
||||
stepFunc: fail,
|
||||
want: &pcsStepRetry{aft: 60 * time.Second},
|
||||
},
|
||||
}
|
||||
|
||||
for i, tt := range tests {
|
||||
got := tt.stepper.step(tt.stepFunc)
|
||||
if !reflect.DeepEqual(tt.want, got) {
|
||||
t.Errorf("case %d: want=%#v got=%#v", i, tt.want, got)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestProviderConfigSupportsGrantType(t *testing.T) {
|
||||
tests := []struct {
|
||||
types []string
|
||||
typ string
|
||||
want bool
|
||||
}{
|
||||
// explicitly supported
|
||||
{
|
||||
types: []string{"foo_type"},
|
||||
typ: "foo_type",
|
||||
want: true,
|
||||
},
|
||||
|
||||
// explicitly unsupported
|
||||
{
|
||||
types: []string{"bar_type"},
|
||||
typ: "foo_type",
|
||||
want: false,
|
||||
},
|
||||
|
||||
// default type explicitly unsupported
|
||||
{
|
||||
types: []string{oauth2.GrantTypeImplicit},
|
||||
typ: oauth2.GrantTypeAuthCode,
|
||||
want: false,
|
||||
},
|
||||
|
||||
// type not found in default set
|
||||
{
|
||||
types: []string{},
|
||||
typ: "foo_type",
|
||||
want: false,
|
||||
},
|
||||
|
||||
// type found in default set
|
||||
{
|
||||
types: []string{},
|
||||
typ: oauth2.GrantTypeAuthCode,
|
||||
want: true,
|
||||
},
|
||||
}
|
||||
|
||||
for i, tt := range tests {
|
||||
cfg := ProviderConfig{
|
||||
GrantTypesSupported: tt.types,
|
||||
}
|
||||
got := cfg.SupportsGrantType(tt.typ)
|
||||
if tt.want != got {
|
||||
t.Errorf("case %d: assert %v supports %v: want=%t got=%t", i, tt.types, tt.typ, tt.want, got)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestWaitForProviderConfigImmediateSuccess(t *testing.T) {
|
||||
cfg := newValidProviderConfig()
|
||||
b, err := json.Marshal(&cfg)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed marshaling provider config")
|
||||
}
|
||||
|
||||
resp := http.Response{Body: ioutil.NopCloser(bytes.NewBuffer(b))}
|
||||
hc := &phttp.RequestRecorder{Response: &resp}
|
||||
fc := clockwork.NewFakeClock()
|
||||
|
||||
reschan := make(chan ProviderConfig)
|
||||
go func() {
|
||||
reschan <- waitForProviderConfig(hc, cfg.Issuer.String(), fc)
|
||||
}()
|
||||
|
||||
var got ProviderConfig
|
||||
select {
|
||||
case got = <-reschan:
|
||||
case <-time.After(time.Second):
|
||||
t.Fatalf("Did not receive result within 1s")
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(cfg, got) {
|
||||
t.Fatalf("Received incorrect provider config: want=%#v got=%#v", cfg, got)
|
||||
}
|
||||
}
|
167
vendor/github.com/coreos/go-oidc/oidc/transport_test.go
generated
vendored
Normal file
167
vendor/github.com/coreos/go-oidc/oidc/transport_test.go
generated
vendored
Normal file
|
@ -0,0 +1,167 @@
|
|||
package oidc
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net/http"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
phttp "github.com/coreos/go-oidc/http"
|
||||
"github.com/coreos/go-oidc/jose"
|
||||
)
|
||||
|
||||
type staticTokenRefresher struct {
|
||||
verify func(jose.JWT) error
|
||||
refresh func() (jose.JWT, error)
|
||||
}
|
||||
|
||||
func (s *staticTokenRefresher) Verify(jwt jose.JWT) error {
|
||||
return s.verify(jwt)
|
||||
}
|
||||
|
||||
func (s *staticTokenRefresher) Refresh() (jose.JWT, error) {
|
||||
return s.refresh()
|
||||
}
|
||||
|
||||
func TestAuthenticatedTransportVerifiedJWT(t *testing.T) {
|
||||
tests := []struct {
|
||||
refresher TokenRefresher
|
||||
startJWT jose.JWT
|
||||
wantJWT jose.JWT
|
||||
wantError error
|
||||
}{
|
||||
// verification succeeds, so refresh is not called
|
||||
{
|
||||
refresher: &staticTokenRefresher{
|
||||
verify: func(jose.JWT) error { return nil },
|
||||
refresh: func() (jose.JWT, error) { return jose.JWT{RawPayload: "2"}, nil },
|
||||
},
|
||||
startJWT: jose.JWT{RawPayload: "1"},
|
||||
wantJWT: jose.JWT{RawPayload: "1"},
|
||||
},
|
||||
|
||||
// verification fails, refresh succeeds so cached JWT changes
|
||||
{
|
||||
refresher: &staticTokenRefresher{
|
||||
verify: func(jose.JWT) error { return errors.New("fail!") },
|
||||
refresh: func() (jose.JWT, error) { return jose.JWT{RawPayload: "2"}, nil },
|
||||
},
|
||||
startJWT: jose.JWT{RawPayload: "1"},
|
||||
wantJWT: jose.JWT{RawPayload: "2"},
|
||||
},
|
||||
|
||||
// verification succeeds, so failing refresh isn't attempted
|
||||
{
|
||||
refresher: &staticTokenRefresher{
|
||||
verify: func(jose.JWT) error { return nil },
|
||||
refresh: func() (jose.JWT, error) { return jose.JWT{}, errors.New("fail!") },
|
||||
},
|
||||
startJWT: jose.JWT{RawPayload: "1"},
|
||||
wantJWT: jose.JWT{RawPayload: "1"},
|
||||
},
|
||||
|
||||
// verification fails, but refresh fails, too
|
||||
{
|
||||
refresher: &staticTokenRefresher{
|
||||
verify: func(jose.JWT) error { return errors.New("fail!") },
|
||||
refresh: func() (jose.JWT, error) { return jose.JWT{}, errors.New("fail!") },
|
||||
},
|
||||
startJWT: jose.JWT{RawPayload: "1"},
|
||||
wantJWT: jose.JWT{},
|
||||
wantError: errors.New("unable to acquire valid JWT: fail!"),
|
||||
},
|
||||
}
|
||||
|
||||
for i, tt := range tests {
|
||||
at := &AuthenticatedTransport{
|
||||
TokenRefresher: tt.refresher,
|
||||
jwt: tt.startJWT,
|
||||
}
|
||||
|
||||
gotJWT, err := at.verifiedJWT()
|
||||
if !reflect.DeepEqual(tt.wantError, err) {
|
||||
t.Errorf("#%d: unexpected error: want=%#v got=%#v", i, tt.wantError, err)
|
||||
}
|
||||
if !reflect.DeepEqual(tt.wantJWT, gotJWT) {
|
||||
t.Errorf("#%d: incorrect JWT returned from verifiedJWT: want=%#v got=%#v", i, tt.wantJWT, gotJWT)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestAuthenticatedTransportJWTCaching(t *testing.T) {
|
||||
at := &AuthenticatedTransport{
|
||||
TokenRefresher: &staticTokenRefresher{
|
||||
verify: func(jose.JWT) error { return errors.New("fail!") },
|
||||
refresh: func() (jose.JWT, error) { return jose.JWT{RawPayload: "2"}, nil },
|
||||
},
|
||||
jwt: jose.JWT{RawPayload: "1"},
|
||||
}
|
||||
|
||||
wantJWT := jose.JWT{RawPayload: "2"}
|
||||
gotJWT, err := at.verifiedJWT()
|
||||
if err != nil {
|
||||
t.Fatalf("got non-nil error: %#v", err)
|
||||
}
|
||||
if !reflect.DeepEqual(wantJWT, gotJWT) {
|
||||
t.Fatalf("incorrect JWT returned from verifiedJWT: want=%#v got=%#v", wantJWT, gotJWT)
|
||||
}
|
||||
|
||||
at.TokenRefresher = &staticTokenRefresher{
|
||||
verify: func(jose.JWT) error { return nil },
|
||||
refresh: func() (jose.JWT, error) { return jose.JWT{RawPayload: "3"}, nil },
|
||||
}
|
||||
|
||||
// the previous JWT should still be cached on the AuthenticatedTransport since
|
||||
// it is still valid, even though there's a new token ready to refresh
|
||||
gotJWT, err = at.verifiedJWT()
|
||||
if err != nil {
|
||||
t.Fatalf("got non-nil error: %#v", err)
|
||||
}
|
||||
if !reflect.DeepEqual(wantJWT, gotJWT) {
|
||||
t.Fatalf("incorrect JWT returned from verifiedJWT: want=%#v got=%#v", wantJWT, gotJWT)
|
||||
}
|
||||
}
|
||||
|
||||
func TestAuthenticatedTransportRoundTrip(t *testing.T) {
|
||||
rr := &phttp.RequestRecorder{Response: &http.Response{StatusCode: http.StatusOK}}
|
||||
at := &AuthenticatedTransport{
|
||||
TokenRefresher: &staticTokenRefresher{
|
||||
verify: func(jose.JWT) error { return nil },
|
||||
},
|
||||
RoundTripper: rr,
|
||||
jwt: jose.JWT{RawPayload: "1"},
|
||||
}
|
||||
|
||||
req := http.Request{}
|
||||
_, err := at.RoundTrip(&req)
|
||||
if err != nil {
|
||||
t.Errorf("unexpected error: %v", err)
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(req, http.Request{}) {
|
||||
t.Errorf("http.Request object was modified")
|
||||
}
|
||||
|
||||
want := []string{"Bearer .1."}
|
||||
got := rr.Request.Header["Authorization"]
|
||||
if !reflect.DeepEqual(want, got) {
|
||||
t.Errorf("incorrect Authorization header: want=%#v got=%#v", want, got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestAuthenticatedTransportRoundTripRefreshFail(t *testing.T) {
|
||||
rr := &phttp.RequestRecorder{Response: &http.Response{StatusCode: http.StatusOK}}
|
||||
at := &AuthenticatedTransport{
|
||||
TokenRefresher: &staticTokenRefresher{
|
||||
verify: func(jose.JWT) error { return errors.New("fail!") },
|
||||
refresh: func() (jose.JWT, error) { return jose.JWT{}, errors.New("fail!") },
|
||||
},
|
||||
RoundTripper: rr,
|
||||
jwt: jose.JWT{RawPayload: "1"},
|
||||
}
|
||||
|
||||
_, err := at.RoundTrip(&http.Request{})
|
||||
if err == nil {
|
||||
t.Errorf("expected non-nil error")
|
||||
}
|
||||
}
|
110
vendor/github.com/coreos/go-oidc/oidc/util_test.go
generated
vendored
Normal file
110
vendor/github.com/coreos/go-oidc/oidc/util_test.go
generated
vendored
Normal file
|
@ -0,0 +1,110 @@
|
|||
package oidc
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"reflect"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/coreos/go-oidc/jose"
|
||||
)
|
||||
|
||||
func TestCookieTokenExtractorInvalid(t *testing.T) {
|
||||
ckName := "tokenCookie"
|
||||
tests := []*http.Cookie{
|
||||
&http.Cookie{},
|
||||
&http.Cookie{Name: ckName},
|
||||
&http.Cookie{Name: ckName, Value: ""},
|
||||
}
|
||||
|
||||
for i, tt := range tests {
|
||||
r, _ := http.NewRequest("", "", nil)
|
||||
r.AddCookie(tt)
|
||||
_, err := CookieTokenExtractor(ckName)(r)
|
||||
if err == nil {
|
||||
t.Errorf("case %d: want: error for invalid cookie token, got: no error.", i)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestCookieTokenExtractorValid(t *testing.T) {
|
||||
validToken := "eyJ0eXAiOiJKV1QiLA0KICJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJqb2UiLA0KICJleHAiOjEzMDA4MTkzODAsDQogImh0dHA6Ly9leGFtcGxlLmNvbS9pc19yb290Ijp0cnVlfQ.dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk"
|
||||
ckName := "tokenCookie"
|
||||
tests := []*http.Cookie{
|
||||
&http.Cookie{Name: ckName, Value: "some non-empty value"},
|
||||
&http.Cookie{Name: ckName, Value: validToken},
|
||||
}
|
||||
|
||||
for i, tt := range tests {
|
||||
r, _ := http.NewRequest("", "", nil)
|
||||
r.AddCookie(tt)
|
||||
_, err := CookieTokenExtractor(ckName)(r)
|
||||
if err != nil {
|
||||
t.Errorf("case %d: want: valid cookie with no error, got: %v", i, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestExtractBearerTokenInvalid(t *testing.T) {
|
||||
tests := []string{"", "x", "Bearer", "xxxxxxx", "Bearer "}
|
||||
|
||||
for i, tt := range tests {
|
||||
r, _ := http.NewRequest("", "", nil)
|
||||
r.Header.Add("Authorization", tt)
|
||||
_, err := ExtractBearerToken(r)
|
||||
if err == nil {
|
||||
t.Errorf("case %d: want: invalid Authorization header, got: valid Authorization header.", i)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestExtractBearerTokenValid(t *testing.T) {
|
||||
validToken := "eyJ0eXAiOiJKV1QiLA0KICJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJqb2UiLA0KICJleHAiOjEzMDA4MTkzODAsDQogImh0dHA6Ly9leGFtcGxlLmNvbS9pc19yb290Ijp0cnVlfQ.dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk"
|
||||
tests := []string{
|
||||
fmt.Sprintf("Bearer %s", validToken),
|
||||
}
|
||||
|
||||
for i, tt := range tests {
|
||||
r, _ := http.NewRequest("", "", nil)
|
||||
r.Header.Add("Authorization", tt)
|
||||
_, err := ExtractBearerToken(r)
|
||||
if err != nil {
|
||||
t.Errorf("case %d: want: valid Authorization header, got: invalid Authorization header: %v.", i, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestNewClaims(t *testing.T) {
|
||||
issAt := time.Date(2, time.January, 1, 0, 0, 0, 0, time.UTC)
|
||||
expAt := time.Date(2, time.January, 1, 1, 0, 0, 0, time.UTC)
|
||||
|
||||
want := jose.Claims{
|
||||
"iss": "https://example.com",
|
||||
"sub": "user-123",
|
||||
"aud": "client-abc",
|
||||
"iat": float64(issAt.Unix()),
|
||||
"exp": float64(expAt.Unix()),
|
||||
}
|
||||
|
||||
got := NewClaims("https://example.com", "user-123", "client-abc", issAt, expAt)
|
||||
|
||||
if !reflect.DeepEqual(want, got) {
|
||||
t.Fatalf("want=%#v got=%#v", want, got)
|
||||
}
|
||||
|
||||
want2 := jose.Claims{
|
||||
"iss": "https://example.com",
|
||||
"sub": "user-123",
|
||||
"aud": []string{"client-abc", "client-def"},
|
||||
"iat": float64(issAt.Unix()),
|
||||
"exp": float64(expAt.Unix()),
|
||||
}
|
||||
|
||||
got2 := NewClaims("https://example.com", "user-123", []string{"client-abc", "client-def"}, issAt, expAt)
|
||||
|
||||
if !reflect.DeepEqual(want2, got2) {
|
||||
t.Fatalf("want=%#v got=%#v", want2, got2)
|
||||
}
|
||||
|
||||
}
|
379
vendor/github.com/coreos/go-oidc/oidc/verification_test.go
generated
vendored
Normal file
379
vendor/github.com/coreos/go-oidc/oidc/verification_test.go
generated
vendored
Normal file
|
@ -0,0 +1,379 @@
|
|||
package oidc
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/coreos/go-oidc/jose"
|
||||
"github.com/coreos/go-oidc/key"
|
||||
)
|
||||
|
||||
func TestVerifyClientClaims(t *testing.T) {
|
||||
validIss := "https://example.com"
|
||||
validClientID := "valid-client"
|
||||
now := time.Now()
|
||||
tomorrow := now.Add(24 * time.Hour)
|
||||
header := jose.JOSEHeader{
|
||||
jose.HeaderKeyAlgorithm: "test-alg",
|
||||
jose.HeaderKeyID: "1",
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
claims jose.Claims
|
||||
ok bool
|
||||
}{
|
||||
// valid token
|
||||
{
|
||||
claims: jose.Claims{
|
||||
"iss": validIss,
|
||||
"sub": validClientID,
|
||||
"aud": validClientID,
|
||||
"iat": float64(now.Unix()),
|
||||
"exp": float64(tomorrow.Unix()),
|
||||
},
|
||||
ok: true,
|
||||
},
|
||||
// valid token, ('aud' claim is []string)
|
||||
{
|
||||
claims: jose.Claims{
|
||||
"iss": validIss,
|
||||
"sub": validClientID,
|
||||
"aud": []string{"foo", validClientID},
|
||||
"iat": float64(now.Unix()),
|
||||
"exp": float64(tomorrow.Unix()),
|
||||
},
|
||||
ok: true,
|
||||
},
|
||||
// valid token, ('aud' claim is []interface{})
|
||||
{
|
||||
claims: jose.Claims{
|
||||
"iss": validIss,
|
||||
"sub": validClientID,
|
||||
"aud": []interface{}{"foo", validClientID},
|
||||
"iat": float64(now.Unix()),
|
||||
"exp": float64(tomorrow.Unix()),
|
||||
},
|
||||
ok: true,
|
||||
},
|
||||
// missing 'iss' claim
|
||||
{
|
||||
claims: jose.Claims{
|
||||
"sub": validClientID,
|
||||
"aud": validClientID,
|
||||
"iat": float64(now.Unix()),
|
||||
"exp": float64(tomorrow.Unix()),
|
||||
},
|
||||
ok: false,
|
||||
},
|
||||
// invalid 'iss' claim
|
||||
{
|
||||
claims: jose.Claims{
|
||||
"iss": "INVALID",
|
||||
"sub": validClientID,
|
||||
"aud": validClientID,
|
||||
"iat": float64(now.Unix()),
|
||||
"exp": float64(tomorrow.Unix()),
|
||||
},
|
||||
ok: false,
|
||||
},
|
||||
// missing 'sub' claim
|
||||
{
|
||||
claims: jose.Claims{
|
||||
"iss": validIss,
|
||||
"aud": validClientID,
|
||||
"iat": float64(now.Unix()),
|
||||
"exp": float64(tomorrow.Unix()),
|
||||
},
|
||||
ok: false,
|
||||
},
|
||||
// invalid 'sub' claim
|
||||
{
|
||||
claims: jose.Claims{
|
||||
"iss": validIss,
|
||||
"sub": "INVALID",
|
||||
"aud": validClientID,
|
||||
"iat": float64(now.Unix()),
|
||||
"exp": float64(tomorrow.Unix()),
|
||||
},
|
||||
ok: false,
|
||||
},
|
||||
// missing 'aud' claim
|
||||
{
|
||||
claims: jose.Claims{
|
||||
"iss": validIss,
|
||||
"sub": validClientID,
|
||||
"iat": float64(now.Unix()),
|
||||
"exp": float64(tomorrow.Unix()),
|
||||
},
|
||||
ok: false,
|
||||
},
|
||||
// invalid 'aud' claim
|
||||
{
|
||||
claims: jose.Claims{
|
||||
"iss": validIss,
|
||||
"sub": validClientID,
|
||||
"aud": "INVALID",
|
||||
"iat": float64(now.Unix()),
|
||||
"exp": float64(tomorrow.Unix()),
|
||||
},
|
||||
ok: false,
|
||||
},
|
||||
// invalid 'aud' claim
|
||||
{
|
||||
claims: jose.Claims{
|
||||
"iss": validIss,
|
||||
"sub": validClientID,
|
||||
"aud": []string{"INVALID1", "INVALID2"},
|
||||
"iat": float64(now.Unix()),
|
||||
"exp": float64(tomorrow.Unix()),
|
||||
},
|
||||
ok: false,
|
||||
},
|
||||
// invalid 'aud' type
|
||||
{
|
||||
claims: jose.Claims{
|
||||
"iss": validIss,
|
||||
"sub": validClientID,
|
||||
"aud": struct{}{},
|
||||
"iat": float64(now.Unix()),
|
||||
"exp": float64(tomorrow.Unix()),
|
||||
},
|
||||
ok: false,
|
||||
},
|
||||
// expired
|
||||
{
|
||||
claims: jose.Claims{
|
||||
"iss": validIss,
|
||||
"sub": validClientID,
|
||||
"aud": validClientID,
|
||||
"iat": float64(now.Unix()),
|
||||
"exp": float64(now.Unix()),
|
||||
},
|
||||
ok: false,
|
||||
},
|
||||
}
|
||||
|
||||
for i, tt := range tests {
|
||||
jwt, err := jose.NewJWT(header, tt.claims)
|
||||
if err != nil {
|
||||
t.Fatalf("case %d: Failed to generate JWT, error=%v", i, err)
|
||||
}
|
||||
|
||||
got, err := VerifyClientClaims(jwt, validIss)
|
||||
if tt.ok {
|
||||
if err != nil {
|
||||
t.Errorf("case %d: unexpected error, err=%v", i, err)
|
||||
}
|
||||
if got != validClientID {
|
||||
t.Errorf("case %d: incorrect client ID, want=%s, got=%s", i, validClientID, got)
|
||||
}
|
||||
} else if err == nil {
|
||||
t.Errorf("case %d: expected error but err is nil", i)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestJWTVerifier(t *testing.T) {
|
||||
iss := "http://example.com"
|
||||
now := time.Now()
|
||||
future12 := now.Add(12 * time.Hour)
|
||||
past36 := now.Add(-36 * time.Hour)
|
||||
past12 := now.Add(-12 * time.Hour)
|
||||
|
||||
priv1, err := key.GeneratePrivateKey()
|
||||
if err != nil {
|
||||
t.Fatalf("failed to generate private key, error=%v", err)
|
||||
}
|
||||
pk1 := *key.NewPublicKey(priv1.JWK())
|
||||
|
||||
priv2, err := key.GeneratePrivateKey()
|
||||
if err != nil {
|
||||
t.Fatalf("failed to generate private key, error=%v", err)
|
||||
}
|
||||
pk2 := *key.NewPublicKey(priv2.JWK())
|
||||
|
||||
jwtPK1, err := jose.NewSignedJWT(NewClaims(iss, "XXX", "XXX", past12, future12), priv1.Signer())
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
|
||||
jwtPK1BadClaims, err := jose.NewSignedJWT(NewClaims(iss, "XXX", "YYY", past12, future12), priv1.Signer())
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
|
||||
jwtPK1BadClaims2, err := jose.NewSignedJWT(NewClaims(iss, "XXX", []string{"YYY", "ZZZ"}, past12, future12), priv1.Signer())
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
|
||||
jwtExpired, err := jose.NewSignedJWT(NewClaims(iss, "XXX", "XXX", past36, past12), priv1.Signer())
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
|
||||
jwtPK2, err := jose.NewSignedJWT(NewClaims(iss, "XXX", "XXX", past12, future12), priv2.Signer())
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
|
||||
jwtPK3, err := jose.NewSignedJWT(NewClaims(iss, "XXX", []string{"ZZZ", "XXX"}, past12, future12), priv1.Signer())
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
verifier JWTVerifier
|
||||
jwt jose.JWT
|
||||
wantErr bool
|
||||
}{
|
||||
// JWT signed with available key
|
||||
{
|
||||
verifier: JWTVerifier{
|
||||
issuer: "example.com",
|
||||
clientID: "XXX",
|
||||
syncFunc: func() error { return nil },
|
||||
keysFunc: func() []key.PublicKey {
|
||||
return []key.PublicKey{pk1}
|
||||
},
|
||||
},
|
||||
jwt: *jwtPK1,
|
||||
wantErr: false,
|
||||
},
|
||||
|
||||
// JWT signed with available key, with bad claims
|
||||
{
|
||||
verifier: JWTVerifier{
|
||||
issuer: "example.com",
|
||||
clientID: "XXX",
|
||||
syncFunc: func() error { return nil },
|
||||
keysFunc: func() []key.PublicKey {
|
||||
return []key.PublicKey{pk1}
|
||||
},
|
||||
},
|
||||
jwt: *jwtPK1BadClaims,
|
||||
wantErr: true,
|
||||
},
|
||||
|
||||
// JWT signed with available key,
|
||||
{
|
||||
verifier: JWTVerifier{
|
||||
issuer: "example.com",
|
||||
clientID: "XXX",
|
||||
syncFunc: func() error { return nil },
|
||||
keysFunc: func() []key.PublicKey {
|
||||
return []key.PublicKey{pk1}
|
||||
},
|
||||
},
|
||||
jwt: *jwtPK1BadClaims2,
|
||||
wantErr: true,
|
||||
},
|
||||
|
||||
// expired JWT signed with available key
|
||||
{
|
||||
verifier: JWTVerifier{
|
||||
issuer: "example.com",
|
||||
clientID: "XXX",
|
||||
syncFunc: func() error { return nil },
|
||||
keysFunc: func() []key.PublicKey {
|
||||
return []key.PublicKey{pk1}
|
||||
},
|
||||
},
|
||||
jwt: *jwtExpired,
|
||||
wantErr: true,
|
||||
},
|
||||
|
||||
// JWT signed with unrecognized key, verifiable after sync
|
||||
{
|
||||
verifier: JWTVerifier{
|
||||
issuer: "example.com",
|
||||
clientID: "XXX",
|
||||
syncFunc: func() error { return nil },
|
||||
keysFunc: func() func() []key.PublicKey {
|
||||
var i int
|
||||
return func() []key.PublicKey {
|
||||
defer func() { i++ }()
|
||||
return [][]key.PublicKey{
|
||||
[]key.PublicKey{pk1},
|
||||
[]key.PublicKey{pk2},
|
||||
}[i]
|
||||
}
|
||||
}(),
|
||||
},
|
||||
jwt: *jwtPK2,
|
||||
wantErr: false,
|
||||
},
|
||||
|
||||
// JWT signed with unrecognized key, not verifiable after sync
|
||||
{
|
||||
verifier: JWTVerifier{
|
||||
issuer: "example.com",
|
||||
clientID: "XXX",
|
||||
syncFunc: func() error { return nil },
|
||||
keysFunc: func() []key.PublicKey {
|
||||
return []key.PublicKey{pk1}
|
||||
},
|
||||
},
|
||||
jwt: *jwtPK2,
|
||||
wantErr: true,
|
||||
},
|
||||
|
||||
// verifier gets no keys from keysFunc, still not verifiable after sync
|
||||
{
|
||||
verifier: JWTVerifier{
|
||||
issuer: "example.com",
|
||||
clientID: "XXX",
|
||||
syncFunc: func() error { return nil },
|
||||
keysFunc: func() []key.PublicKey {
|
||||
return []key.PublicKey{}
|
||||
},
|
||||
},
|
||||
jwt: *jwtPK1,
|
||||
wantErr: true,
|
||||
},
|
||||
|
||||
// verifier gets no keys from keysFunc, verifiable after sync
|
||||
{
|
||||
verifier: JWTVerifier{
|
||||
issuer: "example.com",
|
||||
clientID: "XXX",
|
||||
syncFunc: func() error { return nil },
|
||||
keysFunc: func() func() []key.PublicKey {
|
||||
var i int
|
||||
return func() []key.PublicKey {
|
||||
defer func() { i++ }()
|
||||
return [][]key.PublicKey{
|
||||
[]key.PublicKey{},
|
||||
[]key.PublicKey{pk2},
|
||||
}[i]
|
||||
}
|
||||
}(),
|
||||
},
|
||||
jwt: *jwtPK2,
|
||||
wantErr: false,
|
||||
},
|
||||
|
||||
// JWT signed with available key, 'aud' is a string array.
|
||||
{
|
||||
verifier: JWTVerifier{
|
||||
issuer: "example.com",
|
||||
clientID: "XXX",
|
||||
syncFunc: func() error { return nil },
|
||||
keysFunc: func() []key.PublicKey {
|
||||
return []key.PublicKey{pk1}
|
||||
},
|
||||
},
|
||||
jwt: *jwtPK3,
|
||||
wantErr: false,
|
||||
},
|
||||
}
|
||||
|
||||
for i, tt := range tests {
|
||||
err := tt.verifier.Verify(tt.jwt)
|
||||
if tt.wantErr && (err == nil) {
|
||||
t.Errorf("case %d: wanted non-nil error", i)
|
||||
} else if !tt.wantErr && (err != nil) {
|
||||
t.Errorf("case %d: wanted nil error, got %v", i, err)
|
||||
}
|
||||
}
|
||||
}
|
58
vendor/github.com/coreos/go-oidc/test
generated
vendored
Executable file
58
vendor/github.com/coreos/go-oidc/test
generated
vendored
Executable file
|
@ -0,0 +1,58 @@
|
|||
#!/bin/bash -e
|
||||
#
|
||||
# Run all tests (not including functional)
|
||||
# ./test
|
||||
# ./test -v
|
||||
#
|
||||
# Run tests for one package
|
||||
# PKG=./unit ./test
|
||||
# PKG=ssh ./test
|
||||
#
|
||||
|
||||
# Invoke ./cover for HTML output
|
||||
COVER=${COVER:-"-cover"}
|
||||
|
||||
RACE=${RACE:-"-race"}
|
||||
|
||||
source ./build
|
||||
|
||||
TESTABLE="http jose key oauth2 oidc"
|
||||
FORMATTABLE="$TESTABLE"
|
||||
|
||||
# user has not provided PKG override
|
||||
if [ -z "$PKG" ]; then
|
||||
TEST=$TESTABLE
|
||||
FMT=$FORMATTABLE
|
||||
|
||||
# user has provided PKG override
|
||||
else
|
||||
# strip out slashes and dots from PKG=./foo/
|
||||
TEST=${PKG//\//}
|
||||
TEST=${TEST//./}
|
||||
|
||||
# only run gofmt on packages provided by user
|
||||
FMT="$TEST"
|
||||
fi
|
||||
|
||||
# split TEST into an array and prepend repo path to each local package
|
||||
split=(${TEST// / })
|
||||
TEST=${split[@]/#/github.com/coreos/go-oidc/}
|
||||
|
||||
echo "Running tests..."
|
||||
go test $RACE ${COVER} $@ ${TEST}
|
||||
|
||||
echo "Checking gofmt..."
|
||||
fmtRes=$(gofmt -l $FMT)
|
||||
if [ -n "${fmtRes}" ]; then
|
||||
echo -e "gofmt checking failed:\n${fmtRes}"
|
||||
exit 255
|
||||
fi
|
||||
|
||||
echo "Checking govet..."
|
||||
vetRes=$(go vet $TEST)
|
||||
if [ -n "${vetRes}" ]; then
|
||||
echo -e "govet checking failed:\n${vetRes}"
|
||||
exit 255
|
||||
fi
|
||||
|
||||
echo "Success"
|
27
vendor/github.com/coreos/pkg/.gitignore
generated
vendored
Normal file
27
vendor/github.com/coreos/pkg/.gitignore
generated
vendored
Normal file
|
@ -0,0 +1,27 @@
|
|||
# Compiled Object files, Static and Dynamic libs (Shared Objects)
|
||||
*.o
|
||||
*.a
|
||||
*.so
|
||||
|
||||
# Folders
|
||||
_obj
|
||||
_test
|
||||
|
||||
# Architecture specific extensions/prefixes
|
||||
*.[568vq]
|
||||
[568vq].out
|
||||
|
||||
*.cgo1.go
|
||||
*.cgo2.c
|
||||
_cgo_defun.c
|
||||
_cgo_gotypes.go
|
||||
_cgo_export.*
|
||||
|
||||
_testmain.go
|
||||
|
||||
*.exe
|
||||
*.test
|
||||
*.prof
|
||||
|
||||
bin/
|
||||
coverage/
|
71
vendor/github.com/coreos/pkg/CONTRIBUTING.md
generated
vendored
Normal file
71
vendor/github.com/coreos/pkg/CONTRIBUTING.md
generated
vendored
Normal file
|
@ -0,0 +1,71 @@
|
|||
# How to Contribute
|
||||
|
||||
CoreOS projects are [Apache 2.0 licensed](LICENSE) and accept contributions via
|
||||
GitHub pull requests. This document outlines some of the conventions on
|
||||
development workflow, commit message formatting, contact points and other
|
||||
resources to make it easier to get your contribution accepted.
|
||||
|
||||
# Certificate of Origin
|
||||
|
||||
By contributing to this project you agree to the Developer Certificate of
|
||||
Origin (DCO). This document was created by the Linux Kernel community and is a
|
||||
simple statement that you, as a contributor, have the legal right to make the
|
||||
contribution. See the [DCO](DCO) file for details.
|
||||
|
||||
# Email and Chat
|
||||
|
||||
The project currently uses the general CoreOS email list and IRC channel:
|
||||
- Email: [coreos-dev](https://groups.google.com/forum/#!forum/coreos-dev)
|
||||
- IRC: #[coreos](irc://irc.freenode.org:6667/#coreos) IRC channel on freenode.org
|
||||
|
||||
Please avoid emailing maintainers found in the MAINTAINERS file directly. They
|
||||
are very busy and read the mailing lists.
|
||||
|
||||
## Getting Started
|
||||
|
||||
- Fork the repository on GitHub
|
||||
- Read the [README](README.md) for build and test instructions
|
||||
- Play with the project, submit bugs, submit patches!
|
||||
|
||||
## Contribution Flow
|
||||
|
||||
This is a rough outline of what a contributor's workflow looks like:
|
||||
|
||||
- Create a topic branch from where you want to base your work (usually master).
|
||||
- Make commits of logical units.
|
||||
- Make sure your commit messages are in the proper format (see below).
|
||||
- Push your changes to a topic branch in your fork of the repository.
|
||||
- Make sure the tests pass, and add any new tests as appropriate.
|
||||
- Submit a pull request to the original repository.
|
||||
|
||||
Thanks for your contributions!
|
||||
|
||||
### Format of the Commit Message
|
||||
|
||||
We follow a rough convention for commit messages that is designed to answer two
|
||||
questions: what changed and why. The subject line should feature the what and
|
||||
the body of the commit should describe the why.
|
||||
|
||||
```
|
||||
scripts: add the test-cluster command
|
||||
|
||||
this uses tmux to setup a test cluster that you can easily kill and
|
||||
start for debugging.
|
||||
|
||||
Fixes #38
|
||||
```
|
||||
|
||||
The format can be described more formally as follows:
|
||||
|
||||
```
|
||||
<subsystem>: <what changed>
|
||||
<BLANK LINE>
|
||||
<why this change was made>
|
||||
<BLANK LINE>
|
||||
<footer>
|
||||
```
|
||||
|
||||
The first line is the subject and should be no longer than 70 characters, the
|
||||
second line is always blank, and other lines should be wrapped at 80 characters.
|
||||
This allows the message to be easier to read on GitHub as well as in various
|
||||
git tools.
|
36
vendor/github.com/coreos/pkg/DCO
generated
vendored
Normal file
36
vendor/github.com/coreos/pkg/DCO
generated
vendored
Normal file
|
@ -0,0 +1,36 @@
|
|||
Developer Certificate of Origin
|
||||
Version 1.1
|
||||
|
||||
Copyright (C) 2004, 2006 The Linux Foundation and its contributors.
|
||||
660 York Street, Suite 102,
|
||||
San Francisco, CA 94110 USA
|
||||
|
||||
Everyone is permitted to copy and distribute verbatim copies of this
|
||||
license document, but changing it is not allowed.
|
||||
|
||||
|
||||
Developer's Certificate of Origin 1.1
|
||||
|
||||
By making a contribution to this project, I certify that:
|
||||
|
||||
(a) The contribution was created in whole or in part by me and I
|
||||
have the right to submit it under the open source license
|
||||
indicated in the file; or
|
||||
|
||||
(b) The contribution is based upon previous work that, to the best
|
||||
of my knowledge, is covered under an appropriate open source
|
||||
license and I have the right under that license to submit that
|
||||
work with modifications, whether created in whole or in part
|
||||
by me, under the same open source license (unless I am
|
||||
permitted to submit under a different license), as indicated
|
||||
in the file; or
|
||||
|
||||
(c) The contribution was provided directly to me by some other
|
||||
person who certified (a), (b) or (c) and I have not modified
|
||||
it.
|
||||
|
||||
(d) I understand and agree that this project and the contribution
|
||||
are public and that a record of the contribution (including all
|
||||
personal information I submit with it, including my sign-off) is
|
||||
maintained indefinitely and may be redistributed consistent with
|
||||
this project or the open source license(s) involved.
|
1
vendor/github.com/coreos/pkg/MAINTAINERS
generated
vendored
Normal file
1
vendor/github.com/coreos/pkg/MAINTAINERS
generated
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
Ed Rooth <ed.rooth@coreos.com> (@sym3tri)
|
3
vendor/github.com/coreos/pkg/README.md
generated
vendored
Normal file
3
vendor/github.com/coreos/pkg/README.md
generated
vendored
Normal file
|
@ -0,0 +1,3 @@
|
|||
a collection of go utility packages
|
||||
|
||||
[![Build Status](https://semaphoreci.com/api/v1/projects/14b3f261-22c2-4f56-b1ff-f23f4aa03f5c/411991/badge.svg)](https://semaphoreci.com/coreos/pkg)
|
3
vendor/github.com/coreos/pkg/build
generated
vendored
Executable file
3
vendor/github.com/coreos/pkg/build
generated
vendored
Executable file
|
@ -0,0 +1,3 @@
|
|||
#!/bin/bash -e
|
||||
|
||||
go build ./...
|
94
vendor/github.com/coreos/pkg/cryptoutil/aes.go
generated
vendored
Normal file
94
vendor/github.com/coreos/pkg/cryptoutil/aes.go
generated
vendored
Normal file
|
@ -0,0 +1,94 @@
|
|||
package cryptoutil
|
||||
|
||||
import (
|
||||
"crypto/aes"
|
||||
"crypto/cipher"
|
||||
"crypto/rand"
|
||||
"errors"
|
||||
)
|
||||
|
||||
// pad uses the PKCS#7 padding scheme to align the a payload to a specific block size
|
||||
func pad(plaintext []byte, bsize int) ([]byte, error) {
|
||||
if bsize >= 256 {
|
||||
return nil, errors.New("bsize must be < 256")
|
||||
}
|
||||
pad := bsize - (len(plaintext) % bsize)
|
||||
if pad == 0 {
|
||||
pad = bsize
|
||||
}
|
||||
for i := 0; i < pad; i++ {
|
||||
plaintext = append(plaintext, byte(pad))
|
||||
}
|
||||
return plaintext, nil
|
||||
}
|
||||
|
||||
// unpad strips the padding previously added using the PKCS#7 padding scheme
|
||||
func unpad(paddedtext []byte) ([]byte, error) {
|
||||
length := len(paddedtext)
|
||||
paddedtext, lbyte := paddedtext[:length-1], paddedtext[length-1]
|
||||
pad := int(lbyte)
|
||||
if pad >= 256 || pad > length {
|
||||
return nil, errors.New("padding malformed")
|
||||
}
|
||||
return paddedtext[:length-(pad)], nil
|
||||
}
|
||||
|
||||
// AESEncrypt encrypts a payload with an AES cipher.
|
||||
// The returned ciphertext has three notable properties:
|
||||
// 1. ciphertext is aligned to the standard AES block size
|
||||
// 2. ciphertext is padded using PKCS#7
|
||||
// 3. IV is prepended to the ciphertext
|
||||
func AESEncrypt(plaintext, key []byte) ([]byte, error) {
|
||||
plaintext, err := pad(plaintext, aes.BlockSize)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
block, err := aes.NewCipher(key)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ciphertext := make([]byte, aes.BlockSize+len(plaintext))
|
||||
iv := ciphertext[:aes.BlockSize]
|
||||
if _, err := rand.Read(iv); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
mode := cipher.NewCBCEncrypter(block, iv)
|
||||
mode.CryptBlocks(ciphertext[aes.BlockSize:], plaintext)
|
||||
|
||||
return ciphertext, nil
|
||||
}
|
||||
|
||||
// AESDecrypt decrypts an encrypted payload with an AES cipher.
|
||||
// The decryption algorithm makes three assumptions:
|
||||
// 1. ciphertext is aligned to the standard AES block size
|
||||
// 2. ciphertext is padded using PKCS#7
|
||||
// 3. the IV is prepended to ciphertext
|
||||
func AESDecrypt(ciphertext, key []byte) ([]byte, error) {
|
||||
if len(ciphertext) < aes.BlockSize {
|
||||
return nil, errors.New("ciphertext too short")
|
||||
}
|
||||
|
||||
iv := ciphertext[:aes.BlockSize]
|
||||
ciphertext = ciphertext[aes.BlockSize:]
|
||||
|
||||
if len(ciphertext)%aes.BlockSize != 0 {
|
||||
return nil, errors.New("ciphertext is not a multiple of the block size")
|
||||
}
|
||||
|
||||
block, err := aes.NewCipher(key)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
mode := cipher.NewCBCDecrypter(block, iv)
|
||||
mode.CryptBlocks(ciphertext, ciphertext)
|
||||
|
||||
if len(ciphertext)%aes.BlockSize != 0 {
|
||||
return nil, errors.New("ciphertext is not a multiple of the block size")
|
||||
}
|
||||
|
||||
return unpad(ciphertext)
|
||||
}
|
93
vendor/github.com/coreos/pkg/cryptoutil/aes_test.go
generated
vendored
Normal file
93
vendor/github.com/coreos/pkg/cryptoutil/aes_test.go
generated
vendored
Normal file
|
@ -0,0 +1,93 @@
|
|||
package cryptoutil
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestPadUnpad(t *testing.T) {
|
||||
tests := []struct {
|
||||
plaintext []byte
|
||||
bsize int
|
||||
padded []byte
|
||||
}{
|
||||
{
|
||||
plaintext: []byte{1, 2, 3, 4},
|
||||
bsize: 7,
|
||||
padded: []byte{1, 2, 3, 4, 3, 3, 3},
|
||||
},
|
||||
{
|
||||
plaintext: []byte{1, 2, 3, 4, 5, 6, 7},
|
||||
bsize: 3,
|
||||
padded: []byte{1, 2, 3, 4, 5, 6, 7, 2, 2},
|
||||
},
|
||||
{
|
||||
plaintext: []byte{9, 9, 9, 9},
|
||||
bsize: 4,
|
||||
padded: []byte{9, 9, 9, 9, 4, 4, 4, 4},
|
||||
},
|
||||
}
|
||||
|
||||
for i, tt := range tests {
|
||||
padded, err := pad(tt.plaintext, tt.bsize)
|
||||
if err != nil {
|
||||
t.Errorf("case %d: unexpected error: %v", i, err)
|
||||
continue
|
||||
}
|
||||
if !reflect.DeepEqual(tt.padded, padded) {
|
||||
t.Errorf("case %d: want=%v got=%v", i, tt.padded, padded)
|
||||
continue
|
||||
}
|
||||
|
||||
plaintext, err := unpad(tt.padded)
|
||||
if err != nil {
|
||||
t.Errorf("case %d: unexpected error: %v", i, err)
|
||||
continue
|
||||
}
|
||||
if !reflect.DeepEqual(tt.plaintext, plaintext) {
|
||||
t.Errorf("case %d: want=%v got=%v", i, tt.plaintext, plaintext)
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestPadMaxBlockSize(t *testing.T) {
|
||||
_, err := pad([]byte{1, 2, 3}, 256)
|
||||
if err == nil {
|
||||
t.Errorf("Expected non-nil error")
|
||||
}
|
||||
}
|
||||
|
||||
func TestAESEncryptDecrypt(t *testing.T) {
|
||||
message := []byte("Let me worry about blank.")
|
||||
key := append([]byte("shark"), make([]byte, 27)...)
|
||||
|
||||
ciphertext, err := AESEncrypt(message, key)
|
||||
if err != nil {
|
||||
t.Fatalf("Unexpected error: %v", err)
|
||||
}
|
||||
if reflect.DeepEqual(message, ciphertext) {
|
||||
t.Fatal("Encrypted data matches original payload")
|
||||
}
|
||||
|
||||
decrypted, err := AESDecrypt(ciphertext, key)
|
||||
if !reflect.DeepEqual(message, decrypted) {
|
||||
t.Fatalf("Decrypted data does not match original payload: want=%v got=%v", message, decrypted)
|
||||
}
|
||||
}
|
||||
|
||||
func TestAESDecryptWrongKey(t *testing.T) {
|
||||
message := []byte("My bones!")
|
||||
key := append([]byte("shark"), make([]byte, 27)...)
|
||||
|
||||
ciphertext, err := AESEncrypt(message, key)
|
||||
if err != nil {
|
||||
t.Fatalf("Unexpected error: %v", err)
|
||||
}
|
||||
|
||||
wrongKey := append([]byte("sheep"), make([]byte, 27)...)
|
||||
decrypted, _ := AESDecrypt(ciphertext, wrongKey)
|
||||
if reflect.DeepEqual(message, decrypted) {
|
||||
t.Fatalf("Data decrypted with different key matches original payload")
|
||||
}
|
||||
}
|
64
vendor/github.com/coreos/pkg/flagutil/env_test.go
generated
vendored
Normal file
64
vendor/github.com/coreos/pkg/flagutil/env_test.go
generated
vendored
Normal file
|
@ -0,0 +1,64 @@
|
|||
package flagutil
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"os"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestSetFlagsFromEnv(t *testing.T) {
|
||||
fs := flag.NewFlagSet("testing", flag.ExitOnError)
|
||||
fs.String("a", "", "")
|
||||
fs.String("b", "", "")
|
||||
fs.String("c", "", "")
|
||||
fs.Parse([]string{})
|
||||
|
||||
os.Clearenv()
|
||||
// flags should be settable using env vars
|
||||
os.Setenv("MYPROJ_A", "foo")
|
||||
// and command-line flags
|
||||
if err := fs.Set("b", "bar"); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
// command-line flags take precedence over env vars
|
||||
os.Setenv("MYPROJ_C", "woof")
|
||||
if err := fs.Set("c", "quack"); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// first verify that flags are as expected before reading the env
|
||||
for f, want := range map[string]string{
|
||||
"a": "",
|
||||
"b": "bar",
|
||||
"c": "quack",
|
||||
} {
|
||||
if got := fs.Lookup(f).Value.String(); got != want {
|
||||
t.Fatalf("flag %q=%q, want %q", f, got, want)
|
||||
}
|
||||
}
|
||||
|
||||
// now read the env and verify flags were updated as expected
|
||||
err := SetFlagsFromEnv(fs, "MYPROJ")
|
||||
if err != nil {
|
||||
t.Errorf("err=%v, want nil", err)
|
||||
}
|
||||
for f, want := range map[string]string{
|
||||
"a": "foo",
|
||||
"b": "bar",
|
||||
"c": "quack",
|
||||
} {
|
||||
if got := fs.Lookup(f).Value.String(); got != want {
|
||||
t.Errorf("flag %q=%q, want %q", f, got, want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestSetFlagsFromEnvBad(t *testing.T) {
|
||||
// now verify that an error is propagated
|
||||
fs := flag.NewFlagSet("testing", flag.ExitOnError)
|
||||
fs.Int("x", 0, "")
|
||||
os.Setenv("MYPROJ_X", "not_a_number")
|
||||
if err := SetFlagsFromEnv(fs, "MYPROJ"); err == nil {
|
||||
t.Errorf("err=nil, want != nil")
|
||||
}
|
||||
}
|
57
vendor/github.com/coreos/pkg/flagutil/types_test.go
generated
vendored
Normal file
57
vendor/github.com/coreos/pkg/flagutil/types_test.go
generated
vendored
Normal file
|
@ -0,0 +1,57 @@
|
|||
package flagutil
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestIPv4FlagSetInvalidArgument(t *testing.T) {
|
||||
tests := []string{
|
||||
"",
|
||||
"foo",
|
||||
"::",
|
||||
"127.0.0.1:4328",
|
||||
}
|
||||
|
||||
for i, tt := range tests {
|
||||
var f IPv4Flag
|
||||
if err := f.Set(tt); err == nil {
|
||||
t.Errorf("case %d: expected non-nil error", i)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestIPv4FlagSetValidArgument(t *testing.T) {
|
||||
tests := []string{
|
||||
"127.0.0.1",
|
||||
"0.0.0.0",
|
||||
}
|
||||
|
||||
for i, tt := range tests {
|
||||
var f IPv4Flag
|
||||
if err := f.Set(tt); err != nil {
|
||||
t.Errorf("case %d: err=%v", i, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestStringSliceFlag(t *testing.T) {
|
||||
tests := []struct {
|
||||
input string
|
||||
want []string
|
||||
}{
|
||||
{input: "", want: []string{""}},
|
||||
{input: "foo", want: []string{"foo"}},
|
||||
{input: "foo,bar", want: []string{"foo", "bar"}},
|
||||
}
|
||||
|
||||
for i, tt := range tests {
|
||||
var f StringSliceFlag
|
||||
if err := f.Set(tt.input); err != nil {
|
||||
t.Errorf("case %d: err=%v", i, err)
|
||||
}
|
||||
if !reflect.DeepEqual(tt.want, []string(f)) {
|
||||
t.Errorf("case %d: want=%v got=%v", i, tt.want, f)
|
||||
}
|
||||
}
|
||||
}
|
198
vendor/github.com/coreos/pkg/health/health_test.go
generated
vendored
Normal file
198
vendor/github.com/coreos/pkg/health/health_test.go
generated
vendored
Normal file
|
@ -0,0 +1,198 @@
|
|||
package health
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
|
||||
"github.com/coreos/pkg/httputil"
|
||||
)
|
||||
|
||||
type boolChecker bool
|
||||
|
||||
func (b boolChecker) Healthy() error {
|
||||
if b {
|
||||
return nil
|
||||
}
|
||||
return errors.New("Unhealthy")
|
||||
}
|
||||
|
||||
func errString(err error) string {
|
||||
if err == nil {
|
||||
return ""
|
||||
}
|
||||
return err.Error()
|
||||
}
|
||||
|
||||
func TestCheck(t *testing.T) {
|
||||
for i, test := range []struct {
|
||||
checks []Checkable
|
||||
expected string
|
||||
}{
|
||||
{[]Checkable{}, ""},
|
||||
|
||||
{[]Checkable{boolChecker(true)}, ""},
|
||||
|
||||
{[]Checkable{boolChecker(true), boolChecker(true)}, ""},
|
||||
|
||||
{[]Checkable{boolChecker(true), boolChecker(false)}, "Unhealthy"},
|
||||
|
||||
{[]Checkable{boolChecker(true), boolChecker(false), boolChecker(false)}, "multiple health check failure: [Unhealthy Unhealthy]"},
|
||||
} {
|
||||
err := Check(test.checks)
|
||||
|
||||
if errString(err) != test.expected {
|
||||
t.Errorf("case %d: want %v, got %v", i, test.expected, errString(err))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestHandlerFunc(t *testing.T) {
|
||||
for i, test := range []struct {
|
||||
checker Checker
|
||||
method string
|
||||
expectedStatus string
|
||||
expectedCode int
|
||||
expectedMessage string
|
||||
}{
|
||||
{
|
||||
Checker{
|
||||
Checks: []Checkable{
|
||||
boolChecker(true),
|
||||
},
|
||||
},
|
||||
"GET",
|
||||
"ok",
|
||||
http.StatusOK,
|
||||
"",
|
||||
},
|
||||
|
||||
// Wrong method.
|
||||
{
|
||||
Checker{
|
||||
Checks: []Checkable{
|
||||
boolChecker(true),
|
||||
},
|
||||
},
|
||||
"POST",
|
||||
"",
|
||||
http.StatusMethodNotAllowed,
|
||||
"GET only acceptable method",
|
||||
},
|
||||
|
||||
// Health check fails.
|
||||
{
|
||||
Checker{
|
||||
Checks: []Checkable{
|
||||
boolChecker(false),
|
||||
},
|
||||
},
|
||||
"GET",
|
||||
"error",
|
||||
http.StatusInternalServerError,
|
||||
"Unhealthy",
|
||||
},
|
||||
|
||||
// Health check fails, with overridden ErrorHandler.
|
||||
{
|
||||
Checker{
|
||||
Checks: []Checkable{
|
||||
boolChecker(false),
|
||||
},
|
||||
UnhealthyHandler: func(w http.ResponseWriter, r *http.Request, err error) {
|
||||
httputil.WriteJSONResponse(w,
|
||||
http.StatusInternalServerError, StatusResponse{
|
||||
Status: "error",
|
||||
Details: &StatusResponseDetails{
|
||||
Code: http.StatusInternalServerError,
|
||||
Message: "Override!",
|
||||
},
|
||||
})
|
||||
},
|
||||
},
|
||||
"GET",
|
||||
"error",
|
||||
http.StatusInternalServerError,
|
||||
"Override!",
|
||||
},
|
||||
|
||||
// Health check succeeds, with overridden SuccessHandler.
|
||||
{
|
||||
Checker{
|
||||
Checks: []Checkable{
|
||||
boolChecker(true),
|
||||
},
|
||||
HealthyHandler: func(w http.ResponseWriter, r *http.Request) {
|
||||
httputil.WriteJSONResponse(w,
|
||||
http.StatusOK, StatusResponse{
|
||||
Status: "okey-dokey",
|
||||
})
|
||||
},
|
||||
},
|
||||
"GET",
|
||||
"okey-dokey",
|
||||
http.StatusOK,
|
||||
"",
|
||||
},
|
||||
} {
|
||||
w := httptest.NewRecorder()
|
||||
r := &http.Request{}
|
||||
r.Method = test.method
|
||||
test.checker.ServeHTTP(w, r)
|
||||
if w.Code != test.expectedCode {
|
||||
t.Errorf("case %d: w.code == %v, want %v", i, w.Code, test.expectedCode)
|
||||
}
|
||||
|
||||
if test.expectedStatus == "" {
|
||||
// This is to handle the wrong-method case, when the
|
||||
// body of the response is empty.
|
||||
continue
|
||||
}
|
||||
|
||||
statusMap := make(map[string]interface{})
|
||||
err := json.Unmarshal(w.Body.Bytes(), &statusMap)
|
||||
if err != nil {
|
||||
t.Fatalf("case %d: failed to Unmarshal response body: %v", i, err)
|
||||
}
|
||||
|
||||
status, ok := statusMap["status"].(string)
|
||||
if !ok {
|
||||
t.Errorf("case %d: status not present or not a string in json: %q", i, w.Body.Bytes())
|
||||
}
|
||||
if status != test.expectedStatus {
|
||||
t.Errorf("case %d: status == %v, want %v", i, status, test.expectedStatus)
|
||||
}
|
||||
|
||||
detailMap, ok := statusMap["details"].(map[string]interface{})
|
||||
if test.expectedMessage != "" {
|
||||
if !ok {
|
||||
t.Fatalf("case %d: could not find/unmarshal detailMap", i)
|
||||
}
|
||||
message, ok := detailMap["message"].(string)
|
||||
if !ok {
|
||||
t.Fatalf("case %d: message not present or not a string in json: %q",
|
||||
i, w.Body.Bytes())
|
||||
}
|
||||
if message != test.expectedMessage {
|
||||
t.Errorf("case %d: message == %v, want %v", i, message, test.expectedMessage)
|
||||
}
|
||||
|
||||
code, ok := detailMap["code"].(float64)
|
||||
if !ok {
|
||||
t.Fatalf("case %d: code not present or not an int in json: %q",
|
||||
i, w.Body.Bytes())
|
||||
}
|
||||
if int(code) != test.expectedCode {
|
||||
t.Errorf("case %d: code == %v, want %v", i, code, test.expectedCode)
|
||||
}
|
||||
|
||||
} else {
|
||||
if ok {
|
||||
t.Errorf("case %d: unwanted detailMap present: %q", i, detailMap)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
56
vendor/github.com/coreos/pkg/httputil/json_test.go
generated
vendored
Normal file
56
vendor/github.com/coreos/pkg/httputil/json_test.go
generated
vendored
Normal file
|
@ -0,0 +1,56 @@
|
|||
package httputil
|
||||
|
||||
import (
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestWriteJSONResponse(t *testing.T) {
|
||||
for i, test := range []struct {
|
||||
code int
|
||||
resp interface{}
|
||||
expectedJSON string
|
||||
expectErr bool
|
||||
}{
|
||||
{
|
||||
200,
|
||||
struct {
|
||||
A string
|
||||
B string
|
||||
}{A: "foo", B: "bar"},
|
||||
`{"A":"foo","B":"bar"}`,
|
||||
false,
|
||||
},
|
||||
{
|
||||
500,
|
||||
// Something that json.Marshal cannot serialize.
|
||||
make(chan int),
|
||||
"",
|
||||
true,
|
||||
},
|
||||
} {
|
||||
w := httptest.NewRecorder()
|
||||
err := WriteJSONResponse(w, test.code, test.resp)
|
||||
|
||||
if w.Code != test.code {
|
||||
t.Errorf("case %d: w.code == %v, want %v", i, w.Code, test.code)
|
||||
}
|
||||
|
||||
if (err != nil) != test.expectErr {
|
||||
t.Errorf("case %d: (err != nil) == %v, want %v. err: %v", i, err != nil, test.expectErr, err)
|
||||
}
|
||||
|
||||
if string(w.Body.Bytes()) != test.expectedJSON {
|
||||
t.Errorf("case %d: w.Body.Bytes()) == %q, want %q", i,
|
||||
string(w.Body.Bytes()), test.expectedJSON)
|
||||
}
|
||||
|
||||
if !test.expectErr {
|
||||
contentType := w.Header()["Content-Type"][0]
|
||||
if contentType != JSONContentType {
|
||||
t.Errorf("case %d: contentType == %v, want %v", i, contentType, JSONContentType)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
55
vendor/github.com/coreos/pkg/netutil/proxy.go
generated
vendored
Normal file
55
vendor/github.com/coreos/pkg/netutil/proxy.go
generated
vendored
Normal file
|
@ -0,0 +1,55 @@
|
|||
package netutil
|
||||
|
||||
import (
|
||||
"io"
|
||||
"net"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/coreos/pkg/capnslog"
|
||||
)
|
||||
|
||||
var (
|
||||
log = capnslog.NewPackageLogger("github.com/coreos/pkg/netutil", "main")
|
||||
)
|
||||
|
||||
// ProxyTCP proxies between two TCP connections.
|
||||
// Because TLS connections don't have CloseRead() and CloseWrite() methods, our
|
||||
// temporary solution is to use timeouts.
|
||||
func ProxyTCP(conn1, conn2 net.Conn, tlsWriteDeadline, tlsReadDeadline time.Duration) {
|
||||
var wg sync.WaitGroup
|
||||
wg.Add(2)
|
||||
|
||||
go copyBytes(conn1, conn2, &wg, tlsWriteDeadline, tlsReadDeadline)
|
||||
go copyBytes(conn2, conn1, &wg, tlsWriteDeadline, tlsReadDeadline)
|
||||
|
||||
wg.Wait()
|
||||
conn1.Close()
|
||||
conn2.Close()
|
||||
}
|
||||
|
||||
func copyBytes(dst, src net.Conn, wg *sync.WaitGroup, writeDeadline, readDeadline time.Duration) {
|
||||
defer wg.Done()
|
||||
n, err := io.Copy(dst, src)
|
||||
if err != nil {
|
||||
log.Errorf("proxy i/o error: %v", err)
|
||||
}
|
||||
|
||||
if cr, ok := src.(*net.TCPConn); ok {
|
||||
cr.CloseRead()
|
||||
} else {
|
||||
// For TLS connections.
|
||||
wto := time.Now().Add(writeDeadline)
|
||||
src.SetWriteDeadline(wto)
|
||||
}
|
||||
|
||||
if cw, ok := dst.(*net.TCPConn); ok {
|
||||
cw.CloseWrite()
|
||||
} else {
|
||||
// For TLS connections.
|
||||
rto := time.Now().Add(readDeadline)
|
||||
dst.SetReadDeadline(rto)
|
||||
}
|
||||
|
||||
log.Debugf("proxy copied %d bytes %s -> %s", n, src.RemoteAddr(), dst.RemoteAddr())
|
||||
}
|
17
vendor/github.com/coreos/pkg/netutil/url.go
generated
vendored
Normal file
17
vendor/github.com/coreos/pkg/netutil/url.go
generated
vendored
Normal file
|
@ -0,0 +1,17 @@
|
|||
package netutil
|
||||
|
||||
import (
|
||||
"net/url"
|
||||
)
|
||||
|
||||
// MergeQuery appends additional query values to an existing URL.
|
||||
func MergeQuery(u url.URL, q url.Values) url.URL {
|
||||
uv := u.Query()
|
||||
for k, vs := range q {
|
||||
for _, v := range vs {
|
||||
uv.Add(k, v)
|
||||
}
|
||||
}
|
||||
u.RawQuery = uv.Encode()
|
||||
return u
|
||||
}
|
86
vendor/github.com/coreos/pkg/netutil/url_test.go
generated
vendored
Normal file
86
vendor/github.com/coreos/pkg/netutil/url_test.go
generated
vendored
Normal file
|
@ -0,0 +1,86 @@
|
|||
package netutil
|
||||
|
||||
import (
|
||||
"net/url"
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestMergeQuery(t *testing.T) {
|
||||
tests := []struct {
|
||||
u string
|
||||
q url.Values
|
||||
w string
|
||||
}{
|
||||
// No values
|
||||
{
|
||||
u: "http://example.com",
|
||||
q: nil,
|
||||
w: "http://example.com",
|
||||
},
|
||||
// No additional values
|
||||
{
|
||||
u: "http://example.com?foo=bar",
|
||||
q: nil,
|
||||
w: "http://example.com?foo=bar",
|
||||
},
|
||||
// Simple addition
|
||||
{
|
||||
u: "http://example.com",
|
||||
q: url.Values{
|
||||
"foo": []string{"bar"},
|
||||
},
|
||||
w: "http://example.com?foo=bar",
|
||||
},
|
||||
// Addition with existing values
|
||||
{
|
||||
u: "http://example.com?dog=boo",
|
||||
q: url.Values{
|
||||
"foo": []string{"bar"},
|
||||
},
|
||||
w: "http://example.com?dog=boo&foo=bar",
|
||||
},
|
||||
// Merge
|
||||
{
|
||||
u: "http://example.com?dog=boo",
|
||||
q: url.Values{
|
||||
"dog": []string{"elroy"},
|
||||
},
|
||||
w: "http://example.com?dog=boo&dog=elroy",
|
||||
},
|
||||
// Add and merge
|
||||
{
|
||||
u: "http://example.com?dog=boo",
|
||||
q: url.Values{
|
||||
"dog": []string{"elroy"},
|
||||
"foo": []string{"bar"},
|
||||
},
|
||||
w: "http://example.com?dog=boo&dog=elroy&foo=bar",
|
||||
},
|
||||
// Multivalue merge
|
||||
{
|
||||
u: "http://example.com?dog=boo",
|
||||
q: url.Values{
|
||||
"dog": []string{"elroy", "penny"},
|
||||
},
|
||||
w: "http://example.com?dog=boo&dog=elroy&dog=penny",
|
||||
},
|
||||
}
|
||||
|
||||
for i, tt := range tests {
|
||||
ur, err := url.Parse(tt.u)
|
||||
if err != nil {
|
||||
t.Errorf("case %d: failed parsing test url: %v, error: %v", i, tt.u, err)
|
||||
}
|
||||
|
||||
got := MergeQuery(*ur, tt.q)
|
||||
want, err := url.Parse(tt.w)
|
||||
if err != nil {
|
||||
t.Errorf("case %d: failed parsing want url: %v, error: %v", i, tt.w, err)
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(*want, got) {
|
||||
t.Errorf("case %d: want: %v, got: %v", i, *want, got)
|
||||
}
|
||||
}
|
||||
}
|
56
vendor/github.com/coreos/pkg/test
generated
vendored
Executable file
56
vendor/github.com/coreos/pkg/test
generated
vendored
Executable file
|
@ -0,0 +1,56 @@
|
|||
#!/bin/bash -e
|
||||
#
|
||||
# Run all tests (not including functional)
|
||||
# ./test
|
||||
# ./test -v
|
||||
#
|
||||
# Run tests for one package
|
||||
# PKG=./unit ./test
|
||||
# PKG=ssh ./test
|
||||
#
|
||||
|
||||
# Invoke ./cover for HTML output
|
||||
COVER=${COVER:-"-cover"}
|
||||
|
||||
source ./build
|
||||
|
||||
TESTABLE="cryptoutil flagutil timeutil netutil yamlutil httputil health"
|
||||
FORMATTABLE="$TESTABLE capnslog"
|
||||
|
||||
# user has not provided PKG override
|
||||
if [ -z "$PKG" ]; then
|
||||
TEST=$TESTABLE
|
||||
FMT=$FORMATTABLE
|
||||
|
||||
# user has provided PKG override
|
||||
else
|
||||
# strip out slashes and dots from PKG=./foo/
|
||||
TEST=${PKG//\//}
|
||||
TEST=${TEST//./}
|
||||
|
||||
# only run gofmt on packages provided by user
|
||||
FMT="$TEST"
|
||||
fi
|
||||
|
||||
# split TEST into an array and prepend repo path to each local package
|
||||
split=(${TEST// / })
|
||||
TEST=${split[@]/#/github.com/coreos/pkg/}
|
||||
|
||||
echo "Running tests..."
|
||||
go test ${COVER} $@ ${TEST}
|
||||
|
||||
echo "Checking gofmt..."
|
||||
fmtRes=$(gofmt -l $FMT)
|
||||
if [ -n "${fmtRes}" ]; then
|
||||
echo -e "gofmt checking failed:\n${fmtRes}"
|
||||
exit 255
|
||||
fi
|
||||
|
||||
echo "Checking govet..."
|
||||
vetRes=$(go vet $TEST)
|
||||
if [ -n "${vetRes}" ]; then
|
||||
echo -e "govet checking failed:\n${vetRes}"
|
||||
exit 255
|
||||
fi
|
||||
|
||||
echo "Success"
|
52
vendor/github.com/coreos/pkg/timeutil/backoff_test.go
generated
vendored
Normal file
52
vendor/github.com/coreos/pkg/timeutil/backoff_test.go
generated
vendored
Normal file
|
@ -0,0 +1,52 @@
|
|||
package timeutil
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestExpBackoff(t *testing.T) {
|
||||
tests := []struct {
|
||||
prev time.Duration
|
||||
max time.Duration
|
||||
want time.Duration
|
||||
}{
|
||||
{
|
||||
prev: time.Duration(0),
|
||||
max: time.Minute,
|
||||
want: time.Second,
|
||||
},
|
||||
{
|
||||
prev: time.Second,
|
||||
max: time.Minute,
|
||||
want: 2 * time.Second,
|
||||
},
|
||||
{
|
||||
prev: 16 * time.Second,
|
||||
max: time.Minute,
|
||||
want: 32 * time.Second,
|
||||
},
|
||||
{
|
||||
prev: 32 * time.Second,
|
||||
max: time.Minute,
|
||||
want: time.Minute,
|
||||
},
|
||||
{
|
||||
prev: time.Minute,
|
||||
max: time.Minute,
|
||||
want: time.Minute,
|
||||
},
|
||||
{
|
||||
prev: 2 * time.Minute,
|
||||
max: time.Minute,
|
||||
want: time.Minute,
|
||||
},
|
||||
}
|
||||
|
||||
for i, tt := range tests {
|
||||
got := ExpBackoff(tt.prev, tt.max)
|
||||
if tt.want != got {
|
||||
t.Errorf("case %d: want=%v got=%v", i, tt.want, got)
|
||||
}
|
||||
}
|
||||
}
|
55
vendor/github.com/coreos/pkg/yamlutil/yaml.go
generated
vendored
Normal file
55
vendor/github.com/coreos/pkg/yamlutil/yaml.go
generated
vendored
Normal file
|
@ -0,0 +1,55 @@
|
|||
package yamlutil
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"gopkg.in/yaml.v1"
|
||||
)
|
||||
|
||||
// SetFlagsFromYaml goes through all registered flags in the given flagset,
|
||||
// and if they are not already set it attempts to set their values from
|
||||
// the YAML config. It will use the key REPLACE(UPPERCASE(flagname), '-', '_')
|
||||
func SetFlagsFromYaml(fs *flag.FlagSet, rawYaml []byte) (err error) {
|
||||
conf := make(map[string]string)
|
||||
if err = yaml.Unmarshal(rawYaml, conf); err != nil {
|
||||
return
|
||||
}
|
||||
alreadySet := map[string]struct{}{}
|
||||
fs.Visit(func(f *flag.Flag) {
|
||||
alreadySet[f.Name] = struct{}{}
|
||||
})
|
||||
|
||||
errs := make([]error, 0)
|
||||
fs.VisitAll(func(f *flag.Flag) {
|
||||
if f.Name == "" {
|
||||
return
|
||||
}
|
||||
if _, ok := alreadySet[f.Name]; ok {
|
||||
return
|
||||
}
|
||||
tag := strings.Replace(strings.ToUpper(f.Name), "-", "_", -1)
|
||||
val, ok := conf[tag]
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
if serr := fs.Set(f.Name, val); serr != nil {
|
||||
errs = append(errs, fmt.Errorf("invalid value %q for %s: %v", val, tag, serr))
|
||||
}
|
||||
})
|
||||
if len(errs) != 0 {
|
||||
err = ErrorSlice(errs)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
type ErrorSlice []error
|
||||
|
||||
func (e ErrorSlice) Error() string {
|
||||
s := ""
|
||||
for _, err := range e {
|
||||
s += ", " + err.Error()
|
||||
}
|
||||
return "Errors: " + s
|
||||
}
|
80
vendor/github.com/coreos/pkg/yamlutil/yaml_test.go
generated
vendored
Normal file
80
vendor/github.com/coreos/pkg/yamlutil/yaml_test.go
generated
vendored
Normal file
|
@ -0,0 +1,80 @@
|
|||
package yamlutil
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestSetFlagsFromYaml(t *testing.T) {
|
||||
config := "A: foo\nC: woof"
|
||||
fs := flag.NewFlagSet("testing", flag.ExitOnError)
|
||||
fs.String("a", "", "")
|
||||
fs.String("b", "", "")
|
||||
fs.String("c", "", "")
|
||||
fs.Parse([]string{})
|
||||
|
||||
// flags should be settable using yaml vars
|
||||
// and command-line flags
|
||||
if err := fs.Set("b", "bar"); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
// command-line flags take precedence over the file
|
||||
if err := fs.Set("c", "quack"); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// first verify that flags are as expected before reading the file
|
||||
for f, want := range map[string]string{
|
||||
"a": "",
|
||||
"b": "bar",
|
||||
"c": "quack",
|
||||
} {
|
||||
if got := fs.Lookup(f).Value.String(); got != want {
|
||||
t.Fatalf("flag %q=%q, want %q", f, got, want)
|
||||
}
|
||||
}
|
||||
|
||||
// now read the yaml and verify flags were updated as expected
|
||||
err := SetFlagsFromYaml(fs, []byte(config))
|
||||
if err != nil {
|
||||
t.Errorf("err=%v, want nil", err)
|
||||
}
|
||||
for f, want := range map[string]string{
|
||||
"a": "foo",
|
||||
"b": "bar",
|
||||
"c": "quack",
|
||||
} {
|
||||
if got := fs.Lookup(f).Value.String(); got != want {
|
||||
t.Errorf("flag %q=%q, want %q", f, got, want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestSetFlagsFromYamlBad(t *testing.T) {
|
||||
// now verify that an error is propagated
|
||||
fs := flag.NewFlagSet("testing", flag.ExitOnError)
|
||||
fs.Int("x", 0, "")
|
||||
badConf := "X: not_a_number"
|
||||
if err := SetFlagsFromYaml(fs, []byte(badConf)); err == nil {
|
||||
t.Errorf("got err=nil, flag x=%q, want err != nil", fs.Lookup("x").Value.String())
|
||||
}
|
||||
}
|
||||
|
||||
func TestSetFlagsFromYamlMultiError(t *testing.T) {
|
||||
fs := flag.NewFlagSet("testing", flag.ExitOnError)
|
||||
fs.Int("x", 0, "")
|
||||
fs.Int("y", 0, "")
|
||||
fs.Int("z", 0, "")
|
||||
conf := "X: foo\nY: bar\nZ: 3"
|
||||
err := SetFlagsFromYaml(fs, []byte(conf))
|
||||
if err == nil {
|
||||
t.Errorf("got err= nil, want err != nil")
|
||||
}
|
||||
es, ok := err.(ErrorSlice)
|
||||
if !ok {
|
||||
t.Errorf("Got ok=false want ok=true")
|
||||
}
|
||||
if len(es) != 2 {
|
||||
t.Errorf("2 errors should be contained in the error, got %d errors", len(es))
|
||||
}
|
||||
}
|
2340
vendor/github.com/go-gorp/gorp/gorp_test.go
generated
vendored
Normal file
2340
vendor/github.com/go-gorp/gorp/gorp_test.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load diff
0
vendor/github.com/go-gorp/gorp/test_all.sh
generated
vendored
Normal file → Executable file
0
vendor/github.com/go-gorp/gorp/test_all.sh
generated
vendored
Normal file → Executable file
73
vendor/github.com/gorilla/handlers/canonical_test.go
generated
vendored
Normal file
73
vendor/github.com/gorilla/handlers/canonical_test.go
generated
vendored
Normal file
|
@ -0,0 +1,73 @@
|
|||
package handlers
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestCleanHost(t *testing.T) {
|
||||
tests := []struct {
|
||||
in, want string
|
||||
}{
|
||||
{"www.google.com", "www.google.com"},
|
||||
{"www.google.com foo", "www.google.com"},
|
||||
{"www.google.com/foo", "www.google.com"},
|
||||
{" first character is a space", ""},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
got := cleanHost(tt.in)
|
||||
if tt.want != got {
|
||||
t.Errorf("cleanHost(%q) = %q, want %q", tt.in, got, tt.want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestCanonicalHost(t *testing.T) {
|
||||
gorilla := "http://www.gorillatoolkit.org"
|
||||
|
||||
rr := httptest.NewRecorder()
|
||||
r := newRequest("GET", "http://www.example.com/")
|
||||
|
||||
testHandler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {})
|
||||
|
||||
// Test a re-direct: should return a 302 Found.
|
||||
CanonicalHost(gorilla, http.StatusFound)(testHandler).ServeHTTP(rr, r)
|
||||
|
||||
if rr.Code != http.StatusFound {
|
||||
t.Fatalf("bad status: got %v want %v", rr.Code, http.StatusFound)
|
||||
}
|
||||
|
||||
if rr.Header().Get("Location") != gorilla+r.URL.Path {
|
||||
t.Fatalf("bad re-direct: got %q want %q", rr.Header().Get("Location"), gorilla+r.URL.Path)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestBadDomain(t *testing.T) {
|
||||
rr := httptest.NewRecorder()
|
||||
r := newRequest("GET", "http://www.example.com/")
|
||||
|
||||
testHandler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {})
|
||||
|
||||
// Test a bad domain - should return 200 OK.
|
||||
CanonicalHost("%", http.StatusFound)(testHandler).ServeHTTP(rr, r)
|
||||
|
||||
if rr.Code != http.StatusOK {
|
||||
t.Fatalf("bad status: got %v want %v", rr.Code, http.StatusOK)
|
||||
}
|
||||
}
|
||||
|
||||
func TestEmptyHost(t *testing.T) {
|
||||
rr := httptest.NewRecorder()
|
||||
r := newRequest("GET", "http://www.example.com/")
|
||||
|
||||
testHandler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {})
|
||||
|
||||
// Test a domain that returns an empty url.Host from url.Parse.
|
||||
CanonicalHost("hello.com", http.StatusFound)(testHandler).ServeHTTP(rr, r)
|
||||
|
||||
if rr.Code != http.StatusOK {
|
||||
t.Fatalf("bad status: got %v want %v", rr.Code, http.StatusOK)
|
||||
}
|
||||
}
|
65
vendor/github.com/gorilla/handlers/compress_test.go
generated
vendored
Normal file
65
vendor/github.com/gorilla/handlers/compress_test.go
generated
vendored
Normal file
|
@ -0,0 +1,65 @@
|
|||
// Copyright 2013 The Gorilla Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"io"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func compressedRequest(w *httptest.ResponseRecorder, compression string) {
|
||||
CompressHandler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
for i := 0; i < 1024; i++ {
|
||||
io.WriteString(w, "Gorilla!\n")
|
||||
}
|
||||
})).ServeHTTP(w, &http.Request{
|
||||
Method: "GET",
|
||||
Header: http.Header{
|
||||
"Accept-Encoding": []string{compression},
|
||||
},
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
func TestCompressHandlerGzip(t *testing.T) {
|
||||
w := httptest.NewRecorder()
|
||||
compressedRequest(w, "gzip")
|
||||
if w.HeaderMap.Get("Content-Encoding") != "gzip" {
|
||||
t.Fatalf("wrong content encoding, got %d want %d", w.HeaderMap.Get("Content-Encoding"), "gzip")
|
||||
}
|
||||
if w.HeaderMap.Get("Content-Type") != "text/plain; charset=utf-8" {
|
||||
t.Fatalf("wrong content type, got %s want %s", w.HeaderMap.Get("Content-Type"), "text/plain; charset=utf-8")
|
||||
}
|
||||
if w.Body.Len() != 72 {
|
||||
t.Fatalf("wrong len, got %d want %d", w.Body.Len(), 72)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCompressHandlerDeflate(t *testing.T) {
|
||||
w := httptest.NewRecorder()
|
||||
compressedRequest(w, "deflate")
|
||||
if w.HeaderMap.Get("Content-Encoding") != "deflate" {
|
||||
t.Fatalf("wrong content encoding, got %d want %d", w.HeaderMap.Get("Content-Encoding"), "deflate")
|
||||
}
|
||||
if w.HeaderMap.Get("Content-Type") != "text/plain; charset=utf-8" {
|
||||
t.Fatalf("wrong content type, got %s want %s", w.HeaderMap.Get("Content-Type"), "text/plain; charset=utf-8")
|
||||
}
|
||||
if w.Body.Len() != 54 {
|
||||
t.Fatalf("wrong len, got %d want %d", w.Body.Len(), 54)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCompressHandlerGzipDeflate(t *testing.T) {
|
||||
w := httptest.NewRecorder()
|
||||
compressedRequest(w, "gzip, deflate ")
|
||||
if w.HeaderMap.Get("Content-Encoding") != "gzip" {
|
||||
t.Fatalf("wrong content encoding, got %s want %s", w.HeaderMap.Get("Content-Encoding"), "gzip")
|
||||
}
|
||||
if w.HeaderMap.Get("Content-Type") != "text/plain; charset=utf-8" {
|
||||
t.Fatalf("wrong content type, got %s want %s", w.HeaderMap.Get("Content-Type"), "text/plain; charset=utf-8")
|
||||
}
|
||||
}
|
305
vendor/github.com/gorilla/handlers/handlers_test.go
generated
vendored
Normal file
305
vendor/github.com/gorilla/handlers/handlers_test.go
generated
vendored
Normal file
|
@ -0,0 +1,305 @@
|
|||
// Copyright 2013 The Gorilla Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"net/url"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
ok = "ok\n"
|
||||
notAllowed = "Method not allowed\n"
|
||||
)
|
||||
|
||||
var okHandler = http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
|
||||
w.Write([]byte(ok))
|
||||
})
|
||||
|
||||
func newRequest(method, url string) *http.Request {
|
||||
req, err := http.NewRequest(method, url, nil)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return req
|
||||
}
|
||||
|
||||
func TestMethodHandler(t *testing.T) {
|
||||
tests := []struct {
|
||||
req *http.Request
|
||||
handler http.Handler
|
||||
code int
|
||||
allow string // Contents of the Allow header
|
||||
body string
|
||||
}{
|
||||
// No handlers
|
||||
{newRequest("GET", "/foo"), MethodHandler{}, http.StatusMethodNotAllowed, "", notAllowed},
|
||||
{newRequest("OPTIONS", "/foo"), MethodHandler{}, http.StatusOK, "", ""},
|
||||
|
||||
// A single handler
|
||||
{newRequest("GET", "/foo"), MethodHandler{"GET": okHandler}, http.StatusOK, "", ok},
|
||||
{newRequest("POST", "/foo"), MethodHandler{"GET": okHandler}, http.StatusMethodNotAllowed, "GET", notAllowed},
|
||||
|
||||
// Multiple handlers
|
||||
{newRequest("GET", "/foo"), MethodHandler{"GET": okHandler, "POST": okHandler}, http.StatusOK, "", ok},
|
||||
{newRequest("POST", "/foo"), MethodHandler{"GET": okHandler, "POST": okHandler}, http.StatusOK, "", ok},
|
||||
{newRequest("DELETE", "/foo"), MethodHandler{"GET": okHandler, "POST": okHandler}, http.StatusMethodNotAllowed, "GET, POST", notAllowed},
|
||||
{newRequest("OPTIONS", "/foo"), MethodHandler{"GET": okHandler, "POST": okHandler}, http.StatusOK, "GET, POST", ""},
|
||||
|
||||
// Override OPTIONS
|
||||
{newRequest("OPTIONS", "/foo"), MethodHandler{"OPTIONS": okHandler}, http.StatusOK, "", ok},
|
||||
}
|
||||
|
||||
for i, test := range tests {
|
||||
rec := httptest.NewRecorder()
|
||||
test.handler.ServeHTTP(rec, test.req)
|
||||
if rec.Code != test.code {
|
||||
t.Fatalf("%d: wrong code, got %d want %d", i, rec.Code, test.code)
|
||||
}
|
||||
if allow := rec.HeaderMap.Get("Allow"); allow != test.allow {
|
||||
t.Fatalf("%d: wrong Allow, got %s want %s", i, allow, test.allow)
|
||||
}
|
||||
if body := rec.Body.String(); body != test.body {
|
||||
t.Fatalf("%d: wrong body, got %q want %q", i, body, test.body)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestWriteLog(t *testing.T) {
|
||||
loc, err := time.LoadLocation("Europe/Warsaw")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
ts := time.Date(1983, 05, 26, 3, 30, 45, 0, loc)
|
||||
|
||||
// A typical request with an OK response
|
||||
req := newRequest("GET", "http://example.com")
|
||||
req.RemoteAddr = "192.168.100.5"
|
||||
|
||||
buf := new(bytes.Buffer)
|
||||
writeLog(buf, req, *req.URL, ts, http.StatusOK, 100)
|
||||
log := buf.String()
|
||||
|
||||
expected := "192.168.100.5 - - [26/May/1983:03:30:45 +0200] \"GET / HTTP/1.1\" 200 100\n"
|
||||
if log != expected {
|
||||
t.Fatalf("wrong log, got %q want %q", log, expected)
|
||||
}
|
||||
|
||||
// Request with an unauthorized user
|
||||
req = newRequest("GET", "http://example.com")
|
||||
req.RemoteAddr = "192.168.100.5"
|
||||
req.URL.User = url.User("kamil")
|
||||
|
||||
buf.Reset()
|
||||
writeLog(buf, req, *req.URL, ts, http.StatusUnauthorized, 500)
|
||||
log = buf.String()
|
||||
|
||||
expected = "192.168.100.5 - kamil [26/May/1983:03:30:45 +0200] \"GET / HTTP/1.1\" 401 500\n"
|
||||
if log != expected {
|
||||
t.Fatalf("wrong log, got %q want %q", log, expected)
|
||||
}
|
||||
|
||||
// Request with url encoded parameters
|
||||
req = newRequest("GET", "http://example.com/test?abc=hello%20world&a=b%3F")
|
||||
req.RemoteAddr = "192.168.100.5"
|
||||
|
||||
buf.Reset()
|
||||
writeLog(buf, req, *req.URL, ts, http.StatusOK, 100)
|
||||
log = buf.String()
|
||||
|
||||
expected = "192.168.100.5 - - [26/May/1983:03:30:45 +0200] \"GET /test?abc=hello%20world&a=b%3F HTTP/1.1\" 200 100\n"
|
||||
if log != expected {
|
||||
t.Fatalf("wrong log, got %q want %q", log, expected)
|
||||
}
|
||||
}
|
||||
|
||||
func TestWriteCombinedLog(t *testing.T) {
|
||||
loc, err := time.LoadLocation("Europe/Warsaw")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
ts := time.Date(1983, 05, 26, 3, 30, 45, 0, loc)
|
||||
|
||||
// A typical request with an OK response
|
||||
req := newRequest("GET", "http://example.com")
|
||||
req.RemoteAddr = "192.168.100.5"
|
||||
req.Header.Set("Referer", "http://example.com")
|
||||
req.Header.Set(
|
||||
"User-Agent",
|
||||
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_2) AppleWebKit/537.33 "+
|
||||
"(KHTML, like Gecko) Chrome/27.0.1430.0 Safari/537.33",
|
||||
)
|
||||
|
||||
buf := new(bytes.Buffer)
|
||||
writeCombinedLog(buf, req, *req.URL, ts, http.StatusOK, 100)
|
||||
log := buf.String()
|
||||
|
||||
expected := "192.168.100.5 - - [26/May/1983:03:30:45 +0200] \"GET / HTTP/1.1\" 200 100 \"http://example.com\" " +
|
||||
"\"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_2) " +
|
||||
"AppleWebKit/537.33 (KHTML, like Gecko) Chrome/27.0.1430.0 Safari/537.33\"\n"
|
||||
if log != expected {
|
||||
t.Fatalf("wrong log, got %q want %q", log, expected)
|
||||
}
|
||||
|
||||
// Request with an unauthorized user
|
||||
req.URL.User = url.User("kamil")
|
||||
|
||||
buf.Reset()
|
||||
writeCombinedLog(buf, req, *req.URL, ts, http.StatusUnauthorized, 500)
|
||||
log = buf.String()
|
||||
|
||||
expected = "192.168.100.5 - kamil [26/May/1983:03:30:45 +0200] \"GET / HTTP/1.1\" 401 500 \"http://example.com\" " +
|
||||
"\"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_2) " +
|
||||
"AppleWebKit/537.33 (KHTML, like Gecko) Chrome/27.0.1430.0 Safari/537.33\"\n"
|
||||
if log != expected {
|
||||
t.Fatalf("wrong log, got %q want %q", log, expected)
|
||||
}
|
||||
|
||||
// Test with remote ipv6 address
|
||||
req.RemoteAddr = "::1"
|
||||
|
||||
buf.Reset()
|
||||
writeCombinedLog(buf, req, *req.URL, ts, http.StatusOK, 100)
|
||||
log = buf.String()
|
||||
|
||||
expected = "::1 - kamil [26/May/1983:03:30:45 +0200] \"GET / HTTP/1.1\" 200 100 \"http://example.com\" " +
|
||||
"\"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_2) " +
|
||||
"AppleWebKit/537.33 (KHTML, like Gecko) Chrome/27.0.1430.0 Safari/537.33\"\n"
|
||||
if log != expected {
|
||||
t.Fatalf("wrong log, got %q want %q", log, expected)
|
||||
}
|
||||
|
||||
// Test remote ipv6 addr, with port
|
||||
req.RemoteAddr = net.JoinHostPort("::1", "65000")
|
||||
|
||||
buf.Reset()
|
||||
writeCombinedLog(buf, req, *req.URL, ts, http.StatusOK, 100)
|
||||
log = buf.String()
|
||||
|
||||
expected = "::1 - kamil [26/May/1983:03:30:45 +0200] \"GET / HTTP/1.1\" 200 100 \"http://example.com\" " +
|
||||
"\"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_2) " +
|
||||
"AppleWebKit/537.33 (KHTML, like Gecko) Chrome/27.0.1430.0 Safari/537.33\"\n"
|
||||
if log != expected {
|
||||
t.Fatalf("wrong log, got %q want %q", log, expected)
|
||||
}
|
||||
}
|
||||
|
||||
func TestLogPathRewrites(t *testing.T) {
|
||||
var buf bytes.Buffer
|
||||
|
||||
handler := http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
|
||||
req.URL.Path = "/" // simulate http.StripPrefix and friends
|
||||
w.WriteHeader(200)
|
||||
})
|
||||
logger := LoggingHandler(&buf, handler)
|
||||
|
||||
logger.ServeHTTP(httptest.NewRecorder(), newRequest("GET", "/subdir/asdf"))
|
||||
|
||||
if !strings.Contains(buf.String(), "GET /subdir/asdf HTTP") {
|
||||
t.Fatalf("Got log %#v, wanted substring %#v", buf.String(), "GET /subdir/asdf HTTP")
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkWriteLog(b *testing.B) {
|
||||
loc, err := time.LoadLocation("Europe/Warsaw")
|
||||
if err != nil {
|
||||
b.Fatalf(err.Error())
|
||||
}
|
||||
ts := time.Date(1983, 05, 26, 3, 30, 45, 0, loc)
|
||||
|
||||
req := newRequest("GET", "http://example.com")
|
||||
req.RemoteAddr = "192.168.100.5"
|
||||
|
||||
b.ResetTimer()
|
||||
|
||||
buf := &bytes.Buffer{}
|
||||
for i := 0; i < b.N; i++ {
|
||||
buf.Reset()
|
||||
writeLog(buf, req, *req.URL, ts, http.StatusUnauthorized, 500)
|
||||
}
|
||||
}
|
||||
|
||||
func TestContentTypeHandler(t *testing.T) {
|
||||
tests := []struct {
|
||||
Method string
|
||||
AllowContentTypes []string
|
||||
ContentType string
|
||||
Code int
|
||||
}{
|
||||
{"POST", []string{"application/json"}, "application/json", http.StatusOK},
|
||||
{"POST", []string{"application/json", "application/xml"}, "application/json", http.StatusOK},
|
||||
{"POST", []string{"application/json"}, "application/json; charset=utf-8", http.StatusOK},
|
||||
{"POST", []string{"application/json"}, "application/json+xxx", http.StatusUnsupportedMediaType},
|
||||
{"POST", []string{"application/json"}, "text/plain", http.StatusUnsupportedMediaType},
|
||||
{"GET", []string{"application/json"}, "", http.StatusOK},
|
||||
{"GET", []string{}, "", http.StatusOK},
|
||||
}
|
||||
for _, test := range tests {
|
||||
r, err := http.NewRequest(test.Method, "/", nil)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
continue
|
||||
}
|
||||
|
||||
h := ContentTypeHandler(okHandler, test.AllowContentTypes...)
|
||||
r.Header.Set("Content-Type", test.ContentType)
|
||||
w := httptest.NewRecorder()
|
||||
h.ServeHTTP(w, r)
|
||||
if w.Code != test.Code {
|
||||
t.Errorf("expected %d, got %d", test.Code, w.Code)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestHTTPMethodOverride(t *testing.T) {
|
||||
var tests = []struct {
|
||||
Method string
|
||||
OverrideMethod string
|
||||
ExpectedMethod string
|
||||
}{
|
||||
{"POST", "PUT", "PUT"},
|
||||
{"POST", "PATCH", "PATCH"},
|
||||
{"POST", "DELETE", "DELETE"},
|
||||
{"PUT", "DELETE", "PUT"},
|
||||
{"GET", "GET", "GET"},
|
||||
{"HEAD", "HEAD", "HEAD"},
|
||||
{"GET", "PUT", "GET"},
|
||||
{"HEAD", "DELETE", "HEAD"},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
h := HTTPMethodOverrideHandler(okHandler)
|
||||
reqs := make([]*http.Request, 0, 2)
|
||||
|
||||
rHeader, err := http.NewRequest(test.Method, "/", nil)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
rHeader.Header.Set(HTTPMethodOverrideHeader, test.OverrideMethod)
|
||||
reqs = append(reqs, rHeader)
|
||||
|
||||
f := url.Values{HTTPMethodOverrideFormKey: []string{test.OverrideMethod}}
|
||||
rForm, err := http.NewRequest(test.Method, "/", strings.NewReader(f.Encode()))
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
rForm.Header.Set("Content-Type", "application/x-www-form-urlencoded")
|
||||
reqs = append(reqs, rForm)
|
||||
|
||||
for _, r := range reqs {
|
||||
w := httptest.NewRecorder()
|
||||
h.ServeHTTP(w, r)
|
||||
if r.Method != test.ExpectedMethod {
|
||||
t.Errorf("Expected %s, got %s", test.ExpectedMethod, r.Method)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
100
vendor/github.com/gorilla/handlers/proxy_headers_test.go
generated
vendored
Normal file
100
vendor/github.com/gorilla/handlers/proxy_headers_test.go
generated
vendored
Normal file
|
@ -0,0 +1,100 @@
|
|||
package handlers
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
)
|
||||
|
||||
type headerTable struct {
|
||||
key string // header key
|
||||
val string // header val
|
||||
expected string // expected result
|
||||
}
|
||||
|
||||
func TestGetIP(t *testing.T) {
|
||||
headers := []headerTable{
|
||||
{xForwardedFor, "8.8.8.8", "8.8.8.8"}, // Single address
|
||||
{xForwardedFor, "8.8.8.8, 8.8.4.4", "8.8.8.8"}, // Multiple
|
||||
{xForwardedFor, "[2001:db8:cafe::17]:4711", "[2001:db8:cafe::17]:4711"}, // IPv6 address
|
||||
{xForwardedFor, "", ""}, // None
|
||||
{xRealIP, "8.8.8.8", "8.8.8.8"}, // Single address
|
||||
{xRealIP, "8.8.8.8, 8.8.4.4", "8.8.8.8, 8.8.4.4"}, // Multiple
|
||||
{xRealIP, "[2001:db8:cafe::17]:4711", "[2001:db8:cafe::17]:4711"}, // IPv6 address
|
||||
{xRealIP, "", ""}, // None
|
||||
{forwarded, `for="_gazonk"`, "_gazonk"}, // Hostname
|
||||
{forwarded, `For="[2001:db8:cafe::17]:4711`, `[2001:db8:cafe::17]:4711`}, // IPv6 address
|
||||
{forwarded, `for=192.0.2.60;proto=http;by=203.0.113.43`, `192.0.2.60`}, // Multiple params
|
||||
{forwarded, `for=192.0.2.43, for=198.51.100.17`, "192.0.2.43"}, // Multiple params
|
||||
{forwarded, `for="workstation.local",for=198.51.100.17`, "workstation.local"}, // Hostname
|
||||
}
|
||||
|
||||
for _, v := range headers {
|
||||
req := &http.Request{
|
||||
Header: http.Header{
|
||||
v.key: []string{v.val},
|
||||
}}
|
||||
res := getIP(req)
|
||||
if res != v.expected {
|
||||
t.Fatalf("wrong header for %s: got %s want %s", v.key, res,
|
||||
v.expected)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetScheme(t *testing.T) {
|
||||
headers := []headerTable{
|
||||
{xForwardedProto, "https", "https"},
|
||||
{xForwardedProto, "http", "http"},
|
||||
{xForwardedProto, "HTTP", "http"},
|
||||
{forwarded, `For="[2001:db8:cafe::17]:4711`, ""}, // No proto
|
||||
{forwarded, `for=192.0.2.43, for=198.51.100.17;proto=https`, "https"}, // Multiple params before proto
|
||||
{forwarded, `for=172.32.10.15; proto=https;by=127.0.0.1`, "https"}, // Space before proto
|
||||
{forwarded, `for=192.0.2.60;proto=http;by=203.0.113.43`, "http"}, // Multiple params
|
||||
}
|
||||
|
||||
for _, v := range headers {
|
||||
req := &http.Request{
|
||||
Header: http.Header{
|
||||
v.key: []string{v.val},
|
||||
},
|
||||
}
|
||||
res := getScheme(req)
|
||||
if res != v.expected {
|
||||
t.Fatalf("wrong header for %s: got %s want %s", v.key, res,
|
||||
v.expected)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Test the middleware end-to-end
|
||||
func TestProxyHeaders(t *testing.T) {
|
||||
rr := httptest.NewRecorder()
|
||||
r := newRequest("GET", "/")
|
||||
|
||||
r.Header.Set(xForwardedFor, "8.8.8.8")
|
||||
r.Header.Set(xForwardedProto, "https")
|
||||
|
||||
var addr string
|
||||
var proto string
|
||||
ProxyHeaders(http.HandlerFunc(
|
||||
func(w http.ResponseWriter, r *http.Request) {
|
||||
addr = r.RemoteAddr
|
||||
proto = r.URL.Scheme
|
||||
})).ServeHTTP(rr, r)
|
||||
|
||||
if rr.Code != http.StatusOK {
|
||||
t.Fatalf("bad status: got %d want %d", rr.Code, http.StatusOK)
|
||||
}
|
||||
|
||||
if addr != r.Header.Get(xForwardedFor) {
|
||||
t.Fatalf("wrong address: got %s want %s", addr,
|
||||
r.Header.Get(xForwardedFor))
|
||||
}
|
||||
|
||||
if proto != r.Header.Get(xForwardedProto) {
|
||||
t.Fatalf("wrong address: got %s want %s", proto,
|
||||
r.Header.Get(xForwardedProto))
|
||||
}
|
||||
|
||||
}
|
120
vendor/github.com/jonboulle/clockwork/clockwork_test.go
generated
vendored
Normal file
120
vendor/github.com/jonboulle/clockwork/clockwork_test.go
generated
vendored
Normal file
|
@ -0,0 +1,120 @@
|
|||
package clockwork
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestFakeClockAfter(t *testing.T) {
|
||||
fc := &fakeClock{}
|
||||
|
||||
zero := fc.After(0)
|
||||
select {
|
||||
case <-zero:
|
||||
default:
|
||||
t.Errorf("zero did not return!")
|
||||
}
|
||||
one := fc.After(1)
|
||||
two := fc.After(2)
|
||||
six := fc.After(6)
|
||||
ten := fc.After(10)
|
||||
fc.Advance(1)
|
||||
select {
|
||||
case <-one:
|
||||
default:
|
||||
t.Errorf("one did not return!")
|
||||
}
|
||||
select {
|
||||
case <-two:
|
||||
t.Errorf("two returned prematurely!")
|
||||
case <-six:
|
||||
t.Errorf("six returned prematurely!")
|
||||
case <-ten:
|
||||
t.Errorf("ten returned prematurely!")
|
||||
default:
|
||||
}
|
||||
fc.Advance(1)
|
||||
select {
|
||||
case <-two:
|
||||
default:
|
||||
t.Errorf("two did not return!")
|
||||
}
|
||||
select {
|
||||
case <-six:
|
||||
t.Errorf("six returned prematurely!")
|
||||
case <-ten:
|
||||
t.Errorf("ten returned prematurely!")
|
||||
default:
|
||||
}
|
||||
fc.Advance(1)
|
||||
select {
|
||||
case <-six:
|
||||
t.Errorf("six returned prematurely!")
|
||||
case <-ten:
|
||||
t.Errorf("ten returned prematurely!")
|
||||
default:
|
||||
}
|
||||
fc.Advance(3)
|
||||
select {
|
||||
case <-six:
|
||||
default:
|
||||
t.Errorf("six did not return!")
|
||||
}
|
||||
select {
|
||||
case <-ten:
|
||||
t.Errorf("ten returned prematurely!")
|
||||
default:
|
||||
}
|
||||
fc.Advance(100)
|
||||
select {
|
||||
case <-ten:
|
||||
default:
|
||||
t.Errorf("ten did not return!")
|
||||
}
|
||||
}
|
||||
|
||||
func TestNotifyBlockers(t *testing.T) {
|
||||
b1 := &blocker{1, make(chan struct{})}
|
||||
b2 := &blocker{2, make(chan struct{})}
|
||||
b3 := &blocker{5, make(chan struct{})}
|
||||
b4 := &blocker{10, make(chan struct{})}
|
||||
b5 := &blocker{10, make(chan struct{})}
|
||||
bs := []*blocker{b1, b2, b3, b4, b5}
|
||||
bs1 := notifyBlockers(bs, 2)
|
||||
if n := len(bs1); n != 4 {
|
||||
t.Fatalf("got %d blockers, want %d", n, 4)
|
||||
}
|
||||
select {
|
||||
case <-b2.ch:
|
||||
case <-time.After(time.Second):
|
||||
t.Fatalf("timed out waiting for channel close!")
|
||||
}
|
||||
bs2 := notifyBlockers(bs1, 10)
|
||||
if n := len(bs2); n != 2 {
|
||||
t.Fatalf("got %d blockers, want %d", n, 2)
|
||||
}
|
||||
select {
|
||||
case <-b4.ch:
|
||||
case <-time.After(time.Second):
|
||||
t.Fatalf("timed out waiting for channel close!")
|
||||
}
|
||||
select {
|
||||
case <-b5.ch:
|
||||
case <-time.After(time.Second):
|
||||
t.Fatalf("timed out waiting for channel close!")
|
||||
}
|
||||
}
|
||||
|
||||
func TestNewFakeClock(t *testing.T) {
|
||||
fc := NewFakeClock()
|
||||
now := fc.Now()
|
||||
if now.IsZero() {
|
||||
t.Fatalf("fakeClock.Now() fulfills IsZero")
|
||||
}
|
||||
|
||||
now2 := fc.Now()
|
||||
if !reflect.DeepEqual(now, now2) {
|
||||
t.Fatalf("fakeClock.Now() returned different value: want=%#v got=%#v", now, now2)
|
||||
}
|
||||
}
|
49
vendor/github.com/jonboulle/clockwork/example_test.go
generated
vendored
Normal file
49
vendor/github.com/jonboulle/clockwork/example_test.go
generated
vendored
Normal file
|
@ -0,0 +1,49 @@
|
|||
package clockwork
|
||||
|
||||
import (
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
// my_func is an example of a time-dependent function, using an
|
||||
// injected clock
|
||||
func my_func(clock Clock, i *int) {
|
||||
clock.Sleep(3 * time.Second)
|
||||
*i += 1
|
||||
}
|
||||
|
||||
// assert_state is an example of a state assertion in a test
|
||||
func assert_state(t *testing.T, i, j int) {
|
||||
if i != j {
|
||||
t.Fatalf("i %d, j %d", i, j)
|
||||
}
|
||||
}
|
||||
|
||||
// TestMyFunc tests my_func's behaviour with a FakeClock
|
||||
func TestMyFunc(t *testing.T) {
|
||||
var i int
|
||||
c := NewFakeClock()
|
||||
|
||||
var wg sync.WaitGroup
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
my_func(c, &i)
|
||||
wg.Done()
|
||||
}()
|
||||
|
||||
// Wait until my_func is actually sleeping on the clock
|
||||
c.BlockUntil(1)
|
||||
|
||||
// Assert the initial state
|
||||
assert_state(t, i, 0)
|
||||
|
||||
// Now advance the clock forward in time
|
||||
c.Advance(1 * time.Hour)
|
||||
|
||||
// Wait until the function completes
|
||||
wg.Wait()
|
||||
|
||||
// Assert the final state
|
||||
assert_state(t, i, 1)
|
||||
}
|
92
vendor/github.com/julienschmidt/httprouter/path_test.go
generated
vendored
Normal file
92
vendor/github.com/julienschmidt/httprouter/path_test.go
generated
vendored
Normal file
|
@ -0,0 +1,92 @@
|
|||
// Copyright 2013 Julien Schmidt. All rights reserved.
|
||||
// Based on the path package, Copyright 2009 The Go Authors.
|
||||
// Use of this source code is governed by a BSD-style license that can be found
|
||||
// in the LICENSE file.
|
||||
|
||||
package httprouter
|
||||
|
||||
import (
|
||||
"runtime"
|
||||
"testing"
|
||||
)
|
||||
|
||||
var cleanTests = []struct {
|
||||
path, result string
|
||||
}{
|
||||
// Already clean
|
||||
{"/", "/"},
|
||||
{"/abc", "/abc"},
|
||||
{"/a/b/c", "/a/b/c"},
|
||||
{"/abc/", "/abc/"},
|
||||
{"/a/b/c/", "/a/b/c/"},
|
||||
|
||||
// missing root
|
||||
{"", "/"},
|
||||
{"abc", "/abc"},
|
||||
{"abc/def", "/abc/def"},
|
||||
{"a/b/c", "/a/b/c"},
|
||||
|
||||
// Remove doubled slash
|
||||
{"//", "/"},
|
||||
{"/abc//", "/abc/"},
|
||||
{"/abc/def//", "/abc/def/"},
|
||||
{"/a/b/c//", "/a/b/c/"},
|
||||
{"/abc//def//ghi", "/abc/def/ghi"},
|
||||
{"//abc", "/abc"},
|
||||
{"///abc", "/abc"},
|
||||
{"//abc//", "/abc/"},
|
||||
|
||||
// Remove . elements
|
||||
{".", "/"},
|
||||
{"./", "/"},
|
||||
{"/abc/./def", "/abc/def"},
|
||||
{"/./abc/def", "/abc/def"},
|
||||
{"/abc/.", "/abc/"},
|
||||
|
||||
// Remove .. elements
|
||||
{"..", "/"},
|
||||
{"../", "/"},
|
||||
{"../../", "/"},
|
||||
{"../..", "/"},
|
||||
{"../../abc", "/abc"},
|
||||
{"/abc/def/ghi/../jkl", "/abc/def/jkl"},
|
||||
{"/abc/def/../ghi/../jkl", "/abc/jkl"},
|
||||
{"/abc/def/..", "/abc"},
|
||||
{"/abc/def/../..", "/"},
|
||||
{"/abc/def/../../..", "/"},
|
||||
{"/abc/def/../../..", "/"},
|
||||
{"/abc/def/../../../ghi/jkl/../../../mno", "/mno"},
|
||||
|
||||
// Combinations
|
||||
{"abc/./../def", "/def"},
|
||||
{"abc//./../def", "/def"},
|
||||
{"abc/../../././../def", "/def"},
|
||||
}
|
||||
|
||||
func TestPathClean(t *testing.T) {
|
||||
for _, test := range cleanTests {
|
||||
if s := CleanPath(test.path); s != test.result {
|
||||
t.Errorf("CleanPath(%q) = %q, want %q", test.path, s, test.result)
|
||||
}
|
||||
if s := CleanPath(test.result); s != test.result {
|
||||
t.Errorf("CleanPath(%q) = %q, want %q", test.result, s, test.result)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestPathCleanMallocs(t *testing.T) {
|
||||
if testing.Short() {
|
||||
t.Skip("skipping malloc count in short mode")
|
||||
}
|
||||
if runtime.GOMAXPROCS(0) > 1 {
|
||||
t.Log("skipping AllocsPerRun checks; GOMAXPROCS>1")
|
||||
return
|
||||
}
|
||||
|
||||
for _, test := range cleanTests {
|
||||
allocs := testing.AllocsPerRun(100, func() { CleanPath(test.result) })
|
||||
if allocs > 0 {
|
||||
t.Errorf("CleanPath(%q): %v allocs, want zero", test.result, allocs)
|
||||
}
|
||||
}
|
||||
}
|
378
vendor/github.com/julienschmidt/httprouter/router_test.go
generated
vendored
Normal file
378
vendor/github.com/julienschmidt/httprouter/router_test.go
generated
vendored
Normal file
|
@ -0,0 +1,378 @@
|
|||
// Copyright 2013 Julien Schmidt. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be found
|
||||
// in the LICENSE file.
|
||||
|
||||
package httprouter
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
type mockResponseWriter struct{}
|
||||
|
||||
func (m *mockResponseWriter) Header() (h http.Header) {
|
||||
return http.Header{}
|
||||
}
|
||||
|
||||
func (m *mockResponseWriter) Write(p []byte) (n int, err error) {
|
||||
return len(p), nil
|
||||
}
|
||||
|
||||
func (m *mockResponseWriter) WriteString(s string) (n int, err error) {
|
||||
return len(s), nil
|
||||
}
|
||||
|
||||
func (m *mockResponseWriter) WriteHeader(int) {}
|
||||
|
||||
func TestParams(t *testing.T) {
|
||||
ps := Params{
|
||||
Param{"param1", "value1"},
|
||||
Param{"param2", "value2"},
|
||||
Param{"param3", "value3"},
|
||||
}
|
||||
for i := range ps {
|
||||
if val := ps.ByName(ps[i].Key); val != ps[i].Value {
|
||||
t.Errorf("Wrong value for %s: Got %s; Want %s", ps[i].Key, val, ps[i].Value)
|
||||
}
|
||||
}
|
||||
if val := ps.ByName("noKey"); val != "" {
|
||||
t.Errorf("Expected empty string for not found key; got: %s", val)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRouter(t *testing.T) {
|
||||
router := New()
|
||||
|
||||
routed := false
|
||||
router.Handle("GET", "/user/:name", func(w http.ResponseWriter, r *http.Request, ps Params) {
|
||||
routed = true
|
||||
want := Params{Param{"name", "gopher"}}
|
||||
if !reflect.DeepEqual(ps, want) {
|
||||
t.Fatalf("wrong wildcard values: want %v, got %v", want, ps)
|
||||
}
|
||||
})
|
||||
|
||||
w := new(mockResponseWriter)
|
||||
|
||||
req, _ := http.NewRequest("GET", "/user/gopher", nil)
|
||||
router.ServeHTTP(w, req)
|
||||
|
||||
if !routed {
|
||||
t.Fatal("routing failed")
|
||||
}
|
||||
}
|
||||
|
||||
type handlerStruct struct {
|
||||
handeled *bool
|
||||
}
|
||||
|
||||
func (h handlerStruct) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
*h.handeled = true
|
||||
}
|
||||
|
||||
func TestRouterAPI(t *testing.T) {
|
||||
var get, head, options, post, put, patch, delete, handler, handlerFunc bool
|
||||
|
||||
httpHandler := handlerStruct{&handler}
|
||||
|
||||
router := New()
|
||||
router.GET("/GET", func(w http.ResponseWriter, r *http.Request, _ Params) {
|
||||
get = true
|
||||
})
|
||||
router.HEAD("/GET", func(w http.ResponseWriter, r *http.Request, _ Params) {
|
||||
head = true
|
||||
})
|
||||
router.OPTIONS("/GET", func(w http.ResponseWriter, r *http.Request, _ Params) {
|
||||
options = true
|
||||
})
|
||||
router.POST("/POST", func(w http.ResponseWriter, r *http.Request, _ Params) {
|
||||
post = true
|
||||
})
|
||||
router.PUT("/PUT", func(w http.ResponseWriter, r *http.Request, _ Params) {
|
||||
put = true
|
||||
})
|
||||
router.PATCH("/PATCH", func(w http.ResponseWriter, r *http.Request, _ Params) {
|
||||
patch = true
|
||||
})
|
||||
router.DELETE("/DELETE", func(w http.ResponseWriter, r *http.Request, _ Params) {
|
||||
delete = true
|
||||
})
|
||||
router.Handler("GET", "/Handler", httpHandler)
|
||||
router.HandlerFunc("GET", "/HandlerFunc", func(w http.ResponseWriter, r *http.Request) {
|
||||
handlerFunc = true
|
||||
})
|
||||
|
||||
w := new(mockResponseWriter)
|
||||
|
||||
r, _ := http.NewRequest("GET", "/GET", nil)
|
||||
router.ServeHTTP(w, r)
|
||||
if !get {
|
||||
t.Error("routing GET failed")
|
||||
}
|
||||
|
||||
r, _ = http.NewRequest("HEAD", "/GET", nil)
|
||||
router.ServeHTTP(w, r)
|
||||
if !head {
|
||||
t.Error("routing HEAD failed")
|
||||
}
|
||||
|
||||
r, _ = http.NewRequest("OPTIONS", "/GET", nil)
|
||||
router.ServeHTTP(w, r)
|
||||
if !options {
|
||||
t.Error("routing OPTIONS failed")
|
||||
}
|
||||
|
||||
r, _ = http.NewRequest("POST", "/POST", nil)
|
||||
router.ServeHTTP(w, r)
|
||||
if !post {
|
||||
t.Error("routing POST failed")
|
||||
}
|
||||
|
||||
r, _ = http.NewRequest("PUT", "/PUT", nil)
|
||||
router.ServeHTTP(w, r)
|
||||
if !put {
|
||||
t.Error("routing PUT failed")
|
||||
}
|
||||
|
||||
r, _ = http.NewRequest("PATCH", "/PATCH", nil)
|
||||
router.ServeHTTP(w, r)
|
||||
if !patch {
|
||||
t.Error("routing PATCH failed")
|
||||
}
|
||||
|
||||
r, _ = http.NewRequest("DELETE", "/DELETE", nil)
|
||||
router.ServeHTTP(w, r)
|
||||
if !delete {
|
||||
t.Error("routing DELETE failed")
|
||||
}
|
||||
|
||||
r, _ = http.NewRequest("GET", "/Handler", nil)
|
||||
router.ServeHTTP(w, r)
|
||||
if !handler {
|
||||
t.Error("routing Handler failed")
|
||||
}
|
||||
|
||||
r, _ = http.NewRequest("GET", "/HandlerFunc", nil)
|
||||
router.ServeHTTP(w, r)
|
||||
if !handlerFunc {
|
||||
t.Error("routing HandlerFunc failed")
|
||||
}
|
||||
}
|
||||
|
||||
func TestRouterRoot(t *testing.T) {
|
||||
router := New()
|
||||
recv := catchPanic(func() {
|
||||
router.GET("noSlashRoot", nil)
|
||||
})
|
||||
if recv == nil {
|
||||
t.Fatal("registering path not beginning with '/' did not panic")
|
||||
}
|
||||
}
|
||||
|
||||
func TestRouterNotAllowed(t *testing.T) {
|
||||
handlerFunc := func(_ http.ResponseWriter, _ *http.Request, _ Params) {}
|
||||
|
||||
router := New()
|
||||
router.POST("/path", handlerFunc)
|
||||
|
||||
// Test not allowed
|
||||
r, _ := http.NewRequest("GET", "/path", nil)
|
||||
w := httptest.NewRecorder()
|
||||
router.ServeHTTP(w, r)
|
||||
if !(w.Code == http.StatusMethodNotAllowed) {
|
||||
t.Errorf("NotAllowed handling failed: Code=%d, Header=%v", w.Code, w.Header())
|
||||
}
|
||||
|
||||
w = httptest.NewRecorder()
|
||||
responseText := "custom method"
|
||||
router.MethodNotAllowed = func(w http.ResponseWriter, req *http.Request) {
|
||||
w.WriteHeader(http.StatusTeapot)
|
||||
w.Write([]byte(responseText))
|
||||
}
|
||||
router.ServeHTTP(w, r)
|
||||
if got := w.Body.String(); !(got == responseText) {
|
||||
t.Errorf("unexpected response got %q want %q", got, responseText)
|
||||
}
|
||||
if w.Code != http.StatusTeapot {
|
||||
t.Errorf("unexpected response code %d want %d", w.Code, http.StatusTeapot)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRouterNotFound(t *testing.T) {
|
||||
handlerFunc := func(_ http.ResponseWriter, _ *http.Request, _ Params) {}
|
||||
|
||||
router := New()
|
||||
router.GET("/path", handlerFunc)
|
||||
router.GET("/dir/", handlerFunc)
|
||||
router.GET("/", handlerFunc)
|
||||
|
||||
testRoutes := []struct {
|
||||
route string
|
||||
code int
|
||||
header string
|
||||
}{
|
||||
{"/path/", 301, "map[Location:[/path]]"}, // TSR -/
|
||||
{"/dir", 301, "map[Location:[/dir/]]"}, // TSR +/
|
||||
{"", 301, "map[Location:[/]]"}, // TSR +/
|
||||
{"/PATH", 301, "map[Location:[/path]]"}, // Fixed Case
|
||||
{"/DIR/", 301, "map[Location:[/dir/]]"}, // Fixed Case
|
||||
{"/PATH/", 301, "map[Location:[/path]]"}, // Fixed Case -/
|
||||
{"/DIR", 301, "map[Location:[/dir/]]"}, // Fixed Case +/
|
||||
{"/../path", 301, "map[Location:[/path]]"}, // CleanPath
|
||||
{"/nope", 404, ""}, // NotFound
|
||||
}
|
||||
for _, tr := range testRoutes {
|
||||
r, _ := http.NewRequest("GET", tr.route, nil)
|
||||
w := httptest.NewRecorder()
|
||||
router.ServeHTTP(w, r)
|
||||
if !(w.Code == tr.code && (w.Code == 404 || fmt.Sprint(w.Header()) == tr.header)) {
|
||||
t.Errorf("NotFound handling route %s failed: Code=%d, Header=%v", tr.route, w.Code, w.Header())
|
||||
}
|
||||
}
|
||||
|
||||
// Test custom not found handler
|
||||
var notFound bool
|
||||
router.NotFound = func(rw http.ResponseWriter, r *http.Request) {
|
||||
rw.WriteHeader(404)
|
||||
notFound = true
|
||||
}
|
||||
r, _ := http.NewRequest("GET", "/nope", nil)
|
||||
w := httptest.NewRecorder()
|
||||
router.ServeHTTP(w, r)
|
||||
if !(w.Code == 404 && notFound == true) {
|
||||
t.Errorf("Custom NotFound handler failed: Code=%d, Header=%v", w.Code, w.Header())
|
||||
}
|
||||
|
||||
// Test other method than GET (want 307 instead of 301)
|
||||
router.PATCH("/path", handlerFunc)
|
||||
r, _ = http.NewRequest("PATCH", "/path/", nil)
|
||||
w = httptest.NewRecorder()
|
||||
router.ServeHTTP(w, r)
|
||||
if !(w.Code == 307 && fmt.Sprint(w.Header()) == "map[Location:[/path]]") {
|
||||
t.Errorf("Custom NotFound handler failed: Code=%d, Header=%v", w.Code, w.Header())
|
||||
}
|
||||
|
||||
// Test special case where no node for the prefix "/" exists
|
||||
router = New()
|
||||
router.GET("/a", handlerFunc)
|
||||
r, _ = http.NewRequest("GET", "/", nil)
|
||||
w = httptest.NewRecorder()
|
||||
router.ServeHTTP(w, r)
|
||||
if !(w.Code == 404) {
|
||||
t.Errorf("NotFound handling route / failed: Code=%d", w.Code)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRouterPanicHandler(t *testing.T) {
|
||||
router := New()
|
||||
panicHandled := false
|
||||
|
||||
router.PanicHandler = func(rw http.ResponseWriter, r *http.Request, p interface{}) {
|
||||
panicHandled = true
|
||||
}
|
||||
|
||||
router.Handle("PUT", "/user/:name", func(_ http.ResponseWriter, _ *http.Request, _ Params) {
|
||||
panic("oops!")
|
||||
})
|
||||
|
||||
w := new(mockResponseWriter)
|
||||
req, _ := http.NewRequest("PUT", "/user/gopher", nil)
|
||||
|
||||
defer func() {
|
||||
if rcv := recover(); rcv != nil {
|
||||
t.Fatal("handling panic failed")
|
||||
}
|
||||
}()
|
||||
|
||||
router.ServeHTTP(w, req)
|
||||
|
||||
if !panicHandled {
|
||||
t.Fatal("simulating failed")
|
||||
}
|
||||
}
|
||||
|
||||
func TestRouterLookup(t *testing.T) {
|
||||
routed := false
|
||||
wantHandle := func(_ http.ResponseWriter, _ *http.Request, _ Params) {
|
||||
routed = true
|
||||
}
|
||||
wantParams := Params{Param{"name", "gopher"}}
|
||||
|
||||
router := New()
|
||||
|
||||
// try empty router first
|
||||
handle, _, tsr := router.Lookup("GET", "/nope")
|
||||
if handle != nil {
|
||||
t.Fatalf("Got handle for unregistered pattern: %v", handle)
|
||||
}
|
||||
if tsr {
|
||||
t.Error("Got wrong TSR recommendation!")
|
||||
}
|
||||
|
||||
// insert route and try again
|
||||
router.GET("/user/:name", wantHandle)
|
||||
|
||||
handle, params, tsr := router.Lookup("GET", "/user/gopher")
|
||||
if handle == nil {
|
||||
t.Fatal("Got no handle!")
|
||||
} else {
|
||||
handle(nil, nil, nil)
|
||||
if !routed {
|
||||
t.Fatal("Routing failed!")
|
||||
}
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(params, wantParams) {
|
||||
t.Fatalf("Wrong parameter values: want %v, got %v", wantParams, params)
|
||||
}
|
||||
|
||||
handle, _, tsr = router.Lookup("GET", "/user/gopher/")
|
||||
if handle != nil {
|
||||
t.Fatalf("Got handle for unregistered pattern: %v", handle)
|
||||
}
|
||||
if !tsr {
|
||||
t.Error("Got no TSR recommendation!")
|
||||
}
|
||||
|
||||
handle, _, tsr = router.Lookup("GET", "/nope")
|
||||
if handle != nil {
|
||||
t.Fatalf("Got handle for unregistered pattern: %v", handle)
|
||||
}
|
||||
if tsr {
|
||||
t.Error("Got wrong TSR recommendation!")
|
||||
}
|
||||
}
|
||||
|
||||
type mockFileSystem struct {
|
||||
opened bool
|
||||
}
|
||||
|
||||
func (mfs *mockFileSystem) Open(name string) (http.File, error) {
|
||||
mfs.opened = true
|
||||
return nil, errors.New("this is just a mock")
|
||||
}
|
||||
|
||||
func TestRouterServeFiles(t *testing.T) {
|
||||
router := New()
|
||||
mfs := &mockFileSystem{}
|
||||
|
||||
recv := catchPanic(func() {
|
||||
router.ServeFiles("/noFilepath", mfs)
|
||||
})
|
||||
if recv == nil {
|
||||
t.Fatal("registering path not ending with '*filepath' did not panic")
|
||||
}
|
||||
|
||||
router.ServeFiles("/*filepath", mfs)
|
||||
w := new(mockResponseWriter)
|
||||
r, _ := http.NewRequest("GET", "/favicon.ico", nil)
|
||||
router.ServeHTTP(w, r)
|
||||
if !mfs.opened {
|
||||
t.Error("serving file failed")
|
||||
}
|
||||
}
|
611
vendor/github.com/julienschmidt/httprouter/tree_test.go
generated
vendored
Normal file
611
vendor/github.com/julienschmidt/httprouter/tree_test.go
generated
vendored
Normal file
|
@ -0,0 +1,611 @@
|
|||
// Copyright 2013 Julien Schmidt. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be found
|
||||
// in the LICENSE file.
|
||||
|
||||
package httprouter
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"reflect"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func printChildren(n *node, prefix string) {
|
||||
fmt.Printf(" %02d:%02d %s%s[%d] %v %t %d \r\n", n.priority, n.maxParams, prefix, n.path, len(n.children), n.handle, n.wildChild, n.nType)
|
||||
for l := len(n.path); l > 0; l-- {
|
||||
prefix += " "
|
||||
}
|
||||
for _, child := range n.children {
|
||||
printChildren(child, prefix)
|
||||
}
|
||||
}
|
||||
|
||||
// Used as a workaround since we can't compare functions or their adresses
|
||||
var fakeHandlerValue string
|
||||
|
||||
func fakeHandler(val string) Handle {
|
||||
return func(http.ResponseWriter, *http.Request, Params) {
|
||||
fakeHandlerValue = val
|
||||
}
|
||||
}
|
||||
|
||||
type testRequests []struct {
|
||||
path string
|
||||
nilHandler bool
|
||||
route string
|
||||
ps Params
|
||||
}
|
||||
|
||||
func checkRequests(t *testing.T, tree *node, requests testRequests) {
|
||||
for _, request := range requests {
|
||||
handler, ps, _ := tree.getValue(request.path)
|
||||
|
||||
if handler == nil {
|
||||
if !request.nilHandler {
|
||||
t.Errorf("handle mismatch for route '%s': Expected non-nil handle", request.path)
|
||||
}
|
||||
} else if request.nilHandler {
|
||||
t.Errorf("handle mismatch for route '%s': Expected nil handle", request.path)
|
||||
} else {
|
||||
handler(nil, nil, nil)
|
||||
if fakeHandlerValue != request.route {
|
||||
t.Errorf("handle mismatch for route '%s': Wrong handle (%s != %s)", request.path, fakeHandlerValue, request.route)
|
||||
}
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(ps, request.ps) {
|
||||
t.Errorf("Params mismatch for route '%s'", request.path)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func checkPriorities(t *testing.T, n *node) uint32 {
|
||||
var prio uint32
|
||||
for i := range n.children {
|
||||
prio += checkPriorities(t, n.children[i])
|
||||
}
|
||||
|
||||
if n.handle != nil {
|
||||
prio++
|
||||
}
|
||||
|
||||
if n.priority != prio {
|
||||
t.Errorf(
|
||||
"priority mismatch for node '%s': is %d, should be %d",
|
||||
n.path, n.priority, prio,
|
||||
)
|
||||
}
|
||||
|
||||
return prio
|
||||
}
|
||||
|
||||
func checkMaxParams(t *testing.T, n *node) uint8 {
|
||||
var maxParams uint8
|
||||
for i := range n.children {
|
||||
params := checkMaxParams(t, n.children[i])
|
||||
if params > maxParams {
|
||||
maxParams = params
|
||||
}
|
||||
}
|
||||
if n.nType != static && !n.wildChild {
|
||||
maxParams++
|
||||
}
|
||||
|
||||
if n.maxParams != maxParams {
|
||||
t.Errorf(
|
||||
"maxParams mismatch for node '%s': is %d, should be %d",
|
||||
n.path, n.maxParams, maxParams,
|
||||
)
|
||||
}
|
||||
|
||||
return maxParams
|
||||
}
|
||||
|
||||
func TestCountParams(t *testing.T) {
|
||||
if countParams("/path/:param1/static/*catch-all") != 2 {
|
||||
t.Fail()
|
||||
}
|
||||
if countParams(strings.Repeat("/:param", 256)) != 255 {
|
||||
t.Fail()
|
||||
}
|
||||
}
|
||||
|
||||
func TestTreeAddAndGet(t *testing.T) {
|
||||
tree := &node{}
|
||||
|
||||
routes := [...]string{
|
||||
"/hi",
|
||||
"/contact",
|
||||
"/co",
|
||||
"/c",
|
||||
"/a",
|
||||
"/ab",
|
||||
"/doc/",
|
||||
"/doc/go_faq.html",
|
||||
"/doc/go1.html",
|
||||
"/α",
|
||||
"/β",
|
||||
}
|
||||
for _, route := range routes {
|
||||
tree.addRoute(route, fakeHandler(route))
|
||||
}
|
||||
|
||||
//printChildren(tree, "")
|
||||
|
||||
checkRequests(t, tree, testRequests{
|
||||
{"/a", false, "/a", nil},
|
||||
{"/", true, "", nil},
|
||||
{"/hi", false, "/hi", nil},
|
||||
{"/contact", false, "/contact", nil},
|
||||
{"/co", false, "/co", nil},
|
||||
{"/con", true, "", nil}, // key mismatch
|
||||
{"/cona", true, "", nil}, // key mismatch
|
||||
{"/no", true, "", nil}, // no matching child
|
||||
{"/ab", false, "/ab", nil},
|
||||
{"/α", false, "/α", nil},
|
||||
{"/β", false, "/β", nil},
|
||||
})
|
||||
|
||||
checkPriorities(t, tree)
|
||||
checkMaxParams(t, tree)
|
||||
}
|
||||
|
||||
func TestTreeWildcard(t *testing.T) {
|
||||
tree := &node{}
|
||||
|
||||
routes := [...]string{
|
||||
"/",
|
||||
"/cmd/:tool/:sub",
|
||||
"/cmd/:tool/",
|
||||
"/src/*filepath",
|
||||
"/search/",
|
||||
"/search/:query",
|
||||
"/user_:name",
|
||||
"/user_:name/about",
|
||||
"/files/:dir/*filepath",
|
||||
"/doc/",
|
||||
"/doc/go_faq.html",
|
||||
"/doc/go1.html",
|
||||
"/info/:user/public",
|
||||
"/info/:user/project/:project",
|
||||
}
|
||||
for _, route := range routes {
|
||||
tree.addRoute(route, fakeHandler(route))
|
||||
}
|
||||
|
||||
//printChildren(tree, "")
|
||||
|
||||
checkRequests(t, tree, testRequests{
|
||||
{"/", false, "/", nil},
|
||||
{"/cmd/test/", false, "/cmd/:tool/", Params{Param{"tool", "test"}}},
|
||||
{"/cmd/test", true, "", Params{Param{"tool", "test"}}},
|
||||
{"/cmd/test/3", false, "/cmd/:tool/:sub", Params{Param{"tool", "test"}, Param{"sub", "3"}}},
|
||||
{"/src/", false, "/src/*filepath", Params{Param{"filepath", "/"}}},
|
||||
{"/src/some/file.png", false, "/src/*filepath", Params{Param{"filepath", "/some/file.png"}}},
|
||||
{"/search/", false, "/search/", nil},
|
||||
{"/search/someth!ng+in+ünìcodé", false, "/search/:query", Params{Param{"query", "someth!ng+in+ünìcodé"}}},
|
||||
{"/search/someth!ng+in+ünìcodé/", true, "", Params{Param{"query", "someth!ng+in+ünìcodé"}}},
|
||||
{"/user_gopher", false, "/user_:name", Params{Param{"name", "gopher"}}},
|
||||
{"/user_gopher/about", false, "/user_:name/about", Params{Param{"name", "gopher"}}},
|
||||
{"/files/js/inc/framework.js", false, "/files/:dir/*filepath", Params{Param{"dir", "js"}, Param{"filepath", "/inc/framework.js"}}},
|
||||
{"/info/gordon/public", false, "/info/:user/public", Params{Param{"user", "gordon"}}},
|
||||
{"/info/gordon/project/go", false, "/info/:user/project/:project", Params{Param{"user", "gordon"}, Param{"project", "go"}}},
|
||||
})
|
||||
|
||||
checkPriorities(t, tree)
|
||||
checkMaxParams(t, tree)
|
||||
}
|
||||
|
||||
func catchPanic(testFunc func()) (recv interface{}) {
|
||||
defer func() {
|
||||
recv = recover()
|
||||
}()
|
||||
|
||||
testFunc()
|
||||
return
|
||||
}
|
||||
|
||||
type testRoute struct {
|
||||
path string
|
||||
conflict bool
|
||||
}
|
||||
|
||||
func testRoutes(t *testing.T, routes []testRoute) {
|
||||
tree := &node{}
|
||||
|
||||
for _, route := range routes {
|
||||
recv := catchPanic(func() {
|
||||
tree.addRoute(route.path, nil)
|
||||
})
|
||||
|
||||
if route.conflict {
|
||||
if recv == nil {
|
||||
t.Errorf("no panic for conflicting route '%s'", route.path)
|
||||
}
|
||||
} else if recv != nil {
|
||||
t.Errorf("unexpected panic for route '%s': %v", route.path, recv)
|
||||
}
|
||||
}
|
||||
|
||||
//printChildren(tree, "")
|
||||
}
|
||||
|
||||
func TestTreeWildcardConflict(t *testing.T) {
|
||||
routes := []testRoute{
|
||||
{"/cmd/:tool/:sub", false},
|
||||
{"/cmd/vet", true},
|
||||
{"/src/*filepath", false},
|
||||
{"/src/*filepathx", true},
|
||||
{"/src/", true},
|
||||
{"/src1/", false},
|
||||
{"/src1/*filepath", true},
|
||||
{"/src2*filepath", true},
|
||||
{"/search/:query", false},
|
||||
{"/search/invalid", true},
|
||||
{"/user_:name", false},
|
||||
{"/user_x", true},
|
||||
{"/user_:name", false},
|
||||
{"/id:id", false},
|
||||
{"/id/:id", true},
|
||||
}
|
||||
testRoutes(t, routes)
|
||||
}
|
||||
|
||||
func TestTreeChildConflict(t *testing.T) {
|
||||
routes := []testRoute{
|
||||
{"/cmd/vet", false},
|
||||
{"/cmd/:tool/:sub", true},
|
||||
{"/src/AUTHORS", false},
|
||||
{"/src/*filepath", true},
|
||||
{"/user_x", false},
|
||||
{"/user_:name", true},
|
||||
{"/id/:id", false},
|
||||
{"/id:id", true},
|
||||
{"/:id", true},
|
||||
{"/*filepath", true},
|
||||
}
|
||||
testRoutes(t, routes)
|
||||
}
|
||||
|
||||
func TestTreeDupliatePath(t *testing.T) {
|
||||
tree := &node{}
|
||||
|
||||
routes := [...]string{
|
||||
"/",
|
||||
"/doc/",
|
||||
"/src/*filepath",
|
||||
"/search/:query",
|
||||
"/user_:name",
|
||||
}
|
||||
for _, route := range routes {
|
||||
recv := catchPanic(func() {
|
||||
tree.addRoute(route, fakeHandler(route))
|
||||
})
|
||||
if recv != nil {
|
||||
t.Fatalf("panic inserting route '%s': %v", route, recv)
|
||||
}
|
||||
|
||||
// Add again
|
||||
recv = catchPanic(func() {
|
||||
tree.addRoute(route, nil)
|
||||
})
|
||||
if recv == nil {
|
||||
t.Fatalf("no panic while inserting duplicate route '%s", route)
|
||||
}
|
||||
}
|
||||
|
||||
//printChildren(tree, "")
|
||||
|
||||
checkRequests(t, tree, testRequests{
|
||||
{"/", false, "/", nil},
|
||||
{"/doc/", false, "/doc/", nil},
|
||||
{"/src/some/file.png", false, "/src/*filepath", Params{Param{"filepath", "/some/file.png"}}},
|
||||
{"/search/someth!ng+in+ünìcodé", false, "/search/:query", Params{Param{"query", "someth!ng+in+ünìcodé"}}},
|
||||
{"/user_gopher", false, "/user_:name", Params{Param{"name", "gopher"}}},
|
||||
})
|
||||
}
|
||||
|
||||
func TestEmptyWildcardName(t *testing.T) {
|
||||
tree := &node{}
|
||||
|
||||
routes := [...]string{
|
||||
"/user:",
|
||||
"/user:/",
|
||||
"/cmd/:/",
|
||||
"/src/*",
|
||||
}
|
||||
for _, route := range routes {
|
||||
recv := catchPanic(func() {
|
||||
tree.addRoute(route, nil)
|
||||
})
|
||||
if recv == nil {
|
||||
t.Fatalf("no panic while inserting route with empty wildcard name '%s", route)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestTreeCatchAllConflict(t *testing.T) {
|
||||
routes := []testRoute{
|
||||
{"/src/*filepath/x", true},
|
||||
{"/src2/", false},
|
||||
{"/src2/*filepath/x", true},
|
||||
}
|
||||
testRoutes(t, routes)
|
||||
}
|
||||
|
||||
func TestTreeCatchAllConflictRoot(t *testing.T) {
|
||||
routes := []testRoute{
|
||||
{"/", false},
|
||||
{"/*filepath", true},
|
||||
}
|
||||
testRoutes(t, routes)
|
||||
}
|
||||
|
||||
func TestTreeDoubleWildcard(t *testing.T) {
|
||||
const panicMsg = "only one wildcard per path segment is allowed"
|
||||
|
||||
routes := [...]string{
|
||||
"/:foo:bar",
|
||||
"/:foo:bar/",
|
||||
"/:foo*bar",
|
||||
}
|
||||
|
||||
for _, route := range routes {
|
||||
tree := &node{}
|
||||
recv := catchPanic(func() {
|
||||
tree.addRoute(route, nil)
|
||||
})
|
||||
|
||||
if rs, ok := recv.(string); !ok || !strings.HasPrefix(rs, panicMsg) {
|
||||
t.Fatalf(`"Expected panic "%s" for route '%s', got "%v"`, panicMsg, route, recv)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*func TestTreeDuplicateWildcard(t *testing.T) {
|
||||
tree := &node{}
|
||||
|
||||
routes := [...]string{
|
||||
"/:id/:name/:id",
|
||||
}
|
||||
for _, route := range routes {
|
||||
...
|
||||
}
|
||||
}*/
|
||||
|
||||
func TestTreeTrailingSlashRedirect(t *testing.T) {
|
||||
tree := &node{}
|
||||
|
||||
routes := [...]string{
|
||||
"/hi",
|
||||
"/b/",
|
||||
"/search/:query",
|
||||
"/cmd/:tool/",
|
||||
"/src/*filepath",
|
||||
"/x",
|
||||
"/x/y",
|
||||
"/y/",
|
||||
"/y/z",
|
||||
"/0/:id",
|
||||
"/0/:id/1",
|
||||
"/1/:id/",
|
||||
"/1/:id/2",
|
||||
"/aa",
|
||||
"/a/",
|
||||
"/doc",
|
||||
"/doc/go_faq.html",
|
||||
"/doc/go1.html",
|
||||
"/no/a",
|
||||
"/no/b",
|
||||
"/api/hello/:name",
|
||||
}
|
||||
for _, route := range routes {
|
||||
recv := catchPanic(func() {
|
||||
tree.addRoute(route, fakeHandler(route))
|
||||
})
|
||||
if recv != nil {
|
||||
t.Fatalf("panic inserting route '%s': %v", route, recv)
|
||||
}
|
||||
}
|
||||
|
||||
//printChildren(tree, "")
|
||||
|
||||
tsrRoutes := [...]string{
|
||||
"/hi/",
|
||||
"/b",
|
||||
"/search/gopher/",
|
||||
"/cmd/vet",
|
||||
"/src",
|
||||
"/x/",
|
||||
"/y",
|
||||
"/0/go/",
|
||||
"/1/go",
|
||||
"/a",
|
||||
"/doc/",
|
||||
}
|
||||
for _, route := range tsrRoutes {
|
||||
handler, _, tsr := tree.getValue(route)
|
||||
if handler != nil {
|
||||
t.Fatalf("non-nil handler for TSR route '%s", route)
|
||||
} else if !tsr {
|
||||
t.Errorf("expected TSR recommendation for route '%s'", route)
|
||||
}
|
||||
}
|
||||
|
||||
noTsrRoutes := [...]string{
|
||||
"/",
|
||||
"/no",
|
||||
"/no/",
|
||||
"/_",
|
||||
"/_/",
|
||||
"/api/world/abc",
|
||||
}
|
||||
for _, route := range noTsrRoutes {
|
||||
handler, _, tsr := tree.getValue(route)
|
||||
if handler != nil {
|
||||
t.Fatalf("non-nil handler for No-TSR route '%s", route)
|
||||
} else if tsr {
|
||||
t.Errorf("expected no TSR recommendation for route '%s'", route)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestTreeFindCaseInsensitivePath(t *testing.T) {
|
||||
tree := &node{}
|
||||
|
||||
routes := [...]string{
|
||||
"/hi",
|
||||
"/b/",
|
||||
"/ABC/",
|
||||
"/search/:query",
|
||||
"/cmd/:tool/",
|
||||
"/src/*filepath",
|
||||
"/x",
|
||||
"/x/y",
|
||||
"/y/",
|
||||
"/y/z",
|
||||
"/0/:id",
|
||||
"/0/:id/1",
|
||||
"/1/:id/",
|
||||
"/1/:id/2",
|
||||
"/aa",
|
||||
"/a/",
|
||||
"/doc",
|
||||
"/doc/go_faq.html",
|
||||
"/doc/go1.html",
|
||||
"/doc/go/away",
|
||||
"/no/a",
|
||||
"/no/b",
|
||||
}
|
||||
|
||||
for _, route := range routes {
|
||||
recv := catchPanic(func() {
|
||||
tree.addRoute(route, fakeHandler(route))
|
||||
})
|
||||
if recv != nil {
|
||||
t.Fatalf("panic inserting route '%s': %v", route, recv)
|
||||
}
|
||||
}
|
||||
|
||||
// Check out == in for all registered routes
|
||||
// With fixTrailingSlash = true
|
||||
for _, route := range routes {
|
||||
out, found := tree.findCaseInsensitivePath(route, true)
|
||||
if !found {
|
||||
t.Errorf("Route '%s' not found!", route)
|
||||
} else if string(out) != route {
|
||||
t.Errorf("Wrong result for route '%s': %s", route, string(out))
|
||||
}
|
||||
}
|
||||
// With fixTrailingSlash = false
|
||||
for _, route := range routes {
|
||||
out, found := tree.findCaseInsensitivePath(route, false)
|
||||
if !found {
|
||||
t.Errorf("Route '%s' not found!", route)
|
||||
} else if string(out) != route {
|
||||
t.Errorf("Wrong result for route '%s': %s", route, string(out))
|
||||
}
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
in string
|
||||
out string
|
||||
found bool
|
||||
slash bool
|
||||
}{
|
||||
{"/HI", "/hi", true, false},
|
||||
{"/HI/", "/hi", true, true},
|
||||
{"/B", "/b/", true, true},
|
||||
{"/B/", "/b/", true, false},
|
||||
{"/abc", "/ABC/", true, true},
|
||||
{"/abc/", "/ABC/", true, false},
|
||||
{"/aBc", "/ABC/", true, true},
|
||||
{"/aBc/", "/ABC/", true, false},
|
||||
{"/abC", "/ABC/", true, true},
|
||||
{"/abC/", "/ABC/", true, false},
|
||||
{"/SEARCH/QUERY", "/search/QUERY", true, false},
|
||||
{"/SEARCH/QUERY/", "/search/QUERY", true, true},
|
||||
{"/CMD/TOOL/", "/cmd/TOOL/", true, false},
|
||||
{"/CMD/TOOL", "/cmd/TOOL/", true, true},
|
||||
{"/SRC/FILE/PATH", "/src/FILE/PATH", true, false},
|
||||
{"/x/Y", "/x/y", true, false},
|
||||
{"/x/Y/", "/x/y", true, true},
|
||||
{"/X/y", "/x/y", true, false},
|
||||
{"/X/y/", "/x/y", true, true},
|
||||
{"/X/Y", "/x/y", true, false},
|
||||
{"/X/Y/", "/x/y", true, true},
|
||||
{"/Y/", "/y/", true, false},
|
||||
{"/Y", "/y/", true, true},
|
||||
{"/Y/z", "/y/z", true, false},
|
||||
{"/Y/z/", "/y/z", true, true},
|
||||
{"/Y/Z", "/y/z", true, false},
|
||||
{"/Y/Z/", "/y/z", true, true},
|
||||
{"/y/Z", "/y/z", true, false},
|
||||
{"/y/Z/", "/y/z", true, true},
|
||||
{"/Aa", "/aa", true, false},
|
||||
{"/Aa/", "/aa", true, true},
|
||||
{"/AA", "/aa", true, false},
|
||||
{"/AA/", "/aa", true, true},
|
||||
{"/aA", "/aa", true, false},
|
||||
{"/aA/", "/aa", true, true},
|
||||
{"/A/", "/a/", true, false},
|
||||
{"/A", "/a/", true, true},
|
||||
{"/DOC", "/doc", true, false},
|
||||
{"/DOC/", "/doc", true, true},
|
||||
{"/NO", "", false, true},
|
||||
{"/DOC/GO", "", false, true},
|
||||
}
|
||||
// With fixTrailingSlash = true
|
||||
for _, test := range tests {
|
||||
out, found := tree.findCaseInsensitivePath(test.in, true)
|
||||
if found != test.found || (found && (string(out) != test.out)) {
|
||||
t.Errorf("Wrong result for '%s': got %s, %t; want %s, %t",
|
||||
test.in, string(out), found, test.out, test.found)
|
||||
return
|
||||
}
|
||||
}
|
||||
// With fixTrailingSlash = false
|
||||
for _, test := range tests {
|
||||
out, found := tree.findCaseInsensitivePath(test.in, false)
|
||||
if test.slash {
|
||||
if found { // test needs a trailingSlash fix. It must not be found!
|
||||
t.Errorf("Found without fixTrailingSlash: %s; got %s", test.in, string(out))
|
||||
}
|
||||
} else {
|
||||
if found != test.found || (found && (string(out) != test.out)) {
|
||||
t.Errorf("Wrong result for '%s': got %s, %t; want %s, %t",
|
||||
test.in, string(out), found, test.out, test.found)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestTreeInvalidNodeType(t *testing.T) {
|
||||
const panicMsg = "invalid node type"
|
||||
|
||||
tree := &node{}
|
||||
tree.addRoute("/", fakeHandler("/"))
|
||||
tree.addRoute("/:page", fakeHandler("/:page"))
|
||||
|
||||
// set invalid node type
|
||||
tree.children[0].nType = 42
|
||||
|
||||
// normal lookup
|
||||
recv := catchPanic(func() {
|
||||
tree.getValue("/test")
|
||||
})
|
||||
if rs, ok := recv.(string); !ok || rs != panicMsg {
|
||||
t.Fatalf("Expected panic '"+panicMsg+"', got '%v'", recv)
|
||||
}
|
||||
|
||||
// case-insensitive lookup
|
||||
recv = catchPanic(func() {
|
||||
tree.findCaseInsensitivePath("/test", true)
|
||||
})
|
||||
if rs, ok := recv.(string); !ok || rs != panicMsg {
|
||||
t.Fatalf("Expected panic '"+panicMsg+"', got '%v'", recv)
|
||||
}
|
||||
}
|
120
vendor/github.com/kylelemons/godebug/diff/diff_test.go
generated
vendored
Normal file
120
vendor/github.com/kylelemons/godebug/diff/diff_test.go
generated
vendored
Normal file
|
@ -0,0 +1,120 @@
|
|||
// Copyright 2013 Google Inc. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package diff
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestDiff(t *testing.T) {
|
||||
tests := []struct {
|
||||
desc string
|
||||
A, B []string
|
||||
chunks []Chunk
|
||||
}{
|
||||
{
|
||||
desc: "constitution",
|
||||
A: []string{
|
||||
"We the People of the United States, in Order to form a more perfect Union,",
|
||||
"establish Justice, insure domestic Tranquility, provide for the common defence,",
|
||||
"and secure the Blessings of Liberty to ourselves",
|
||||
"and our Posterity, do ordain and establish this Constitution for the United",
|
||||
"States of America.",
|
||||
},
|
||||
B: []string{
|
||||
"We the People of the United States, in Order to form a more perfect Union,",
|
||||
"establish Justice, insure domestic Tranquility, provide for the common defence,",
|
||||
"promote the general Welfare, and secure the Blessings of Liberty to ourselves",
|
||||
"and our Posterity, do ordain and establish this Constitution for the United",
|
||||
"States of America.",
|
||||
},
|
||||
chunks: []Chunk{
|
||||
0: {
|
||||
Equal: []string{
|
||||
"We the People of the United States, in Order to form a more perfect Union,",
|
||||
"establish Justice, insure domestic Tranquility, provide for the common defence,",
|
||||
},
|
||||
},
|
||||
1: {
|
||||
Deleted: []string{
|
||||
"and secure the Blessings of Liberty to ourselves",
|
||||
},
|
||||
},
|
||||
2: {
|
||||
Added: []string{
|
||||
"promote the general Welfare, and secure the Blessings of Liberty to ourselves",
|
||||
},
|
||||
Equal: []string{
|
||||
"and our Posterity, do ordain and establish this Constitution for the United",
|
||||
"States of America.",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
got := DiffChunks(test.A, test.B)
|
||||
if got, want := len(got), len(test.chunks); got != want {
|
||||
t.Errorf("%s: edit distance = %v, want %v", test.desc, got-1, want-1)
|
||||
continue
|
||||
}
|
||||
for i := range got {
|
||||
got, want := got[i], test.chunks[i]
|
||||
if got, want := got.Added, want.Added; !reflect.DeepEqual(got, want) {
|
||||
t.Errorf("%s[%d]: Added = %v, want %v", test.desc, i, got, want)
|
||||
}
|
||||
if got, want := got.Deleted, want.Deleted; !reflect.DeepEqual(got, want) {
|
||||
t.Errorf("%s[%d]: Deleted = %v, want %v", test.desc, i, got, want)
|
||||
}
|
||||
if got, want := got.Equal, want.Equal; !reflect.DeepEqual(got, want) {
|
||||
t.Errorf("%s[%d]: Equal = %v, want %v", test.desc, i, got, want)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func ExampleDiff() {
|
||||
constitution := strings.TrimSpace(`
|
||||
We the People of the United States, in Order to form a more perfect Union,
|
||||
establish Justice, insure domestic Tranquility, provide for the common defence,
|
||||
promote the general Welfare, and secure the Blessings of Liberty to ourselves
|
||||
and our Posterity, do ordain and establish this Constitution for the United
|
||||
States of America.
|
||||
`)
|
||||
|
||||
got := strings.TrimSpace(`
|
||||
:wq
|
||||
We the People of the United States, in Order to form a more perfect Union,
|
||||
establish Justice, insure domestic Tranquility, provide for the common defence,
|
||||
and secure the Blessings of Liberty to ourselves
|
||||
and our Posterity, do ordain and establish this Constitution for the United
|
||||
States of America.
|
||||
`)
|
||||
|
||||
fmt.Println(Diff(got, constitution))
|
||||
|
||||
// Output:
|
||||
// -:wq
|
||||
// We the People of the United States, in Order to form a more perfect Union,
|
||||
// establish Justice, insure domestic Tranquility, provide for the common defence,
|
||||
// -and secure the Blessings of Liberty to ourselves
|
||||
// +promote the general Welfare, and secure the Blessings of Liberty to ourselves
|
||||
// and our Posterity, do ordain and establish this Constitution for the United
|
||||
// States of America.
|
||||
}
|
158
vendor/github.com/kylelemons/godebug/pretty/examples_test.go
generated
vendored
Normal file
158
vendor/github.com/kylelemons/godebug/pretty/examples_test.go
generated
vendored
Normal file
|
@ -0,0 +1,158 @@
|
|||
// Copyright 2013 Google Inc. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package pretty_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/kylelemons/godebug/pretty"
|
||||
)
|
||||
|
||||
func ExampleConfig_Sprint() {
|
||||
type Pair [2]int
|
||||
type Map struct {
|
||||
Name string
|
||||
Players map[string]Pair
|
||||
Obstacles map[Pair]string
|
||||
}
|
||||
|
||||
m := Map{
|
||||
Name: "Rock Creek",
|
||||
Players: map[string]Pair{
|
||||
"player1": {1, 3},
|
||||
"player2": {0, -1},
|
||||
},
|
||||
Obstacles: map[Pair]string{
|
||||
Pair{0, 0}: "rock",
|
||||
Pair{2, 1}: "pond",
|
||||
Pair{1, 1}: "stream",
|
||||
Pair{0, 1}: "stream",
|
||||
},
|
||||
}
|
||||
|
||||
// Specific output formats
|
||||
compact := &pretty.Config{
|
||||
Compact: true,
|
||||
}
|
||||
diffable := &pretty.Config{
|
||||
Diffable: true,
|
||||
}
|
||||
|
||||
// Print out a summary
|
||||
fmt.Printf("Players: %s\n", compact.Sprint(m.Players))
|
||||
|
||||
// Print diffable output
|
||||
fmt.Printf("Map State:\n%s", diffable.Sprint(m))
|
||||
|
||||
// Output:
|
||||
// Players: {player1:[1,3],player2:[0,-1]}
|
||||
// Map State:
|
||||
// {
|
||||
// Name: "Rock Creek",
|
||||
// Players: {
|
||||
// player1: [
|
||||
// 1,
|
||||
// 3,
|
||||
// ],
|
||||
// player2: [
|
||||
// 0,
|
||||
// -1,
|
||||
// ],
|
||||
// },
|
||||
// Obstacles: {
|
||||
// [0,0]: "rock",
|
||||
// [0,1]: "stream",
|
||||
// [1,1]: "stream",
|
||||
// [2,1]: "pond",
|
||||
// },
|
||||
// }
|
||||
}
|
||||
|
||||
func ExamplePrint() {
|
||||
type ShipManifest struct {
|
||||
Name string
|
||||
Crew map[string]string
|
||||
Androids int
|
||||
Stolen bool
|
||||
}
|
||||
|
||||
manifest := &ShipManifest{
|
||||
Name: "Spaceship Heart of Gold",
|
||||
Crew: map[string]string{
|
||||
"Zaphod Beeblebrox": "Galactic President",
|
||||
"Trillian": "Human",
|
||||
"Ford Prefect": "A Hoopy Frood",
|
||||
"Arthur Dent": "Along for the Ride",
|
||||
},
|
||||
Androids: 1,
|
||||
Stolen: true,
|
||||
}
|
||||
|
||||
pretty.Print(manifest)
|
||||
|
||||
// Output:
|
||||
// {Name: "Spaceship Heart of Gold",
|
||||
// Crew: {Arthur Dent: "Along for the Ride",
|
||||
// Ford Prefect: "A Hoopy Frood",
|
||||
// Trillian: "Human",
|
||||
// Zaphod Beeblebrox: "Galactic President"},
|
||||
// Androids: 1,
|
||||
// Stolen: true}
|
||||
}
|
||||
|
||||
func ExampleCompare() {
|
||||
type ShipManifest struct {
|
||||
Name string
|
||||
Crew map[string]string
|
||||
Androids int
|
||||
Stolen bool
|
||||
}
|
||||
|
||||
reported := &ShipManifest{
|
||||
Name: "Spaceship Heart of Gold",
|
||||
Crew: map[string]string{
|
||||
"Zaphod Beeblebrox": "Galactic President",
|
||||
"Trillian": "Human",
|
||||
"Ford Prefect": "A Hoopy Frood",
|
||||
"Arthur Dent": "Along for the Ride",
|
||||
},
|
||||
Androids: 1,
|
||||
Stolen: true,
|
||||
}
|
||||
|
||||
expected := &ShipManifest{
|
||||
Name: "Spaceship Heart of Gold",
|
||||
Crew: map[string]string{
|
||||
"Rowan Artosok": "Captain",
|
||||
},
|
||||
Androids: 1,
|
||||
Stolen: false,
|
||||
}
|
||||
|
||||
fmt.Println(pretty.Compare(reported, expected))
|
||||
// Output:
|
||||
// {
|
||||
// Name: "Spaceship Heart of Gold",
|
||||
// Crew: {
|
||||
// - Arthur Dent: "Along for the Ride",
|
||||
// - Ford Prefect: "A Hoopy Frood",
|
||||
// - Trillian: "Human",
|
||||
// - Zaphod Beeblebrox: "Galactic President",
|
||||
// + Rowan Artosok: "Captain",
|
||||
// },
|
||||
// Androids: 1,
|
||||
// - Stolen: true,
|
||||
// + Stolen: false,
|
||||
// }
|
||||
}
|
72
vendor/github.com/kylelemons/godebug/pretty/public_test.go
generated
vendored
Normal file
72
vendor/github.com/kylelemons/godebug/pretty/public_test.go
generated
vendored
Normal file
|
@ -0,0 +1,72 @@
|
|||
// Copyright 2013 Google Inc. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package pretty
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestDiff(t *testing.T) {
|
||||
type example struct {
|
||||
Name string
|
||||
Age int
|
||||
Friends []string
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
desc string
|
||||
got, want interface{}
|
||||
diff string
|
||||
}{
|
||||
{
|
||||
desc: "basic struct",
|
||||
got: example{
|
||||
Name: "Zaphd",
|
||||
Age: 42,
|
||||
Friends: []string{
|
||||
"Ford Prefect",
|
||||
"Trillian",
|
||||
"Marvin",
|
||||
},
|
||||
},
|
||||
want: example{
|
||||
Name: "Zaphod",
|
||||
Age: 42,
|
||||
Friends: []string{
|
||||
"Ford Prefect",
|
||||
"Trillian",
|
||||
},
|
||||
},
|
||||
diff: ` {
|
||||
- Name: "Zaphd",
|
||||
+ Name: "Zaphod",
|
||||
Age: 42,
|
||||
Friends: [
|
||||
"Ford Prefect",
|
||||
"Trillian",
|
||||
- "Marvin",
|
||||
],
|
||||
}`,
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
if got, want := Compare(test.got, test.want), test.diff; got != want {
|
||||
t.Errorf("%s:", test.desc)
|
||||
t.Errorf(" got: %q", got)
|
||||
t.Errorf(" want: %q", want)
|
||||
}
|
||||
}
|
||||
}
|
143
vendor/github.com/kylelemons/godebug/pretty/reflect_test.go
generated
vendored
Normal file
143
vendor/github.com/kylelemons/godebug/pretty/reflect_test.go
generated
vendored
Normal file
|
@ -0,0 +1,143 @@
|
|||
// Copyright 2013 Google Inc. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package pretty
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestVal2nodeDefault(t *testing.T) {
|
||||
tests := []struct {
|
||||
desc string
|
||||
raw interface{}
|
||||
want node
|
||||
}{
|
||||
{
|
||||
"nil",
|
||||
(*int)(nil),
|
||||
rawVal("nil"),
|
||||
},
|
||||
{
|
||||
"string",
|
||||
"zaphod",
|
||||
stringVal("zaphod"),
|
||||
},
|
||||
{
|
||||
"slice",
|
||||
[]string{"a", "b"},
|
||||
list{stringVal("a"), stringVal("b")},
|
||||
},
|
||||
{
|
||||
"map",
|
||||
map[string]string{
|
||||
"zaphod": "beeblebrox",
|
||||
"ford": "prefect",
|
||||
},
|
||||
keyvals{
|
||||
{"ford", stringVal("prefect")},
|
||||
{"zaphod", stringVal("beeblebrox")},
|
||||
},
|
||||
},
|
||||
{
|
||||
"map of [2]int",
|
||||
map[[2]int]string{
|
||||
[2]int{-1, 2}: "school",
|
||||
[2]int{0, 0}: "origin",
|
||||
[2]int{1, 3}: "home",
|
||||
},
|
||||
keyvals{
|
||||
{"[-1,2]", stringVal("school")},
|
||||
{"[0,0]", stringVal("origin")},
|
||||
{"[1,3]", stringVal("home")},
|
||||
},
|
||||
},
|
||||
{
|
||||
"struct",
|
||||
struct{ Zaphod, Ford string }{"beeblebrox", "prefect"},
|
||||
keyvals{
|
||||
{"Zaphod", stringVal("beeblebrox")},
|
||||
{"Ford", stringVal("prefect")},
|
||||
},
|
||||
},
|
||||
{
|
||||
"int",
|
||||
3,
|
||||
rawVal("3"),
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
if got, want := DefaultConfig.val2node(reflect.ValueOf(test.raw)), test.want; !reflect.DeepEqual(got, want) {
|
||||
t.Errorf("%s: got %#v, want %#v", test.desc, got, want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestVal2node(t *testing.T) {
|
||||
tests := []struct {
|
||||
desc string
|
||||
raw interface{}
|
||||
cfg *Config
|
||||
want node
|
||||
}{
|
||||
{
|
||||
"struct default",
|
||||
struct{ Zaphod, Ford, foo string }{"beeblebrox", "prefect", "BAD"},
|
||||
DefaultConfig,
|
||||
keyvals{
|
||||
{"Zaphod", stringVal("beeblebrox")},
|
||||
{"Ford", stringVal("prefect")},
|
||||
},
|
||||
},
|
||||
{
|
||||
"struct w/ IncludeUnexported",
|
||||
struct{ Zaphod, Ford, foo string }{"beeblebrox", "prefect", "GOOD"},
|
||||
&Config{
|
||||
IncludeUnexported: true,
|
||||
},
|
||||
keyvals{
|
||||
{"Zaphod", stringVal("beeblebrox")},
|
||||
{"Ford", stringVal("prefect")},
|
||||
{"foo", stringVal("GOOD")},
|
||||
},
|
||||
},
|
||||
{
|
||||
"time default",
|
||||
struct{ Date time.Time }{time.Unix(1234567890, 0).UTC()},
|
||||
DefaultConfig,
|
||||
keyvals{
|
||||
{"Date", keyvals{}}, // empty struct, it has unexported fields
|
||||
},
|
||||
},
|
||||
{
|
||||
"time w/ PrintStringers",
|
||||
struct{ Date time.Time }{time.Unix(1234567890, 0).UTC()},
|
||||
&Config{
|
||||
PrintStringers: true,
|
||||
},
|
||||
keyvals{
|
||||
{"Date", stringVal("2009-02-13 23:31:30 +0000 UTC")},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
if got, want := test.cfg.val2node(reflect.ValueOf(test.raw)), test.want; !reflect.DeepEqual(got, want) {
|
||||
t.Errorf("%s: got %#v, want %#v", test.desc, got, want)
|
||||
}
|
||||
}
|
||||
}
|
262
vendor/github.com/kylelemons/godebug/pretty/structure_test.go
generated
vendored
Normal file
262
vendor/github.com/kylelemons/godebug/pretty/structure_test.go
generated
vendored
Normal file
|
@ -0,0 +1,262 @@
|
|||
// Copyright 2013 Google Inc. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package pretty
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestWriteTo(t *testing.T) {
|
||||
tests := []struct {
|
||||
desc string
|
||||
node node
|
||||
normal string
|
||||
extended string
|
||||
}{
|
||||
{
|
||||
desc: "string",
|
||||
node: stringVal("zaphod"),
|
||||
normal: `"zaphod"`,
|
||||
extended: `"zaphod"`,
|
||||
},
|
||||
{
|
||||
desc: "raw",
|
||||
node: rawVal("42"),
|
||||
normal: `42`,
|
||||
extended: `42`,
|
||||
},
|
||||
{
|
||||
desc: "keyvals",
|
||||
node: keyvals{
|
||||
{"name", stringVal("zaphod")},
|
||||
{"age", rawVal("42")},
|
||||
},
|
||||
normal: `{name: "zaphod",
|
||||
age: 42}`,
|
||||
extended: `{
|
||||
name: "zaphod",
|
||||
age: 42,
|
||||
}`,
|
||||
},
|
||||
{
|
||||
desc: "list",
|
||||
node: list{
|
||||
stringVal("zaphod"),
|
||||
rawVal("42"),
|
||||
},
|
||||
normal: `["zaphod",
|
||||
42]`,
|
||||
extended: `[
|
||||
"zaphod",
|
||||
42,
|
||||
]`,
|
||||
},
|
||||
{
|
||||
desc: "nested",
|
||||
node: list{
|
||||
stringVal("first"),
|
||||
list{rawVal("1"), rawVal("2"), rawVal("3")},
|
||||
keyvals{
|
||||
{"trillian", keyvals{
|
||||
{"race", stringVal("human")},
|
||||
{"age", rawVal("36")},
|
||||
}},
|
||||
{"zaphod", keyvals{
|
||||
{"occupation", stringVal("president of the galaxy")},
|
||||
{"features", stringVal("two heads")},
|
||||
}},
|
||||
},
|
||||
keyvals{},
|
||||
},
|
||||
normal: `["first",
|
||||
[1,
|
||||
2,
|
||||
3],
|
||||
{trillian: {race: "human",
|
||||
age: 36},
|
||||
zaphod: {occupation: "president of the galaxy",
|
||||
features: "two heads"}},
|
||||
{}]`,
|
||||
extended: `[
|
||||
"first",
|
||||
[
|
||||
1,
|
||||
2,
|
||||
3,
|
||||
],
|
||||
{
|
||||
trillian: {
|
||||
race: "human",
|
||||
age: 36,
|
||||
},
|
||||
zaphod: {
|
||||
occupation: "president of the galaxy",
|
||||
features: "two heads",
|
||||
},
|
||||
},
|
||||
{},
|
||||
]`,
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
buf := new(bytes.Buffer)
|
||||
test.node.WriteTo(buf, "", &Config{})
|
||||
if got, want := buf.String(), test.normal; got != want {
|
||||
t.Errorf("%s: normal rendendered incorrectly\ngot:\n%s\nwant:\n%s", test.desc, got, want)
|
||||
}
|
||||
buf.Reset()
|
||||
test.node.WriteTo(buf, "", &Config{Diffable: true})
|
||||
if got, want := buf.String(), test.extended; got != want {
|
||||
t.Errorf("%s: extended rendendered incorrectly\ngot:\n%s\nwant:\n%s", test.desc, got, want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestCompactString(t *testing.T) {
|
||||
tests := []struct {
|
||||
node
|
||||
compact string
|
||||
}{
|
||||
{
|
||||
stringVal("abc"),
|
||||
"abc",
|
||||
},
|
||||
{
|
||||
rawVal("2"),
|
||||
"2",
|
||||
},
|
||||
{
|
||||
list{
|
||||
rawVal("2"),
|
||||
rawVal("3"),
|
||||
},
|
||||
"[2,3]",
|
||||
},
|
||||
{
|
||||
keyvals{
|
||||
{"name", stringVal("zaphod")},
|
||||
{"age", rawVal("42")},
|
||||
},
|
||||
`{name:"zaphod",age:42}`,
|
||||
},
|
||||
{
|
||||
list{
|
||||
list{
|
||||
rawVal("0"),
|
||||
rawVal("1"),
|
||||
rawVal("2"),
|
||||
rawVal("3"),
|
||||
},
|
||||
list{
|
||||
rawVal("1"),
|
||||
rawVal("2"),
|
||||
rawVal("3"),
|
||||
rawVal("0"),
|
||||
},
|
||||
list{
|
||||
rawVal("2"),
|
||||
rawVal("3"),
|
||||
rawVal("0"),
|
||||
rawVal("1"),
|
||||
},
|
||||
},
|
||||
`[[0,1,2,3],[1,2,3,0],[2,3,0,1]]`,
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
if got, want := compactString(test.node), test.compact; got != want {
|
||||
t.Errorf("%#v: compact = %q, want %q", test.node, got, want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestShortList(t *testing.T) {
|
||||
cfg := &Config{
|
||||
ShortList: 16,
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
node
|
||||
want string
|
||||
}{
|
||||
{
|
||||
list{
|
||||
list{
|
||||
rawVal("0"),
|
||||
rawVal("1"),
|
||||
rawVal("2"),
|
||||
rawVal("3"),
|
||||
},
|
||||
list{
|
||||
rawVal("1"),
|
||||
rawVal("2"),
|
||||
rawVal("3"),
|
||||
rawVal("0"),
|
||||
},
|
||||
list{
|
||||
rawVal("2"),
|
||||
rawVal("3"),
|
||||
rawVal("0"),
|
||||
rawVal("1"),
|
||||
},
|
||||
},
|
||||
`[[0,1,2,3],
|
||||
[1,2,3,0],
|
||||
[2,3,0,1]]`,
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
buf := new(bytes.Buffer)
|
||||
test.node.WriteTo(buf, "", cfg)
|
||||
if got, want := buf.String(), test.want; got != want {
|
||||
t.Errorf("%#v: got:\n%s\nwant:\n%s", test.node, got, want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var benchNode = keyvals{
|
||||
{"list", list{
|
||||
rawVal("0"),
|
||||
rawVal("1"),
|
||||
rawVal("2"),
|
||||
rawVal("3"),
|
||||
}},
|
||||
{"keyvals", keyvals{
|
||||
{"a", stringVal("b")},
|
||||
{"c", stringVal("e")},
|
||||
{"d", stringVal("f")},
|
||||
}},
|
||||
}
|
||||
|
||||
func benchOpts(b *testing.B, cfg *Config) {
|
||||
buf := new(bytes.Buffer)
|
||||
benchNode.WriteTo(buf, "", cfg)
|
||||
b.SetBytes(int64(buf.Len()))
|
||||
b.ResetTimer()
|
||||
|
||||
for i := 0; i < b.N; i++ {
|
||||
buf.Reset()
|
||||
benchNode.WriteTo(buf, "", cfg)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkWriteDefault(b *testing.B) { benchOpts(b, DefaultConfig) }
|
||||
func BenchmarkWriteShortList(b *testing.B) { benchOpts(b, &Config{ShortList: 16}) }
|
||||
func BenchmarkWriteCompact(b *testing.B) { benchOpts(b, &Config{Compact: true}) }
|
||||
func BenchmarkWriteDiffable(b *testing.B) { benchOpts(b, &Config{Diffable: true}) }
|
436
vendor/github.com/lib/pq/bench_test.go
generated
vendored
Normal file
436
vendor/github.com/lib/pq/bench_test.go
generated
vendored
Normal file
|
@ -0,0 +1,436 @@
|
|||
// +build go1.1
|
||||
|
||||
package pq
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"database/sql"
|
||||
"database/sql/driver"
|
||||
"github.com/lib/pq/oid"
|
||||
"io"
|
||||
"math/rand"
|
||||
"net"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
var (
|
||||
selectStringQuery = "SELECT '" + strings.Repeat("0123456789", 10) + "'"
|
||||
selectSeriesQuery = "SELECT generate_series(1, 100)"
|
||||
)
|
||||
|
||||
func BenchmarkSelectString(b *testing.B) {
|
||||
var result string
|
||||
benchQuery(b, selectStringQuery, &result)
|
||||
}
|
||||
|
||||
func BenchmarkSelectSeries(b *testing.B) {
|
||||
var result int
|
||||
benchQuery(b, selectSeriesQuery, &result)
|
||||
}
|
||||
|
||||
func benchQuery(b *testing.B, query string, result interface{}) {
|
||||
b.Skip("current pq database-backed benchmarks are inconsistent")
|
||||
b.StopTimer()
|
||||
db := openTestConn(b)
|
||||
defer db.Close()
|
||||
b.StartTimer()
|
||||
|
||||
for i := 0; i < b.N; i++ {
|
||||
benchQueryLoop(b, db, query, result)
|
||||
}
|
||||
}
|
||||
|
||||
func benchQueryLoop(b *testing.B, db *sql.DB, query string, result interface{}) {
|
||||
rows, err := db.Query(query)
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
defer rows.Close()
|
||||
for rows.Next() {
|
||||
err = rows.Scan(result)
|
||||
if err != nil {
|
||||
b.Fatal("failed to scan", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// reading from circularConn yields content[:prefixLen] once, followed by
|
||||
// content[prefixLen:] over and over again. It never returns EOF.
|
||||
type circularConn struct {
|
||||
content string
|
||||
prefixLen int
|
||||
pos int
|
||||
net.Conn // for all other net.Conn methods that will never be called
|
||||
}
|
||||
|
||||
func (r *circularConn) Read(b []byte) (n int, err error) {
|
||||
n = copy(b, r.content[r.pos:])
|
||||
r.pos += n
|
||||
if r.pos >= len(r.content) {
|
||||
r.pos = r.prefixLen
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (r *circularConn) Write(b []byte) (n int, err error) { return len(b), nil }
|
||||
|
||||
func (r *circularConn) Close() error { return nil }
|
||||
|
||||
func fakeConn(content string, prefixLen int) *conn {
|
||||
c := &circularConn{content: content, prefixLen: prefixLen}
|
||||
return &conn{buf: bufio.NewReader(c), c: c}
|
||||
}
|
||||
|
||||
// This benchmark is meant to be the same as BenchmarkSelectString, but takes
|
||||
// out some of the factors this package can't control. The numbers are less noisy,
|
||||
// but also the costs of network communication aren't accurately represented.
|
||||
func BenchmarkMockSelectString(b *testing.B) {
|
||||
b.StopTimer()
|
||||
// taken from a recorded run of BenchmarkSelectString
|
||||
// See: http://www.postgresql.org/docs/current/static/protocol-message-formats.html
|
||||
const response = "1\x00\x00\x00\x04" +
|
||||
"t\x00\x00\x00\x06\x00\x00" +
|
||||
"T\x00\x00\x00!\x00\x01?column?\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\xc1\xff\xfe\xff\xff\xff\xff\x00\x00" +
|
||||
"Z\x00\x00\x00\x05I" +
|
||||
"2\x00\x00\x00\x04" +
|
||||
"D\x00\x00\x00n\x00\x01\x00\x00\x00d0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789" +
|
||||
"C\x00\x00\x00\rSELECT 1\x00" +
|
||||
"Z\x00\x00\x00\x05I" +
|
||||
"3\x00\x00\x00\x04" +
|
||||
"Z\x00\x00\x00\x05I"
|
||||
c := fakeConn(response, 0)
|
||||
b.StartTimer()
|
||||
|
||||
for i := 0; i < b.N; i++ {
|
||||
benchMockQuery(b, c, selectStringQuery)
|
||||
}
|
||||
}
|
||||
|
||||
var seriesRowData = func() string {
|
||||
var buf bytes.Buffer
|
||||
for i := 1; i <= 100; i++ {
|
||||
digits := byte(2)
|
||||
if i >= 100 {
|
||||
digits = 3
|
||||
} else if i < 10 {
|
||||
digits = 1
|
||||
}
|
||||
buf.WriteString("D\x00\x00\x00")
|
||||
buf.WriteByte(10 + digits)
|
||||
buf.WriteString("\x00\x01\x00\x00\x00")
|
||||
buf.WriteByte(digits)
|
||||
buf.WriteString(strconv.Itoa(i))
|
||||
}
|
||||
return buf.String()
|
||||
}()
|
||||
|
||||
func BenchmarkMockSelectSeries(b *testing.B) {
|
||||
b.StopTimer()
|
||||
var response = "1\x00\x00\x00\x04" +
|
||||
"t\x00\x00\x00\x06\x00\x00" +
|
||||
"T\x00\x00\x00!\x00\x01?column?\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\xc1\xff\xfe\xff\xff\xff\xff\x00\x00" +
|
||||
"Z\x00\x00\x00\x05I" +
|
||||
"2\x00\x00\x00\x04" +
|
||||
seriesRowData +
|
||||
"C\x00\x00\x00\x0fSELECT 100\x00" +
|
||||
"Z\x00\x00\x00\x05I" +
|
||||
"3\x00\x00\x00\x04" +
|
||||
"Z\x00\x00\x00\x05I"
|
||||
c := fakeConn(response, 0)
|
||||
b.StartTimer()
|
||||
|
||||
for i := 0; i < b.N; i++ {
|
||||
benchMockQuery(b, c, selectSeriesQuery)
|
||||
}
|
||||
}
|
||||
|
||||
func benchMockQuery(b *testing.B, c *conn, query string) {
|
||||
stmt, err := c.Prepare(query)
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
defer stmt.Close()
|
||||
rows, err := stmt.Query(nil)
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
defer rows.Close()
|
||||
var dest [1]driver.Value
|
||||
for {
|
||||
if err := rows.Next(dest[:]); err != nil {
|
||||
if err == io.EOF {
|
||||
break
|
||||
}
|
||||
b.Fatal(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkPreparedSelectString(b *testing.B) {
|
||||
var result string
|
||||
benchPreparedQuery(b, selectStringQuery, &result)
|
||||
}
|
||||
|
||||
func BenchmarkPreparedSelectSeries(b *testing.B) {
|
||||
var result int
|
||||
benchPreparedQuery(b, selectSeriesQuery, &result)
|
||||
}
|
||||
|
||||
func benchPreparedQuery(b *testing.B, query string, result interface{}) {
|
||||
b.Skip("current pq database-backed benchmarks are inconsistent")
|
||||
b.StopTimer()
|
||||
db := openTestConn(b)
|
||||
defer db.Close()
|
||||
stmt, err := db.Prepare(query)
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
defer stmt.Close()
|
||||
b.StartTimer()
|
||||
|
||||
for i := 0; i < b.N; i++ {
|
||||
benchPreparedQueryLoop(b, db, stmt, result)
|
||||
}
|
||||
}
|
||||
|
||||
func benchPreparedQueryLoop(b *testing.B, db *sql.DB, stmt *sql.Stmt, result interface{}) {
|
||||
rows, err := stmt.Query()
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
if !rows.Next() {
|
||||
rows.Close()
|
||||
b.Fatal("no rows")
|
||||
}
|
||||
defer rows.Close()
|
||||
for rows.Next() {
|
||||
err = rows.Scan(&result)
|
||||
if err != nil {
|
||||
b.Fatal("failed to scan")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// See the comment for BenchmarkMockSelectString.
|
||||
func BenchmarkMockPreparedSelectString(b *testing.B) {
|
||||
b.StopTimer()
|
||||
const parseResponse = "1\x00\x00\x00\x04" +
|
||||
"t\x00\x00\x00\x06\x00\x00" +
|
||||
"T\x00\x00\x00!\x00\x01?column?\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\xc1\xff\xfe\xff\xff\xff\xff\x00\x00" +
|
||||
"Z\x00\x00\x00\x05I"
|
||||
const responses = parseResponse +
|
||||
"2\x00\x00\x00\x04" +
|
||||
"D\x00\x00\x00n\x00\x01\x00\x00\x00d0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789" +
|
||||
"C\x00\x00\x00\rSELECT 1\x00" +
|
||||
"Z\x00\x00\x00\x05I"
|
||||
c := fakeConn(responses, len(parseResponse))
|
||||
|
||||
stmt, err := c.Prepare(selectStringQuery)
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
b.StartTimer()
|
||||
|
||||
for i := 0; i < b.N; i++ {
|
||||
benchPreparedMockQuery(b, c, stmt)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkMockPreparedSelectSeries(b *testing.B) {
|
||||
b.StopTimer()
|
||||
const parseResponse = "1\x00\x00\x00\x04" +
|
||||
"t\x00\x00\x00\x06\x00\x00" +
|
||||
"T\x00\x00\x00!\x00\x01?column?\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\xc1\xff\xfe\xff\xff\xff\xff\x00\x00" +
|
||||
"Z\x00\x00\x00\x05I"
|
||||
var responses = parseResponse +
|
||||
"2\x00\x00\x00\x04" +
|
||||
seriesRowData +
|
||||
"C\x00\x00\x00\x0fSELECT 100\x00" +
|
||||
"Z\x00\x00\x00\x05I"
|
||||
c := fakeConn(responses, len(parseResponse))
|
||||
|
||||
stmt, err := c.Prepare(selectSeriesQuery)
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
b.StartTimer()
|
||||
|
||||
for i := 0; i < b.N; i++ {
|
||||
benchPreparedMockQuery(b, c, stmt)
|
||||
}
|
||||
}
|
||||
|
||||
func benchPreparedMockQuery(b *testing.B, c *conn, stmt driver.Stmt) {
|
||||
rows, err := stmt.Query(nil)
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
defer rows.Close()
|
||||
var dest [1]driver.Value
|
||||
for {
|
||||
if err := rows.Next(dest[:]); err != nil {
|
||||
if err == io.EOF {
|
||||
break
|
||||
}
|
||||
b.Fatal(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkEncodeInt64(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
encode(¶meterStatus{}, int64(1234), oid.T_int8)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkEncodeFloat64(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
encode(¶meterStatus{}, 3.14159, oid.T_float8)
|
||||
}
|
||||
}
|
||||
|
||||
var testByteString = []byte("abcdefghijklmnopqrstuvwxyz")
|
||||
|
||||
func BenchmarkEncodeByteaHex(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
encode(¶meterStatus{serverVersion: 90000}, testByteString, oid.T_bytea)
|
||||
}
|
||||
}
|
||||
func BenchmarkEncodeByteaEscape(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
encode(¶meterStatus{serverVersion: 84000}, testByteString, oid.T_bytea)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkEncodeBool(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
encode(¶meterStatus{}, true, oid.T_bool)
|
||||
}
|
||||
}
|
||||
|
||||
var testTimestamptz = time.Date(2001, time.January, 1, 0, 0, 0, 0, time.Local)
|
||||
|
||||
func BenchmarkEncodeTimestamptz(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
encode(¶meterStatus{}, testTimestamptz, oid.T_timestamptz)
|
||||
}
|
||||
}
|
||||
|
||||
var testIntBytes = []byte("1234")
|
||||
|
||||
func BenchmarkDecodeInt64(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
decode(¶meterStatus{}, testIntBytes, oid.T_int8)
|
||||
}
|
||||
}
|
||||
|
||||
var testFloatBytes = []byte("3.14159")
|
||||
|
||||
func BenchmarkDecodeFloat64(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
decode(¶meterStatus{}, testFloatBytes, oid.T_float8)
|
||||
}
|
||||
}
|
||||
|
||||
var testBoolBytes = []byte{'t'}
|
||||
|
||||
func BenchmarkDecodeBool(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
decode(¶meterStatus{}, testBoolBytes, oid.T_bool)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDecodeBool(t *testing.T) {
|
||||
db := openTestConn(t)
|
||||
rows, err := db.Query("select true")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
rows.Close()
|
||||
}
|
||||
|
||||
var testTimestamptzBytes = []byte("2013-09-17 22:15:32.360754-07")
|
||||
|
||||
func BenchmarkDecodeTimestamptz(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
decode(¶meterStatus{}, testTimestamptzBytes, oid.T_timestamptz)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkDecodeTimestamptzMultiThread(b *testing.B) {
|
||||
oldProcs := runtime.GOMAXPROCS(0)
|
||||
defer runtime.GOMAXPROCS(oldProcs)
|
||||
runtime.GOMAXPROCS(runtime.NumCPU())
|
||||
globalLocationCache = newLocationCache()
|
||||
|
||||
f := func(wg *sync.WaitGroup, loops int) {
|
||||
defer wg.Done()
|
||||
for i := 0; i < loops; i++ {
|
||||
decode(¶meterStatus{}, testTimestamptzBytes, oid.T_timestamptz)
|
||||
}
|
||||
}
|
||||
|
||||
wg := &sync.WaitGroup{}
|
||||
b.ResetTimer()
|
||||
for j := 0; j < 10; j++ {
|
||||
wg.Add(1)
|
||||
go f(wg, b.N/10)
|
||||
}
|
||||
wg.Wait()
|
||||
}
|
||||
|
||||
func BenchmarkLocationCache(b *testing.B) {
|
||||
globalLocationCache = newLocationCache()
|
||||
for i := 0; i < b.N; i++ {
|
||||
globalLocationCache.getLocation(rand.Intn(10000))
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkLocationCacheMultiThread(b *testing.B) {
|
||||
oldProcs := runtime.GOMAXPROCS(0)
|
||||
defer runtime.GOMAXPROCS(oldProcs)
|
||||
runtime.GOMAXPROCS(runtime.NumCPU())
|
||||
globalLocationCache = newLocationCache()
|
||||
|
||||
f := func(wg *sync.WaitGroup, loops int) {
|
||||
defer wg.Done()
|
||||
for i := 0; i < loops; i++ {
|
||||
globalLocationCache.getLocation(rand.Intn(10000))
|
||||
}
|
||||
}
|
||||
|
||||
wg := &sync.WaitGroup{}
|
||||
b.ResetTimer()
|
||||
for j := 0; j < 10; j++ {
|
||||
wg.Add(1)
|
||||
go f(wg, b.N/10)
|
||||
}
|
||||
wg.Wait()
|
||||
}
|
||||
|
||||
// Stress test the performance of parsing results from the wire.
|
||||
func BenchmarkResultParsing(b *testing.B) {
|
||||
b.StopTimer()
|
||||
|
||||
db := openTestConn(b)
|
||||
defer db.Close()
|
||||
_, err := db.Exec("BEGIN")
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
|
||||
b.StartTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
res, err := db.Query("SELECT generate_series(1, 50000)")
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
res.Close()
|
||||
}
|
||||
}
|
1244
vendor/github.com/lib/pq/conn_test.go
generated
vendored
Normal file
1244
vendor/github.com/lib/pq/conn_test.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load diff
Some files were not shown because too many files have changed in this diff Show more
Reference in a new issue