forked from Silverfish/proton-bridge
GODT-2181(test): Basic ATLAS test in test context
This commit is contained in:
@ -18,27 +18,73 @@
|
|||||||
package tests
|
package tests
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"net/url"
|
||||||
|
"os"
|
||||||
|
|
||||||
"github.com/Masterminds/semver/v3"
|
"github.com/Masterminds/semver/v3"
|
||||||
|
"github.com/ProtonMail/go-proton-api"
|
||||||
"github.com/ProtonMail/go-proton-api/server"
|
"github.com/ProtonMail/go-proton-api/server"
|
||||||
)
|
)
|
||||||
|
|
||||||
type API interface {
|
type API interface {
|
||||||
SetMinAppVersion(*semver.Version)
|
SetMinAppVersion(*semver.Version)
|
||||||
|
AddCallWatcher(func(server.Call), ...string)
|
||||||
|
|
||||||
GetHostURL() string
|
GetHostURL() string
|
||||||
GetDomain() string
|
GetDomain() string
|
||||||
AddCallWatcher(func(server.Call), ...string)
|
GetAppVersion() string
|
||||||
RemoveAddressKey(userID, addrID, keyID string) error
|
|
||||||
|
|
||||||
Close()
|
Close()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func newTestAPI() API {
|
||||||
|
if hostURL := os.Getenv("FEATURE_TEST_HOST_URL"); hostURL != "" {
|
||||||
|
return newLiveAPI(hostURL)
|
||||||
|
}
|
||||||
|
|
||||||
|
return newFakeAPI()
|
||||||
|
}
|
||||||
|
|
||||||
type fakeAPI struct {
|
type fakeAPI struct {
|
||||||
*server.Server
|
*server.Server
|
||||||
}
|
}
|
||||||
|
|
||||||
func newFakeAPI() *fakeAPI {
|
func newFakeAPI() API {
|
||||||
return &fakeAPI{
|
return &fakeAPI{
|
||||||
Server: server.New(),
|
Server: server.New(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (api *fakeAPI) GetAppVersion() string {
|
||||||
|
return proton.DefaultAppVersion
|
||||||
|
}
|
||||||
|
|
||||||
|
type liveAPI struct {
|
||||||
|
*server.Server
|
||||||
|
|
||||||
|
domain string
|
||||||
|
}
|
||||||
|
|
||||||
|
func newLiveAPI(hostURL string) API {
|
||||||
|
url, err := url.Parse(hostURL)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return &liveAPI{
|
||||||
|
Server: server.New(server.WithProxyOrigin(hostURL)),
|
||||||
|
domain: url.Hostname(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (api *liveAPI) GetHostURL() string {
|
||||||
|
return api.Server.GetProxyURL()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (api *liveAPI) GetDomain() string {
|
||||||
|
return api.domain
|
||||||
|
}
|
||||||
|
|
||||||
|
func (api *liveAPI) GetAppVersion() string {
|
||||||
|
return os.Getenv("FEATURE_TEST_APP_VERSION")
|
||||||
|
}
|
||||||
|
|||||||
@ -113,13 +113,13 @@ func TestFeatures(testingT *testing.T) {
|
|||||||
// ==== SETUP ====
|
// ==== SETUP ====
|
||||||
ctx.Step(`^there exists an account with username "([^"]*)" and password "([^"]*)"$`, s.thereExistsAnAccountWithUsernameAndPassword)
|
ctx.Step(`^there exists an account with username "([^"]*)" and password "([^"]*)"$`, s.thereExistsAnAccountWithUsernameAndPassword)
|
||||||
ctx.Step(`^the account "([^"]*)" has additional address "([^"]*)"$`, s.theAccountHasAdditionalAddress)
|
ctx.Step(`^the account "([^"]*)" has additional address "([^"]*)"$`, s.theAccountHasAdditionalAddress)
|
||||||
|
ctx.Step(`^the account "([^"]*)" has additional address "([^"]*)" without keys$`, s.theAccountHasAdditionalAddressWithoutKeys)
|
||||||
ctx.Step(`^the account "([^"]*)" no longer has additional address "([^"]*)"$`, s.theAccountNoLongerHasAdditionalAddress)
|
ctx.Step(`^the account "([^"]*)" no longer has additional address "([^"]*)"$`, s.theAccountNoLongerHasAdditionalAddress)
|
||||||
ctx.Step(`^the account "([^"]*)" has (\d+) custom folders$`, s.theAccountHasCustomFolders)
|
ctx.Step(`^the account "([^"]*)" has (\d+) custom folders$`, s.theAccountHasCustomFolders)
|
||||||
ctx.Step(`^the account "([^"]*)" has (\d+) custom labels$`, s.theAccountHasCustomLabels)
|
ctx.Step(`^the account "([^"]*)" has (\d+) custom labels$`, s.theAccountHasCustomLabels)
|
||||||
ctx.Step(`^the account "([^"]*)" has the following custom mailboxes:$`, s.theAccountHasTheFollowingCustomMailboxes)
|
ctx.Step(`^the account "([^"]*)" has the following custom mailboxes:$`, s.theAccountHasTheFollowingCustomMailboxes)
|
||||||
ctx.Step(`^the address "([^"]*)" of account "([^"]*)" has the following messages in "([^"]*)":$`, s.theAddressOfAccountHasTheFollowingMessagesInMailbox)
|
ctx.Step(`^the address "([^"]*)" of account "([^"]*)" has the following messages in "([^"]*)":$`, s.theAddressOfAccountHasTheFollowingMessagesInMailbox)
|
||||||
ctx.Step(`^the address "([^"]*)" of account "([^"]*)" has (\d+) messages in "([^"]*)"$`, s.theAddressOfAccountHasMessagesInMailbox)
|
ctx.Step(`^the address "([^"]*)" of account "([^"]*)" has (\d+) messages in "([^"]*)"$`, s.theAddressOfAccountHasMessagesInMailbox)
|
||||||
ctx.Step(`^the address "([^"]*)" of account "([^"]*)" has no keys$`, s.theAddressOfAccountHasNoKeys)
|
|
||||||
ctx.Step(`^the following fields were changed in draft (\d+) for address "([^"]*)" of account "([^"]*)":$`, s.theFollowingFieldsWereChangedInDraftForAddressOfAccount)
|
ctx.Step(`^the following fields were changed in draft (\d+) for address "([^"]*)" of account "([^"]*)":$`, s.theFollowingFieldsWereChangedInDraftForAddressOfAccount)
|
||||||
|
|
||||||
// ==== BRIDGE ====
|
// ==== BRIDGE ====
|
||||||
|
|||||||
@ -32,6 +32,7 @@ func (t *testCtx) withProton(fn func(*proton.Manager) error) error {
|
|||||||
m := proton.New(
|
m := proton.New(
|
||||||
proton.WithHostURL(t.api.GetHostURL()),
|
proton.WithHostURL(t.api.GetHostURL()),
|
||||||
proton.WithTransport(proton.InsecureTransport()),
|
proton.WithTransport(proton.InsecureTransport()),
|
||||||
|
proton.WithAppVersion(t.api.GetAppVersion()),
|
||||||
)
|
)
|
||||||
defer m.Close()
|
defer m.Close()
|
||||||
|
|
||||||
@ -65,10 +66,23 @@ func (t *testCtx) withClientPass(ctx context.Context, username, password string,
|
|||||||
}
|
}
|
||||||
|
|
||||||
// runQuarkCmd runs the given quark command with the given arguments.
|
// runQuarkCmd runs the given quark command with the given arguments.
|
||||||
func (t *testCtx) runQuarkCmd(ctx context.Context, command string, args ...string) error {
|
func (t *testCtx) runQuarkCmd(ctx context.Context, command string, args ...string) ([]byte, error) {
|
||||||
return t.withProton(func(m *proton.Manager) error {
|
var out []byte
|
||||||
return m.Quark(ctx, command, args...)
|
|
||||||
})
|
if err := t.withProton(func(m *proton.Manager) error {
|
||||||
|
res, err := m.QuarkRes(ctx, command, args...)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
out = res
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return out, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *testCtx) withAddrKR(
|
func (t *testCtx) withAddrKR(
|
||||||
|
|||||||
@ -43,7 +43,7 @@ import (
|
|||||||
"google.golang.org/grpc"
|
"google.golang.org/grpc"
|
||||||
)
|
)
|
||||||
|
|
||||||
var defaultVersion = semver.MustParse("1.0.0")
|
var defaultVersion = semver.MustParse("3.0.6")
|
||||||
|
|
||||||
type testCtx struct {
|
type testCtx struct {
|
||||||
// These are the objects supporting the test.
|
// These are the objects supporting the test.
|
||||||
@ -104,7 +104,7 @@ func newTestCtx(tb testing.TB) *testCtx {
|
|||||||
|
|
||||||
t := &testCtx{
|
t := &testCtx{
|
||||||
dir: dir,
|
dir: dir,
|
||||||
api: newFakeAPI(),
|
api: newTestAPI(),
|
||||||
netCtl: proton.NewNetCtl(),
|
netCtl: proton.NewNetCtl(),
|
||||||
locator: locations.New(bridge.NewTestLocationsProvider(dir), "config-name"),
|
locator: locations.New(bridge.NewTestLocationsProvider(dir), "config-name"),
|
||||||
storeKey: []byte("super-secret-store-key"),
|
storeKey: []byte("super-secret-store-key"),
|
||||||
|
|||||||
@ -60,20 +60,12 @@ Feature: Bridge can fully sync an account
|
|||||||
| Labels | 0 | 0 |
|
| Labels | 0 | 0 |
|
||||||
| Labels/three | 0 | 0 |
|
| Labels/three | 0 | 0 |
|
||||||
|
|
||||||
Scenario: If an address has no keys, the account is still synced
|
Scenario: If an address has no keys, it does not break other addresses
|
||||||
Given the account "user" has additional address "alias@[domain]"
|
Given the account "user" has additional address "alias@[domain]" without keys
|
||||||
And the account "user" has the following custom mailboxes:
|
|
||||||
| name | type |
|
|
||||||
| encrypted | folder |
|
|
||||||
And the address "alias@[domain]" of account "user" has the following messages in "encrypted":
|
|
||||||
| from | to | subject |
|
|
||||||
| a@[domain] | a@[domain] | no key |
|
|
||||||
| b@[domain] | b@[domain] | no key |
|
|
||||||
And the address "alias@[domain]" of account "user" has no keys
|
|
||||||
When the user logs in with username "user" and password "password"
|
When the user logs in with username "user" and password "password"
|
||||||
And user "user" finishes syncing
|
And user "user" finishes syncing
|
||||||
When user "user" connects and authenticates IMAP client "1"
|
When user "user" connects and authenticates IMAP client "1"
|
||||||
Then IMAP client "1" eventually sees the following messages in "Folders/encrypted":
|
Then IMAP client "1" eventually sees the following messages in "Folders/one":
|
||||||
| from | to | subject | mime-type |
|
| from | to | subject | unread |
|
||||||
| a@[domain] | a@[domain] | no key | multipart/encrypted |
|
| a@[domain] | a@[domain] | one | true |
|
||||||
| b@[domain] | b@[domain] | no key | multipart/encrypted |
|
| b@[domain] | b@[domain] | two | false |
|
||||||
@ -35,7 +35,7 @@ import (
|
|||||||
|
|
||||||
func (s *scenario) thereExistsAnAccountWithUsernameAndPassword(username, password string) error {
|
func (s *scenario) thereExistsAnAccountWithUsernameAndPassword(username, password string) error {
|
||||||
// Create the user and generate its default address (with keys).
|
// Create the user and generate its default address (with keys).
|
||||||
if err := s.t.runQuarkCmd(
|
if _, err := s.t.runQuarkCmd(
|
||||||
context.Background(),
|
context.Background(),
|
||||||
"user:create",
|
"user:create",
|
||||||
"--name", username,
|
"--name", username,
|
||||||
@ -51,6 +51,22 @@ func (s *scenario) thereExistsAnAccountWithUsernameAndPassword(username, passwor
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Decrypt the user's encrypted ID for use with quark.
|
||||||
|
userDecID, err := s.t.runQuarkCmd(context.Background(), "encryption:id", "--decrypt", user.ID)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Upgrade the user to a paid account.
|
||||||
|
if _, err := s.t.runQuarkCmd(
|
||||||
|
context.Background(),
|
||||||
|
"user:create:subscription",
|
||||||
|
"--planID", "plus",
|
||||||
|
string(userDecID),
|
||||||
|
); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
addr, err := c.GetAddresses(ctx)
|
addr, err := c.GetAddresses(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@ -72,12 +88,51 @@ func (s *scenario) thereExistsAnAccountWithUsernameAndPassword(username, passwor
|
|||||||
func (s *scenario) theAccountHasAdditionalAddress(username, address string) error {
|
func (s *scenario) theAccountHasAdditionalAddress(username, address string) error {
|
||||||
userID := s.t.getUserID(username)
|
userID := s.t.getUserID(username)
|
||||||
|
|
||||||
|
// Decrypt the user's encrypted ID for use with quark.
|
||||||
|
userDecID, err := s.t.runQuarkCmd(context.Background(), "encryption:id", "--decrypt", userID)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
// Create the user's additional address.
|
// Create the user's additional address.
|
||||||
if err := s.t.runQuarkCmd(
|
if _, err := s.t.runQuarkCmd(
|
||||||
context.Background(),
|
context.Background(),
|
||||||
"user:create:address",
|
"user:create:address",
|
||||||
"--gen-keys", "RSA2048",
|
"--gen-keys", "RSA2048",
|
||||||
userID,
|
string(userDecID),
|
||||||
|
s.t.getUserPass(userID),
|
||||||
|
address,
|
||||||
|
); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return s.t.withClient(context.Background(), username, func(ctx context.Context, c *proton.Client) error {
|
||||||
|
addr, err := c.GetAddresses(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set the new address of the user.
|
||||||
|
s.t.setUserAddr(userID, addr[len(addr)-1].ID, address)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *scenario) theAccountHasAdditionalAddressWithoutKeys(username, address string) error {
|
||||||
|
userID := s.t.getUserID(username)
|
||||||
|
|
||||||
|
// Decrypt the user's encrypted ID for use with quark.
|
||||||
|
userDecID, err := s.t.runQuarkCmd(context.Background(), "--decrypt", "encryption:id", userID)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create the user's additional address.
|
||||||
|
if _, err := s.t.runQuarkCmd(
|
||||||
|
context.Background(),
|
||||||
|
"user:create:address",
|
||||||
|
string(userDecID),
|
||||||
s.t.getUserPass(userID),
|
s.t.getUserPass(userID),
|
||||||
address,
|
address,
|
||||||
); err != nil {
|
); err != nil {
|
||||||
@ -241,26 +296,6 @@ func (s *scenario) theAddressOfAccountHasMessagesInMailbox(address, username str
|
|||||||
})))
|
})))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *scenario) theAddressOfAccountHasNoKeys(address, username string) error {
|
|
||||||
userID := s.t.getUserID(username)
|
|
||||||
addrID := s.t.getUserAddrID(userID, address)
|
|
||||||
|
|
||||||
return s.t.withClient(context.Background(), username, func(ctx context.Context, client *proton.Client) error {
|
|
||||||
address, err := client.GetAddress(ctx, addrID)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, key := range address.Keys {
|
|
||||||
if err := s.t.api.RemoveAddressKey(userID, addrID, key.ID); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// accountDraftChanged changes the draft attributes, where draftIndex is
|
// accountDraftChanged changes the draft attributes, where draftIndex is
|
||||||
// similar to sequential ID i.e. 1 represents the first message of draft folder
|
// similar to sequential ID i.e. 1 represents the first message of draft folder
|
||||||
// sorted by API creation time.
|
// sorted by API creation time.
|
||||||
|
|||||||
Reference in New Issue
Block a user