storj/pkg/linksharing/handler_test.go

285 lines
6.7 KiB
Go
Raw Normal View History

// Copyright (C) 2019 Storj Labs, Inc.
// See LICENSE for copying information.
package linksharing
import (
"context"
"net/http"
"net/http/httptest"
"path"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"go.uber.org/zap/zaptest"
"storj.io/storj/internal/testcontext"
"storj.io/storj/internal/testplanet"
"storj.io/storj/lib/uplink"
"storj.io/storj/pkg/storj"
)
func TestNewHandler(t *testing.T) {
ctx := testcontext.New(t)
defer ctx.Cleanup()
uplink := newUplink(ctx, t)
defer ctx.Check(uplink.Close)
testCases := []struct {
name string
config HandlerConfig
err string
}{
{
name: "missing uplink",
config: HandlerConfig{
URLBase: "http://localhost",
},
err: "uplink is required",
},
{
name: "URL base must be http or https",
config: HandlerConfig{
Uplink: uplink,
URLBase: "gopher://chunks",
},
err: "URL base must be http:// or https://",
},
{
name: "URL base must contain host",
config: HandlerConfig{
Uplink: uplink,
URLBase: "http://",
},
err: "URL base must contain host",
},
{
name: "URL base can have a port",
config: HandlerConfig{
Uplink: uplink,
URLBase: "http://host:99",
},
},
{
name: "URL base can have a path",
config: HandlerConfig{
Uplink: uplink,
URLBase: "http://host/gopher",
},
},
{
name: "URL base must not contain user info",
config: HandlerConfig{
Uplink: uplink,
URLBase: "http://joe@host",
},
err: "URL base must not contain user info",
},
{
name: "URL base must not contain query values",
config: HandlerConfig{
Uplink: uplink,
URLBase: "http://host/?gopher=chunks",
},
err: "URL base must not contain query values",
},
{
name: "URL base must not contain a fragment",
config: HandlerConfig{
Uplink: uplink,
URLBase: "http://host/#gopher-chunks",
},
err: "URL base must not contain a fragment",
},
}
for _, testCase := range testCases {
testCase := testCase
t.Run(testCase.name, func(t *testing.T) {
handler, err := NewHandler(testCase.config)
if testCase.err != "" {
require.EqualError(t, err, testCase.err)
return
}
require.NoError(t, err)
require.NotNil(t, handler)
})
}
}
func TestHandlerRequests(t *testing.T) {
testplanet.Run(t, testplanet.Config{
SatelliteCount: 2,
StorageNodeCount: 1,
UplinkCount: 1,
}, testHandlerRequests)
}
func testHandlerRequests(t *testing.T, ctx *testcontext.Context, planet *testplanet.Planet) {
err := planet.Uplinks[0].Upload(ctx, planet.Satellites[0], "testbucket", "test/foo", []byte("FOO"))
require.NoError(t, err)
apiKey, err := uplink.ParseAPIKey(planet.Uplinks[0].APIKey[planet.Satellites[0].ID()])
require.NoError(t, err)
scope, err := (&uplink.Scope{
SatelliteAddr: planet.Satellites[0].Addr(),
APIKey: apiKey,
EncryptionAccess: uplink.NewEncryptionAccessWithDefaultKey(storj.Key{}),
}).Serialize()
require.NoError(t, err)
testCases := []struct {
name string
method string
path string
status int
header http.Header
body string
}{
{
name: "invalid method",
method: "PUT",
status: http.StatusMethodNotAllowed,
body: "method not allowed\n",
},
{
name: "GET missing scope",
method: "GET",
status: http.StatusBadRequest,
body: "invalid request: missing scope\n",
},
{
name: "GET malformed scope",
method: "GET",
path: path.Join("BADSCOPE", "testbucket", "test/foo"),
status: http.StatusBadRequest,
body: "invalid request: invalid scope format\n",
},
{
name: "GET missing bucket",
method: "GET",
path: scope,
status: http.StatusBadRequest,
body: "invalid request: missing bucket\n",
},
{
name: "GET bucket not found",
method: "GET",
path: path.Join(scope, "someotherbucket", "test/foo"),
status: http.StatusNotFound,
body: "bucket not found\n",
},
{
name: "GET missing bucket path",
method: "GET",
path: path.Join(scope, "testbucket"),
status: http.StatusBadRequest,
body: "invalid request: missing bucket path\n",
},
{
name: "GET object not found",
method: "GET",
path: path.Join(scope, "testbucket", "test/bar"),
status: http.StatusNotFound,
body: "object not found\n",
},
{
name: "GET success",
method: "GET",
path: path.Join(scope, "testbucket", "test/foo"),
status: http.StatusOK,
body: "FOO",
},
{
name: "HEAD missing scope",
method: "HEAD",
status: http.StatusBadRequest,
body: "invalid request: missing scope\n",
},
{
name: "HEAD malformed scope",
method: "HEAD",
path: path.Join("BADSCOPE", "testbucket", "test/foo"),
status: http.StatusBadRequest,
body: "invalid request: invalid scope format\n",
},
{
name: "HEAD missing bucket",
method: "HEAD",
path: scope,
status: http.StatusBadRequest,
body: "invalid request: missing bucket\n",
},
{
name: "HEAD bucket not found",
method: "HEAD",
path: path.Join(scope, "someotherbucket", "test/foo"),
status: http.StatusNotFound,
body: "bucket not found\n",
},
{
name: "HEAD missing bucket path",
method: "HEAD",
path: path.Join(scope, "testbucket"),
status: http.StatusBadRequest,
body: "invalid request: missing bucket path\n",
},
{
name: "HEAD object not found",
method: "HEAD",
path: path.Join(scope, "testbucket", "test/bar"),
status: http.StatusNotFound,
body: "object not found\n",
},
{
name: "HEAD success",
method: "HEAD",
path: path.Join(scope, "testbucket", "test/foo"),
status: http.StatusFound,
header: http.Header{
"Location": []string{"http://localhost/" + path.Join(scope, "testbucket", "test/foo")},
},
body: "",
},
}
for _, testCase := range testCases {
testCase := testCase
t.Run(testCase.name, func(t *testing.T) {
uplink := newUplink(ctx, t)
defer ctx.Check(uplink.Close)
handler, err := NewHandler(HandlerConfig{
Log: zaptest.NewLogger(t),
Uplink: uplink,
URLBase: "http://localhost",
})
require.NoError(t, err)
url := "http://localhost/" + testCase.path
w := httptest.NewRecorder()
r, err := http.NewRequest(testCase.method, url, nil)
require.NoError(t, err)
handler.ServeHTTP(w, r)
assert.Equal(t, testCase.status, w.Code, "status code does not match")
for h, v := range testCase.header {
assert.Equal(t, v, w.Header()[h], "%q header does not match", h)
}
assert.Equal(t, testCase.body, w.Body.String(), "body does not match")
})
}
}
func newUplink(ctx context.Context, tb testing.TB) *uplink.Uplink {
cfg := new(uplink.Config)
cfg.Volatile.TLS.SkipPeerCAWhitelist = true
up, err := uplink.NewUplink(ctx, cfg)
require.NoError(tb, err)
return up
}