diff --git a/commonspace/object/tree/objecttree/objecttree_test.go b/commonspace/object/tree/objecttree/objecttree_test.go index dbb31321..7ec9fac9 100644 --- a/commonspace/object/tree/objecttree/objecttree_test.go +++ b/commonspace/object/tree/objecttree/objecttree_test.go @@ -2,83 +2,19 @@ package objecttree import ( "context" - "crypto/rand" "github.com/anytypeio/any-sync/commonspace/object/accountdata" "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/treestorage" - "github.com/anytypeio/any-sync/util/crypto" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "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 { aclList list.AclList treeStorage treestorage.TreeStorage - changeBuilder ChangeBuilder - changeCreator *mockChangeCreator + changeCreator *MockChangeCreator objTree ObjectTree } @@ -91,12 +27,12 @@ func prepareAclList(t *testing.T) list.AclList { return aclList } -func prepareTreeDeps(aclList list.AclList) (*mockChangeCreator, objectTreeDeps) { - changeCreator := &mockChangeCreator{} - treeStorage := changeCreator.createNewTreeStorage("0", aclList.Head().Id) +func prepareTreeDeps(aclList list.AclList) (*MockChangeCreator, objectTreeDeps) { + changeCreator := NewMockChangeCreator() + treeStorage := changeCreator.CreateNewTreeStorage("0", aclList.Head().Id) root, _ := treeStorage.Root() changeBuilder := &nonVerifiableChangeBuilder{ - ChangeBuilder: NewChangeBuilder(newKeyStorage(), root), + ChangeBuilder: NewChangeBuilder(newMockKeyStorage(), root), } deps := objectTreeDeps{ changeBuilder: changeBuilder, @@ -110,23 +46,9 @@ func prepareTreeDeps(aclList list.AclList) (*mockChangeCreator, objectTreeDeps) } func prepareTreeContext(t *testing.T, aclList list.AclList) testTreeContext { - changeCreator := &mockChangeCreator{} - treeStorage := changeCreator.createNewTreeStorage("0", aclList.Head().Id) - root, _ := treeStorage.Root() - 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) + changeCreator := NewMockChangeCreator() + treeStorage := changeCreator.CreateNewTreeStorage("0", aclList.Head().Id) + objTree, err := BuildTestableTree(aclList, treeStorage) require.NoError(t, err, "building tree should be without error") // check tree iterate @@ -140,7 +62,6 @@ func prepareTreeContext(t *testing.T, aclList list.AclList) testTreeContext { return testTreeContext{ aclList: aclList, treeStorage: treeStorage, - changeBuilder: changeBuilder, changeCreator: changeCreator, objTree: objTree, } @@ -156,8 +77,8 @@ func TestObjectTree(t *testing.T) { objTree := ctx.objTree rawChanges := []*treechangeproto.RawTreeChangeWithId{ - changeCreator.createRaw("1", aclList.Head().Id, "0", false, "0"), - changeCreator.createRaw("2", aclList.Head().Id, "0", false, "1"), + changeCreator.CreateRaw("1", aclList.Head().Id, "0", false, "0"), + changeCreator.CreateRaw("2", aclList.Head().Id, "0", false, "1"), } payload := RawChangesPayload{ NewHeads: []string{rawChanges[len(rawChanges)-1].Id}, @@ -201,7 +122,7 @@ func TestObjectTree(t *testing.T) { objTree := ctx.objTree rawChanges := []*treechangeproto.RawTreeChangeWithId{ - changeCreator.createRaw("0", aclList.Head().Id, "", true, ""), + changeCreator.CreateRaw("0", aclList.Head().Id, "", true, ""), } payload := RawChangesPayload{ NewHeads: []string{rawChanges[len(rawChanges)-1].Id}, @@ -225,7 +146,33 @@ func TestObjectTree(t *testing.T) { objTree := ctx.objTree 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{ NewHeads: []string{rawChanges[len(rawChanges)-1].Id}, @@ -251,10 +198,10 @@ func TestObjectTree(t *testing.T) { objTree := ctx.objTree rawChanges := []*treechangeproto.RawTreeChangeWithId{ - 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, "3", false, "3"), + 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, "3", false, "3"), } payload := RawChangesPayload{ NewHeads: []string{rawChanges[len(rawChanges)-1].Id}, @@ -301,9 +248,9 @@ func TestObjectTree(t *testing.T) { objTree := ctx.objTree rawChanges := []*treechangeproto.RawTreeChangeWithId{ - 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("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"), } payload := RawChangesPayload{ NewHeads: []string{rawChanges[len(rawChanges)-1].Id}, @@ -325,12 +272,12 @@ func TestObjectTree(t *testing.T) { objTree := ctx.objTree rawChanges := []*treechangeproto.RawTreeChangeWithId{ - 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"), + 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"), } payload := RawChangesPayload{ @@ -404,13 +351,13 @@ func TestObjectTree(t *testing.T) { objTree := ctx.objTree rawChanges := []*treechangeproto.RawTreeChangeWithId{ - 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("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"), // 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{ @@ -485,9 +432,9 @@ func TestObjectTree(t *testing.T) { objTree := ctx.objTree rawChanges := []*treechangeproto.RawTreeChangeWithId{ - 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("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"), } payload := RawChangesPayload{ NewHeads: []string{rawChanges[len(rawChanges)-1].Id}, @@ -499,9 +446,9 @@ func TestObjectTree(t *testing.T) { require.Equal(t, "3", objTree.Root().Id) rawChanges = []*treechangeproto.RawTreeChangeWithId{ - 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"), + 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"), } payload = RawChangesPayload{ NewHeads: []string{rawChanges[len(rawChanges)-1].Id}, @@ -545,12 +492,12 @@ func TestObjectTree(t *testing.T) { changeCreator, deps := prepareTreeDeps(aclList) rawChanges := []*treechangeproto.RawTreeChangeWithId{ - 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"), + 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"), } deps.treeStorage.TransactionAdd(rawChanges, []string{"6"}) hTree, err := buildHistoryTree(deps, HistoryTreeParams{ @@ -576,12 +523,12 @@ func TestObjectTree(t *testing.T) { changeCreator, deps := prepareTreeDeps(aclList) rawChanges := []*treechangeproto.RawTreeChangeWithId{ - 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"), + 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"), } deps.treeStorage.TransactionAdd(rawChanges, []string{"6"}) hTree, err := buildHistoryTree(deps, HistoryTreeParams{ @@ -623,4 +570,40 @@ func TestObjectTree(t *testing.T) { assert.Equal(t, []string{"0"}, iterChangesId) 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) + }) } diff --git a/commonspace/object/tree/objecttree/objecttreefactory.go b/commonspace/object/tree/objecttree/objecttreefactory.go index b3b757fb..0ab51ec1 100644 --- a/commonspace/object/tree/objecttree/objecttreefactory.go +++ b/commonspace/object/tree/objecttree/objecttreefactory.go @@ -33,7 +33,9 @@ type objectTreeDeps struct { aclList list.AclList } -func defaultObjectTreeDeps( +var defaultObjectTreeDeps = verifiableTreeDeps + +func verifiableTreeDeps( rootChange *treechangeproto.RawTreeChangeWithId, treeStorage treestorage.TreeStorage, aclList list.AclList) objectTreeDeps { @@ -53,7 +55,7 @@ func nonVerifiableTreeDeps( rootChange *treechangeproto.RawTreeChangeWithId, treeStorage treestorage.TreeStorage, aclList list.AclList) objectTreeDeps { - changeBuilder := &nonVerifiableChangeBuilder{NewChangeBuilder(nil, rootChange)} + changeBuilder := &nonVerifiableChangeBuilder{NewChangeBuilder(newMockKeyStorage(), rootChange)} treeBuilder := newTreeBuilder(treeStorage, changeBuilder) return objectTreeDeps{ changeBuilder: changeBuilder, diff --git a/commonspace/object/tree/objecttree/objecttreevalidator.go b/commonspace/object/tree/objecttree/objecttreevalidator.go index 937dcc96..e2816246 100644 --- a/commonspace/object/tree/objecttree/objecttreevalidator.go +++ b/commonspace/object/tree/objecttree/objecttreevalidator.go @@ -1,10 +1,12 @@ package objecttree import ( + "context" "fmt" "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/tree/treestorage" + "github.com/anytypeio/any-sync/util/slice" ) 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) { - treeStorage, err := treestorage.NewInMemoryTreeStorage(payload.RootRawChange, payload.Heads, payload.Changes) + treeStorage, err := treestorage.NewInMemoryTreeStorage(payload.RootRawChange, []string{payload.RootRawChange.Id}, nil) if err != nil { return } - - _, err = BuildObjectTree(treeStorage, aclList) + tree, 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 } diff --git a/commonspace/object/tree/objecttree/testutils.go b/commonspace/object/tree/objecttree/testutils.go index 61cd0fe7..6385b9f7 100644 --- a/commonspace/object/tree/objecttree/testutils.go +++ b/commonspace/object/tree/objecttree/testutils.go @@ -1,11 +1,66 @@ package objecttree import ( + "fmt" "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/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{} 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) { root, _ := treeStorage.Root() changeBuilder := &nonVerifiableChangeBuilder{ - ChangeBuilder: NewChangeBuilder(nil, root), + ChangeBuilder: NewChangeBuilder(newMockKeyStorage(), root), } deps := objectTreeDeps{ changeBuilder: changeBuilder, diff --git a/commonspace/object/tree/synctree/syncclient_test.go b/commonspace/object/tree/synctree/syncclient_test.go index 086cb883..73db3f42 100644 --- a/commonspace/object/tree/synctree/syncclient_test.go +++ b/commonspace/object/tree/synctree/syncclient_test.go @@ -29,10 +29,11 @@ type processMsg struct { } type msgDescription struct { - name string - from string - to string - heads []string + name string + from string + to string + heads []string + changes []*treechangeproto.RawTreeChangeWithId } func (p *processMsg) description() (descr msgDescription) { @@ -50,14 +51,17 @@ func (p *processMsg) description() (descr msgDescription) { cnt := unmarshalled.GetContent().GetHeadUpdate() descr.name = "HeadUpdate" descr.heads = cnt.Heads + descr.changes = unmarshalled.GetContent().GetHeadUpdate().Changes case unmarshalled.GetContent().GetFullSyncRequest() != nil: cnt := unmarshalled.GetContent().GetFullSyncRequest() descr.name = "FullSyncRequest" descr.heads = cnt.Heads + descr.changes = unmarshalled.GetContent().GetFullSyncRequest().Changes case unmarshalled.GetContent().GetFullSyncResponse() != nil: cnt := unmarshalled.GetContent().GetFullSyncResponse() descr.name = "FullSyncResponse" descr.heads = cnt.Heads + descr.changes = unmarshalled.GetContent().GetFullSyncResponse().Changes } return } @@ -322,7 +326,7 @@ func (p *processFixture) stop() { p.wg.Wait() } -func TestSend_EmptyClient(t *testing.T) { +func TestSend_EmptyClientGetsFullHistory(t *testing.T) { treeId := "treeId" spaceId := "spaceId" keys, err := accountdata.NewRandom() @@ -345,7 +349,7 @@ func TestSend_EmptyClient(t *testing.T) { fx.handlers["peer1"].sendRawChanges(context.Background(), objecttree.RawChangesPayload{ NewHeads: nil, RawChanges: []*treechangeproto.RawTreeChangeWithId{ - changeCreator.CreateRaw("1", aclList.Id(), treeId, false, treeId), + changeCreator.CreateRaw("1", aclList.Id(), treeId, true, treeId), }, }) time.Sleep(100 * time.Millisecond) @@ -357,9 +361,17 @@ func TestSend_EmptyClient(t *testing.T) { require.Equal(t, firstHeads, secondHeads) require.Equal(t, []string{"1"}, firstHeads) logMsgs := fx.log.batcher.GetAll() + + var fullResponseMsg *processMsg 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) {