Improve validator logic and add tests
This commit is contained in:
parent
4a1a95a7b9
commit
977308edcc
@ -2,83 +2,19 @@ package objecttree
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"crypto/rand"
|
|
||||||
"github.com/anytypeio/any-sync/commonspace/object/accountdata"
|
"github.com/anytypeio/any-sync/commonspace/object/accountdata"
|
||||||
"github.com/anytypeio/any-sync/commonspace/object/acl/list"
|
"github.com/anytypeio/any-sync/commonspace/object/acl/list"
|
||||||
"github.com/anytypeio/any-sync/commonspace/object/tree/treechangeproto"
|
"github.com/anytypeio/any-sync/commonspace/object/tree/treechangeproto"
|
||||||
"github.com/anytypeio/any-sync/commonspace/object/tree/treestorage"
|
"github.com/anytypeio/any-sync/commonspace/object/tree/treestorage"
|
||||||
"github.com/anytypeio/any-sync/util/crypto"
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
type mockKeyStorage struct {
|
|
||||||
key crypto.PubKey
|
|
||||||
}
|
|
||||||
|
|
||||||
func newKeyStorage() mockKeyStorage {
|
|
||||||
_, pk, _ := crypto.GenerateEd25519Key(rand.Reader)
|
|
||||||
return mockKeyStorage{pk}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m mockKeyStorage) PubKeyFromProto(protoBytes []byte) (crypto.PubKey, error) {
|
|
||||||
return m.key, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
type mockChangeCreator struct{}
|
|
||||||
|
|
||||||
func (c *mockChangeCreator) createRoot(id, aclId string) *treechangeproto.RawTreeChangeWithId {
|
|
||||||
aclChange := &treechangeproto.RootChange{
|
|
||||||
AclHeadId: aclId,
|
|
||||||
}
|
|
||||||
res, _ := aclChange.Marshal()
|
|
||||||
|
|
||||||
raw := &treechangeproto.RawTreeChange{
|
|
||||||
Payload: res,
|
|
||||||
Signature: nil,
|
|
||||||
}
|
|
||||||
rawMarshalled, _ := raw.Marshal()
|
|
||||||
|
|
||||||
return &treechangeproto.RawTreeChangeWithId{
|
|
||||||
RawChange: rawMarshalled,
|
|
||||||
Id: id,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *mockChangeCreator) createRaw(id, aclId, snapshotId string, isSnapshot bool, prevIds ...string) *treechangeproto.RawTreeChangeWithId {
|
|
||||||
aclChange := &treechangeproto.TreeChange{
|
|
||||||
TreeHeadIds: prevIds,
|
|
||||||
AclHeadId: aclId,
|
|
||||||
SnapshotBaseId: snapshotId,
|
|
||||||
ChangesData: nil,
|
|
||||||
IsSnapshot: isSnapshot,
|
|
||||||
}
|
|
||||||
res, _ := aclChange.Marshal()
|
|
||||||
|
|
||||||
raw := &treechangeproto.RawTreeChange{
|
|
||||||
Payload: res,
|
|
||||||
Signature: nil,
|
|
||||||
}
|
|
||||||
rawMarshalled, _ := raw.Marshal()
|
|
||||||
|
|
||||||
return &treechangeproto.RawTreeChangeWithId{
|
|
||||||
RawChange: rawMarshalled,
|
|
||||||
Id: id,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *mockChangeCreator) createNewTreeStorage(treeId, aclHeadId string) treestorage.TreeStorage {
|
|
||||||
root := c.createRoot(treeId, aclHeadId)
|
|
||||||
treeStorage, _ := treestorage.NewInMemoryTreeStorage(root, []string{root.Id}, []*treechangeproto.RawTreeChangeWithId{root})
|
|
||||||
return treeStorage
|
|
||||||
}
|
|
||||||
|
|
||||||
type testTreeContext struct {
|
type testTreeContext struct {
|
||||||
aclList list.AclList
|
aclList list.AclList
|
||||||
treeStorage treestorage.TreeStorage
|
treeStorage treestorage.TreeStorage
|
||||||
changeBuilder ChangeBuilder
|
changeCreator *MockChangeCreator
|
||||||
changeCreator *mockChangeCreator
|
|
||||||
objTree ObjectTree
|
objTree ObjectTree
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -91,12 +27,12 @@ func prepareAclList(t *testing.T) list.AclList {
|
|||||||
return aclList
|
return aclList
|
||||||
}
|
}
|
||||||
|
|
||||||
func prepareTreeDeps(aclList list.AclList) (*mockChangeCreator, objectTreeDeps) {
|
func prepareTreeDeps(aclList list.AclList) (*MockChangeCreator, objectTreeDeps) {
|
||||||
changeCreator := &mockChangeCreator{}
|
changeCreator := NewMockChangeCreator()
|
||||||
treeStorage := changeCreator.createNewTreeStorage("0", aclList.Head().Id)
|
treeStorage := changeCreator.CreateNewTreeStorage("0", aclList.Head().Id)
|
||||||
root, _ := treeStorage.Root()
|
root, _ := treeStorage.Root()
|
||||||
changeBuilder := &nonVerifiableChangeBuilder{
|
changeBuilder := &nonVerifiableChangeBuilder{
|
||||||
ChangeBuilder: NewChangeBuilder(newKeyStorage(), root),
|
ChangeBuilder: NewChangeBuilder(newMockKeyStorage(), root),
|
||||||
}
|
}
|
||||||
deps := objectTreeDeps{
|
deps := objectTreeDeps{
|
||||||
changeBuilder: changeBuilder,
|
changeBuilder: changeBuilder,
|
||||||
@ -110,23 +46,9 @@ func prepareTreeDeps(aclList list.AclList) (*mockChangeCreator, objectTreeDeps)
|
|||||||
}
|
}
|
||||||
|
|
||||||
func prepareTreeContext(t *testing.T, aclList list.AclList) testTreeContext {
|
func prepareTreeContext(t *testing.T, aclList list.AclList) testTreeContext {
|
||||||
changeCreator := &mockChangeCreator{}
|
changeCreator := NewMockChangeCreator()
|
||||||
treeStorage := changeCreator.createNewTreeStorage("0", aclList.Head().Id)
|
treeStorage := changeCreator.CreateNewTreeStorage("0", aclList.Head().Id)
|
||||||
root, _ := treeStorage.Root()
|
objTree, err := BuildTestableTree(aclList, treeStorage)
|
||||||
changeBuilder := &nonVerifiableChangeBuilder{
|
|
||||||
ChangeBuilder: NewChangeBuilder(newKeyStorage(), root),
|
|
||||||
}
|
|
||||||
deps := objectTreeDeps{
|
|
||||||
changeBuilder: changeBuilder,
|
|
||||||
treeBuilder: newTreeBuilder(treeStorage, changeBuilder),
|
|
||||||
treeStorage: treeStorage,
|
|
||||||
rawChangeLoader: newRawChangeLoader(treeStorage, changeBuilder),
|
|
||||||
validator: &noOpTreeValidator{},
|
|
||||||
aclList: aclList,
|
|
||||||
}
|
|
||||||
|
|
||||||
// check build
|
|
||||||
objTree, err := buildObjectTree(deps)
|
|
||||||
require.NoError(t, err, "building tree should be without error")
|
require.NoError(t, err, "building tree should be without error")
|
||||||
|
|
||||||
// check tree iterate
|
// check tree iterate
|
||||||
@ -140,7 +62,6 @@ func prepareTreeContext(t *testing.T, aclList list.AclList) testTreeContext {
|
|||||||
return testTreeContext{
|
return testTreeContext{
|
||||||
aclList: aclList,
|
aclList: aclList,
|
||||||
treeStorage: treeStorage,
|
treeStorage: treeStorage,
|
||||||
changeBuilder: changeBuilder,
|
|
||||||
changeCreator: changeCreator,
|
changeCreator: changeCreator,
|
||||||
objTree: objTree,
|
objTree: objTree,
|
||||||
}
|
}
|
||||||
@ -156,8 +77,8 @@ func TestObjectTree(t *testing.T) {
|
|||||||
objTree := ctx.objTree
|
objTree := ctx.objTree
|
||||||
|
|
||||||
rawChanges := []*treechangeproto.RawTreeChangeWithId{
|
rawChanges := []*treechangeproto.RawTreeChangeWithId{
|
||||||
changeCreator.createRaw("1", aclList.Head().Id, "0", false, "0"),
|
changeCreator.CreateRaw("1", aclList.Head().Id, "0", false, "0"),
|
||||||
changeCreator.createRaw("2", aclList.Head().Id, "0", false, "1"),
|
changeCreator.CreateRaw("2", aclList.Head().Id, "0", false, "1"),
|
||||||
}
|
}
|
||||||
payload := RawChangesPayload{
|
payload := RawChangesPayload{
|
||||||
NewHeads: []string{rawChanges[len(rawChanges)-1].Id},
|
NewHeads: []string{rawChanges[len(rawChanges)-1].Id},
|
||||||
@ -201,7 +122,7 @@ func TestObjectTree(t *testing.T) {
|
|||||||
objTree := ctx.objTree
|
objTree := ctx.objTree
|
||||||
|
|
||||||
rawChanges := []*treechangeproto.RawTreeChangeWithId{
|
rawChanges := []*treechangeproto.RawTreeChangeWithId{
|
||||||
changeCreator.createRaw("0", aclList.Head().Id, "", true, ""),
|
changeCreator.CreateRaw("0", aclList.Head().Id, "", true, ""),
|
||||||
}
|
}
|
||||||
payload := RawChangesPayload{
|
payload := RawChangesPayload{
|
||||||
NewHeads: []string{rawChanges[len(rawChanges)-1].Id},
|
NewHeads: []string{rawChanges[len(rawChanges)-1].Id},
|
||||||
@ -225,7 +146,33 @@ func TestObjectTree(t *testing.T) {
|
|||||||
objTree := ctx.objTree
|
objTree := ctx.objTree
|
||||||
|
|
||||||
rawChanges := []*treechangeproto.RawTreeChangeWithId{
|
rawChanges := []*treechangeproto.RawTreeChangeWithId{
|
||||||
changeCreator.createRaw("2", aclList.Head().Id, "0", false, "1"),
|
changeCreator.CreateRaw("2", aclList.Head().Id, "0", false, "1"),
|
||||||
|
}
|
||||||
|
payload := RawChangesPayload{
|
||||||
|
NewHeads: []string{rawChanges[len(rawChanges)-1].Id},
|
||||||
|
RawChanges: rawChanges,
|
||||||
|
}
|
||||||
|
res, err := objTree.AddRawChanges(context.Background(), payload)
|
||||||
|
require.NoError(t, err, "adding changes should be without error")
|
||||||
|
|
||||||
|
// check result
|
||||||
|
assert.Equal(t, []string{"0"}, res.OldHeads)
|
||||||
|
assert.Equal(t, []string{"0"}, res.Heads)
|
||||||
|
assert.Equal(t, 0, len(res.Added))
|
||||||
|
assert.Equal(t, Nothing, res.Mode)
|
||||||
|
|
||||||
|
// check tree heads
|
||||||
|
assert.Equal(t, []string{"0"}, objTree.Heads())
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("add not connected changes", func(t *testing.T) {
|
||||||
|
ctx := prepareTreeContext(t, aclList)
|
||||||
|
changeCreator := ctx.changeCreator
|
||||||
|
objTree := ctx.objTree
|
||||||
|
|
||||||
|
// this change could in theory replace current snapshot, we should prevent that
|
||||||
|
rawChanges := []*treechangeproto.RawTreeChangeWithId{
|
||||||
|
changeCreator.CreateRaw("2", aclList.Head().Id, "0", true, "1"),
|
||||||
}
|
}
|
||||||
payload := RawChangesPayload{
|
payload := RawChangesPayload{
|
||||||
NewHeads: []string{rawChanges[len(rawChanges)-1].Id},
|
NewHeads: []string{rawChanges[len(rawChanges)-1].Id},
|
||||||
@ -251,10 +198,10 @@ func TestObjectTree(t *testing.T) {
|
|||||||
objTree := ctx.objTree
|
objTree := ctx.objTree
|
||||||
|
|
||||||
rawChanges := []*treechangeproto.RawTreeChangeWithId{
|
rawChanges := []*treechangeproto.RawTreeChangeWithId{
|
||||||
changeCreator.createRaw("1", aclList.Head().Id, "0", false, "0"),
|
changeCreator.CreateRaw("1", aclList.Head().Id, "0", false, "0"),
|
||||||
changeCreator.createRaw("2", aclList.Head().Id, "0", false, "1"),
|
changeCreator.CreateRaw("2", aclList.Head().Id, "0", false, "1"),
|
||||||
changeCreator.createRaw("3", aclList.Head().Id, "0", true, "2"),
|
changeCreator.CreateRaw("3", aclList.Head().Id, "0", true, "2"),
|
||||||
changeCreator.createRaw("4", aclList.Head().Id, "3", false, "3"),
|
changeCreator.CreateRaw("4", aclList.Head().Id, "3", false, "3"),
|
||||||
}
|
}
|
||||||
payload := RawChangesPayload{
|
payload := RawChangesPayload{
|
||||||
NewHeads: []string{rawChanges[len(rawChanges)-1].Id},
|
NewHeads: []string{rawChanges[len(rawChanges)-1].Id},
|
||||||
@ -301,9 +248,9 @@ func TestObjectTree(t *testing.T) {
|
|||||||
objTree := ctx.objTree
|
objTree := ctx.objTree
|
||||||
|
|
||||||
rawChanges := []*treechangeproto.RawTreeChangeWithId{
|
rawChanges := []*treechangeproto.RawTreeChangeWithId{
|
||||||
changeCreator.createRaw("1", aclList.Head().Id, "0", false, "0"),
|
changeCreator.CreateRaw("1", aclList.Head().Id, "0", false, "0"),
|
||||||
changeCreator.createRaw("2", aclList.Head().Id, "0", false, "1"),
|
changeCreator.CreateRaw("2", aclList.Head().Id, "0", false, "1"),
|
||||||
changeCreator.createRaw("3", aclList.Head().Id, "0", true, "2"),
|
changeCreator.CreateRaw("3", aclList.Head().Id, "0", true, "2"),
|
||||||
}
|
}
|
||||||
payload := RawChangesPayload{
|
payload := RawChangesPayload{
|
||||||
NewHeads: []string{rawChanges[len(rawChanges)-1].Id},
|
NewHeads: []string{rawChanges[len(rawChanges)-1].Id},
|
||||||
@ -325,12 +272,12 @@ func TestObjectTree(t *testing.T) {
|
|||||||
objTree := ctx.objTree
|
objTree := ctx.objTree
|
||||||
|
|
||||||
rawChanges := []*treechangeproto.RawTreeChangeWithId{
|
rawChanges := []*treechangeproto.RawTreeChangeWithId{
|
||||||
changeCreator.createRaw("1", aclList.Head().Id, "0", false, "0"),
|
changeCreator.CreateRaw("1", aclList.Head().Id, "0", false, "0"),
|
||||||
changeCreator.createRaw("2", aclList.Head().Id, "0", false, "1"),
|
changeCreator.CreateRaw("2", aclList.Head().Id, "0", false, "1"),
|
||||||
changeCreator.createRaw("3", aclList.Head().Id, "0", true, "2"),
|
changeCreator.CreateRaw("3", aclList.Head().Id, "0", true, "2"),
|
||||||
changeCreator.createRaw("4", aclList.Head().Id, "0", false, "2"),
|
changeCreator.CreateRaw("4", aclList.Head().Id, "0", false, "2"),
|
||||||
changeCreator.createRaw("5", aclList.Head().Id, "0", false, "1"),
|
changeCreator.CreateRaw("5", aclList.Head().Id, "0", false, "1"),
|
||||||
changeCreator.createRaw("6", aclList.Head().Id, "0", false, "3", "4", "5"),
|
changeCreator.CreateRaw("6", aclList.Head().Id, "0", false, "3", "4", "5"),
|
||||||
}
|
}
|
||||||
|
|
||||||
payload := RawChangesPayload{
|
payload := RawChangesPayload{
|
||||||
@ -404,13 +351,13 @@ func TestObjectTree(t *testing.T) {
|
|||||||
objTree := ctx.objTree
|
objTree := ctx.objTree
|
||||||
|
|
||||||
rawChanges := []*treechangeproto.RawTreeChangeWithId{
|
rawChanges := []*treechangeproto.RawTreeChangeWithId{
|
||||||
changeCreator.createRaw("1", aclList.Head().Id, "0", false, "0"),
|
changeCreator.CreateRaw("1", aclList.Head().Id, "0", false, "0"),
|
||||||
changeCreator.createRaw("2", aclList.Head().Id, "0", false, "1"),
|
changeCreator.CreateRaw("2", aclList.Head().Id, "0", false, "1"),
|
||||||
changeCreator.createRaw("3", aclList.Head().Id, "0", true, "2"),
|
changeCreator.CreateRaw("3", aclList.Head().Id, "0", true, "2"),
|
||||||
changeCreator.createRaw("4", aclList.Head().Id, "0", false, "2"),
|
changeCreator.CreateRaw("4", aclList.Head().Id, "0", false, "2"),
|
||||||
changeCreator.createRaw("5", aclList.Head().Id, "0", false, "1"),
|
changeCreator.CreateRaw("5", aclList.Head().Id, "0", false, "1"),
|
||||||
// main difference from tree example
|
// main difference from tree example
|
||||||
changeCreator.createRaw("6", aclList.Head().Id, "0", true, "3", "4", "5"),
|
changeCreator.CreateRaw("6", aclList.Head().Id, "0", true, "3", "4", "5"),
|
||||||
}
|
}
|
||||||
|
|
||||||
payload := RawChangesPayload{
|
payload := RawChangesPayload{
|
||||||
@ -485,9 +432,9 @@ func TestObjectTree(t *testing.T) {
|
|||||||
objTree := ctx.objTree
|
objTree := ctx.objTree
|
||||||
|
|
||||||
rawChanges := []*treechangeproto.RawTreeChangeWithId{
|
rawChanges := []*treechangeproto.RawTreeChangeWithId{
|
||||||
changeCreator.createRaw("1", aclList.Head().Id, "0", false, "0"),
|
changeCreator.CreateRaw("1", aclList.Head().Id, "0", false, "0"),
|
||||||
changeCreator.createRaw("2", aclList.Head().Id, "0", false, "1"),
|
changeCreator.CreateRaw("2", aclList.Head().Id, "0", false, "1"),
|
||||||
changeCreator.createRaw("3", aclList.Head().Id, "0", true, "2"),
|
changeCreator.CreateRaw("3", aclList.Head().Id, "0", true, "2"),
|
||||||
}
|
}
|
||||||
payload := RawChangesPayload{
|
payload := RawChangesPayload{
|
||||||
NewHeads: []string{rawChanges[len(rawChanges)-1].Id},
|
NewHeads: []string{rawChanges[len(rawChanges)-1].Id},
|
||||||
@ -499,9 +446,9 @@ func TestObjectTree(t *testing.T) {
|
|||||||
require.Equal(t, "3", objTree.Root().Id)
|
require.Equal(t, "3", objTree.Root().Id)
|
||||||
|
|
||||||
rawChanges = []*treechangeproto.RawTreeChangeWithId{
|
rawChanges = []*treechangeproto.RawTreeChangeWithId{
|
||||||
changeCreator.createRaw("4", aclList.Head().Id, "0", false, "2"),
|
changeCreator.CreateRaw("4", aclList.Head().Id, "0", false, "2"),
|
||||||
changeCreator.createRaw("5", aclList.Head().Id, "0", false, "1"),
|
changeCreator.CreateRaw("5", aclList.Head().Id, "0", false, "1"),
|
||||||
changeCreator.createRaw("6", aclList.Head().Id, "0", false, "3", "4", "5"),
|
changeCreator.CreateRaw("6", aclList.Head().Id, "0", false, "3", "4", "5"),
|
||||||
}
|
}
|
||||||
payload = RawChangesPayload{
|
payload = RawChangesPayload{
|
||||||
NewHeads: []string{rawChanges[len(rawChanges)-1].Id},
|
NewHeads: []string{rawChanges[len(rawChanges)-1].Id},
|
||||||
@ -545,12 +492,12 @@ func TestObjectTree(t *testing.T) {
|
|||||||
changeCreator, deps := prepareTreeDeps(aclList)
|
changeCreator, deps := prepareTreeDeps(aclList)
|
||||||
|
|
||||||
rawChanges := []*treechangeproto.RawTreeChangeWithId{
|
rawChanges := []*treechangeproto.RawTreeChangeWithId{
|
||||||
changeCreator.createRaw("1", aclList.Head().Id, "0", false, "0"),
|
changeCreator.CreateRaw("1", aclList.Head().Id, "0", false, "0"),
|
||||||
changeCreator.createRaw("2", aclList.Head().Id, "0", false, "1"),
|
changeCreator.CreateRaw("2", aclList.Head().Id, "0", false, "1"),
|
||||||
changeCreator.createRaw("3", aclList.Head().Id, "0", true, "2"),
|
changeCreator.CreateRaw("3", aclList.Head().Id, "0", true, "2"),
|
||||||
changeCreator.createRaw("4", aclList.Head().Id, "0", false, "2"),
|
changeCreator.CreateRaw("4", aclList.Head().Id, "0", false, "2"),
|
||||||
changeCreator.createRaw("5", aclList.Head().Id, "0", false, "1"),
|
changeCreator.CreateRaw("5", aclList.Head().Id, "0", false, "1"),
|
||||||
changeCreator.createRaw("6", aclList.Head().Id, "0", false, "3", "4", "5"),
|
changeCreator.CreateRaw("6", aclList.Head().Id, "0", false, "3", "4", "5"),
|
||||||
}
|
}
|
||||||
deps.treeStorage.TransactionAdd(rawChanges, []string{"6"})
|
deps.treeStorage.TransactionAdd(rawChanges, []string{"6"})
|
||||||
hTree, err := buildHistoryTree(deps, HistoryTreeParams{
|
hTree, err := buildHistoryTree(deps, HistoryTreeParams{
|
||||||
@ -576,12 +523,12 @@ func TestObjectTree(t *testing.T) {
|
|||||||
changeCreator, deps := prepareTreeDeps(aclList)
|
changeCreator, deps := prepareTreeDeps(aclList)
|
||||||
|
|
||||||
rawChanges := []*treechangeproto.RawTreeChangeWithId{
|
rawChanges := []*treechangeproto.RawTreeChangeWithId{
|
||||||
changeCreator.createRaw("1", aclList.Head().Id, "0", false, "0"),
|
changeCreator.CreateRaw("1", aclList.Head().Id, "0", false, "0"),
|
||||||
changeCreator.createRaw("2", aclList.Head().Id, "0", false, "1"),
|
changeCreator.CreateRaw("2", aclList.Head().Id, "0", false, "1"),
|
||||||
changeCreator.createRaw("3", aclList.Head().Id, "0", true, "2"),
|
changeCreator.CreateRaw("3", aclList.Head().Id, "0", true, "2"),
|
||||||
changeCreator.createRaw("4", aclList.Head().Id, "0", false, "2"),
|
changeCreator.CreateRaw("4", aclList.Head().Id, "0", false, "2"),
|
||||||
changeCreator.createRaw("5", aclList.Head().Id, "0", false, "1"),
|
changeCreator.CreateRaw("5", aclList.Head().Id, "0", false, "1"),
|
||||||
changeCreator.createRaw("6", aclList.Head().Id, "0", false, "3", "4", "5"),
|
changeCreator.CreateRaw("6", aclList.Head().Id, "0", false, "3", "4", "5"),
|
||||||
}
|
}
|
||||||
deps.treeStorage.TransactionAdd(rawChanges, []string{"6"})
|
deps.treeStorage.TransactionAdd(rawChanges, []string{"6"})
|
||||||
hTree, err := buildHistoryTree(deps, HistoryTreeParams{
|
hTree, err := buildHistoryTree(deps, HistoryTreeParams{
|
||||||
@ -623,4 +570,40 @@ func TestObjectTree(t *testing.T) {
|
|||||||
assert.Equal(t, []string{"0"}, iterChangesId)
|
assert.Equal(t, []string{"0"}, iterChangesId)
|
||||||
assert.Equal(t, "0", hTree.Root().Id)
|
assert.Equal(t, "0", hTree.Root().Id)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
t.Run("validate correct tree", func(t *testing.T) {
|
||||||
|
ctx := prepareTreeContext(t, aclList)
|
||||||
|
changeCreator := ctx.changeCreator
|
||||||
|
|
||||||
|
rawChanges := []*treechangeproto.RawTreeChangeWithId{
|
||||||
|
ctx.objTree.Header(),
|
||||||
|
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"),
|
||||||
|
}
|
||||||
|
defaultObjectTreeDeps = nonVerifiableTreeDeps
|
||||||
|
err := ValidateRawTree(treestorage.TreeStorageCreatePayload{
|
||||||
|
RootRawChange: ctx.objTree.Header(),
|
||||||
|
Heads: []string{"3"},
|
||||||
|
Changes: rawChanges,
|
||||||
|
}, ctx.aclList)
|
||||||
|
require.NoError(t, err)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("fail to validate not connected tree", func(t *testing.T) {
|
||||||
|
ctx := prepareTreeContext(t, aclList)
|
||||||
|
changeCreator := ctx.changeCreator
|
||||||
|
|
||||||
|
rawChanges := []*treechangeproto.RawTreeChangeWithId{
|
||||||
|
ctx.objTree.Header(),
|
||||||
|
changeCreator.CreateRaw("3", aclList.Head().Id, "0", true, "2"),
|
||||||
|
}
|
||||||
|
defaultObjectTreeDeps = nonVerifiableTreeDeps
|
||||||
|
err := ValidateRawTree(treestorage.TreeStorageCreatePayload{
|
||||||
|
RootRawChange: ctx.objTree.Header(),
|
||||||
|
Heads: []string{"3"},
|
||||||
|
Changes: rawChanges,
|
||||||
|
}, ctx.aclList)
|
||||||
|
require.Equal(t, ErrHasInvalidChanges, err)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@ -33,7 +33,9 @@ type objectTreeDeps struct {
|
|||||||
aclList list.AclList
|
aclList list.AclList
|
||||||
}
|
}
|
||||||
|
|
||||||
func defaultObjectTreeDeps(
|
var defaultObjectTreeDeps = verifiableTreeDeps
|
||||||
|
|
||||||
|
func verifiableTreeDeps(
|
||||||
rootChange *treechangeproto.RawTreeChangeWithId,
|
rootChange *treechangeproto.RawTreeChangeWithId,
|
||||||
treeStorage treestorage.TreeStorage,
|
treeStorage treestorage.TreeStorage,
|
||||||
aclList list.AclList) objectTreeDeps {
|
aclList list.AclList) objectTreeDeps {
|
||||||
@ -53,7 +55,7 @@ func nonVerifiableTreeDeps(
|
|||||||
rootChange *treechangeproto.RawTreeChangeWithId,
|
rootChange *treechangeproto.RawTreeChangeWithId,
|
||||||
treeStorage treestorage.TreeStorage,
|
treeStorage treestorage.TreeStorage,
|
||||||
aclList list.AclList) objectTreeDeps {
|
aclList list.AclList) objectTreeDeps {
|
||||||
changeBuilder := &nonVerifiableChangeBuilder{NewChangeBuilder(nil, rootChange)}
|
changeBuilder := &nonVerifiableChangeBuilder{NewChangeBuilder(newMockKeyStorage(), rootChange)}
|
||||||
treeBuilder := newTreeBuilder(treeStorage, changeBuilder)
|
treeBuilder := newTreeBuilder(treeStorage, changeBuilder)
|
||||||
return objectTreeDeps{
|
return objectTreeDeps{
|
||||||
changeBuilder: changeBuilder,
|
changeBuilder: changeBuilder,
|
||||||
|
|||||||
@ -1,10 +1,12 @@
|
|||||||
package objecttree
|
package objecttree
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/anytypeio/any-sync/commonspace/object/acl/aclrecordproto"
|
"github.com/anytypeio/any-sync/commonspace/object/acl/aclrecordproto"
|
||||||
"github.com/anytypeio/any-sync/commonspace/object/acl/list"
|
"github.com/anytypeio/any-sync/commonspace/object/acl/list"
|
||||||
"github.com/anytypeio/any-sync/commonspace/object/tree/treestorage"
|
"github.com/anytypeio/any-sync/commonspace/object/tree/treestorage"
|
||||||
|
"github.com/anytypeio/any-sync/util/slice"
|
||||||
)
|
)
|
||||||
|
|
||||||
type ObjectTreeValidator interface {
|
type ObjectTreeValidator interface {
|
||||||
@ -88,11 +90,23 @@ func (v *objectTreeValidator) validateChange(tree *Tree, aclList list.AclList, c
|
|||||||
}
|
}
|
||||||
|
|
||||||
func ValidateRawTree(payload treestorage.TreeStorageCreatePayload, aclList list.AclList) (err error) {
|
func ValidateRawTree(payload treestorage.TreeStorageCreatePayload, aclList list.AclList) (err error) {
|
||||||
treeStorage, err := treestorage.NewInMemoryTreeStorage(payload.RootRawChange, payload.Heads, payload.Changes)
|
treeStorage, err := treestorage.NewInMemoryTreeStorage(payload.RootRawChange, []string{payload.RootRawChange.Id}, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
tree, err := BuildObjectTree(treeStorage, aclList)
|
||||||
_, err = BuildObjectTree(treeStorage, aclList)
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
res, err := tree.AddRawChanges(context.Background(), RawChangesPayload{
|
||||||
|
NewHeads: payload.Heads,
|
||||||
|
RawChanges: payload.Changes,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if !slice.UnsortedEquals(res.Heads, payload.Heads) {
|
||||||
|
return ErrHasInvalidChanges
|
||||||
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,11 +1,66 @@
|
|||||||
package objecttree
|
package objecttree
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"github.com/anytypeio/any-sync/commonspace/object/acl/list"
|
"github.com/anytypeio/any-sync/commonspace/object/acl/list"
|
||||||
"github.com/anytypeio/any-sync/commonspace/object/tree/treechangeproto"
|
"github.com/anytypeio/any-sync/commonspace/object/tree/treechangeproto"
|
||||||
"github.com/anytypeio/any-sync/commonspace/object/tree/treestorage"
|
"github.com/anytypeio/any-sync/commonspace/object/tree/treestorage"
|
||||||
|
"github.com/anytypeio/any-sync/util/crypto"
|
||||||
|
libcrypto "github.com/libp2p/go-libp2p/core/crypto"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type mockPubKey struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
const mockKeyValue = "mockKey"
|
||||||
|
|
||||||
|
func (m mockPubKey) Equals(key crypto.Key) bool {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m mockPubKey) Raw() ([]byte, error) {
|
||||||
|
return []byte(mockKeyValue), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m mockPubKey) Encrypt(message []byte) ([]byte, error) {
|
||||||
|
return message, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m mockPubKey) Verify(data []byte, sig []byte) (bool, error) {
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m mockPubKey) Marshall() ([]byte, error) {
|
||||||
|
return []byte(mockKeyValue), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m mockPubKey) Storage() []byte {
|
||||||
|
return []byte(mockKeyValue)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m mockPubKey) Account() string {
|
||||||
|
return mockKeyValue
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m mockPubKey) PeerId() string {
|
||||||
|
return mockKeyValue
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m mockPubKey) LibP2P() (libcrypto.PubKey, error) {
|
||||||
|
return nil, fmt.Errorf("can't be converted in libp2p")
|
||||||
|
}
|
||||||
|
|
||||||
|
type mockKeyStorage struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
func newMockKeyStorage() mockKeyStorage {
|
||||||
|
return mockKeyStorage{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m mockKeyStorage) PubKeyFromProto(protoBytes []byte) (crypto.PubKey, error) {
|
||||||
|
return mockPubKey{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
type MockChangeCreator struct{}
|
type MockChangeCreator struct{}
|
||||||
|
|
||||||
func NewMockChangeCreator() *MockChangeCreator {
|
func NewMockChangeCreator() *MockChangeCreator {
|
||||||
@ -61,7 +116,7 @@ func (c *MockChangeCreator) CreateNewTreeStorage(treeId, aclHeadId string) trees
|
|||||||
func BuildTestableTree(aclList list.AclList, treeStorage treestorage.TreeStorage) (ObjectTree, error) {
|
func BuildTestableTree(aclList list.AclList, treeStorage treestorage.TreeStorage) (ObjectTree, error) {
|
||||||
root, _ := treeStorage.Root()
|
root, _ := treeStorage.Root()
|
||||||
changeBuilder := &nonVerifiableChangeBuilder{
|
changeBuilder := &nonVerifiableChangeBuilder{
|
||||||
ChangeBuilder: NewChangeBuilder(nil, root),
|
ChangeBuilder: NewChangeBuilder(newMockKeyStorage(), root),
|
||||||
}
|
}
|
||||||
deps := objectTreeDeps{
|
deps := objectTreeDeps{
|
||||||
changeBuilder: changeBuilder,
|
changeBuilder: changeBuilder,
|
||||||
|
|||||||
@ -29,10 +29,11 @@ type processMsg struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type msgDescription struct {
|
type msgDescription struct {
|
||||||
name string
|
name string
|
||||||
from string
|
from string
|
||||||
to string
|
to string
|
||||||
heads []string
|
heads []string
|
||||||
|
changes []*treechangeproto.RawTreeChangeWithId
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *processMsg) description() (descr msgDescription) {
|
func (p *processMsg) description() (descr msgDescription) {
|
||||||
@ -50,14 +51,17 @@ func (p *processMsg) description() (descr msgDescription) {
|
|||||||
cnt := unmarshalled.GetContent().GetHeadUpdate()
|
cnt := unmarshalled.GetContent().GetHeadUpdate()
|
||||||
descr.name = "HeadUpdate"
|
descr.name = "HeadUpdate"
|
||||||
descr.heads = cnt.Heads
|
descr.heads = cnt.Heads
|
||||||
|
descr.changes = unmarshalled.GetContent().GetHeadUpdate().Changes
|
||||||
case unmarshalled.GetContent().GetFullSyncRequest() != nil:
|
case unmarshalled.GetContent().GetFullSyncRequest() != nil:
|
||||||
cnt := unmarshalled.GetContent().GetFullSyncRequest()
|
cnt := unmarshalled.GetContent().GetFullSyncRequest()
|
||||||
descr.name = "FullSyncRequest"
|
descr.name = "FullSyncRequest"
|
||||||
descr.heads = cnt.Heads
|
descr.heads = cnt.Heads
|
||||||
|
descr.changes = unmarshalled.GetContent().GetFullSyncRequest().Changes
|
||||||
case unmarshalled.GetContent().GetFullSyncResponse() != nil:
|
case unmarshalled.GetContent().GetFullSyncResponse() != nil:
|
||||||
cnt := unmarshalled.GetContent().GetFullSyncResponse()
|
cnt := unmarshalled.GetContent().GetFullSyncResponse()
|
||||||
descr.name = "FullSyncResponse"
|
descr.name = "FullSyncResponse"
|
||||||
descr.heads = cnt.Heads
|
descr.heads = cnt.Heads
|
||||||
|
descr.changes = unmarshalled.GetContent().GetFullSyncResponse().Changes
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -322,7 +326,7 @@ func (p *processFixture) stop() {
|
|||||||
p.wg.Wait()
|
p.wg.Wait()
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestSend_EmptyClient(t *testing.T) {
|
func TestSend_EmptyClientGetsFullHistory(t *testing.T) {
|
||||||
treeId := "treeId"
|
treeId := "treeId"
|
||||||
spaceId := "spaceId"
|
spaceId := "spaceId"
|
||||||
keys, err := accountdata.NewRandom()
|
keys, err := accountdata.NewRandom()
|
||||||
@ -345,7 +349,7 @@ func TestSend_EmptyClient(t *testing.T) {
|
|||||||
fx.handlers["peer1"].sendRawChanges(context.Background(), objecttree.RawChangesPayload{
|
fx.handlers["peer1"].sendRawChanges(context.Background(), objecttree.RawChangesPayload{
|
||||||
NewHeads: nil,
|
NewHeads: nil,
|
||||||
RawChanges: []*treechangeproto.RawTreeChangeWithId{
|
RawChanges: []*treechangeproto.RawTreeChangeWithId{
|
||||||
changeCreator.CreateRaw("1", aclList.Id(), treeId, false, treeId),
|
changeCreator.CreateRaw("1", aclList.Id(), treeId, true, treeId),
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
time.Sleep(100 * time.Millisecond)
|
time.Sleep(100 * time.Millisecond)
|
||||||
@ -357,9 +361,17 @@ func TestSend_EmptyClient(t *testing.T) {
|
|||||||
require.Equal(t, firstHeads, secondHeads)
|
require.Equal(t, firstHeads, secondHeads)
|
||||||
require.Equal(t, []string{"1"}, firstHeads)
|
require.Equal(t, []string{"1"}, firstHeads)
|
||||||
logMsgs := fx.log.batcher.GetAll()
|
logMsgs := fx.log.batcher.GetAll()
|
||||||
|
|
||||||
|
var fullResponseMsg *processMsg
|
||||||
for _, msg := range logMsgs {
|
for _, msg := range logMsgs {
|
||||||
fmt.Println(msg.description())
|
descr := msg.description()
|
||||||
|
if descr.name == "FullSyncResponse" {
|
||||||
|
fullResponseMsg = &msg
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
require.NotNil(t, fullResponseMsg)
|
||||||
|
// that means that we got not only the last snapshot, but also the first one
|
||||||
|
require.Len(t, fullResponseMsg.description().changes, 2)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestSimple_TwoPeers(t *testing.T) {
|
func TestSimple_TwoPeers(t *testing.T) {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user