diff --git a/config/config.go b/config/config.go index 56375f1..b7450c2 100644 --- a/config/config.go +++ b/config/config.go @@ -36,7 +36,7 @@ func New() Config { 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, "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.DURATION, "authlifetime", "duration auth is valid for", time.Hour) as.Append(args.DURATION, "delay", "time to delay requests", time.Duration(0)) @@ -55,7 +55,7 @@ func New() Config { FilePrefix: as.GetString("fileprefix"), FileRoot: as.GetString("fileroot"), Database: as.GetString("database"), - DriverType: as.GetString("drivertype"), + DriverType: as.GetString("driver-type"), Auth: as.GetBool("auth"), AuthLifetime: as.GetDuration("authlifetime"), Delay: as.GetDuration("delay"), diff --git a/storage/driver/boltdb.go b/storage/driver/boltdb.go index c27e538..ff6331e 100644 --- a/storage/driver/boltdb.go +++ b/storage/driver/boltdb.go @@ -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) n := 0 for _ = range ch { diff --git a/storage/driver/boltdb_test.go b/storage/driver/boltdb_test.go index afc99ef..0b7eb9d 100644 --- a/storage/driver/boltdb_test.go +++ b/storage/driver/boltdb_test.go @@ -30,84 +30,88 @@ func TestBoltDBCount(t *testing.T) { bdb, can := tempBoltDB(t) defer can() - ch, err := bdb.Find(context.TODO(), testNS, map[string]string{}) - if err != nil { - t.Fatal(err) - } - 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) + 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 { 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) + 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 := 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) defer can() - ch, err := bdb.Find(context.TODO(), testNS, map[string]string{}) - if err != nil { - t.Fatal(err) - } - n := 0 - 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]) + 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 { + t.Fatal(err) } - if o.Connections[k].Title == "" { - t.Error(o.Connections[k]) + n := 0 + 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 == "" { - t.Error(o.Connections[k]) + if n != testN { + 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) defer can() - ch, err := bdb.Find(context.TODO(), testNS, map[string]string{}) - if err != nil { - t.Fatal(err) - } - 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 _, 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 { + t.Fatal(err) + } + 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++ + } - if err := bdb.Update(context.TODO(), testNS, ones[0].Query(), operator.Set{Key: entity.Title, Value: "NEWTITLE"}); err != nil { - t.Fatal(err) - } + if err := driver.Update(context.TODO(), testNS, ones[0].Query(), operator.Set{Key: entity.Title, Value: "NEWTITLE"}); err != nil { + t.Fatal(err) + } - if err := bdb.Update(context.TODO(), testNS, ones[0].Query(), operator.Unset(entity.Type)); err != nil { - t.Fatal(err) - } + if err := driver.Update(context.TODO(), testNS, ones[0].Query(), operator.Unset(entity.Type)); err != nil { + t.Fatal(err) + } - if n, err := bdb.count(context.TODO(), testNS, map[string]string{}); err != nil { - t.Fatal(err) - } else if n != testN { - t.Fatal(n) - } + if n, err := driver.Count(context.TODO(), testNS, map[string]string{}); err != nil { + t.Fatal(err) + } else if n != testN { + t.Fatal(n) + } - if n, err := bdb.count(context.TODO(), testNS, map[string]string{entity.Title: "NEWTITLE"}); err != nil { - t.Fatal(err) - } else if n != 1 { - t.Fatal(n) - } + if n, err := driver.Count(context.TODO(), testNS, map[string]string{entity.Title: "NEWTITLE"}); err != nil { + t.Fatal(err) + } else if n != 1 { + t.Fatal(n) + } - ch, err = bdb.Find(context.TODO(), testNS, ones[0].Query()) - if err != nil { - t.Fatal(err) - } - i = 0 - for j := range ch { - i++ - o := entity.One{} - if err := bson.Unmarshal(j, &o); err != nil { - t.Fatal(err) - } - if o.Type != "" { - t.Fatalf("doc still has Type even though $unset called: %+v", o) - } - if fmt.Sprint(ones[0]) == fmt.Sprint(o) { - t.Fatal(ones[0], o) - } - ones[0].Title = "" - o.Title = "" - if fmt.Sprint(ones[0]) == fmt.Sprint(o) { - t.Fatal(ones[0], o) - } - ones[0].Type = "" - o.Type = "" - if fmt.Sprint(ones[0]) == fmt.Sprint(o) { - t.Fatal(ones[0], o) - } - ones[0].Modified = 0 - o.Modified = 0 - 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) - } - } - if i != 1 { - t.Fatal(i) + ch, err = driver.Find(context.TODO(), testNS, ones[0].Query()) + if err != nil { + t.Fatal(err) + } + i = 0 + for j := range ch { + i++ + o := entity.One{} + if err := bson.Unmarshal(j, &o); err != nil { + t.Fatal(err) + } + if o.Type != "" { + t.Fatalf("doc still has Type even though $unset called: %+v", o) + } + if fmt.Sprint(ones[0]) == fmt.Sprint(o) { + t.Fatal(ones[0], o) + } + ones[0].Title = "" + o.Title = "" + if fmt.Sprint(ones[0]) == fmt.Sprint(o) { + t.Fatal(ones[0], o) + } + ones[0].Type = "" + o.Type = "" + if fmt.Sprint(ones[0]) == fmt.Sprint(o) { + t.Fatal(ones[0], o) + } + ones[0].Modified = 0 + o.Modified = 0 + 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) + } + } + if i != 1 { + t.Fatal(i) + } + }) } } @@ -250,71 +262,75 @@ func TestBoltDBInsert(t *testing.T) { bdb, can := tempBoltDB(t) defer can() - ch, err := bdb.Find(context.TODO(), testNS, map[string]string{}) - if err != nil { - t.Fatal(err) - } - 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++ - } - - 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) + 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 { + t.Fatal(err) } - } - for k := range o.Connections { - if _, ok := ones[0].Connections[k]; !ok { - t.Fatalf("db had more connections than real: %s", k) + 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++ } - 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]) - } + if err := driver.Insert(context.TODO(), testNS, ones[0]); err == nil { + t.Fatal("could insert colliding object:", err) + } + + ones[0].Name = "NEWNAME" + if err := driver.Insert(context.TODO(), testNS, ones[0]); err != nil { + t.Fatal("could not insert object with new Name:", err) + } + + 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) defer can() - ch, err := bdb.Find(context.TODO(), testNS, map[string]string{}) - if err != nil { - t.Fatal(err) - } - 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 _, 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 { + t.Fatal(err) + } + 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++ + } - wantN := testN - for _, filter := range []interface{}{ - ones[0].Query(), - operator.NewFilterIn(entity.Title, []string{ones[1].Title}), - operator.Regex{Key: entity.Text, Value: ones[2].Text}, - } { - err = bdb.Delete(context.TODO(), testNS, filter) - if err != nil { - t.Fatal(err) - } - wantN-- - n, err := bdb.count(context.TODO(), testNS, map[string]string{}) - if err != nil { - t.Fatal(err) - } - if n != wantN { - t.Error(n, filter) - } + wantN := testN + for _, filter := range []interface{}{ + ones[0].Query(), + operator.NewFilterIn(entity.Title, []string{ones[1].Title}), + operator.Regex{Key: entity.Text, Value: ones[2].Text}, + } { + err = driver.Delete(context.TODO(), testNS, filter) + if err != nil { + t.Fatal(err) + } + wantN-- + n, err := driver.Count(context.TODO(), testNS, map[string]string{}) + if err != nil { + t.Fatal(err) + } + if n != wantN { + t.Error(n, filter) + } + } + }) } } diff --git a/storage/driver/driver.go b/storage/driver/driver.go index 5ef12bf..71e0527 100644 --- a/storage/driver/driver.go +++ b/storage/driver/driver.go @@ -9,6 +9,7 @@ import ( ) type Driver interface { + Count(context.Context, string, interface{}) (int, error) Find(context.Context, string, interface{}) (chan bson.Raw, error) Update(context.Context, string, interface{}, interface{}) error Insert(context.Context, string, interface{}) error @@ -20,6 +21,8 @@ func New(path ...string) Driver { path = []string{config.New().DBURI} } switch strings.ToLower(config.New().DriverType) { + case "map": + return NewMap() case "mongo": return NewMongo(path[0]) case "boltdb": diff --git a/storage/driver/map.go b/storage/driver/map.go new file mode 100644 index 0000000..b2d545a --- /dev/null +++ b/storage/driver/map.go @@ -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 +} diff --git a/storage/driver/map_test.go b/storage/driver/map_test.go new file mode 100644 index 0000000..94c5a1c --- /dev/null +++ b/storage/driver/map_test.go @@ -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 +} diff --git a/storage/driver/mon.go b/storage/driver/mon.go index 95c4b81..4559494 100644 --- a/storage/driver/mon.go +++ b/storage/driver/mon.go @@ -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) { c := m.client.Database(m.db).Collection(namespace) cursor, err := c.Find(ctx, filter) diff --git a/storage/graph_test.go b/storage/graph_test.go index 12840fc..b469d44 100644 --- a/storage/graph_test.go +++ b/storage/graph_test.go @@ -3,7 +3,6 @@ package storage import ( "context" "fmt" - "io/ioutil" "local/dndex/storage/entity" "local/dndex/storage/operator" "os" @@ -16,13 +15,6 @@ import ( func TestIntegration(t *testing.T) { 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() ctx, can := context.WithCancel(context.TODO()) defer can() diff --git a/view/auth_test.go b/view/auth_test.go index 6443dd1..86bead7 100644 --- a/view/auth_test.go +++ b/view/auth_test.go @@ -3,7 +3,6 @@ package view import ( "context" "fmt" - "io/ioutil" "local/dndex/storage" "local/dndex/storage/entity" "net/http" @@ -17,14 +16,6 @@ import ( func TestAuth(t *testing.T) { 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") defer os.Setenv("AUTH", "false") diff --git a/view/files_test.go b/view/files_test.go index ef353b9..fd75dd4 100644 --- a/view/files_test.go +++ b/view/files_test.go @@ -17,13 +17,6 @@ import ( func TestFiles(t *testing.T) { 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*") if err != nil { diff --git a/view/port_test.go b/view/port_test.go index 7fa8980..b11978c 100644 --- a/view/port_test.go +++ b/view/port_test.go @@ -16,13 +16,6 @@ import ( func TestPort(t *testing.T) { 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") g := storage.NewRateLimitedGraph() diff --git a/view/register_test.go b/view/register_test.go index e64db0c..7800afa 100644 --- a/view/register_test.go +++ b/view/register_test.go @@ -2,7 +2,6 @@ package view import ( "fmt" - "io/ioutil" "local/dndex/storage" "net/http" "net/http/httptest" @@ -15,14 +14,6 @@ import ( func TestRegister(t *testing.T) { 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") defer os.Setenv("AUTH", "false") diff --git a/view/who_test.go b/view/who_test.go index 0907acd..4c03d92 100644 --- a/view/who_test.go +++ b/view/who_test.go @@ -5,7 +5,6 @@ import ( "context" "encoding/json" "fmt" - "io/ioutil" "local/dndex/config" "local/dndex/storage" "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()) { - f, err := ioutil.TempFile(os.TempDir(), "pattern*") - if err != nil { - t.Fatal(err) - } - f.Close() - - g := storage.NewRateLimitedGraph(f.Name()) + g := storage.NewRateLimitedGraph("") if err := g.Delete(context.TODO(), "col", map[string]string{}); err != nil { t.Fatal(err) @@ -912,7 +905,6 @@ func fresh(t *testing.T) (http.Handler, []entity.One, entity.One, storage.RateLi handler := jsonHandler(g) return handler, ones, ones[0], g, func() { - os.Remove(f.Name()) } }