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

View File

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

View File

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

View File

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

View File

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

View File

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