make map driver type for faster tests

master
breel 2020-08-01 20:33:44 -06:00
parent b28bc74f8b
commit 37fe9415e7
13 changed files with 494 additions and 332 deletions

View File

@ -36,7 +36,7 @@ func New() Config {
as.Append(args.STRING, "fileprefix", "path prefix for file service", "/__files__") as.Append(args.STRING, "fileprefix", "path prefix for file service", "/__files__")
as.Append(args.STRING, "fileroot", "path to file hosting root", "/tmp/") as.Append(args.STRING, "fileroot", "path to file hosting root", "/tmp/")
as.Append(args.STRING, "database", "database name to use", "db") as.Append(args.STRING, "database", "database name to use", "db")
as.Append(args.STRING, "drivertype", "database driver to use", "boltdb") as.Append(args.STRING, "driver-type", "database driver to use, [boltdb mongo map]", "map")
as.Append(args.BOOL, "auth", "check for authorized access", false) as.Append(args.BOOL, "auth", "check for authorized access", false)
as.Append(args.DURATION, "authlifetime", "duration auth is valid for", time.Hour) as.Append(args.DURATION, "authlifetime", "duration auth is valid for", time.Hour)
as.Append(args.DURATION, "delay", "time to delay requests", time.Duration(0)) as.Append(args.DURATION, "delay", "time to delay requests", time.Duration(0))
@ -55,7 +55,7 @@ func New() Config {
FilePrefix: as.GetString("fileprefix"), FilePrefix: as.GetString("fileprefix"),
FileRoot: as.GetString("fileroot"), FileRoot: as.GetString("fileroot"),
Database: as.GetString("database"), Database: as.GetString("database"),
DriverType: as.GetString("drivertype"), DriverType: as.GetString("driver-type"),
Auth: as.GetBool("auth"), Auth: as.GetBool("auth"),
AuthLifetime: as.GetDuration("authlifetime"), AuthLifetime: as.GetDuration("authlifetime"),
Delay: as.GetDuration("delay"), Delay: as.GetDuration("delay"),

View File

@ -31,7 +31,7 @@ func NewBoltDB(path string) *BoltDB {
} }
} }
func (bdb *BoltDB) count(ctx context.Context, namespace string, filter interface{}) (int, error) { func (bdb *BoltDB) Count(ctx context.Context, namespace string, filter interface{}) (int, error) {
ch, err := bdb.Find(ctx, namespace, filter) ch, err := bdb.Find(ctx, namespace, filter)
n := 0 n := 0
for _ = range ch { for _ = range ch {

View File

@ -30,84 +30,88 @@ func TestBoltDBCount(t *testing.T) {
bdb, can := tempBoltDB(t) bdb, can := tempBoltDB(t)
defer can() defer can()
ch, err := bdb.Find(context.TODO(), testNS, map[string]string{}) for _, driver := range []Driver{bdb, tempMap(t)} {
if err != nil { t.Run(fmt.Sprintf("%T", driver), func(t *testing.T) {
t.Fatal(err) ch, err := driver.Find(context.TODO(), testNS, map[string]string{})
}
ones := make([]entity.One, testN)
i := 0
for j := range ch {
var o entity.One
if err := bson.Unmarshal(j, &o); err != nil {
t.Fatal(err)
}
ones[i] = o
i++
}
for name, filter := range map[string]struct {
filter interface{}
matchOne bool
matchMany bool
}{
"one.Query": {
filter: ones[0].Query(),
matchOne: true,
},
"title:title": {
filter: map[string]interface{}{entity.Title: ones[1].Title},
matchOne: true,
},
"title:title, text:text": {
filter: map[string]interface{}{entity.Title: ones[2].Title, entity.Text: ones[2].Text},
matchOne: true,
},
"title:title, text:gibberish": {
filter: map[string]interface{}{entity.Title: ones[3].Title, entity.Text: ones[2].Text},
},
"name:$in[gibberish]": {
filter: operator.NewFilterIn(entity.Name, []string{ones[0].Name + ones[1].Name}),
},
"name:$in[name]": {
filter: operator.NewFilterIn(entity.Name, []string{ones[0].Name}),
matchOne: true,
},
"name:$regex[gibberish]": {
filter: operator.Regex{Key: entity.Name, Value: ones[3].Name + ones[4].Name},
},
"name:$regex[name]": {
filter: operator.Regex{Key: entity.Name, Value: ones[3].Name},
matchOne: true,
},
"name:caseInsensitive[]": {
filter: operator.CaseInsensitive{Key: entity.Name, Value: ""},
matchMany: true,
},
"name:caseInsensitive[NAME]": {
filter: operator.CaseInsensitive{Key: entity.Name, Value: strings.ToUpper(ones[3].Name)},
matchOne: true,
},
"name:caseInsensitives[[]{}]": {
filter: operator.CaseInsensitives{Key: entity.Name, Values: []string{}},
matchMany: true,
},
"name:caseInsensitives[[]{NAME}]": {
filter: operator.CaseInsensitives{Key: entity.Name, Values: []string{strings.ToUpper(ones[3].Name)}},
matchOne: true,
},
} {
f := filter
t.Run(name, func(t *testing.T) {
n, err := bdb.count(context.TODO(), testNS, f.filter)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
if f.matchOne && n != 1 { ones := make([]entity.One, testN)
t.Fatalf("%v results for %+v, want matchOne=%v", n, f, f.matchOne) i := 0
} else if f.matchMany && n < 2 { for j := range ch {
t.Fatalf("%v results for %+v, want matchMany=%v", n, f, f.matchMany) var o entity.One
} else if !f.matchOne && !f.matchMany && n != 0 { if err := bson.Unmarshal(j, &o); err != nil {
t.Fatalf("%v results for %+v, want match=%v", n, f, f.matchOne || f.matchMany) t.Fatal(err)
}
ones[i] = o
i++
}
for name, filter := range map[string]struct {
filter interface{}
matchOne bool
matchMany bool
}{
"one.Query": {
filter: ones[0].Query(),
matchOne: true,
},
"title:title": {
filter: map[string]interface{}{entity.Title: ones[1].Title},
matchOne: true,
},
"title:title, text:text": {
filter: map[string]interface{}{entity.Title: ones[2].Title, entity.Text: ones[2].Text},
matchOne: true,
},
"title:title, text:gibberish": {
filter: map[string]interface{}{entity.Title: ones[3].Title, entity.Text: ones[2].Text},
},
"name:$in[gibberish]": {
filter: operator.NewFilterIn(entity.Name, []string{ones[0].Name + ones[1].Name}),
},
"name:$in[name]": {
filter: operator.NewFilterIn(entity.Name, []string{ones[0].Name}),
matchOne: true,
},
"name:$regex[gibberish]": {
filter: operator.Regex{Key: entity.Name, Value: ones[3].Name + ones[4].Name},
},
"name:$regex[name]": {
filter: operator.Regex{Key: entity.Name, Value: ones[3].Name},
matchOne: true,
},
"name:caseInsensitive[]": {
filter: operator.CaseInsensitive{Key: entity.Name, Value: ""},
matchMany: true,
},
"name:caseInsensitive[NAME]": {
filter: operator.CaseInsensitive{Key: entity.Name, Value: strings.ToUpper(ones[3].Name)},
matchOne: true,
},
"name:caseInsensitives[[]{}]": {
filter: operator.CaseInsensitives{Key: entity.Name, Values: []string{}},
matchMany: true,
},
"name:caseInsensitives[[]{NAME}]": {
filter: operator.CaseInsensitives{Key: entity.Name, Values: []string{strings.ToUpper(ones[3].Name)}},
matchOne: true,
},
} {
f := filter
t.Run(name, func(t *testing.T) {
n, err := driver.Count(context.TODO(), testNS, f.filter)
if err != nil {
t.Fatal(err)
}
if f.matchOne && n != 1 {
t.Fatalf("%v results for %+v, want matchOne=%v", n, f, f.matchOne)
} else if f.matchMany && n < 2 {
t.Fatalf("%v results for %+v, want matchMany=%v", n, f, f.matchMany)
} else if !f.matchOne && !f.matchMany && n != 0 {
t.Fatalf("%v results for %+v, want match=%v", n, f, f.matchOne || f.matchMany)
}
})
} }
}) })
} }
@ -117,55 +121,59 @@ func TestBoltDBFind(t *testing.T) {
bdb, can := tempBoltDB(t) bdb, can := tempBoltDB(t)
defer can() defer can()
ch, err := bdb.Find(context.TODO(), testNS, map[string]string{}) for _, driver := range []Driver{bdb, tempMap(t)} {
if err != nil { t.Run(fmt.Sprintf("%T", driver), func(t *testing.T) {
t.Fatal(err) ch, err := driver.Find(context.TODO(), testNS, map[string]string{})
} if err != nil {
n := 0 t.Fatal(err)
for b := range ch {
n++
o := entity.One{}
if err := bson.Unmarshal(b, &o); err != nil {
t.Fatal(err)
}
if o.Type == "" {
t.Error(o.Type)
}
if o.Title == "" {
t.Error(o.Title)
}
if o.Text == "" {
t.Error(o.Text)
}
if o.Relationship != "" {
t.Error(o.Relationship)
}
if o.Modified == 0 {
t.Error(o.Modified)
}
if len(o.Attachments) == 0 {
t.Error(o.Attachments)
}
if len(o.Connections) == 0 {
t.Error(o.Connections)
}
for k := range o.Connections {
if o.Connections[k].Name == "" {
t.Error(o.Connections[k])
} }
if o.Connections[k].Title == "" { n := 0
t.Error(o.Connections[k]) for b := range ch {
n++
o := entity.One{}
if err := bson.Unmarshal(b, &o); err != nil {
t.Fatal(err)
}
if o.Type == "" {
t.Error(o.Type)
}
if o.Title == "" {
t.Error(o.Title)
}
if o.Text == "" {
t.Error(o.Text)
}
if o.Relationship != "" {
t.Error(o.Relationship)
}
if o.Modified == 0 {
t.Error(o.Modified)
}
if len(o.Attachments) == 0 {
t.Error(o.Attachments)
}
if len(o.Connections) == 0 {
t.Error(o.Connections)
}
for k := range o.Connections {
if o.Connections[k].Name == "" {
t.Error(o.Connections[k])
}
if o.Connections[k].Title == "" {
t.Error(o.Connections[k])
}
if o.Connections[k].Relationship == "" {
t.Error(o.Connections[k])
}
if o.Connections[k].Type == "" {
t.Error(o.Connections[k])
}
}
} }
if o.Connections[k].Relationship == "" { if n != testN {
t.Error(o.Connections[k]) t.Fatal(n)
} }
if o.Connections[k].Type == "" { })
t.Error(o.Connections[k])
}
}
}
if n != testN {
t.Fatal(n)
} }
} }
@ -173,76 +181,80 @@ func TestBoltDBUpdate(t *testing.T) {
bdb, can := tempBoltDB(t) bdb, can := tempBoltDB(t)
defer can() defer can()
ch, err := bdb.Find(context.TODO(), testNS, map[string]string{}) for _, driver := range []Driver{bdb, tempMap(t)} {
if err != nil { t.Run(fmt.Sprintf("%T", driver), func(t *testing.T) {
t.Fatal(err) ch, err := driver.Find(context.TODO(), testNS, map[string]string{})
} if err != nil {
ones := make([]entity.One, testN) t.Fatal(err)
i := 0 }
for j := range ch { ones := make([]entity.One, testN)
var o entity.One i := 0
if err := bson.Unmarshal(j, &o); err != nil { for j := range ch {
t.Fatal(err) var o entity.One
} if err := bson.Unmarshal(j, &o); err != nil {
ones[i] = o t.Fatal(err)
i++ }
} ones[i] = o
i++
}
if err := bdb.Update(context.TODO(), testNS, ones[0].Query(), operator.Set{Key: entity.Title, Value: "NEWTITLE"}); err != nil { if err := driver.Update(context.TODO(), testNS, ones[0].Query(), operator.Set{Key: entity.Title, Value: "NEWTITLE"}); err != nil {
t.Fatal(err) t.Fatal(err)
} }
if err := bdb.Update(context.TODO(), testNS, ones[0].Query(), operator.Unset(entity.Type)); err != nil { if err := driver.Update(context.TODO(), testNS, ones[0].Query(), operator.Unset(entity.Type)); err != nil {
t.Fatal(err) t.Fatal(err)
} }
if n, err := bdb.count(context.TODO(), testNS, map[string]string{}); err != nil { if n, err := driver.Count(context.TODO(), testNS, map[string]string{}); err != nil {
t.Fatal(err) t.Fatal(err)
} else if n != testN { } else if n != testN {
t.Fatal(n) t.Fatal(n)
} }
if n, err := bdb.count(context.TODO(), testNS, map[string]string{entity.Title: "NEWTITLE"}); err != nil { if n, err := driver.Count(context.TODO(), testNS, map[string]string{entity.Title: "NEWTITLE"}); err != nil {
t.Fatal(err) t.Fatal(err)
} else if n != 1 { } else if n != 1 {
t.Fatal(n) t.Fatal(n)
} }
ch, err = bdb.Find(context.TODO(), testNS, ones[0].Query()) ch, err = driver.Find(context.TODO(), testNS, ones[0].Query())
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
i = 0 i = 0
for j := range ch { for j := range ch {
i++ i++
o := entity.One{} o := entity.One{}
if err := bson.Unmarshal(j, &o); err != nil { if err := bson.Unmarshal(j, &o); err != nil {
t.Fatal(err) t.Fatal(err)
} }
if o.Type != "" { if o.Type != "" {
t.Fatalf("doc still has Type even though $unset called: %+v", o) t.Fatalf("doc still has Type even though $unset called: %+v", o)
} }
if fmt.Sprint(ones[0]) == fmt.Sprint(o) { if fmt.Sprint(ones[0]) == fmt.Sprint(o) {
t.Fatal(ones[0], o) t.Fatal(ones[0], o)
} }
ones[0].Title = "" ones[0].Title = ""
o.Title = "" o.Title = ""
if fmt.Sprint(ones[0]) == fmt.Sprint(o) { if fmt.Sprint(ones[0]) == fmt.Sprint(o) {
t.Fatal(ones[0], o) t.Fatal(ones[0], o)
} }
ones[0].Type = "" ones[0].Type = ""
o.Type = "" o.Type = ""
if fmt.Sprint(ones[0]) == fmt.Sprint(o) { if fmt.Sprint(ones[0]) == fmt.Sprint(o) {
t.Fatal(ones[0], o) t.Fatal(ones[0], o)
} }
ones[0].Modified = 0 ones[0].Modified = 0
o.Modified = 0 o.Modified = 0
if fmt.Sprint(ones[0]) != fmt.Sprint(o) { if fmt.Sprint(ones[0]) != fmt.Sprint(o) {
t.Fatalf("after removing fields that should differ, still not the same:\n%+v\n%+v", ones[0], o) t.Fatalf("after removing fields that should differ, still not the same:\n%+v\n%+v", ones[0], o)
} }
} }
if i != 1 { if i != 1 {
t.Fatal(i) t.Fatal(i)
}
})
} }
} }
@ -250,71 +262,75 @@ func TestBoltDBInsert(t *testing.T) {
bdb, can := tempBoltDB(t) bdb, can := tempBoltDB(t)
defer can() defer can()
ch, err := bdb.Find(context.TODO(), testNS, map[string]string{}) for _, driver := range []Driver{bdb, tempMap(t)} {
if err != nil { t.Run(fmt.Sprintf("%T", driver), func(t *testing.T) {
t.Fatal(err) ch, err := driver.Find(context.TODO(), testNS, map[string]string{})
} if err != nil {
ones := make([]entity.One, testN) t.Fatal(err)
i := 0
for j := range ch {
var o entity.One
if err := bson.Unmarshal(j, &o); err != nil {
t.Fatal(err)
}
ones[i] = o
i++
}
if err := bdb.Insert(context.TODO(), testNS, ones[0]); err == nil {
t.Fatal("could insert colliding object:", err)
}
ones[0].Name = "NEWNAME"
if err := bdb.Insert(context.TODO(), testNS, ones[0]); err != nil {
t.Fatal("could not insert object with new Name:", err)
}
if n, err := bdb.count(context.TODO(), testNS, ones[0].Query()); err != nil {
t.Fatal(err)
} else if n != 1 {
t.Fatal(err)
}
ch, err = bdb.Find(context.TODO(), testNS, ones[0].Query())
if err != nil {
t.Fatal(err)
}
for i := range ch {
o := entity.One{}
if err := bson.Unmarshal(i, &o); err != nil {
t.Fatal(err)
}
if fmt.Sprint(o) == fmt.Sprint(ones[0]) {
t.Fatal(o, ones[0])
}
o.Modified = 0
for k := range ones[0].Connections {
if _, ok := o.Connections[k]; !ok {
t.Fatalf("db had fewer connections than real: %s", k)
} }
} ones := make([]entity.One, testN)
for k := range o.Connections { i := 0
if _, ok := ones[0].Connections[k]; !ok { for j := range ch {
t.Fatalf("db had more connections than real: %s", k) var o entity.One
if err := bson.Unmarshal(j, &o); err != nil {
t.Fatal(err)
}
ones[i] = o
i++
} }
c := o.Connections[k]
c.Modified = 0
o.Connections[k] = c
c = ones[0].Connections[k] if err := driver.Insert(context.TODO(), testNS, ones[0]); err == nil {
c.Modified = 0 t.Fatal("could insert colliding object:", err)
ones[0].Connections[k] = c }
}
o.Modified = 0 ones[0].Name = "NEWNAME"
ones[0].Modified = 0 if err := driver.Insert(context.TODO(), testNS, ones[0]); err != nil {
if fmt.Sprint(o) != fmt.Sprint(ones[0]) { t.Fatal("could not insert object with new Name:", err)
t.Fatalf("objects should match after removing modify:\n%+v\n%+v", o, ones[0]) }
}
if n, err := driver.Count(context.TODO(), testNS, ones[0].Query()); err != nil {
t.Fatal(err)
} else if n != 1 {
t.Fatal(err)
}
ch, err = driver.Find(context.TODO(), testNS, ones[0].Query())
if err != nil {
t.Fatal(err)
}
for i := range ch {
o := entity.One{}
if err := bson.Unmarshal(i, &o); err != nil {
t.Fatal(err)
}
if fmt.Sprint(o) == fmt.Sprint(ones[0]) {
t.Fatal(o, ones[0])
}
o.Modified = 0
for k := range ones[0].Connections {
if _, ok := o.Connections[k]; !ok {
t.Fatalf("db had fewer connections than real: %s", k)
}
}
for k := range o.Connections {
if _, ok := ones[0].Connections[k]; !ok {
t.Fatalf("db had more connections than real: %s", k)
}
c := o.Connections[k]
c.Modified = 0
o.Connections[k] = c
c = ones[0].Connections[k]
c.Modified = 0
ones[0].Connections[k] = c
}
o.Modified = 0
ones[0].Modified = 0
if fmt.Sprint(o) != fmt.Sprint(ones[0]) {
t.Fatalf("objects should match after removing modify:\n%+v\n%+v", o, ones[0])
}
}
})
} }
} }
@ -322,39 +338,43 @@ func TestBoltDBDelete(t *testing.T) {
bdb, can := tempBoltDB(t) bdb, can := tempBoltDB(t)
defer can() defer can()
ch, err := bdb.Find(context.TODO(), testNS, map[string]string{}) for _, driver := range []Driver{bdb, tempMap(t)} {
if err != nil { t.Run(fmt.Sprintf("%T", driver), func(t *testing.T) {
t.Fatal(err) ch, err := driver.Find(context.TODO(), testNS, map[string]string{})
} if err != nil {
ones := make([]entity.One, testN) t.Fatal(err)
i := 0 }
for j := range ch { ones := make([]entity.One, testN)
var o entity.One i := 0
if err := bson.Unmarshal(j, &o); err != nil { for j := range ch {
t.Fatal(err) var o entity.One
} if err := bson.Unmarshal(j, &o); err != nil {
ones[i] = o t.Fatal(err)
i++ }
} ones[i] = o
i++
}
wantN := testN wantN := testN
for _, filter := range []interface{}{ for _, filter := range []interface{}{
ones[0].Query(), ones[0].Query(),
operator.NewFilterIn(entity.Title, []string{ones[1].Title}), operator.NewFilterIn(entity.Title, []string{ones[1].Title}),
operator.Regex{Key: entity.Text, Value: ones[2].Text}, operator.Regex{Key: entity.Text, Value: ones[2].Text},
} { } {
err = bdb.Delete(context.TODO(), testNS, filter) err = driver.Delete(context.TODO(), testNS, filter)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
wantN-- wantN--
n, err := bdb.count(context.TODO(), testNS, map[string]string{}) n, err := driver.Count(context.TODO(), testNS, map[string]string{})
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
if n != wantN { if n != wantN {
t.Error(n, filter) t.Error(n, filter)
} }
}
})
} }
} }

View File

@ -9,6 +9,7 @@ import (
) )
type Driver interface { type Driver interface {
Count(context.Context, string, interface{}) (int, error)
Find(context.Context, string, interface{}) (chan bson.Raw, error) Find(context.Context, string, interface{}) (chan bson.Raw, error)
Update(context.Context, string, interface{}, interface{}) error Update(context.Context, string, interface{}, interface{}) error
Insert(context.Context, string, interface{}) error Insert(context.Context, string, interface{}) error
@ -20,6 +21,8 @@ func New(path ...string) Driver {
path = []string{config.New().DBURI} path = []string{config.New().DBURI}
} }
switch strings.ToLower(config.New().DriverType) { switch strings.ToLower(config.New().DriverType) {
case "map":
return NewMap()
case "mongo": case "mongo":
return NewMongo(path[0]) return NewMongo(path[0])
case "boltdb": case "boltdb":

145
storage/driver/map.go Normal file
View File

@ -0,0 +1,145 @@
package driver
import (
"context"
"errors"
"local/dndex/storage/entity"
"sync"
"go.mongodb.org/mongo-driver/bson"
)
type Map struct {
db map[string]map[string][]byte
lock sync.RWMutex
}
func NewMap() *Map {
return &Map{
db: make(map[string]map[string][]byte),
lock: sync.RWMutex{},
}
}
func (mp *Map) Count(ctx context.Context, namespace string, filter interface{}) (int, error) {
ch, err := mp.Find(ctx, namespace, filter)
if err != nil {
return 0, err
}
n := 0
for _ = range ch {
n++
}
return n, nil
}
func (mp *Map) Find(_ context.Context, namespace string, filter interface{}) (chan bson.Raw, error) {
mp.lock.RLock()
defer mp.lock.RUnlock()
b, err := bson.Marshal(filter)
if err != nil {
return nil, err
}
m := bson.M{}
if err := bson.Unmarshal(b, &m); err != nil {
return nil, err
}
results := make([]bson.Raw, 0)
for _, v := range mp.db[namespace] {
n := bson.M{}
if err := bson.Unmarshal(v, &n); err != nil {
return nil, err
}
if matches(n, m) {
results = append(results, bson.Raw(v))
}
}
ch := make(chan bson.Raw)
go func() {
defer close(ch)
for i := range results {
ch <- results[i]
}
}()
return ch, err
}
func (mp *Map) Update(_ context.Context, namespace string, filter, operator interface{}) error {
mp.lock.Lock()
defer mp.lock.Unlock()
b, err := bson.Marshal(filter)
if err != nil {
return err
}
m := bson.M{}
if err := bson.Unmarshal(b, &m); err != nil {
return err
}
for k, v := range mp.db[namespace] {
n := bson.M{}
if err := bson.Unmarshal(v, &n); err != nil {
return err
}
if matches(n, m) {
n, err := apply(n, operator)
if err != nil {
return err
}
v, err := bson.Marshal(n)
if err != nil {
return err
}
mp.db[namespace][k] = v
}
}
return nil
}
func (mp *Map) Insert(_ context.Context, namespace string, doc interface{}) error {
mp.lock.Lock()
defer mp.lock.Unlock()
b, err := bson.Marshal(doc)
if err != nil {
return err
}
m := bson.M{}
if err := bson.Unmarshal(b, &m); err != nil {
return err
}
if _, ok := m[entity.Name]; !ok {
return errors.New("primary key required to insert: did not find " + entity.Name)
} else if _, ok := m[entity.Name].(string); !ok {
return errors.New("primary key must be a string")
}
if _, ok := mp.db[namespace][m[entity.Name].(string)]; ok {
return errors.New("cannot insert: collision on primary key")
}
if _, ok := mp.db[namespace]; !ok {
mp.db[namespace] = make(map[string][]byte)
}
mp.db[namespace][m[entity.Name].(string)] = b
return nil
}
func (mp *Map) Delete(_ context.Context, namespace string, filter interface{}) error {
mp.lock.Lock()
defer mp.lock.Unlock()
b, err := bson.Marshal(filter)
if err != nil {
return err
}
m := bson.M{}
if err := bson.Unmarshal(b, &m); err != nil {
return err
}
for k, v := range mp.db[namespace] {
n := bson.M{}
if err := bson.Unmarshal(v, &n); err != nil {
return err
}
if matches(n, m) {
delete(mp.db[namespace], k)
}
}
return nil
}

View File

@ -0,0 +1,38 @@
package driver
import (
"local/dndex/storage/entity"
"testing"
"time"
"github.com/google/uuid"
"go.mongodb.org/mongo-driver/bson"
)
func tempMap(t *testing.T) *Map {
mp := NewMap()
mp.db[testNS] = map[string][]byte{}
for i := 0; i < testN; i++ {
p := entity.One{
Name: "name-" + uuid.New().String()[:5],
Type: "type-" + uuid.New().String()[:5],
Relationship: "rshp-" + uuid.New().String()[:5],
Title: "titl-" + uuid.New().String()[:5],
}
o := entity.One{
Name: "name-" + uuid.New().String()[:5],
Type: "type-" + uuid.New().String()[:5],
Title: "titl-" + uuid.New().String()[:5],
Text: "text-" + uuid.New().String()[:5],
Modified: time.Now().UnixNano(),
Connections: map[string]entity.One{p.Name: p},
Attachments: map[string]string{"filename": "/path/to/file"},
}
b, err := bson.Marshal(o)
if err != nil {
t.Fatal(err)
}
mp.db[testNS][o.Name] = b
}
return mp
}

View File

@ -37,6 +37,10 @@ func NewMongo(path string) Mongo {
} }
} }
func (m Mongo) Count(_ context.Context, _ string, _ interface{}) (int, error) {
return 0, errors.New("not impl")
}
func (m Mongo) Find(ctx context.Context, namespace string, filter interface{}) (chan bson.Raw, error) { func (m Mongo) Find(ctx context.Context, namespace string, filter interface{}) (chan bson.Raw, error) {
c := m.client.Database(m.db).Collection(namespace) c := m.client.Database(m.db).Collection(namespace)
cursor, err := c.Find(ctx, filter) cursor, err := c.Find(ctx, filter)

View File

@ -3,7 +3,6 @@ package storage
import ( import (
"context" "context"
"fmt" "fmt"
"io/ioutil"
"local/dndex/storage/entity" "local/dndex/storage/entity"
"local/dndex/storage/operator" "local/dndex/storage/operator"
"os" "os"
@ -16,13 +15,6 @@ import (
func TestIntegration(t *testing.T) { func TestIntegration(t *testing.T) {
os.Args = os.Args[:1] os.Args = os.Args[:1]
f, err := ioutil.TempFile(os.TempDir(), "pattern*")
if err != nil {
t.Fatal(err)
}
f.Close()
defer os.Remove(f.Name())
os.Setenv("DBURI", f.Name())
graph := NewRateLimitedGraph() graph := NewRateLimitedGraph()
ctx, can := context.WithCancel(context.TODO()) ctx, can := context.WithCancel(context.TODO())
defer can() defer can()

View File

@ -3,7 +3,6 @@ package view
import ( import (
"context" "context"
"fmt" "fmt"
"io/ioutil"
"local/dndex/storage" "local/dndex/storage"
"local/dndex/storage/entity" "local/dndex/storage/entity"
"net/http" "net/http"
@ -17,14 +16,6 @@ import (
func TestAuth(t *testing.T) { func TestAuth(t *testing.T) {
os.Args = os.Args[:1] os.Args = os.Args[:1]
f, err := ioutil.TempFile(os.TempDir(), "pattern*")
if err != nil {
t.Fatal(err)
}
f.Close()
defer os.Remove(f.Name())
os.Setenv("DBURI", f.Name())
os.Setenv("AUTH", "true") os.Setenv("AUTH", "true")
defer os.Setenv("AUTH", "false") defer os.Setenv("AUTH", "false")

View File

@ -17,13 +17,6 @@ import (
func TestFiles(t *testing.T) { func TestFiles(t *testing.T) {
os.Args = os.Args[:1] os.Args = os.Args[:1]
f, err := ioutil.TempFile(os.TempDir(), "pattern*")
if err != nil {
t.Fatal(err)
}
f.Close()
defer os.Remove(f.Name())
os.Setenv("DBURI", f.Name())
d, err := ioutil.TempDir(os.TempDir(), "pattern*") d, err := ioutil.TempDir(os.TempDir(), "pattern*")
if err != nil { if err != nil {

View File

@ -16,13 +16,6 @@ import (
func TestPort(t *testing.T) { func TestPort(t *testing.T) {
os.Args = os.Args[:1] os.Args = os.Args[:1]
f, err := ioutil.TempFile(os.TempDir(), "pattern*")
if err != nil {
t.Fatal(err)
}
f.Close()
defer os.Remove(f.Name())
os.Setenv("DBURI", f.Name())
os.Setenv("AUTH", "false") os.Setenv("AUTH", "false")
g := storage.NewRateLimitedGraph() g := storage.NewRateLimitedGraph()

View File

@ -2,7 +2,6 @@ package view
import ( import (
"fmt" "fmt"
"io/ioutil"
"local/dndex/storage" "local/dndex/storage"
"net/http" "net/http"
"net/http/httptest" "net/http/httptest"
@ -15,14 +14,6 @@ import (
func TestRegister(t *testing.T) { func TestRegister(t *testing.T) {
os.Args = os.Args[:1] os.Args = os.Args[:1]
f, err := ioutil.TempFile(os.TempDir(), "pattern*")
if err != nil {
t.Fatal(err)
}
f.Close()
defer os.Remove(f.Name())
os.Setenv("DBURI", f.Name())
os.Setenv("AUTH", "true") os.Setenv("AUTH", "true")
defer os.Setenv("AUTH", "false") defer os.Setenv("AUTH", "false")

View File

@ -5,7 +5,6 @@ import (
"context" "context"
"encoding/json" "encoding/json"
"fmt" "fmt"
"io/ioutil"
"local/dndex/config" "local/dndex/config"
"local/dndex/storage" "local/dndex/storage"
"local/dndex/storage/entity" "local/dndex/storage/entity"
@ -896,13 +895,7 @@ func TestSortOnes(t *testing.T) {
} }
func fresh(t *testing.T) (http.Handler, []entity.One, entity.One, storage.RateLimitedGraph, func()) { func fresh(t *testing.T) (http.Handler, []entity.One, entity.One, storage.RateLimitedGraph, func()) {
f, err := ioutil.TempFile(os.TempDir(), "pattern*") g := storage.NewRateLimitedGraph("")
if err != nil {
t.Fatal(err)
}
f.Close()
g := storage.NewRateLimitedGraph(f.Name())
if err := g.Delete(context.TODO(), "col", map[string]string{}); err != nil { if err := g.Delete(context.TODO(), "col", map[string]string{}); err != nil {
t.Fatal(err) t.Fatal(err)
@ -912,7 +905,6 @@ func fresh(t *testing.T) (http.Handler, []entity.One, entity.One, storage.RateLi
handler := jsonHandler(g) handler := jsonHandler(g)
return handler, ones, ones[0], g, func() { return handler, ones, ones[0], g, func() {
os.Remove(f.Name())
} }
} }