test(http): add unit tests for /api/v2/write (#15128)

test(http): add unit tests for /api/v2/write
pull/15138/head
Chris Goller 2019-09-12 13:57:06 -05:00 committed by GitHub
commit 5b4c0db4a0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 379 additions and 70 deletions

View File

@ -4,7 +4,7 @@ import (
"context" "context"
"fmt" "fmt"
platform "github.com/influxdata/influxdb" "github.com/influxdata/influxdb"
) )
type contextKey string type contextKey string
@ -14,17 +14,23 @@ const (
) )
// SetAuthorizer sets an authorizer on context. // SetAuthorizer sets an authorizer on context.
func SetAuthorizer(ctx context.Context, a platform.Authorizer) context.Context { func SetAuthorizer(ctx context.Context, a influxdb.Authorizer) context.Context {
return context.WithValue(ctx, authorizerCtxKey, a) return context.WithValue(ctx, authorizerCtxKey, a)
} }
// GetAuthorizer retrieves an authorizer from context. // GetAuthorizer retrieves an authorizer from context.
func GetAuthorizer(ctx context.Context) (platform.Authorizer, error) { func GetAuthorizer(ctx context.Context) (influxdb.Authorizer, error) {
a, ok := ctx.Value(authorizerCtxKey).(platform.Authorizer) a, ok := ctx.Value(authorizerCtxKey).(influxdb.Authorizer)
if !ok { if !ok {
return nil, &platform.Error{ return nil, &influxdb.Error{
Msg: "authorizer not found on context", Msg: "authorizer not found on context",
Code: platform.EInternal, Code: influxdb.EInternal,
}
}
if a == nil {
return nil, &influxdb.Error{
Code: influxdb.EInternal,
Msg: "unexpected invalid authorizer",
} }
} }
@ -33,19 +39,19 @@ func GetAuthorizer(ctx context.Context) (platform.Authorizer, error) {
// GetToken retrieves a token from the context; errors if no token. // GetToken retrieves a token from the context; errors if no token.
func GetToken(ctx context.Context) (string, error) { func GetToken(ctx context.Context) (string, error) {
a, ok := ctx.Value(authorizerCtxKey).(platform.Authorizer) a, ok := ctx.Value(authorizerCtxKey).(influxdb.Authorizer)
if !ok { if !ok {
return "", &platform.Error{ return "", &influxdb.Error{
Msg: "authorizer not found on context", Msg: "authorizer not found on context",
Code: platform.EInternal, Code: influxdb.EInternal,
} }
} }
auth, ok := a.(*platform.Authorization) auth, ok := a.(*influxdb.Authorization)
if !ok { if !ok {
return "", &platform.Error{ return "", &influxdb.Error{
Msg: fmt.Sprintf("authorizer not an authorization but a %T", a), Msg: fmt.Sprintf("authorizer not an authorization but a %T", a),
Code: platform.EInternal, Code: influxdb.EInternal,
} }
} }

View File

@ -121,16 +121,16 @@ func (h *AuthenticationHandler) extractSession(ctx context.Context, r *http.Requ
return ctx, err return ctx, err
} }
s, e := h.SessionService.FindSession(ctx, k) s, err := h.SessionService.FindSession(ctx, k)
if e != nil { if err != nil {
return ctx, e return ctx, err
} }
if !h.SessionRenewDisabled { if !h.SessionRenewDisabled {
// if the session is not expired, renew the session // if the session is not expired, renew the session
e = h.SessionService.RenewSession(ctx, s, time.Now().Add(platform.RenewSessionTime)) err = h.SessionService.RenewSession(ctx, s, time.Now().Add(platform.RenewSessionTime))
if e != nil { if err != nil {
return ctx, e return ctx, err
} }
} }

View File

@ -19,3 +19,9 @@ type Event struct {
ResponseBytes int ResponseBytes int
Status int Status int
} }
// NopEventRecorder never records events.
type NopEventRecorder struct{}
// Record never records events.
func (n *NopEventRecorder) Record(ctx context.Context, e Event) {}

View File

@ -15,6 +15,12 @@ const (
) )
// queryOrganization returns the organization for any http request. // queryOrganization returns the organization for any http request.
//
// It checks the org= and then orgID= parameter of the request.
//
// This will try to find the organization using an ID string or
// the name. It interprets the &org= parameter as either the name
// or the ID.
func queryOrganization(ctx context.Context, r *http.Request, svc platform.OrganizationService) (o *platform.Organization, err error) { func queryOrganization(ctx context.Context, r *http.Request, svc platform.OrganizationService) (o *platform.Organization, err error) {
filter := platform.OrganizationFilter{} filter := platform.OrganizationFilter{}

View File

@ -125,7 +125,7 @@ type signoutRequest struct {
Key string Key string
} }
func decodeSignoutRequest(ctx context.Context, r *http.Request) (*signoutRequest, *platform.Error) { func decodeSignoutRequest(ctx context.Context, r *http.Request) (*signoutRequest, error) {
key, err := decodeCookieSession(ctx, r) key, err := decodeCookieSession(ctx, r)
if err != nil { if err != nil {
return nil, err return nil, err
@ -145,12 +145,12 @@ func encodeCookieSession(w http.ResponseWriter, s *platform.Session) {
http.SetCookie(w, c) http.SetCookie(w, c)
} }
func decodeCookieSession(ctx context.Context, r *http.Request) (string, *platform.Error) { func decodeCookieSession(ctx context.Context, r *http.Request) (string, error) {
c, err := r.Cookie(cookieSessionName) c, err := r.Cookie(cookieSessionName)
if err != nil { if err != nil {
return "", &platform.Error{ return "", &platform.Error{
Err: err,
Code: platform.EInvalid, Code: platform.EInvalid,
Err: err,
} }
} }
return c.Value, nil return c.Value, nil

View File

@ -13,7 +13,7 @@ import (
"github.com/julienschmidt/httprouter" "github.com/julienschmidt/httprouter"
"go.uber.org/zap" "go.uber.org/zap"
platform "github.com/influxdata/influxdb" "github.com/influxdata/influxdb"
pcontext "github.com/influxdata/influxdb/context" pcontext "github.com/influxdata/influxdb/context"
"github.com/influxdata/influxdb/kit/tracing" "github.com/influxdata/influxdb/kit/tracing"
"github.com/influxdata/influxdb/models" "github.com/influxdata/influxdb/models"
@ -24,13 +24,13 @@ import (
// WriteBackend is all services and associated parameters required to construct // WriteBackend is all services and associated parameters required to construct
// the WriteHandler. // the WriteHandler.
type WriteBackend struct { type WriteBackend struct {
platform.HTTPErrorHandler influxdb.HTTPErrorHandler
Logger *zap.Logger Logger *zap.Logger
WriteEventRecorder metric.EventRecorder WriteEventRecorder metric.EventRecorder
PointsWriter storage.PointsWriter PointsWriter storage.PointsWriter
BucketService platform.BucketService BucketService influxdb.BucketService
OrganizationService platform.OrganizationService OrganizationService influxdb.OrganizationService
} }
// NewWriteBackend returns a new instance of WriteBackend. // NewWriteBackend returns a new instance of WriteBackend.
@ -49,11 +49,11 @@ func NewWriteBackend(b *APIBackend) *WriteBackend {
// WriteHandler receives line protocol and sends to a publish function. // WriteHandler receives line protocol and sends to a publish function.
type WriteHandler struct { type WriteHandler struct {
*httprouter.Router *httprouter.Router
platform.HTTPErrorHandler influxdb.HTTPErrorHandler
Logger *zap.Logger Logger *zap.Logger
BucketService platform.BucketService BucketService influxdb.BucketService
OrganizationService platform.OrganizationService OrganizationService influxdb.OrganizationService
PointsWriter storage.PointsWriter PointsWriter storage.PointsWriter
@ -92,7 +92,7 @@ func (h *WriteHandler) handleWrite(w http.ResponseWriter, r *http.Request) {
// TODO(desa): I really don't like how we're recording the usage metrics here // TODO(desa): I really don't like how we're recording the usage metrics here
// Ideally this will be moved when we solve https://github.com/influxdata/influxdb/issues/13403 // Ideally this will be moved when we solve https://github.com/influxdata/influxdb/issues/13403
var orgID platform.ID var orgID influxdb.ID
var requestBytes int var requestBytes int
sw := newStatusResponseWriter(w) sw := newStatusResponseWriter(w)
w = sw w = sw
@ -111,8 +111,8 @@ func (h *WriteHandler) handleWrite(w http.ResponseWriter, r *http.Request) {
var err error var err error
in, err = gzip.NewReader(r.Body) in, err = gzip.NewReader(r.Body)
if err != nil { if err != nil {
h.HandleHTTPError(ctx, &platform.Error{ h.HandleHTTPError(ctx, &influxdb.Error{
Code: platform.EInvalid, Code: influxdb.EInvalid,
Op: "http/handleWrite", Op: "http/handleWrite",
Msg: errInvalidGzipHeader, Msg: errInvalidGzipHeader,
Err: err, Err: err,
@ -136,7 +136,7 @@ func (h *WriteHandler) handleWrite(w http.ResponseWriter, r *http.Request) {
logger := h.Logger.With(zap.String("org", req.Org), zap.String("bucket", req.Bucket)) logger := h.Logger.With(zap.String("org", req.Org), zap.String("bucket", req.Bucket))
var org *platform.Organization var org *influxdb.Organization
org, err = queryOrganization(ctx, r, h.OrganizationService) org, err = queryOrganization(ctx, r, h.OrganizationService)
if err != nil { if err != nil {
logger.Info("Failed to find organization", zap.Error(err)) logger.Info("Failed to find organization", zap.Error(err))
@ -146,31 +146,28 @@ func (h *WriteHandler) handleWrite(w http.ResponseWriter, r *http.Request) {
orgID = org.ID orgID = org.ID
var bucket *platform.Bucket var bucket *influxdb.Bucket
if id, err := platform.IDFromString(req.Bucket); err == nil { if id, err := influxdb.IDFromString(req.Bucket); err == nil {
// Decoded ID successfully. Make sure it's a real bucket. // Decoded ID successfully. Make sure it's a real bucket.
b, err := h.BucketService.FindBucket(ctx, platform.BucketFilter{ b, err := h.BucketService.FindBucket(ctx, influxdb.BucketFilter{
OrganizationID: &org.ID, OrganizationID: &org.ID,
ID: id, ID: id,
}) })
if err == nil { if err == nil {
bucket = b bucket = b
} else if platform.ErrorCode(err) != platform.ENotFound { } else if influxdb.ErrorCode(err) != influxdb.ENotFound {
h.HandleHTTPError(ctx, err, w) h.HandleHTTPError(ctx, err, w)
return return
} }
} }
if bucket == nil { if bucket == nil {
b, err := h.BucketService.FindBucket(ctx, platform.BucketFilter{ b, err := h.BucketService.FindBucket(ctx, influxdb.BucketFilter{
OrganizationID: &org.ID, OrganizationID: &org.ID,
Name: &req.Bucket, Name: &req.Bucket,
}) })
if err != nil { if err != nil {
h.HandleHTTPError(ctx, &platform.Error{ h.HandleHTTPError(ctx, err, w)
Op: "http/handleWrite",
Err: err,
}, w)
return return
} }
@ -179,18 +176,18 @@ func (h *WriteHandler) handleWrite(w http.ResponseWriter, r *http.Request) {
// TODO(jade): remove this after system buckets issue is resolved // TODO(jade): remove this after system buckets issue is resolved
if bucket.IsSystem() { if bucket.IsSystem() {
h.HandleHTTPError(ctx, &platform.Error{ h.HandleHTTPError(ctx, &influxdb.Error{
Code: platform.EForbidden, Code: influxdb.EForbidden,
Op: "http/handleWrite", Op: "http/handleWrite",
Msg: fmt.Sprintf("cannot write to internal bucket %s", bucket.Name), Msg: fmt.Sprintf("cannot write to internal bucket %s", bucket.Name),
}, w) }, w)
return return
} }
p, err := platform.NewPermissionAtID(bucket.ID, platform.WriteAction, platform.BucketsResourceType, org.ID) p, err := influxdb.NewPermissionAtID(bucket.ID, influxdb.WriteAction, influxdb.BucketsResourceType, org.ID)
if err != nil { if err != nil {
h.HandleHTTPError(ctx, &platform.Error{ h.HandleHTTPError(ctx, &influxdb.Error{
Code: platform.EInternal, Code: influxdb.EInternal,
Op: "http/handleWrite", Op: "http/handleWrite",
Msg: fmt.Sprintf("unable to create permission for bucket: %v", err), Msg: fmt.Sprintf("unable to create permission for bucket: %v", err),
Err: err, Err: err,
@ -199,8 +196,8 @@ func (h *WriteHandler) handleWrite(w http.ResponseWriter, r *http.Request) {
} }
if !a.Allowed(*p) { if !a.Allowed(*p) {
h.HandleHTTPError(ctx, &platform.Error{ h.HandleHTTPError(ctx, &influxdb.Error{
Code: platform.EForbidden, Code: influxdb.EForbidden,
Op: "http/handleWrite", Op: "http/handleWrite",
Msg: "insufficient permissions for write", Msg: "insufficient permissions for write",
}, w) }, w)
@ -213,36 +210,43 @@ func (h *WriteHandler) handleWrite(w http.ResponseWriter, r *http.Request) {
data, err := ioutil.ReadAll(in) data, err := ioutil.ReadAll(in)
if err != nil { if err != nil {
logger.Error("Error reading body", zap.Error(err)) logger.Error("Error reading body", zap.Error(err))
h.HandleHTTPError(ctx, &platform.Error{ h.HandleHTTPError(ctx, &influxdb.Error{
Code: platform.EInternal, Code: influxdb.EInternal,
Op: "http/handleWrite", Op: "http/handleWrite",
Msg: fmt.Sprintf("unable to read data: %v", err), Msg: fmt.Sprintf("unable to read data: %v", err),
Err: err, Err: err,
}, w) }, w)
return return
} }
requestBytes = len(data) requestBytes = len(data)
if requestBytes == 0 {
h.HandleHTTPError(ctx, &influxdb.Error{
Code: influxdb.EInvalid,
Op: "http/handleWrite",
Msg: "writing requires points",
}, w)
return
}
encoded := tsdb.EncodeName(org.ID, bucket.ID) encoded := tsdb.EncodeName(org.ID, bucket.ID)
mm := models.EscapeMeasurement(encoded[:]) mm := models.EscapeMeasurement(encoded[:])
points, err := models.ParsePointsWithPrecision(data, mm, time.Now(), req.Precision) points, err := models.ParsePointsWithPrecision(data, mm, time.Now(), req.Precision)
if err != nil { if err != nil {
logger.Error("Error parsing points", zap.Error(err)) logger.Error("Error parsing points", zap.Error(err))
h.HandleHTTPError(ctx, &platform.Error{ h.HandleHTTPError(ctx, &influxdb.Error{
Code: platform.EInvalid, Code: influxdb.EInvalid,
Op: "http/handleWrite", Msg: err.Error(),
Msg: fmt.Sprintf("unable to parse points: %v", err),
Err: err,
}, w) }, w)
return return
} }
if err := h.PointsWriter.WritePoints(ctx, points); err != nil { if err := h.PointsWriter.WritePoints(ctx, points); err != nil {
logger.Error("Error writing points", zap.Error(err)) logger.Error("Error writing points", zap.Error(err))
h.HandleHTTPError(ctx, &platform.Error{ h.HandleHTTPError(ctx, &influxdb.Error{
Code: platform.EInternal, Code: influxdb.EInternal,
Op: "http/handleWrite", Op: "http/handleWrite",
Msg: fmt.Sprintf("unable to write points to database: %v", err), Msg: "unexpected error writing points to database",
Err: err, Err: err,
}, w) }, w)
return return
@ -259,8 +263,8 @@ func decodeWriteRequest(ctx context.Context, r *http.Request) (*postWriteRequest
} }
if !models.ValidPrecision(p) { if !models.ValidPrecision(p) {
return nil, &platform.Error{ return nil, &influxdb.Error{
Code: platform.EInvalid, Code: influxdb.EInvalid,
Op: "http/decodeWriteRequest", Op: "http/decodeWriteRequest",
Msg: errInvalidPrecision, Msg: errInvalidPrecision,
} }
@ -287,17 +291,17 @@ type WriteService struct {
InsecureSkipVerify bool InsecureSkipVerify bool
} }
var _ platform.WriteService = (*WriteService)(nil) var _ influxdb.WriteService = (*WriteService)(nil)
func (s *WriteService) Write(ctx context.Context, orgID, bucketID platform.ID, r io.Reader) error { func (s *WriteService) Write(ctx context.Context, orgID, bucketID influxdb.ID, r io.Reader) error {
precision := s.Precision precision := s.Precision
if precision == "" { if precision == "" {
precision = "ns" precision = "ns"
} }
if !models.ValidPrecision(precision) { if !models.ValidPrecision(precision) {
return &platform.Error{ return &influxdb.Error{
Code: platform.EInvalid, Code: influxdb.EInvalid,
Op: "http/Write", Op: "http/Write",
Msg: errInvalidPrecision, Msg: errInvalidPrecision,
} }

View File

@ -3,6 +3,7 @@ package http
import ( import (
"compress/gzip" "compress/gzip"
"context" "context"
"fmt"
"io" "io"
"io/ioutil" "io/ioutil"
"net/http" "net/http"
@ -10,13 +11,18 @@ import (
"strings" "strings"
"testing" "testing"
platform "github.com/influxdata/influxdb" "github.com/influxdata/influxdb"
"github.com/influxdata/influxdb/http/metric"
httpmock "github.com/influxdata/influxdb/http/mock"
"github.com/influxdata/influxdb/mock"
influxtesting "github.com/influxdata/influxdb/testing"
"go.uber.org/zap/zaptest"
) )
func TestWriteService_Write(t *testing.T) { func TestWriteService_Write(t *testing.T) {
type args struct { type args struct {
org platform.ID org influxdb.ID
bucket platform.ID bucket influxdb.ID
r io.Reader r io.Reader
} }
tests := []struct { tests := []struct {
@ -38,11 +44,11 @@ func TestWriteService_Write(t *testing.T) {
} }
for _, tt := range tests { for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) { t.Run(tt.name, func(t *testing.T) {
var org, bucket *platform.ID var org, bucket *influxdb.ID
var lp []byte var lp []byte
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
org, _ = platform.IDFromString(r.URL.Query().Get("org")) org, _ = influxdb.IDFromString(r.URL.Query().Get("org"))
bucket, _ = platform.IDFromString(r.URL.Query().Get("bucket")) bucket, _ = influxdb.IDFromString(r.URL.Query().Get("bucket"))
defer r.Body.Close() defer r.Body.Close()
in, _ := gzip.NewReader(r.Body) in, _ := gzip.NewReader(r.Body)
defer in.Close() defer in.Close()
@ -69,3 +75,284 @@ func TestWriteService_Write(t *testing.T) {
}) })
} }
} }
func TestWriteHandler_handleWrite(t *testing.T) {
// state is the internal state of org and bucket services
type state struct {
org *influxdb.Organization // org to return in org service
orgErr error // err to return in org servce
bucket *influxdb.Bucket // bucket to return in bucket service
bucketErr error // err to return in bucket service
writeErr error // err to return from the points writer
}
// want is the expected output of the HTTP endpoint
type wants struct {
body string
code int
}
// request is sent to the HTTP endpoint
type request struct {
auth influxdb.Authorizer
org string
bucket string
body string
}
tests := []struct {
name string
request request
state state
wants wants
}{
{
name: "simple body is accepted",
request: request{
org: "043e0780ee2b1000",
bucket: "04504b356e23b000",
body: "m1,t1=v1 f1=1",
auth: bucketWritePermission("043e0780ee2b1000", "04504b356e23b000"),
},
state: state{
org: testOrg("043e0780ee2b1000"),
bucket: testBucket("043e0780ee2b1000", "04504b356e23b000"),
},
wants: wants{
code: 204,
},
},
{
name: "points writer error is an internal error",
request: request{
org: "043e0780ee2b1000",
bucket: "04504b356e23b000",
body: "m1,t1=v1 f1=1",
auth: bucketWritePermission("043e0780ee2b1000", "04504b356e23b000"),
},
state: state{
org: testOrg("043e0780ee2b1000"),
bucket: testBucket("043e0780ee2b1000", "04504b356e23b000"),
writeErr: fmt.Errorf("error"),
},
wants: wants{
code: 500,
body: `{"code":"internal error","message":"unexpected error writing points to database","op":"http/handleWrite","error":"error"}`,
},
},
{
name: "empty request body returns 400 error",
request: request{
org: "043e0780ee2b1000",
bucket: "04504b356e23b000",
auth: bucketWritePermission("043e0780ee2b1000", "04504b356e23b000"),
},
state: state{
org: testOrg("043e0780ee2b1000"),
bucket: testBucket("043e0780ee2b1000", "04504b356e23b000"),
},
wants: wants{
code: 400,
body: `{"code":"invalid","message":"writing requires points","op":"http/handleWrite"}`,
},
},
{
name: "org error returns 404 error",
request: request{
org: "043e0780ee2b1000",
bucket: "04504b356e23b000",
body: "m1,t1=v1 f1=1",
auth: bucketWritePermission("043e0780ee2b1000", "04504b356e23b000"),
},
state: state{
orgErr: &influxdb.Error{Code: influxdb.ENotFound, Msg: "not found"},
},
wants: wants{
code: 404,
body: `{"code":"not found","message":"not found"}`,
},
},
{
name: "bucket error returns 404 error",
request: request{
org: "043e0780ee2b1000",
bucket: "04504b356e23b000",
body: "m1,t1=v1 f1=1",
auth: bucketWritePermission("043e0780ee2b1000", "04504b356e23b000"),
},
state: state{
org: testOrg("043e0780ee2b1000"),
bucketErr: &influxdb.Error{Code: influxdb.ENotFound, Msg: "not found"},
},
wants: wants{
code: 404,
body: `{"code":"not found","message":"not found"}`,
},
},
{
name: "500 when bucket service returns internal error",
request: request{
org: "043e0780ee2b1000",
bucket: "04504b356e23b000",
auth: bucketWritePermission("043e0780ee2b1000", "04504b356e23b000"),
},
state: state{
org: testOrg("043e0780ee2b1000"),
bucketErr: &influxdb.Error{Code: influxdb.EInternal, Msg: "internal error"},
},
wants: wants{
code: 500,
body: `{"code":"internal error","message":"internal error"}`,
},
},
{
name: "invalid line protocol returns 400",
request: request{
org: "043e0780ee2b1000",
bucket: "04504b356e23b000",
auth: bucketWritePermission("043e0780ee2b1000", "04504b356e23b000"),
body: "invalid",
},
state: state{
org: testOrg("043e0780ee2b1000"),
bucket: testBucket("043e0780ee2b1000", "04504b356e23b000"),
},
wants: wants{
code: 400,
body: `{"code":"invalid","message":"unable to parse 'invalid': missing fields"}`,
},
},
{
name: "forbidden to write to system buckets",
request: request{
org: "043e0780ee2b1000",
bucket: "000000000000000a",
auth: bucketWritePermission("043e0780ee2b1000", "000000000000000a"),
body: "invalid",
},
state: state{
org: testOrg("043e0780ee2b1000"),
bucket: testBucket("043e0780ee2b1000", "000000000000000a"),
},
wants: wants{
code: 403,
body: `{"code":"forbidden","message":"cannot write to internal bucket ","op":"http/handleWrite"}`,
},
},
{
name: "forbidden to write with insufficient permission",
request: request{
org: "043e0780ee2b1000",
bucket: "04504b356e23b000",
body: "m1,t1=v1 f1=1",
auth: bucketWritePermission("043e0780ee2b1000", "000000000000000a"),
},
state: state{
org: testOrg("043e0780ee2b1000"),
bucket: testBucket("043e0780ee2b1000", "04504b356e23b000"),
},
wants: wants{
code: 403,
body: `{"code":"forbidden","message":"insufficient permissions for write","op":"http/handleWrite"}`,
},
},
{
// authorization extraction happens in a different middleware.
name: "no authorizer is an internal error",
request: request{
org: "043e0780ee2b1000",
bucket: "04504b356e23b000",
},
state: state{
org: testOrg("043e0780ee2b1000"),
bucket: testBucket("043e0780ee2b1000", "04504b356e23b000"),
},
wants: wants{
code: 500,
body: `{"code":"internal error","message":"authorizer not found on context"}`,
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
orgs := mock.NewOrganizationService()
orgs.FindOrganizationF = func(ctx context.Context, filter influxdb.OrganizationFilter) (*influxdb.Organization, error) {
return tt.state.org, tt.state.orgErr
}
buckets := mock.NewBucketService()
buckets.FindBucketFn = func(context.Context, influxdb.BucketFilter) (*influxdb.Bucket, error) {
return tt.state.bucket, tt.state.bucketErr
}
b := &APIBackend{
HTTPErrorHandler: DefaultErrorHandler,
Logger: zaptest.NewLogger(t),
OrganizationService: orgs,
BucketService: buckets,
PointsWriter: &mock.PointsWriter{Err: tt.state.writeErr},
WriteEventRecorder: &metric.NopEventRecorder{},
}
writeHandler := NewWriteHandler(NewWriteBackend(b))
handler := httpmock.NewAuthMiddlewareHandler(writeHandler, tt.request.auth)
r := httptest.NewRequest(
"POST",
"http://localhost:9999/api/v2/write",
strings.NewReader(tt.request.body),
)
params := r.URL.Query()
params.Set("org", tt.request.org)
params.Set("bucket", tt.request.bucket)
r.URL.RawQuery = params.Encode()
w := httptest.NewRecorder()
handler.ServeHTTP(w, r)
if got, want := w.Code, tt.wants.code; got != want {
t.Errorf("unexpected status code: got %d want %d", got, want)
}
if got, want := w.Body.String(), tt.wants.body; got != want {
t.Errorf("unexpected body: got %s want %s", got, want)
}
})
}
}
var DefaultErrorHandler = ErrorHandler(0)
func bucketWritePermission(org, bucket string) *influxdb.Authorization {
oid := influxtesting.MustIDBase16(org)
bid := influxtesting.MustIDBase16(bucket)
return &influxdb.Authorization{
OrgID: oid,
Status: influxdb.Active,
Permissions: []influxdb.Permission{
{
Action: influxdb.WriteAction,
Resource: influxdb.Resource{
Type: influxdb.BucketsResourceType,
OrgID: &oid,
ID: &bid,
},
},
},
}
}
func testOrg(org string) *influxdb.Organization {
oid := influxtesting.MustIDBase16(org)
return &influxdb.Organization{
ID: oid,
}
}
func testBucket(org, bucket string) *influxdb.Bucket {
oid := influxtesting.MustIDBase16(org)
bid := influxtesting.MustIDBase16(bucket)
return &influxdb.Bucket{
ID: bid,
OrgID: oid,
}
}