diff --git a/acltree/aclstatebuilder_test.go b/acltree/aclstatebuilder_test.go deleted file mode 100644 index c5ea0c79..00000000 --- a/acltree/aclstatebuilder_test.go +++ /dev/null @@ -1,231 +0,0 @@ -package acltree - -import ( - "testing" - - "github.com/anytypeio/go-anytype-infrastructure-experiments/account" - "github.com/anytypeio/go-anytype-infrastructure-experiments/aclchanges/pb" - "github.com/anytypeio/go-anytype-infrastructure-experiments/testutils/threadbuilder" - "github.com/anytypeio/go-anytype-infrastructure-experiments/thread" - "github.com/anytypeio/go-anytype-infrastructure-experiments/util/keys" - - "github.com/stretchr/testify/assert" -) - -type ACLContext struct { - Tree *Tree - ACLState *ACLState -} - -func createTreeFromThread(t thread.Thread, fromStart bool) (*Tree, error) { - treeBuilder := newTreeBuilder(t, keys.NewEd25519Decoder()) - treeBuilder.Init() - return treeBuilder.Build(fromStart) -} - -func createACLStateFromThread( - t thread.Thread, - identity string, - key keys.EncryptionPrivKey, - decoder keys.SigningPubKeyDecoder, - fromStart bool) (*ACLContext, error) { - tree, err := createTreeFromThread(t, fromStart) - if err != nil { - return nil, err - } - - accountData := &account.AccountData{ - Identity: identity, - EncKey: key, - } - - aclTreeBuilder := newACLTreeBuilder(t, decoder) - aclTreeBuilder.Init() - aclTree, err := aclTreeBuilder.Build() - if err != nil { - return nil, err - } - - if !fromStart { - snapshotValidator := newSnapshotValidator(decoder, accountData) - snapshotValidator.Init(aclTree) - valid, err := snapshotValidator.ValidateSnapshot(tree.root) - if err != nil { - return nil, err - } - if !valid { - // TODO: think about what to do if the snapshot is invalid - should we rebuild the Tree without it - return createACLStateFromThread(t, identity, key, decoder, true) - } - } - - aclBuilder := newACLStateBuilder(decoder, accountData) - err = aclBuilder.Init(tree) - if err != nil { - return nil, err - } - - aclState, err := aclBuilder.Build() - if err != nil { - return nil, err - } - return &ACLContext{ - Tree: tree, - ACLState: aclState, - }, nil -} - -func TestACLStateBuilder_UserJoinBuild(t *testing.T) { - thread, err := threadbuilder.NewThreadBuilderFromFile("threadbuilder/userjoinexample.yml") - if err != nil { - t.Fatal(err) - } - keychain := thread.GetKeychain() - ctx, err := createACLStateFromThread( - thread, - keychain.GetIdentity("A"), - keychain.EncryptionKeys["A"], - keys.NewEd25519Decoder(), - false) - if err != nil { - t.Fatalf("should Build acl ACLState without err: %v", err) - } - aclState := ctx.ACLState - //fmt.Println(ctx.Tree.Graph()) - aId := keychain.GeneratedIdentities["A"] - bId := keychain.GeneratedIdentities["B"] - cId := keychain.GeneratedIdentities["C"] - - assert.Equal(t, aclState.identity, aId) - assert.Equal(t, aclState.userStates[aId].Permissions, pb.ACLChange_Admin) - assert.Equal(t, aclState.userStates[bId].Permissions, pb.ACLChange_Writer) - assert.Equal(t, aclState.userStates[cId].Permissions, pb.ACLChange_Reader) - - var changeIds []string - ctx.Tree.Iterate(ctx.Tree.root.Id, func(c *Change) (isContinue bool) { - changeIds = append(changeIds, c.Id) - return true - }) - assert.Equal(t, changeIds, []string{"A.1.1", "A.1.2", "B.1.1", "B.1.2"}) -} - -func TestACLStateBuilder_UserRemoveBuild(t *testing.T) { - thread, err := threadbuilder.NewThreadBuilderFromFile("threadbuilder/userremoveexample.yml") - if err != nil { - t.Fatal(err) - } - keychain := thread.GetKeychain() - ctx, err := createACLStateFromThread( - thread, - keychain.GetIdentity("A"), - keychain.EncryptionKeys["A"], - keys.NewEd25519Decoder(), - false) - if err != nil { - t.Fatalf("should Build acl ACLState without err: %v", err) - } - aclState := ctx.ACLState - //fmt.Println(ctx.Tree.Graph()) - aId := keychain.GeneratedIdentities["A"] - - assert.Equal(t, aclState.identity, aId) - assert.Equal(t, aclState.userStates[aId].Permissions, pb.ACLChange_Admin) - - var changeIds []string - ctx.Tree.Iterate(ctx.Tree.root.Id, func(c *Change) (isContinue bool) { - changeIds = append(changeIds, c.Id) - return true - }) - assert.Equal(t, changeIds, []string{"A.1.1", "A.1.2", "B.1.1", "A.1.3", "A.1.4"}) -} - -func TestACLStateBuilder_UserRemoveBeforeBuild(t *testing.T) { - thread, err := threadbuilder.NewThreadBuilderFromFile("threadbuilder/userremovebeforeexample.yml") - if err != nil { - t.Fatal(err) - } - keychain := thread.GetKeychain() - ctx, err := createACLStateFromThread( - thread, - keychain.GetIdentity("A"), - keychain.EncryptionKeys["A"], - keys.NewEd25519Decoder(), - false) - if err != nil { - t.Fatalf("should Build acl ACLState without err: %v", err) - } - aclState := ctx.ACLState - //fmt.Println(ctx.Tree.Graph()) - for _, s := range []string{"A", "C", "E"} { - assert.Equal(t, aclState.userStates[keychain.GetIdentity(s)].Permissions, pb.ACLChange_Admin) - } - assert.Equal(t, aclState.identity, keychain.GetIdentity("A")) - assert.Nil(t, aclState.userStates[keychain.GetIdentity("B")]) - - var changeIds []string - ctx.Tree.Iterate(ctx.Tree.root.Id, func(c *Change) (isContinue bool) { - changeIds = append(changeIds, c.Id) - return true - }) - assert.Equal(t, changeIds, []string{"A.1.1", "B.1.1", "A.1.2", "A.1.3"}) -} - -func TestACLStateBuilder_InvalidSnapshotBuild(t *testing.T) { - thread, err := threadbuilder.NewThreadBuilderFromFile("threadbuilder/invalidsnapshotexample.yml") - if err != nil { - t.Fatal(err) - } - keychain := thread.GetKeychain() - ctx, err := createACLStateFromThread( - thread, - keychain.GetIdentity("A"), - keychain.EncryptionKeys["A"], - keys.NewEd25519Decoder(), - false) - if err != nil { - t.Fatalf("should Build acl ACLState without err: %v", err) - } - aclState := ctx.ACLState - //fmt.Println(ctx.Tree.Graph()) - for _, s := range []string{"A", "B", "C", "D", "E", "F"} { - assert.Equal(t, aclState.userStates[keychain.GetIdentity(s)].Permissions, pb.ACLChange_Admin) - } - assert.Equal(t, aclState.identity, keychain.GetIdentity("A")) - - var changeIds []string - ctx.Tree.Iterate(ctx.Tree.root.Id, func(c *Change) (isContinue bool) { - changeIds = append(changeIds, c.Id) - return true - }) - assert.Equal(t, []string{"A.1.1", "B.1.1", "A.1.2", "A.1.3", "B.1.2"}, changeIds) -} - -func TestACLStateBuilder_ValidSnapshotBuild(t *testing.T) { - thread, err := threadbuilder.NewThreadBuilderFromFile("threadbuilder/validsnapshotexample.yml") - if err != nil { - t.Fatal(err) - } - keychain := thread.GetKeychain() - ctx, err := createACLStateFromThread( - thread, - keychain.GetIdentity("A"), - keychain.EncryptionKeys["A"], - keys.NewEd25519Decoder(), - false) - if err != nil { - t.Fatalf("should Build acl ACLState without err: %v", err) - } - aclState := ctx.ACLState - //fmt.Println(ctx.Tree.Graph()) - for _, s := range []string{"A", "B", "C", "D", "E", "F"} { - assert.Equal(t, aclState.userStates[keychain.GetIdentity(s)].Permissions, pb.ACLChange_Admin) - } - assert.Equal(t, aclState.identity, keychain.GetIdentity("A")) - - var changeIds []string - ctx.Tree.Iterate(ctx.Tree.root.Id, func(c *Change) (isContinue bool) { - changeIds = append(changeIds, c.Id) - return true - }) - assert.Equal(t, []string{"A.1.2", "A.1.3", "B.1.2"}, changeIds) -} diff --git a/acltree/acltree_test.go b/acltree/acltree_test.go new file mode 100644 index 00000000..ebf3e4c2 --- /dev/null +++ b/acltree/acltree_test.go @@ -0,0 +1,166 @@ +package acltree + +import ( + "testing" + + "github.com/stretchr/testify/assert" + + "github.com/anytypeio/go-anytype-infrastructure-experiments/account" + "github.com/anytypeio/go-anytype-infrastructure-experiments/aclchanges/pb" + "github.com/anytypeio/go-anytype-infrastructure-experiments/testutils/threadbuilder" +) + +func TestACLTree_UserJoinBuild(t *testing.T) { + thr, err := threadbuilder.NewThreadBuilderFromFile("threadbuilder/userjoinexample.yml") + if err != nil { + t.Fatal(err) + } + keychain := thr.GetKeychain() + accountData := &account.AccountData{ + Identity: keychain.GetIdentity("A"), + SignKey: keychain.SigningKeys["A"], + EncKey: keychain.EncryptionKeys["A"], + } + tree, err := BuildACLTree(thr, accountData) + if err != nil { + t.Fatalf("should Build acl ACLState without err: %v", err) + } + aclState := tree.ACLState() + //fmt.Println(ctx.Tree.Graph()) + aId := keychain.GeneratedIdentities["A"] + bId := keychain.GeneratedIdentities["B"] + cId := keychain.GeneratedIdentities["C"] + + assert.Equal(t, aclState.identity, aId) + assert.Equal(t, aclState.userStates[aId].Permissions, pb.ACLChange_Admin) + assert.Equal(t, aclState.userStates[bId].Permissions, pb.ACLChange_Writer) + assert.Equal(t, aclState.userStates[cId].Permissions, pb.ACLChange_Reader) + + var changeIds []string + tree.Iterate(func(c *Change) (isContinue bool) { + changeIds = append(changeIds, c.Id) + return true + }) + assert.Equal(t, changeIds, []string{"A.1.1", "A.1.2", "B.1.1", "B.1.2"}) +} + +func TestACLTree_UserRemoveBuild(t *testing.T) { + thr, err := threadbuilder.NewThreadBuilderFromFile("threadbuilder/userremoveexample.yml") + if err != nil { + t.Fatal(err) + } + keychain := thr.GetKeychain() + accountData := &account.AccountData{ + Identity: keychain.GetIdentity("A"), + SignKey: keychain.SigningKeys["A"], + EncKey: keychain.EncryptionKeys["A"], + } + tree, err := BuildACLTree(thr, accountData) + if err != nil { + t.Fatalf("should Build acl ACLState without err: %v", err) + } + aclState := tree.ACLState() + //fmt.Println(ctx.Tree.Graph()) + aId := keychain.GeneratedIdentities["A"] + + assert.Equal(t, aclState.identity, aId) + assert.Equal(t, aclState.userStates[aId].Permissions, pb.ACLChange_Admin) + + var changeIds []string + tree.Iterate(func(c *Change) (isContinue bool) { + changeIds = append(changeIds, c.Id) + return true + }) + assert.Equal(t, changeIds, []string{"A.1.1", "A.1.2", "B.1.1", "A.1.3", "A.1.4"}) +} + +func TestACLTree_UserRemoveBeforeBuild(t *testing.T) { + thr, err := threadbuilder.NewThreadBuilderFromFile("threadbuilder/userremovebeforeexample.yml") + if err != nil { + t.Fatal(err) + } + keychain := thr.GetKeychain() + accountData := &account.AccountData{ + Identity: keychain.GetIdentity("A"), + SignKey: keychain.SigningKeys["A"], + EncKey: keychain.EncryptionKeys["A"], + } + tree, err := BuildACLTree(thr, accountData) + if err != nil { + t.Fatalf("should Build acl ACLState without err: %v", err) + } + aclState := tree.ACLState() + //fmt.Println(ctx.Tree.Graph()) + for _, s := range []string{"A", "C", "E"} { + assert.Equal(t, aclState.userStates[keychain.GetIdentity(s)].Permissions, pb.ACLChange_Admin) + } + assert.Equal(t, aclState.identity, keychain.GetIdentity("A")) + assert.Nil(t, aclState.userStates[keychain.GetIdentity("B")]) + + var changeIds []string + tree.Iterate(func(c *Change) (isContinue bool) { + changeIds = append(changeIds, c.Id) + return true + }) + assert.Equal(t, changeIds, []string{"A.1.1", "B.1.1", "A.1.2", "A.1.3"}) +} + +func TestACLTree_InvalidSnapshotBuild(t *testing.T) { + thr, err := threadbuilder.NewThreadBuilderFromFile("threadbuilder/invalidsnapshotexample.yml") + if err != nil { + t.Fatal(err) + } + keychain := thr.GetKeychain() + accountData := &account.AccountData{ + Identity: keychain.GetIdentity("A"), + SignKey: keychain.SigningKeys["A"], + EncKey: keychain.EncryptionKeys["A"], + } + tree, err := BuildACLTree(thr, accountData) + if err != nil { + t.Fatalf("should Build acl ACLState without err: %v", err) + } + aclState := tree.ACLState() + //fmt.Println(ctx.Tree.Graph()) + for _, s := range []string{"A", "B", "C", "D", "E", "F"} { + assert.Equal(t, aclState.userStates[keychain.GetIdentity(s)].Permissions, pb.ACLChange_Admin) + } + assert.Equal(t, aclState.identity, keychain.GetIdentity("A")) + + var changeIds []string + tree.Iterate(func(c *Change) (isContinue bool) { + changeIds = append(changeIds, c.Id) + return true + }) + assert.Equal(t, []string{"A.1.1", "B.1.1", "A.1.2", "A.1.3", "B.1.2"}, changeIds) +} + +func TestACLTree_ValidSnapshotBuild(t *testing.T) { + thr, err := threadbuilder.NewThreadBuilderFromFile("threadbuilder/validsnapshotexample.yml") + if err != nil { + t.Fatal(err) + } + keychain := thr.GetKeychain() + accountData := &account.AccountData{ + Identity: keychain.GetIdentity("A"), + SignKey: keychain.SigningKeys["A"], + EncKey: keychain.EncryptionKeys["A"], + } + tree, err := BuildACLTree(thr, accountData) + if err != nil { + t.Fatalf("should Build acl ACLState without err: %v", err) + } + aclState := tree.ACLState() + //fmt.Println(ctx.Tree.Graph()) + for _, s := range []string{"A", "B", "C", "D", "E", "F"} { + assert.Equal(t, aclState.userStates[keychain.GetIdentity(s)].Permissions, pb.ACLChange_Admin) + } + assert.Equal(t, aclState.identity, keychain.GetIdentity("A")) + + var changeIds []string + tree.Iterate(func(c *Change) (isContinue bool) { + changeIds = append(changeIds, c.Id) + return true + }) + assert.Equal(t, []string{"A.1.2", "A.1.3", "B.1.2"}, changeIds) +} diff --git a/acltree/treebuilder_test.go b/acltree/treebuilder_test.go index e7f8145a..02d0a317 100644 --- a/acltree/treebuilder_test.go +++ b/acltree/treebuilder_test.go @@ -1,5 +1,11 @@ package acltree +//func createTreeFromThread(t thread.Thread, fromStart bool) (*Tree, error) { +// treeBuilder := newTreeBuilder(t, keys.NewEd25519Decoder()) +// treeBuilder.Init() +// return treeBuilder.Build(fromStart) +//} +// //func TestACLTreeBuilder_UserJoinCorrectHeadsAndLen(t *testing.T) { // thread, err := threadbuilder.NewThreadBuilderFromFile("threadbuilder/userjoinexample.yml") // if err != nil { diff --git a/testutils/threadbuilder/invalidsnapshotexample.yml b/testutils/threadbuilder/invalidsnapshotexample.yml index 317e575b..e3877b47 100644 --- a/testutils/threadbuilder/invalidsnapshotexample.yml +++ b/testutils/threadbuilder/invalidsnapshotexample.yml @@ -117,7 +117,7 @@ graph: baseSnapshot: A.1.2 aclHeads: [A.1.2] treeHeads: [A.1.2] -maybeHeads: +orphans: - A.1.3 - B.1.2 diff --git a/testutils/threadbuilder/threadbuilder.go b/testutils/threadbuilder/threadbuilder.go index 081a961f..72784452 100644 --- a/testutils/threadbuilder/threadbuilder.go +++ b/testutils/threadbuilder/threadbuilder.go @@ -4,6 +4,7 @@ import ( "context" "fmt" "github.com/anytypeio/go-anytype-infrastructure-experiments/aclchanges" + "github.com/anytypeio/go-anytype-infrastructure-experiments/util/slice" "io/ioutil" "github.com/gogo/protobuf/proto" @@ -31,7 +32,7 @@ type ThreadBuilder struct { allChanges map[string]*threadChange updatedChanges map[string]*threadChange heads []string - maybeHeads []string + orphans []string keychain *Keychain } @@ -109,8 +110,8 @@ func (t *ThreadBuilder) AddRawChange(change *thread.RawChange) error { return nil } -func (t *ThreadBuilder) AddOrphans(head string) { - t.maybeHeads = append(t.maybeHeads, head) +func (t *ThreadBuilder) AddOrphans(orphans ...string) { + t.orphans = append(t.orphans, orphans...) } func (t *ThreadBuilder) AddChange(change aclchanges.Change) error { @@ -141,12 +142,7 @@ func (t *ThreadBuilder) AddChange(change aclchanges.Change) error { } func (t *ThreadBuilder) Orphans() []string { - return t.maybeHeads -} - -func (t *ThreadBuilder) SetPossibleHeads(heads []string) { - // we should copy here instead of just setting the value - t.maybeHeads = heads + return t.orphans } func (t *ThreadBuilder) SetHeads(heads []string) { @@ -154,6 +150,10 @@ func (t *ThreadBuilder) SetHeads(heads []string) { t.heads = heads } +func (t *ThreadBuilder) RemoveOrphans(orphans ...string) { + t.orphans = slice.Difference(t.orphans, orphans) +} + func (t *ThreadBuilder) GetChange(ctx context.Context, recordID string) (*thread.RawChange, error) { return t.getChange(recordID, t.allChanges), nil } @@ -214,7 +214,7 @@ func (t *ThreadBuilder) Parse(thread *YMLThread) { } t.parseGraph(thread) - t.parseHeads(thread) + t.parseOrphans(thread) } func (t *ThreadBuilder) parseChange(ch *Change) *threadChange { @@ -469,8 +469,8 @@ func (t *ThreadBuilder) convertPermission(perm string) pb.ACLChangeUserPermissio func (t *ThreadBuilder) traverseFromHeads(f func(t *threadChange) error) error { uniqMap := map[string]struct{}{} - stack := make([]string, len(t.maybeHeads), 10) - copy(stack, t.maybeHeads) + stack := make([]string, len(t.orphans), 10) + copy(stack, t.orphans) for len(stack) > 0 { id := stack[len(stack)-1] stack = stack[:len(stack)-1] @@ -507,7 +507,6 @@ func (t *ThreadBuilder) parseGraph(thread *YMLThread) { } } -func (t *ThreadBuilder) parseHeads(thread *YMLThread) { - t.heads = thread.Heads - t.maybeHeads = thread.MaybeHeads +func (t *ThreadBuilder) parseOrphans(thread *YMLThread) { + t.orphans = thread.Orphans } diff --git a/testutils/threadbuilder/userjoinexample.yml b/testutils/threadbuilder/userjoinexample.yml index 02092a8a..c2a0b11b 100644 --- a/testutils/threadbuilder/userjoinexample.yml +++ b/testutils/threadbuilder/userjoinexample.yml @@ -99,7 +99,7 @@ graph: baseSnapshot: A.1.1 aclHeads: [B.1.1] treeHeads: [B.1.1] -maybeHeads: +orphans: - "A.1.3" updatedChanges: - id: B.1.3 diff --git a/testutils/threadbuilder/userjoinexampleupdate.yml b/testutils/threadbuilder/userjoinexampleupdate.yml index 80f4fcda..daf5d550 100644 --- a/testutils/threadbuilder/userjoinexampleupdate.yml +++ b/testutils/threadbuilder/userjoinexampleupdate.yml @@ -101,7 +101,7 @@ graph: baseSnapshot: A.1.1 aclHeads: [B.1.1] treeHeads: [B.1.1] -maybeHeads: +orphans: - "A.1.3" updatedChanges: - id: B.1.3 diff --git a/testutils/threadbuilder/userremovebeforeexample.yml b/testutils/threadbuilder/userremovebeforeexample.yml index fe4d2b6e..7569ec04 100644 --- a/testutils/threadbuilder/userremovebeforeexample.yml +++ b/testutils/threadbuilder/userremovebeforeexample.yml @@ -101,6 +101,6 @@ graph: baseSnapshot: A.1.1 aclHeads: [A.1.2] treeHeads: [A.1.2] -maybeHeads: +orphans: - "A.1.3" - "B.1.2" diff --git a/testutils/threadbuilder/userremoveexample.yml b/testutils/threadbuilder/userremoveexample.yml index ab131fb5..36a4a191 100644 --- a/testutils/threadbuilder/userremoveexample.yml +++ b/testutils/threadbuilder/userremoveexample.yml @@ -101,6 +101,6 @@ graph: aclSnapshot: A.1.1 aclHeads: [A.1.3] treeHeads: [A.1.3] -maybeHeads: +orphans: - "A.1.4" - "B.1.2" diff --git a/testutils/threadbuilder/validsnapshotexample.yml b/testutils/threadbuilder/validsnapshotexample.yml index 2c3e49d0..1fef3c35 100644 --- a/testutils/threadbuilder/validsnapshotexample.yml +++ b/testutils/threadbuilder/validsnapshotexample.yml @@ -124,7 +124,7 @@ graph: baseSnapshot: A.1.2 aclHeads: [A.1.2] treeHeads: [A.1.2] -maybeHeads: +orphans: - "A.1.3" - "B.1.2" diff --git a/testutils/threadbuilder/ymlentities.go b/testutils/threadbuilder/ymlentities.go index 33a9a34e..68c7f3ac 100644 --- a/testutils/threadbuilder/ymlentities.go +++ b/testutils/threadbuilder/ymlentities.go @@ -100,6 +100,6 @@ type YMLThread struct { Graph []*GraphNode `yaml:"graph"` UpdatedGraph []*GraphNode `yaml:"updatedGraph"` - Heads []string `yaml:"heads"` - MaybeHeads []string `yaml:"maybeHeads"` + Heads []string `yaml:"heads"` + Orphans []string `yaml:"orphans"` }