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,7 +30,9 @@ 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)} {
t.Run(fmt.Sprintf("%T", driver), func(t *testing.T) {
ch, err := driver.Find(context.TODO(), testNS, map[string]string{})
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -98,7 +100,7 @@ func TestBoltDBCount(t *testing.T) {
} { } {
f := filter f := filter
t.Run(name, func(t *testing.T) { t.Run(name, func(t *testing.T) {
n, err := bdb.count(context.TODO(), testNS, f.filter) n, err := driver.Count(context.TODO(), testNS, f.filter)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -111,13 +113,17 @@ func TestBoltDBCount(t *testing.T) {
} }
}) })
} }
})
}
} }
func TestBoltDBFind(t *testing.T) { 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)} {
t.Run(fmt.Sprintf("%T", driver), func(t *testing.T) {
ch, err := driver.Find(context.TODO(), testNS, map[string]string{})
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -167,13 +173,17 @@ func TestBoltDBFind(t *testing.T) {
if n != testN { if n != testN {
t.Fatal(n) t.Fatal(n)
} }
})
}
} }
func TestBoltDBUpdate(t *testing.T) { 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)} {
t.Run(fmt.Sprintf("%T", driver), func(t *testing.T) {
ch, err := driver.Find(context.TODO(), testNS, map[string]string{})
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -188,27 +198,27 @@ func TestBoltDBUpdate(t *testing.T) {
i++ 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)
} }
@ -244,13 +254,17 @@ func TestBoltDBUpdate(t *testing.T) {
if i != 1 { if i != 1 {
t.Fatal(i) t.Fatal(i)
} }
})
}
} }
func TestBoltDBInsert(t *testing.T) { 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)} {
t.Run(fmt.Sprintf("%T", driver), func(t *testing.T) {
ch, err := driver.Find(context.TODO(), testNS, map[string]string{})
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -265,22 +279,22 @@ func TestBoltDBInsert(t *testing.T) {
i++ i++
} }
if err := bdb.Insert(context.TODO(), testNS, ones[0]); err == nil { if err := driver.Insert(context.TODO(), testNS, ones[0]); err == nil {
t.Fatal("could insert colliding object:", err) t.Fatal("could insert colliding object:", err)
} }
ones[0].Name = "NEWNAME" ones[0].Name = "NEWNAME"
if err := bdb.Insert(context.TODO(), testNS, ones[0]); err != nil { if err := driver.Insert(context.TODO(), testNS, ones[0]); err != nil {
t.Fatal("could not insert object with new Name:", err) t.Fatal("could not insert object with new Name:", err)
} }
if n, err := bdb.count(context.TODO(), testNS, ones[0].Query()); err != nil { if n, err := driver.Count(context.TODO(), testNS, ones[0].Query()); err != nil {
t.Fatal(err) t.Fatal(err)
} else if n != 1 { } else if n != 1 {
t.Fatal(err) t.Fatal(err)
} }
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)
} }
@ -316,13 +330,17 @@ func TestBoltDBInsert(t *testing.T) {
t.Fatalf("objects should match after removing modify:\n%+v\n%+v", o, ones[0]) t.Fatalf("objects should match after removing modify:\n%+v\n%+v", o, ones[0])
} }
} }
})
}
} }
func TestBoltDBDelete(t *testing.T) { 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)} {
t.Run(fmt.Sprintf("%T", driver), func(t *testing.T) {
ch, err := driver.Find(context.TODO(), testNS, map[string]string{})
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -343,12 +361,12 @@ func TestBoltDBDelete(t *testing.T) {
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)
} }
@ -356,6 +374,8 @@ func TestBoltDBDelete(t *testing.T) {
t.Error(n, filter) t.Error(n, filter)
} }
} }
})
}
} }
func tempBoltDB(t *testing.T) (*BoltDB, func()) { func tempBoltDB(t *testing.T) (*BoltDB, func()) {

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())
} }
} }