mirror of
https://github.com/anyproto/anytype-heart.git
synced 2025-06-08 05:47:07 +09:00
194 lines
5.7 KiB
Go
194 lines
5.7 KiB
Go
//go:build integration
|
|
|
|
package tests
|
|
|
|
import (
|
|
"os"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/gogo/protobuf/types"
|
|
"github.com/samber/lo"
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
|
|
"github.com/anyproto/anytype-heart/pb"
|
|
"github.com/anyproto/anytype-heart/pkg/lib/bundle"
|
|
"github.com/anyproto/anytype-heart/pkg/lib/pb/model"
|
|
"github.com/anyproto/anytype-heart/tests/blockbuilder"
|
|
"github.com/anyproto/anytype-heart/util/pbtypes"
|
|
)
|
|
|
|
const (
|
|
migrationMnemonicKey = "migration_mnemonic"
|
|
migrationAccounIDKey = "migration_account_id"
|
|
)
|
|
|
|
func newImportSession(t *testing.T, port string) *testSession {
|
|
var s testSession
|
|
|
|
c, err := newClient(port)
|
|
require.NoError(t, err)
|
|
s.ClientCommandsClient = c
|
|
|
|
mnemonic, err := readStringFromCache(migrationMnemonicKey)
|
|
require.NoError(t, err)
|
|
t.Log("your mnemonic:", mnemonic)
|
|
|
|
cctx := s.newCallCtx(t)
|
|
_ = call(cctx, s.WalletRecover, &pb.RpcWalletRecoverRequest{
|
|
Mnemonic: mnemonic,
|
|
RootPath: rootPath,
|
|
})
|
|
|
|
cctx, s.eventReceiver = s.openClientSession(t, mnemonic)
|
|
|
|
accountID, err := readStringFromCache(migrationAccounIDKey)
|
|
require.NoError(t, err)
|
|
t.Log("your account ID:", accountID)
|
|
|
|
return &s
|
|
}
|
|
|
|
func fetchObjects(t *testing.T, s *testSession, ids []string) map[string]*model.ObjectView {
|
|
cctx := s.newCallCtx(t)
|
|
|
|
res := map[string]*model.ObjectView{}
|
|
for _, id := range ids {
|
|
resp := call(cctx, s.ObjectShow, &pb.RpcObjectShowRequest{
|
|
ObjectId: id,
|
|
})
|
|
|
|
res[id] = resp.ObjectView
|
|
}
|
|
return res
|
|
}
|
|
|
|
func createAndExportAccount(t *testing.T) (string, map[string]*model.ObjectView) {
|
|
exportPort := os.Getenv("ANYTYPE_OLD_TEST_GRPC_PORT")
|
|
if exportPort == "" {
|
|
t.Fatal("you must specify ANYTYPE_OLD_TEST_GRPC_PORT env variable")
|
|
}
|
|
|
|
exportSession := newTestSession(t, exportPort, migrationMnemonicKey, migrationAccounIDKey)
|
|
cctx := exportSession.newCallCtx(t)
|
|
|
|
resp := call(cctx, exportSession.ObjectSearch, &pb.RpcObjectSearchRequest{
|
|
Keys: []string{bundle.RelationKeyId.String()},
|
|
})
|
|
|
|
oldObjectIDs := lo.Map(resp.Records, func(r *types.Struct, _ int) string {
|
|
return r.Fields[bundle.RelationKeyId.String()].GetStringValue()
|
|
})
|
|
oldObjects := fetchObjects(t, exportSession, oldObjectIDs)
|
|
|
|
exportResp := call(cctx, exportSession.ObjectListExport, &pb.RpcObjectListExportRequest{
|
|
Path: "/var/anytype_old/",
|
|
Format: pb.RpcObjectListExport_Protobuf,
|
|
Zip: true,
|
|
IncludeArchived: true,
|
|
IncludeFiles: true,
|
|
IncludeNested: true,
|
|
})
|
|
|
|
call(cctx, exportSession.AccountStop, &pb.RpcAccountStopRequest{
|
|
RemoveData: false,
|
|
})
|
|
|
|
return exportResp.Path, oldObjects
|
|
}
|
|
|
|
func TestMigration(t *testing.T) {
|
|
_ = os.RemoveAll(cacheFilename(migrationMnemonicKey))
|
|
_ = os.RemoveAll(cacheFilename(migrationAccounIDKey))
|
|
|
|
exportPath, oldObjects := createAndExportAccount(t)
|
|
|
|
importSession := newImportSession(t, os.Getenv("ANYTYPE_TEST_GRPC_PORT"))
|
|
cctx := importSession.newCallCtx(t)
|
|
|
|
call(cctx, importSession.AccountRecoverFromLegacyExport, &pb.RpcAccountRecoverFromLegacyExportRequest{
|
|
Path: exportPath,
|
|
RootPath: rootPath + "_new",
|
|
})
|
|
|
|
call(cctx, importSession.ObjectImport, &pb.RpcObjectImportRequest{
|
|
Params: &pb.RpcObjectImportRequestParamsOfPbParams{
|
|
PbParams: &pb.RpcObjectImportRequestPbParams{
|
|
Path: []string{exportPath},
|
|
},
|
|
},
|
|
Type: pb.RpcObjectImportRequest_Pb,
|
|
})
|
|
|
|
time.Sleep(1 * time.Minute)
|
|
|
|
resp := call(cctx, importSession.ObjectSearch, &pb.RpcObjectSearchRequest{
|
|
Keys: []string{bundle.RelationKeyId.String(), bundle.RelationKeyOldAnytypeID.String()},
|
|
})
|
|
|
|
filtered := lo.Filter(resp.Records, func(r *types.Struct, _ int) bool {
|
|
return r.Fields[bundle.RelationKeyOldAnytypeID.String()].GetStringValue() != ""
|
|
})
|
|
newIDtoOldID := lo.SliceToMap(filtered, func(r *types.Struct) (string, string) {
|
|
return r.Fields[bundle.RelationKeyId.String()].GetStringValue(), r.Fields[bundle.RelationKeyOldAnytypeID.String()].GetStringValue()
|
|
})
|
|
|
|
newObjectIDs := lo.Map(filtered, func(r *types.Struct, _ int) string {
|
|
return r.Fields[bundle.RelationKeyId.String()].GetStringValue()
|
|
})
|
|
newObjects := fetchObjects(t, importSession, newObjectIDs)
|
|
|
|
for id, newObject := range newObjects {
|
|
t.Run("details for "+id, func(t *testing.T) {
|
|
oldID := newIDtoOldID[id]
|
|
oldObject := oldObjects[oldID]
|
|
|
|
oldDetails := normalizeDetails(oldObject.Details[0].Details)
|
|
newDetails := normalizeDetails(substituteLinksInDetails(newObject.Details[0].Details, newIDtoOldID))
|
|
assertDetails(t, oldDetails, newDetails)
|
|
|
|
blockbuilder.AssertPagesEqualWithLinks(t, oldObject.Blocks, newObject.Blocks, newIDtoOldID)
|
|
})
|
|
}
|
|
}
|
|
|
|
func assertDetails(t *testing.T, wantdetails *domain.Details, gotdetails *domain.Details) {
|
|
for key, want := range wantDetails.Fields {
|
|
got := gotDetails.Fields[key]
|
|
assert.Equal(t, want, got, key)
|
|
}
|
|
}
|
|
|
|
func substituteLinksInDetails(d *types.Struct, idsMap map[string]string) *types.Struct {
|
|
for k := range d.Fields {
|
|
if id := pbtypes.GetString(d, k); id != "" {
|
|
if newID, ok := idsMap[id]; ok {
|
|
d.Fields[k] = pbtypes.String(newID)
|
|
}
|
|
} else if ids := pbtypes.GetStringList(d, k); len(ids) > 0 {
|
|
newIDs := lo.Map(ids, func(newID string, _ int) string {
|
|
if oldID, ok := idsMap[newID]; ok {
|
|
return oldID
|
|
} else {
|
|
return newID
|
|
}
|
|
})
|
|
d.Fields[k] = pbtypes.StringList(newIDs)
|
|
}
|
|
}
|
|
|
|
return d
|
|
}
|
|
|
|
func normalizeDetails(d *types.Struct) *types.Struct {
|
|
delete(d.Fields, bundle.RelationKeyId.String())
|
|
delete(d.Fields, bundle.RelationKeyLastModifiedBy.String())
|
|
delete(d.Fields, bundle.RelationKeyLastModifiedDate.String())
|
|
delete(d.Fields, bundle.RelationKeyCreatedDate.String())
|
|
delete(d.Fields, bundle.RelationKeyLinks.String())
|
|
delete(d.Fields, bundle.RelationKeyOldAnytypeID.String())
|
|
delete(d.Fields, bundle.RelationKeyCreator.String())
|
|
delete(d.Fields, bundle.RelationKeySource.String())
|
|
return d
|
|
}
|