GODT-2181(test): Basic ATLAS test in test context

This commit is contained in:
James Houlahan
2022-12-13 01:46:02 +01:00
parent b9b4c1c38d
commit 9623e2de6f
6 changed files with 134 additions and 47 deletions

View File

@ -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")
}

View File

@ -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 ====

View File

@ -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(

View File

@ -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"),

View File

@ -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 |

View File

@ -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.