From 9623e2de6f98a6fd60c2919c5aac7678455c195f Mon Sep 17 00:00:00 2001 From: James Houlahan Date: Tue, 13 Dec 2022 01:46:02 +0100 Subject: [PATCH] GODT-2181(test): Basic ATLAS test in test context --- tests/api_test.go | 52 ++++++++++++++++++-- tests/bdd_test.go | 2 +- tests/ctx_helper_test.go | 22 +++++++-- tests/ctx_test.go | 4 +- tests/features/user/sync.feature | 20 +++----- tests/user_test.go | 81 +++++++++++++++++++++++--------- 6 files changed, 134 insertions(+), 47 deletions(-) diff --git a/tests/api_test.go b/tests/api_test.go index a4991acb..e7b07e3c 100644 --- a/tests/api_test.go +++ b/tests/api_test.go @@ -18,27 +18,73 @@ package tests import ( + "net/url" + "os" + "github.com/Masterminds/semver/v3" + "github.com/ProtonMail/go-proton-api" "github.com/ProtonMail/go-proton-api/server" ) type API interface { SetMinAppVersion(*semver.Version) + AddCallWatcher(func(server.Call), ...string) GetHostURL() string GetDomain() string - AddCallWatcher(func(server.Call), ...string) - RemoveAddressKey(userID, addrID, keyID string) error + GetAppVersion() string Close() } +func newTestAPI() API { + if hostURL := os.Getenv("FEATURE_TEST_HOST_URL"); hostURL != "" { + return newLiveAPI(hostURL) + } + + return newFakeAPI() +} + type fakeAPI struct { *server.Server } -func newFakeAPI() *fakeAPI { +func newFakeAPI() API { return &fakeAPI{ 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") +} diff --git a/tests/bdd_test.go b/tests/bdd_test.go index 05f3d3a5..a2f3216a 100644 --- a/tests/bdd_test.go +++ b/tests/bdd_test.go @@ -113,13 +113,13 @@ func TestFeatures(testingT *testing.T) { // ==== SETUP ==== 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 "([^"]*)" without keys$`, s.theAccountHasAdditionalAddressWithoutKeys) 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 labels$`, s.theAccountHasCustomLabels) 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 (\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) // ==== BRIDGE ==== diff --git a/tests/ctx_helper_test.go b/tests/ctx_helper_test.go index 0e7fc6e4..6642484d 100644 --- a/tests/ctx_helper_test.go +++ b/tests/ctx_helper_test.go @@ -32,6 +32,7 @@ func (t *testCtx) withProton(fn func(*proton.Manager) error) error { m := proton.New( proton.WithHostURL(t.api.GetHostURL()), proton.WithTransport(proton.InsecureTransport()), + proton.WithAppVersion(t.api.GetAppVersion()), ) 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. -func (t *testCtx) runQuarkCmd(ctx context.Context, command string, args ...string) error { - return t.withProton(func(m *proton.Manager) error { - return m.Quark(ctx, command, args...) - }) +func (t *testCtx) runQuarkCmd(ctx context.Context, command string, args ...string) ([]byte, error) { + var out []byte + + 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( diff --git a/tests/ctx_test.go b/tests/ctx_test.go index d1cf9377..e7a0ef38 100644 --- a/tests/ctx_test.go +++ b/tests/ctx_test.go @@ -43,7 +43,7 @@ import ( "google.golang.org/grpc" ) -var defaultVersion = semver.MustParse("1.0.0") +var defaultVersion = semver.MustParse("3.0.6") type testCtx struct { // These are the objects supporting the test. @@ -104,7 +104,7 @@ func newTestCtx(tb testing.TB) *testCtx { t := &testCtx{ dir: dir, - api: newFakeAPI(), + api: newTestAPI(), netCtl: proton.NewNetCtl(), locator: locations.New(bridge.NewTestLocationsProvider(dir), "config-name"), storeKey: []byte("super-secret-store-key"), diff --git a/tests/features/user/sync.feature b/tests/features/user/sync.feature index e7c84f59..bdbab918 100644 --- a/tests/features/user/sync.feature +++ b/tests/features/user/sync.feature @@ -60,20 +60,12 @@ Feature: Bridge can fully sync an account | Labels | 0 | 0 | | Labels/three | 0 | 0 | - Scenario: If an address has no keys, the account is still synced - Given the account "user" has additional address "alias@[domain]" - 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 + Scenario: If an address has no keys, it does not break other addresses + Given the account "user" has additional address "alias@[domain]" without keys When the user logs in with username "user" and password "password" And user "user" finishes syncing When user "user" connects and authenticates IMAP client "1" - Then IMAP client "1" eventually sees the following messages in "Folders/encrypted": - | from | to | subject | mime-type | - | a@[domain] | a@[domain] | no key | multipart/encrypted | - | b@[domain] | b@[domain] | no key | multipart/encrypted | \ No newline at end of file + Then IMAP client "1" eventually sees the following messages in "Folders/one": + | from | to | subject | unread | + | a@[domain] | a@[domain] | one | true | + | b@[domain] | b@[domain] | two | false | \ No newline at end of file diff --git a/tests/user_test.go b/tests/user_test.go index 19da22b4..6f5ccacb 100644 --- a/tests/user_test.go +++ b/tests/user_test.go @@ -35,7 +35,7 @@ import ( func (s *scenario) thereExistsAnAccountWithUsernameAndPassword(username, password string) error { // Create the user and generate its default address (with keys). - if err := s.t.runQuarkCmd( + if _, err := s.t.runQuarkCmd( context.Background(), "user:create", "--name", username, @@ -51,6 +51,22 @@ func (s *scenario) thereExistsAnAccountWithUsernameAndPassword(username, passwor 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) if err != nil { return err @@ -72,12 +88,51 @@ func (s *scenario) thereExistsAnAccountWithUsernameAndPassword(username, passwor func (s *scenario) theAccountHasAdditionalAddress(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(), "encryption:id", "--decrypt", userID) + if err != nil { + return err + } + // Create the user's additional address. - if err := s.t.runQuarkCmd( + if _, err := s.t.runQuarkCmd( context.Background(), "user:create:address", "--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), address, ); 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 // similar to sequential ID i.e. 1 represents the first message of draft folder // sorted by API creation time.