1
0
Fork 0
mirror of https://github.com/anyproto/any-sync.git synced 2025-06-08 05:57:03 +09:00

Fix commonspace tests

This commit is contained in:
mcrakhman 2025-02-05 13:09:32 +01:00
parent daefdae641
commit 4de44b020a
No known key found for this signature in database
GPG key ID: DED12CFEF5B8396B
6 changed files with 357 additions and 343 deletions

View file

@ -1,268 +1,224 @@
package commonspace package commonspace
// import (
//func addIncorrectSnapshot(settingsObject settings.SettingsObject, acc *accountdata.AccountKeys, partialIds map[string]struct{}, newId string) (err error) { "context"
// factory := settingsstate.NewChangeFactory() "crypto/rand"
// bytes, err := factory.CreateObjectDeleteChange(newId, &settingsstate.State{DeletedIds: partialIds}, true) "testing"
// if err != nil { "time"
// return
// } anystore "github.com/anyproto/any-store"
// ch, err := settingsObject.PrepareChange(objecttree.SignableChangeContent{ "github.com/stretchr/testify/require"
// Data: bytes,
// Key: acc.SignKey, "github.com/anyproto/any-sync/commonspace/object/accountdata"
// IsSnapshot: true, "github.com/anyproto/any-sync/commonspace/object/tree/objecttree"
// IsEncrypted: false, "github.com/anyproto/any-sync/commonspace/spacestorage"
// Timestamp: time.Now().Unix(), "github.com/anyproto/any-sync/commonspace/syncstatus"
// }) "github.com/anyproto/any-sync/util/crypto"
// if err != nil { )
// return
// } func createTree(t *testing.T, ctx context.Context, spc Space, acc *accountdata.AccountKeys) string {
// res, err := settingsObject.AddRawChanges(context.Background(), objecttree.RawChangesPayload{ bytes := make([]byte, 32)
// NewHeads: []string{ch.Id}, rand.Read(bytes)
// RawChanges: []*treechangeproto.RawTreeChangeWithId{ch}, doc, err := spc.TreeBuilder().CreateTree(ctx, objecttree.ObjectTreeCreatePayload{
// }) PrivKey: acc.SignKey,
// if err != nil { ChangeType: "some",
// return SpaceId: spc.Id(),
// } IsEncrypted: false,
// if res.Mode != objecttree.Rebuild { Seed: bytes,
// return fmt.Errorf("incorrect mode: %d", res.Mode) Timestamp: time.Now().Unix(),
// } })
// return require.NoError(t, err)
//} tr, err := spc.TreeBuilder().PutTree(ctx, doc, nil)
// require.NoError(t, err)
//func TestSpaceDeleteIds(t *testing.T) { tr.Close()
// fx := newFixture(t) return tr.Id()
// acc := fx.account.Account() }
// rk := crypto.NewAES()
// privKey, _, _ := crypto.GenerateRandomEd25519KeyPair() type storeSetter interface {
// ctx := context.Background() SetStore(id string, store anystore.DB)
// totalObjs := 1500 }
//
// // creating space func TestSpaceDeleteIdsMarkDeleted(t *testing.T) {
// sp, err := fx.spaceService.CreateSpace(ctx, SpaceCreatePayload{ fx := newFixture(t)
// SigningKey: acc.SignKey, acc := fx.account.Account()
// SpaceType: "type", rk := crypto.NewAES()
// ReadKey: rk, privKey, _, _ := crypto.GenerateRandomEd25519KeyPair()
// MetadataKey: privKey, ctx := context.Background()
// ReplicationKey: 10, totalObjs := 1000
// MasterKey: acc.PeerKey,
// }) // creating space
// require.NoError(t, err) sp, err := fx.spaceService.CreateSpace(ctx, SpaceCreatePayload{
// require.NotNil(t, sp) SigningKey: acc.SignKey,
// SpaceType: "type",
// // initializing space ReadKey: rk,
// spc, err := fx.spaceService.NewSpace(ctx, sp, Deps{TreeSyncer: mockTreeSyncer{}, SyncStatus: syncstatus.NewNoOpSyncStatus()}) MetadataKey: privKey,
// require.NoError(t, err) ReplicationKey: 10,
// require.NotNil(t, spc) MasterKey: acc.PeerKey,
// // adding space to tree manager })
// fx.treeManager.space = spc require.NoError(t, err)
// err = spc.Init(ctx) require.NotNil(t, sp)
// require.NoError(t, err)
// close(fx.treeManager.waitLoad) // initializing space
// spc, err := fx.spaceService.NewSpace(ctx, sp, Deps{TreeSyncer: mockTreeSyncer{}, SyncStatus: syncstatus.NewNoOpSyncStatus()})
// var ids []string require.NoError(t, err)
// for i := 0; i < totalObjs; i++ { require.NotNil(t, spc)
// // creating a tree // adding space to tree manager
// bytes := make([]byte, 32) fx.treeManager.space = spc
// rand.Read(bytes) err = spc.Init(ctx)
// doc, err := spc.TreeBuilder().CreateTree(ctx, objecttree.ObjectTreeCreatePayload{ require.NoError(t, err)
// PrivKey: acc.SignKey, close(fx.treeManager.waitLoad)
// ChangeType: "some",
// SpaceId: spc.Id(), anyStore := spc.Storage().AnyStore()
// IsEncrypted: false, newStore := objecttree.CopyStore(ctx, t, anyStore.(objecttree.TestStore), "store")
// Seed: bytes,
// Timestamp: time.Now().Unix(), var ids []string
// }) for i := 0; i < totalObjs; i++ {
// require.NoError(t, err) id := createTree(t, ctx, spc, acc)
// tr, err := spc.TreeBuilder().PutTree(ctx, doc, nil) ids = append(ids, id)
// require.NoError(t, err) }
// ids = append(ids, tr.Id()) // deleting trees, this will prepare the document to have all the deletion changes
// tr.Close() for _, id := range ids {
// } err = spc.DeleteTree(ctx, id)
// // deleting trees require.NoError(t, err)
// for _, id := range ids { }
// err = spc.DeleteTree(ctx, id) settingsId := spc.Storage().StateStorage().SettingsId()
// require.NoError(t, err) settingsStorage, err := spc.Storage().TreeStorage(ctx, settingsId)
// } require.NoError(t, err)
// time.Sleep(3 * time.Second) var allChanges []objecttree.StorageChange
// spc.Close() err = settingsStorage.GetAfterOrder(ctx, "", func(ctx context.Context, change objecttree.StorageChange) (shouldContinue bool, err error) {
// require.Equal(t, len(ids), len(fx.treeManager.deletedIds)) rawCh := make([]byte, len(change.RawChange))
//} copy(rawCh, change.RawChange)
// change.RawChange = rawCh
//func createTree(t *testing.T, ctx context.Context, spc Space, acc *accountdata.AccountKeys) string { allChanges = append(allChanges, change)
// bytes := make([]byte, 32) return true, nil
// rand.Read(bytes) })
// doc, err := spc.TreeBuilder().CreateTree(ctx, objecttree.ObjectTreeCreatePayload{ require.NoError(t, err)
// PrivKey: acc.SignKey, heads, err := settingsStorage.Heads(ctx)
// ChangeType: "some", require.NoError(t, err)
// SpaceId: spc.Id(), commonSnapshot, err := settingsStorage.CommonSnapshot(ctx)
// IsEncrypted: false, require.NoError(t, err)
// Seed: bytes, newStorage, err := spacestorage.New(ctx, spc.Id(), newStore)
// Timestamp: time.Now().Unix(), require.NoError(t, err)
// }) newSettingsStorage, err := newStorage.TreeStorage(ctx, settingsId)
// require.NoError(t, err) require.NoError(t, err)
// tr, err := spc.TreeBuilder().PutTree(ctx, doc, nil) err = newSettingsStorage.AddAllNoError(ctx, allChanges, heads, commonSnapshot)
// require.NoError(t, err) require.NoError(t, err)
// tr.Close() err = spc.Close()
// return tr.Id() require.NoError(t, err)
//} time.Sleep(100 * time.Millisecond)
// storeSetter := fx.storageProvider.(storeSetter)
//func TestSpaceDeleteIdsIncorrectSnapshot(t *testing.T) { storeSetter.SetStore(sp, newStore)
// fx := newFixture(t) spc, err = fx.spaceService.NewSpace(ctx, sp, Deps{TreeSyncer: mockTreeSyncer{}, SyncStatus: syncstatus.NewNoOpSyncStatus()})
// acc := fx.account.Account() require.NoError(t, err)
// rk := crypto.NewAES() require.NotNil(t, spc)
// privKey, _, _ := crypto.GenerateRandomEd25519KeyPair() waitTest := make(chan struct{})
// ctx := context.Background() fx.treeManager.wait = true
// totalObjs := 1500 fx.treeManager.space = spc
// partialObjs := 300 fx.treeManager.condFunc = func() {
// if len(fx.treeManager.markedIds) == len(ids) {
// // creating space close(waitTest)
// sp, err := fx.spaceService.CreateSpace(ctx, SpaceCreatePayload{ }
// SigningKey: acc.SignKey, }
// SpaceType: "type", fx.treeManager.waitLoad = make(chan struct{})
// ReadKey: rk, fx.treeManager.deletedIds = nil
// MetadataKey: privKey, fx.treeManager.markedIds = nil
// ReplicationKey: 10, err = spc.Init(ctx)
// MasterKey: acc.PeerKey, require.NoError(t, err)
// }) close(fx.treeManager.waitLoad)
// require.NoError(t, err)
// require.NotNil(t, sp) <-waitTest
// require.Equal(t, len(ids), len(fx.treeManager.markedIds))
// // initializing space require.Zero(t, len(fx.treeManager.deletedIds))
// spc, err := fx.spaceService.NewSpace(ctx, sp, Deps{TreeSyncer: mockTreeSyncer{}, SyncStatus: syncstatus.NewNoOpSyncStatus()}) }
// require.NoError(t, err)
// require.NotNil(t, spc) func TestSpaceDeleteIds(t *testing.T) {
// // adding space to tree manager fx := newFixture(t)
// fx.treeManager.space = spc acc := fx.account.Account()
// err = spc.Init(ctx) rk := crypto.NewAES()
// close(fx.treeManager.waitLoad) privKey, _, _ := crypto.GenerateRandomEd25519KeyPair()
// require.NoError(t, err) ctx := context.Background()
// totalObjs := 1000
// settingsObject := spc.(*space).app.MustComponent(settings.CName).(settings.Settings).SettingsObject()
// var ids []string // creating space
// for i := 0; i < totalObjs; i++ { sp, err := fx.spaceService.CreateSpace(ctx, SpaceCreatePayload{
// id := createTree(t, ctx, spc, acc) SigningKey: acc.SignKey,
// ids = append(ids, id) SpaceType: "type",
// } ReadKey: rk,
// // copying storage, so we will have all the trees locally MetadataKey: privKey,
// inmemory := spc.Storage().(*spacestorage.InMemorySpaceStorage) ReplicationKey: 10,
// storageCopy := inmemory.CopyStorage() MasterKey: acc.PeerKey,
// treesCopy := inmemory.AllTrees() })
// require.NoError(t, err)
// // deleting trees require.NotNil(t, sp)
// for _, id := range ids {
// err = spc.DeleteTree(ctx, id) // initializing space
// require.NoError(t, err) spc, err := fx.spaceService.NewSpace(ctx, sp, Deps{TreeSyncer: mockTreeSyncer{}, SyncStatus: syncstatus.NewNoOpSyncStatus()})
// } require.NoError(t, err)
// mapIds := map[string]struct{}{} require.NotNil(t, spc)
// for _, id := range ids[:partialObjs] { // adding space to tree manager
// mapIds[id] = struct{}{} fx.treeManager.space = spc
// } err = spc.Init(ctx)
// // adding snapshot that breaks the state require.NoError(t, err)
// err = addIncorrectSnapshot(settingsObject, acc, mapIds, ids[partialObjs]) close(fx.treeManager.waitLoad)
// require.NoError(t, err)
// // copying the contents of the settings tree var ids []string
// treesCopy[settingsObject.Id()] = settingsObject.Storage() for i := 0; i < totalObjs; i++ {
// storageCopy.SetTrees(treesCopy) id := createTree(t, ctx, spc, acc)
// spc.Close() ids = append(ids, id)
// time.Sleep(100 * time.Millisecond) }
// // now we replace the storage, so the trees are back, but the settings object says that they are deleted // copying storage, so we will have the same contents, except for empty trees
// fx.storageProvider.(*spacestorage.InMemorySpaceStorageProvider).SetStorage(storageCopy) anyStore := spc.Storage().AnyStore()
// newStore := objecttree.CopyStore(ctx, t, anyStore.(objecttree.TestStore), "store")
// spc, err = fx.spaceService.NewSpace(ctx, sp, Deps{TreeSyncer: mockTreeSyncer{}, SyncStatus: syncstatus.NewNoOpSyncStatus()}) // deleting trees, this will prepare the document to have all the deletion changes
// require.NoError(t, err) for _, id := range ids {
// require.NotNil(t, spc) err = spc.DeleteTree(ctx, id)
// fx.treeManager.waitLoad = make(chan struct{}) require.NoError(t, err)
// fx.treeManager.space = spc }
// fx.treeManager.deletedIds = nil settingsId := spc.Storage().StateStorage().SettingsId()
// fx.treeManager.wait = true settingsStorage, err := spc.Storage().TreeStorage(ctx, settingsId)
// err = spc.Init(ctx) require.NoError(t, err)
// require.NoError(t, err) var allChanges []objecttree.StorageChange
// close(fx.treeManager.waitLoad) err = settingsStorage.GetAfterOrder(ctx, "", func(ctx context.Context, change objecttree.StorageChange) (shouldContinue bool, err error) {
// rawCh := make([]byte, len(change.RawChange))
// // waiting until everything is deleted copy(rawCh, change.RawChange)
// time.Sleep(3 * time.Second) change.RawChange = rawCh
// require.Equal(t, len(ids), len(fx.treeManager.deletedIds)) allChanges = append(allChanges, change)
// return true, nil
// // checking that new snapshot will contain all the changes })
// settingsObject = spc.(*space).app.MustComponent(settings.CName).(settings.Settings).SettingsObject() require.NoError(t, err)
// settings.DoSnapshot = func(treeLen int) bool { heads, err := settingsStorage.Heads(ctx)
// return true require.NoError(t, err)
// } commonSnapshot, err := settingsStorage.CommonSnapshot(ctx)
// id := createTree(t, ctx, spc, acc) require.NoError(t, err)
// err = spc.DeleteTree(ctx, id) newStorage, err := spacestorage.New(ctx, spc.Id(), newStore)
// require.NoError(t, err) require.NoError(t, err)
// delIds := settingsObject.Root().Model.(*spacesyncproto.SettingsData).Snapshot.DeletedIds newSettingsStorage, err := newStorage.TreeStorage(ctx, settingsId)
// require.Equal(t, totalObjs+1, len(delIds)) require.NoError(t, err)
//} err = newSettingsStorage.AddAllNoError(ctx, allChanges, heads, commonSnapshot)
// require.NoError(t, err)
//func TestSpaceDeleteIdsMarkDeleted(t *testing.T) { err = spc.Close()
// fx := newFixture(t) require.NoError(t, err)
// acc := fx.account.Account() time.Sleep(100 * time.Millisecond)
// rk := crypto.NewAES() storeSetter := fx.storageProvider.(storeSetter)
// privKey, _, _ := crypto.GenerateRandomEd25519KeyPair() storeSetter.SetStore(sp, newStore)
// ctx := context.Background() spc, err = fx.spaceService.NewSpace(ctx, sp, Deps{TreeSyncer: mockTreeSyncer{}, SyncStatus: syncstatus.NewNoOpSyncStatus()})
// totalObjs := 1500 require.NoError(t, err)
// require.NotNil(t, spc)
// // creating space waitTest := make(chan struct{})
// sp, err := fx.spaceService.CreateSpace(ctx, SpaceCreatePayload{ fx.treeManager.wait = true
// SigningKey: acc.SignKey, fx.treeManager.space = spc
// SpaceType: "type", fx.treeManager.waitLoad = make(chan struct{})
// ReadKey: rk, fx.treeManager.deletedIds = nil
// MetadataKey: privKey, fx.treeManager.markedIds = nil
// ReplicationKey: 10, fx.treeManager.condFunc = func() {
// MasterKey: acc.PeerKey, if len(fx.treeManager.deletedIds) == len(ids) {
// }) close(waitTest)
// require.NoError(t, err) }
// require.NotNil(t, sp) }
// err = spc.Init(ctx)
// // initializing space require.NoError(t, err)
// spc, err := fx.spaceService.NewSpace(ctx, sp, Deps{TreeSyncer: mockTreeSyncer{}, SyncStatus: syncstatus.NewNoOpSyncStatus()}) close(fx.treeManager.waitLoad)
// require.NoError(t, err) // waiting until everything is deleted
// require.NotNil(t, spc) <-waitTest
// // adding space to tree manager require.Equal(t, len(ids), len(fx.treeManager.deletedIds))
// fx.treeManager.space = spc }
// err = spc.Init(ctx)
// require.NoError(t, err)
// close(fx.treeManager.waitLoad)
//
// settingsObject := spc.(*space).app.MustComponent(settings.CName).(settings.Settings).SettingsObject()
// var ids []string
// for i := 0; i < totalObjs; i++ {
// id := createTree(t, ctx, spc, acc)
// ids = append(ids, id)
// }
// // copying storage, so we will have the same contents, except for empty trees
// inmemory := spc.Storage().(*spacestorage.InMemorySpaceStorage)
// storageCopy := inmemory.CopyStorage()
//
// // deleting trees, this will prepare the document to have all the deletion changes
// for _, id := range ids {
// err = spc.DeleteTree(ctx, id)
// require.NoError(t, err)
// }
// treesMap := map[string]treestorage.TreeStorage{}
// // copying the contents of the settings tree
// treesMap[settingsObject.Id()] = settingsObject.Storage()
// storageCopy.SetTrees(treesMap)
// spc.Close()
// time.Sleep(100 * time.Millisecond)
// // now we replace the storage, so the trees are back, but the settings object says that they are deleted
// fx.storageProvider.(*spacestorage.InMemorySpaceStorageProvider).SetStorage(storageCopy)
//
// spc, err = fx.spaceService.NewSpace(ctx, sp, Deps{TreeSyncer: mockTreeSyncer{}, SyncStatus: syncstatus.NewNoOpSyncStatus()})
// require.NoError(t, err)
// require.NotNil(t, spc)
// fx.treeManager.space = spc
// fx.treeManager.waitLoad = make(chan struct{})
// fx.treeManager.deletedIds = nil
// fx.treeManager.markedIds = nil
// err = spc.Init(ctx)
// require.NoError(t, err)
// close(fx.treeManager.waitLoad)
//
// // waiting until everything is deleted
// time.Sleep(3 * time.Second)
// require.Equal(t, len(ids), len(fx.treeManager.markedIds))
// require.Zero(t, len(fx.treeManager.deletedIds))
//}

View file

@ -4,10 +4,7 @@ import (
"context" "context"
"errors" "errors"
"fmt" "fmt"
"io"
"io/fs"
"math/rand" "math/rand"
"os"
"path/filepath" "path/filepath"
"testing" "testing"
"time" "time"
@ -125,12 +122,6 @@ type testTreeContext struct {
objTree ObjectTree objTree ObjectTree
} }
type testStore struct {
anystore.DB
path string
errAdd bool
}
func createStore(ctx context.Context, t *testing.T) anystore.DB { func createStore(ctx context.Context, t *testing.T) anystore.DB {
return createNamedStore(ctx, t, "changes.db") return createNamedStore(ctx, t, "changes.db")
} }
@ -144,9 +135,9 @@ func createNamedStore(ctx context.Context, t *testing.T, name string) anystore.D
require.NoError(t, err) require.NoError(t, err)
unix.Rmdir(path) unix.Rmdir(path)
}) })
return testStore{ return TestStore{
DB: db, DB: db,
path: path, Path: path,
} }
} }
@ -159,68 +150,6 @@ func allChanges(ctx context.Context, t *testing.T, store Storage) (res []*treech
return return
} }
func copyFolder(src string, dst string) error {
return filepath.WalkDir(src, func(path string, d fs.DirEntry, err error) error {
if err != nil {
return err
}
relPath, err := filepath.Rel(src, path)
if err != nil {
return err
}
dstPath := filepath.Join(dst, relPath)
if d.IsDir() {
info, err := d.Info()
if err != nil {
return err
}
return os.MkdirAll(dstPath, info.Mode())
} else {
return copyFile(path, dstPath, d)
}
})
}
func copyFile(src, dst string, d fs.DirEntry) error {
srcFile, err := os.Open(src)
if err != nil {
return err
}
defer srcFile.Close()
dstFile, err := os.Create(dst)
if err != nil {
return err
}
defer dstFile.Close()
_, err = io.Copy(dstFile, srcFile)
if err != nil {
return err
}
info, err := d.Info()
if err != nil {
return err
}
return os.Chmod(dst, info.Mode())
}
func copyStore(ctx context.Context, t *testing.T, store testStore, name string) anystore.DB {
err := store.Checkpoint(ctx, true)
require.NoError(t, err)
newPath := filepath.Join(t.TempDir(), name)
err = copyFolder(store.path, newPath)
require.NoError(t, err)
db, err := anystore.Open(ctx, newPath, nil)
require.NoError(t, err)
t.Cleanup(func() {
err := db.Close()
require.NoError(t, err)
})
return testStore{
DB: db,
path: newPath,
}
}
func prepareAclList(t *testing.T) (list.AclList, *accountdata.AccountKeys) { func prepareAclList(t *testing.T) (list.AclList, *accountdata.AccountKeys) {
randKeys, err := accountdata.NewRandom() randKeys, err := accountdata.NewRandom()
require.NoError(t, err) require.NoError(t, err)
@ -378,7 +307,7 @@ func TestObjectTree(t *testing.T) {
DataType: mockDataType, DataType: mockDataType,
}) })
require.NoError(t, err) require.NoError(t, err)
storeB := copyStore(ctx, t, storeA.(testStore), "b") storeB := CopyStore(ctx, t, storeA.(TestStore), "b")
bHeadsStorage, err := headstorage.New(ctx, storeB) bHeadsStorage, err := headstorage.New(ctx, storeB)
require.NoError(t, err) require.NoError(t, err)
bStore, err := NewStorage(ctx, root.Id, bHeadsStorage, storeB) bStore, err := NewStorage(ctx, root.Id, bHeadsStorage, storeB)
@ -506,7 +435,7 @@ func TestObjectTree(t *testing.T) {
DataType: mockDataType, DataType: mockDataType,
}) })
require.NoError(t, err) require.NoError(t, err)
storeB := copyStore(ctx, t, storeA.(testStore), "b") storeB := CopyStore(ctx, t, storeA.(TestStore), "b")
bHeadsStorage, err := headstorage.New(ctx, storeB) bHeadsStorage, err := headstorage.New(ctx, storeB)
require.NoError(t, err) require.NoError(t, err)
bStore, err := NewStorage(ctx, root.Id, bHeadsStorage, storeB) bStore, err := NewStorage(ctx, root.Id, bHeadsStorage, storeB)

View file

@ -59,6 +59,7 @@ type Storage interface {
Get(ctx context.Context, id string) (StorageChange, error) Get(ctx context.Context, id string) (StorageChange, error)
GetAfterOrder(ctx context.Context, orderId string, iter StorageIterator) error GetAfterOrder(ctx context.Context, orderId string, iter StorageIterator) error
AddAll(ctx context.Context, changes []StorageChange, heads []string, commonSnapshot string) error AddAll(ctx context.Context, changes []StorageChange, heads []string, commonSnapshot string) error
AddAllNoError(ctx context.Context, changes []StorageChange, heads []string, commonSnapshot string) error
Delete(ctx context.Context) error Delete(ctx context.Context) error
Close() error Close() error
} }
@ -235,6 +236,36 @@ func (s *storage) AddAll(ctx context.Context, changes []StorageChange, heads []s
return tx.Commit() return tx.Commit()
} }
func (s *storage) AddAllNoError(ctx context.Context, changes []StorageChange, heads []string, commonSnapshot string) error {
arena := s.arena
defer arena.Reset()
tx, err := s.store.WriteTx(ctx)
if err != nil {
return fmt.Errorf("failed to create write tx: %w", err)
}
for _, ch := range changes {
ch.TreeId = s.id
newVal := newStorageChangeValue(ch, arena)
err = s.changesColl.Insert(tx.Context(), newVal)
arena.Reset()
if err != nil && !errors.Is(err, anystore.ErrDocExists) {
tx.Rollback()
return err
}
}
update := headstorage.HeadsUpdate{
Id: s.id,
Heads: heads,
CommonSnapshot: &commonSnapshot,
}
err = s.headStorage.UpdateEntryTx(tx.Context(), update)
if err != nil {
tx.Rollback()
return err
}
return tx.Commit()
}
func (s *storage) Delete(ctx context.Context) error { func (s *storage) Delete(ctx context.Context) error {
tx, err := s.store.WriteTx(ctx) tx, err := s.store.WriteTx(ctx)
if err != nil { if err != nil {

View file

@ -3,6 +3,10 @@ package objecttree
import ( import (
"context" "context"
"fmt" "fmt"
"io"
"io/fs"
"os"
"path/filepath"
"testing" "testing"
anystore "github.com/anyproto/any-store" anystore "github.com/anyproto/any-store"
@ -175,3 +179,71 @@ func (c *MockChangeCreator) CreateNewTreeStorage(t *testing.T, treeId, aclHeadId
Storage: storage, Storage: storage,
} }
} }
func copyFolder(src string, dst string) error {
return filepath.WalkDir(src, func(path string, d fs.DirEntry, err error) error {
if err != nil {
return err
}
relPath, err := filepath.Rel(src, path)
if err != nil {
return err
}
dstPath := filepath.Join(dst, relPath)
if d.IsDir() {
info, err := d.Info()
if err != nil {
return err
}
return os.MkdirAll(dstPath, info.Mode())
} else {
return copyFile(path, dstPath, d)
}
})
}
func copyFile(src, dst string, d fs.DirEntry) error {
srcFile, err := os.Open(src)
if err != nil {
return err
}
defer srcFile.Close()
dstFile, err := os.Create(dst)
if err != nil {
return err
}
defer dstFile.Close()
_, err = io.Copy(dstFile, srcFile)
if err != nil {
return err
}
info, err := d.Info()
if err != nil {
return err
}
return os.Chmod(dst, info.Mode())
}
type TestStore struct {
anystore.DB
Path string
errAdd bool
}
func CopyStore(ctx context.Context, t *testing.T, store TestStore, name string) anystore.DB {
err := store.Checkpoint(ctx, true)
require.NoError(t, err)
newPath := filepath.Join(t.TempDir(), name)
err = copyFolder(store.Path, newPath)
require.NoError(t, err)
db, err := anystore.Open(ctx, newPath, nil)
require.NoError(t, err)
t.Cleanup(func() {
err := db.Close()
require.NoError(t, err)
})
return TestStore{
DB: db,
Path: newPath,
}
}

View file

@ -355,6 +355,7 @@ type testTreeManager struct {
accService accountService.Service accService accountService.Service
deletedIds []string deletedIds []string
markedIds []string markedIds []string
condFunc func()
treesToPut map[string]treestorage.TreeStorageCreatePayload treesToPut map[string]treestorage.TreeStorageCreatePayload
wait bool wait bool
waitLoad chan struct{} waitLoad chan struct{}
@ -376,6 +377,9 @@ func (t *testTreeManager) MarkTreeDeleted(ctx context.Context, spaceId, treeId s
t.mx.Lock() t.mx.Lock()
defer t.mx.Unlock() defer t.mx.Unlock()
t.markedIds = append(t.markedIds, treeId) t.markedIds = append(t.markedIds, treeId)
if t.condFunc != nil {
t.condFunc()
}
return nil return nil
} }
@ -470,6 +474,9 @@ func (t *testTreeManager) DeleteTree(ctx context.Context, spaceId, treeId string
} }
t.deletedIds = append(t.deletedIds, treeId) t.deletedIds = append(t.deletedIds, treeId)
_, err = t.cache.Remove(ctx, treeId) _, err = t.cache.Remove(ctx, treeId)
if t.condFunc != nil {
t.condFunc()
}
return nil return nil
} }

View file

@ -9,11 +9,13 @@ import (
"golang.org/x/sys/unix" "golang.org/x/sys/unix"
"github.com/anyproto/any-sync/app" "github.com/anyproto/any-sync/app"
"github.com/anyproto/any-sync/commonspace/object/tree/objecttree"
"github.com/anyproto/any-sync/commonspace/spacestorage" "github.com/anyproto/any-sync/commonspace/spacestorage"
) )
type spaceStorageProvider struct { type spaceStorageProvider struct {
rootPath string rootPath string
anyStores map[string]anystore.DB
} }
func (s *spaceStorageProvider) Run(ctx context.Context) (err error) { func (s *spaceStorageProvider) Run(ctx context.Context) (err error) {
@ -33,6 +35,12 @@ func (s *spaceStorageProvider) Name() (name string) {
} }
func (s *spaceStorageProvider) WaitSpaceStorage(ctx context.Context, id string) (spacestorage.SpaceStorage, error) { func (s *spaceStorageProvider) WaitSpaceStorage(ctx context.Context, id string) (spacestorage.SpaceStorage, error) {
if s.anyStores == nil {
s.anyStores = make(map[string]anystore.DB)
}
if store, ok := s.anyStores[id]; ok {
return spacestorage.New(ctx, id, store)
}
dbPath := path.Join(s.rootPath, id) dbPath := path.Join(s.rootPath, id)
if _, err := os.Stat(dbPath); err != nil { if _, err := os.Stat(dbPath); err != nil {
return nil, err return nil, err
@ -41,7 +49,18 @@ func (s *spaceStorageProvider) WaitSpaceStorage(ctx context.Context, id string)
if err != nil { if err != nil {
return nil, err return nil, err
} }
return spacestorage.New(ctx, id, db) testStore := objecttree.TestStore{
DB: db,
Path: dbPath,
}
return spacestorage.New(ctx, id, testStore)
}
func (s *spaceStorageProvider) SetStore(id string, store anystore.DB) {
if s.anyStores == nil {
s.anyStores = make(map[string]anystore.DB)
}
s.anyStores[id] = store
} }
func (s *spaceStorageProvider) SpaceExists(id string) bool { func (s *spaceStorageProvider) SpaceExists(id string) bool {