diff --git a/CHANGELOG.md b/CHANGELOG.md index 48b38049e..f9f5f7cd4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,7 @@ Versioning](http://semver.org/spec/v2.0.0.html) except to the first release. - Support pagination (#246) - A Makefile target to test with race detector (#218) +- Support CRUD API (#108) ### Changed diff --git a/Makefile b/Makefile index 34de54707..055255814 100644 --- a/Makefile +++ b/Makefile @@ -21,12 +21,13 @@ endif .PHONY: clean clean: - ( cd ./queue; rm -rf .rocks ) + ( rm -rf queue/.rocks crud/.rocks ) rm -f $(COVERAGE_FILE) .PHONY: deps deps: clean ( cd ./queue/testdata; $(TTCTL) rocks install queue 1.2.1 ) + ( cd ./crud; $(TTCTL) rocks install crud 0.14.1 ) .PHONY: datetime-timezones datetime-timezones: @@ -99,6 +100,13 @@ test-settings: go clean -testcache go test -tags "$(TAGS)" ./settings/ -v -p 1 +.PHONY: test-crud +test-crud: + @echo "Running tests in crud package" + cd ./crud/ && tarantool -e "require('crud')" + go clean -testcache + go test -tags "$(TAGS)" ./crud/ -v -p 1 + .PHONY: test-main test-main: @echo "Running tests in main package" diff --git a/crud/common.go b/crud/common.go new file mode 100644 index 000000000..8a5ce6b99 --- /dev/null +++ b/crud/common.go @@ -0,0 +1,39 @@ +package crud + +import ( + "context" + + "github.com/tarantool/go-tarantool" +) + +type baseRequest struct { + impl *tarantool.CallRequest +} + +func (req *baseRequest) initImpl(methodName string) { + req.impl = tarantool.NewCall17Request(methodName) +} + +// Code returns IPROTO code for CRUD request. +func (req *baseRequest) Code() int32 { + return req.impl.Code() +} + +// Ctx returns a context of CRUD request. +func (req *baseRequest) Ctx() context.Context { + return req.impl.Ctx() +} + +// Async returns is CRUD request expects a response. +func (req *baseRequest) Async() bool { + return req.impl.Async() +} + +type spaceRequest struct { + baseRequest + space interface{} +} + +func (req *spaceRequest) setSpace(space interface{}) { + req.space = space +} diff --git a/crud/count.go b/crud/count.go new file mode 100644 index 000000000..c6765623e --- /dev/null +++ b/crud/count.go @@ -0,0 +1,53 @@ +package crud + +import ( + "context" + + "github.com/tarantool/go-tarantool" +) + +// CountRequest helps you to create request +// object to call `crud.count` for execution +// by a Connection. +type CountRequest struct { + spaceRequest + conditions interface{} + opts interface{} +} + +// NewCountRequest returns a new empty CountRequest. +func NewCountRequest(space interface{}) *SelectRequest { + req := new(SelectRequest) + req.initImpl("crud.count") + req.setSpace(space) + req.conditions = []interface{}{} + req.opts = map[string]interface{}{} + return req +} + +// Conditions sets the conditions for the CountRequest request. +// Note: default value is nil. +func (req *CountRequest) Conditions(conditions interface{}) *CountRequest { + req.conditions = conditions + return req +} + +// Opts sets the options for the CountRequest request. +// Note: default value is nil. +func (req *CountRequest) Opts(opts interface{}) *CountRequest { + req.opts = opts + return req +} + +// Body fills an encoder with the call request body. +func (req *CountRequest) Body(res tarantool.SchemaResolver, enc *encoder) error { + req.impl.Args([]interface{}{req.space, req.conditions, req.opts}) + return req.impl.Body(res, enc) +} + +// Context sets a passed context to CRUD request. +func (req *CountRequest) Context(ctx context.Context) *CountRequest { + req.impl = req.impl.Context(ctx) + + return req +} diff --git a/crud/delete.go b/crud/delete.go new file mode 100644 index 000000000..271501dd6 --- /dev/null +++ b/crud/delete.go @@ -0,0 +1,53 @@ +package crud + +import ( + "context" + + "github.com/tarantool/go-tarantool" +) + +// DeleteRequest helps you to create request +// object to call `crud.delete` for execution +// by a Connection. +type DeleteRequest struct { + spaceRequest + key interface{} + opts interface{} +} + +// NewDeleteRequest returns a new empty DeleteRequest. +func NewDeleteRequest(space interface{}) *DeleteRequest { + req := new(DeleteRequest) + req.initImpl("crud.delete") + req.setSpace(space) + req.key = []interface{}{} + req.opts = map[string]interface{}{} + return req +} + +// Key sets the key for the DeleteRequest request. +// Note: default value is nil. +func (req *DeleteRequest) Key(key interface{}) *DeleteRequest { + req.key = key + return req +} + +// Opts sets the options for the DeleteRequest request. +// Note: default value is nil. +func (req *DeleteRequest) Opts(opts interface{}) *DeleteRequest { + req.opts = opts + return req +} + +// Body fills an encoder with the call request body. +func (req *DeleteRequest) Body(res tarantool.SchemaResolver, enc *encoder) error { + req.impl.Args([]interface{}{req.space, req.key, req.opts}) + return req.impl.Body(res, enc) +} + +// Context sets a passed context to CRUD request. +func (req *DeleteRequest) Context(ctx context.Context) *DeleteRequest { + req.impl = req.impl.Context(ctx) + + return req +} diff --git a/crud/get.go b/crud/get.go new file mode 100644 index 000000000..e2796e2a9 --- /dev/null +++ b/crud/get.go @@ -0,0 +1,53 @@ +package crud + +import ( + "context" + + "github.com/tarantool/go-tarantool" +) + +// GetRequest helps you to create request +// object to call `crud.get` for execution +// by a Connection. +type GetRequest struct { + spaceRequest + key interface{} + opts interface{} +} + +// NewGetRequest returns a new empty GetRequest. +func NewGetRequest(space interface{}) *GetRequest { + req := new(GetRequest) + req.initImpl("crud.get") + req.setSpace(space) + req.key = []interface{}{} + req.opts = map[string]interface{}{} + return req +} + +// Key sets the key for the GetRequest request. +// Note: default value is nil. +func (req *GetRequest) Key(key interface{}) *GetRequest { + req.key = key + return req +} + +// Opts sets the options for the GetRequest request. +// Note: default value is nil. +func (req *GetRequest) Opts(opts interface{}) *GetRequest { + req.opts = opts + return req +} + +// Body fills an encoder with the call request body. +func (req *GetRequest) Body(res tarantool.SchemaResolver, enc *encoder) error { + req.impl.Args([]interface{}{req.space, req.key, req.opts}) + return req.impl.Body(res, enc) +} + +// Context sets a passed context to CRUD request. +func (req *GetRequest) Context(ctx context.Context) *GetRequest { + req.impl = req.impl.Context(ctx) + + return req +} diff --git a/crud/insert.go b/crud/insert.go new file mode 100644 index 000000000..54f9c2071 --- /dev/null +++ b/crud/insert.go @@ -0,0 +1,99 @@ +package crud + +import ( + "context" + + "github.com/tarantool/go-tarantool" +) + +// InsertRequest helps you to create request +// object to call `crud.insert` for execution +// by a Connection. +type InsertRequest struct { + spaceRequest + tuple interface{} + opts interface{} +} + +// NewInsertRequest returns a new empty InsertRequest. +func NewInsertRequest(space interface{}) *InsertRequest { + req := new(InsertRequest) + req.initImpl("crud.insert") + req.setSpace(space) + req.tuple = []interface{}{} + req.opts = map[string]interface{}{} + return req +} + +// Tuple sets the tuple for the InsertRequest request. +// Note: default value is nil. +func (req *InsertRequest) Tuple(tuple interface{}) *InsertRequest { + req.tuple = tuple + return req +} + +// Opts sets the options for the insert request. +// Note: default value is nil. +func (req *InsertRequest) Opts(opts interface{}) *InsertRequest { + req.opts = opts + return req +} + +// Body fills an encoder with the call request body. +func (req *InsertRequest) Body(res tarantool.SchemaResolver, enc *encoder) error { + req.impl.Args([]interface{}{req.space, req.tuple, req.opts}) + return req.impl.Body(res, enc) +} + +// Context sets a passed context to CRUD request. +func (req *InsertRequest) Context(ctx context.Context) *InsertRequest { + req.impl = req.impl.Context(ctx) + + return req +} + +// InsertObjectRequest helps you to create request +// object to call `crud.insert_object` for execution +// by a Connection. +type InsertObjectRequest struct { + spaceRequest + object interface{} + opts interface{} +} + +// NewInsertObjectRequest returns a new empty InsertObjectRequest. +func NewInsertObjectRequest(space interface{}) *InsertObjectRequest { + req := new(InsertObjectRequest) + req.initImpl("crud.insert_object") + req.setSpace(space) + req.object = map[string]interface{}{} + req.opts = map[string]interface{}{} + return req +} + +// Object sets the tuple for the InsertObjectRequest request. +// Note: default value is nil. +func (req *InsertObjectRequest) Object(object interface{}) *InsertObjectRequest { + req.object = object + return req +} + +// Opts sets the options for the InsertObjectRequest request. +// Note: default value is nil. +func (req *InsertObjectRequest) Opts(opts interface{}) *InsertObjectRequest { + req.opts = opts + return req +} + +// Body fills an encoder with the call request body. +func (req *InsertObjectRequest) Body(res tarantool.SchemaResolver, enc *encoder) error { + req.impl.Args([]interface{}{req.space, req.object, req.opts}) + return req.impl.Body(res, enc) +} + +// Context sets a passed context to CRUD request. +func (req *InsertObjectRequest) Context(ctx context.Context) *InsertObjectRequest { + req.impl = req.impl.Context(ctx) + + return req +} diff --git a/crud/insert_many.go b/crud/insert_many.go new file mode 100644 index 000000000..36b807b6f --- /dev/null +++ b/crud/insert_many.go @@ -0,0 +1,99 @@ +package crud + +import ( + "context" + + "github.com/tarantool/go-tarantool" +) + +// InsertManyRequest helps you to create request +// object to call `crud.insert_many` for execution +// by a Connection. +type InsertManyRequest struct { + spaceRequest + tuples interface{} + opts interface{} +} + +// NewInsertManyRequest returns a new empty InsertManyRequest. +func NewInsertManyRequest(space interface{}) *InsertManyRequest { + req := new(InsertManyRequest) + req.initImpl("crud.insert_many") + req.setSpace(space) + req.tuples = []interface{}{} + req.opts = map[string]interface{}{} + return req +} + +// Tuples sets the tuples for the InsertManyRequest request. +// Note: default value is nil. +func (req *InsertManyRequest) Tuples(tuples interface{}) *InsertManyRequest { + req.tuples = tuples + return req +} + +// Opts sets the options for the InsertManyRequest request. +// Note: default value is nil. +func (req *InsertManyRequest) Opts(opts interface{}) *InsertManyRequest { + req.opts = opts + return req +} + +// Body fills an encoder with the call request body. +func (req *InsertManyRequest) Body(res tarantool.SchemaResolver, enc *encoder) error { + req.impl.Args([]interface{}{req.space, req.tuples, req.opts}) + return req.impl.Body(res, enc) +} + +// Context sets a passed context to CRUD request. +func (req *InsertManyRequest) Context(ctx context.Context) *InsertManyRequest { + req.impl = req.impl.Context(ctx) + + return req +} + +// InsertObjectManyRequest helps you to create request +// object to call `crud.insert_object_many` for execution +// by a Connection. +type InsertObjectManyRequest struct { + spaceRequest + objects interface{} + opts interface{} +} + +// NewInsertObjectManyRequest returns a new empty InsertObjectManyRequest. +func NewInsertObjectManyRequest(space interface{}) *InsertObjectManyRequest { + req := new(InsertObjectManyRequest) + req.initImpl("crud.insert_object_many") + req.setSpace(space) + req.objects = []map[string]interface{}{} + req.opts = map[string]interface{}{} + return req +} + +// Objects sets the objects for the InsertObjectManyRequest request. +// Note: default value is nil. +func (req *InsertObjectManyRequest) Objects(objects interface{}) *InsertObjectManyRequest { + req.objects = objects + return req +} + +// Opts sets the options for the InsertObjectManyRequest request. +// Note: default value is nil. +func (req *InsertObjectManyRequest) Opts(opts interface{}) *InsertObjectManyRequest { + req.opts = opts + return req +} + +// Body fills an encoder with the call request body. +func (req *InsertObjectManyRequest) Body(res tarantool.SchemaResolver, enc *encoder) error { + req.impl.Args([]interface{}{req.space, req.objects, req.opts}) + return req.impl.Body(res, enc) +} + +// Context sets a passed context to CRUD request. +func (req *InsertObjectManyRequest) Context(ctx context.Context) *InsertObjectManyRequest { + req.impl = req.impl.Context(ctx) + + return req +} diff --git a/crud/len.go b/crud/len.go new file mode 100644 index 000000000..baf341d80 --- /dev/null +++ b/crud/len.go @@ -0,0 +1,44 @@ +package crud + +import ( + "context" + + "github.com/tarantool/go-tarantool" +) + +// LeneRequest helps you to create request +// object to call `crud.len` for execution +// by a Connection. +type LenRequest struct { + spaceRequest + opts interface{} +} + +// NewLenRequest returns a new empty LenRequest. +func NewLenRequest(space interface{}) *LenRequest { + req := new(LenRequest) + req.initImpl("crud.len") + req.setSpace(space) + req.opts = map[string]interface{}{} + return req +} + +// Opts sets the options for the LenRequest request. +// Note: default value is nil. +func (req *LenRequest) Opts(opts interface{}) *LenRequest { + req.opts = opts + return req +} + +// Body fills an encoder with the call request body. +func (req *LenRequest) Body(res tarantool.SchemaResolver, enc *encoder) error { + req.impl.Args([]interface{}{req.space, req.opts}) + return req.impl.Body(res, enc) +} + +// Context sets a passed context to CRUD request. +func (req *LenRequest) Context(ctx context.Context) *LenRequest { + req.impl = req.impl.Context(ctx) + + return req +} diff --git a/crud/max.go b/crud/max.go new file mode 100644 index 000000000..476b0f71f --- /dev/null +++ b/crud/max.go @@ -0,0 +1,53 @@ +package crud + +import ( + "context" + + "github.com/tarantool/go-tarantool" +) + +// MaxRequest helps you to create request +// object to call `crud.max` for execution +// by a Connection. +type MaxRequest struct { + spaceRequest + index interface{} + opts interface{} +} + +// NewMaxRequest returns a new empty MaxRequest. +func NewMaxRequest(space interface{}) *MaxRequest { + req := new(MaxRequest) + req.initImpl("crud.max") + req.setSpace(space) + req.index = []interface{}{} + req.opts = map[string]interface{}{} + return req +} + +// Index sets the index name/id for the MaxRequest request. +// Note: default value is nil. +func (req *MaxRequest) Index(index interface{}) *MaxRequest { + req.index = index + return req +} + +// Opts sets the options for the MaxRequest request. +// Note: default value is nil. +func (req *MaxRequest) Opts(opts interface{}) *MaxRequest { + req.opts = opts + return req +} + +// Body fills an encoder with the call request body. +func (req *MaxRequest) Body(res tarantool.SchemaResolver, enc *encoder) error { + req.impl.Args([]interface{}{req.space, req.index, req.opts}) + return req.impl.Body(res, enc) +} + +// Context sets a passed context to CRUD request. +func (req *MaxRequest) Context(ctx context.Context) *MaxRequest { + req.impl = req.impl.Context(ctx) + + return req +} diff --git a/crud/min.go b/crud/min.go new file mode 100644 index 000000000..7d6810e57 --- /dev/null +++ b/crud/min.go @@ -0,0 +1,53 @@ +package crud + +import ( + "context" + + "github.com/tarantool/go-tarantool" +) + +// MinRequest helps you to create request +// object to call `crud.min` for execution +// by a Connection. +type MinRequest struct { + spaceRequest + index interface{} + opts interface{} +} + +// NewMinRequest returns a new empty MinRequest. +func NewMinRequest(space interface{}) *MinRequest { + req := new(MinRequest) + req.initImpl("crud.min") + req.setSpace(space) + req.index = []interface{}{} + req.opts = map[string]interface{}{} + return req +} + +// Index sets the index name/id for the MinRequest request. +// Note: default value is nil. +func (req *MinRequest) Index(index interface{}) *MinRequest { + req.index = index + return req +} + +// Opts sets the options for the MinRequest request. +// Note: default value is nil. +func (req *MinRequest) Opts(opts interface{}) *MinRequest { + req.opts = opts + return req +} + +// Body fills an encoder with the call request body. +func (req *MinRequest) Body(res tarantool.SchemaResolver, enc *encoder) error { + req.impl.Args([]interface{}{req.space, req.index, req.opts}) + return req.impl.Body(res, enc) +} + +// Context sets a passed context to CRUD request. +func (req *MinRequest) Context(ctx context.Context) *MinRequest { + req.impl = req.impl.Context(ctx) + + return req +} diff --git a/crud/msgpack.go b/crud/msgpack.go new file mode 100644 index 000000000..7185f49c9 --- /dev/null +++ b/crud/msgpack.go @@ -0,0 +1,16 @@ +//go:build !go_tarantool_msgpack_v5 +// +build !go_tarantool_msgpack_v5 + +package crud + +import ( + "io" + + "gopkg.in/vmihailenco/msgpack.v2" +) + +type encoder = msgpack.Encoder + +func NewEncoder(w io.Writer) *encoder { + return msgpack.NewEncoder(w) +} diff --git a/crud/msgpack_v5.go b/crud/msgpack_v5.go new file mode 100644 index 000000000..e74abb53e --- /dev/null +++ b/crud/msgpack_v5.go @@ -0,0 +1,14 @@ +//go:build go_tarantool_msgpack_v5 +// +build go_tarantool_msgpack_v5 + +package crud + +import ( + "github.com/vmihailenco/msgpack/v5" +) + +type encoder = msgpack.Encoder + +func NewEncoder(w io.Writer) *encoder { + return msgpack.NewEncoder(w) +} diff --git a/crud/replace.go b/crud/replace.go new file mode 100644 index 000000000..38c86b789 --- /dev/null +++ b/crud/replace.go @@ -0,0 +1,99 @@ +package crud + +import ( + "context" + + "github.com/tarantool/go-tarantool" +) + +// ReplaceRequest helps you to create request +// object to call `crud.replace` for execution +// by a Connection. +type ReplaceRequest struct { + spaceRequest + tuple interface{} + opts interface{} +} + +// NewReplaceRequest returns a new empty ReplaceRequest. +func NewReplaceRequest(space interface{}) *ReplaceRequest { + req := new(ReplaceRequest) + req.initImpl("crud.replace") + req.setSpace(space) + req.tuple = []interface{}{} + req.opts = map[string]interface{}{} + return req +} + +// Tuple sets the tuple for the ReplaceRequest request. +// Note: default value is nil. +func (req *ReplaceRequest) Tuple(tuple interface{}) *ReplaceRequest { + req.tuple = tuple + return req +} + +// Opts sets the options for the ReplaceRequest request. +// Note: default value is nil. +func (req *ReplaceRequest) Opts(opts interface{}) *ReplaceRequest { + req.opts = opts + return req +} + +// Body fills an encoder with the call request body. +func (req *ReplaceRequest) Body(res tarantool.SchemaResolver, enc *encoder) error { + req.impl.Args([]interface{}{req.space, req.tuple, req.opts}) + return req.impl.Body(res, enc) +} + +// Context sets a passed context to CRUD request. +func (req *ReplaceRequest) Context(ctx context.Context) *ReplaceRequest { + req.impl = req.impl.Context(ctx) + + return req +} + +// ReplaceObjectRequest helps you to create request +// object to call `crud.replace_object` for execution +// by a Connection. +type ReplaceObjectRequest struct { + spaceRequest + object interface{} + opts interface{} +} + +// NewReplaceObjectRequest returns a new empty ReplaceObjectRequest. +func NewReplaceObjectRequest(space interface{}) *ReplaceObjectRequest { + req := new(ReplaceObjectRequest) + req.initImpl("crud.replace_object") + req.setSpace(space) + req.object = map[string]interface{}{} + req.opts = map[string]interface{}{} + return req +} + +// Object sets the tuple for the ReplaceObjectRequest request. +// Note: default value is nil. +func (req *ReplaceObjectRequest) Object(object interface{}) *ReplaceObjectRequest { + req.object = object + return req +} + +// Opts sets the options for the ReplaceObjectRequest request. +// Note: default value is nil. +func (req *ReplaceObjectRequest) Opts(opts interface{}) *ReplaceObjectRequest { + req.opts = opts + return req +} + +// Body fills an encoder with the call request body. +func (req *ReplaceObjectRequest) Body(res tarantool.SchemaResolver, enc *encoder) error { + req.impl.Args([]interface{}{req.space, req.object, req.opts}) + return req.impl.Body(res, enc) +} + +// Context sets a passed context to CRUD request. +func (req *ReplaceObjectRequest) Context(ctx context.Context) *ReplaceObjectRequest { + req.impl = req.impl.Context(ctx) + + return req +} diff --git a/crud/replace_many.go b/crud/replace_many.go new file mode 100644 index 000000000..26997f94b --- /dev/null +++ b/crud/replace_many.go @@ -0,0 +1,99 @@ +package crud + +import ( + "context" + + "github.com/tarantool/go-tarantool" +) + +// ReplaceManyRequest helps you to create request +// object to call `crud.replace_many` for execution +// by a Connection. +type ReplaceManyRequest struct { + spaceRequest + tuples interface{} + opts interface{} +} + +// NewReplaceManyRequest returns a new empty ReplaceManyRequest. +func NewReplaceManyRequest(space interface{}) *ReplaceManyRequest { + req := new(ReplaceManyRequest) + req.initImpl("crud.replace_many") + req.setSpace(space) + req.tuples = []interface{}{} + req.opts = map[string]interface{}{} + return req +} + +// Tuples sets the tuples for the ReplaceManyRequest request. +// Note: default value is nil. +func (req *ReplaceManyRequest) Tuples(tuples interface{}) *ReplaceManyRequest { + req.tuples = tuples + return req +} + +// Opts sets the options for the ReplaceManyRequest request. +// Note: default value is nil. +func (req *ReplaceManyRequest) Opts(opts interface{}) *ReplaceManyRequest { + req.opts = opts + return req +} + +// Body fills an encoder with the call request body. +func (req *ReplaceManyRequest) Body(res tarantool.SchemaResolver, enc *encoder) error { + req.impl.Args([]interface{}{req.space, req.tuples, req.opts}) + return req.impl.Body(res, enc) +} + +// Context sets a passed context to CRUD request. +func (req *ReplaceManyRequest) Context(ctx context.Context) *ReplaceManyRequest { + req.impl = req.impl.Context(ctx) + + return req +} + +// ReplaceObjectManyRequest helps you to create request +// object to call `crud.replace_object_many` for execution +// by a Connection. +type ReplaceObjectManyRequest struct { + spaceRequest + objects interface{} + opts interface{} +} + +// NewReplaceObjectManyRequest returns a new empty ReplaceObjectManyRequest. +func NewReplaceObjectManyRequest(space interface{}) *ReplaceObjectManyRequest { + req := new(ReplaceObjectManyRequest) + req.initImpl("crud.replace_object_many") + req.setSpace(space) + req.objects = []map[string]interface{}{} + req.opts = map[string]interface{}{} + return req +} + +// Objects sets the tuple for the ReplaceObjectManyRequest request. +// Note: default value is nil. +func (req *ReplaceObjectManyRequest) Objects(objects interface{}) *ReplaceObjectManyRequest { + req.objects = objects + return req +} + +// Opts sets the options for the ReplaceObjectManyRequest request. +// Note: default value is nil. +func (req *ReplaceObjectManyRequest) Opts(opts interface{}) *ReplaceObjectManyRequest { + req.opts = opts + return req +} + +// Body fills an encoder with the call request body. +func (req *ReplaceObjectManyRequest) Body(res tarantool.SchemaResolver, enc *encoder) error { + req.impl.Args([]interface{}{req.space, req.objects, req.opts}) + return req.impl.Body(res, enc) +} + +// Context sets a passed context to CRUD request. +func (req *ReplaceObjectManyRequest) Context(ctx context.Context) *ReplaceObjectManyRequest { + req.impl = req.impl.Context(ctx) + + return req +} diff --git a/crud/request_test.go b/crud/request_test.go new file mode 100644 index 000000000..5fb3873af --- /dev/null +++ b/crud/request_test.go @@ -0,0 +1,378 @@ +package crud_test + +import ( + "bytes" + "context" + "errors" + "testing" + + "github.com/tarantool/go-tarantool" + "github.com/tarantool/go-tarantool/crud" + "github.com/tarantool/go-tarantool/test_helpers" +) + +const invalidSpaceMsg = "invalid space" +const invalidIndexMsg = "invalid index" + +const invalidSpace = 2 +const invalidIndex = 2 +const validSpace = 1 // Any valid value != default. +const defaultSpace = 0 // And valid too. +const defaultIndex = 0 // And valid too. + +const CrudRequestCode = tarantool.Call17RequestCode + +type ValidSchemeResolver struct { +} + +func (*ValidSchemeResolver) ResolveSpaceIndex(s, i interface{}) (spaceNo, indexNo uint32, err error) { + if s != nil { + spaceNo = uint32(s.(int)) + } else { + spaceNo = defaultSpace + } + if i != nil { + indexNo = uint32(i.(int)) + } else { + indexNo = defaultIndex + } + if spaceNo == invalidSpace { + return 0, 0, errors.New(invalidSpaceMsg) + } + if indexNo == invalidIndex { + return 0, 0, errors.New(invalidIndexMsg) + } + return spaceNo, indexNo, nil +} + +var resolver ValidSchemeResolver + +func assertBodyEqual(t testing.TB, reference tarantool.Request, req tarantool.Request) { + t.Helper() + + reqBody, err := test_helpers.ExtractRequestBody(req, &resolver, crud.NewEncoder) + if err != nil { + t.Fatalf("An unexpected Response.Body() error: %q", err.Error()) + } + + refBody, err := test_helpers.ExtractRequestBody(reference, &resolver, crud.NewEncoder) + if err != nil { + t.Fatalf("An unexpected Response.Body() error: %q", err.Error()) + } + + if !bytes.Equal(reqBody, refBody) { + t.Errorf("Encoded request %v != reference %v", reqBody, refBody) + } +} + +func TestRequestsCodes(t *testing.T) { + tests := []struct { + req tarantool.Request + code int32 + }{ + {req: crud.NewInsertRequest(validSpace), code: CrudRequestCode}, + {req: crud.NewInsertObjectRequest(validSpace), code: CrudRequestCode}, + {req: crud.NewInsertManyRequest(validSpace), code: CrudRequestCode}, + {req: crud.NewInsertObjectManyRequest(validSpace), code: CrudRequestCode}, + {req: crud.NewGetRequest(validSpace), code: CrudRequestCode}, + {req: crud.NewUpdateRequest(validSpace), code: CrudRequestCode}, + {req: crud.NewDeleteRequest(validSpace), code: CrudRequestCode}, + {req: crud.NewReplaceRequest(validSpace), code: CrudRequestCode}, + {req: crud.NewReplaceObjectRequest(validSpace), code: CrudRequestCode}, + {req: crud.NewReplaceManyRequest(validSpace), code: CrudRequestCode}, + {req: crud.NewReplaceObjectManyRequest(validSpace), code: CrudRequestCode}, + {req: crud.NewUpsertRequest(validSpace), code: CrudRequestCode}, + {req: crud.NewUpsertObjectRequest(validSpace), code: CrudRequestCode}, + {req: crud.NewUpsertManyRequest(validSpace), code: CrudRequestCode}, + {req: crud.NewUpsertObjectManyRequest(validSpace), code: CrudRequestCode}, + {req: crud.NewMinRequest(validSpace), code: CrudRequestCode}, + {req: crud.NewMaxRequest(validSpace), code: CrudRequestCode}, + {req: crud.NewSelectRequest(validSpace), code: CrudRequestCode}, + {req: crud.NewTruncateRequest(validSpace), code: CrudRequestCode}, + {req: crud.NewLenRequest(validSpace), code: CrudRequestCode}, + {req: crud.NewCountRequest(validSpace), code: CrudRequestCode}, + {req: crud.NewStorageInfoRequest(), code: CrudRequestCode}, + {req: crud.NewUnflattenRowsRequest(), code: CrudRequestCode}, + {req: crud.NewStatsRequest(), code: CrudRequestCode}, + } + + for _, test := range tests { + if code := test.req.Code(); code != test.code { + t.Errorf("An invalid request code 0x%x, expected 0x%x", code, test.code) + } + } +} + +func TestRequestsAsync(t *testing.T) { + tests := []struct { + req tarantool.Request + async bool + }{ + {req: crud.NewInsertRequest(validSpace), async: false}, + {req: crud.NewInsertObjectRequest(validSpace), async: false}, + {req: crud.NewInsertManyRequest(validSpace), async: false}, + {req: crud.NewInsertObjectManyRequest(validSpace), async: false}, + {req: crud.NewGetRequest(validSpace), async: false}, + {req: crud.NewUpdateRequest(validSpace), async: false}, + {req: crud.NewDeleteRequest(validSpace), async: false}, + {req: crud.NewReplaceRequest(validSpace), async: false}, + {req: crud.NewReplaceObjectRequest(validSpace), async: false}, + {req: crud.NewReplaceManyRequest(validSpace), async: false}, + {req: crud.NewReplaceObjectManyRequest(validSpace), async: false}, + {req: crud.NewUpsertRequest(validSpace), async: false}, + {req: crud.NewUpsertObjectRequest(validSpace), async: false}, + {req: crud.NewUpsertManyRequest(validSpace), async: false}, + {req: crud.NewUpsertObjectManyRequest(validSpace), async: false}, + {req: crud.NewMinRequest(validSpace), async: false}, + {req: crud.NewMaxRequest(validSpace), async: false}, + {req: crud.NewSelectRequest(validSpace), async: false}, + {req: crud.NewTruncateRequest(validSpace), async: false}, + {req: crud.NewLenRequest(validSpace), async: false}, + {req: crud.NewCountRequest(validSpace), async: false}, + {req: crud.NewStorageInfoRequest(), async: false}, + {req: crud.NewUnflattenRowsRequest(), async: false}, + {req: crud.NewStatsRequest(), async: false}, + } + + for _, test := range tests { + if async := test.req.Async(); async != test.async { + t.Errorf("An invalid async %t, expected %t", async, test.async) + } + } +} + +func TestRequestsCtx_default(t *testing.T) { + tests := []struct { + req tarantool.Request + expected context.Context + }{ + {req: crud.NewInsertRequest(validSpace), expected: nil}, + {req: crud.NewInsertObjectRequest(validSpace), expected: nil}, + {req: crud.NewInsertManyRequest(validSpace), expected: nil}, + {req: crud.NewInsertObjectManyRequest(validSpace), expected: nil}, + {req: crud.NewGetRequest(validSpace), expected: nil}, + {req: crud.NewUpdateRequest(validSpace), expected: nil}, + {req: crud.NewDeleteRequest(validSpace), expected: nil}, + {req: crud.NewReplaceRequest(validSpace), expected: nil}, + {req: crud.NewReplaceObjectRequest(validSpace), expected: nil}, + {req: crud.NewReplaceManyRequest(validSpace), expected: nil}, + {req: crud.NewReplaceObjectManyRequest(validSpace), expected: nil}, + {req: crud.NewUpsertRequest(validSpace), expected: nil}, + {req: crud.NewUpsertObjectRequest(validSpace), expected: nil}, + {req: crud.NewUpsertManyRequest(validSpace), expected: nil}, + {req: crud.NewUpsertObjectManyRequest(validSpace), expected: nil}, + {req: crud.NewMinRequest(validSpace), expected: nil}, + {req: crud.NewMaxRequest(validSpace), expected: nil}, + {req: crud.NewSelectRequest(validSpace), expected: nil}, + {req: crud.NewTruncateRequest(validSpace), expected: nil}, + {req: crud.NewLenRequest(validSpace), expected: nil}, + {req: crud.NewCountRequest(validSpace), expected: nil}, + {req: crud.NewStorageInfoRequest(), expected: nil}, + {req: crud.NewUnflattenRowsRequest(), expected: nil}, + {req: crud.NewStatsRequest(), expected: nil}, + } + + for _, test := range tests { + if ctx := test.req.Ctx(); ctx != test.expected { + t.Errorf("An invalid ctx %t, expected %t", ctx, test.expected) + } + } +} + +func TestRequestsCtx_setter(t *testing.T) { + ctx := context.Background() + tests := []struct { + req tarantool.Request + expected context.Context + }{ + {req: crud.NewInsertRequest(validSpace).Context(ctx), expected: ctx}, + {req: crud.NewInsertObjectRequest(validSpace).Context(ctx), expected: ctx}, + {req: crud.NewInsertManyRequest(validSpace).Context(ctx), expected: ctx}, + {req: crud.NewInsertObjectManyRequest(validSpace).Context(ctx), expected: ctx}, + {req: crud.NewGetRequest(validSpace).Context(ctx), expected: ctx}, + {req: crud.NewUpdateRequest(validSpace).Context(ctx), expected: ctx}, + {req: crud.NewDeleteRequest(validSpace).Context(ctx), expected: ctx}, + {req: crud.NewReplaceRequest(validSpace).Context(ctx), expected: ctx}, + {req: crud.NewReplaceObjectRequest(validSpace).Context(ctx), expected: ctx}, + {req: crud.NewReplaceManyRequest(validSpace).Context(ctx), expected: ctx}, + {req: crud.NewReplaceObjectManyRequest(validSpace).Context(ctx), expected: ctx}, + {req: crud.NewUpsertRequest(validSpace).Context(ctx), expected: ctx}, + {req: crud.NewUpsertObjectRequest(validSpace).Context(ctx), expected: ctx}, + {req: crud.NewUpsertManyRequest(validSpace).Context(ctx), expected: ctx}, + {req: crud.NewUpsertObjectManyRequest(validSpace).Context(ctx), expected: ctx}, + {req: crud.NewMinRequest(validSpace).Context(ctx), expected: ctx}, + {req: crud.NewMaxRequest(validSpace).Context(ctx), expected: ctx}, + {req: crud.NewSelectRequest(validSpace).Context(ctx), expected: ctx}, + {req: crud.NewTruncateRequest(validSpace).Context(ctx), expected: ctx}, + {req: crud.NewLenRequest(validSpace).Context(ctx), expected: ctx}, + {req: crud.NewCountRequest(validSpace).Context(ctx), expected: ctx}, + {req: crud.NewStorageInfoRequest().Context(ctx), expected: ctx}, + {req: crud.NewUnflattenRowsRequest().Context(ctx), expected: ctx}, + {req: crud.NewStatsRequest().Context(ctx), expected: ctx}, + } + + for _, test := range tests { + if ctx := test.req.Ctx(); ctx != test.expected { + t.Errorf("An invalid ctx %t, expected %t", ctx, test.expected) + } + } +} + +func TestRequestsDefaultValues(t *testing.T) { + testCases := []struct { + name string + ref tarantool.Request + target tarantool.Request + }{ + { + name: "InsertRequest", + ref: tarantool.NewCall17Request("crud.insert").Args([]interface{}{validSpace, []interface{}{}, + map[string]interface{}{}}), + target: crud.NewInsertRequest(validSpace), + }, + { + name: "InsertObjectRequest", + ref: tarantool.NewCall17Request("crud.insert_object").Args([]interface{}{validSpace, map[string]interface{}{}, + map[string]interface{}{}}), + target: crud.NewInsertObjectRequest(validSpace), + }, + { + name: "InsertManyRequest", + ref: tarantool.NewCall17Request("crud.insert_many").Args([]interface{}{validSpace, []interface{}{}, + map[string]interface{}{}}), + target: crud.NewInsertManyRequest(validSpace), + }, + { + name: "InsertObjectManyRequest", + ref: tarantool.NewCall17Request("crud.insert_object_many").Args([]interface{}{validSpace, []map[string]interface{}{}, + map[string]interface{}{}}), + target: crud.NewInsertObjectManyRequest(validSpace), + }, + { + name: "GetRequest", + ref: tarantool.NewCall17Request("crud.get").Args([]interface{}{validSpace, []interface{}{}, + map[string]interface{}{}}), + target: crud.NewGetRequest(validSpace), + }, + { + name: "UpdateRequest", + ref: tarantool.NewCall17Request("crud.update").Args([]interface{}{validSpace, []interface{}{}, + []interface{}{}, map[string]interface{}{}}), + target: crud.NewUpdateRequest(validSpace), + }, + { + name: "DeleteRequest", + ref: tarantool.NewCall17Request("crud.delete").Args([]interface{}{validSpace, []interface{}{}, + map[string]interface{}{}}), + target: crud.NewDeleteRequest(validSpace), + }, + { + name: "ReplaceRequest", + ref: tarantool.NewCall17Request("crud.replace").Args([]interface{}{validSpace, []interface{}{}, + map[string]interface{}{}}), + target: crud.NewReplaceRequest(validSpace), + }, + { + name: "ReplaceObjectRequest", + ref: tarantool.NewCall17Request("crud.replace_object").Args([]interface{}{validSpace, + map[string]interface{}{}, map[string]interface{}{}}), + target: crud.NewReplaceObjectRequest(validSpace), + }, + { + name: "ReplaceManyRequest", + ref: tarantool.NewCall17Request("crud.replace_many").Args([]interface{}{validSpace, + []interface{}{}, map[string]interface{}{}}), + target: crud.NewReplaceManyRequest(validSpace), + }, + { + name: "ReplaceObjectManyRequest", + ref: tarantool.NewCall17Request("crud.replace_object_many").Args([]interface{}{validSpace, + []map[string]interface{}{}, map[string]interface{}{}}), + target: crud.NewReplaceObjectManyRequest(validSpace), + }, + { + name: "UpsertRequest", + ref: tarantool.NewCall17Request("crud.upsert").Args([]interface{}{validSpace, []interface{}{}, + []interface{}{}, map[string]interface{}{}}), + target: crud.NewUpsertRequest(validSpace), + }, + { + name: "UpsertObjectRequest", + ref: tarantool.NewCall17Request("crud.upsert_object").Args([]interface{}{validSpace, + map[string]interface{}{}, []interface{}{}, map[string]interface{}{}}), + target: crud.NewUpsertObjectRequest(validSpace), + }, + { + name: "UpsertManyRequest", + ref: tarantool.NewCall17Request("crud.upsert_many").Args([]interface{}{validSpace, + []interface{}{}, map[string]interface{}{}}), + target: crud.NewUpsertManyRequest(validSpace), + }, + { + name: "UpsertObjectManyRequest", + ref: tarantool.NewCall17Request("crud.upsert_object_many").Args([]interface{}{validSpace, + []interface{}{}, map[string]interface{}{}}), + target: crud.NewUpsertObjectManyRequest(validSpace), + }, + { + name: "SelectRequest", + ref: tarantool.NewCall17Request("crud.select").Args([]interface{}{validSpace, + []interface{}{}, map[string]interface{}{}}), + target: crud.NewSelectRequest(validSpace), + }, + { + name: "MinRequest", + ref: tarantool.NewCall17Request("crud.min").Args([]interface{}{validSpace, + []interface{}{}, map[string]interface{}{}}), + target: crud.NewMinRequest(validSpace), + }, + { + name: "MaxRequest", + ref: tarantool.NewCall17Request("crud.max").Args([]interface{}{validSpace, + []interface{}{}, map[string]interface{}{}}), + target: crud.NewMaxRequest(validSpace), + }, + { + name: "TruncateRequest", + ref: tarantool.NewCall17Request("crud.truncate").Args([]interface{}{validSpace, + map[string]interface{}{}}), + target: crud.NewTruncateRequest(validSpace), + }, + { + name: "LenRequest", + ref: tarantool.NewCall17Request("crud.len").Args([]interface{}{validSpace, + map[string]interface{}{}}), + target: crud.NewLenRequest(validSpace), + }, + { + name: "CountRequest", + ref: tarantool.NewCall17Request("crud.count").Args([]interface{}{validSpace, + []interface{}{}, map[string]interface{}{}}), + target: crud.NewCountRequest(validSpace), + }, + { + name: "StorageInfoRequest", + ref: tarantool.NewCall17Request("crud.storage_info").Args( + []interface{}{map[string]interface{}{}}), + target: crud.NewStorageInfoRequest(), + }, + { + name: "UnflattenRowsRequest", + ref: tarantool.NewCall17Request("crud.unflatten_rows").Args( + []interface{}{[]interface{}{}, []interface{}{}}), + target: crud.NewUnflattenRowsRequest(), + }, + { + name: "StatsRequest", + ref: tarantool.NewCall17Request("crud.stats").Args( + []interface{}{[]interface{}{}}), + target: crud.NewStatsRequest(), + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + assertBodyEqual(t, tc.ref, tc.target) + }) + } +} diff --git a/crud/select.go b/crud/select.go new file mode 100644 index 000000000..a3fa73473 --- /dev/null +++ b/crud/select.go @@ -0,0 +1,53 @@ +package crud + +import ( + "context" + + "github.com/tarantool/go-tarantool" +) + +// SelectRequest helps you to create request +// object to call `crud.select` for execution +// by a Connection. +type SelectRequest struct { + spaceRequest + conditions interface{} + opts interface{} +} + +// NewSelectRequest returns a new empty SelectRequest. +func NewSelectRequest(space interface{}) *SelectRequest { + req := new(SelectRequest) + req.initImpl("crud.select") + req.setSpace(space) + req.conditions = []interface{}{} + req.opts = map[string]interface{}{} + return req +} + +// Conditions sets the conditions for the SelectRequest request. +// Note: default value is nil. +func (req *SelectRequest) Conditions(conditions interface{}) *SelectRequest { + req.conditions = conditions + return req +} + +// Opts sets the options for the SelectRequest request. +// Note: default value is nil. +func (req *SelectRequest) Opts(opts interface{}) *SelectRequest { + req.opts = opts + return req +} + +// Body fills an encoder with the call request body. +func (req *SelectRequest) Body(res tarantool.SchemaResolver, enc *encoder) error { + req.impl.Args([]interface{}{req.space, req.conditions, req.opts}) + return req.impl.Body(res, enc) +} + +// Context sets a passed context to CRUD request. +func (req *SelectRequest) Context(ctx context.Context) *SelectRequest { + req.impl = req.impl.Context(ctx) + + return req +} diff --git a/crud/stats.go b/crud/stats.go new file mode 100644 index 000000000..90a187ca8 --- /dev/null +++ b/crud/stats.go @@ -0,0 +1,42 @@ +package crud + +import ( + "context" + + "github.com/tarantool/go-tarantool" +) + +// StatsRequest helps you to create request +// object to call `crud.stats` for execution +// by a Connection. +type StatsRequest struct { + spaceRequest +} + +// NewStatsRequest returns a new empty StatsRequest. +func NewStatsRequest() *StatsRequest { + req := new(StatsRequest) + req.initImpl("crud.stats") + req.space = []interface{}{} + return req +} + +// Space sets the space name for the StatsRequest request. +// Note: default value is nil. +func (req *StatsRequest) Space(space interface{}) *StatsRequest { + req.setSpace(space) + return req +} + +// Body fills an encoder with the call request body. +func (req *StatsRequest) Body(res tarantool.SchemaResolver, enc *encoder) error { + req.impl.Args([]interface{}{req.space}) + return req.impl.Body(res, enc) +} + +// Context sets a passed context to CRUD request. +func (req *StatsRequest) Context(ctx context.Context) *StatsRequest { + req.impl = req.impl.Context(ctx) + + return req +} diff --git a/crud/storage_info.go b/crud/storage_info.go new file mode 100644 index 000000000..f040749e4 --- /dev/null +++ b/crud/storage_info.go @@ -0,0 +1,43 @@ +package crud + +import ( + "context" + + "github.com/tarantool/go-tarantool" +) + +// StorageInfoRequest helps you to create request +// object to call `crud.storage_info` for execution +// by a Connection. +type StorageInfoRequest struct { + baseRequest + opts interface{} +} + +// NewStorageInfoRequest returns a new empty StorageInfoRequest. +func NewStorageInfoRequest() *StorageInfoRequest { + req := new(StorageInfoRequest) + req.initImpl("crud.storage_info") + req.opts = map[string]interface{}{} + return req +} + +// Opts sets the options for the torageInfoRequest request. +// Note: default value is nil. +func (req *StorageInfoRequest) Opts(opts interface{}) *StorageInfoRequest { + req.opts = opts + return req +} + +// Body fills an encoder with the call request body. +func (req *StorageInfoRequest) Body(res tarantool.SchemaResolver, enc *encoder) error { + req.impl.Args([]interface{}{req.opts}) + return req.impl.Body(res, enc) +} + +// Context sets a passed context to CRUD request. +func (req *StorageInfoRequest) Context(ctx context.Context) *StorageInfoRequest { + req.impl = req.impl.Context(ctx) + + return req +} diff --git a/crud/tarantool_test.go b/crud/tarantool_test.go new file mode 100644 index 000000000..e5fe3bd2a --- /dev/null +++ b/crud/tarantool_test.go @@ -0,0 +1,446 @@ +package crud_test + +import ( + "fmt" + "log" + "os" + "testing" + "time" + + "github.com/tarantool/go-tarantool" + "github.com/tarantool/go-tarantool/crud" + "github.com/tarantool/go-tarantool/test_helpers" +) + +var server = "127.0.0.1:3013" +var spaceNo = uint32(617) +var spaceName = "test" +var indexNo = uint32(0) +var indexName = "primary_index" +var opts = tarantool.Opts{ + Timeout: 500 * time.Millisecond, + User: "test", + Pass: "test", +} + +var startOpts test_helpers.StartOpts = test_helpers.StartOpts{ + InitScript: "testdata/config.lua", + Listen: server, + User: opts.User, + Pass: opts.Pass, + WaitStart: 100 * time.Millisecond, + ConnectRetry: 3, + RetryTimeout: 500 * time.Millisecond, +} + +var crudOpts = map[string]interface{}{ + "timeout": 1, +} + +var operations = []interface{}{ + []interface{}{"=", "name", "bye"}, +} + +var tuples = generateTuples() +var objects = generateObjects() + +var testProcessDataCases = []struct { + name string + expectedRespLen int + req tarantool.Request +}{ + { + "Select", + 2, + crud.NewSelectRequest(spaceName). + Conditions([]interface{}{[]interface{}{"<", "id", uint(1020)}}). + Opts(crudOpts), + }, + { + "Get", + 2, + crud.NewGetRequest(spaceName). + Key([]interface{}{uint(1019)}). + Opts(crudOpts), + }, + { + "Update", + 2, + crud.NewUpdateRequest(spaceName). + Key([]interface{}{uint(1019)}). + Operations(operations). + Opts(crudOpts), + }, + { + "Delete", + 2, + crud.NewDeleteRequest(spaceName). + Key([]interface{}{uint(1019)}). + Opts(crudOpts), + }, + { + "Min", + 2, + crud.NewMinRequest(spaceName).Index(indexName).Opts(crudOpts), + }, + { + "Max", + 2, + crud.NewMaxRequest(spaceName).Index(indexName).Opts(crudOpts), + }, + { + "Truncate", + 1, + crud.NewTruncateRequest(spaceName).Opts(crudOpts), + }, + { + "Len", + 1, + crud.NewLenRequest(spaceName).Opts(crudOpts), + }, + { + "Count", + 2, + crud.NewCountRequest(spaceName). + Conditions([]interface{}{[]interface{}{"<", "id", uint(1020)}}). + Opts(crudOpts), + }, + { + "Stats", + 1, + crud.NewStatsRequest().Space(spaceName), + }, + { + "StorageInfo", + 1, + crud.NewStorageInfoRequest().Opts(crudOpts), + }, + { + "UnflattenRows", + 1, + crud.NewUnflattenRowsRequest().Tuples([]interface{}{ + []interface{}{uint(1), "Alex"}, + []interface{}{uint(2), "Anna"}, + }). + SpaceFormat([]interface{}{ + map[string]string{ + "name": "id", + "type": "number", + }, + map[string]string{ + "name": "name", + "type": "string", + }, + }), + }, +} + +var tuplesOperationsData = generateTuplesOperationsData(tuples, operations) +var objectsOperationData = generateObjectsOperationsData(objects, operations) + +var testGenerateDataCases = []struct { + name string + expectedRespLen int + expectedTuplesCount int + req tarantool.Request +}{ + { + "Insert", + 2, + 1, + crud.NewInsertRequest(spaceName). + Tuple([]interface{}{uint(1019), nil, "bla"}). + Opts(crudOpts), + }, + { + "InsertObject", + 2, + 1, + crud.NewInsertObjectRequest(spaceName). + Object(map[string]interface{}{ + "id": uint(1019), + "name": "bla", + }). + Opts(crudOpts), + }, + { + "InsertMany", + 2, + 10, + crud.NewInsertManyRequest(spaceName). + Tuples(tuples). + Opts(crudOpts), + }, + { + "InsertObjectMany", + 2, + 10, + crud.NewInsertObjectManyRequest(spaceName). + Objects(objects). + Opts(crudOpts), + }, + { + "Replace", + 2, + 1, + crud.NewReplaceRequest(spaceName). + Tuple([]interface{}{uint(1019), nil, "bla"}). + Opts(crudOpts), + }, + { + "ReplaceObject", + 2, + 1, + crud.NewReplaceObjectRequest(spaceName). + Object(map[string]interface{}{ + "id": uint(1019), + "name": "bla", + }). + Opts(crudOpts), + }, + { + "ReplaceMany", + 2, + 10, + crud.NewReplaceManyRequest(spaceName). + Tuples(tuples). + Opts(crudOpts), + }, + { + "ReplaceObjectMany", + 2, + 10, + crud.NewReplaceObjectManyRequest(spaceName). + Objects(objects). + Opts(crudOpts), + }, + { + "Upsert", + 2, + 1, + crud.NewUpsertRequest(spaceName). + Tuple([]interface{}{uint(1019), nil, "bla"}). + Opts(crudOpts), + }, + { + "UpsertObject", + 2, + 1, + crud.NewUpsertObjectRequest(spaceName). + Object(map[string]interface{}{ + "id": uint(1019), + "name": "bla", + }). + Opts(crudOpts), + }, + { + "UpsertMany", + 2, + 10, + crud.NewUpsertManyRequest(spaceName). + TuplesOperationData(tuplesOperationsData). + Opts(crudOpts), + }, + { + "UpsertObjectMany", + 2, + 10, + crud.NewUpsertObjectManyRequest(spaceName). + ObjectsOperationData(objectsOperationData). + Opts(crudOpts), + }, +} + +func generateTuples() []interface{} { + tuples := []interface{}{} + for i := 1010; i < 1020; i++ { + tuples = append(tuples, []interface{}{uint(i), nil, "bla"}) + } + + return tuples +} + +func generateTuplesOperationsData(tuples, operations []interface{}) []interface{} { + tuplesOperationsData := []interface{}{} + for _, tuple := range tuples { + tuplesOperationsData = append(tuplesOperationsData, []interface{}{ + tuple, + operations, + }) + } + + return tuplesOperationsData +} + +func generateObjects() []map[string]interface{} { + objects := []map[string]interface{}{} + for i := 1010; i < 1020; i++ { + key := uint(i) + objects = append(objects, map[string]interface{}{ + "id": key, + "name": "bla", + }) + } + + return objects +} + +func generateObjectsOperationsData(tuples []map[string]interface{}, + operations []interface{}) []interface{} { + objectsOperationsData := []interface{}{} + for _, object := range objects { + objectsOperationsData = append(objectsOperationsData, []interface{}{ + object, + operations, + }) + } + + return objectsOperationsData +} + +func getCrudError(req tarantool.Request, crudError interface{}) (interface{}, error) { + var err []interface{} + var ok bool + + code := req.Code() + if crudError != nil { + if code == tarantool.Call17RequestCode { + return crudError, nil + } + + if err, ok = crudError.([]interface{}); !ok { + return nil, fmt.Errorf("Incorrect CRUD error format") + } + + if len(err) < 1 { + return nil, fmt.Errorf("Incorrect CRUD error format") + } + + if err[0] != nil { + return err[0], nil + } + } + + return nil, nil +} + +func testCrudRequestPrepareData(t *testing.T, conn tarantool.Connector) { + t.Helper() + + for i := 1010; i < 1020; i++ { + req := tarantool.NewReplaceRequest(spaceName).Tuple( + []interface{}{uint(i), nil, "bla"}) + if _, err := conn.Do(req).Get(); err != nil { + t.Fatalf("Unable to prepare tuples: %s", err) + } + } +} + +func testSelectGeneratedData(t *testing.T, conn tarantool.Connector, + expectedTuplesCount int) { + req := tarantool.NewSelectRequest(spaceNo). + Index(indexNo). + Limit(20). + Iterator(tarantool.IterGe). + Key([]interface{}{uint(1010)}) + resp, err := conn.Do(req).Get() + if err != nil { + t.Fatalf("Failed to Select: %s", err.Error()) + } + if resp == nil { + t.Fatalf("Response is nil after Select") + } + if len(resp.Data) != expectedTuplesCount { + t.Fatalf("Response Data len %d != %d", len(resp.Data), expectedTuplesCount) + } +} + +func testCrudRequestCheck(t *testing.T, req tarantool.Request, + resp *tarantool.Response, err error, expectedLen int) { + t.Helper() + + if err != nil { + t.Fatalf("Failed to Do CRUD request: %s", err.Error()) + } + + if resp == nil { + t.Fatalf("Response is nil after Do CRUD request") + } + + if len(resp.Data) < expectedLen { + t.Fatalf("Response Body len < %#v, actual len %#v", + expectedLen, len(resp.Data)) + } + + // resp.Data[0] - CRUD res. + // resp.Data[1] - CRUD err. + if expectedLen >= 2 { + if crudErr, err := getCrudError(req, resp.Data[1]); err != nil { + t.Fatalf("Failed to get CRUD error: %#v", err) + } else if crudErr != nil { + t.Fatalf("Failed to perform CRUD request on CRUD side: %#v", crudErr) + } + } +} + +func TestCrudGenerateData(t *testing.T) { + conn := test_helpers.ConnectWithValidation(t, server, opts) + defer conn.Close() + + for _, testCase := range testGenerateDataCases { + t.Run(testCase.name, func(t *testing.T) { + for i := 1010; i < 1020; i++ { + conn.Delete(spaceName, nil, []interface{}{uint(i)}) + } + + resp, err := conn.Do(testCase.req).Get() + testCrudRequestCheck(t, testCase.req, resp, + err, testCase.expectedRespLen) + + testSelectGeneratedData(t, conn, testCase.expectedTuplesCount) + + for i := 1010; i < 1020; i++ { + conn.Delete(spaceName, nil, []interface{}{uint(i)}) + } + }) + } +} + +func TestCrudProcessData(t *testing.T) { + conn := test_helpers.ConnectWithValidation(t, server, opts) + defer conn.Close() + + for _, testCase := range testProcessDataCases { + t.Run(testCase.name, func(t *testing.T) { + testCrudRequestPrepareData(t, conn) + resp, err := conn.Do(testCase.req).Get() + testCrudRequestCheck(t, testCase.req, resp, + err, testCase.expectedRespLen) + + for i := 1010; i < 1020; i++ { + conn.Delete(spaceName, nil, []interface{}{uint(i)}) + } + }) + } +} + +// runTestMain is a body of TestMain function +// (see https://pkg.go.dev/testing#hdr-Main). +// Using defer + os.Exit is not works so TestMain body +// is a separate function, see +// https://stackoverflow.com/questions/27629380/how-to-exit-a-go-program-honoring-deferred-calls +func runTestMain(m *testing.M) int { + inst, err := test_helpers.StartTarantool(startOpts) + defer test_helpers.StopTarantoolWithCleanup(inst) + + if err != nil { + log.Fatalf("Failed to prepare test tarantool: %s", err) + } + + return m.Run() +} + +func TestMain(m *testing.M) { + code := runTestMain(m) + os.Exit(code) +} diff --git a/crud/testdata/config.lua b/crud/testdata/config.lua new file mode 100644 index 000000000..9663d3c13 --- /dev/null +++ b/crud/testdata/config.lua @@ -0,0 +1,74 @@ +local crud = require('crud') +local vshard = require('vshard') + +-- Do not set listen for now so connector won't be +-- able to send requests until everything is configured. +box.cfg{ + work_dir = os.getenv("TEST_TNT_WORK_DIR"), +} + +box.schema.user.grant( + 'guest', + 'read,write,execute', + 'universe' +) + +local s = box.schema.space.create('test', { + id = 617, + if_not_exists = true, + format = { + {name = 'id', type = 'unsigned'}, + {name = 'bucket_id', type = 'unsigned', is_nullable = true}, + {name = 'name', type = 'string'}, + } +}) +s:create_index('primary_index', { + parts = { + {field = 1, type = 'unsigned'}, + }, +}) +s:create_index('bucket_id', { + parts = { + {field = 2, type = 'unsigned'}, + }, + unique = false, +}) + +-- Setup vshard. +_G.vshard = vshard +box.once('guest', function() + box.schema.user.grant('guest', 'super') +end) +local uri = 'guest@127.0.0.1:3013' +local cfg = { + bucket_count = 300, + sharding = { + [box.info().cluster.uuid] = { + replicas = { + [box.info().uuid] = { + uri = uri, + name = 'storage', + master = true, + }, + }, + }, + }, +} +vshard.storage.cfg(cfg, box.info().uuid) +vshard.router.cfg(cfg) +vshard.router.bootstrap() + +-- Initialize crud. +crud.init_storage() +crud.init_router() +crud.cfg{stats = true} + +box.schema.user.create('test', { password = 'test' , if_not_exists = true }) +box.schema.user.grant('test', 'execute', 'universe', nil, { if_not_exists = true }) +box.schema.user.grant('test', 'create,read,write,drop,alter', 'space', nil, { if_not_exists = true }) +box.schema.user.grant('test', 'create', 'sequence', nil, { if_not_exists = true }) + +-- Set listen only when every other thing is configured. +box.cfg{ + listen = os.getenv("TEST_TNT_LISTEN"), +} diff --git a/crud/truncate.go b/crud/truncate.go new file mode 100644 index 000000000..c2a9f2c7c --- /dev/null +++ b/crud/truncate.go @@ -0,0 +1,44 @@ +package crud + +import ( + "context" + + "github.com/tarantool/go-tarantool" +) + +// TruncateRequest helps you to create request +// object to call `crud.truncate` for execution +// by a Connection. +type TruncateRequest struct { + spaceRequest + opts interface{} +} + +// NewTruncateRequest returns a new empty TruncateRequest. +func NewTruncateRequest(space interface{}) *TruncateRequest { + req := new(TruncateRequest) + req.initImpl("crud.truncate") + req.setSpace(space) + req.opts = map[string]interface{}{} + return req +} + +// Opts sets the options for the TruncateRequest request. +// Note: default value is nil. +func (req *TruncateRequest) Opts(opts interface{}) *TruncateRequest { + req.opts = opts + return req +} + +// Body fills an encoder with the call request body. +func (req *TruncateRequest) Body(res tarantool.SchemaResolver, enc *encoder) error { + req.impl.Args([]interface{}{req.space, req.opts}) + return req.impl.Body(res, enc) +} + +// Context sets a passed context to CRUD request. +func (req *TruncateRequest) Context(ctx context.Context) *TruncateRequest { + req.impl = req.impl.Context(ctx) + + return req +} diff --git a/crud/unflatten_rows.go b/crud/unflatten_rows.go new file mode 100644 index 000000000..029ff8ce3 --- /dev/null +++ b/crud/unflatten_rows.go @@ -0,0 +1,52 @@ +package crud + +import ( + "context" + + "github.com/tarantool/go-tarantool" +) + +// UnflattenRowsRequest helps you to create request +// object to call `crud.unflatten_rows` for execution +// by a Connection. +type UnflattenRowsRequest struct { + baseRequest + spaceFormat interface{} + tuples interface{} +} + +// NewUnflattenRowsRequest returns a new empty UnflattenRowsRequest. +func NewUnflattenRowsRequest() *UnflattenRowsRequest { + req := new(UnflattenRowsRequest) + req.initImpl("crud.unflatten_rows") + req.tuples = []interface{}{} + req.spaceFormat = []interface{}{} + return req +} + +// SpaceFormat sets the space format for the UnflattenRowsRequest request. +// Note: default value is nil. +func (req *UnflattenRowsRequest) SpaceFormat(format interface{}) *UnflattenRowsRequest { + req.spaceFormat = format + return req +} + +// Tuples sets the tuples for the UnflattenRowsRequest equest. +// Note: default value is nil. +func (req *UnflattenRowsRequest) Tuples(tuples interface{}) *UnflattenRowsRequest { + req.tuples = tuples + return req +} + +// Body fills an encoder with the call request body. +func (req *UnflattenRowsRequest) Body(res tarantool.SchemaResolver, enc *encoder) error { + req.impl.Args([]interface{}{req.tuples, req.spaceFormat}) + return req.impl.Body(res, enc) +} + +// Context sets a passed context to CRUD request. +func (req *UnflattenRowsRequest) Context(ctx context.Context) *UnflattenRowsRequest { + req.impl = req.impl.Context(ctx) + + return req +} diff --git a/crud/update.go b/crud/update.go new file mode 100644 index 000000000..f1da13eaf --- /dev/null +++ b/crud/update.go @@ -0,0 +1,62 @@ +package crud + +import ( + "context" + + "github.com/tarantool/go-tarantool" +) + +// UpdateRequest helps you to create request +// object to call `crud.update` for execution +// by a Connection. +type UpdateRequest struct { + spaceRequest + key interface{} + operations interface{} + opts interface{} +} + +// NewUpdateRequest returns a new empty UpdateRequest. +func NewUpdateRequest(space interface{}) *UpdateRequest { + req := new(UpdateRequest) + req.initImpl("crud.update") + req.setSpace(space) + req.key = []interface{}{} + req.operations = []interface{}{} + req.opts = map[string]interface{}{} + return req +} + +// Key sets the key for the UpdateRequest request. +// Note: default value is nil. +func (req *UpdateRequest) Key(key interface{}) *UpdateRequest { + req.key = key + return req +} + +// Operations sets the operations for UpdateRequest request. +// Note: default value is nil. +func (req *UpdateRequest) Operations(operations interface{}) *UpdateRequest { + req.operations = operations + return req +} + +// Opts sets the options for the UpdateRequest request. +// Note: default value is nil. +func (req *UpdateRequest) Opts(opts interface{}) *UpdateRequest { + req.opts = opts + return req +} + +// Body fills an encoder with the call request body. +func (req *UpdateRequest) Body(res tarantool.SchemaResolver, enc *encoder) error { + req.impl.Args([]interface{}{req.space, req.key, req.operations, req.opts}) + return req.impl.Body(res, enc) +} + +// Context sets a passed context to CRUD request. +func (req *UpdateRequest) Context(ctx context.Context) *UpdateRequest { + req.impl = req.impl.Context(ctx) + + return req +} diff --git a/crud/upsert.go b/crud/upsert.go new file mode 100644 index 000000000..b2fd2fab8 --- /dev/null +++ b/crud/upsert.go @@ -0,0 +1,117 @@ +package crud + +import ( + "context" + + "github.com/tarantool/go-tarantool" +) + +// UpsertRequest helps you to create request +// object to call `crud.upsert` for execution +// by a Connection. +type UpsertRequest struct { + spaceRequest + tuple interface{} + operations interface{} + opts interface{} +} + +// NewUpsertRequest returns a new empty UpsertRequest. +func NewUpsertRequest(space interface{}) *UpsertRequest { + req := new(UpsertRequest) + req.initImpl("crud.upsert") + req.setSpace(space) + req.tuple = []interface{}{} + req.operations = []interface{}{} + req.opts = map[string]interface{}{} + return req +} + +// Tuple sets the tuple for the UpsertRequest request. +// Note: default value is nil. +func (req *UpsertRequest) Tuple(tuple interface{}) *UpsertRequest { + req.tuple = tuple + return req +} + +// Operations sets the operations for the UpsertRequest request. +// Note: default value is nil. +func (req *UpsertRequest) Operations(operations interface{}) *UpsertRequest { + req.operations = operations + return req +} + +// Opts sets the options for the UpsertRequest request. +// Note: default value is nil. +func (req *UpsertRequest) Opts(opts interface{}) *UpsertRequest { + req.opts = opts + return req +} + +// Body fills an encoder with the call request body. +func (req *UpsertRequest) Body(res tarantool.SchemaResolver, enc *encoder) error { + req.impl.Args([]interface{}{req.space, req.tuple, req.operations, req.opts}) + return req.impl.Body(res, enc) +} + +// Context sets a passed context to CRUD request. +func (req *UpsertRequest) Context(ctx context.Context) *UpsertRequest { + req.impl = req.impl.Context(ctx) + + return req +} + +// UpsertObjectRequest helps you to create request +// object to call `crud.upsert_object` for execution +// by a Connection. +type UpsertObjectRequest struct { + spaceRequest + object interface{} + operations interface{} + opts interface{} +} + +// NewUpsertObjectRequest returns a new empty UpsertObjectRequest. +func NewUpsertObjectRequest(space interface{}) *UpsertObjectRequest { + req := new(UpsertObjectRequest) + req.initImpl("crud.upsert_object") + req.setSpace(space) + req.object = map[string]interface{}{} + req.operations = []interface{}{} + req.opts = map[string]interface{}{} + return req +} + +// Object sets the tuple for the UpsertObjectRequest request. +// Note: default value is nil. +func (req *UpsertObjectRequest) Object(object interface{}) *UpsertObjectRequest { + req.object = object + return req +} + +// Operations sets the operations for the UpsertObjectRequest request. +// Note: default value is nil. +func (req *UpsertObjectRequest) Operations(operations interface{}) *UpsertObjectRequest { + req.operations = operations + return req +} + +// Opts sets the options for the UpsertObjectRequest request. +// Note: default value is nil. +func (req *UpsertObjectRequest) Opts(opts interface{}) *UpsertObjectRequest { + req.opts = opts + return req +} + +// Body fills an encoder with the call request body. +func (req *UpsertObjectRequest) Body(res tarantool.SchemaResolver, enc *encoder) error { + req.impl.Args([]interface{}{req.space, req.object, req.operations, req.opts}) + return req.impl.Body(res, enc) +} + +// Context sets a passed context to CRUD request. +func (req *UpsertObjectRequest) Context(ctx context.Context) *UpsertObjectRequest { + req.impl = req.impl.Context(ctx) + + return req +} diff --git a/crud/upsert_many.go b/crud/upsert_many.go new file mode 100644 index 000000000..0d77e825f --- /dev/null +++ b/crud/upsert_many.go @@ -0,0 +1,102 @@ +package crud + +import ( + "context" + + "github.com/tarantool/go-tarantool" +) + +// UpsertManyRequest helps you to create request +// object to call `crud.upsert_many` for execution +// by a Connection. +type UpsertManyRequest struct { + spaceRequest + tuplesOperationData interface{} + opts interface{} +} + +// NewUpsertManyRequest returns a new empty UpsertManyRequest. +func NewUpsertManyRequest(space interface{}) *UpsertManyRequest { + req := new(UpsertManyRequest) + req.initImpl("crud.upsert_many") + req.setSpace(space) + req.tuplesOperationData = []interface{}{} + req.opts = map[string]interface{}{} + return req +} + +// TuplesOperationData sets tuples and operations +// for the UpsertManyRequest request. +// Note: default value is nil. +func (req *UpsertManyRequest) TuplesOperationData(tuplesOperationData interface{}) *UpsertManyRequest { + req.tuplesOperationData = tuplesOperationData + return req +} + +// Opts sets the options for the UpsertManyRequest request. +// Note: default value is nil. +func (req *UpsertManyRequest) Opts(opts interface{}) *UpsertManyRequest { + req.opts = opts + return req +} + +// Body fills an encoder with the call request body. +func (req *UpsertManyRequest) Body(res tarantool.SchemaResolver, enc *encoder) error { + req.impl.Args([]interface{}{req.space, req.tuplesOperationData, req.opts}) + return req.impl.Body(res, enc) +} + +// Context sets a passed context to CRUD request. +func (req *UpsertManyRequest) Context(ctx context.Context) *UpsertManyRequest { + req.impl = req.impl.Context(ctx) + + return req +} + +// UpsertObjectManyRequest helps you to create request +// object to call `crud.upsert_object_many` for execution +// by a Connection. +type UpsertObjectManyRequest struct { + spaceRequest + objectsOperationData interface{} + opts interface{} +} + +// NewUpsertObjectManyRequest returns a new empty UpsertObjectManyRequest. +func NewUpsertObjectManyRequest(space interface{}) *UpsertObjectManyRequest { + req := new(UpsertObjectManyRequest) + req.initImpl("crud.upsert_object_many") + req.setSpace(space) + req.objectsOperationData = []interface{}{} + req.opts = map[string]interface{}{} + return req +} + +// ObjectOperationData sets objects and operations +// for the UpsertObjectManyRequest request. +// Note: default value is nil. +func (req *UpsertObjectManyRequest) ObjectsOperationData( + objectsOperationData interface{}) *UpsertObjectManyRequest { + req.objectsOperationData = objectsOperationData + return req +} + +// Opts sets the options for the UpsertObjectManyRequest request. +// Note: default value is nil. +func (req *UpsertObjectManyRequest) Opts(opts interface{}) *UpsertObjectManyRequest { + req.opts = opts + return req +} + +// Body fills an encoder with the call request body. +func (req *UpsertObjectManyRequest) Body(res tarantool.SchemaResolver, enc *encoder) error { + req.impl.Args([]interface{}{req.space, req.objectsOperationData, req.opts}) + return req.impl.Body(res, enc) +} + +// Context sets a passed context to CRUD request. +func (req *UpsertObjectManyRequest) Context(ctx context.Context) *UpsertObjectManyRequest { + req.impl = req.impl.Context(ctx) + + return req +} diff --git a/request_test.go b/request_test.go index 32bdd87e1..22967320e 100644 --- a/request_test.go +++ b/request_test.go @@ -10,6 +10,8 @@ import ( "github.com/stretchr/testify/assert" . "github.com/tarantool/go-tarantool" + "github.com/tarantool/go-tarantool/crud" + "github.com/tarantool/go-tarantool/test_helpers" ) const invalidSpaceMsg = "invalid space" @@ -85,17 +87,13 @@ func assertBodyCall(t testing.TB, requests []Request, errorMsg string) { func assertBodyEqual(t testing.TB, reference []byte, req Request) { t.Helper() - var reqBuf bytes.Buffer - reqEnc := NewEncoder(&reqBuf) - - err := req.Body(&resolver, reqEnc) + reqBody, err := test_helpers.ExtractRequestBody(req, &resolver, NewEncoder) if err != nil { - t.Errorf("An unexpected Response.Body() error: %q", err.Error()) - } else { - reqBody := reqBuf.Bytes() - if !bytes.Equal(reqBody, reference) { - t.Errorf("Encoded request %v != reference %v", reqBody, reference) - } + t.Fatalf("An unexpected Response.Body() error: %q", err.Error()) + } + + if !bytes.Equal(reqBody, reference) { + t.Errorf("Encoded request %v != reference %v", reqBody, reference) } } @@ -815,3 +813,908 @@ func TestBroadcastRequestSetters(t *testing.T) { req := NewBroadcastRequest(validKey).Value(value) assertBodyEqual(t, refBuf.Bytes(), req) } + +func TestCrudInsertRequestDefaultValues(t *testing.T) { + var refBuf bytes.Buffer + + refEnc := NewEncoder(&refBuf) + expectedArgs := []interface{}{validSpace, []interface{}{}, + map[string]interface{}{}} + err := RefImplCallBody(refEnc, "crud.insert", expectedArgs) + if err != nil { + t.Errorf("An unexpected RefImplCallBody() error: %q", err.Error()) + return + } + + req := crud.NewInsertRequest(validSpace) + assertBodyEqual(t, refBuf.Bytes(), req) +} + +func TestCrudInsertRequestSetters(t *testing.T) { + tuple := []interface{}{uint(24)} + opts := map[string]interface{}{ + "fields": []interface{}{"id", "name"}, + } + var refBuf bytes.Buffer + + refEnc := NewEncoder(&refBuf) + expectedArgs := []interface{}{validSpace, tuple, opts} + err := RefImplCallBody(refEnc, "crud.insert", expectedArgs) + if err != nil { + t.Errorf("An unexpected RefImplCallBody() error: %q", err.Error()) + return + } + + req := crud.NewInsertRequest(validSpace).Tuple(tuple).Opts(opts) + assertBodyEqual(t, refBuf.Bytes(), req) +} + +func TestCrudInsertObjectRequestDefaultValues(t *testing.T) { + var refBuf bytes.Buffer + + refEnc := NewEncoder(&refBuf) + expectedArgs := []interface{}{validSpace, map[string]interface{}{}, + map[string]interface{}{}} + err := RefImplCallBody(refEnc, "crud.insert_object", expectedArgs) + if err != nil { + t.Errorf("An unexpected RefImplCallBody() error: %q", err.Error()) + return + } + + req := crud.NewInsertObjectRequest(validSpace) + assertBodyEqual(t, refBuf.Bytes(), req) +} + +func TestCrudInsertObjectRequestSetters(t *testing.T) { + object := map[string]interface{}{ + "id": uint(24), + } + opts := map[string]interface{}{ + "fields": []interface{}{"id", "name"}, + } + var refBuf bytes.Buffer + + refEnc := NewEncoder(&refBuf) + expectedArgs := []interface{}{validSpace, object, opts} + err := RefImplCallBody(refEnc, "crud.insert_object", expectedArgs) + if err != nil { + t.Errorf("An unexpected RefImplCallBody() error: %q", err.Error()) + return + } + + req := crud.NewInsertObjectRequest(validSpace). + Object(object).Opts(opts) + assertBodyEqual(t, refBuf.Bytes(), req) +} + +func TestCrudInsertManyRequestDefaultValues(t *testing.T) { + var refBuf bytes.Buffer + + refEnc := NewEncoder(&refBuf) + expectedArgs := []interface{}{validSpace, []interface{}{}, + map[string]interface{}{}} + err := RefImplCallBody(refEnc, "crud.insert_many", expectedArgs) + if err != nil { + t.Errorf("An unexpected RefImplCallBody() error: %q", err.Error()) + return + } + + req := crud.NewInsertManyRequest(validSpace) + assertBodyEqual(t, refBuf.Bytes(), req) +} + +func TestCrudInsertManyRequestSetters(t *testing.T) { + tuples := []interface{}{ + []interface{}{uint(24)}, + []interface{}{uint(25)}, + } + opts := map[string]interface{}{ + "fields": []interface{}{"id", "name"}, + } + var refBuf bytes.Buffer + + refEnc := NewEncoder(&refBuf) + expectedArgs := []interface{}{validSpace, tuples, opts} + err := RefImplCallBody(refEnc, "crud.insert_many", expectedArgs) + if err != nil { + t.Errorf("An unexpected RefImplCallBody() error: %q", err.Error()) + return + } + + req := crud.NewInsertManyRequest(validSpace).Tuples(tuples).Opts(opts) + assertBodyEqual(t, refBuf.Bytes(), req) +} + +func TestCrudInsertObjectManyRequestDefaultValues(t *testing.T) { + var refBuf bytes.Buffer + + refEnc := NewEncoder(&refBuf) + expectedArgs := []interface{}{validSpace, []map[string]interface{}{}, + map[string]interface{}{}} + err := RefImplCallBody(refEnc, "crud.insert_object_many", expectedArgs) + if err != nil { + t.Errorf("An unexpected RefImplCallBody() error: %q", err.Error()) + return + } + + req := crud.NewInsertObjectManyRequest(validSpace) + assertBodyEqual(t, refBuf.Bytes(), req) +} + +func TestCrudInsertObjectManyRequestSetters(t *testing.T) { + objects := []map[string]interface{}{ + map[string]interface{}{ + "id": uint(24), + }, + map[string]interface{}{ + "id": uint(25), + }, + } + opts := map[string]interface{}{ + "fields": []interface{}{"id", "name"}, + } + var refBuf bytes.Buffer + + refEnc := NewEncoder(&refBuf) + expectedArgs := []interface{}{validSpace, objects, opts} + err := RefImplCallBody(refEnc, "crud.insert_object_many", expectedArgs) + if err != nil { + t.Errorf("An unexpected RefImplCallBody() error: %q", err.Error()) + return + } + + req := crud.NewInsertObjectManyRequest(validSpace). + Objects(objects).Opts(opts) + assertBodyEqual(t, refBuf.Bytes(), req) +} + +func TestCrudGetRequestDefaultValues(t *testing.T) { + var refBuf bytes.Buffer + + refEnc := NewEncoder(&refBuf) + expectedArgs := []interface{}{validSpace, []interface{}{}, + map[string]interface{}{}} + err := RefImplCallBody(refEnc, "crud.get", expectedArgs) + if err != nil { + t.Errorf("An unexpected RefImplCallBody() error: %q", err.Error()) + return + } + + req := crud.NewGetRequest(validSpace) + assertBodyEqual(t, refBuf.Bytes(), req) +} + +func TestCrudGetRequestSetters(t *testing.T) { + key := []interface{}{uint(24)} + opts := map[string]interface{}{ + "fields": []interface{}{"id", "name"}, + } + var refBuf bytes.Buffer + + refEnc := NewEncoder(&refBuf) + expectedArgs := []interface{}{validSpace, key, opts} + err := RefImplCallBody(refEnc, "crud.get", expectedArgs) + if err != nil { + t.Errorf("An unexpected RefImplCallBody() error: %q", err.Error()) + return + } + + req := crud.NewGetRequest(validSpace).Key(key).Opts(opts) + assertBodyEqual(t, refBuf.Bytes(), req) +} + +func TestCrudUpdateRequestDefaultValues(t *testing.T) { + var refBuf bytes.Buffer + + refEnc := NewEncoder(&refBuf) + expectedArgs := []interface{}{validSpace, []interface{}{}, + []interface{}{}, map[string]interface{}{}} + err := RefImplCallBody(refEnc, "crud.update", expectedArgs) + if err != nil { + t.Errorf("An unexpected RefImplCallBody() error: %q", err.Error()) + return + } + + req := crud.NewUpdateRequest(validSpace) + assertBodyEqual(t, refBuf.Bytes(), req) +} + +func TestCrudUpdateRequestSetters(t *testing.T) { + key := []interface{}{uint(24)} + operations := []interface{}{[]interface{}{"+", "id", uint(1020)}} + opts := map[string]interface{}{ + "fields": []interface{}{"id", "name"}, + } + var refBuf bytes.Buffer + + refEnc := NewEncoder(&refBuf) + expectedArgs := []interface{}{validSpace, key, operations, opts} + err := RefImplCallBody(refEnc, "crud.update", expectedArgs) + if err != nil { + t.Errorf("An unexpected RefImplCallBody() error: %q", err.Error()) + return + } + + req := crud.NewUpdateRequest(validSpace). + Key(key).Operations(operations).Opts(opts) + assertBodyEqual(t, refBuf.Bytes(), req) +} + +func TestCrudDeleteRequestDefaultValues(t *testing.T) { + var refBuf bytes.Buffer + + refEnc := NewEncoder(&refBuf) + expectedArgs := []interface{}{validSpace, []interface{}{}, + map[string]interface{}{}} + err := RefImplCallBody(refEnc, "crud.delete", expectedArgs) + if err != nil { + t.Errorf("An unexpected RefImplCallBody() error: %q", err.Error()) + return + } + + req := crud.NewDeleteRequest(validSpace) + assertBodyEqual(t, refBuf.Bytes(), req) +} + +func TestCrudDeleteRequestSetters(t *testing.T) { + key := []interface{}{uint(24)} + opts := map[string]interface{}{ + "fields": []interface{}{"id", "name"}, + } + var refBuf bytes.Buffer + + refEnc := NewEncoder(&refBuf) + expectedArgs := []interface{}{validSpace, key, opts} + err := RefImplCallBody(refEnc, "crud.delete", expectedArgs) + if err != nil { + t.Errorf("An unexpected RefImplCallBody() error: %q", err.Error()) + return + } + + req := crud.NewDeleteRequest(validSpace).Key(key).Opts(opts) + assertBodyEqual(t, refBuf.Bytes(), req) +} + +func TestCrudReplaceRequestDefaultValues(t *testing.T) { + var refBuf bytes.Buffer + + refEnc := NewEncoder(&refBuf) + expectedArgs := []interface{}{validSpace, []interface{}{}, + map[string]interface{}{}} + err := RefImplCallBody(refEnc, "crud.replace", expectedArgs) + if err != nil { + t.Errorf("An unexpected RefImplCallBody() error: %q", err.Error()) + return + } + + req := crud.NewReplaceRequest(validSpace) + assertBodyEqual(t, refBuf.Bytes(), req) +} + +func TestCrudReplaceRequestSetters(t *testing.T) { + tuple := []interface{}{uint(24)} + opts := map[string]interface{}{ + "fields": []interface{}{"id", "name"}, + } + var refBuf bytes.Buffer + + refEnc := NewEncoder(&refBuf) + expectedArgs := []interface{}{validSpace, tuple, opts} + err := RefImplCallBody(refEnc, "crud.replace", expectedArgs) + if err != nil { + t.Errorf("An unexpected RefImplCallBody() error: %q", err.Error()) + return + } + + req := crud.NewReplaceRequest(validSpace).Tuple(tuple).Opts(opts) + assertBodyEqual(t, refBuf.Bytes(), req) +} + +func TestCrudReplaceObjectRequestDefaultValues(t *testing.T) { + var refBuf bytes.Buffer + + refEnc := NewEncoder(&refBuf) + expectedArgs := []interface{}{validSpace, map[string]interface{}{}, + map[string]interface{}{}} + err := RefImplCallBody(refEnc, "crud.replace_object", expectedArgs) + if err != nil { + t.Errorf("An unexpected RefImplCallBody() error: %q", err.Error()) + return + } + + req := crud.NewReplaceObjectRequest(validSpace) + assertBodyEqual(t, refBuf.Bytes(), req) +} + +func TestCrudReplaceObjectRequestSetters(t *testing.T) { + object := map[string]interface{}{ + "id": uint(24), + } + opts := map[string]interface{}{ + "fields": []interface{}{"id", "name"}, + } + var refBuf bytes.Buffer + + refEnc := NewEncoder(&refBuf) + expectedArgs := []interface{}{validSpace, object, opts} + err := RefImplCallBody(refEnc, "crud.replace_object", expectedArgs) + if err != nil { + t.Errorf("An unexpected RefImplCallBody() error: %q", err.Error()) + return + } + + req := crud.NewReplaceObjectRequest(validSpace). + Object(object).Opts(opts) + assertBodyEqual(t, refBuf.Bytes(), req) +} + +func TestCrudReplaceManyRequestDefaultValues(t *testing.T) { + var refBuf bytes.Buffer + + refEnc := NewEncoder(&refBuf) + expectedArgs := []interface{}{validSpace, []interface{}{}, + map[string]interface{}{}} + err := RefImplCallBody(refEnc, "crud.replace_many", expectedArgs) + if err != nil { + t.Errorf("An unexpected RefImplCallBody() error: %q", err.Error()) + return + } + + req := crud.NewReplaceManyRequest(validSpace) + assertBodyEqual(t, refBuf.Bytes(), req) +} + +func TestCrudReplaceManyRequestSetters(t *testing.T) { + tuples := []interface{}{ + []interface{}{uint(24)}, + []interface{}{uint(25)}, + } + opts := map[string]interface{}{ + "fields": []interface{}{"id", "name"}, + } + var refBuf bytes.Buffer + + refEnc := NewEncoder(&refBuf) + expectedArgs := []interface{}{validSpace, tuples, opts} + err := RefImplCallBody(refEnc, "crud.replace_many", expectedArgs) + if err != nil { + t.Errorf("An unexpected RefImplCallBody() error: %q", err.Error()) + return + } + + req := crud.NewReplaceManyRequest(validSpace).Tuples(tuples).Opts(opts) + assertBodyEqual(t, refBuf.Bytes(), req) +} + +func TestCrudReplaceObjectManyRequestDefaultValues(t *testing.T) { + var refBuf bytes.Buffer + + refEnc := NewEncoder(&refBuf) + expectedArgs := []interface{}{validSpace, []map[string]interface{}{}, + map[string]interface{}{}} + err := RefImplCallBody(refEnc, "crud.replace_object_many", expectedArgs) + if err != nil { + t.Errorf("An unexpected RefImplCallBody() error: %q", err.Error()) + return + } + + req := crud.NewReplaceObjectManyRequest(validSpace) + assertBodyEqual(t, refBuf.Bytes(), req) +} + +func TestCrudReplaceObjectManyRequestSetters(t *testing.T) { + objects := []map[string]interface{}{ + map[string]interface{}{ + "id": uint(24), + }, + map[string]interface{}{ + "id": uint(25), + }, + } + opts := map[string]interface{}{ + "fields": []interface{}{"id", "name"}, + } + var refBuf bytes.Buffer + + refEnc := NewEncoder(&refBuf) + expectedArgs := []interface{}{validSpace, objects, opts} + err := RefImplCallBody(refEnc, "crud.replace_object_many", expectedArgs) + if err != nil { + t.Errorf("An unexpected RefImplCallBody() error: %q", err.Error()) + return + } + + req := crud.NewReplaceObjectManyRequest(validSpace). + Objects(objects).Opts(opts) + assertBodyEqual(t, refBuf.Bytes(), req) +} + +func TestCrudUpsertRequestDefaultValues(t *testing.T) { + var refBuf bytes.Buffer + + refEnc := NewEncoder(&refBuf) + expectedArgs := []interface{}{validSpace, []interface{}{}, + []interface{}{}, map[string]interface{}{}} + err := RefImplCallBody(refEnc, "crud.upsert", expectedArgs) + if err != nil { + t.Errorf("An unexpected RefImplCallBody() error: %q", err.Error()) + return + } + + req := crud.NewUpsertRequest(validSpace) + assertBodyEqual(t, refBuf.Bytes(), req) +} + +func TestCrudUpsertRequestSetters(t *testing.T) { + tuple := []interface{}{uint(24)} + operations := []interface{}{[]interface{}{"+", "id", uint(1020)}} + opts := map[string]interface{}{ + "fields": []interface{}{"id", "name"}, + } + var refBuf bytes.Buffer + + refEnc := NewEncoder(&refBuf) + expectedArgs := []interface{}{validSpace, tuple, operations, opts} + err := RefImplCallBody(refEnc, "crud.upsert", expectedArgs) + if err != nil { + t.Errorf("An unexpected RefImplCallBody() error: %q", err.Error()) + return + } + + req := crud.NewUpsertRequest(validSpace). + Tuple(tuple).Operations(operations).Opts(opts) + assertBodyEqual(t, refBuf.Bytes(), req) +} + +func TestCrudUpsertObjectRequestDefaultValues(t *testing.T) { + var refBuf bytes.Buffer + + refEnc := NewEncoder(&refBuf) + expectedArgs := []interface{}{validSpace, map[string]interface{}{}, + []interface{}{}, map[string]interface{}{}} + err := RefImplCallBody(refEnc, "crud.upsert_object", expectedArgs) + if err != nil { + t.Errorf("An unexpected RefImplCallBody() error: %q", err.Error()) + return + } + + req := crud.NewUpsertObjectRequest(validSpace) + assertBodyEqual(t, refBuf.Bytes(), req) +} + +func TestCrudUpsertObjectRequestSetters(t *testing.T) { + object := map[string]interface{}{ + "id": uint(24), + } + operations := []interface{}{[]interface{}{"+", "id", uint(1020)}} + opts := map[string]interface{}{ + "fields": []interface{}{"id", "name"}, + } + var refBuf bytes.Buffer + + refEnc := NewEncoder(&refBuf) + expectedArgs := []interface{}{validSpace, object, operations, opts} + err := RefImplCallBody(refEnc, "crud.upsert_object", expectedArgs) + if err != nil { + t.Errorf("An unexpected RefImplCallBody() error: %q", err.Error()) + return + } + + req := crud.NewUpsertObjectRequest(validSpace). + Object(object).Operations(operations).Opts(opts) + assertBodyEqual(t, refBuf.Bytes(), req) +} + +func TestCrudUpsertManyRequestDefaultValues(t *testing.T) { + var refBuf bytes.Buffer + + refEnc := NewEncoder(&refBuf) + expectedArgs := []interface{}{validSpace, []interface{}{}, + map[string]interface{}{}} + err := RefImplCallBody(refEnc, "crud.upsert_many", expectedArgs) + if err != nil { + t.Errorf("An unexpected RefImplCallBody() error: %q", err.Error()) + return + } + + req := crud.NewUpsertManyRequest(validSpace) + assertBodyEqual(t, refBuf.Bytes(), req) +} + +func TestCrudUpsertManyRequestSetters(t *testing.T) { + tuplesOperationsData := []interface{}{ + []interface{}{ + []interface{}{uint(24)}, + []interface{}{[]interface{}{"+", "id", uint(1020)}}, + }, + []interface{}{ + []interface{}{uint(25)}, + []interface{}{[]interface{}{"+", "id", uint(1020)}}, + }, + } + opts := map[string]interface{}{ + "fields": []interface{}{"id", "name"}, + } + var refBuf bytes.Buffer + + refEnc := NewEncoder(&refBuf) + expectedArgs := []interface{}{validSpace, tuplesOperationsData, opts} + err := RefImplCallBody(refEnc, "crud.upsert_many", expectedArgs) + if err != nil { + t.Errorf("An unexpected RefImplCallBody() error: %q", err.Error()) + return + } + + req := crud.NewUpsertManyRequest(validSpace). + TuplesOperationData(tuplesOperationsData). + Opts(opts) + assertBodyEqual(t, refBuf.Bytes(), req) +} + +func TestCrudUpsertObjectManyRequestDefaultValues(t *testing.T) { + var refBuf bytes.Buffer + + refEnc := NewEncoder(&refBuf) + expectedArgs := []interface{}{validSpace, []interface{}{}, + map[string]interface{}{}} + err := RefImplCallBody(refEnc, "crud.upsert_object_many", expectedArgs) + if err != nil { + t.Errorf("An unexpected RefImplCallBody() error: %q", err.Error()) + return + } + + req := crud.NewUpsertObjectManyRequest(validSpace) + assertBodyEqual(t, refBuf.Bytes(), req) +} + +func TestCrudUpsertObjectManyRequestSetters(t *testing.T) { + objectsOperationsData := []interface{}{ + []interface{}{ + map[string]interface{}{ + "id": uint(24), + }, + []interface{}{[]interface{}{"+", "id", uint(1020)}}, + }, + []interface{}{ + map[string]interface{}{ + "id": uint(25), + }, + []interface{}{[]interface{}{"+", "id", uint(1020)}}, + }, + } + opts := map[string]interface{}{ + "fields": []interface{}{"id", "name"}, + } + var refBuf bytes.Buffer + + refEnc := NewEncoder(&refBuf) + expectedArgs := []interface{}{validSpace, objectsOperationsData, opts} + err := RefImplCallBody(refEnc, "crud.upsert_object_many", expectedArgs) + if err != nil { + t.Errorf("An unexpected RefImplCallBody() error: %q", err.Error()) + return + } + + req := crud.NewUpsertObjectManyRequest(validSpace). + ObjectsOperationData(objectsOperationsData). + Opts(opts) + assertBodyEqual(t, refBuf.Bytes(), req) +} + +func TestCrudSelectRequestDefaultValues(t *testing.T) { + var refBuf bytes.Buffer + + refEnc := NewEncoder(&refBuf) + expectedArgs := []interface{}{validSpace, []interface{}{}, + map[string]interface{}{}} + err := RefImplCallBody(refEnc, "crud.select", expectedArgs) + if err != nil { + t.Errorf("An unexpected RefImplCallBody() error %q", err.Error()) + return + } + + req := crud.NewSelectRequest(validSpace) + assertBodyEqual(t, refBuf.Bytes(), req) +} + +func TestCrudSelectRequestSetter(t *testing.T) { + conditions := []interface{}{[]interface{}{"<", "id", uint(1020)}} + opts := map[string]interface{}{ + "fields": []interface{}{"id", "name"}, + } + var refBuf bytes.Buffer + + refEnc := NewEncoder(&refBuf) + expectedArgs := []interface{}{validSpace, conditions, opts} + err := RefImplCallBody(refEnc, "crud.select", expectedArgs) + if err != nil { + t.Errorf("An unexpected RefImplCallBody() error %q", err.Error()) + return + } + + req := crud.NewSelectRequest(validSpace). + Conditions(conditions). + Opts(opts) + assertBodyEqual(t, refBuf.Bytes(), req) +} + +func TestCrudMinRequestDefaultValues(t *testing.T) { + var refBuf bytes.Buffer + + refEnc := NewEncoder(&refBuf) + expectedArgs := []interface{}{validSpace, []interface{}{}, + map[string]interface{}{}} + err := RefImplCallBody(refEnc, "crud.min", expectedArgs) + if err != nil { + t.Errorf("An unexpected RefImplCallBody() error %q", err.Error()) + return + } + + req := crud.NewMinRequest(validSpace) + assertBodyEqual(t, refBuf.Bytes(), req) +} + +func TestCrudMinRequestSetter(t *testing.T) { + index := []interface{}{"name"} + opts := map[string]interface{}{ + "fields": []interface{}{"id", "name"}, + } + var refBuf bytes.Buffer + + refEnc := NewEncoder(&refBuf) + expectedArgs := []interface{}{validSpace, index, opts} + err := RefImplCallBody(refEnc, "crud.min", expectedArgs) + if err != nil { + t.Errorf("An unexpected RefImplCallBody() error %q", err.Error()) + return + } + + req := crud.NewMinRequest(validSpace).Index(index).Opts(opts) + assertBodyEqual(t, refBuf.Bytes(), req) +} + +func TestCrudMaxRequestDefaultValues(t *testing.T) { + var refBuf bytes.Buffer + + refEnc := NewEncoder(&refBuf) + expectedArgs := []interface{}{validSpace, []interface{}{}, + map[string]interface{}{}} + err := RefImplCallBody(refEnc, "crud.max", expectedArgs) + if err != nil { + t.Errorf("An unexpected RefImplCallBody() error %q", err.Error()) + return + } + + req := crud.NewMaxRequest(validSpace) + assertBodyEqual(t, refBuf.Bytes(), req) +} + +func TestCrudMaxRequestSetter(t *testing.T) { + index := []interface{}{"name"} + opts := map[string]interface{}{ + "fields": []interface{}{"id", "name"}, + } + var refBuf bytes.Buffer + + refEnc := NewEncoder(&refBuf) + expectedArgs := []interface{}{validSpace, index, opts} + err := RefImplCallBody(refEnc, "crud.max", expectedArgs) + if err != nil { + t.Errorf("An unexpected RefImplCallBody() error %q", err.Error()) + return + } + + req := crud.NewMaxRequest(validSpace).Index(index).Opts(opts) + assertBodyEqual(t, refBuf.Bytes(), req) +} + +func TestCrudTruncateRequestDefaultValues(t *testing.T) { + var refBuf bytes.Buffer + + refEnc := NewEncoder(&refBuf) + expectedArgs := []interface{}{validSpace, map[string]interface{}{}} + err := RefImplCallBody(refEnc, "crud.truncate", expectedArgs) + if err != nil { + t.Errorf("An unexpected RefImplCallBody() error %q", err.Error()) + return + } + + req := crud.NewTruncateRequest(validSpace) + assertBodyEqual(t, refBuf.Bytes(), req) +} + +func TestCrudTruncateRequestSetter(t *testing.T) { + opts := map[string]interface{}{ + "fields": []interface{}{"id", "name"}, + } + var refBuf bytes.Buffer + + refEnc := NewEncoder(&refBuf) + expectedArgs := []interface{}{validSpace, opts} + err := RefImplCallBody(refEnc, "crud.truncate", expectedArgs) + if err != nil { + t.Errorf("An unexpected RefImplCallBody() error %q", err.Error()) + return + } + + req := crud.NewTruncateRequest(validSpace).Opts(opts) + assertBodyEqual(t, refBuf.Bytes(), req) +} + +func TestCrudLenRequestDefaultValues(t *testing.T) { + var refBuf bytes.Buffer + + refEnc := NewEncoder(&refBuf) + expectedArgs := []interface{}{validSpace, map[string]interface{}{}} + err := RefImplCallBody(refEnc, "crud.len", expectedArgs) + if err != nil { + t.Errorf("An unexpected RefImplCallBody() error %q", err.Error()) + return + } + + req := crud.NewLenRequest(validSpace) + assertBodyEqual(t, refBuf.Bytes(), req) +} + +func TestCrudLenRequestSetter(t *testing.T) { + opts := map[string]interface{}{ + "fields": []interface{}{"id", "name"}, + } + var refBuf bytes.Buffer + + refEnc := NewEncoder(&refBuf) + expectedArgs := []interface{}{validSpace, opts} + err := RefImplCallBody(refEnc, "crud.len", expectedArgs) + if err != nil { + t.Errorf("An unexpected RefImplCallBody() error %q", err.Error()) + return + } + + req := crud.NewLenRequest(validSpace).Opts(opts) + assertBodyEqual(t, refBuf.Bytes(), req) +} + +func TestCrudCountRequestDefaultValues(t *testing.T) { + var refBuf bytes.Buffer + + refEnc := NewEncoder(&refBuf) + expectedArgs := []interface{}{validSpace, []interface{}{}, + map[string]interface{}{}} + err := RefImplCallBody(refEnc, "crud.count", expectedArgs) + if err != nil { + t.Errorf("An unexpected RefImplCallBody() error %q", err.Error()) + return + } + + req := crud.NewCountRequest(validSpace) + assertBodyEqual(t, refBuf.Bytes(), req) +} + +func TestCrudCountRequestSetter(t *testing.T) { + conditions := []interface{}{[]interface{}{"<", "id", uint(1020)}} + opts := map[string]interface{}{ + "fields": []interface{}{"id", "name"}, + } + var refBuf bytes.Buffer + + refEnc := NewEncoder(&refBuf) + expectedArgs := []interface{}{validSpace, conditions, opts} + err := RefImplCallBody(refEnc, "crud.count", expectedArgs) + if err != nil { + t.Errorf("An unexpected RefImplCallBody() error %q", err.Error()) + return + } + + req := crud.NewCountRequest(validSpace). + Conditions(conditions). + Opts(opts) + assertBodyEqual(t, refBuf.Bytes(), req) +} + +func TestCrudStorageInfoRequestDefaultValues(t *testing.T) { + var refBuf bytes.Buffer + + refEnc := NewEncoder(&refBuf) + expectedArgs := []interface{}{map[string]interface{}{}} + err := RefImplCallBody(refEnc, "crud.storage_info", expectedArgs) + if err != nil { + t.Errorf("An unexpected RefImplCallBody() error %q", err.Error()) + return + } + + req := crud.NewStorageInfoRequest() + assertBodyEqual(t, refBuf.Bytes(), req) +} + +func TestCrudStorageInfoRequestSetter(t *testing.T) { + opts := map[string]interface{}{ + "timeout": 1, + } + var refBuf bytes.Buffer + + refEnc := NewEncoder(&refBuf) + expectedArgs := []interface{}{opts} + err := RefImplCallBody(refEnc, "crud.storage_info", expectedArgs) + if err != nil { + t.Errorf("An unexpected RefImplCallBody() error %q", err.Error()) + return + } + + req := crud.NewStorageInfoRequest().Opts(opts) + assertBodyEqual(t, refBuf.Bytes(), req) +} + +func TestCrudUnflattenRowsRequestDefaultValues(t *testing.T) { + var refBuf bytes.Buffer + + refEnc := NewEncoder(&refBuf) + expectedArgs := []interface{}{[]interface{}{}, []interface{}{}} + err := RefImplCallBody(refEnc, "crud.unflatten_rows", expectedArgs) + if err != nil { + t.Errorf("An unexpected RefImplCallBody() error %q", err.Error()) + return + } + + req := crud.NewUnflattenRowsRequest() + assertBodyEqual(t, refBuf.Bytes(), req) +} + +func TestCrudUnflattenRowsRequestSetter(t *testing.T) { + tuples := []interface{}{ + []interface{}{uint(24)}, + []interface{}{uint(25)}, + } + + spaceFormat := []interface{}{ + map[string]string{ + "name": "id", + }, + map[string]string{ + "name": "name", + }, + } + + var refBuf bytes.Buffer + + refEnc := NewEncoder(&refBuf) + expectedArgs := []interface{}{tuples, spaceFormat} + err := RefImplCallBody(refEnc, "crud.unflatten_rows", expectedArgs) + if err != nil { + t.Errorf("An unexpected RefImplCallBody() error %q", err.Error()) + return + } + + req := crud.NewUnflattenRowsRequest(). + Tuples(tuples).SpaceFormat(spaceFormat) + assertBodyEqual(t, refBuf.Bytes(), req) +} + +func TestCrudStatsRequestDefaultValues(t *testing.T) { + var refBuf bytes.Buffer + + refEnc := NewEncoder(&refBuf) + expectedArgs := []interface{}{[]interface{}{}} + err := RefImplCallBody(refEnc, "crud.stats", expectedArgs) + if err != nil { + t.Errorf("An unexpected RefImplSelectBody() error %q", err.Error()) + return + } + + req := crud.NewStatsRequest() + assertBodyEqual(t, refBuf.Bytes(), req) +} + +func TestCrudStatsRequestSetter(t *testing.T) { + var refBuf bytes.Buffer + + refEnc := NewEncoder(&refBuf) + expectedArgs := []interface{}{validSpace} + err := RefImplCallBody(refEnc, "crud.stats", expectedArgs) + if err != nil { + t.Errorf("An unexpected RefImplSelectBody() error %q", err.Error()) + return + } + + req := crud.NewStatsRequest().Space(validSpace) + assertBodyEqual(t, refBuf.Bytes(), req) +} diff --git a/test_helpers/utils.go b/test_helpers/utils.go index 12c65009e..34a2e2980 100644 --- a/test_helpers/utils.go +++ b/test_helpers/utils.go @@ -1,7 +1,9 @@ package test_helpers import ( + "bytes" "fmt" + "io" "testing" "time" @@ -228,3 +230,16 @@ func CheckEqualBoxErrors(t *testing.T, expected tarantool.BoxError, actual taran } } } + +func ExtractRequestBody(req tarantool.Request, resolver tarantool.SchemaResolver, + newEncFunc func(w io.Writer) *encoder) ([]byte, error) { + var reqBuf bytes.Buffer + reqEnc := newEncFunc(&reqBuf) + + err := req.Body(resolver, reqEnc) + if err != nil { + return nil, fmt.Errorf("An unexpected Response.Body() error: %q", err.Error()) + } + + return reqBuf.Bytes(), nil +}