Import/Export backend

This commit is contained in:
Michal Horejsek
2020-06-17 15:29:41 +02:00
parent 49316a935c
commit 1c10cc5065
107 changed files with 2869 additions and 743 deletions

View File

@ -32,9 +32,10 @@ func (ctx *TestContext) GetBridge() *bridge.Bridge {
}
// withBridgeInstance creates a bridge instance for use in the test.
// Every TestContext has this by default and thus this doesn't need to be exported.
// TestContext has this by default once called with env variable TEST_APP=bridge.
func (ctx *TestContext) withBridgeInstance() {
ctx.bridge = newBridgeInstance(ctx.t, ctx.cfg, ctx.credStore, ctx.listener, ctx.clientManager)
ctx.users = ctx.bridge.Users
ctx.addCleanupChecked(ctx.bridge.ClearData, "Cleaning bridge data")
}
@ -69,13 +70,3 @@ func newBridgeInstance(
pref := preferences.New(cfg)
return bridge.New(cfg, pref, panicHandler, eventListener, clientManager, credStore)
}
// SetLastBridgeError sets the last error that occurred while executing a bridge action.
func (ctx *TestContext) SetLastBridgeError(err error) {
ctx.bridgeLastError = err
}
// GetLastBridgeError returns the last error that occurred while executing a bridge action.
func (ctx *TestContext) GetLastBridgeError() error {
return ctx.bridgeLastError
}

View File

@ -77,6 +77,9 @@ func (c *fakeConfig) GetLogPrefix() string {
func (c *fakeConfig) GetPreferencesPath() string {
return filepath.Join(c.dir, "prefs.json")
}
func (c *fakeConfig) GetTransferDir() string {
return c.dir
}
func (c *fakeConfig) GetTLSCertPath() string {
return filepath.Join(c.dir, "cert.pem")
}

View File

@ -20,6 +20,8 @@ package context
import (
"github.com/ProtonMail/proton-bridge/internal/bridge"
"github.com/ProtonMail/proton-bridge/internal/importexport"
"github.com/ProtonMail/proton-bridge/internal/transfer"
"github.com/ProtonMail/proton-bridge/internal/users"
"github.com/ProtonMail/proton-bridge/pkg/listener"
"github.com/ProtonMail/proton-bridge/pkg/pmapi"
@ -46,10 +48,12 @@ type TestContext struct {
pmapiController PMAPIController
clientManager *pmapi.ClientManager
// Bridge core related variables.
bridge *bridge.Bridge
bridgeLastError error
credStore users.CredentialsStorer
// Core related variables.
bridge *bridge.Bridge
importExport *importexport.ImportExport
users *users.Users
credStore users.CredentialsStorer
lastError error
// IMAP related variables.
imapAddr string
@ -63,6 +67,12 @@ type TestContext struct {
smtpClients map[string]*mocks.SMTPClient
smtpLastResponses map[string]*mocks.SMTPResponse
// Transfer related variables.
transferLocalRootForImport string
transferLocalRootForExport string
transferRemoteIMAPServer *mocks.IMAPServer
transferProgress *transfer.Progress
// These are the cleanup steps executed when Cleanup() is called.
cleanupSteps []*Cleaner
@ -71,7 +81,7 @@ type TestContext struct {
}
// New returns a new test TestContext.
func New() *TestContext {
func New(app string) *TestContext {
setLogrusVerbosityFromEnv()
cfg := newFakeConfig()
@ -96,8 +106,15 @@ func New() *TestContext {
// Ensure that the config is cleaned up after the test is over.
ctx.addCleanupChecked(cfg.ClearData, "Cleaning bridge config data")
// Create bridge instance under test.
ctx.withBridgeInstance()
// Create bridge or import/export instance under test.
switch app {
case "bridge":
ctx.withBridgeInstance()
case "ie":
ctx.withImportExportInstance()
default:
panic("unknown app: " + app)
}
return ctx
}
@ -125,3 +142,13 @@ func (ctx *TestContext) GetTestingT() *bddT { //nolint[golint]
func (ctx *TestContext) GetTestingError() error {
return ctx.t.getErrors()
}
// SetLastError sets the last error that occurred while executing an action.
func (ctx *TestContext) SetLastError(err error) {
ctx.lastError = err
}
// GetLastError returns the last error that occurred while executing an action.
func (ctx *TestContext) GetLastError() error {
return ctx.lastError
}

View File

@ -0,0 +1,48 @@
// Copyright (c) 2020 Proton Technologies AG
//
// This file is part of ProtonMail Bridge.Bridge.
//
// ProtonMail Bridge is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// ProtonMail Bridge is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with ProtonMail Bridge. If not, see <https://www.gnu.org/licenses/>.
package context
import (
"github.com/ProtonMail/proton-bridge/internal/importexport"
"github.com/ProtonMail/proton-bridge/internal/users"
"github.com/ProtonMail/proton-bridge/pkg/listener"
)
// GetImportExport returns import/export instance.
func (ctx *TestContext) GetImportExport() *importexport.ImportExport {
return ctx.importExport
}
// withImportExportInstance creates a import/export instance for use in the test.
// TestContext has this by default once called with env variable TEST_APP=ie.
func (ctx *TestContext) withImportExportInstance() {
ctx.importExport = newImportExportInstance(ctx.t, ctx.cfg, ctx.credStore, ctx.listener, ctx.clientManager)
ctx.users = ctx.importExport.Users
}
// newImportExportInstance creates a new import/export instance configured to use the given config/credstore.
func newImportExportInstance(
t *bddT,
cfg importexport.Configer,
credStore users.CredentialsStorer,
eventListener listener.Listener,
clientManager users.ClientManager,
) *importexport.ImportExport {
panicHandler := &panicHandler{t: t}
return importexport.New(cfg, panicHandler, eventListener, clientManager, credStore)
}

View File

@ -33,6 +33,7 @@ type PMAPIController interface {
GetLabelIDs(username string, labelNames []string) ([]string, error)
AddUserMessage(username string, message *pmapi.Message) error
GetMessageID(username, messageIndex string) string
GetMessages(username, labelID string) ([]*pmapi.Message, error)
ReorderAddresses(user *pmapi.User, addressIDs []string) error
PrintCalls()
WasCalled(method, path string, expectedRequest []byte) bool

91
test/context/transfer.go Normal file
View File

@ -0,0 +1,91 @@
// Copyright (c) 2020 Proton Technologies AG
//
// This file is part of ProtonMail Bridge.Bridge.
//
// ProtonMail Bridge is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// ProtonMail Bridge is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with ProtonMail Bridge. If not, see <https://www.gnu.org/licenses/>.
package context
import (
"io/ioutil"
"math/rand"
"os"
"strconv"
"github.com/ProtonMail/proton-bridge/internal/transfer"
"github.com/ProtonMail/proton-bridge/test/mocks"
)
// SetTransferProgress sets transfer progress.
func (ctx *TestContext) SetTransferProgress(progress *transfer.Progress) {
ctx.transferProgress = progress
}
// GetTransferProgress returns transfer progress.
func (ctx *TestContext) GetTransferProgress() *transfer.Progress {
return ctx.transferProgress
}
// GetTransferLocalRootForImport creates temporary root for importing
// if it not exists yet, and returns its path.
func (ctx *TestContext) GetTransferLocalRootForImport() string {
if ctx.transferLocalRootForImport != "" {
return ctx.transferLocalRootForImport
}
root := ctx.createLocalRoot()
ctx.transferLocalRootForImport = root
return root
}
// GetTransferLocalRootForExport creates temporary root for exporting
// if it not exists yet, and returns its path.
func (ctx *TestContext) GetTransferLocalRootForExport() string {
if ctx.transferLocalRootForExport != "" {
return ctx.transferLocalRootForExport
}
root := ctx.createLocalRoot()
ctx.transferLocalRootForExport = root
return root
}
func (ctx *TestContext) createLocalRoot() string {
root, err := ioutil.TempDir("", "transfer")
if err != nil {
panic("failed to create temp transfer root: " + err.Error())
}
ctx.addCleanupChecked(func() error {
return os.RemoveAll(root)
}, "Cleaning transfer data")
return root
}
// GetTransferRemoteIMAPServer creates mocked IMAP server if it not created yet, and returns it.
func (ctx *TestContext) GetTransferRemoteIMAPServer() *mocks.IMAPServer {
if ctx.transferRemoteIMAPServer != nil {
return ctx.transferRemoteIMAPServer
}
port := 21300 + rand.Intn(100)
ctx.transferRemoteIMAPServer = mocks.NewIMAPServer("user", "pass", "127.0.0.1", strconv.Itoa(port))
ctx.transferRemoteIMAPServer.Start()
ctx.addCleanupChecked(func() error {
ctx.transferRemoteIMAPServer.Stop()
return nil
}, "Cleaning transfer IMAP server")
return ctx.transferRemoteIMAPServer
}

View File

@ -30,11 +30,16 @@ import (
"github.com/stretchr/testify/assert"
)
// GetUsers returns users instance.
func (ctx *TestContext) GetUsers() *users.Users {
return ctx.users
}
// LoginUser logs in the user with the given username, password, and mailbox password.
func (ctx *TestContext) LoginUser(username, password, mailboxPassword string) (err error) {
srp.RandReader = rand.New(rand.NewSource(42))
client, auth, err := ctx.bridge.Login(username, password)
client, auth, err := ctx.users.Login(username, password)
if err != nil {
return errors.Wrap(err, "failed to login")
}
@ -45,7 +50,7 @@ func (ctx *TestContext) LoginUser(username, password, mailboxPassword string) (e
}
}
user, err := ctx.bridge.FinishLogin(client, auth, mailboxPassword)
user, err := ctx.users.FinishLogin(client, auth, mailboxPassword)
if err != nil {
return errors.Wrap(err, "failed to finish login")
}
@ -57,7 +62,7 @@ func (ctx *TestContext) LoginUser(username, password, mailboxPassword string) (e
// GetUser retrieves the bridge user matching the given query string.
func (ctx *TestContext) GetUser(username string) (*users.User, error) {
return ctx.bridge.GetUser(username)
return ctx.users.GetUser(username)
}
// GetStore retrieves the store for given username.
@ -100,6 +105,9 @@ func (ctx *TestContext) WaitForSync(username string) error {
if err != nil {
return err
}
if store == nil {
return nil
}
// First wait for ongoing sync to be done before starting and waiting for new one.
ctx.eventuallySyncIsFinished(store)
store.TestSync()
@ -121,7 +129,7 @@ func (ctx *TestContext) EventuallySyncIsFinishedForUsername(username string) {
// LogoutUser logs out the given user.
func (ctx *TestContext) LogoutUser(query string) (err error) {
user, err := ctx.bridge.GetUser(query)
user, err := ctx.users.GetUser(query)
if err != nil {
return errors.Wrap(err, "failed to get user")
}
@ -135,12 +143,12 @@ func (ctx *TestContext) LogoutUser(query string) (err error) {
// DeleteUser deletes the given user.
func (ctx *TestContext) DeleteUser(query string, deleteStore bool) (err error) {
user, err := ctx.bridge.GetUser(query)
user, err := ctx.users.GetUser(query)
if err != nil {
return errors.Wrap(err, "failed to get user")
}
if err = ctx.bridge.DeleteUser(user.ID(), deleteStore); err != nil {
if err = ctx.users.DeleteUser(user.ID(), deleteStore); err != nil {
err = errors.Wrap(err, "failed to delete user")
}