new tree crud test
parent
ba154be6c2
commit
eedb6cc6a5
|
|
@ -12,10 +12,8 @@ import (
|
|||
"net/http"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
|
@ -83,7 +81,8 @@ func (server *Server) apiV0TreeHandler(w http.ResponseWriter, r *http.Request) e
|
|||
|
||||
func (server *Server) apiV0TreePrettyHandler(w http.ResponseWriter, r *http.Request) error {
|
||||
tree := server.tree()
|
||||
branches, err := tree.GetPretty()
|
||||
//branches, err := tree.GetPretty()
|
||||
branches, err := tree.GetRoot()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
@ -92,7 +91,7 @@ func (server *Server) apiV0TreePrettyHandler(w http.ResponseWriter, r *http.Requ
|
|||
|
||||
func (server *Server) apiV0TreePlainHandler(w http.ResponseWriter, r *http.Request) error {
|
||||
tree := server.tree()
|
||||
branches, err := tree.Get()
|
||||
branches, err := tree.GetRoot()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
@ -137,12 +136,7 @@ func (server *Server) apiV0MediaIDDelHandler(w http.ResponseWriter, r *http.Requ
|
|||
|
||||
func (server *Server) apiV0MediaIDGetHandler(w http.ResponseWriter, r *http.Request) error {
|
||||
id := path.Base(r.URL.Path)
|
||||
tree := server.tree()
|
||||
fullId, err := tree.FullId(id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return server.getContentHandler(server.diskMediaPath(fullId), w, r)
|
||||
return server.getContentHandler(server.diskMediaPath(id), w, r)
|
||||
}
|
||||
|
||||
func (server *Server) getContentHandler(filePath string, w http.ResponseWriter, r *http.Request) error {
|
||||
|
|
@ -217,15 +211,7 @@ func (server *Server) rootHandler(w http.ResponseWriter, r *http.Request) error
|
|||
}
|
||||
|
||||
func (server *Server) tree() *Tree {
|
||||
return NewTree(path.Dir(server.diskFileDir("id")))
|
||||
}
|
||||
|
||||
func (server *Server) diskFileDir(id string) string {
|
||||
return path.Dir(server.diskFilePath(id))
|
||||
}
|
||||
|
||||
func (server *Server) diskFilePath(id string) string {
|
||||
return path.Join(server.root, "files", id, "data")
|
||||
return nil
|
||||
}
|
||||
|
||||
func (server *Server) diskMediaPath(id string) string {
|
||||
|
|
@ -233,109 +219,11 @@ func (server *Server) diskMediaPath(id string) string {
|
|||
}
|
||||
|
||||
func (server *Server) apiV0FilesHandler(w http.ResponseWriter, r *http.Request) error {
|
||||
fileDir := server.diskFileDir("id")
|
||||
tree := server.tree()
|
||||
if pid := r.Header.Get("PID"); pid == "" {
|
||||
} else if fullId, err := tree.FullId(pid); err != nil {
|
||||
return err
|
||||
} else {
|
||||
fileDir = server.diskFileDir(fullId)
|
||||
}
|
||||
id, err := server.postContentHandler(fileDir, w, r)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := tree.Put(id, Branch{
|
||||
Title: r.Header.Get("Title"),
|
||||
PID: r.Header.Get("PID"),
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
return json.NewEncoder(w).Encode(map[string]map[string]string{
|
||||
"data": map[string]string{
|
||||
"filePath": path.Join("/api/v0/files", id),
|
||||
},
|
||||
})
|
||||
return errors.New("not impl")
|
||||
}
|
||||
|
||||
func (server *Server) apiV0FilesIDHandler(w http.ResponseWriter, r *http.Request) error {
|
||||
switch r.Method {
|
||||
case http.MethodPut:
|
||||
return server.apiV0FilesIDPutHandler(w, r)
|
||||
case http.MethodGet:
|
||||
return server.apiV0FilesIDGetHandler(w, r)
|
||||
case http.MethodDelete:
|
||||
return server.apiV0FilesIDDelHandler(w, r)
|
||||
}
|
||||
http.NotFound(w, r)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (server *Server) apiV0FilesIDPutHandler(w http.ResponseWriter, r *http.Request) error {
|
||||
id := path.Base(r.URL.Path)
|
||||
tree := server.tree()
|
||||
branches, err := tree.Get()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
branch, _ := branches[id]
|
||||
branch.Updated = time.Now().UTC()
|
||||
if title := r.Header.Get("Title"); title != "" {
|
||||
branch.Title = title
|
||||
}
|
||||
if pid := r.Header.Get("PID"); pid != "" {
|
||||
branch.PID = pid
|
||||
}
|
||||
branch.Deleted = false
|
||||
fullPid, err := tree.FullId(branch.PID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fullId := path.Join(fullPid, id)
|
||||
if err := server.putContentHandler(server.diskFilePath(fullId), w, r); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := tree.Put(id, branch); err != nil {
|
||||
return err
|
||||
}
|
||||
return json.NewEncoder(w).Encode(map[string]map[string]string{
|
||||
"data": map[string]string{
|
||||
"filePath": path.Join("/api/v0/files", id),
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func (server *Server) apiV0FilesIDGetHandler(w http.ResponseWriter, r *http.Request) error {
|
||||
tree := server.tree()
|
||||
branches, err := tree.Get()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
id := path.Base(r.URL.Path)
|
||||
fullId, err := tree.FullId(id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
branch, _ := branches[id]
|
||||
w.Header().Set("Title", branch.Title)
|
||||
w.Header().Set("PID", branch.PID)
|
||||
return server.getContentHandler(server.diskFilePath(fullId), w, r)
|
||||
}
|
||||
|
||||
func (server *Server) apiV0FilesIDDelHandler(w http.ResponseWriter, r *http.Request) error {
|
||||
tree := server.tree()
|
||||
branches, err := tree.Get()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
id := path.Base(r.URL.Path)
|
||||
branch, ok := branches[id]
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
branch.Deleted = true
|
||||
branch.Updated = time.Now().UTC()
|
||||
return tree.Put(id, branch)
|
||||
return errors.New("not impl")
|
||||
}
|
||||
|
||||
func (server *Server) apiV0SearchHandler(w http.ResponseWriter, r *http.Request) error {
|
||||
|
|
@ -357,30 +245,5 @@ func (server *Server) apiV0SearchHandler(w http.ResponseWriter, r *http.Request)
|
|||
w.Write([]byte(`[]`))
|
||||
return nil
|
||||
}
|
||||
results := []string{}
|
||||
if err := filepath.Walk(server.diskFileDir("id"), func(p string, info os.FileInfo, err error) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if info.IsDir() {
|
||||
return nil
|
||||
}
|
||||
if info.Name() != "data" {
|
||||
return nil
|
||||
}
|
||||
b, err := ioutil.ReadFile(p)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, pattern := range patterns {
|
||||
if !pattern.Match(b) {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
results = append(results, path.Base(p))
|
||||
return err
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
return json.NewEncoder(w).Encode(results)
|
||||
return errors.New("not impl")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,15 +1,6 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"path"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
/*
|
||||
func TestServerRoutes(t *testing.T) {
|
||||
server := NewServer(t.TempDir())
|
||||
if err := server.Routes(); err != nil {
|
||||
|
|
@ -198,3 +189,4 @@ func TestServerPutTreeGetFile(t *testing.T) {
|
|||
}
|
||||
})
|
||||
}
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -1,190 +1,130 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"time"
|
||||
|
||||
yaml "gopkg.in/yaml.v2"
|
||||
)
|
||||
|
||||
var errChainNotFound = errors.New("pid chain not found")
|
||||
|
||||
type Tree struct {
|
||||
root string
|
||||
type Branch struct {
|
||||
Leaf Leaf
|
||||
Branches map[string]Branch
|
||||
}
|
||||
|
||||
type Branch struct {
|
||||
func (branch Branch) IsZero() bool {
|
||||
return branch.Leaf == (Leaf{}) && len(branch.Branches) == 0
|
||||
}
|
||||
|
||||
type Leaf struct {
|
||||
Title string
|
||||
Deleted bool
|
||||
Updated time.Time
|
||||
PID string
|
||||
Content string
|
||||
}
|
||||
|
||||
type Pretty struct {
|
||||
Children map[string]*Pretty
|
||||
Title string
|
||||
}
|
||||
|
||||
func NewTree(root string) *Tree {
|
||||
return &Tree{
|
||||
root: root,
|
||||
func (base Leaf) Merge(updated Leaf) Leaf {
|
||||
if updated.Title != "" {
|
||||
base.Title = updated.Title
|
||||
}
|
||||
base.Deleted = updated.Deleted
|
||||
base.Content = updated.Content
|
||||
return base
|
||||
}
|
||||
|
||||
func (tree *Tree) GetPretty() (map[string]*Pretty, error) {
|
||||
branches, err := tree.Get()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
_ = branches
|
||||
realpretty := map[string]*Pretty{}
|
||||
lookuppretty := map[string]*Pretty{}
|
||||
timesZero := 0
|
||||
for len(branches) > 0 && timesZero < 3 {
|
||||
topop := []string{}
|
||||
for id, branch := range branches {
|
||||
if branch.Deleted {
|
||||
} else if branch.PID == "" {
|
||||
realpretty[id] = &Pretty{
|
||||
Title: branch.Title,
|
||||
Children: map[string]*Pretty{},
|
||||
}
|
||||
lookuppretty[id] = realpretty[id]
|
||||
} else if ptr, ok := lookuppretty[branch.PID]; ok {
|
||||
ptr.Children[id] = &Pretty{
|
||||
Title: branch.Title,
|
||||
Children: map[string]*Pretty{},
|
||||
}
|
||||
lookuppretty[id] = ptr.Children[id]
|
||||
} else {
|
||||
continue
|
||||
}
|
||||
topop = append(topop, id)
|
||||
}
|
||||
for _, id := range topop {
|
||||
delete(branches, id)
|
||||
}
|
||||
if len(topop) == 0 {
|
||||
timesZero++
|
||||
} else {
|
||||
timesZero = 0
|
||||
}
|
||||
}
|
||||
return realpretty, nil
|
||||
type Tree struct {
|
||||
root string
|
||||
cachedRoot Branch
|
||||
}
|
||||
|
||||
func (tree *Tree) Get() (map[string]Branch, error) {
|
||||
m := map[string]Branch{}
|
||||
err := filepath.Walk(tree.root, func(p string, info os.FileInfo, err error) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if info.IsDir() {
|
||||
return nil
|
||||
}
|
||||
if info.Name() != "meta.yaml" {
|
||||
return nil
|
||||
}
|
||||
b, err := ioutil.ReadFile(p)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var branch Branch
|
||||
if err := yaml.Unmarshal(b, &branch); err != nil {
|
||||
return err
|
||||
}
|
||||
id := path.Base(path.Dir(p))
|
||||
pidFullPath := path.Dir(path.Dir(p))
|
||||
if pidFullPath != tree.root {
|
||||
branch.PID = path.Base(pidFullPath)
|
||||
} else {
|
||||
branch.PID = ""
|
||||
}
|
||||
m[id] = branch
|
||||
return nil
|
||||
})
|
||||
func NewTree(root string) Tree {
|
||||
return Tree{root: root}
|
||||
}
|
||||
|
||||
func (tree Tree) WithRoot(root string) Tree {
|
||||
tree.root = root
|
||||
return tree
|
||||
}
|
||||
|
||||
func (tree Tree) GetRoot() (Branch, error) {
|
||||
if !tree.cachedRoot.IsZero() {
|
||||
return tree.cachedRoot, nil
|
||||
}
|
||||
got, err := tree.getRoot()
|
||||
if err == nil {
|
||||
tree.cachedRoot = got
|
||||
}
|
||||
return got, err
|
||||
}
|
||||
|
||||
func (tree Tree) getRoot() (Branch, error) {
|
||||
m := Branch{Branches: map[string]Branch{}}
|
||||
entries, err := os.ReadDir(tree.root)
|
||||
if os.IsNotExist(err) {
|
||||
return m, nil
|
||||
}
|
||||
return m, err
|
||||
}
|
||||
|
||||
func (tree *Tree) FullId(id string) (string, error) {
|
||||
fullId, _, err := tree.fullIdAndMeta(id)
|
||||
return fullId, err
|
||||
}
|
||||
|
||||
func (tree *Tree) fullIdAndMeta(id string) (string, Branch, error) {
|
||||
m, err := tree.Get()
|
||||
if err != nil {
|
||||
return "", Branch{}, err
|
||||
return Branch{}, err
|
||||
}
|
||||
branch, ok := m[id]
|
||||
if !ok {
|
||||
return "", Branch{}, errChainNotFound
|
||||
}
|
||||
p := id
|
||||
for branch.PID != "" {
|
||||
p = path.Join(branch.PID, p)
|
||||
branch, ok = m[branch.PID]
|
||||
if !ok {
|
||||
return "", Branch{}, errChainNotFound
|
||||
for _, entry := range entries {
|
||||
if entry.Name() == "data.yaml" {
|
||||
if b, err := ioutil.ReadFile(path.Join(tree.root, entry.Name())); err != nil {
|
||||
return Branch{}, err
|
||||
} else if err := yaml.Unmarshal(b, &m.Leaf); err != nil {
|
||||
return Branch{}, err
|
||||
}
|
||||
} else if entry.IsDir() {
|
||||
if branch, err := tree.WithRoot(path.Join(tree.root, entry.Name())).getRoot(); err != nil {
|
||||
return Branch{}, err
|
||||
} else {
|
||||
m.Branches[entry.Name()] = branch
|
||||
}
|
||||
}
|
||||
}
|
||||
return path.Join(tree.root, p), branch, nil
|
||||
return m, nil
|
||||
}
|
||||
|
||||
func (tree *Tree) putMeta(fullId string, branch Branch) error {
|
||||
b, err := yaml.Marshal(branch)
|
||||
func (tree Tree) toDir(id []string) string {
|
||||
return path.Dir(tree.toData(id))
|
||||
}
|
||||
|
||||
func (tree Tree) toData(id []string) string {
|
||||
return path.Join(tree.root, path.Join(id...), "data.yaml")
|
||||
}
|
||||
|
||||
func (tree Tree) Put(id []string, input Leaf) error {
|
||||
if _, err := os.Stat(tree.toData(id)); os.IsNotExist(err) {
|
||||
b, err := yaml.Marshal(Leaf{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := ensureAndWrite(tree.toData(id), b); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
old, err := tree.Get(id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return ensureAndWrite(path.Join(fullId, "meta.yaml"), b)
|
||||
}
|
||||
|
||||
func (tree *Tree) Del(id string) error {
|
||||
fullId, meta, err := tree.fullIdAndMeta(id)
|
||||
if err != nil {
|
||||
if err == errChainNotFound {
|
||||
return nil
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
meta.Updated = time.Now().UTC()
|
||||
meta.Deleted = true
|
||||
|
||||
return tree.putMeta(fullId, meta)
|
||||
}
|
||||
|
||||
func (tree *Tree) Put(id string, branch Branch) error {
|
||||
fullId, meta, err := tree.fullIdAndMeta(id)
|
||||
if err == errChainNotFound {
|
||||
if branch.PID != "" {
|
||||
fullId, _, err = tree.fullIdAndMeta(branch.PID)
|
||||
fullId = path.Join(fullId, id)
|
||||
} else {
|
||||
err = nil
|
||||
fullId = path.Join(tree.root, fullId, id)
|
||||
}
|
||||
}
|
||||
b, err := yaml.Marshal(old.Merge(input))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
meta.Updated = time.Now().UTC()
|
||||
if meta.Title == "" {
|
||||
meta.Title = "Untitled (" + time.Now().UTC().String() + ")"
|
||||
}
|
||||
if branch.Title != "" {
|
||||
meta.Title = branch.Title
|
||||
}
|
||||
meta.PID = branch.PID
|
||||
meta.Deleted = branch.Deleted
|
||||
|
||||
return tree.putMeta(fullId, meta)
|
||||
return ensureAndWrite(tree.toData(id), b)
|
||||
}
|
||||
|
||||
func (tree Tree) Del(id []string) error {
|
||||
os.RemoveAll(tree.toDir(id))
|
||||
return nil
|
||||
}
|
||||
|
||||
func (tree Tree) Get(id []string) (Leaf, error) {
|
||||
f, err := os.Open(tree.toData(id))
|
||||
if err != nil {
|
||||
return Leaf{}, err
|
||||
}
|
||||
defer f.Close()
|
||||
var got Leaf
|
||||
err = yaml.NewDecoder(f).Decode(&got)
|
||||
return got, err
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,148 +1,30 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"testing"
|
||||
"time"
|
||||
import "testing"
|
||||
|
||||
"go.mongodb.org/mongo-driver/bson"
|
||||
)
|
||||
|
||||
func TestTree(t *testing.T) {
|
||||
func TestTreeCrud(t *testing.T) {
|
||||
tree := NewTree(t.TempDir())
|
||||
|
||||
t.Logf("tree.Get() from zero")
|
||||
if m, err := tree.Get(); err != nil {
|
||||
t.Fatal("failed to get empty tree:", err)
|
||||
} else if m == nil {
|
||||
t.Fatal("got a nil tree:", m)
|
||||
if m, err := tree.GetRoot(); err != nil {
|
||||
t.Fatal(err)
|
||||
} else if m.Branches == nil {
|
||||
t.Fatal(m)
|
||||
}
|
||||
|
||||
t.Logf("tree.Del() from zero")
|
||||
if err := tree.Del("id"); err != nil {
|
||||
t.Fatal("failed to del a nil id:", err)
|
||||
}
|
||||
|
||||
t.Logf("tree.Put(bad pid) from zero")
|
||||
if err := tree.Put("id", Branch{PID: "fake"}); err == nil {
|
||||
t.Fatal("failed to put with a fake pid:", err)
|
||||
}
|
||||
|
||||
t.Logf("tree.Put() from zero")
|
||||
if err := tree.Put("id", Branch{}); err != nil {
|
||||
t.Fatal("failed to put with no pid:", err)
|
||||
} else if branches, err := tree.Get(); err != nil {
|
||||
t.Fatal("failed to get after put:", err)
|
||||
} else if branch, ok := branches["id"]; !ok {
|
||||
t.Fatal("got tree without put id:", branches)
|
||||
} else if branch.Title == "" {
|
||||
t.Fatal("got no default title", branch)
|
||||
} else if time.Since(branch.Updated) > time.Hour {
|
||||
t.Fatal("got not updated", branch)
|
||||
} else if branch.Deleted {
|
||||
t.Fatal("got deleted after put", branch)
|
||||
}
|
||||
|
||||
t.Logf("tree.Put(good pid)")
|
||||
if err := tree.Put("id2", Branch{PID: "id"}); err != nil {
|
||||
if err := tree.Del([]string{"id"}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
t.Logf("tree.Del(good pid)")
|
||||
if err := tree.Del("id"); err != nil {
|
||||
want := Leaf{
|
||||
Title: "leaf title",
|
||||
Deleted: true,
|
||||
Content: "leaf content",
|
||||
}
|
||||
if err := tree.Put([]string{"id"}, want); err != nil {
|
||||
t.Fatal(err)
|
||||
} else if branches, err := tree.Get(); err != nil {
|
||||
} else if l, err := tree.Get([]string{"id"}); err != nil {
|
||||
t.Fatal(err)
|
||||
} else if branch, ok := branches["id"]; !ok {
|
||||
t.Fatal(ok)
|
||||
} else if !branch.Deleted {
|
||||
t.Fatal(branch)
|
||||
}
|
||||
}
|
||||
|
||||
func TestTreePretty(t *testing.T) {
|
||||
tree := NewTree(t.TempDir())
|
||||
tree.Put("A", Branch{Title: "A", PID: ""})
|
||||
tree.Put("AA", Branch{Title: "AA", PID: "A", Deleted: true})
|
||||
tree.Put("B", Branch{Title: "B", PID: ""})
|
||||
tree.Put("BA", Branch{Title: "BA", PID: "B"})
|
||||
tree.Put("BAA", Branch{Title: "BAA", PID: "BA", Deleted: true})
|
||||
tree.Put("BB", Branch{Title: "BB", PID: "B"})
|
||||
tree.Put("BBA", Branch{Title: "BBA", PID: "BB"})
|
||||
tree.Put("BBB", Branch{Title: "BBB", PID: "BB"})
|
||||
tree.Put("BBBC", Branch{Title: "BBBC", PID: "BBB"})
|
||||
tree.Put("C", Branch{Title: "C", PID: "", Deleted: true})
|
||||
tree.Put("D", Branch{Title: "D", PID: ""})
|
||||
tree.Put("DA", Branch{Title: "DA", PID: "D"})
|
||||
tree.Put("DAA", Branch{Title: "DAA", PID: "DA", Deleted: true})
|
||||
tree.Put("DAAA", Branch{Title: "DAAA", PID: "DAA"})
|
||||
got, err := tree.GetPretty()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
gotb, _ := json.MarshalIndent(got, "", " ")
|
||||
t.Logf("%s", gotb)
|
||||
/*
|
||||
A
|
||||
-AA
|
||||
B
|
||||
BA
|
||||
-BAA
|
||||
BB
|
||||
BBA
|
||||
BBB
|
||||
BBBC
|
||||
-C
|
||||
D
|
||||
DA
|
||||
-DAA
|
||||
DAAA
|
||||
*/
|
||||
want := bson.M{
|
||||
"A": bson.M{
|
||||
"Title": "A",
|
||||
"Children": bson.M{},
|
||||
},
|
||||
"B": bson.M{
|
||||
"Title": "B",
|
||||
"Children": bson.M{
|
||||
"BA": bson.M{
|
||||
"Title": "BA",
|
||||
"Children": bson.M{},
|
||||
},
|
||||
"BB": bson.M{
|
||||
"Title": "BB",
|
||||
"Children": bson.M{
|
||||
"BBA": bson.M{
|
||||
"Title": "BBA",
|
||||
"Children": bson.M{},
|
||||
},
|
||||
"BBB": bson.M{
|
||||
"Title": "BBB",
|
||||
"Children": bson.M{
|
||||
"BBBC": bson.M{
|
||||
"Title": "BBBC",
|
||||
"Children": bson.M{},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
"D": bson.M{
|
||||
"Title": "D",
|
||||
"Children": bson.M{
|
||||
"DA": bson.M{
|
||||
"Title": "DA",
|
||||
"Children": bson.M{},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
wantb, _ := json.MarshalIndent(want, "", " ")
|
||||
if !bytes.Equal(gotb, wantb) {
|
||||
t.Fatalf("want:\n\t%s, got\n\t%s", wantb, gotb)
|
||||
} else if l != want {
|
||||
t.Fatal(want, l)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue