WIP ACLTree tests

This commit is contained in:
mcrakhman 2022-07-10 17:33:27 +02:00 committed by Mikhail Iudin
parent 8372abb4e1
commit b07a137fb9
No known key found for this signature in database
GPG Key ID: FAAAA8BAABDFF1C0
11 changed files with 194 additions and 254 deletions

View File

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

166
acltree/acltree_test.go Normal file
View File

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

View File

@ -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 {

View File

@ -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

View File

@ -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
}

View File

@ -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

View File

@ -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

View File

@ -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"

View File

@ -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"

View File

@ -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"

View File

@ -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"`
}