From 78537ce0288ae6d054f29f8bd251c3d74f5bd859 Mon Sep 17 00:00:00 2001 From: mcrakhman Date: Wed, 7 Sep 2022 00:12:11 +0200 Subject: [PATCH] Add more tests and fix bugs --- pkg/acl/tree/objecttree.go | 1 + pkg/acl/tree/objecttree_test.go | 169 +++++++++++++++++++++++++++++++- pkg/acl/tree/treebuilder.go | 23 ++--- 3 files changed, 177 insertions(+), 16 deletions(-) diff --git a/pkg/acl/tree/objecttree.go b/pkg/acl/tree/objecttree.go index 74f92b10..62798038 100644 --- a/pkg/acl/tree/objecttree.go +++ b/pkg/acl/tree/objecttree.go @@ -481,6 +481,7 @@ func (ot *objectTree) Close() error { } func (ot *objectTree) SnapshotPath() []string { + // TODO: Add error as return parameter if ot.snapshotPathIsActual() { return ot.snapshotPath } diff --git a/pkg/acl/tree/objecttree_test.go b/pkg/acl/tree/objecttree_test.go index ea7bddfd..2c96aaf1 100644 --- a/pkg/acl/tree/objecttree_test.go +++ b/pkg/acl/tree/objecttree_test.go @@ -69,6 +69,14 @@ func (m *mockChangeValidator) ValidateTree(tree *Tree, aclList list.ACLList) err return nil } +type testTreeContext struct { + aclList list.ACLList + treeStorage storage.TreeStorage + changeBuilder *mockChangeBuilder + changeCreator *mockChangeCreator + objTree ObjectTree +} + func prepareACLList(t *testing.T) list.ACLList { st, err := acllistbuilder.NewListStorageWithTestName("userjoinexample.yml") require.NoError(t, err, "building storage should not result in error") @@ -79,8 +87,7 @@ func prepareACLList(t *testing.T) list.ACLList { return aclList } -func TestObjectTree(t *testing.T) { - aclList := prepareACLList(t) +func prepareTreeContext(t *testing.T, aclList list.ACLList) testTreeContext { changeCreator := &mockChangeCreator{} treeStorage := changeCreator.createNewTreeStorage("treeId", aclList.ID(), aclList.Head().Id, "0") changeBuilder := &mockChangeBuilder{} @@ -105,8 +112,24 @@ func TestObjectTree(t *testing.T) { }) require.NoError(t, err, "iterate should be without error") assert.Equal(t, []string{"0"}, iterChangesId) + return testTreeContext{ + aclList: aclList, + treeStorage: treeStorage, + changeBuilder: changeBuilder, + changeCreator: changeCreator, + objTree: objTree, + } +} + +func TestObjectTree(t *testing.T) { + aclList := prepareACLList(t) t.Run("add simple", func(t *testing.T) { + ctx := prepareTreeContext(t, aclList) + treeStorage := ctx.treeStorage + changeCreator := ctx.changeCreator + objTree := ctx.objTree + rawChanges := []*aclpb.RawChange{ changeCreator.createRaw("1", aclList.Head().Id, "0", false, "0"), changeCreator.createRaw("2", aclList.Head().Id, "0", false, "1"), @@ -141,4 +164,146 @@ func TestObjectTree(t *testing.T) { assert.Equal(t, ch, raw, "the changes in the storage should be the same") } }) + + t.Run("add new snapshot simple", func(t *testing.T) { + ctx := prepareTreeContext(t, aclList) + treeStorage := ctx.treeStorage + changeCreator := ctx.changeCreator + objTree := ctx.objTree + + rawChanges := []*aclpb.RawChange{ + changeCreator.createRaw("1", aclList.Head().Id, "0", false, "0"), + changeCreator.createRaw("2", aclList.Head().Id, "0", false, "1"), + changeCreator.createRaw("3", aclList.Head().Id, "0", true, "2"), + } + res, err := objTree.AddRawChanges(context.Background(), rawChanges...) + require.NoError(t, err, "adding changes should be without error") + + // check result + assert.Equal(t, []string{"0"}, res.OldHeads) + assert.Equal(t, []string{"3"}, res.Heads) + assert.Equal(t, len(rawChanges), len(res.Added)) + + // check tree heads + assert.Equal(t, []string{"3"}, objTree.Heads()) + + // check tree iterate + var iterChangesId []string + err = objTree.Iterate(nil, func(change *Change) bool { + iterChangesId = append(iterChangesId, change.Id) + return true + }) + require.NoError(t, err, "iterate should be without error") + assert.Equal(t, []string{"3"}, iterChangesId) + assert.Equal(t, "3", objTree.Root().Id) + + // check storage + heads, _ := treeStorage.Heads() + assert.Equal(t, []string{"3"}, heads) + + for _, ch := range rawChanges { + raw, err := treeStorage.GetRawChange(context.Background(), ch.Id) + assert.NoError(t, err, "storage should have all the changes") + assert.Equal(t, ch, raw, "the changes in the storage should be the same") + } + }) + + t.Run("snapshot path", func(t *testing.T) { + ctx := prepareTreeContext(t, aclList) + changeCreator := ctx.changeCreator + objTree := ctx.objTree + + rawChanges := []*aclpb.RawChange{ + changeCreator.createRaw("1", aclList.Head().Id, "0", false, "0"), + changeCreator.createRaw("2", aclList.Head().Id, "0", false, "1"), + changeCreator.createRaw("3", aclList.Head().Id, "0", true, "2"), + } + _, err := objTree.AddRawChanges(context.Background(), rawChanges...) + require.NoError(t, err, "adding changes should be without error") + + snapshotPath := objTree.SnapshotPath() + assert.Equal(t, []string{"3", "0"}, snapshotPath) + }) + + t.Run("changes after common snapshot", func(t *testing.T) { + ctx := prepareTreeContext(t, aclList) + changeCreator := ctx.changeCreator + objTree := ctx.objTree + + rawChanges := []*aclpb.RawChange{ + changeCreator.createRaw("1", aclList.Head().Id, "0", false, "0"), + changeCreator.createRaw("2", aclList.Head().Id, "0", false, "1"), + changeCreator.createRaw("3", aclList.Head().Id, "0", true, "2"), + changeCreator.createRaw("4", aclList.Head().Id, "0", false, "2"), + changeCreator.createRaw("5", aclList.Head().Id, "0", false, "1"), + changeCreator.createRaw("6", aclList.Head().Id, "0", false, "3", "4", "5"), + } + + _, err := objTree.AddRawChanges(context.Background(), rawChanges...) + require.NoError(t, err, "adding changes should be without error") + require.Equal(t, "0", objTree.Root().Id) + + changeIds := make(map[string]struct{}) + changes, err := objTree.ChangesAfterCommonSnapshot([]string{"3", "0"}) + for _, ch := range changes { + changeIds[ch.Id] = struct{}{} + } + + for _, raw := range rawChanges { + _, ok := changeIds[raw.Id] + assert.Equal(t, true, ok) + } + }) + + t.Run("add new changes related to previous snapshot", func(t *testing.T) { + ctx := prepareTreeContext(t, aclList) + treeStorage := ctx.treeStorage + changeCreator := ctx.changeCreator + objTree := ctx.objTree + + rawChanges := []*aclpb.RawChange{ + changeCreator.createRaw("1", aclList.Head().Id, "0", false, "0"), + changeCreator.createRaw("2", aclList.Head().Id, "0", false, "1"), + changeCreator.createRaw("3", aclList.Head().Id, "0", true, "2"), + } + res, err := objTree.AddRawChanges(context.Background(), rawChanges...) + require.NoError(t, err, "adding changes should be without error") + require.Equal(t, "3", objTree.Root().Id) + + rawChanges = []*aclpb.RawChange{ + changeCreator.createRaw("4", aclList.Head().Id, "0", false, "2"), + changeCreator.createRaw("5", aclList.Head().Id, "0", false, "1"), + changeCreator.createRaw("6", aclList.Head().Id, "0", false, "3", "4", "5"), + } + res, err = objTree.AddRawChanges(context.Background(), rawChanges...) + require.NoError(t, err, "adding changes should be without error") + + // check result + assert.Equal(t, []string{"3"}, res.OldHeads) + assert.Equal(t, []string{"6"}, res.Heads) + assert.Equal(t, len(rawChanges), len(res.Added)) + + // check tree heads + assert.Equal(t, []string{"6"}, objTree.Heads()) + + // check tree iterate + var iterChangesId []string + err = objTree.Iterate(nil, func(change *Change) bool { + iterChangesId = append(iterChangesId, change.Id) + return true + }) + require.NoError(t, err, "iterate should be without error") + assert.Equal(t, []string{"0", "1", "2", "3", "4", "5", "6"}, iterChangesId) + assert.Equal(t, "0", objTree.Root().Id) + + // check storage + heads, _ := treeStorage.Heads() + assert.Equal(t, []string{"6"}, heads) + + for _, ch := range rawChanges { + raw, err := treeStorage.GetRawChange(context.Background(), ch.Id) + assert.NoError(t, err, "storage should have all the changes") + assert.Equal(t, ch, raw, "the changes in the storage should be the same") + } + }) } diff --git a/pkg/acl/tree/treebuilder.go b/pkg/acl/tree/treebuilder.go index e284336d..c88324b3 100644 --- a/pkg/acl/tree/treebuilder.go +++ b/pkg/acl/tree/treebuilder.go @@ -85,18 +85,13 @@ func (tb *treeBuilder) dfs( load func(string) (*Change, error)) (buf []*Change, err error) { tb.idStack = tb.idStack[:0] tb.loadBuffer = tb.loadBuffer[:0] - buf = tb.loadBuffer - var ( - stack = tb.idStack - uniqMap = map[string]struct{}{breakpoint: {}} - ) + uniqMap := map[string]struct{}{breakpoint: {}} + tb.idStack = append(tb.idStack, heads...) - copy(stack, heads) - - for len(stack) > 0 { - id := stack[len(stack)-1] - stack = stack[:len(stack)-1] + for len(tb.idStack) > 0 { + id := tb.idStack[len(tb.idStack)-1] + tb.idStack = tb.idStack[:len(tb.idStack)-1] if _, exists := uniqMap[id]; exists { continue } @@ -107,16 +102,16 @@ func (tb *treeBuilder) dfs( } uniqMap[id] = struct{}{} - buf = append(buf, ch) + tb.loadBuffer = append(tb.loadBuffer, ch) for _, prev := range ch.PreviousIds { - if _, exists := uniqMap[id]; exists { + if _, exists := uniqMap[prev]; exists { continue } - stack = append(stack, prev) + tb.idStack = append(tb.idStack, prev) } } - return buf, nil + return tb.loadBuffer, nil } func (tb *treeBuilder) loadChange(id string) (ch *Change, err error) {