feature/increase_test_coverage #112

Merged
alexvanin merged 3 commits from r.loginov/frostfs-http-gw:feature/increase_test_coverage into master 2024-09-04 19:50:57 +00:00
4 changed files with 272 additions and 45 deletions

View file

@ -6,25 +6,30 @@ import (
"archive/zip" "archive/zip"
"bytes" "bytes"
"context" "context"
"encoding/base64"
"encoding/json" "encoding/json"
"fmt" "fmt"
"io" "io"
"mime/multipart" "mime/multipart"
"net/http" "net/http"
"os"
"sort" "sort"
"testing" "testing"
"time" "time"
containerv2 "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/container" containerv2 "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/container"
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/bearer"
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container" "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container"
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/acl" "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/acl"
cid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id" cid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id"
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/eacl"
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/netmap" "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/netmap"
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object" "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object"
oid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/id" oid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/id"
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/pool" "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/pool"
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/user" "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/user"
"github.com/nspcc-dev/neo-go/pkg/crypto/keys" "github.com/nspcc-dev/neo-go/pkg/crypto/keys"
"github.com/nspcc-dev/neo-go/pkg/wallet"
"github.com/spf13/viper" "github.com/spf13/viper"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"github.com/testcontainers/testcontainers-go" "github.com/testcontainers/testcontainers-go"
@ -47,11 +52,17 @@ func TestIntegration(t *testing.T) {
rootCtx := context.Background() rootCtx := context.Background()
aioImage := "truecloudlab/frostfs-aio:" aioImage := "truecloudlab/frostfs-aio:"
versions := []string{ versions := []string{
"1.2.7", // frostfs-storage v0.36.0 RC "1.2.7",
"1.3.0",
} }
key, err := keys.NewPrivateKeyFromHex("1dd37fba80fec4e6a6f13fd708d8dcb3b29def768017052f6c930fa1c5d90bbb") key, err := keys.NewPrivateKeyFromHex("1dd37fba80fec4e6a6f13fd708d8dcb3b29def768017052f6c930fa1c5d90bbb")
require.NoError(t, err) require.NoError(t, err)
file, err := os.CreateTemp("", "wallet")
require.NoError(t, err)
defer os.Remove(file.Name())
makeTempWallet(t, key, file.Name())
var ownerID user.ID var ownerID user.ID
user.IDFromKey(&ownerID, key.PrivateKey.PublicKey) user.IDFromKey(&ownerID, key.PrivateKey.PublicKey)
@ -59,12 +70,16 @@ func TestIntegration(t *testing.T) {
ctx, cancel2 := context.WithCancel(rootCtx) ctx, cancel2 := context.WithCancel(rootCtx)
aioContainer := createDockerContainer(ctx, t, aioImage+version) aioContainer := createDockerContainer(ctx, t, aioImage+version)
server, cancel := runServer() server, cancel := runServer(file.Name())
clientPool := getPool(ctx, t, key) clientPool := getPool(ctx, t, key)
CID, err := createContainer(ctx, t, clientPool, ownerID, version) CID, err := createContainer(ctx, t, clientPool, ownerID, version)
require.NoError(t, err, version) require.NoError(t, err, version)
token := makeBearerToken(t, key, ownerID, version)
t.Run("simple put "+version, func(t *testing.T) { simplePut(ctx, t, clientPool, CID, version) }) t.Run("simple put "+version, func(t *testing.T) { simplePut(ctx, t, clientPool, CID, version) })
t.Run("put with bearer token in header"+version, func(t *testing.T) { putWithBearerTokenInHeader(ctx, t, clientPool, CID, token) })
t.Run("put with bearer token in cookie"+version, func(t *testing.T) { putWithBearerTokenInCookie(ctx, t, clientPool, CID, token) })
t.Run("put with duplicate keys "+version, func(t *testing.T) { putWithDuplicateKeys(t, CID) }) t.Run("put with duplicate keys "+version, func(t *testing.T) { putWithDuplicateKeys(t, CID) })
t.Run("simple get "+version, func(t *testing.T) { simpleGet(ctx, t, clientPool, ownerID, CID, version) }) t.Run("simple get "+version, func(t *testing.T) { simpleGet(ctx, t, clientPool, ownerID, CID, version) })
t.Run("get by attribute "+version, func(t *testing.T) { getByAttr(ctx, t, clientPool, ownerID, CID, version) }) t.Run("get by attribute "+version, func(t *testing.T) { getByAttr(ctx, t, clientPool, ownerID, CID, version) })
@ -79,10 +94,13 @@ func TestIntegration(t *testing.T) {
} }
} }
func runServer() (App, context.CancelFunc) { func runServer(pathToWallet string) (App, context.CancelFunc) {
cancelCtx, cancel := context.WithCancel(context.Background()) cancelCtx, cancel := context.WithCancel(context.Background())
v := getDefaultConfig() v := getDefaultConfig()
v.Set(cfgWalletPath, pathToWallet)
v.Set(cfgWalletPassphrase, "")
l, lvl := newStdoutLogger(zapcore.DebugLevel) l, lvl := newStdoutLogger(zapcore.DebugLevel)
application := newApp(cancelCtx, WithConfig(v), WithLogger(l, lvl)) application := newApp(cancelCtx, WithConfig(v), WithLogger(l, lvl))
go application.Serve() go application.Serve()
@ -98,7 +116,38 @@ func simplePut(ctx context.Context, t *testing.T, p *pool.Pool, CID cid.ID, vers
makePutRequestAndCheck(ctx, t, p, CID, url) makePutRequestAndCheck(ctx, t, p, CID, url)
} }
func putWithBearerTokenInHeader(ctx context.Context, t *testing.T, p *pool.Pool, CID cid.ID, token string) {
url := testHost + "/upload/" + CID.String()
request, content, attributes := makePutRequest(t, url)
request.Header.Set("Authorization", "Bearer "+token)
resp, err := http.DefaultClient.Do(request)
require.NoError(t, err)
checkPutResponse(ctx, t, p, CID, resp, content, attributes)
}
func putWithBearerTokenInCookie(ctx context.Context, t *testing.T, p *pool.Pool, CID cid.ID, token string) {
url := testHost + "/upload/" + CID.String()
request, content, attributes := makePutRequest(t, url)
request.AddCookie(&http.Cookie{Name: "Bearer", Value: token})
resp, err := http.DefaultClient.Do(request)
require.NoError(t, err)
checkPutResponse(ctx, t, p, CID, resp, content, attributes)
}
func makePutRequestAndCheck(ctx context.Context, t *testing.T, p *pool.Pool, cnrID cid.ID, url string) { func makePutRequestAndCheck(ctx context.Context, t *testing.T, p *pool.Pool, cnrID cid.ID, url string) {
request, content, attributes := makePutRequest(t, url)
resp, err := http.DefaultClient.Do(request)
require.NoError(t, err)
checkPutResponse(ctx, t, p, cnrID, resp, content, attributes)
}
func makePutRequest(t *testing.T, url string) (*http.Request, string, map[string]string) {
content := "content of file" content := "content of file"
keyAttr, valAttr := "User-Attribute", "user value" keyAttr, valAttr := "User-Attribute", "user value"
attributes := map[string]string{ attributes := map[string]string{
@ -120,9 +169,10 @@ func makePutRequestAndCheck(ctx context.Context, t *testing.T, p *pool.Pool, cnr
request.Header.Set("Content-Type", w.FormDataContentType()) request.Header.Set("Content-Type", w.FormDataContentType())
request.Header.Set("X-Attribute-"+keyAttr, valAttr) request.Header.Set("X-Attribute-"+keyAttr, valAttr)
resp, err := http.DefaultClient.Do(request) return request, content, attributes
require.NoError(t, err) }
func checkPutResponse(ctx context.Context, t *testing.T, p *pool.Pool, cnrID cid.ID, resp *http.Response, content string, attributes map[string]string) {
defer func() { defer func() {
err := resp.Body.Close() err := resp.Body.Close()
require.NoError(t, err) require.NoError(t, err)
@ -476,3 +526,37 @@ func putObject(ctx context.Context, t *testing.T, clientPool *pool.Pool, ownerID
return id return id
} }
func makeBearerToken(t *testing.T, key *keys.PrivateKey, ownerID user.ID, version string) string {
tkn := new(bearer.Token)
tkn.ForUser(ownerID)
tkn.SetExp(10000)
if version == "1.2.7" {
tkn.SetEACLTable(*eacl.NewTable())
} else {
tkn.SetImpersonate(true)
}
err := tkn.Sign(key.PrivateKey)
require.NoError(t, err)
t64 := base64.StdEncoding.EncodeToString(tkn.Marshal())
require.NotEmpty(t, t64)
return t64
}
func makeTempWallet(t *testing.T, key *keys.PrivateKey, path string) {
w, err := wallet.NewWallet(path)
require.NoError(t, err)
acc := wallet.NewAccountFromPrivateKey(key)
err = acc.Encrypt("", w.Scrypt)
require.NoError(t, err)
w.AddAccount(acc)
err = w.Save()
require.NoError(t, err)
}

10
go.mod
View file

@ -22,7 +22,7 @@ require (
go.opentelemetry.io/otel/trace v1.16.0 go.opentelemetry.io/otel/trace v1.16.0
go.uber.org/zap v1.24.0 go.uber.org/zap v1.24.0
golang.org/x/exp v0.0.0-20230515195305-f3d0a9c9a5cc golang.org/x/exp v0.0.0-20230515195305-f3d0a9c9a5cc
golang.org/x/net v0.10.0 golang.org/x/net v0.23.0
google.golang.org/grpc v1.55.0 google.golang.org/grpc v1.55.0
) )
@ -103,11 +103,11 @@ require (
go.opentelemetry.io/proto/otlp v0.19.0 // indirect go.opentelemetry.io/proto/otlp v0.19.0 // indirect
go.uber.org/atomic v1.10.0 // indirect go.uber.org/atomic v1.10.0 // indirect
go.uber.org/multierr v1.11.0 // indirect go.uber.org/multierr v1.11.0 // indirect
golang.org/x/crypto v0.9.0 // indirect golang.org/x/crypto v0.21.0 // indirect
golang.org/x/sync v0.2.0 // indirect golang.org/x/sync v0.2.0 // indirect
golang.org/x/sys v0.8.0 // indirect golang.org/x/sys v0.18.0 // indirect
golang.org/x/term v0.8.0 // indirect golang.org/x/term v0.18.0 // indirect
golang.org/x/text v0.9.0 // indirect golang.org/x/text v0.14.0 // indirect
golang.org/x/time v0.3.0 // indirect golang.org/x/time v0.3.0 // indirect
google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1 // indirect google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1 // indirect
google.golang.org/protobuf v1.30.0 // indirect google.golang.org/protobuf v1.30.0 // indirect

20
go.sum
View file

@ -1024,8 +1024,8 @@ golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5
golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20220214200702-86341886e292/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20220214200702-86341886e292/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.9.0 h1:LF6fAI+IutBocDJ2OT0Q1g8plpYljMZ4+lty+dsqw3g= golang.org/x/crypto v0.21.0 h1:X31++rzVUdKhX5sWmSOFZxx8UW/ldWx55cbf08iNAMA=
golang.org/x/crypto v0.9.0/go.mod h1:yrmDGqONDYtNj3tH8X9dzUun2m2lzPa9ngI6/RUPGR0= golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
@ -1118,8 +1118,8 @@ golang.org/x/net v0.0.0-20211108170745-6635138e15ea/go.mod h1:9nx3DQGgdP8bBQD5qx
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
golang.org/x/net v0.10.0 h1:X2//UzNDwYmtCLn7To6G58Wr6f5ahEAQgKNzv9Y951M= golang.org/x/net v0.23.0 h1:7EYJ93RZ9vYSZAIb2x3lnuvqO5zneoD6IvWjuhfxjTs=
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= golang.org/x/net v0.23.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
@ -1245,13 +1245,13 @@ golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.8.0 h1:EBmGv8NaZBZTWvrbjNoL6HVt+IVy3QDQpJs7VRIw3tU= golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4=
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210429154555-c04ba851c2a4/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210429154555-c04ba851c2a4/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.8.0 h1:n5xxQn2i3PC0yLAbjTpNT85q/Kgzcr2gIoX9OrJUols= golang.org/x/term v0.18.0 h1:FcHjZXDMxI8mM3nwhX9HlKop4C0YQvCVCdwYl2wOtE8=
golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= golang.org/x/term v0.18.0/go.mod h1:ILwASektA3OnRv7amZ1xhE/KTR+u50pbXfZ03+6Nx58=
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
@ -1261,8 +1261,8 @@ golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE= golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=

View file

@ -23,19 +23,29 @@ func makeTestCookie(value []byte) *fasthttp.RequestHeader {
func makeTestHeader(value []byte) *fasthttp.RequestHeader { func makeTestHeader(value []byte) *fasthttp.RequestHeader {
header := new(fasthttp.RequestHeader) header := new(fasthttp.RequestHeader)
if value != nil { if value != nil {
header.Set(fasthttp.HeaderAuthorization, bearerTokenHdr+" "+string(value)) header.Set(fasthttp.HeaderAuthorization, string(value))
} }
return header return header
} }
func Test_fromCookie(t *testing.T) { func makeBearer(value string) string {
return bearerTokenHdr + " " + value
}
func TestBearerTokenFromCookie(t *testing.T) {
cases := []struct { cases := []struct {
name string name string
actual []byte actual []byte
expect []byte expect []byte
}{ }{
{name: "empty"}, {
{name: "normal", actual: []byte("TOKEN"), expect: []byte("TOKEN")}, name: "empty",
},
{
name: "normal",
actual: []byte("TOKEN"),
expect: []byte("TOKEN"),
},
} }
for _, tt := range cases { for _, tt := range cases {
@ -45,14 +55,31 @@ func Test_fromCookie(t *testing.T) {
} }
} }
func Test_fromHeader(t *testing.T) { func TestBearerTokenFromHeader(t *testing.T) {
validToken := "token"
tokenWithoutPrefix := "invalid-token"
cases := []struct { cases := []struct {
name string name string
actual []byte actual []byte
expect []byte expect []byte
}{ }{
{name: "empty"}, {
{name: "normal", actual: []byte("TOKEN"), expect: []byte("TOKEN")}, name: "empty",
},
{
name: "token without the bearer prefix",
actual: []byte(tokenWithoutPrefix),
},
{
name: "token without payload",
actual: []byte(makeBearer("")),
},
{
name: "normal",
actual: []byte(makeBearer(validToken)),
expect: []byte(validToken),
},
} }
for _, tt := range cases { for _, tt := range cases {
@ -62,7 +89,7 @@ func Test_fromHeader(t *testing.T) {
} }
} }
func Test_fetchBearerToken(t *testing.T) { func TestFetchBearerToken(t *testing.T) {
key, err := keys.NewPrivateKey() key, err := keys.NewPrivateKey()
require.NoError(t, err) require.NoError(t, err)
var uid user.ID var uid user.ID
@ -75,43 +102,77 @@ func Test_fetchBearerToken(t *testing.T) {
require.NotEmpty(t, t64) require.NotEmpty(t, t64)
cases := []struct { cases := []struct {
name string name string
cookie string cookie string
header string header string
error string error string
nilCtx bool
expect *bearer.Token expect *bearer.Token
}{ }{
{name: "empty"}, {
name: "empty",
{name: "bad base64 header", header: "WRONG BASE64", error: "can't base64-decode bearer token"}, },
{name: "bad base64 cookie", cookie: "WRONG BASE64", error: "can't base64-decode bearer token"}, {
name: "nil context",
{name: "header token unmarshal error", header: "dGVzdAo=", error: "can't unmarshal bearer token"}, nilCtx: true,
{name: "cookie token unmarshal error", cookie: "dGVzdAo=", error: "can't unmarshal bearer token"}, },
{
name: "bad base64 header",
header: "WRONG BASE64",
error: "can't base64-decode bearer token",
},
{
name: "bad base64 cookie",
cookie: "WRONG BASE64",
error: "can't base64-decode bearer token",
},
{
name: "header token unmarshal error",
header: "dGVzdAo=",
error: "can't unmarshal bearer token",
},
{
name: "cookie token unmarshal error",
cookie: "dGVzdAo=",
error: "can't unmarshal bearer token",
},
{ {
name: "bad header and cookie", name: "bad header and cookie",
header: "WRONG BASE64", header: "WRONG BASE64",
cookie: "dGVzdAo=", cookie: "dGVzdAo=",
error: "can't unmarshal bearer token", error: "can't unmarshal bearer token",
}, },
{ {
name: "bad header, but good cookie", name: "bad header, but good cookie",
header: "dGVzdAo=", header: "dGVzdAo=",
cookie: t64, cookie: t64,
expect: tkn, expect: tkn,
}, },
{
{name: "ok for header", header: t64, expect: tkn}, name: "bad cookie, but good header",
{name: "ok for cookie", cookie: t64, expect: tkn}, header: t64,
cookie: "dGVzdAo=",
expect: tkn,
},
{
name: "ok for header",
header: t64,
expect: tkn,
},
{
name: "ok for cookie",
cookie: t64,
expect: tkn,
},
} }
for _, tt := range cases { for _, tt := range cases {
t.Run(tt.name, func(t *testing.T) { t.Run(tt.name, func(t *testing.T) {
ctx := makeTestRequest(tt.cookie, tt.header) var ctx *fasthttp.RequestCtx
if !tt.nilCtx {
ctx = makeTestRequest(tt.cookie, tt.header)
}
actual, err := fetchBearerToken(ctx) actual, err := fetchBearerToken(ctx)
if tt.error == "" { if tt.error == "" {
@ -139,7 +200,7 @@ func makeTestRequest(cookie, header string) *fasthttp.RequestCtx {
return ctx return ctx
} }
func Test_checkAndPropagateBearerToken(t *testing.T) { func TestCheckAndPropagateBearerToken(t *testing.T) {
key, err := keys.NewPrivateKey() key, err := keys.NewPrivateKey()
require.NoError(t, err) require.NoError(t, err)
var uid user.ID var uid user.ID
@ -162,3 +223,85 @@ func Test_checkAndPropagateBearerToken(t *testing.T) {
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, tkn, actual) require.Equal(t, tkn, actual)
} }
func TestLoadBearerToken(t *testing.T) {
ctx := context.Background()
token := new(bearer.Token)
cases := []struct {
name string
appCtx context.Context
error string
}{
{
name: "token is missing in the context",
appCtx: ctx,
error: "found empty bearer token",
},
{
name: "normal",
appCtx: context.WithValue(ctx, bearerTokenKey, token),
},
}
for _, tt := range cases {
t.Run(tt.name, func(t *testing.T) {
tkn, err := LoadBearerToken(tt.appCtx)
if tt.error == "" {
require.NoError(t, err)
require.Equal(t, token, tkn)
return
}
require.Contains(t, err.Error(), tt.error)
})
}
}
func TestStoreBearerTokenAppCtx(t *testing.T) {
key, err := keys.NewPrivateKey()
require.NoError(t, err)
var uid user.ID
user.IDFromKey(&uid, key.PrivateKey.PublicKey)
tkn := new(bearer.Token)
tkn.ForUser(uid)
t64 := base64.StdEncoding.EncodeToString(tkn.Marshal())
require.NotEmpty(t, t64)
cases := []struct {
name string
req *fasthttp.RequestCtx
error string
}{
{
name: "invalid token",
req: makeTestRequest("dGVzdAo=", ""),
error: "can't unmarshal bearer token",
},
{
name: "normal",
req: makeTestRequest(t64, ""),
},
}
for _, tt := range cases {
t.Run(tt.name, func(t *testing.T) {
ctx, err := StoreBearerTokenAppCtx(context.Background(), tt.req)
if tt.error == "" {
require.NoError(t, err)
actualToken, ok := ctx.Value(bearerTokenKey).(*bearer.Token)
require.True(t, ok)
require.Equal(t, tkn, actualToken)
return
}
require.Contains(t, err.Error(), tt.error)
})
}
}