influxdb/testing/kv.go

931 lines
18 KiB
Go
Raw Normal View History

feat(platform): add generic kv store Co-authored-by: Leonardo Di Donato <leodidonato@gmail.com> Co-authored-by: Michael Desa <mjdesa@gmail.com> feat(kv): add kv store interface for services feat(bolt): add boltdb implementation of kv.Store spike(platform): add kv backed user service feat(kv): add static cursor Note here that this operation cannot be transactionally done. This poses a bit of issues that will need to be worked out. fix(bolt): use error explicit error message squash: play with interface a bit fix(kv): remove commit and rollback from kv interface feat(inmem): add inmem kv store chore: add note for inmem transactions fix(bolt): remove call to tx in kv store tests feat(kv): add tests for static cursor doc(kv): add comments to store and associated interfaces doc(bolt): add comments to key value store feat(testing): add kv store tests test(testing): add conformance test for kv.Store test(inmem): add kv.Store conformance tests doc(inmem): add comments to key value store feat(inmem): remove CreateBucketIfNotExists from Tx interface feat(bolt): remove CreateBucketIfNotExists from Tx feat(inmem): remove CreateBucketIfNotExists from Tx doc(kv): add note to bucket interface about conditions methods can be called feat(kv): add context methods to kv.Tx feat(bolt): add context methods to bolt.Tx feat(inmem): add context methods to inmem.Tx test(kv): add contract tests for view/update transactions feat(kv): ensure that static cursor is always valid Co-authored-by: Leonardo Di Donato <leodidonato@gmail.com> Co-authored-by: Michael Desa <mjdesa@gmail.com> fix(kv): remove error from cursor methods test(kv): remove want errors from cursor test test(testing): add concurrent update test for kv.Store feat(kv): make kv user service an example service fix(testing): add concurrnent update test to the kv.Store contract tests test(platform): fix example kv service tests dep(platform): make platform tidy
2018-12-18 15:44:25 +00:00
package testing
import (
"bytes"
2019-03-05 00:38:10 +00:00
"context"
feat(platform): add generic kv store Co-authored-by: Leonardo Di Donato <leodidonato@gmail.com> Co-authored-by: Michael Desa <mjdesa@gmail.com> feat(kv): add kv store interface for services feat(bolt): add boltdb implementation of kv.Store spike(platform): add kv backed user service feat(kv): add static cursor Note here that this operation cannot be transactionally done. This poses a bit of issues that will need to be worked out. fix(bolt): use error explicit error message squash: play with interface a bit fix(kv): remove commit and rollback from kv interface feat(inmem): add inmem kv store chore: add note for inmem transactions fix(bolt): remove call to tx in kv store tests feat(kv): add tests for static cursor doc(kv): add comments to store and associated interfaces doc(bolt): add comments to key value store feat(testing): add kv store tests test(testing): add conformance test for kv.Store test(inmem): add kv.Store conformance tests doc(inmem): add comments to key value store feat(inmem): remove CreateBucketIfNotExists from Tx interface feat(bolt): remove CreateBucketIfNotExists from Tx feat(inmem): remove CreateBucketIfNotExists from Tx doc(kv): add note to bucket interface about conditions methods can be called feat(kv): add context methods to kv.Tx feat(bolt): add context methods to bolt.Tx feat(inmem): add context methods to inmem.Tx test(kv): add contract tests for view/update transactions feat(kv): ensure that static cursor is always valid Co-authored-by: Leonardo Di Donato <leodidonato@gmail.com> Co-authored-by: Michael Desa <mjdesa@gmail.com> fix(kv): remove error from cursor methods test(kv): remove want errors from cursor test test(testing): add concurrent update test for kv.Store feat(kv): make kv user service an example service fix(testing): add concurrnent update test to the kv.Store contract tests test(platform): fix example kv service tests dep(platform): make platform tidy
2018-12-18 15:44:25 +00:00
"fmt"
"testing"
"time"
"github.com/influxdata/influxdb/kv"
feat(platform): add generic kv store Co-authored-by: Leonardo Di Donato <leodidonato@gmail.com> Co-authored-by: Michael Desa <mjdesa@gmail.com> feat(kv): add kv store interface for services feat(bolt): add boltdb implementation of kv.Store spike(platform): add kv backed user service feat(kv): add static cursor Note here that this operation cannot be transactionally done. This poses a bit of issues that will need to be worked out. fix(bolt): use error explicit error message squash: play with interface a bit fix(kv): remove commit and rollback from kv interface feat(inmem): add inmem kv store chore: add note for inmem transactions fix(bolt): remove call to tx in kv store tests feat(kv): add tests for static cursor doc(kv): add comments to store and associated interfaces doc(bolt): add comments to key value store feat(testing): add kv store tests test(testing): add conformance test for kv.Store test(inmem): add kv.Store conformance tests doc(inmem): add comments to key value store feat(inmem): remove CreateBucketIfNotExists from Tx interface feat(bolt): remove CreateBucketIfNotExists from Tx feat(inmem): remove CreateBucketIfNotExists from Tx doc(kv): add note to bucket interface about conditions methods can be called feat(kv): add context methods to kv.Tx feat(bolt): add context methods to bolt.Tx feat(inmem): add context methods to inmem.Tx test(kv): add contract tests for view/update transactions feat(kv): ensure that static cursor is always valid Co-authored-by: Leonardo Di Donato <leodidonato@gmail.com> Co-authored-by: Michael Desa <mjdesa@gmail.com> fix(kv): remove error from cursor methods test(kv): remove want errors from cursor test test(testing): add concurrent update test for kv.Store feat(kv): make kv user service an example service fix(testing): add concurrnent update test to the kv.Store contract tests test(platform): fix example kv service tests dep(platform): make platform tidy
2018-12-18 15:44:25 +00:00
)
// KVStoreFields are background data that has to be set before
// the test runs.
type KVStoreFields struct {
Bucket []byte
Pairs []kv.Pair
}
// KVStore tests the key value store contract
func KVStore(
init func(KVStoreFields, *testing.T) (kv.Store, func()),
t *testing.T,
) {
tests := []struct {
name string
fn func(
init func(KVStoreFields, *testing.T) (kv.Store, func()),
t *testing.T,
)
}{
{
name: "Get",
fn: KVGet,
},
{
name: "Put",
fn: KVPut,
},
{
name: "Delete",
fn: KVDelete,
},
{
name: "Cursor",
fn: KVCursor,
},
{
name: "View",
fn: KVView,
},
{
name: "Update",
fn: KVUpdate,
},
{
name: "ConcurrentUpdate",
fn: KVConcurrentUpdate,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
tt.fn(init, t)
})
}
}
// KVGet tests the get method contract for the key value store.
func KVGet(
init func(KVStoreFields, *testing.T) (kv.Store, func()),
t *testing.T,
) {
type args struct {
bucket []byte
key []byte
}
type wants struct {
err error
val []byte
}
tests := []struct {
name string
fields KVStoreFields
args args
wants wants
}{
{
name: "get key",
fields: KVStoreFields{
Bucket: []byte("bucket"),
Pairs: []kv.Pair{
{
Key: []byte("hello"),
Value: []byte("world"),
},
},
},
args: args{
bucket: []byte("bucket"),
key: []byte("hello"),
},
wants: wants{
val: []byte("world"),
},
},
{
name: "get missing key",
fields: KVStoreFields{
Bucket: []byte("bucket"),
Pairs: []kv.Pair{},
},
args: args{
bucket: []byte("bucket"),
key: []byte("hello"),
},
wants: wants{
err: kv.ErrKeyNotFound,
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
s, close := init(tt.fields, t)
defer close()
2019-03-05 00:38:10 +00:00
err := s.View(context.Background(), func(tx kv.Tx) error {
feat(platform): add generic kv store Co-authored-by: Leonardo Di Donato <leodidonato@gmail.com> Co-authored-by: Michael Desa <mjdesa@gmail.com> feat(kv): add kv store interface for services feat(bolt): add boltdb implementation of kv.Store spike(platform): add kv backed user service feat(kv): add static cursor Note here that this operation cannot be transactionally done. This poses a bit of issues that will need to be worked out. fix(bolt): use error explicit error message squash: play with interface a bit fix(kv): remove commit and rollback from kv interface feat(inmem): add inmem kv store chore: add note for inmem transactions fix(bolt): remove call to tx in kv store tests feat(kv): add tests for static cursor doc(kv): add comments to store and associated interfaces doc(bolt): add comments to key value store feat(testing): add kv store tests test(testing): add conformance test for kv.Store test(inmem): add kv.Store conformance tests doc(inmem): add comments to key value store feat(inmem): remove CreateBucketIfNotExists from Tx interface feat(bolt): remove CreateBucketIfNotExists from Tx feat(inmem): remove CreateBucketIfNotExists from Tx doc(kv): add note to bucket interface about conditions methods can be called feat(kv): add context methods to kv.Tx feat(bolt): add context methods to bolt.Tx feat(inmem): add context methods to inmem.Tx test(kv): add contract tests for view/update transactions feat(kv): ensure that static cursor is always valid Co-authored-by: Leonardo Di Donato <leodidonato@gmail.com> Co-authored-by: Michael Desa <mjdesa@gmail.com> fix(kv): remove error from cursor methods test(kv): remove want errors from cursor test test(testing): add concurrent update test for kv.Store feat(kv): make kv user service an example service fix(testing): add concurrnent update test to the kv.Store contract tests test(platform): fix example kv service tests dep(platform): make platform tidy
2018-12-18 15:44:25 +00:00
b, err := tx.Bucket(tt.args.bucket)
if err != nil {
t.Errorf("unexpected error retrieving bucket: %v", err)
return err
}
val, err := b.Get(tt.args.key)
if (err != nil) != (tt.wants.err != nil) {
t.Errorf("expected error '%v' got '%v'", tt.wants.err, err)
return err
}
if err != nil && tt.wants.err != nil {
if err.Error() != tt.wants.err.Error() {
t.Errorf("expected error messages to match '%v' got '%v'", tt.wants.err, err.Error())
return err
}
}
if want, got := tt.wants.val, val; !bytes.Equal(want, got) {
t.Errorf("exptected to get value %s got %s", string(want), string(got))
return err
}
return nil
})
if err != nil {
t.Fatalf("error during view transaction: %v", err)
}
})
}
}
// KVPut tests the get method contract for the key value store.
func KVPut(
init func(KVStoreFields, *testing.T) (kv.Store, func()),
t *testing.T,
) {
type args struct {
bucket []byte
key []byte
val []byte
}
type wants struct {
err error
}
tests := []struct {
name string
fields KVStoreFields
args args
wants wants
}{
{
name: "put pair",
fields: KVStoreFields{
Bucket: []byte("bucket"),
Pairs: []kv.Pair{},
},
args: args{
bucket: []byte("bucket"),
key: []byte("hello"),
val: []byte("world"),
},
wants: wants{},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
s, close := init(tt.fields, t)
defer close()
2019-03-05 00:38:10 +00:00
err := s.Update(context.Background(), func(tx kv.Tx) error {
feat(platform): add generic kv store Co-authored-by: Leonardo Di Donato <leodidonato@gmail.com> Co-authored-by: Michael Desa <mjdesa@gmail.com> feat(kv): add kv store interface for services feat(bolt): add boltdb implementation of kv.Store spike(platform): add kv backed user service feat(kv): add static cursor Note here that this operation cannot be transactionally done. This poses a bit of issues that will need to be worked out. fix(bolt): use error explicit error message squash: play with interface a bit fix(kv): remove commit and rollback from kv interface feat(inmem): add inmem kv store chore: add note for inmem transactions fix(bolt): remove call to tx in kv store tests feat(kv): add tests for static cursor doc(kv): add comments to store and associated interfaces doc(bolt): add comments to key value store feat(testing): add kv store tests test(testing): add conformance test for kv.Store test(inmem): add kv.Store conformance tests doc(inmem): add comments to key value store feat(inmem): remove CreateBucketIfNotExists from Tx interface feat(bolt): remove CreateBucketIfNotExists from Tx feat(inmem): remove CreateBucketIfNotExists from Tx doc(kv): add note to bucket interface about conditions methods can be called feat(kv): add context methods to kv.Tx feat(bolt): add context methods to bolt.Tx feat(inmem): add context methods to inmem.Tx test(kv): add contract tests for view/update transactions feat(kv): ensure that static cursor is always valid Co-authored-by: Leonardo Di Donato <leodidonato@gmail.com> Co-authored-by: Michael Desa <mjdesa@gmail.com> fix(kv): remove error from cursor methods test(kv): remove want errors from cursor test test(testing): add concurrent update test for kv.Store feat(kv): make kv user service an example service fix(testing): add concurrnent update test to the kv.Store contract tests test(platform): fix example kv service tests dep(platform): make platform tidy
2018-12-18 15:44:25 +00:00
b, err := tx.Bucket(tt.args.bucket)
if err != nil {
t.Errorf("unexpected error retrieving bucket: %v", err)
return err
}
{
err := b.Put(tt.args.key, tt.args.val)
if (err != nil) != (tt.wants.err != nil) {
t.Errorf("expected error '%v' got '%v'", tt.wants.err, err)
return err
}
if err != nil && tt.wants.err != nil {
if err.Error() != tt.wants.err.Error() {
t.Errorf("expected error messages to match '%v' got '%v'", tt.wants.err, err.Error())
return err
}
}
val, err := b.Get(tt.args.key)
if err != nil {
t.Errorf("unexpected error retrieving value: %v", err)
return err
}
if want, got := tt.args.val, val; !bytes.Equal(want, got) {
t.Errorf("exptected to get value %s got %s", string(want), string(got))
return err
}
}
return nil
})
if err != nil {
t.Fatalf("error during view transaction: %v", err)
}
})
}
}
// KVDelete tests the delete method contract for the key value store.
func KVDelete(
init func(KVStoreFields, *testing.T) (kv.Store, func()),
t *testing.T,
) {
type args struct {
bucket []byte
key []byte
}
type wants struct {
err error
}
tests := []struct {
name string
fields KVStoreFields
args args
wants wants
}{
{
name: "delete key",
fields: KVStoreFields{
Bucket: []byte("bucket"),
Pairs: []kv.Pair{
{
Key: []byte("hello"),
Value: []byte("world"),
},
},
},
args: args{
bucket: []byte("bucket"),
key: []byte("hello"),
},
wants: wants{},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
s, close := init(tt.fields, t)
defer close()
2019-03-05 00:38:10 +00:00
err := s.Update(context.Background(), func(tx kv.Tx) error {
feat(platform): add generic kv store Co-authored-by: Leonardo Di Donato <leodidonato@gmail.com> Co-authored-by: Michael Desa <mjdesa@gmail.com> feat(kv): add kv store interface for services feat(bolt): add boltdb implementation of kv.Store spike(platform): add kv backed user service feat(kv): add static cursor Note here that this operation cannot be transactionally done. This poses a bit of issues that will need to be worked out. fix(bolt): use error explicit error message squash: play with interface a bit fix(kv): remove commit and rollback from kv interface feat(inmem): add inmem kv store chore: add note for inmem transactions fix(bolt): remove call to tx in kv store tests feat(kv): add tests for static cursor doc(kv): add comments to store and associated interfaces doc(bolt): add comments to key value store feat(testing): add kv store tests test(testing): add conformance test for kv.Store test(inmem): add kv.Store conformance tests doc(inmem): add comments to key value store feat(inmem): remove CreateBucketIfNotExists from Tx interface feat(bolt): remove CreateBucketIfNotExists from Tx feat(inmem): remove CreateBucketIfNotExists from Tx doc(kv): add note to bucket interface about conditions methods can be called feat(kv): add context methods to kv.Tx feat(bolt): add context methods to bolt.Tx feat(inmem): add context methods to inmem.Tx test(kv): add contract tests for view/update transactions feat(kv): ensure that static cursor is always valid Co-authored-by: Leonardo Di Donato <leodidonato@gmail.com> Co-authored-by: Michael Desa <mjdesa@gmail.com> fix(kv): remove error from cursor methods test(kv): remove want errors from cursor test test(testing): add concurrent update test for kv.Store feat(kv): make kv user service an example service fix(testing): add concurrnent update test to the kv.Store contract tests test(platform): fix example kv service tests dep(platform): make platform tidy
2018-12-18 15:44:25 +00:00
b, err := tx.Bucket(tt.args.bucket)
if err != nil {
t.Errorf("unexpected error retrieving bucket: %v", err)
return err
}
{
err := b.Delete(tt.args.key)
if (err != nil) != (tt.wants.err != nil) {
t.Errorf("expected error '%v' got '%v'", tt.wants.err, err)
return err
}
if err != nil && tt.wants.err != nil {
if err.Error() != tt.wants.err.Error() {
t.Errorf("expected error messages to match '%v' got '%v'", tt.wants.err, err.Error())
return err
}
}
if _, err := b.Get(tt.args.key); err != kv.ErrKeyNotFound {
t.Errorf("expected key not found error got %v", err)
return err
}
}
return nil
})
if err != nil {
t.Fatalf("error during view transaction: %v", err)
}
})
}
}
// KVCursor tests the cursor contract for the key value store.
func KVCursor(
init func(KVStoreFields, *testing.T) (kv.Store, func()),
t *testing.T,
) {
type args struct {
bucket []byte
seek []byte
}
type wants struct {
err error
first kv.Pair
last kv.Pair
seek kv.Pair
next kv.Pair
prev kv.Pair
}
tests := []struct {
name string
fields KVStoreFields
args args
wants wants
}{
{
name: "basic cursor",
fields: KVStoreFields{
Bucket: []byte("bucket"),
Pairs: []kv.Pair{
{
Key: []byte("a"),
Value: []byte("1"),
},
{
Key: []byte("ab"),
Value: []byte("2"),
},
{
Key: []byte("abc"),
Value: []byte("3"),
},
{
Key: []byte("abcd"),
Value: []byte("4"),
},
{
Key: []byte("abcde"),
Value: []byte("5"),
},
{
Key: []byte("bcd"),
Value: []byte("6"),
},
{
Key: []byte("cd"),
Value: []byte("7"),
},
},
},
args: args{
bucket: []byte("bucket"),
seek: []byte("abc"),
},
wants: wants{
first: kv.Pair{
Key: []byte("a"),
Value: []byte("1"),
},
last: kv.Pair{
Key: []byte("cd"),
Value: []byte("7"),
},
seek: kv.Pair{
Key: []byte("abc"),
Value: []byte("3"),
},
next: kv.Pair{
Key: []byte("abcd"),
Value: []byte("4"),
},
prev: kv.Pair{
Key: []byte("abc"),
Value: []byte("3"),
},
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
s, close := init(tt.fields, t)
defer close()
2019-03-05 00:38:10 +00:00
err := s.View(context.Background(), func(tx kv.Tx) error {
feat(platform): add generic kv store Co-authored-by: Leonardo Di Donato <leodidonato@gmail.com> Co-authored-by: Michael Desa <mjdesa@gmail.com> feat(kv): add kv store interface for services feat(bolt): add boltdb implementation of kv.Store spike(platform): add kv backed user service feat(kv): add static cursor Note here that this operation cannot be transactionally done. This poses a bit of issues that will need to be worked out. fix(bolt): use error explicit error message squash: play with interface a bit fix(kv): remove commit and rollback from kv interface feat(inmem): add inmem kv store chore: add note for inmem transactions fix(bolt): remove call to tx in kv store tests feat(kv): add tests for static cursor doc(kv): add comments to store and associated interfaces doc(bolt): add comments to key value store feat(testing): add kv store tests test(testing): add conformance test for kv.Store test(inmem): add kv.Store conformance tests doc(inmem): add comments to key value store feat(inmem): remove CreateBucketIfNotExists from Tx interface feat(bolt): remove CreateBucketIfNotExists from Tx feat(inmem): remove CreateBucketIfNotExists from Tx doc(kv): add note to bucket interface about conditions methods can be called feat(kv): add context methods to kv.Tx feat(bolt): add context methods to bolt.Tx feat(inmem): add context methods to inmem.Tx test(kv): add contract tests for view/update transactions feat(kv): ensure that static cursor is always valid Co-authored-by: Leonardo Di Donato <leodidonato@gmail.com> Co-authored-by: Michael Desa <mjdesa@gmail.com> fix(kv): remove error from cursor methods test(kv): remove want errors from cursor test test(testing): add concurrent update test for kv.Store feat(kv): make kv user service an example service fix(testing): add concurrnent update test to the kv.Store contract tests test(platform): fix example kv service tests dep(platform): make platform tidy
2018-12-18 15:44:25 +00:00
b, err := tx.Bucket(tt.args.bucket)
if err != nil {
t.Errorf("unexpected error retrieving bucket: %v", err)
return err
}
cur, err := b.Cursor()
if (err != nil) != (tt.wants.err != nil) {
t.Errorf("expected error '%v' got '%v'", tt.wants.err, err)
return err
}
if err != nil && tt.wants.err != nil {
if err.Error() != tt.wants.err.Error() {
t.Errorf("expected error messages to match '%v' got '%v'", tt.wants.err, err.Error())
return err
}
}
{
key, val := cur.First()
if want, got := tt.wants.first.Key, key; !bytes.Equal(want, got) {
t.Errorf("exptected to get key %s got %s", string(want), string(got))
return err
}
if want, got := tt.wants.first.Value, val; !bytes.Equal(want, got) {
t.Errorf("exptected to get value %s got %s", string(want), string(got))
return err
}
}
{
key, val := cur.Last()
if want, got := tt.wants.last.Key, key; !bytes.Equal(want, got) {
t.Errorf("exptected to get key %s got %s", string(want), string(got))
return err
}
if want, got := tt.wants.last.Value, val; !bytes.Equal(want, got) {
t.Errorf("exptected to get value %s got %s", string(want), string(got))
return err
}
}
{
key, val := cur.Seek(tt.args.seek)
if want, got := tt.wants.seek.Key, key; !bytes.Equal(want, got) {
t.Errorf("exptected to get key %s got %s", string(want), string(got))
return err
}
if want, got := tt.wants.seek.Value, val; !bytes.Equal(want, got) {
t.Errorf("exptected to get value %s got %s", string(want), string(got))
return err
}
}
{
key, val := cur.Next()
if want, got := tt.wants.next.Key, key; !bytes.Equal(want, got) {
t.Errorf("exptected to get key %s got %s", string(want), string(got))
return err
}
if want, got := tt.wants.next.Value, val; !bytes.Equal(want, got) {
t.Errorf("exptected to get value %s got %s", string(want), string(got))
return err
}
}
{
key, val := cur.Prev()
if want, got := tt.wants.prev.Key, key; !bytes.Equal(want, got) {
t.Errorf("exptected to get key %s got %s", string(want), string(got))
return err
}
if want, got := tt.wants.prev.Value, val; !bytes.Equal(want, got) {
t.Errorf("exptected to get value %s got %s", string(want), string(got))
return err
}
}
return nil
})
if err != nil {
t.Fatalf("error during view transaction: %v", err)
}
})
}
}
// KVView tests the view method contract for the key value store.
func KVView(
init func(KVStoreFields, *testing.T) (kv.Store, func()),
t *testing.T,
) {
type args struct {
bucket []byte
key []byte
// If len(value) == 0 the test will not attempt a put
value []byte
// If true, the test will attempt to delete the provided key
delete bool
}
type wants struct {
value []byte
}
tests := []struct {
name string
fields KVStoreFields
args args
wants wants
}{
{
name: "basic view",
fields: KVStoreFields{
Bucket: []byte("bucket"),
Pairs: []kv.Pair{
{
Key: []byte("hello"),
Value: []byte("cruel world"),
},
},
},
args: args{
bucket: []byte("bucket"),
key: []byte("hello"),
},
wants: wants{
value: []byte("cruel world"),
},
},
{
name: "basic view with delete",
fields: KVStoreFields{
Bucket: []byte("bucket"),
Pairs: []kv.Pair{
{
Key: []byte("hello"),
Value: []byte("cruel world"),
},
},
},
args: args{
bucket: []byte("bucket"),
key: []byte("hello"),
delete: true,
},
wants: wants{
value: []byte("cruel world"),
},
},
{
name: "basic view with put",
fields: KVStoreFields{
Bucket: []byte("bucket"),
Pairs: []kv.Pair{
{
Key: []byte("hello"),
Value: []byte("cruel world"),
},
},
},
args: args{
bucket: []byte("bucket"),
key: []byte("hello"),
value: []byte("world"),
delete: true,
},
wants: wants{
value: []byte("cruel world"),
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
s, close := init(tt.fields, t)
defer close()
2019-03-05 00:38:10 +00:00
err := s.View(context.Background(), func(tx kv.Tx) error {
feat(platform): add generic kv store Co-authored-by: Leonardo Di Donato <leodidonato@gmail.com> Co-authored-by: Michael Desa <mjdesa@gmail.com> feat(kv): add kv store interface for services feat(bolt): add boltdb implementation of kv.Store spike(platform): add kv backed user service feat(kv): add static cursor Note here that this operation cannot be transactionally done. This poses a bit of issues that will need to be worked out. fix(bolt): use error explicit error message squash: play with interface a bit fix(kv): remove commit and rollback from kv interface feat(inmem): add inmem kv store chore: add note for inmem transactions fix(bolt): remove call to tx in kv store tests feat(kv): add tests for static cursor doc(kv): add comments to store and associated interfaces doc(bolt): add comments to key value store feat(testing): add kv store tests test(testing): add conformance test for kv.Store test(inmem): add kv.Store conformance tests doc(inmem): add comments to key value store feat(inmem): remove CreateBucketIfNotExists from Tx interface feat(bolt): remove CreateBucketIfNotExists from Tx feat(inmem): remove CreateBucketIfNotExists from Tx doc(kv): add note to bucket interface about conditions methods can be called feat(kv): add context methods to kv.Tx feat(bolt): add context methods to bolt.Tx feat(inmem): add context methods to inmem.Tx test(kv): add contract tests for view/update transactions feat(kv): ensure that static cursor is always valid Co-authored-by: Leonardo Di Donato <leodidonato@gmail.com> Co-authored-by: Michael Desa <mjdesa@gmail.com> fix(kv): remove error from cursor methods test(kv): remove want errors from cursor test test(testing): add concurrent update test for kv.Store feat(kv): make kv user service an example service fix(testing): add concurrnent update test to the kv.Store contract tests test(platform): fix example kv service tests dep(platform): make platform tidy
2018-12-18 15:44:25 +00:00
b, err := tx.Bucket(tt.args.bucket)
if err != nil {
t.Errorf("unexpected error retrieving bucket: %v", err)
return err
}
if len(tt.args.value) != 0 {
err := b.Put(tt.args.key, tt.args.value)
if err == nil {
return fmt.Errorf("expected transaction to fail")
}
if err != kv.ErrTxNotWritable {
return err
}
return nil
}
value, err := b.Get(tt.args.key)
if err != nil {
return err
}
if want, got := tt.wants.value, value; !bytes.Equal(want, got) {
t.Errorf("exptected to get value %s got %s", string(want), string(got))
return err
}
if tt.args.delete {
err := b.Delete(tt.args.key)
if err == nil {
return fmt.Errorf("expected transaction to fail")
}
if err != kv.ErrTxNotWritable {
return err
}
return nil
}
return nil
})
if err != nil {
t.Fatalf("error during view transaction: %v", err)
}
})
}
}
// KVUpdate tests the update method contract for the key value store.
func KVUpdate(
init func(KVStoreFields, *testing.T) (kv.Store, func()),
t *testing.T,
) {
type args struct {
bucket []byte
key []byte
value []byte
delete bool
}
type wants struct {
value []byte
}
tests := []struct {
name string
fields KVStoreFields
args args
wants wants
}{
{
name: "basic update",
fields: KVStoreFields{
Bucket: []byte("bucket"),
Pairs: []kv.Pair{
{
Key: []byte("hello"),
Value: []byte("cruel world"),
},
},
},
args: args{
bucket: []byte("bucket"),
key: []byte("hello"),
value: []byte("world"),
},
wants: wants{
value: []byte("world"),
},
},
{
name: "basic update with delete",
fields: KVStoreFields{
Bucket: []byte("bucket"),
Pairs: []kv.Pair{
{
Key: []byte("hello"),
Value: []byte("cruel world"),
},
},
},
args: args{
bucket: []byte("bucket"),
key: []byte("hello"),
value: []byte("world"),
delete: true,
},
wants: wants{},
},
// TODO: add case with failed update transaction that doesn't apply all of the changes.
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
s, close := init(tt.fields, t)
defer close()
{
2019-03-05 00:38:10 +00:00
err := s.Update(context.Background(), func(tx kv.Tx) error {
feat(platform): add generic kv store Co-authored-by: Leonardo Di Donato <leodidonato@gmail.com> Co-authored-by: Michael Desa <mjdesa@gmail.com> feat(kv): add kv store interface for services feat(bolt): add boltdb implementation of kv.Store spike(platform): add kv backed user service feat(kv): add static cursor Note here that this operation cannot be transactionally done. This poses a bit of issues that will need to be worked out. fix(bolt): use error explicit error message squash: play with interface a bit fix(kv): remove commit and rollback from kv interface feat(inmem): add inmem kv store chore: add note for inmem transactions fix(bolt): remove call to tx in kv store tests feat(kv): add tests for static cursor doc(kv): add comments to store and associated interfaces doc(bolt): add comments to key value store feat(testing): add kv store tests test(testing): add conformance test for kv.Store test(inmem): add kv.Store conformance tests doc(inmem): add comments to key value store feat(inmem): remove CreateBucketIfNotExists from Tx interface feat(bolt): remove CreateBucketIfNotExists from Tx feat(inmem): remove CreateBucketIfNotExists from Tx doc(kv): add note to bucket interface about conditions methods can be called feat(kv): add context methods to kv.Tx feat(bolt): add context methods to bolt.Tx feat(inmem): add context methods to inmem.Tx test(kv): add contract tests for view/update transactions feat(kv): ensure that static cursor is always valid Co-authored-by: Leonardo Di Donato <leodidonato@gmail.com> Co-authored-by: Michael Desa <mjdesa@gmail.com> fix(kv): remove error from cursor methods test(kv): remove want errors from cursor test test(testing): add concurrent update test for kv.Store feat(kv): make kv user service an example service fix(testing): add concurrnent update test to the kv.Store contract tests test(platform): fix example kv service tests dep(platform): make platform tidy
2018-12-18 15:44:25 +00:00
b, err := tx.Bucket(tt.args.bucket)
if err != nil {
t.Errorf("unexpected error retrieving bucket: %v", err)
return err
}
if len(tt.args.value) != 0 {
err := b.Put(tt.args.key, tt.args.value)
if err != nil {
return err
}
}
if tt.args.delete {
err := b.Delete(tt.args.key)
if err != nil {
return err
}
}
value, err := b.Get(tt.args.key)
if tt.args.delete {
if err != kv.ErrKeyNotFound {
return fmt.Errorf("expected key not found")
}
return nil
} else if err != nil {
return err
}
if want, got := tt.wants.value, value; !bytes.Equal(want, got) {
t.Errorf("exptected to get value %s got %s", string(want), string(got))
return err
}
return nil
})
if err != nil {
t.Fatalf("error during update transaction: %v", err)
}
}
{
2019-03-05 00:38:10 +00:00
err := s.View(context.Background(), func(tx kv.Tx) error {
feat(platform): add generic kv store Co-authored-by: Leonardo Di Donato <leodidonato@gmail.com> Co-authored-by: Michael Desa <mjdesa@gmail.com> feat(kv): add kv store interface for services feat(bolt): add boltdb implementation of kv.Store spike(platform): add kv backed user service feat(kv): add static cursor Note here that this operation cannot be transactionally done. This poses a bit of issues that will need to be worked out. fix(bolt): use error explicit error message squash: play with interface a bit fix(kv): remove commit and rollback from kv interface feat(inmem): add inmem kv store chore: add note for inmem transactions fix(bolt): remove call to tx in kv store tests feat(kv): add tests for static cursor doc(kv): add comments to store and associated interfaces doc(bolt): add comments to key value store feat(testing): add kv store tests test(testing): add conformance test for kv.Store test(inmem): add kv.Store conformance tests doc(inmem): add comments to key value store feat(inmem): remove CreateBucketIfNotExists from Tx interface feat(bolt): remove CreateBucketIfNotExists from Tx feat(inmem): remove CreateBucketIfNotExists from Tx doc(kv): add note to bucket interface about conditions methods can be called feat(kv): add context methods to kv.Tx feat(bolt): add context methods to bolt.Tx feat(inmem): add context methods to inmem.Tx test(kv): add contract tests for view/update transactions feat(kv): ensure that static cursor is always valid Co-authored-by: Leonardo Di Donato <leodidonato@gmail.com> Co-authored-by: Michael Desa <mjdesa@gmail.com> fix(kv): remove error from cursor methods test(kv): remove want errors from cursor test test(testing): add concurrent update test for kv.Store feat(kv): make kv user service an example service fix(testing): add concurrnent update test to the kv.Store contract tests test(platform): fix example kv service tests dep(platform): make platform tidy
2018-12-18 15:44:25 +00:00
b, err := tx.Bucket(tt.args.bucket)
if err != nil {
t.Errorf("unexpected error retrieving bucket: %v", err)
return err
}
value, err := b.Get(tt.args.key)
if tt.args.delete {
if err != kv.ErrKeyNotFound {
return fmt.Errorf("expected key not found")
}
} else if err != nil {
return err
}
if want, got := tt.wants.value, value; !bytes.Equal(want, got) {
t.Errorf("exptected to get value %s got %s", string(want), string(got))
return err
}
return nil
})
if err != nil {
t.Fatalf("error during view transaction: %v", err)
}
}
})
}
}
// KVConcurrentUpdate tests concurrent calls to update.
func KVConcurrentUpdate(
init func(KVStoreFields, *testing.T) (kv.Store, func()),
t *testing.T,
) {
type args struct {
bucket []byte
key []byte
valueA []byte
valueB []byte
}
type wants struct {
value []byte
}
tests := []struct {
name string
fields KVStoreFields
args args
wants wants
}{
{
name: "basic concurrent update",
fields: KVStoreFields{
Bucket: []byte("bucket"),
Pairs: []kv.Pair{
{
Key: []byte("hello"),
Value: []byte("cruel world"),
},
},
},
args: args{
bucket: []byte("bucket"),
key: []byte("hello"),
valueA: []byte("world"),
valueB: []byte("darkness my new friend"),
},
wants: wants{
value: []byte("darkness my new friend"),
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
2019-01-09 17:49:02 +00:00
t.Skip("https://github.com/influxdata/platform/issues/2371")
feat(platform): add generic kv store Co-authored-by: Leonardo Di Donato <leodidonato@gmail.com> Co-authored-by: Michael Desa <mjdesa@gmail.com> feat(kv): add kv store interface for services feat(bolt): add boltdb implementation of kv.Store spike(platform): add kv backed user service feat(kv): add static cursor Note here that this operation cannot be transactionally done. This poses a bit of issues that will need to be worked out. fix(bolt): use error explicit error message squash: play with interface a bit fix(kv): remove commit and rollback from kv interface feat(inmem): add inmem kv store chore: add note for inmem transactions fix(bolt): remove call to tx in kv store tests feat(kv): add tests for static cursor doc(kv): add comments to store and associated interfaces doc(bolt): add comments to key value store feat(testing): add kv store tests test(testing): add conformance test for kv.Store test(inmem): add kv.Store conformance tests doc(inmem): add comments to key value store feat(inmem): remove CreateBucketIfNotExists from Tx interface feat(bolt): remove CreateBucketIfNotExists from Tx feat(inmem): remove CreateBucketIfNotExists from Tx doc(kv): add note to bucket interface about conditions methods can be called feat(kv): add context methods to kv.Tx feat(bolt): add context methods to bolt.Tx feat(inmem): add context methods to inmem.Tx test(kv): add contract tests for view/update transactions feat(kv): ensure that static cursor is always valid Co-authored-by: Leonardo Di Donato <leodidonato@gmail.com> Co-authored-by: Michael Desa <mjdesa@gmail.com> fix(kv): remove error from cursor methods test(kv): remove want errors from cursor test test(testing): add concurrent update test for kv.Store feat(kv): make kv user service an example service fix(testing): add concurrnent update test to the kv.Store contract tests test(platform): fix example kv service tests dep(platform): make platform tidy
2018-12-18 15:44:25 +00:00
s, closeFn := init(tt.fields, t)
defer closeFn()
errCh := make(chan error)
var fn = func(v []byte) {
2019-03-05 00:38:10 +00:00
err := s.Update(context.Background(), func(tx kv.Tx) error {
feat(platform): add generic kv store Co-authored-by: Leonardo Di Donato <leodidonato@gmail.com> Co-authored-by: Michael Desa <mjdesa@gmail.com> feat(kv): add kv store interface for services feat(bolt): add boltdb implementation of kv.Store spike(platform): add kv backed user service feat(kv): add static cursor Note here that this operation cannot be transactionally done. This poses a bit of issues that will need to be worked out. fix(bolt): use error explicit error message squash: play with interface a bit fix(kv): remove commit and rollback from kv interface feat(inmem): add inmem kv store chore: add note for inmem transactions fix(bolt): remove call to tx in kv store tests feat(kv): add tests for static cursor doc(kv): add comments to store and associated interfaces doc(bolt): add comments to key value store feat(testing): add kv store tests test(testing): add conformance test for kv.Store test(inmem): add kv.Store conformance tests doc(inmem): add comments to key value store feat(inmem): remove CreateBucketIfNotExists from Tx interface feat(bolt): remove CreateBucketIfNotExists from Tx feat(inmem): remove CreateBucketIfNotExists from Tx doc(kv): add note to bucket interface about conditions methods can be called feat(kv): add context methods to kv.Tx feat(bolt): add context methods to bolt.Tx feat(inmem): add context methods to inmem.Tx test(kv): add contract tests for view/update transactions feat(kv): ensure that static cursor is always valid Co-authored-by: Leonardo Di Donato <leodidonato@gmail.com> Co-authored-by: Michael Desa <mjdesa@gmail.com> fix(kv): remove error from cursor methods test(kv): remove want errors from cursor test test(testing): add concurrent update test for kv.Store feat(kv): make kv user service an example service fix(testing): add concurrnent update test to the kv.Store contract tests test(platform): fix example kv service tests dep(platform): make platform tidy
2018-12-18 15:44:25 +00:00
b, err := tx.Bucket(tt.args.bucket)
if err != nil {
return err
}
if err := b.Put(tt.args.key, v); err != nil {
return err
}
return nil
})
if err != nil {
errCh <- fmt.Errorf("error during update transaction: %v", err)
} else {
errCh <- nil
}
}
go fn(tt.args.valueA)
// To ensure that a is scheduled before b
time.Sleep(time.Millisecond)
go fn(tt.args.valueB)
count := 0
for err := range errCh {
count++
if err != nil {
t.Fatal(err)
}
if count == 2 {
break
}
}
close(errCh)
{
2019-03-05 00:38:10 +00:00
err := s.View(context.Background(), func(tx kv.Tx) error {
feat(platform): add generic kv store Co-authored-by: Leonardo Di Donato <leodidonato@gmail.com> Co-authored-by: Michael Desa <mjdesa@gmail.com> feat(kv): add kv store interface for services feat(bolt): add boltdb implementation of kv.Store spike(platform): add kv backed user service feat(kv): add static cursor Note here that this operation cannot be transactionally done. This poses a bit of issues that will need to be worked out. fix(bolt): use error explicit error message squash: play with interface a bit fix(kv): remove commit and rollback from kv interface feat(inmem): add inmem kv store chore: add note for inmem transactions fix(bolt): remove call to tx in kv store tests feat(kv): add tests for static cursor doc(kv): add comments to store and associated interfaces doc(bolt): add comments to key value store feat(testing): add kv store tests test(testing): add conformance test for kv.Store test(inmem): add kv.Store conformance tests doc(inmem): add comments to key value store feat(inmem): remove CreateBucketIfNotExists from Tx interface feat(bolt): remove CreateBucketIfNotExists from Tx feat(inmem): remove CreateBucketIfNotExists from Tx doc(kv): add note to bucket interface about conditions methods can be called feat(kv): add context methods to kv.Tx feat(bolt): add context methods to bolt.Tx feat(inmem): add context methods to inmem.Tx test(kv): add contract tests for view/update transactions feat(kv): ensure that static cursor is always valid Co-authored-by: Leonardo Di Donato <leodidonato@gmail.com> Co-authored-by: Michael Desa <mjdesa@gmail.com> fix(kv): remove error from cursor methods test(kv): remove want errors from cursor test test(testing): add concurrent update test for kv.Store feat(kv): make kv user service an example service fix(testing): add concurrnent update test to the kv.Store contract tests test(platform): fix example kv service tests dep(platform): make platform tidy
2018-12-18 15:44:25 +00:00
b, err := tx.Bucket(tt.args.bucket)
if err != nil {
t.Errorf("unexpected error retrieving bucket: %v", err)
return err
}
deadline := time.Now().Add(1 * time.Second)
var returnErr error
for {
if time.Now().After(deadline) {
break
}
value, err := b.Get(tt.args.key)
if err != nil {
return err
}
if want, got := tt.wants.value, value; !bytes.Equal(want, got) {
returnErr = fmt.Errorf("exptected to get value %s got %s", string(want), string(got))
} else {
returnErr = nil
break
}
}
if returnErr != nil {
return returnErr
}
return nil
})
if err != nil {
t.Fatalf("error during view transaction: %v", err)
}
}
})
}
}